feat(env): environment profiles, per-env state + tokens, protected-env guardrail, promotion docs - #56
Merged
Merged
Conversation
Add ct.envs.json environment profiles (host, per-env state file, tokenEnv reference, protected flag) and a prepareEnv wiring helper that resolves a named profile into the existing single-host resolution by writing its host and (for CI) token into the process env — so resolveConfig/authedSession/ resolveStatePath pick them up and the --env-less path stays byte-identical. Extend the Keychain token store to per-host accounts (account name = host) with a backward-compatible fallback to the legacy single blob when its host matches, so one machine can hold logins for several instances. authedSession now resolves the token for the target host; the host<->token binding check is preserved. Also: resolveStatePath takes a per-env fallback; CtClient exposes its resolved CT version; confirmEnv gates protected environments (no force/assumeYes escape).
…22) Wire --env <name> into plan, apply, destroy, adopt, adopt grants, state list, and get (host-only). plan --env surfaces the target env name + its CT version in the header (per-env version gate). apply/destroy against a protected env always require typed confirmation of the env name — --auto-approve/--force never bypass it; --confirm-env <name> substitutes for the typed input in CI.
Env profile loading/validation, prepareEnv wiring (host/token/state, no-env passthrough), confirmEnv guardrail, multi-host tokenStore fallback, and command-level plan --env (two hosts/two state files, version header, cross-contamination refusal) + apply/destroy protected-env guardrail.
2000game
force-pushed
the
feat/environments-22
branch
from
July 9, 2026 11:36
53b10f8 to
e1c2a7e
Compare
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.
Summary
Introduces an explicit environment concept so one config repo drives several ChurchTools instances (e.g.
eqrm-devrehearsal +prod), Terraform-workspace-style, with no file edits when switching. Closes #22.What landed (all scope items)
ct.envs.json(default path;CT_ENVSoverride) declares named(host, state file, token reference)triples. Every state/host-touching command takes--env <name>(-e). No--env→ current single-host behaviour, byte-identical.tokenStorenow keys Keychain accounts by host, so one machine holds logins for several instances at once. Backward-compatible: an existing single blob is still read (as a fallback when its host matches).ct auth loginwrites both the per-host account and the default pointer. Token order per env:CT_LOGINTOKEN(CI; a profiletokenEnvis copied here) → host-keyed Keychain entry.ct-state.<env>.jsonconvention, overridable per profile viastate. Both committed."protected": truemakes apply/destroy always require typed confirmation of the env name, even with--auto-approve/--force.--confirm-env <name>(must match exactly) substitutes for the typed input in CI.--refresh) → plan prod → apply prod.ct plan --env <name>header surfaces the env name and the target instance's live CT version, so a dev/prod skew is visible before promoting.Design
prepareEnvresolves a named profile and writes its host (and, for CI, token) intoprocess.env, so the unchangedresolveConfig/authedSession/resolveStatePathpick them up — no host/token/state triple threaded through every helper, and the--env-less path is untouched. Cross-contamination is impossible: the existing state host-check (Refusing to mix instances) binds each state file to its host; a test covers the env path specifically.Test evidence
npm test→ 382 passed | 4 skippednpm run typecheck→ cleannpm run lint→ cleanNew tests:
envs(profile load/validate),env-context(prepareEnv wiring + no-env passthrough),prompt(confirmEnv),tokenStore-multihost(per-host + legacy fallback),plan-env-command(two hosts/two state files, version header, cross-contamination refusal),apply-env-command+destroy-env-command(protected-env guardrail incl.--auto-approve/--forcerefusal and--confirm-envmatch), pluscliregistration.Merge-coordination note
Per the task brief, other agents are concurrently editing
src/commands/get.ts(pagination + API-client error rendering). This PR makes a minimal touch there — option plumbing only: adds-e, --env <name>to theget <resource>subcommands andget raw, and a one-lineprepareEnvHost(opts)call beforeauthedSession(). No logic in the GET request/response path is changed. Flagging for the merge coordinator.