@intentius/terragucci 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -161,9 +161,16 @@
161
161
  "minLength": 1
162
162
  }
163
163
  }
164
+ },
165
+ "compose": {
166
+ "type": "boolean"
167
+ },
168
+ "compose_max": {
169
+ "type": "integer",
170
+ "minimum": 2
164
171
  }
165
172
  },
166
- "description": "The canary wave, the jobs one wave spreads across, and extra apply order."
173
+ "description": "The canary wave, the jobs one wave spreads across, extra apply order, and whether a change's roots apply as one composed run."
167
174
  },
168
175
  "drift": {
169
176
  "anyOf": [
@@ -731,7 +738,29 @@
731
738
  "minLength": 1
732
739
  },
733
740
  "audience": {
734
- "type": "string"
741
+ "anyOf": [
742
+ {
743
+ "type": "string"
744
+ },
745
+ {
746
+ "type": "object",
747
+ "properties": {
748
+ "plan": {
749
+ "type": "string",
750
+ "minLength": 1
751
+ },
752
+ "apply": {
753
+ "type": "string",
754
+ "minLength": 1
755
+ },
756
+ "drift": {
757
+ "type": "string",
758
+ "minLength": 1
759
+ }
760
+ },
761
+ "additionalProperties": false
762
+ }
763
+ ]
735
764
  },
736
765
  "roles": {
737
766
  "type": "object",
@@ -1386,6 +1415,216 @@
1386
1415
  ],
1387
1416
  "description": "Roots each pull request gets a copy of."
1388
1417
  },
