@intentius/terragucci 0.3.1 → 0.4.1

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.
package/dist/types.d.ts CHANGED
@@ -1,24 +1,62 @@
1
1
  export declare const BINARIES: readonly ["terraform", "tofu", "choudoufu"];
2
2
  export declare const FORGES: readonly ["github", "gitlab", "forgejo"];
3
3
  export declare const GATES: readonly ["always", "on-destroy", "never"];
4
+ /**
5
+ * What counts as the approval of a waiting wave (`approval:`). `ledger` (the
6
+ * default): any `chant approve` of the wave's set digest on chant/lifecycle,
7
+ * signed or not. `pr-review`: also the merged pull request's approving review
8
+ * of its head (on GitLab, an approval after its latest push), when the plans
9
+ * have not moved since (./review.ts). `sealed`: only one sealed with a key the signers file at base
10
+ * lists for its approver. Every mode binds the digest.
11
+ */
12
+ export declare const APPROVALS: readonly ["ledger", "pr-review", "sealed"];
4
13
  /** Every stage runs on the forge's CI. */
5
14
  export declare const RUNTIMES: readonly ["forge"];
6
15
  export declare const DEPENDENTS: readonly ["follow", "plan"];
7
16
  export declare const POLICY_ENGINES: readonly ["conftest", "opa"];
8
17
  export declare const POLICY_INPUTS: readonly ["plan", "hcp"];
18
+ /**
19
+ * When a pull request takes its root locks (`locks:`). `apply` (the default):
20
+ * when it applies before merge, or a writer comments `/terragucci lock`.
21
+ * `plan`: from its first plan, through the `pr-lock` job (GitHub and Forgejo;
22
+ * NO_GITLAB_PLAN_LOCKS), until it merges or closes, or a writer comments
23
+ * `/terragucci unlock`.
24
+ */
25
+ export declare const LOCKS: readonly ["apply", "plan"];
9
26
  /** When a change applies: after it merges (default), or from its open pull request before it merges. */
10
27
  export declare const APPLY_WHEN: readonly ["merge", "pull-request"];
11
28
  /** With `apply.when: pull-request`, who merges once every wave applied: a person (default), or terragucci. */
12
29
  export declare const APPLY_MERGE: readonly ["manual", "auto"];
30
+ /**
31
+ * With `apply.when: pull-request`, what an open pull request needs before a
32
+ * comment applies it: a reviewer's approval of its head (`approved`), a
33
+ * forge that says it can merge (`mergeable`: no conflicts, and on GitHub no
34
+ * branch protection blocking it), a head that contains the default branch
35
+ * (`undiverged`), and every status and check on the head passed (`checks`).
36
+ * All four by default.
37
+ */
38
+ export declare const APPLY_REQUIRES: readonly ["approved", "mergeable", "undiverged", "checks"];
13
39
  export type Binary = (typeof BINARIES)[number];
14
40
  export type ForgeName = (typeof FORGES)[number];
15
41
  export type Gate = (typeof GATES)[number];
42
+ export type Approval = (typeof APPROVALS)[number];
43
+ /**
44
+ * `gitlab.token`: how a GitLab project keeps its forge token. `unprotected`
45
+ * (the default): the plan job posts its note with the token at once, so a
46
+ * merge request's code, which can rewrite the job, can use the token too.
47
+ * `protected`: the token is a protected variable that no merge request or
48
+ * branch pipeline sees, and the comments schedule's job posts the plan notes.
49
+ */
50
+ export declare const TOKEN_PROTECTIONS: readonly ["unprotected", "protected"];
51
+ export type GitLabToken = (typeof TOKEN_PROTECTIONS)[number];
16
52
  export type Runtime = (typeof RUNTIMES)[number];
17
53
  export type Dependents = (typeof DEPENDENTS)[number];
18
54
  export type PolicyEngine = (typeof POLICY_ENGINES)[number];
19
55
  export type PolicyInput = (typeof POLICY_INPUTS)[number];
20
56
  export type ApplyWhen = (typeof APPLY_WHEN)[number];
57
+ export type Locks = (typeof LOCKS)[number];
21
58
  export type ApplyMerge = (typeof APPLY_MERGE)[number];
