feat(cli): take instance bootstrap values from the environment - #336
Open
Lob26 wants to merge 1 commit into
Open
feat(cli): take instance bootstrap values from the environment#336Lob26 wants to merge 1 commit into
Lob26 wants to merge 1 commit into
Conversation
Binding the first owner is the one step between a started bundle and a
usable instance, and today it needs a local Node toolchain, the published
CLI, and ten flags typed correctly. The CLI already travels inside the api
image for exactly this reason, but nothing in docker-compose.yml reaches
it.
`facility instance bootstrap` now reads each option from its
`FACILITY_<OPTION>` variable — `--org-slug` from `FACILITY_ORG_SLUG` — so
a one-shot container task carries the binding in its environment and needs
no command line at all. A `bootstrap` Compose profile runs it:
docker compose --profile bootstrap run --rm bootstrap
Environment rather than arguments is the point, not a convenience. An
organization name and a GitHub login are operator input, and the shape
that would otherwise fit a Compose service is `sh -c "facility instance
bootstrap --org-name $FACILITY_ORG_NAME ..."`, which interpolates that
input into a command. Passing values as variables to an exec-form command
removes the shell from the path instead of quoting around it.
Precedence and validation are deliberate: an explicit option wins over its
variable, a malformed option fails rather than being rescued by an ambient
one, and a value is validated identically whichever way it arrived — a
variable is not more trusted for having come from the environment. Missing
values are reported under both spellings, because the operator reading
that error in a container log has only the variable.
The profile keeps `bootstrap` out of `docker compose up`, and its
variables use `:-` rather than `:?` on purpose: a required-variable
interpolation is evaluated for the whole file and would fail `up` itself.
The CLI names what is missing.
Covered without Postgres or the network by refusing at the first database
call: an environment-only run reaches it, an explicit option overrides a
variable, a blank option still defers to one, a malformed option does not,
and the missing-value message is pinned in full.
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
facility instance bootstrapreads each option from itsFACILITY_<OPTION>environment variable —
--org-slugfromFACILITY_ORG_SLUG— so a one-shotcontainer task can carry the binding without a command line. A
bootstrapCompose profile runs it:
Why
Binding the first owner is the one step between a started bundle and a usable
instance, and today it needs a local Node toolchain, the published CLI, and ten
flags typed correctly. The CLI already travels inside the api image for exactly
this reason — the
Dockerfilesays so and installs afacilitywrapper on PATH— but nothing in
docker-compose.ymlreached it.Environment rather than arguments is the design decision, not a convenience.
An organization name and a GitHub login are operator input. The shape that
otherwise fits a Compose service is
sh -c "facility instance bootstrap --org-name $FACILITY_ORG_NAME …", whichinterpolates that input into a command. Passing values as variables to an
exec-form command removes the shell from the path instead of quoting around it —
the same lesson as #214 and #186, where a discovered directory name and a git ref
reached a shell unquoted.
Precedence and validation are deliberate: an explicit option wins over its
variable, a malformed option fails rather than being rescued by an ambient
one, and a value is validated identically whichever way it arrived — a variable
is not more trusted for having come from the environment. Missing values are
reported under both spellings, because the operator reading that error in a
container log has only the variable.
The profile keeps
bootstrapout ofdocker compose up, and its variables use:-rather than:?on purpose: a required-variable interpolation is evaluatedfor the whole file and would fail
upitself. The CLI names what is missing.Verification
Five new cases, deterministic without PostgreSQL or the network — the injected
client refuses at the first database call, so a run that reaches it has passed
every validation:
(
--org-slug must be a lowercase URL slug, not accepted);That includes the pre-existing transactional/idempotency test, which normally
skips: I ran it against a real PostgreSQL rather than leaving it skipped, so the
advisory-lock and conflict paths were exercised.
The Compose profile was resolved, not assumed:
pnpm verifypasses locallypnpm verifydoes not pass on this machine and not because of this change:test:devreports 116 tests, 92 pass, 24 fail here, and I measured theidentical 92/24 on a clean
main— Windows noise in the patchedimage-sizecases,
tarfailing to resolveC:, and the registry publication tests. Beyondthe suite, the profile was resolved with real
docker compose configruns, whichis what confirmed that
:?would have brokenupfor everyone.Not done on purpose: the master key is not auto-generated. It looks like the
obvious companion, and it is the wrong feature. A generated key that is not
persisted makes every stored project secret undecryptable on the next start, and
one written to a file is a secret on disk with whatever mode the writer chose.
${SECRET_MASTER_KEY:?set SECRET_MASTER_KEY}is already a good error for a valuethe operator must own.
Related: #19, which asks for the operator bootstrap to be automated in a
cloud-agnostic bundle. This is that step; it does not close the issue.