1418
+ "environments": {
1419
+ "type": "object",
1420
+ "minProperties": 1,
1421
+ "propertyNames": {
1422
+ "type": "string",
1423
+ "pattern": "^[a-z0-9_-]+$",
1424
+ "not": {
1425
+ "anyOf": [
1426
+ {
1427
+ "enum": [
1428
+ "default",
1429
+ "decide"
1430
+ ]
1431
+ },
1432
+ {
1433
+ "pattern": "^share-[0-9]+$"
1434
+ }
1435
+ ]
1436
+ }
1437
+ },
1438
+ "additionalProperties": {
1439
+ "type": "object",
1440
+ "properties": {
1441
+ "roots": {
1442
+ "type": "array",
1443
+ "minItems": 1,
1444
+ "items": {
1445
+ "type": "string",
1446
+ "pattern": "^[^\\s,;=']+$"
1447
+ }
1448
+ },
1449
+ "gate": {
1450
+ "enum": [
1451
+ "always",
1452
+ "on-destructive",
1453
+ "never",
1454
+ "on-destroy"
1455
+ ]
1456
+ },
1457
+ "oidc": {
1458
+ "type": "object",
1459
+ "properties": {
1460
+ "plan_role": {
1461
+ "type": "string",
1462
+ "minLength": 1
1463
+ },
1464
+ "apply_role": {
1465
+ "type": "string",
1466
+ "minLength": 1
1467
+ },
1468
+ "audience": {
1469
+ "type": "string",
1470
+ "minLength": 1,
1471
+ "pattern": "\\S"
1472
+ },
1473
+ "gcp": {
1474
+ "type": "object",
1475
+ "properties": {
1476
+ "plan_service_account": {
1477
+ "type": "string",
1478
+ "pattern": "^[^@\\s]+@[^@\\s]+$"
1479
+ },
1480
+ "apply_service_account": {
1481
+ "type": "string",
1482
+ "pattern": "^[^@\\s]+@[^@\\s]+$"
1483
+ }
1484
+ },
1485
+ "additionalProperties": false,
1486
+ "required": [
1487
+ "plan_service_account",
1488
+ "apply_service_account"
1489
+ ]
1490
+ },
1491
+ "azure": {
1492
+ "type": "object",
1493
+ "properties": {
1494
+ "plan_client_id": {
1495
+ "type": "string",
1496
+ "minLength": 1
1497
+ },
1498
+ "apply_client_id": {
1499
+ "type": "string",
1500
+ "minLength": 1
1501
+ }
1502
+ },
1503
+ "additionalProperties": false,
1504
+ "required": [
1505
+ "plan_client_id",
1506
+ "apply_client_id"
1507
+ ]
1508
+ }
1509
+ },
1510
+ "additionalProperties": false,
1511
+ "minProperties": 1,
1512
+ "dependentRequired": {
1513
+ "plan_role": [
1514
+ "apply_role"
1515
+ ],
1516
+ "apply_role": [
1517
+ "plan_role"
1518
+ ]
1519
+ }
1520
+ },
1521
+ "runner": {
1522
+ "anyOf": [
1523
+ {
1524
+ "anyOf": [
1525
+ {
1526
+ "type": "string",
1527
+ "pattern": "^[^\\s,]+$"
1528
+ },
1529
+ {
1530
+ "type": "array",
1531
+ "minItems": 1,
1532
+ "uniqueItems": true,
1533
+ "items": {
1534
+ "type": "string",
1535
+ "pattern": "^[^\\s,]+$"
1536
+ }
1537
+ },
1538
+ {
1539
+ "type": "object",
1540
+ "properties": {
1541
+ "group": {
1542
+ "type": "string",
1543
+ "pattern": "\\S"
1544
+ },
1545
+ "labels": {
1546
+ "type": "array",
1547
+ "minItems": 1,
1548
+ "uniqueItems": true,
1549
+ "items": {
1550
+ "type": "string",
1551
+ "pattern": "^[^\\s,]+$"
1552
+ }
1553
+ }
1554
+ },
1555
+ "additionalProperties": false,
1556
+ "required": [
1557
+ "group"
1558
+ ]
1559
+ }
1560
+ ]
1561
+ },
1562
+ {
1563
+ "type": "object",
1564
+ "properties": {
1565
+ "apply": {
1566
+ "anyOf": [
1567
+ {
1568
+ "type": "string",
1569
+ "pattern": "^[^\\s,]+$"
1570
+ },
1571
+ {
1572
+ "type": "array",
1573
+ "minItems": 1,
1574
+ "uniqueItems": true,
1575
+ "items": {
1576
+ "type": "string",
1577
+ "pattern": "^[^\\s,]+$"
1578
+ }
1579
+ },
1580
+ {
1581
+ "type": "object",
1582
+ "properties": {
1583
+ "group": {
1584
+ "type": "string",
1585
+ "pattern": "\\S"
1586
+ },
1587
+ "labels": {
1588
+ "type": "array",
1589
+ "minItems": 1,
1590
+ "uniqueItems": true,
1591
+ "items": {
1592
+ "type": "string",
1593
+ "pattern": "^[^\\s,]+$"
1594
+ }
1595
+ }
1596
+ },
1597
+ "additionalProperties": false,
1598
+ "required": [
1599
+ "group"
1600
+ ]
1601
+ }
1602
+ ]
1603
+ }
1604
+ },
1605
+ "additionalProperties": false,
1606
+ "required": [
1607
+ "apply"
1608
+ ]
1609
+ }
1610
+ ]
1611
+ },
1612
+ "parallelism": {
1613
+ "type": "integer",
1614
+ "minimum": 1
1615
+ },
1616
+ "forge_environment": {
1617
+ "type": "string",
1618
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_./-]*$"
1619
+ }
1620
+ },
1621
+ "additionalProperties": false,
1622
+ "required": [
1623
+ "roots"
1624
+ ]
1625
+ },
1626
+ "description": "Named environments: each one's root globs, and its gate, identities, apply runner, parallelism and forge environment."
1627
+ },
1389
1628
  "defaults": {
1390
1629
  "$ref": "#/$defs/project",
1391
1630
  "not": {
@@ -1578,9 +1817,16 @@
1578
1817
  "minLength": 1
1579
1818
  }
1580
1819
  }
1820
+ },
1821
+ "compose": {
1822
+ "type": "boolean"
1823
+ },
1824
+ "compose_max": {
1825
+ "type": "integer",
1826
+ "minimum": 2
1581
1827
  }