59
+ export type ApplyRequire = (typeof APPLY_REQUIRES)[number];
22
60
  /**
23
61
  * `apply:`: when a change applies. `when: merge` (the default) applies the
24
62
  * default branch after a merge. `when: pull-request` applies an open pull
@@ -27,12 +65,19 @@ export type ApplyMerge = (typeof APPLY_MERGE)[number];
27
65
  * `merge: auto` merges the pull request once every wave applied, in a job of
28
66
  * its own, with the token in the secret `merge_token_env` names when it is
29
67
  * set (required on Forgejo, whose job token cannot push to the default
30
- * branch). Plain roots on GitHub and Forgejo only (NO_GITLAB_PR_APPLY).
68
+ * branch). `requires` lists what an open pull request needs before it
69
+ * applies (APPLY_REQUIRES, all by default). On every forge, for plain
70
+ * roots and Terragrunt units alike. On GitLab a merge request note starts no
71
+ * pipeline, so `when: pull-request` needs `comments:` (the schedule whose
72
+ * job reads `/terragucci apply`) and `merge_token_env`, a variable whose
73
+ * token may run pipelines on the default branch and merge there, with
74
+ * `merge: manual` too (PR_APPLY_NEEDS_ON_GITLAB).
31
75
  */
32
76
  export interface ApplySettings {
33
77
  when?: ApplyWhen;
34
78
  merge?: ApplyMerge;
35
79
  merge_token_env?: string;
80
+ requires?: ApplyRequire[];
36
81
  }
37
82
  /**
38
83
  * Policy as code, off unless set. `tf-plan` runs the engine over each planned
@@ -42,12 +87,24 @@ export interface ApplySettings {
42
87
  export interface PolicySettings {
43
88
  /** The engine. Default `conftest`, which terragucci installs on demand when it is not on the path. */
44
89
  engine?: PolicyEngine;
45
- /** The directory of Rego policy, relative to the repo root. Default `policy`. */
90
+ /** The directory of Rego policy, relative to the repo root, or to the root of `source` when that is set. Default `policy`. */
46
91
  path?: string;
92
+ /**
93
+ * A shared policy repo, `git+https://<host>/<path>@<ref>`, read at that ref
94
+ * instead of the repo's own directory. The ref is a tag, a branch or a
95
+ * commit. Like the rest of the key, it is read at the base.
96
+ */
97
+ source?: string;
47
98
  /** The Rego package whose `deny`, `violation`, `deny_*` and `warn` rules count. conftest default: every namespace. opa default: `main`, or every package under `terraform.policies` with `input: hcp`. */
48
99
  namespace?: string;
49
100
  /** What `input` holds: `plan`, the bare plan JSON (default); `hcp`, `{plan, run}` as HCP Terraform's OPA policies read it. */
50
101
  input?: PolicyInput;
102
+ /**
103
+ * Who may override a denial: the forge identities or signers whose recorded
104
+ * override (`terragucci override`) lets `tf-apply` apply one denied plan.
105
+ * Read at base, like `approval:`. Unset, no override counts.
106
+ */
107
+ override?: string[];
51
108
  }
52
109
  /**
53
110
  * The jobs' cloud identities over the forge's OIDC token. `plan_role` and
@@ -193,16 +250,31 @@ export interface ProjectSettings {
193
250
  url?: string;
194
251
  /** When a wave waits for an approval. */
195
252
  gate?: Gate;
253
+ /** What counts as a waiting wave's approval; see APPROVALS. Read from the config at base, never the applied commit's own. */
254
+ approval?: Approval;
196
255
  /** When a change applies; see ApplySettings. */
197
256
  apply?: ApplySettings;
257
+ /** When a pull request takes its root locks; see LOCKS. */
258
+ locks?: Locks;
198
259
  waves?: {
199
260
  canary?: string[];
200
261
  };
201
262
  /** A cron schedule for tf-drift, or false. */
202
263
  drift?: string | false;
264
+ /**
265
+ * GitLab only: the cron of the comments schedule, or false. The pipeline
266
+ * gets a `comments` job that reads new merge request notes on that
267
+ * schedule (comment-gitlab.ts), since GitLab starts no pipeline for a note.
268
+ */
269
+ comments?: string | false;
270
+ /** GitLab only: how the project keeps its forge token; see TOKEN_PROTECTIONS. */
271
+ gitlab?: {
272
+ token?: GitLabToken;
273
+ };
203
274
  runtime?: Runtime;
204
275
  /**
205
- * A bucket for plan reports. `url` is the address that serves the bucket's
276
+ * A bucket for plan reports: `s3://<bucket>`, `gs://<bucket>` or
277
+ * `az://<account>/<container>`. `url` is the address that serves the bucket's
206
278
  * objects to a browser (a static site, a CDN, the store's public endpoint);
207
279
  * with it, the note, the index and the dashboards link the bucket's copy.
208
280
  * `role` is an AWS role the job assumes with its OIDC token to write the
@@ -289,14 +361,43 @@ export declare function checkMode(mode: string): "dry-run" | "apply";
289
361
  export declare const CONFIG_NAMES: string[];
290
362
  /** The config file in `dir`, or undefined. Two of them is an error. */
