You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
profile is being misused to select environments. In riffcc/infra, the prod and test k3s clusters share ONE site — same inventory (labs/london), same base secrets (../infra-secrets/london) — differing only by a cluster-identity var (k3s_group_prefix). That's the environment axis, not the site/context axis. But Jetpack's only inventory+secrets preset is profile, so to pick test without -e we wrote profiles: { test: { … } } — which is semantically wrong: profile's own tests (config_file.rs:281-308) use london and ci (a site and a CI context), not env names. There is no first-class way to say "this site, but the test environment."
The consequence: the moment a second site or a staging env appears, the model collapses — london-test, london-staging, nyc-test… all profiles, with site and env mashed into one token.
environment = env-within (prod/test/staging) → layers an env-scoped vars overlay on top of whatever the profile selected, selected independently.
Selecting an env appends its overlay paths to the secrets load list (loaded last → later-wins, loading.rs:135), so an env can pin any per-env var (e.g. k3s_group_prefix: test-k8s) without touching the site inventory. --no-secrets skips it like any other secrets path.
ARGUMENT_ENVIRONMENT (--environment / -E) through the flag tables (:288-289, :332-333, :384-385), the dispatch arm (:718-719), and store_environment (mirror store_profile :1135).
Application — src/cli/parser.rs:1197-1216 (the profile-resolution block):
After profile resolution, resolve let env_name = self.environment.clone().or(defaults.environment.clone());
If Some(name): look up config.environments[name] (clear error if absent — catches typos), and append its secrets_inventory paths to defaults.secrets_inventory (after the profile/base paths, via resolve_repo_relative_path). Order = profile first, env last, so the env overlay wins.
self.active_environment = env_name; (surface for the summary, like active_profile at :1220).
The appended paths flow through inventory_load_paths (:964-968) → skipped by --no-secrets (:966) for free.
Resolution summary: add an Environment: row beside Profile:/Secrets: (~parser.rs:1292).
No change to loading.rs — vars-only overlays (:78-81) and later-wins merge (:134-135) already do the right thing.
Considerations
Orthogonality, not nesting. Environment is a separate axis (flag + defaults.environment + environments: map), NOT a profiles.X.environment attribute — nesting would re-collapse the two axes. (A future profiles.X.environment pin is possible but out of scope for v1.)
Env overlay rides the secrets path (skipped by --no-secrets). An env's non-secret vars (e.g. k3s_group_prefix) therefore don't load under --no-secrets. Acceptable for now (you select an env precisely to get its truth); a future non-secret vars_inventory overlay would refine this.
No auto var injection. The env overlay defines its own vars (environment: test, k3s_group_prefix: test-k8s, …); Jetpack just loads the overlay. Keeps it general.
Additive within v1. All fields optional; SUPPORTED_SCHEMA_VERSION stays 1. Existing contracts unchanged.
CLI: --environment test overrides defaults.environment; -E alias works.
Overlay merge: an env-overlay var shadows the base (later-wins) — integration mirroring group_vars_merge_across_multiple_inventory_paths (loading.rs:488).
Orthogonality: --profile X --environment Y loads X's inventory+secrets AND Y's overlay.
--no-secrets skips the env overlay.
Unknown env (--environment foo, no environments.foo) → clear error naming the env.
Unblocks the clean prod/test model for riffcc/infra (currently misusing profiles: { test }), and scales to multi-site + staging. Natural sibling to profiles (#47/#54) and to the templated play.groups work (#52). After this ships, riffcc/infra migrates profiles: { test } → defaults.environment: prod + environments: { test: { … } }, invoked via --environment test.
Problem
profileis being misused to select environments. Inriffcc/infra, the prod and test k3s clusters share ONE site — same inventory (labs/london), same base secrets (../infra-secrets/london) — differing only by a cluster-identity var (k3s_group_prefix). That's the environment axis, not the site/context axis. But Jetpack's only inventory+secrets preset isprofile, so to pick test without-ewe wroteprofiles: { test: { … } }— which is semantically wrong:profile's own tests (config_file.rs:281-308) uselondonandci(a site and a CI context), not env names. There is no first-class way to say "this site, but the test environment."The consequence: the moment a second site or a staging env appears, the model collapses —
london-test,london-staging,nyc-test… all profiles, with site and env mashed into one token.Proposed
A native
environmentaxis, orthogonal toprofile:profile= location/site/context → selectsinventory+secrets_inventory(unchanged).environment= env-within (prod/test/staging) → layers an env-scoped vars overlay on top of whatever the profile selected, selected independently.Selecting an env appends its overlay paths to the secrets load list (loaded last → later-wins,
loading.rs:135), so an env can pin any per-env var (e.g.k3s_group_prefix: test-k8s) without touching the site inventory.--no-secretsskips it like any other secrets path.Contract shape
Implementation anchors
Schema —
src/cli/config_file.rs:JetpackFileConfig(:44-58): addenvironments: Option<BTreeMap<String, JetpackEnvironment>>besideprofiles(:54).JetpackDefaults(:60-71): addenvironment: Option<String>besideprofile(:70).JetpackEnvironment { secrets_inventory: Option<Vec<String>> }, parallelingJetpackProfile(:76-81). (Some(vec![])clears;Noneinherits.)EffectiveDefaults(:102-111): addenvironment: Option<String>; surface it ineffective()(:119-139).CLI —
src/cli/parser.rs(mirror the--profilemachinery end-to-end):environment+active_environmentbesideprofile/active_profile(:91-97).ARGUMENT_ENVIRONMENT(--environment/-E) through the flag tables (:288-289, :332-333, :384-385), the dispatch arm (:718-719), andstore_environment(mirrorstore_profile:1135).Application —
src/cli/parser.rs:1197-1216(the profile-resolution block):let env_name = self.environment.clone().or(defaults.environment.clone());Some(name): look upconfig.environments[name](clear error if absent — catches typos), and append itssecrets_inventorypaths todefaults.secrets_inventory(after the profile/base paths, viaresolve_repo_relative_path). Order = profile first, env last, so the env overlay wins.self.active_environment = env_name;(surface for the summary, likeactive_profileat :1220).inventory_load_paths(:964-968) → skipped by--no-secrets(:966) for free.Resolution summary: add an
Environment:row besideProfile:/Secrets:(~parser.rs:1292).No change to
loading.rs— vars-only overlays (:78-81) and later-wins merge (:134-135) already do the right thing.Considerations
defaults.environment+environments:map), NOT aprofiles.X.environmentattribute — nesting would re-collapse the two axes. (A futureprofiles.X.environmentpin is possible but out of scope for v1.)--no-secrets). An env's non-secret vars (e.g.k3s_group_prefix) therefore don't load under--no-secrets. Acceptable for now (you select an env precisely to get its truth); a future non-secretvars_inventoryoverlay would refine this.environment: test,k3s_group_prefix: test-k8s, …); Jetpack just loads the overlay. Keeps it general.SUPPORTED_SCHEMA_VERSIONstays 1. Existing contracts unchanged.Tests (TDD)
defaults.environment+environments:parse;effective().environment == Some("prod");JetpackEnvironment.secrets_inventoryround-trips;Some(vec![])vsNonedistinction (mirrorprofile_secrets_empty_vec_is_distinct_from_absent:314).--environment testoverridesdefaults.environment;-Ealias works.group_vars_merge_across_multiple_inventory_paths(loading.rs:488).--profile X --environment Yloads X's inventory+secrets AND Y's overlay.--no-secretsskips the env overlay.--environment foo, noenvironments.foo) → clear error naming the env.groups: ["{{ k3s_group_prefix }}-bootstrap"]resolves to the overlay's value under--environment test, base default otherwise (mirror the Templateplay.groups— parameterize a playbook's target groups from vars #52 e2e attraversal.rs:2019).Context
Unblocks the clean prod/test model for
riffcc/infra(currently misusingprofiles: { test }), and scales to multi-site + staging. Natural sibling to profiles (#47/#54) and to the templatedplay.groupswork (#52). After this ships,riffcc/inframigratesprofiles: { test }→defaults.environment: prod+environments: { test: { … } }, invoked via--environment test.