1582
1828
  },
1583
- "description": "The canary wave, the jobs one wave spreads across, and extra apply order."
1829
+ "description": "The canary wave, the jobs one wave spreads across, extra apply order, and whether a change's roots apply as one composed run."
1584
1830
  },
1585
1831
  "drift": {
1586
1832
  "anyOf": [
@@ -2140,7 +2386,29 @@
2140
2386
  "minLength": 1
2141
2387
  },
2142
2388
  "audience": {
2143
- "type": "string"
2389
+ "anyOf": [
2390
+ {
2391
+ "type": "string"
2392
+ },
2393
+ {
2394
+ "type": "object",
2395
+ "properties": {
2396
+ "plan": {
2397
+ "type": "string",
2398
+ "minLength": 1
2399
+ },
2400
+ "apply": {
2401
+ "type": "string",
2402
+ "minLength": 1
2403
+ },
2404
+ "drift": {
2405
+ "type": "string",
2406
+ "minLength": 1
2407
+ }
2408
+ },
2409
+ "additionalProperties": false
2410
+ }
2411
+ ]
2144
2412
  },
2145
2413
  "roles": {
2146
2414
  "type": "object",
@@ -2794,6 +3062,213 @@
2794
3062
  "roots"
2795
3063
  ],
2796
3064
  "description": "Roots each pull request gets a copy of."
3065
+ },
3066
+ "environments": {
3067
+ "type": "object",
3068
+ "minProperties": 1,
3069
+ "propertyNames": {
3070
+ "type": "string",
3071
+ "pattern": "^[a-z0-9_-]+$",
3072
+ "not": {
3073
+ "anyOf": [
3074
+ {
3075
+ "enum": [
3076
+ "default",
3077
+ "decide"
3078
+ ]
3079
+ },
3080
+ {
3081
+ "pattern": "^share-[0-9]+$"
3082
+ }
3083
+ ]
3084
+ }
3085
+ },
3086
+ "additionalProperties": {
3087
+ "type": "object",
3088
+ "properties": {
3089
+ "roots": {
3090
+ "type": "array",
3091
+ "minItems": 1,
3092
+ "items": {
3093
+ "type": "string",
3094
+ "pattern": "^[^\\s,;=']+$"
3095
+ }
3096
+ },
3097
+ "gate": {
3098
+ "enum": [
3099
+ "always",
3100
+ "on-destructive",
3101
+ "never",
3102
+ "on-destroy"
3103
+ ]
3104
+ },
3105
+ "oidc": {
3106
+ "type": "object",
3107
+ "properties": {
3108
+ "plan_role": {
3109
+ "type": "string",
3110
+ "minLength": 1
3111
+ },
3112
+ "apply_role": {
3113
+ "type": "string",
3114
+ "minLength": 1
3115
+ },
3116
+ "audience": {
3117
+ "type": "string",
3118
+ "minLength": 1,
3119
+ "pattern": "\\S"
3120
+ },
3121
+ "gcp": {
3122
+ "type": "object",
3123
+ "properties": {
3124
+ "plan_service_account": {
3125
+ "type": "string",
3126
+ "pattern": "^[^@\\s]+@[^@\\s]+$"
3127
+ },
3128
+ "apply_service_account": {
3129
+ "type": "string",
3130
+ "pattern": "^[^@\\s]+@[^@\\s]+$"
3131
+ }
3132
+ },
3133
+ "additionalProperties": false,
3134
+ "required": [
3135
+ "plan_service_account",
3136
+ "apply_service_account"
3137
+ ]
3138
+ },
3139
+ "azure": {
3140
+ "type": "object",
3141
+ "properties": {
3142
+ "plan_client_id": {
3143
+ "type": "string",
3144
+ "minLength": 1
3145
+ },
3146
+ "apply_client_id": {
3147
+ "type": "string",
3148
+ "minLength": 1
3149
+ }
3150
+ },
3151
+ "additionalProperties": false,
3152
+ "required": [
3153
+ "plan_client_id",
3154
+ "apply_client_id"
3155
+ ]
3156
+ }
3157
+ },
3158
+ "additionalProperties": false,
3159
+ "minProperties": 1,
3160
+ "dependentRequired": {
3161
+ "plan_role": [
3162
+ "apply_role"
3163
+ ],
3164
+ "apply_role": [
3165
+ "plan_role"
3166
+ ]
3167
+ }
3168
+ },
3169
+ "runner": {
3170
+ "anyOf": [
3171
+ {
3172
+ "anyOf": [
3173
+ {
3174
+ "type": "string",
3175
+ "pattern": "^[^\\s,]+$"
3176
+ },
3177
+ {
3178
+ "type": "array",
3179
+ "minItems": 1,
3180
+ "uniqueItems": true,
3181
+ "items": {
3182
+ "type": "string",
3183
+ "pattern": "^[^\\s,]+$"
3184
+ }
3185
+ },
3186
+ {
3187
+ "type": "object",
3188
+ "properties": {
3189
+ "group": {
3190
+ "type": "string",
3191
+ "pattern": "\\S"
3192
+ },
3193
+ "labels": {
3194
+ "type": "array",
3195
+ "minItems": 1,
3196
+ "uniqueItems": true,
3197
+ "items": {
3198
+ "type": "string",
3199
+ "pattern": "^[^\\s,]+$"
3200
+ }
3201
+ }
3202
+ },
3203
+ "additionalProperties": false,
3204
+ "required": [
3205
+ "group"
3206
+ ]
3207
+ }
3208
+ ]
3209
+ },
3210
+ {
3211
+ "type": "object",
3212
+ "properties": {
3213
+ "apply": {
3214
+ "anyOf": [
3215
+ {
3216
+ "type": "string",
3217
+ "pattern": "^[^\\s,]+$"
3218
+ },
3219
+ {
3220
+ "type": "array",
3221
+ "minItems": 1,
3222
+ "uniqueItems": true,
3223
+ "items": {
3224
+ "type": "string",
3225
+ "pattern": "^[^\\s,]+$"
3226
+ }
3227
+ },
3228
+ {
3229
+ "type": "object",
3230
+ "properties": {
3231
+ "group": {
3232
+ "type": "string",
3233
+ "pattern": "\\S"
3234
+ },
3235
+ "labels": {
3236
+ "type": "array",
3237
+ "minItems": 1,
3238
+ "uniqueItems": true,
3239
+ "items": {
3240
+ "type": "string",
3241
+ "pattern": "^[^\\s,]+$"
3242
+ }
3243
+ }
3244
+ },
3245
+ "additionalProperties": false,
3246
+ "required": [
3247
+ "group"
3248
+ ]
3249
+ }
3250
+ ]
3251
+ }
3252
+ },
3253
+ "additionalProperties": false,
3254
+ "required": [
3255
+ "apply"
3256
+ ]
3257
+ }
3258
+ ]
3259
+ },
3260
+ "parallelism": {
3261
+ "type": "integer",
3262
+ "minimum": 1
3263
+ },
3264
+ "forge_environment": {
3265
+ "type": "string",
3266
+ "pattern": "^[A-Za-z0-9_][A-Za-z0-9_./-]*$"
3267
+ }
3268
+ },
3269
+ "additionalProperties": false
3270
+ },
3271
+ "description": "Named environments: each one's root globs, and its gate, identities, apply runner, parallelism and forge environment."
2797
3272
  }