291
363
  export declare function findConfig(dir: string): string | undefined;
364
+ /** Why `comments` is GitLab's alone: the other forges start a job for each comment. */
365
+ export declare const COMMENTS_GITLAB_ONLY = "comments is for GitLab, which starts no pipeline for a merge request note; GitHub and Forgejo start the comment jobs from the comment itself, so leave comments unset";
366
+ /**
367
+ * What GitLab's apply before merge needs. A merge request's own pipeline
368
+ * runs its own `.gitlab-ci.yml`, so the apply runs in a pipeline of the
369
+ * default branch, which the comments job starts on a `/terragucci apply`
370
+ * note: so `comments:` must be set, and `apply.merge_token_env` must name the
371
+ * variable whose token may start a pipeline on the protected default branch
372
+ * (and, with `merge: auto`, merge there).
373
+ */
374
+ export declare const PR_APPLY_NEEDS_ON_GITLAB: {
375
+ comments: string;
376
+ token: string;
377
+ };
378
+ /**
379
+ * Why `gitlab.token: protected` needs `comments:`: no merge request pipeline
380
+ * then holds a token that may post the plan note, so the comments schedule's
381
+ * job posts it.
382
+ */
383
+ export declare const PROTECTED_TOKEN_NEEDS_COMMENTS = "protected needs comments: <cron>: a merge request's pipeline then holds no token that may post the plan note, so the comments schedule's job posts it";
384
+ /** The problems with a GitLab project's `apply.when: pull-request` and `gitlab.token: protected`, when it has any. */
385
+ export declare function gitlabPrApplyProblems(s: Record<string, unknown>, where: string): string[];
386
+ /** Why GitLab has no plan-time locks: no merge request event runs a pipeline from the default branch. */
387
+ export declare const NO_GITLAB_PLAN_LOCKS = "plan is not supported on GitLab, where no merge request event runs a job from the default branch that could hold the lock; leave locks unset, and with apply.when: pull-request a merge request locks its roots on `/terragucci apply` or `/terragucci lock`";
388
+ /** Where a shared policy is fetched from: the git URL and the ref. */
389
+ export interface PolicySource {
390
+ url: string;
391
+ ref: string;
392
+ }
292
393
  /**
293
- * Why GitLab has no apply before merge. GitLab builds a merge request's
294
- * pipeline from the merge request's own `.gitlab-ci.yml`, so a job that
295
- * applies it would run the checks before the apply (approval, locks, the
296
- * pipeline file) inside a pipeline the merge request controls, and the apply
297
- * role would have to trust every branch of the project.
394
+ * Split `git+<scheme>://<host>/<path>@<ref>` into the URL git fetches and
395
+ * the ref. The ref is what follows the last `@` after the host, so a user
396
+ * name in the URL (`git+https://ci@host/...`) stays in the URL. https and
397
+ * http (a forge on a private network) and file (a repo on the job's disk).
398
+ * Undefined when it is not that shape.
298
399
  */
299
- export declare const NO_GITLAB_PR_APPLY = "pull-request is not supported on GitLab, where a merge request's pipeline is defined by the merge request itself, so nothing it runs can be trusted with the apply role; leave apply.when unset, and the change applies after it merges";
400
+ export declare function parsePolicySource(source: string): PolicySource | undefined;
300
401
  /** Check a parsed config and return it typed, or throw with every problem listed. */
301
402
  export declare function validateConfig(raw: unknown, where: string): TerragucciConfig;
302
403
  export type ConfigMode = "fold" | "run" | "check";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/terragucci",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "The whole Terraform lifecycle, handled: one config file, generated pipelines for GitHub, GitLab and Forgejo.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -45,11 +45,11 @@
45
45
  },
46
46
  "devDependencies": {
47
47
  "@cdktn/hcl2json": "0.24.0",
48
- "@intentius/chant": "0.107.1",
49
- "@intentius/chant-lexicon-forgejo": "0.107.1",
50
- "@intentius/chant-lexicon-github": "0.107.1",
51
- "@intentius/chant-lexicon-gitlab": "0.107.1",
52
- "@intentius/chant-lexicon-terraform": "0.107.1",
48
+ "@intentius/chant": "0.109.0",
49
+ "@intentius/chant-lexicon-forgejo": "0.109.0",
50
+ "@intentius/chant-lexicon-github": "0.109.0",
51
+ "@intentius/chant-lexicon-gitlab": "0.109.0",
52
+ "@intentius/chant-lexicon-terraform": "0.109.0",
53
53
  "@intentius/tsad-reference": "2.1.0"
54
54
  }
55
55
  }