@intentius/chant-lexicon-gitlab 0.116.0 → 0.118.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.
@@ -9,6 +9,15 @@
9
9
  * decision as an artifact, which GitLab hands each share job through `needs:`.
10
10
  * A wave's `environment` is the job's own `environment:`; protect it in the
11
11
  * project settings to put a reviewer in front of the job as well.
12
+ *
13
+ * With `resume` on the spec (#3683), a `<name>-resume` job runs `chant run
14
+ * resume --op <name>` in scheduled pipelines only: create a pipeline schedule
15
+ * with the spec's cron. It retries a waiting wave's job once its approval
16
+ * arrived, with `CHANT_FORGE_TOKEN` (a project access token with the `api`
17
+ * scope and the Developer role; the job token cannot retry jobs).
18
+ *
19
+ * A `pr-review` wave (#3684) adds `<name>-record-plans`, run in merge request
20
+ * pipelines only, which records the digests the merge request's head plans.
12
21
  */
13
22
  import { type OpWavesSpec } from "@intentius/chant/op/op-waves";
14
23
  import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
@@ -1 +1 @@
1
- {"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAK9F,gHAAgH;AAChH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAgDvH"}
1
+ {"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAClG,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAK9F,gHAAgH;AAChH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CA4EvH"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "5dbd479adbef7ff3f5f78326a864313f31bc59e0bc68e6f1ced8bcc3a9213624",
4
+ "manifest.json": "598869766c3821ca4fdf25bbd91ade4e8957542e0694373764d3f8d7120a0cdb",
5
5
  "meta.json": "931fc3246a55645b1493080bbeb160e5d205e42349e8bdca96ca243adb5f0da3",
6
6
  "types/index.d.ts": "5cd2e99f135a929b72bdd822d00d780d39b1cd407ba0cfb3d511c7ac9d667b58",
7
7
  "rules/artifact-no-expiry.ts": "3f3cabf9792cbf8207e53a25f506715466b19ec25e9c3b4d0d77fed6b2eb4542",
@@ -55,5 +55,5 @@
55
55
  "skills/chant-gitlab-migrate.md": "4853d04980560b379e5b0f7267dcb0a1e5d5879cb9e9931681ce76b2a5fe69d8",
56
56
  "skills/chant-gitlab-patterns.md": "6d9a44e9e8de4c3820be9d65381b0d2ede4cb3626aaaf0011e21838a9fbf556e"
57
57
  },
58
- "composite": "5de94034f8a25bce4e7534039b13e564518784255b770f1281849cb5a0821294"
58
+ "composite": "9f524e8c727cefaf1e1cf3d24a63928defc3a947f3a7dbc44a396c7d90d0f36f"
59
59
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab",
3
- "version": "0.116.0",
3
+ "version": "0.118.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitLab",
6
6
  "intrinsics": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-gitlab",
3
- "version": "0.116.0",
3
+ "version": "0.118.0",
4
4
  "description": "GitLab CI lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -67,8 +67,8 @@
67
67
  "typescript": "^5.9.3"
68
68
  },
69
69
  "peerDependencies": {
70
- "@intentius/chant": "^0.116.0",
71
- "@intentius/chant-lexicon-github": "^0.116.0",
70
+ "@intentius/chant": "^0.118.0",
71
+ "@intentius/chant-lexicon-github": "^0.118.0",
72
72
  "typescript": "^5.9.3"
73
73
  },
74
74
  "peerDependenciesMeta": {
@@ -26,4 +26,22 @@ describe("generateGitlabOpWavesPipeline", () => {
26
26
  expect(yaml).toContain("- .chant/op-waves/migrations/wave-2.json");
27
27
  expect(yaml).toContain("environment:\n name: production");
28
28
  });
29
+
30
+ test("resume renders a job that runs only in scheduled pipelines (#3683)", () => {
31
+ const { files } = generateGitlabOpWavesPipeline({ ...spec, resume: { schedule: "*/10 * * * *" } }, { specFile: "waves.json" });
32
+ const yaml = files[0]!.yaml;
33
+ expect(yaml).toContain(`${spec.name}-resume:`);
34
+ expect(yaml).toContain('$CI_PIPELINE_SOURCE == "schedule"');
35
+ expect(yaml).toContain(`chant run resume --op ${spec.name}`);
36
+ expect(yaml).toContain('give the schedule the cron "*/10 * * * *"');
37
+ expect(generateGitlabOpWavesPipeline(spec, { specFile: "waves.json" }).files[0]!.yaml).not.toContain("resume");
38
+ });
39
+
40
+ test("a pr-review wave adds a merge request job that records the head's plans (#3684)", () => {
41
+ const yaml = generateGitlabOpWavesPipeline({ ...spec, waves: spec.waves.map((w, i) => (i === spec.waves.length - 1 ? { ...w, approval: "pr-review" as const } : w)) }, { specFile: "waves.json" }).files[0]!.yaml;
42
+ expect(yaml).toContain(`${spec.name}-record-plans:`);
43
+ expect(yaml).toContain('$CI_PIPELINE_SOURCE == "merge_request_event"');
44
+ expect(yaml).toContain("chant run wave --spec waves.json --record-plans");
45
+ expect(yaml).toMatch(/stages:\n\s+- plans\n/);
46
+ });
29
47
  });
@@ -9,10 +9,19 @@
9
9
  * decision as an artifact, which GitLab hands each share job through `needs:`.
10
10
  * A wave's `environment` is the job's own `environment:`; protect it in the
11
11
  * project settings to put a reviewer in front of the job as well.
12
+ *
13
+ * With `resume` on the spec (#3683), a `<name>-resume` job runs `chant run
14
+ * resume --op <name>` in scheduled pipelines only: create a pipeline schedule
15
+ * with the spec's cron. It retries a waiting wave's job once its approval
16
+ * arrived, with `CHANT_FORGE_TOKEN` (a project access token with the `api`
17
+ * scope and the Developer role; the job token cannot retry jobs).
18
+ *
19
+ * A `pr-review` wave (#3684) adds `<name>-record-plans`, run in merge request
20
+ * pipelines only, which records the digests the merge request's head plans.
12
21
  */
13
22
 
14
23
  import { emitYAMLEntry } from "@intentius/chant/yaml";
15
- import { opWaveJobs, type OpWavesSpec } from "@intentius/chant/op/op-waves";
24
+ import { opWaveJobs, opWavesResumeCommand, type OpWavesSpec } from "@intentius/chant/op/op-waves";
16
25
  import type { OpWavesPipelineOptions, OpWavesPipelineResult } from "@intentius/chant/lexicon";
17
26
 
18
27
  /** The default image: it carries git, which the runner needs to read the base commit. */
@@ -30,7 +39,9 @@ export function generateGitlabOpWavesPipeline(spec: OpWavesSpec, options: OpWave
30
39
  }
31
40
  const rules = branches.map((branch) => ({ if: `$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "${branch}"` }));
32
41
 
33
- const stages = spec.waves.map((wave, i) => `wave-${i + 1}-${wave.name}`);
42
+ const byReview = spec.waves.some((w) => w.approval === "pr-review");
43
+ const stages = [...(byReview ? ["plans"] : []), ...spec.waves.map((wave, i) => `wave-${i + 1}-${wave.name}`)];
44
+ if (spec.resume) stages.push("resume");
34
45
  const sections: string[] = [emitYAMLEntry("stages", stages)];
35
46
  if (options.variables && Object.keys(options.variables).length > 0) sections.push(emitYAMLEntry("variables", options.variables));
36
47
  for (const job of jobs) {
@@ -46,7 +57,7 @@ export function generateGitlabOpWavesPipeline(spec: OpWavesSpec, options: OpWave
46
57
  });
47
58
  sections.push(
48
59
  emitYAMLEntry(job.jobName, {
49
- stage: stages[job.wave - 1],
60
+ stage: stages[job.wave - 1 + (byReview ? 1 : 0)],
50
61
  image,
51
62
  resource_group: `${spec.name}-${wave.name}`,
52
63
  ...(wave.environment
@@ -60,10 +71,36 @@ export function generateGitlabOpWavesPipeline(spec: OpWavesSpec, options: OpWave
60
71
  }),
61
72
  );
62
73
  }
74
+ if (spec.resume) {
75
+ sections.push(
76
+ emitYAMLEntry(`${spec.name}-resume`, {
77
+ stage: "resume",
78
+ image,
79
+ rules: [{ if: '$CI_PIPELINE_SOURCE == "schedule"' }],
80
+ script: [...(options.beforeScript ?? []), opWavesResumeCommand(spec).join(" ")],
81
+ }),
82
+ );
83
+ }
84
+ if (byReview) {
85
+ sections.splice(
86
+ options.variables && Object.keys(options.variables).length > 0 ? 2 : 1,
87
+ 0,
88
+ emitYAMLEntry(`${spec.name}-record-plans`, {
89
+ stage: "plans",
90
+ image,
91
+ variables: { GIT_DEPTH: "0" },
92
+ rules: [{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }],
93
+ script: [...(options.beforeScript ?? []), ["chant", "run", "wave", "--spec", options.specFile, "--record-plans"].join(" ")],
94
+ }),
95
+ );
96
+ }
63
97
  const header = [
64
98
  `# chant Op waves "${spec.name}" (#3679): one stage per wave, each needing the one before.`,
65
99
  "# A wave that waits at its gate exits 3 and stops the waves after it; approve the",
66
100
  "# digest it prints, then retry the job.",
101
+ ...(spec.resume
102
+ ? [`# The ${spec.name}-resume job retries it for you in a pipeline schedule; give the schedule the cron "${spec.resume.schedule}".`]
103
+ : []),
67
104
  ];
68
105
  const name = options.opsFileName ?? `${spec.name}.gitlab-ci.yml`;
69
106
  return { files: [{ name, yaml: header.join("\n") + "\n\n" + sections.join("\n\n") + "\n" }], jobs };