2798
3273
  },
2799
3274
  "additionalProperties": false
package/dist/types.d.ts CHANGED
@@ -182,8 +182,12 @@ export interface OidcSettings {
182
182
  * does this.
183
183
  */
184
184
  roles?: Record<string, RolePair>;
185
- /** The AWS token's audience. Default `sts.amazonaws.com`. */
186
- audience?: string;
185
+ /**
186
+ * The AWS token's audience. Default `sts.amazonaws.com`. A map gives each
187
+ * stage its own (`plan`, `apply`, `drift`); a stage it leaves out keeps the
188
+ * default.
189
+ */
190
+ audience?: string | Partial<Record<AudienceStage, string>>;
187
191
  /** GCP Workload Identity Federation: the provider's resource name and a service account per stage. */
188
192
  gcp?: {
189
193
  workload_identity_provider: string;
@@ -381,11 +385,15 @@ export interface ProjectSettings {
381
385
  /**
382
386
  * `canary`: globs for the wave that applies first. `jobs`: the most jobs one wave's roots or units spread across (GitHub and Forgejo).
383
387
  * `after`: plain roots only, for a root or glob the roots or globs it applies after, beside the order its `terraform_remote_state` reads give.
388
+ * `compose`: plain roots only, opt-in beside the waves: the roots a change reaches plan as one composed run and apply as one (./compose.ts). Default false.
389
+ * `compose_max`: the most roots one composed run covers; a change that reaches more applies in waves. Default 10.
384
390
  */
385
391
  waves?: {
386
392
  canary?: string[];
387
393
  jobs?: number;
388
394
  after?: Record<string, string[]>;
395
+ compose?: boolean;
396
+ compose_max?: number;
389
397
  };
390
398
  /** A cron schedule for tf-drift, or false. */
391
399
  drift?: string | false;
@@ -545,6 +553,54 @@ export interface ProjectSettings {
545
553
  own_jobs?: string | Record<string, Record<string, unknown>>;
546
554
  /** Roots each pull request gets a copy of, under a state key of its own, destroyed on close or once its TTL passes; see EphemeralSettings. */
547
555
  ephemeral?: EphemeralSettings;
556
+ /**
557
+ * Named environments: each names its roots by glob and sets the keys that
558
+ * vary for them (./environments.ts). A root in no environment takes the
559
+ * top-level values. Read at base, like `gate`. A control repo's `defaults`
560
+ * and project blocks merge them per environment name.
561
+ */
562
+ environments?: Record<string, EnvironmentSettings>;
563
+ }
564
+ /** An environment's name: lower-case letters, digits, `-` and `_`. */
565
+ export declare const ENVIRONMENT_NAME: RegExp;
566
+ /** The keys an environment takes. */
567
+ export declare const ENVIRONMENT_KEYS: readonly ["roots", "gate", "oidc", "runner", "parallelism", "forge_environment"];
568
+ /** The keys of an environment's `oidc`. */
569
+ export declare const ENVIRONMENT_OIDC_KEYS: readonly ["plan_role", "apply_role", "audience", "gcp", "azure"];
570
+ /**
571
+ * One environment of `environments:`. `roots` are root globs; a root matches
572
+ * at most one environment. Each other key, when set, replaces the top-level
573
+ * key's value for the environment's roots.
574
+ */
575
+ export interface EnvironmentSettings {
576
+ roots: string[];
577
+ /** The gate of a wave that holds the environment's roots. A wave of roots in several environments takes the strictest of their gates. */
578
+ gate?: GateName;
579
+ /** The environment's identities: its roots plan and apply with them, as `oidc.roles` gives a root glob a pair. */
580
+ oidc?: EnvironmentOidc;
581
+ /** The runner the environment's apply jobs run on: a runner, or `{ apply: <runner> }`. A plan job plans every environment's roots, so it keeps the top-level runner. */
582
+ runner?: RunnerSpec | {
583
+ apply?: RunnerSpec;
584
+ };
585
+ /** How many of the environment's roots of one dependency layer plan at once. */
586
+ parallelism?: number;
587
+ /** The forge environment its apply jobs run in, on GitHub and GitLab. Default: the environment's name. Forgejo has none. */
588
+ forge_environment?: string;
589
+ }
590
+ /** An environment's identities: an AWS role pair, and a GCP service account pair or an Azure client pair under the project's `oidc.gcp` or `oidc.azure`. */
591
+ export interface EnvironmentOidc {
592
+ plan_role?: string;
593
+ apply_role?: string;
594
+ /** The AWS token audience of the environment's apply jobs, over `oidc.audience`. */
595
+ audience?: string;
596
+ gcp?: {
597
+ plan_service_account: string;
598
+ apply_service_account: string;
599
+ };
600
+ azure?: {
601
+ plan_client_id: string;
602
+ apply_client_id: string;
603
+ };
548
604
  }
549
605
  /**
550
606
  * Ephemeral environments: each open pull request gets its own copy of the
@@ -632,7 +688,7 @@ export declare const BUILT_IN: ResolvedSettings;
632
688
  * own clone URL and `rollouts` belongs to a single repo, so `defaults`
633
689
  * refuses both.
634
690
  */
635
- export declare const PROJECT_FILE_KEYS: readonly ["policy", "sources", "reports", "approval", "gate", "roots", "waves", "parallelism", "synth", "steps", "drift", "cost", "tips", "runtime", "telemetry", "respond", "review", "decide", "audit_region", "modules", "terragrunt", "token_env", "generate", "ephemeral"];
691
+ export declare const PROJECT_FILE_KEYS: readonly ["policy", "sources", "reports", "approval", "gate", "roots", "waves", "parallelism", "synth", "steps", "drift", "cost", "tips", "runtime", "telemetry", "respond", "review", "decide", "audit_region", "modules", "terragrunt", "token_env", "generate", "ephemeral", "environments"];
636
692
  export declare class ConfigError extends Error {
637
693
  /** Every problem found, when the error is a validation failure. */
638
694
  readonly problems?: string[];
@@ -653,6 +709,9 @@ export declare const SOURCES_KEYS: string[];
653
709
  /** One `sources` entry: a glob with no whitespace, commas, semicolons or quotes. */
654
710
  export declare const SOURCE_GLOB = "^[^\\s,;'\"]+$";
655
711
  export declare const OIDC_KEYS: string[];
712
+ /** The stages `oidc.audience` takes as a map: the jobs that plan a change, the jobs that apply (confirm among them), and drift. */
713
+ export declare const AUDIENCE_STAGES: readonly ["plan", "apply", "drift"];
714
+ export type AudienceStage = (typeof AUDIENCE_STAGES)[number];
656
715
  export declare const TELEMETRY_KEYS: string[];
657
716
  export declare const TOKEN_PROTECTION_KEYS: string[];
658
717
  export declare const COST_KEYS: string[];
@@ -723,6 +782,10 @@ export interface RegistrySettings {
723
782
  download?: "tarball" | "git-tags" | "oci";
724
783
  }
725
784
  /** Why a control repo's projects take no `rollouts` job: each project's pipeline sees only its own roots. */
785
+ /** Why `waves.compose` is refused with `binary: choudoufu`: one run would have to carry several estates (INTENTIUS/choudoufu#2059). */
786
+ export declare const COMPOSE_NOT_CHOUDOUFU = "a composed run splits one working state back into each root's state, and choudoufu keeps no state to split, so leave waves.compose unset with binary: choudoufu";
787
+ /** Why `waves.compose` is refused in a Terragrunt repo. */
788
+ export declare const COMPOSE_NOT_TERRAGRUNT = "a Terragrunt repo's units apply through terragrunt, one run per unit, so they cannot compose; leave waves.compose unset";
726
789
  export declare const WAVE_JOBS_NOT_PR_APPLY = "apply.when: pull-request applies every wave in the one job a comment starts, so a wave has no jobs to spread across; leave waves.jobs unset";
727
790
  /** Why a control repo's projects take no `rollouts` job: each project's pipeline sees only its own roots. */
728
791
  export declare const ROLLOUTS_SINGLE_REPO = "a control repo's rollout plans its waves across every project, and a project's pipeline sees only its own roots; leave rollouts unset and run terragucci respond rollout --mode apply on a schedule in the control repo";
@@ -795,6 +858,32 @@ export declare const ATMOS_KEYS: string[];
795
858
  export declare const ATMOS_NOT_TERRAGRUNT = "terragucci runs an Atmos repo or a Terragrunt repo, not both; keep the block of the one this repo is";
796
859
  /** The GCP Workload Identity Federation provider's resource name. */
797
860
  export declare const WIF_PROVIDER: RegExp;
861
+ /** The apply group of the roots in no environment, or that no glob gives an identity (./groups.ts). */
862
+ export declare const DEFAULT_GROUP = "default";
863
+ /** Names an apply group cannot take: the rest group's, and chant's own job names (decide, share-<i>). */
864
+ export declare const reservedGroupName: (name: string) => boolean;
865
+ /** Why an environment's `runner.plan` is refused: a plan job plans the roots of every environment. */
866
+ export declare const ENVIRONMENT_PLAN_RUNNER = "a plan job plans the roots of every environment, so an environment's runner is its apply jobs' alone; set runner: { apply: <runner> }, and runner.plan at the top level";
867
+ /** A forge environment's name, as GitHub and GitLab both take it. */
868
+ export declare const FORGE_ENVIRONMENT_NAME: RegExp;
869
+ /** Why an environment cannot take a name the apply jobs use for their own. */
870
+ export declare const ENVIRONMENT_RESERVED = "names the apply jobs use for their own (default holds the roots in no environment; decide and share-<n> are a wave's own jobs)";
871
+ /** Why `environments` refuses `apply.when: pull-request`: a comment's one job applies every environment's roots. */
872
+ export declare const ENVIRONMENTS_PR_APPLY = "with environments each environment's roots apply in jobs of their own, with their own identities, and apply.when: pull-request applies every wave in the one job a comment starts; leave apply.when at merge";
873
+ /** Why `environments` refuses `apply.resume` on GitHub and Forgejo: the resume job applies every wave in one job. */
874
+ export declare const ENVIRONMENTS_RESUME = "with environments each environment's roots apply in jobs of their own, and on GitHub and Forgejo the resume job applies every wave in one job; leave apply.resume unset and re-run the waiting wave's job once it is approved";
875
+ /** Why `environments` refuses `ephemeral`: a pull request's copies apply in one job with the apply identities. */
876
+ export declare const ENVIRONMENTS_EPHEMERAL = "with environments each environment's roots apply in jobs of their own, and an ephemeral copy applies in the pull request's one job; leave ephemeral unset";
877
+ /** Why an environment's `forge_environment` is refused on Forgejo. */
878
+ export declare const FORGE_ENVIRONMENT_NOT_FORGEJO = "Forgejo Actions has no deployment environments, so a job cannot run in one; leave forge_environment out of each environment";
879
+ /**
880
+ * What a whole set of environments needs from the settings around it: a
881
+ * root glob in each, and the project's `oidc.gcp` or `oidc.azure` for an
882
+ * environment that names a GCP or Azure pair, since the provider, the tenant
883
+ * and the token come from there. A repo's own file is checked whole in
884
+ * checkSettings; a control repo's project once its defaults are merged in.
885
+ */
886
+ export declare function environmentNeeds(s: ProjectSettings, where: string): string[];
798
887
  /** Check a parsed config and return it typed, or throw with every problem listed. */
799
888
  export declare function validateConfig(raw: unknown, where: string): TerragucciConfig;
800
889
  export type ConfigMode = "fold" | "run" | "check";
@@ -818,3 +907,5 @@ export declare function forgeFromHost(host: string): ForgeName | undefined;
818
907
  export declare function resolveRepo(config: TerragucciConfig): ResolvedSettings;
819
908
  /** A control repo's project: built-in defaults, then `defaults`, then the project's own keys. */
820
909
  export declare function resolveProject(config: TerragucciConfig, key: string): ResolvedSettings;
910
+ /** `environments` merged per environment name: an environment both name takes `over`'s keys over `base`'s. */
911
+ export declare function mergeEnvironments(base: Record<string, EnvironmentSettings> | undefined, over: Record<string, EnvironmentSettings> | undefined): Record<string, EnvironmentSettings> | undefined;