Repository navigation
Commit 52cab47
authored
Add environment-selected config branches (v0.1.11) (#54)
A `--config` file could describe only one deployment. Teams targeting dev
and prod kept parallel files that drifted, or wrapped the CLI in a shell
script that picked a path from an environment variable. Neither is
reviewable: the selection logic lives outside the config the reviewer reads.
Support a top-level `_select` node in the shared loader, so one file can
carry every environment it serves:
_select:
env: TANGLE_ENV
cases:
dev:
base_url: https://api.dev
prod:
_defaults:
base_url: https://api.prod
configs:
- filter: active
- filter: finished
The selected branch is a complete config document -- a single object, a
list of objects, or `_defaults` + `configs` -- and then feeds the existing
loader unchanged, so every downstream command inherits the feature with no
call-site change. A branch may itself be another `_select`, which composes
independent dimensions such as environment and region.
Selection is deliberately unforgiving, because a config that quietly picks
the wrong deployment is worse than one that refuses to load:
* Matching is exact against `os.environ[NAME]`: no trimming, case folding,
or interpolation. `" dev"` and `"DEV"` do not match `dev`.
* An unset variable or an unmatched value is an error. There is no implicit
default and no implicit production branch.
* Fallback exists only where it is authored, via an explicit `default`
sibling of `cases`. An exact case match always wins over it, and a case
literally named `default` stays an ordinary exact-match case.
* The raw environment value is never echoed. Diagnostics list only the
configured case names, which are themselves validated as printable and
bounded and are rendered through a sanitizer, so a hostile variable
cannot inject control characters into a log.
Validation runs before the environment is read, so a malformed selector
fails identically whichever deployment a reader is testing. Every immediate
case and the `default` branch are checked as complete config documents
using the same shape rules the loader applies to a whole file, recursing
through nested selectors. Only environment *lookups* stay lazy: a dormant
branch is shape-checked but never demands its own variable be set.
At a selector node only `_select` and other underscore-prefixed helper keys
may appear, which keeps YAML anchor holders usable while refusing an
ordinary sibling that a reader would reasonably expect to be merged into
the result. `_select` accepts only `env`, `cases`, and `default`; `else`,
`fallback`, and `defaults` are rejected rather than silently ignored.
Nesting is capped at 32 levels, which also terminates a self-referential
YAML alias. Because anchors let one node be reached through many edges,
structural validation memoizes per load, keyed by node identity and the
greatest depth already proven clean -- success at some depth implies
success anywhere shallower, while a deeper occurrence is revalidated so the
cap stays exact. Without it a compact anchored file was exponential: a
1.4 KB 18-level alias DAG took 3.3s and is now 2ms.
`_select` is the only newly reserved key and is an exact name, not a
prefix, so `_selector` remains an ordinary ignored helper key. A config
without `_select` is unchanged byte for byte and in behavior.
Assisted-By: devx/01a092bb-921e-7186-97f7-1a9db9766e5d1 parent d359f65 commit 52cab47
9 files changed
Lines changed: 1733 additions & 9 deletions
File tree
- packages/tangle-cli/src/tangle_cli
- skills/tangent
- references
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| |||
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
233 | 283 | | |
234 | 284 | | |
235 | 285 | | |
| |||
305 | 355 | | |
306 | 356 | | |
307 | 357 | | |
308 | | - | |
| 358 | + | |
309 | 359 | | |
310 | 360 | | |
311 | 361 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
0 commit comments