@intentius/chant-lexicon-terraform 0.102.0 → 0.104.0
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/__fixtures__/terragrunt/affected-cases.d.ts +23 -0
- package/dist/__fixtures__/terragrunt/affected-cases.d.ts.map +1 -0
- package/dist/__fixtures__/terragrunt/record.d.ts +17 -0
- package/dist/__fixtures__/terragrunt/record.d.ts.map +1 -0
- package/dist/change-set.d.ts +67 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/changed-roots.d.ts +30 -0
- package/dist/changed-roots.d.ts.map +1 -0
- package/dist/commands.d.ts +26 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/components/capability-plugin.d.ts +54 -0
- package/dist/components/capability-plugin.d.ts.map +1 -0
- package/dist/composites/terraform-pin-rollout-op.d.ts +44 -0
- package/dist/composites/terraform-pin-rollout-op.d.ts.map +1 -0
- package/dist/hcl/descend.d.ts +1 -1
- package/dist/hcl/descend.d.ts.map +1 -1
- package/dist/hcl/edges.d.ts +2 -2
- package/dist/hcl/edges.d.ts.map +1 -1
- package/dist/hcl/parse.d.ts +29 -1
- package/dist/hcl/parse.d.ts.map +1 -1
- package/dist/hcl/roots.d.ts +1 -1
- package/dist/hcl/roots.d.ts.map +1 -1
- package/dist/hcl/suppressions.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +13 -4
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/module-source.d.ts +27 -1
- package/dist/lint/post-synth/module-source.d.ts.map +1 -1
- package/dist/lint/post-synth/terragrunt.d.ts +37 -0
- package/dist/lint/post-synth/terragrunt.d.ts.map +1 -0
- package/dist/lint/post-synth/tf002.d.ts +3 -1
- package/dist/lint/post-synth/tf002.d.ts.map +1 -1
- package/dist/lint/post-synth/tf038.d.ts +22 -0
- package/dist/lint/post-synth/tf038.d.ts.map +1 -0
- package/dist/lint/post-synth/tf039.d.ts +23 -0
- package/dist/lint/post-synth/tf039.d.ts.map +1 -0
- package/dist/lint/post-synth/tf040.d.ts +30 -0
- package/dist/lint/post-synth/tf040.d.ts.map +1 -0
- package/dist/lint/post-synth/tf041.d.ts +20 -0
- package/dist/lint/post-synth/tf041.d.ts.map +1 -0
- package/dist/lint/post-synth/tf042.d.ts +18 -0
- package/dist/lint/post-synth/tf042.d.ts.map +1 -0
- package/dist/lint/post-synth/tf043.d.ts +17 -0
- package/dist/lint/post-synth/tf043.d.ts.map +1 -0
- package/dist/lint/post-synth/tf044.d.ts +17 -0
- package/dist/lint/post-synth/tf044.d.ts.map +1 -0
- package/dist/lint/post-synth/tf045.d.ts +22 -0
- package/dist/lint/post-synth/tf045.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +8 -0
- package/dist/okf/rules/TF038.md +11 -0
- package/dist/okf/rules/TF039.md +11 -0
- package/dist/okf/rules/TF040.md +11 -0
- package/dist/okf/rules/TF041.md +11 -0
- package/dist/okf/rules/TF042.md +11 -0
- package/dist/okf/rules/TF043.md +11 -0
- package/dist/okf/rules/TF044.md +11 -0
- package/dist/okf/rules/TF045.md +11 -0
- package/dist/op/activities/index.d.ts +4 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/pin-rollout.d.ts +46 -0
- package/dist/op/activities/pin-rollout.d.ts.map +1 -0
- package/dist/op/activities/terraform.d.ts +24 -50
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activity-contracts.d.ts +48 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/builders.d.ts +9 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/pin/edit-ts.d.ts +21 -0
- package/dist/pin/edit-ts.d.ts.map +1 -0
- package/dist/pin/edit.d.ts +52 -0
- package/dist/pin/edit.d.ts.map +1 -0
- package/dist/pin/forge.d.ts +39 -0
- package/dist/pin/forge.d.ts.map +1 -0
- package/dist/pin/index.d.ts +22 -0
- package/dist/pin/index.d.ts.map +1 -0
- package/dist/pin/rollout.d.ts +116 -0
- package/dist/pin/rollout.d.ts.map +1 -0
- package/dist/pin/scan.d.ts +54 -0
- package/dist/pin/scan.d.ts.map +1 -0
- package/dist/pin/source.d.ts +93 -0
- package/dist/pin/source.d.ts.map +1 -0
- package/dist/pin/waves.d.ts +66 -0
- package/dist/pin/waves.d.ts.map +1 -0
- package/dist/plan-digest.d.ts +59 -0
- package/dist/plan-digest.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/module-source.ts +56 -1
- package/dist/rules/terragrunt.ts +76 -0
- package/dist/rules/tf002.ts +1 -1
- package/dist/rules/tf038.ts +71 -0
- package/dist/rules/tf039.ts +64 -0
- package/dist/rules/tf040.ts +76 -0
- package/dist/rules/tf041.ts +56 -0
- package/dist/rules/tf042.ts +42 -0
- package/dist/rules/tf043.ts +49 -0
- package/dist/rules/tf044.ts +51 -0
- package/dist/rules/tf045.ts +47 -0
- package/dist/terragrunt/affected.d.ts +119 -0
- package/dist/terragrunt/affected.d.ts.map +1 -0
- package/dist/terragrunt/index.d.ts +11 -0
- package/dist/terragrunt/index.d.ts.map +1 -0
- package/dist/terragrunt/mocks.d.ts +147 -0
- package/dist/terragrunt/mocks.d.ts.map +1 -0
- package/dist/terragrunt/run.d.ts +130 -0
- package/dist/terragrunt/run.d.ts.map +1 -0
- package/dist/terragrunt/units.d.ts +103 -0
- package/dist/terragrunt/units.d.ts.map +1 -0
- package/dist/terragrunt/wave.d.ts +89 -0
- package/dist/terragrunt/wave.d.ts.map +1 -0
- package/package.json +12 -2
- package/src/__fixtures__/change-set/choudoufu-set-plan.change-set.golden.json +95 -0
- package/src/__fixtures__/change-set/choudoufu-set-plan.json +76 -0
- package/src/__fixtures__/change-set/choudoufu.part.golden.json +173 -0
- package/src/__fixtures__/change-set/choudoufu.plan.json +610 -0
- package/src/__fixtures__/change-set/terraform.import-forget.plan.json +81 -0
- package/src/__fixtures__/change-set/terraform.part.golden.json +173 -0
- package/src/__fixtures__/change-set/terraform.plan.json +589 -0
- package/src/__fixtures__/change-set/tofu.part.golden.json +173 -0
- package/src/__fixtures__/change-set/tofu.plan.json +588 -0
- package/src/__fixtures__/change-set/v1.tf.txt +21 -0
- package/src/__fixtures__/change-set/v2-module-app.tf.txt +4 -0
- package/src/__fixtures__/change-set/v2.tf.txt +21 -0
- package/src/__fixtures__/pin/choudoufu-live-waves-n5.json +83 -0
- package/src/__fixtures__/pin/largeset-oci-n5/apply-order.txt +5 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e01/estate.chdf.hcl +6 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e01/main.tf +33 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e02/estate.chdf.hcl +6 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e02/main.tf +45 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e03/estate.chdf.hcl +6 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e03/main.tf +47 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e04/estate.chdf.hcl +6 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e04/main.tf +31 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e05/estate.chdf.hcl +6 -0
- package/src/__fixtures__/pin/largeset-oci-n5/estates/e05/main.tf +27 -0
- package/src/__fixtures__/pin/largeset-oci-n5/fixture.json +77 -0
- package/src/__fixtures__/plan-summary/largeset-n5-bump.json +2071 -0
- package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.json +95 -0
- package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.md +22 -0
- package/src/__fixtures__/plan-summary/largeset-n5.summary.golden.txt +12 -0
- package/src/__fixtures__/terragrunt/affected/catalog/units/web/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/affected/gitignore +3 -0
- package/src/__fixtures__/terragrunt/affected/live/dev/app/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/affected/live/dev/vpc/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/affected/live/prod/app/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/affected/live/stg/terragrunt.stack.hcl +5 -0
- package/src/__fixtures__/terragrunt/affected/modules/app/main.tf +6 -0
- package/src/__fixtures__/terragrunt/affected/modules/app/policy.json +1 -0
- package/src/__fixtures__/terragrunt/affected/modules/net/main.tf +2 -0
- package/src/__fixtures__/terragrunt/affected/modules/thing/main.tf +3 -0
- package/src/__fixtures__/terragrunt/affected/root.hcl +5 -0
- package/src/__fixtures__/terragrunt/affected-cases.ts +126 -0
- package/src/__fixtures__/terragrunt/five-units/catalog/units/thing/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/five-units/live/dev/app/terragrunt.hcl +13 -0
- package/src/__fixtures__/terragrunt/five-units/live/dev/db/terragrunt.hcl +8 -0
- package/src/__fixtures__/terragrunt/five-units/live/dev/vpc/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/five-units/live/prod/app/terragrunt.hcl +8 -0
- package/src/__fixtures__/terragrunt/five-units/live/prod/vpc/terragrunt.hcl +3 -0
- package/src/__fixtures__/terragrunt/five-units/modules/thing/main.tf +7 -0
- package/src/__fixtures__/terragrunt/five-units/root.hcl +5 -0
- package/src/__fixtures__/terragrunt/record.ts +56 -0
- package/src/__fixtures__/terragrunt/recorded/affected-find.json +50 -0
- package/src/__fixtures__/terragrunt/recorded/failed-plan-report.json +36 -0
- package/src/__fixtures__/terragrunt/recorded/find.json +67 -0
- package/src/__fixtures__/terragrunt/recorded/version.txt +1 -0
- package/src/__fixtures__/terragrunt/recorded/wave-1/apply-report.json +22 -0
- package/src/__fixtures__/terragrunt/recorded/wave-1/json/live/dev/vpc/tfplan.json +1 -0
- package/src/__fixtures__/terragrunt/recorded/wave-1/json/live/prod/vpc/tfplan.json +1 -0
- package/src/__fixtures__/terragrunt/recorded/wave-1/plan-report.json +22 -0
- package/src/__fixtures__/terragrunt/recorded/wave-2/json/live/dev/db/tfplan.json +1 -0
- package/src/__fixtures__/terragrunt/recorded/wave-2/json/live/prod/app/tfplan.json +1 -0
- package/src/__fixtures__/terragrunt/recorded/wave-2/plan-report.json +22 -0
- package/src/change-set-combined-run.test.ts +156 -0
- package/src/change-set.test.ts +151 -0
- package/src/change-set.ts +273 -0
- package/src/changed-roots.test.ts +58 -0
- package/src/changed-roots.ts +102 -0
- package/src/commands.ts +112 -0
- package/src/components/capability-plugin.test.ts +91 -0
- package/src/components/capability-plugin.ts +131 -0
- package/src/components/pr-loop.tofu.test.ts +244 -0
- package/src/composites/terraform-pin-rollout-op.test.ts +69 -0
- package/src/composites/terraform-pin-rollout-op.ts +60 -0
- package/src/hcl/descend.ts +1 -1
- package/src/hcl/edges.ts +2 -2
- package/src/hcl/parse.test.ts +25 -0
- package/src/hcl/parse.ts +91 -6
- package/src/hcl/roots.ts +1 -1
- package/src/hcl/suppressions.ts +8 -2
- package/src/index.ts +18 -0
- package/src/lint/audit-catalog.ts +82 -0
- package/src/lint/audit.test.ts +3 -1
- package/src/lint/post-synth/fixtures/TF038/positive.tf +3 -0
- package/src/lint/post-synth/fixtures/TF039/positive.tf +4 -0
- package/src/lint/post-synth/fixtures/TF041/negative/terragrunt.hcl +12 -0
- package/src/lint/post-synth/fixtures/TF041/positive/terragrunt.hcl +7 -0
- package/src/lint/post-synth/fixtures/TF041/positive-apply/terragrunt.hcl +8 -0
- package/src/lint/post-synth/fixtures/TF042/negative/terragrunt.hcl +13 -0
- package/src/lint/post-synth/fixtures/TF042/positive/terragrunt.hcl +9 -0
- package/src/lint/post-synth/fixtures/TF043/negative/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF043/negative-other/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF043/positive-git/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF043/positive-oci/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF043/positive-tfr/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF044/negative/terragrunt.hcl +9 -0
- package/src/lint/post-synth/fixtures/TF044/positive/terragrunt.hcl +7 -0
- package/src/lint/post-synth/fixtures/TF044/positive-generate/terragrunt.hcl +9 -0
- package/src/lint/post-synth/fixtures/TF045/negative/root.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF045/negative/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF045/positive/root.hcl +9 -0
- package/src/lint/post-synth/fixtures/TF045/positive/terragrunt.hcl +3 -0
- package/src/lint/post-synth/fixtures/TF045/unit-only/terragrunt.hcl +3 -0
- package/src/lint/post-synth/index.ts +16 -0
- package/src/lint/post-synth/module-source.test.ts +42 -1
- package/src/lint/post-synth/module-source.ts +56 -1
- package/src/lint/post-synth/terragrunt.ts +76 -0
- package/src/lint/post-synth/tf002.ts +1 -1
- package/src/lint/post-synth/tf038.test.ts +51 -0
- package/src/lint/post-synth/tf038.ts +71 -0
- package/src/lint/post-synth/tf039.test.ts +40 -0
- package/src/lint/post-synth/tf039.ts +64 -0
- package/src/lint/post-synth/tf040.test.ts +55 -0
- package/src/lint/post-synth/tf040.ts +76 -0
- package/src/lint/post-synth/tf041.test.ts +50 -0
- package/src/lint/post-synth/tf041.ts +56 -0
- package/src/lint/post-synth/tf042.test.ts +19 -0
- package/src/lint/post-synth/tf042.ts +42 -0
- package/src/lint/post-synth/tf043.test.ts +65 -0
- package/src/lint/post-synth/tf043.ts +49 -0
- package/src/lint/post-synth/tf044.test.ts +27 -0
- package/src/lint/post-synth/tf044.ts +51 -0
- package/src/lint/post-synth/tf045.test.ts +30 -0
- package/src/lint/post-synth/tf045.ts +47 -0
- package/src/op/activities/index.ts +4 -0
- package/src/op/activities/pin-rollout.ts +77 -0
- package/src/op/activities/terraform.test.ts +13 -0
- package/src/op/activities/terraform.ts +46 -86
- package/src/op/activity-contracts.test.ts +4 -1
- package/src/op/activity-contracts.ts +42 -0
- package/src/op/builders.ts +13 -0
- package/src/pin/bundle.test.ts +53 -0
- package/src/pin/edit-ts.test.ts +39 -0
- package/src/pin/edit-ts.ts +77 -0
- package/src/pin/edit.test.ts +199 -0
- package/src/pin/edit.ts +157 -0
- package/src/pin/forge.ts +85 -0
- package/src/pin/index.ts +22 -0
- package/src/pin/rollout.test.ts +232 -0
- package/src/pin/rollout.ts +451 -0
- package/src/pin/scan.ts +309 -0
- package/src/pin/source.ts +206 -0
- package/src/pin/waves.test.ts +101 -0
- package/src/pin/waves.ts +125 -0
- package/src/plan-digest.ts +99 -0
- package/src/plan-summary.test.ts +154 -0
- package/src/plugin.ts +25 -2
- package/src/terragrunt/affected.acceptance.test.ts +83 -0
- package/src/terragrunt/affected.test.ts +177 -0
- package/src/terragrunt/affected.ts +348 -0
- package/src/terragrunt/index.ts +11 -0
- package/src/terragrunt/mocks.acceptance.test.ts +105 -0
- package/src/terragrunt/mocks.test.ts +309 -0
- package/src/terragrunt/mocks.ts +324 -0
- package/src/terragrunt/run.ts +369 -0
- package/src/terragrunt/terragrunt.acceptance.test.ts +122 -0
- package/src/terragrunt/terragrunt.test.ts +332 -0
- package/src/terragrunt/units.ts +235 -0
- package/src/terragrunt/wave.ts +181 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pin-bump rollout end to end over git, with the forge mocked (#3189).
|
|
3
|
+
*
|
|
4
|
+
* The repository is choudoufu#1750's pinned-OCI variant at N=5, generated at
|
|
5
|
+
* choudoufu 52bef26 with
|
|
6
|
+
* `go run ./tools/largeset-gen -estates 5 -source oci -registry localhost:4890`:
|
|
7
|
+
* five estate roots, each calling `oci://localhost:4890/largeset/shared?tag=1.0.0`,
|
|
8
|
+
* where e02 reads e01 and e03 reads e02. It is pushed to a bare remote, so
|
|
9
|
+
* the rollout fetches, builds wave branches in its own worktree and pushes
|
|
10
|
+
* them for real. Only the forge is a mock: it records each PR, and the test
|
|
11
|
+
* merges one by moving the remote's `main` to the PR's head, the way a
|
|
12
|
+
* fast-forward merge would.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
16
|
+
import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
20
|
+
import { loadHcl2json } from "@intentius/chant/terraform/hcl2json";
|
|
21
|
+
import { runPinRollout, type PinRolloutOptions } from "./rollout";
|
|
22
|
+
import type { PinCommitCheck, PinForge, PinPullRequest } from "./forge";
|
|
23
|
+
import { editPinsInTs } from "./edit-ts";
|
|
24
|
+
|
|
25
|
+
const parser = await loadHcl2json();
|
|
26
|
+
const FIXTURE = new URL("../__fixtures__/pin/largeset-oci-n5", import.meta.url).pathname;
|
|
27
|
+
const MODULE = "oci://localhost:4890/largeset/shared";
|
|
28
|
+
const DEPS = [
|
|
29
|
+
{ root: "estates/e01" },
|
|
30
|
+
{ root: "estates/e02", dependsOn: ["estates/e01"] },
|
|
31
|
+
{ root: "estates/e03", dependsOn: ["estates/e02"] },
|
|
32
|
+
{ root: "estates/e04" },
|
|
33
|
+
{ root: "estates/e05" },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
function git(cwd: string, ...args: string[]): string {
|
|
37
|
+
return execFileSync("git", args, { cwd, encoding: "utf-8", env: { ...process.env, GIT_AUTHOR_NAME: "t", GIT_AUTHOR_EMAIL: "t@t", GIT_COMMITTER_NAME: "t", GIT_COMMITTER_EMAIL: "t@t" } });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** An in-memory forge over the bare remote. */
|
|
41
|
+
class MockForge implements PinForge {
|
|
42
|
+
prs = new Map<string, PinPullRequest & { head: string; title: string; base: string }>();
|
|
43
|
+
checks = new Map<string, PinCommitCheck[]>();
|
|
44
|
+
constructor(private remote: string) {}
|
|
45
|
+
async findPullRequest(branch: string) {
|
|
46
|
+
return this.prs.get(branch) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async createPullRequest(input: { base: string; head: string; title: string; body: string }) {
|
|
49
|
+
const url = `https://forge.test/pr/${this.prs.size + 1}`;
|
|
50
|
+
this.prs.set(input.head, { ...input, url, state: "open" });
|
|
51
|
+
return url;
|
|
52
|
+
}
|
|
53
|
+
async commitChecks(sha: string) {
|
|
54
|
+
return this.checks.get(sha) ?? [];
|
|
55
|
+
}
|
|
56
|
+
/** Merge the PR from `branch` by moving the remote's main to its head. */
|
|
57
|
+
merge(branch: string): string {
|
|
58
|
+
const sha = git(this.remote, "rev-parse", `refs/heads/${branch}`).trim();
|
|
59
|
+
git(this.remote, "update-ref", "refs/heads/main", sha);
|
|
60
|
+
const pr = this.prs.get(branch)!;
|
|
61
|
+
pr.state = "merged";
|
|
62
|
+
pr.mergeCommit = sha;
|
|
63
|
+
return sha;
|
|
64
|
+
}
|
|
65
|
+
report(sha: string, roots: string[], state: PinCommitCheck["state"]) {
|
|
66
|
+
this.checks.set(sha, [...(this.checks.get(sha) ?? []), ...roots.map((r) => ({ name: `apply/${r}`, state }))]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let dir: string;
|
|
71
|
+
let remote: string;
|
|
72
|
+
let work: string;
|
|
73
|
+
let forge: MockForge;
|
|
74
|
+
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
dir = mkdtempSync(join(tmpdir(), "pin-rollout-"));
|
|
77
|
+
remote = join(dir, "remote.git");
|
|
78
|
+
work = join(dir, "work");
|
|
79
|
+
git(dir, "init", "--quiet", "--bare", "--initial-branch=main", remote);
|
|
80
|
+
cpSync(FIXTURE, work, { recursive: true });
|
|
81
|
+
git(work, "init", "--quiet", "--initial-branch=main");
|
|
82
|
+
git(work, "add", ".");
|
|
83
|
+
git(work, "commit", "--quiet", "-m", "fixture");
|
|
84
|
+
git(work, "remote", "add", "origin", remote);
|
|
85
|
+
git(work, "push", "--quiet", "origin", "main");
|
|
86
|
+
git(work, "remote", "set-head", "origin", "main");
|
|
87
|
+
forge = new MockForge(remote);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
afterEach(() => rmSync(dir, { recursive: true, force: true }));
|
|
91
|
+
|
|
92
|
+
function options(extra: Partial<PinRolloutOptions> = {}): PinRolloutOptions {
|
|
93
|
+
return { cwd: work, module: MODULE, from: "1.0.0", to: "1.1.0", parser, forge, roots: DEPS, canaries: ["estates/e04"], mode: "pull-request", ...extra };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** The roots a path-diff selection picks for a branch: the root directories of the files it changes against main. */
|
|
97
|
+
function pathDiffRoots(branch: string): string[] {
|
|
98
|
+
const files = git(remote, "diff", "--name-only", `main...${branch}`).split("\n").filter(Boolean);
|
|
99
|
+
return [...new Set(files.map((f) => f.split("/").slice(0, 2).join("/")))].sort();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe("runPinRollout (#3189)", () => {
|
|
103
|
+
test("one PR per wave, each opening only after the last merged and applied", async () => {
|
|
104
|
+
const head = git(work, "rev-parse", "HEAD").trim();
|
|
105
|
+
|
|
106
|
+
// Run 1 opens wave 1, the canary, and nothing else.
|
|
107
|
+
const r1 = await runPinRollout(options());
|
|
108
|
+
expect(r1.status).toBe("opened");
|
|
109
|
+
expect(r1.waves.map((w) => [w.wave, w.roots, w.state])).toEqual([
|
|
110
|
+
[1, ["estates/e04"], "opened"],
|
|
111
|
+
[2, ["estates/e01", "estates/e05"], "not-reached"],
|
|
112
|
+
[3, ["estates/e02"], "not-reached"],
|
|
113
|
+
[4, ["estates/e03"], "not-reached"],
|
|
114
|
+
]);
|
|
115
|
+
expect([...forge.prs.keys()]).toEqual(["chant/pin/localhost-4890-largeset-shared/1.1.0/wave-1"]);
|
|
116
|
+
const pr1 = forge.prs.get(r1.waves[0]!.branch)!;
|
|
117
|
+
expect(pr1.base).toBe("main");
|
|
118
|
+
expect(pr1.body).toContain("`oci://localhost:4890/largeset/shared` pin 1.0.0 -> 1.1.0");
|
|
119
|
+
expect(pr1.body).toContain("wave 1 of 4 (canaries)");
|
|
120
|
+
expect(pr1.body).toContain("- `estates/e04`: estates/e04/main.tf module.shared");
|
|
121
|
+
expect(pathDiffRoots(r1.waves[0]!.branch)).toEqual(["estates/e04"]);
|
|
122
|
+
|
|
123
|
+
// Never the default branch, never the caller's checkout.
|
|
124
|
+
expect(git(remote, "rev-parse", "refs/heads/main").trim()).toBe(head);
|
|
125
|
+
expect(git(work, "rev-parse", "HEAD").trim()).toBe(head);
|
|
126
|
+
expect(git(work, "status", "--porcelain")).toBe("");
|
|
127
|
+
|
|
128
|
+
// Run 2, before the merge: reports where it stands and opens nothing.
|
|
129
|
+
const r2 = await runPinRollout(options());
|
|
130
|
+
expect(r2.status).toBe("waiting");
|
|
131
|
+
expect(r2.waves[0]!.state).toBe("open");
|
|
132
|
+
expect(forge.prs.size).toBe(1);
|
|
133
|
+
|
|
134
|
+
// Merged, apply not reported yet: still nothing opens.
|
|
135
|
+
const merge1 = forge.merge(r1.waves[0]!.branch);
|
|
136
|
+
const r3 = await runPinRollout(options());
|
|
137
|
+
expect(r3.status).toBe("waiting");
|
|
138
|
+
expect(r3.waves[0]).toMatchObject({ state: "waiting-apply", pending: ["estates/e04"] });
|
|
139
|
+
expect(forge.prs.size).toBe(1);
|
|
140
|
+
|
|
141
|
+
// Applied: wave 2 opens, built on the merged main.
|
|
142
|
+
forge.report(merge1, ["estates/e04"], "success");
|
|
143
|
+
const r4 = await runPinRollout(options());
|
|
144
|
+
expect(r4.status).toBe("opened");
|
|
145
|
+
expect(r4.waves.map((w) => w.state)).toEqual(["applied", "opened", "not-reached", "not-reached"]);
|
|
146
|
+
expect(pathDiffRoots(r4.waves[1]!.branch)).toEqual(["estates/e01", "estates/e05"]);
|
|
147
|
+
|
|
148
|
+
// Waves 2 to 4, each merged and applied in turn.
|
|
149
|
+
let last = r4;
|
|
150
|
+
for (const n of [1, 2, 3]) {
|
|
151
|
+
const branch = last.waves[n]!.branch;
|
|
152
|
+
forge.report(forge.merge(branch), last.waves[n]!.roots, "success");
|
|
153
|
+
last = await runPinRollout(options());
|
|
154
|
+
if (n < 3) {
|
|
155
|
+
expect(last.waves[n + 1]!.state).toBe("opened");
|
|
156
|
+
expect(pathDiffRoots(last.waves[n + 1]!.branch)).toEqual(last.waves[n + 1]!.roots);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
expect(last.status).toBe("complete");
|
|
160
|
+
expect(forge.prs.size).toBe(4);
|
|
161
|
+
// Every root is at the new pin on main.
|
|
162
|
+
for (const e of ["e01", "e02", "e03", "e04", "e05"]) {
|
|
163
|
+
expect(git(remote, "show", `main:estates/${e}/main.tf`)).toContain(`${MODULE}?tag=1.1.0`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("a failed root stops the waves after it, and is named", async () => {
|
|
168
|
+
const r1 = await runPinRollout(options({ canaries: [] }));
|
|
169
|
+
const wave1 = r1.waves[0]!;
|
|
170
|
+
expect(wave1.roots).toEqual(["estates/e01", "estates/e04", "estates/e05"]);
|
|
171
|
+
const sha = forge.merge(wave1.branch);
|
|
172
|
+
forge.report(sha, ["estates/e01", "estates/e04"], "success");
|
|
173
|
+
forge.report(sha, ["estates/e05"], "failure");
|
|
174
|
+
const r2 = await runPinRollout(options({ canaries: [] }));
|
|
175
|
+
expect(r2.status).toBe("stopped");
|
|
176
|
+
expect(r2.stop).toBe("wave 1: estates/e05 failed its apply check apply/estates/e05");
|
|
177
|
+
expect(r2.waves.map((w) => w.state)).toEqual(["failed", "not-reached", "not-reached"]);
|
|
178
|
+
expect(forge.prs.size).toBe(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("a PR closed without merging stops the rollout", async () => {
|
|
182
|
+
const r1 = await runPinRollout(options());
|
|
183
|
+
forge.prs.get(r1.waves[0]!.branch)!.state = "closed";
|
|
184
|
+
const r2 = await runPinRollout(options());
|
|
185
|
+
expect(r2.status).toBe("stopped");
|
|
186
|
+
expect(r2.stop).toMatch(/wave 1's PR https:\/\/forge.test\/pr\/1 was closed without merging/);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("report mode opens nothing and pushes nothing", async () => {
|
|
190
|
+
const r = await runPinRollout(options({ mode: "report" }));
|
|
191
|
+
expect(r.status).toBe("would-open");
|
|
192
|
+
expect(r.waves[0]).toMatchObject({ state: "would-open", files: ["estates/e04/main.tf"] });
|
|
193
|
+
expect(forge.prs.size).toBe(0);
|
|
194
|
+
expect(git(remote, "branch", "--list", "chant/*")).toBe("");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("a floating constraint is refused by name and left out of every wave", async () => {
|
|
198
|
+
const tf = join(work, "estates/e05/main.tf");
|
|
199
|
+
writeFileSync(tf, readFileSync(tf, "utf-8").replace(`source = "${MODULE}?tag=1.0.0"`, `source = "app.terraform.io/acme/shared/aws"\n version = "~> 1.0"`));
|
|
200
|
+
git(work, "commit", "--quiet", "-am", "e05 floats");
|
|
201
|
+
git(work, "push", "--quiet", "origin", "main");
|
|
202
|
+
const r = await runPinRollout(options({ module: "app.terraform.io/acme/shared/aws", from: "1.0.0", to: "1.1.0", mode: "report", roots: undefined, canaries: [] }));
|
|
203
|
+
expect(r.roots).toEqual([
|
|
204
|
+
expect.objectContaining({ root: "estates/e05", state: "refused", reason: expect.stringContaining(`version "~> 1.0" is a constraint, not a pin`) }),
|
|
205
|
+
]);
|
|
206
|
+
expect(r.waves).toEqual([]);
|
|
207
|
+
expect(r.status).toBe("complete");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("roots are found when none are named, and choudoufu's waves can order them", async () => {
|
|
211
|
+
const doc = JSON.parse(readFileSync(new URL("../__fixtures__/pin/choudoufu-live-waves-n5.json", import.meta.url), "utf-8"));
|
|
212
|
+
const { wavesFromChoudoufu } = await import("./waves");
|
|
213
|
+
const r = await runPinRollout(options({ roots: undefined, canaries: undefined, waves: wavesFromChoudoufu(doc), mode: "report" }));
|
|
214
|
+
expect(r.roots.map((x) => x.root)).toEqual(["estates/e01", "estates/e02", "estates/e03", "estates/e04", "estates/e05"]);
|
|
215
|
+
expect(r.waves.map((w) => w.roots)).toEqual([["estates/e04"], ["estates/e01", "estates/e05"], ["estates/e02"], ["estates/e03"]]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("a generated root's pin moves in its TypeScript source", async () => {
|
|
219
|
+
writeFileSync(join(work, "estates/e04/shared.ts"), `export const shared = { source: "${MODULE}?tag=1.0.0", name: "e04" };\n`);
|
|
220
|
+
git(work, "add", ".");
|
|
221
|
+
git(work, "commit", "--quiet", "-m", "e04 is generated");
|
|
222
|
+
git(work, "push", "--quiet", "origin", "main");
|
|
223
|
+
const roots = DEPS.map((r) => (r.root === "estates/e04" ? { ...r, tsSource: "estates/e04/shared.ts" } : r));
|
|
224
|
+
|
|
225
|
+
const without = await runPinRollout(options({ roots, mode: "report" }));
|
|
226
|
+
expect(without.roots.find((r) => r.root === "estates/e04")).toMatchObject({ state: "refused", reason: expect.stringContaining("no TypeScript editor") });
|
|
227
|
+
|
|
228
|
+
const r = await runPinRollout(options({ roots, editTs: editPinsInTs }));
|
|
229
|
+
expect(r.waves[0]).toMatchObject({ roots: ["estates/e04"], state: "opened", files: ["estates/e04/shared.ts"] });
|
|
230
|
+
expect(git(remote, "show", `${r.waves[0]!.branch}:estates/e04/shared.ts`)).toContain(`${MODULE}?tag=1.1.0`);
|
|
231
|
+
});
|
|
232
|
+
});
|