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
{{ message }}
Repository navigation
Commit 141d12d
Browse filesBrowse the repository at this point in the historyBrowse files
feat(_)!: new syntax: <key>._.<env>.<directive> = <value>
This breaking change adds a separator between the key section and
profile/tags declaration section.
Also, tags declaration syntax is now: `<key>.tags = [...]`
Reasons:
- Better visiblity - easy to recognise the key part.
- Only `_` is now a reserved keyword for the key segment.
- Only `default` and `tags` and reserved keywords for the profile options.
- Tags are now simplified - detached from profiles.
@@ -43,7 +43,10 @@ The words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOU
43
43
-**Profile:** A named environment or operating mode, such as `dev`, `stage`, or `prod`.
44
44
-**Default profile:** The `default` profile. It provides values that apply to all profiles unless a profile overrides them.
45
45
-**Setting key:** A setting name that can contain dot-separated parts, such as `database.host`.
46
-
-**Directive:** The final part of a setting declaration. Supported directives are `val`, `env`, `null`, and `tags`.
46
+
-**Separator:** The reserved `_` component that separates the setting key from the rest of a declaration.
47
+
-**Profile declaration:** A declaration of the form `{key}._.{profile}.{directive}`.
48
+
-**Directive:** The final part of a profile declaration. Supported directives are `val`, `env`, and `null`.
49
+
-**Tags declaration:** A declaration of the form `{key}._.tags`. It attaches tags to a setting regardless of profile.
47
50
-**Active profile:** The profile selected for the current run.
48
51
-**Export target:** A file or standard output where resolved settings are written.
49
52
@@ -66,17 +69,19 @@ The file has two top-level sections:
66
69
67
70
### 2.2 Setting names
68
71
69
-
SettingSpec uses dot-separated keys to represent settings, profiles, and directives.
72
+
SettingSpec uses dot-separated keys to represent settings, profiles, and directives. The reserved `_` component separates the setting key from the rest of the declaration.
70
73
71
74
```toml
72
75
[settings]
73
-
key1.default.val = "val1"
76
+
key1._.default.val = "val1"
77
+
key1._.tags = ["tag1"]
74
78
```
75
79
76
-
The general form is:
80
+
The general forms are:
77
81
78
82
```text
79
-
{key}.{profile}.{directive} = {value}
83
+
{key}._.{profile}.{directive} = {value}
84
+
{key}._.tags = [{tag}, ...]
80
85
```
81
86
82
87
---
@@ -132,8 +137,8 @@ When `profile.options` is set:
132
137
1. The active profile MUST be in the list.
133
138
2. Every profile used in `[settings]` MUST be in the list or be `default`.
134
139
3.`default` MUST NOT be included in `profile.options`.
135
-
4. Every setting MUST either have a `default` declaration or have a declaration for every profile in `profile.options`.
136
-
5. Profile names MUST NOT be `val`, `env`, `null`, or `tags`.
140
+
4. Every setting MUST either have a `default`profile declaration or have a declaration for every profile in `profile.options`. A tags declaration alone does not count as a profile declaration.
141
+
5. Profile names MUST NOT be `default` or `tags`.
137
142
138
143
If any of these rules fail, configuration loading MUST stop with an error.
139
144
@@ -346,53 +351,71 @@ The `[settings]` section defines setting values, environment variables, tags, an
346
351
347
352
### 4.1 Declaration format
348
353
349
-
Each setting uses this form:
354
+
Each setting is defined by one or more declarations. There are two kinds.
355
+
356
+
A **profile declaration** provides a value for a profile:
350
357
351
358
```text
352
-
{key}.{profile}.{directive} = {value}
359
+
{key}._.{profile}.{directive} = {value}
360
+
```
361
+
362
+
A **tags declaration** attaches tags to a setting:
363
+
364
+
```text
365
+
{key}._.tags = [{tag}, ...]
353
366
```
354
367
355
368
For example:
356
369
357
370
```toml
358
-
app.name.default.val = "MyApp"
359
-
app.port.dev.val = 8080
360
-
app.port.prod.val = 80
371
+
app.name._.default.val = "MyApp"
372
+
app.port._.dev.val = 8080
373
+
app.port._.prod.val = 80
374
+
app.port._.tags = ["network"]
361
375
```
362
376
363
-
The three parts are:
377
+
The parts are:
364
378
365
-
-`{key}` — the setting name, such as `database.host`.
379
+
-`{key}` — the setting name, such as `database.host`. It can contain dot-separated parts.
380
+
-`_` — the reserved separator between the key and the rest of the declaration.
366
381
-`{profile}` — a profile from `profile.options`, or `default`.
367
-
-`{directive}` — `val`, `env`, `null`, or `tags`.
382
+
-`{directive}` — `val`, `env`, or `null`.
368
383
-`{value}` — the value for that directive.
369
384
370
-
SettingSpec reads a dotted declaration by position:
385
+
#### Parsing declarations
386
+
387
+
SettingSpec reads a dotted declaration by splitting it at the `_` component:
388
+
389
+
1. Everything before the `_` component is the setting key. It MUST NOT be empty.
390
+
2. Everything after the `_` component MUST be one of:
391
+
- the single component `tags`, which makes it a tags declaration, or
392
+
- exactly two components, `{profile}.{directive}`, which makes it a profile declaration.
393
+
3. A declaration with no `_` component, more than one `_` component, or any other shape after the `_` component is malformed.
371
394
372
-
1. The last part is the directive.
373
-
2. The part before it is the profile.
374
-
3. Everything before those two parts is the setting key.
395
+
Because the separator is explicit, setting keys MAY contain parts named `val`, `env`, `null`, or `tags`. For example, `group1.env._.default.val` declares the setting `group1.env`.
375
396
376
397
### 4.1.1 Reserved names
377
398
378
-
The names `val`, `env`, `null`, and `tags` are reserved.
399
+
The name `_` is reserved as the separator.
379
400
380
-
They MUST NOT be used:
401
+
- It MUST NOT be used as any part of a setting key.
381
402
382
-
- In any part of a setting key.
383
-
- As a profile name.
403
+
The names `default` and `tags` are reserved in the profile position.
404
+
405
+
-`default` is the default profile. It MUST NOT be listed in `profile.options`.
406
+
-`tags` marks a tags declaration. It MUST NOT be used as a profile name.
384
407
385
408
For example, these are invalid:
386
409
387
410
```toml
388
-
env.default.val = "x"
389
-
group1.tags.prod.val = "y"
390
-
val.default.env = "Z"
411
+
_.default.val = "x"# empty key
412
+
key1.default.val = "x"# missing `_` separator
413
+
key1._.default._.val = "y"# more than one `_` component
414
+
key1._.tags.val = "z"# `tags` used as a profile
415
+
key1._.default = "w"# missing directive
391
416
```
392
417
393
-
This rule prevents dotted names from being interpreted incorrectly.
394
-
395
-
SettingSpec MUST reject these conflicts during configuration validation and report the offending name.
418
+
SettingSpec MUST reject these during configuration validation and report the offending declaration.
396
419
397
420
### 4.2 Directives
398
421
@@ -413,17 +436,17 @@ Supported values include:
413
436
Example:
414
437
415
438
```toml
416
-
app.name.default.val = "MyApp"
417
-
app.port.dev.val = 8080
418
-
app.port.prod.val = 80
439
+
app.name._.default.val = "MyApp"
440
+
app.port._.dev.val = 8080
441
+
app.port._.prod.val = 80
419
442
```
420
443
421
444
#### `env`
422
445
423
446
Gets the value from an environment variable.
424
447
425
448
```toml
426
-
database.password.default.env = "DB_PASSWORD"
449
+
database.password._.default.env = "DB_PASSWORD"
427
450
```
428
451
429
452
SettingSpec reads `DB_PASSWORD` when the setting is resolved. This includes variables loaded from `spec.envfile`.
@@ -433,9 +456,9 @@ SettingSpec reads `DB_PASSWORD` when the setting is resolved. This includes vari
433
456
A setting can provide an environment variable plus a fallback value:
434
457
435
458
```toml
436
-
secret2.default.val = "defaultvalue"
437
-
secret2.default.env = "SECRET2"
438
-
secret2.prod.env = "PRODSECRET"
459
+
secret2._.default.val = "defaultvalue"
460
+
secret2._.default.env = "SECRET2"
461
+
secret2._.prod.env = "PRODSECRET"
439
462
```
440
463
441
464
The rules are:
@@ -449,8 +472,8 @@ The rules are:
449
472
Marks a setting as unset for a profile.
450
473
451
474
```toml
452
-
debug_banner.default.val = "My App BETA 0.0.1"
453
-
debug_banner.prod.null = true
475
+
debug_banner._.default.val = "My App BETA 0.0.1"
476
+
debug_banner._.prod.null = true
454
477
```
455
478
456
479
When exported:
@@ -460,14 +483,21 @@ When exported:
460
483
-`null` can only be `true`.
461
484
-`null` cannot be used together with `val` for the same profile.
462
485
463
-
#### `tags`
486
+
###4.3 Tags
464
487
465
-
Adds metadata tags to a setting.
488
+
Tags add metadata to a setting. They are declared after the key, not per profile, so they apply to the setting in every profile.
466
489
467
490
```toml
468
-
api_key.default.tags = ["sensitive", "auth"]
491
+
api_key._.default.env = "API_KEY"
492
+
api_key._.tags = ["sensitive", "auth"]
469
493
```
470
494
495
+
The rules are:
496
+
497
+
1. The value MUST be an array of strings.
498
+
2. A setting MAY have at most one tags declaration.
499
+
3. A tags declaration does not define a value. A setting that has only a tags declaration and no profile declarations is treated as having no declaration for resolution purposes (see section 5.2).
500
+
471
501
Tags are not exported as setting values. They can be used when selecting settings for an export, for example with `#auth`.
472
502
473
503
---
@@ -518,7 +548,7 @@ flowchart TD
518
548
519
549
For the same rules in numbered form:
520
550
521
-
1. If `K` has a declaration for `P`:
551
+
1. If `K` has a profile declaration for `P`:
522
552
- If its `env` variable exists, use that value.
523
553
- Otherwise, use its `val` or `null` value if one exists.
524
554
2. If there is no declaration for `P`, check `default`:
@@ -528,7 +558,9 @@ For the same rules in numbered form:
528
558
- With `profile.options`, this is a profile-coverage error.
529
559
- Without `profile.options`, the key is omitted.
530
560
531
-
The flow above assumes that the setting key, profile, and directive have already been identified correctly. Reserved names such as `val`, `env`, `null`, and `tags` must be rejected during validation.
561
+
Tags declarations are not profile declarations and play no part in this flow.
562
+
563
+
The flow above assumes that each declaration has already been parsed into its key, profile, and directive. Malformed declarations and reserved names must be rejected during validation.
532
564
533
565
Environment files and their decryption are completed before this process starts, so their variables are available during resolution.
534
566
@@ -540,9 +572,9 @@ It checks that:
540
572
541
573
1. Every profile declared in settings belongs to the options declared in spec.
542
574
2. Every setting has complete profile coverage when strict profile validation is enabled.
543
-
3. Every declaration uses valid directives.
544
-
4. No setting key or profile name uses a reserved directive name.
545
-
5.Envionment values for the active profile are present if required.
575
+
3. Every declaration is well-formed and uses a valid directive (`val`, `env`, or `null`) or is a valid tags declaration.
576
+
4. No setting key contains the reserved `_` component, and no profile name is `default` or `tags` in `profile.options`.
577
+
5.Environment values for the active profile are present if required.
546
578
547
579
---
548
580
@@ -740,7 +772,7 @@ settingspec check [OPTIONS]
740
772
It checks:
741
773
742
774
- TOML syntax.
743
-
-Reserved keywords in setting names and profiles.
775
+
-Malformed declarations, the reserved `_` separator in setting keys, and reserved profile names (`default`, `tags`).
744
776
- Profile completeness.
745
777
- Required environment variables for active profile.
746
778
- Decryption identities for encrypted environment files used by the active profile.
0 commit comments