@intentius/chant-lexicon-gitlab 0.105.1 → 0.106.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/components/generate-pipeline.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +3 -3
- package/src/components/__fixtures__/pr-loop.gitlab.golden.yml +11 -3
- package/src/components/generate-pipeline.test.ts +3 -1
- package/src/components/generate-pipeline.ts +43 -15
- package/src/components/generate-pr-pipeline.test.ts +51 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAKjG,OAAO,KAAK,EACV,oBAAoB,IAAI,YAAY,EACpC,wBAAwB,IAAI,qBAAqB,EACjD,uBAAuB,IAAI,oBAAoB,EAEhD,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;AAS1E;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAKjG,OAAO,KAAK,EACV,oBAAoB,IAAI,YAAY,EACpC,wBAAwB,IAAI,qBAAqB,EACjD,uBAAuB,IAAI,oBAAoB,EAEhD,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;AAS1E;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAqJtB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "47ce6b5b8e35b2dd162ab80d25122abeadf6c896bc1ecca0ae585b3c7cbb6c31",
|
|
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": "
|
|
58
|
+
"composite": "a5370ff33eed54ef2099b3cb8454208518b7a5005cf5ca8249b2a127a997023c"
|
|
59
59
|
}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gitlab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.106.1",
|
|
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.
|
|
71
|
-
"@intentius/chant-lexicon-github": "^0.
|
|
70
|
+
"@intentius/chant": "^0.106.1",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.106.1",
|
|
72
72
|
"typescript": "^5.9.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|
|
@@ -14,13 +14,14 @@ variables:
|
|
|
14
14
|
|
|
15
15
|
plan:
|
|
16
16
|
stage: plan
|
|
17
|
-
image: node:22
|
|
17
|
+
image: node:22
|
|
18
18
|
rules:
|
|
19
19
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
20
20
|
variables:
|
|
21
21
|
BASE_SHA: '$CI_MERGE_REQUEST_DIFF_BASE_SHA'
|
|
22
22
|
PR_NUMBER: '$CI_MERGE_REQUEST_IID'
|
|
23
23
|
script:
|
|
24
|
+
- command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
|
|
24
25
|
- chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge gitlab
|
|
25
26
|
artifacts:
|
|
26
27
|
when: always
|
|
@@ -29,14 +30,21 @@ plan:
|
|
|
29
30
|
|
|
30
31
|
apply:
|
|
31
32
|
stage: apply
|
|
32
|
-
image: node:22
|
|
33
|
+
image: node:22
|
|
33
34
|
resource_group: chant-apply-prod
|
|
34
35
|
rules:
|
|
35
36
|
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
36
37
|
variables:
|
|
37
38
|
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
38
39
|
script:
|
|
39
|
-
-
|
|
40
|
+
- command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
|
|
41
|
+
- if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then BASE_SHA="${CI_MERGE_REQUEST_DIFF_BASE_SHA:-}"; if [ -z "$BASE_SHA" ]; then git fetch origin $CI_DEFAULT_BRANCH && BASE_SHA="$(git merge-base FETCH_HEAD "$CI_COMMIT_SHA")"; if [ "$BASE_SHA" = "$CI_COMMIT_SHA" ]; then BASE_SHA="$(git rev-parse "$CI_COMMIT_SHA^")"; fi; fi; fi; export BASE_SHA
|
|
42
|
+
- chant components pr-apply --base "$BASE_SHA" --env prod --gate pr-apply --output .chant/pr --forge gitlab --require-review --resume .chant/pr-resume/pr-apply.json
|
|
43
|
+
cache:
|
|
44
|
+
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
45
|
+
paths:
|
|
46
|
+
- .chant/pr-resume/pr-apply.json
|
|
47
|
+
when: always
|
|
40
48
|
artifacts:
|
|
41
49
|
when: always
|
|
42
50
|
paths:
|
|
@@ -343,7 +343,9 @@ describe("generateGitlabPipeline: gated waves (#3049)", () => {
|
|
|
343
343
|
expect(result.stages).toEqual(["wave-1", "wave-2", "wave-3"]);
|
|
344
344
|
expect(parsed["wave-2"].needs).toEqual(["wave-1"]);
|
|
345
345
|
expect(parsed["wave-1"].needs).toBeUndefined();
|
|
346
|
+
expect(parsed["wave-3"].image).toBe("node:22");
|
|
346
347
|
expect(parsed["wave-3"].script).toEqual([
|
|
348
|
+
expect.stringMatching(/^command -v tofu /),
|
|
347
349
|
"chant components fan-out --base HEAD~1 --env prod --wave-gate release --wave 3 --resume .chant/fan-out.json",
|
|
348
350
|
]);
|
|
349
351
|
// The record goes to the next job even when this one stops at a gate.
|
|
@@ -357,6 +359,6 @@ describe("generateGitlabPipeline: gated waves (#3049)", () => {
|
|
|
357
359
|
const result = generateGitlabPipeline(roots, { env: "prod", gatedWaves: { gate: "release", canary: ["b"], base: "$CI_COMMIT_BEFORE_SHA" } });
|
|
358
360
|
expect(result.stages).toEqual(["wave-1", "wave-2", "wave-3", "wave-4"]);
|
|
359
361
|
const parsed = parseYAML(result.yaml) as Record<string, Record<string, unknown>>;
|
|
360
|
-
expect((parsed["wave-1"].script as string[])[
|
|
362
|
+
expect((parsed["wave-1"].script as string[])[1]).toContain("--base $CI_COMMIT_BEFORE_SHA --env prod --wave-gate release --canary b --wave 1");
|
|
361
363
|
});
|
|
362
364
|
});
|
|
@@ -36,7 +36,7 @@ import { emitYAMLEntry } from "@intentius/chant/yaml";
|
|
|
36
36
|
import { resolveComponentGraph, type DriverComponent } from "@intentius/chant/components/driver";
|
|
37
37
|
import { hasPublishStep, promoteArchivePaths } from "@intentius/chant/components/promote";
|
|
38
38
|
import { GATED_WAVE_RECORD, gatedWaveJobs } from "@intentius/chant/components/gated-wave-pipeline";
|
|
39
|
-
import { PR_LOOP_REPORT_DIR, prApplyGroup, prLoopJobs } from "@intentius/chant/components/pr-pipeline";
|
|
39
|
+
import { PR_APPLY_RECORD, PR_LOOP_IMAGE, PR_LOOP_REPORT_DIR, prApplyGroup, prApplyRecordKey, prLoopJobs, prLoopSetup } from "@intentius/chant/components/pr-pipeline";
|
|
40
40
|
import { memberGitlabChanges, memberRepoPath, memberShellDir } from "@intentius/chant/lexicon";
|
|
41
41
|
import type {
|
|
42
42
|
ComponentPipelineJob as GeneratedJob,
|
|
@@ -67,7 +67,7 @@ export function generateGitlabPipeline(
|
|
|
67
67
|
options: GenerateGitlabOptions = {},
|
|
68
68
|
): GenerateGitlabResult {
|
|
69
69
|
const env = options.env ?? "production";
|
|
70
|
-
const image = options.image ?? DEFAULT_IMAGE;
|
|
70
|
+
const image = options.image ?? (options.prLoop || options.gatedWaves ? PR_LOOP_IMAGE : DEFAULT_IMAGE);
|
|
71
71
|
const runCommand = options.runCommand ?? ["chant", "run", "--components", "{name}", "--env", env];
|
|
72
72
|
const beforeScript = options.beforeScript ?? [];
|
|
73
73
|
const extraScript = options.extraScript ?? [];
|
|
@@ -79,7 +79,6 @@ export function generateGitlabPipeline(
|
|
|
79
79
|
if (options.gatedWaves || options.promoteTo !== undefined) {
|
|
80
80
|
throw new Error("a pull-request pipeline has no wave or promote jobs; drop --wave-gate and --promote-to, or --pr-loop");
|
|
81
81
|
}
|
|
82
|
-
if (options.member) throw new Error("a pull-request pipeline is not generated for a workspace member yet; generate it from the project outside the workspace");
|
|
83
82
|
return prLoopGitlabPipeline(env, image, beforeScript, extraScript, options);
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -246,7 +245,7 @@ function gatedWaveGitlabPipeline(
|
|
|
246
245
|
stage: job.jobName,
|
|
247
246
|
image,
|
|
248
247
|
variables: { GIT_DEPTH: "0" },
|
|
249
|
-
script: [...beforeScript, job.command.join(" "), ...extraScript],
|
|
248
|
+
script: [...prLoopSetup(image), ...beforeScript, job.command.join(" "), ...extraScript],
|
|
250
249
|
...(job.needs.length > 0 ? { needs: job.needs } : {}),
|
|
251
250
|
artifacts: { when: "always", paths: [GATED_WAVE_RECORD] },
|
|
252
251
|
};
|
|
@@ -268,6 +267,21 @@ function gatedWaveGitlabPipeline(
|
|
|
268
267
|
* they measure the change with. The jobs talk to GitLab with
|
|
269
268
|
* `CHANT_FORGE_TOKEN`, a project or group access token with the `api` scope
|
|
270
269
|
* set as a masked CI/CD variable, since a job token cannot write notes.
|
|
270
|
+
*
|
|
271
|
+
* For a workspace member (#3465) the file is one the root `.gitlab-ci.yml`
|
|
272
|
+
* includes, so the jobs are `<member>-plan` and `<member>-apply`, each
|
|
273
|
+
* script starts with a `cd` into the member's directory and the report
|
|
274
|
+
* artifact is kept from there. The jobs take no `changes:` rule, since a
|
|
275
|
+
* change outside the member can reach it; the plan selects from the whole
|
|
276
|
+
* change and passes `--member`, which keeps the member's gate, note and
|
|
277
|
+
* statuses apart from the other members'. The apply's resource group is the
|
|
278
|
+
* member's own.
|
|
279
|
+
*
|
|
280
|
+
* The apply job keeps its attempt record in the job cache under the pushed
|
|
281
|
+
* commit, uploaded whether the job passed or failed, so a retry of a failed
|
|
282
|
+
* apply passes it to `pr-apply --resume` (#3543). GitLab keeps protected
|
|
283
|
+
* branches' caches apart from other branches' by default, so a merge request
|
|
284
|
+
* pipeline cannot write the record the target branch's apply reads.
|
|
271
285
|
*/
|
|
272
286
|
function prLoopGitlabPipeline(
|
|
273
287
|
env: string,
|
|
@@ -277,39 +291,53 @@ function prLoopGitlabPipeline(
|
|
|
277
291
|
options: GenerateGitlabOptions,
|
|
278
292
|
): GenerateGitlabResult {
|
|
279
293
|
const loop = options.prLoop!;
|
|
280
|
-
const
|
|
294
|
+
const member = options.member;
|
|
295
|
+
const rooted = !member || member.dir === "." || member.dir === "";
|
|
296
|
+
const [plan, apply] = prLoopJobs("gitlab", env, loop, member?.name);
|
|
297
|
+
const reportPath = member ? memberRepoPath(member, PR_LOOP_REPORT_DIR) : PR_LOOP_REPORT_DIR;
|
|
298
|
+
const planJob = member ? `${member.name}-plan` : "plan";
|
|
299
|
+
const applyJob = member ? `${member.name}-apply` : "apply";
|
|
281
300
|
const onMergeRequest = '$CI_PIPELINE_SOURCE == "merge_request_event"';
|
|
282
301
|
const target = loop.branch ? JSON.stringify(loop.branch) : "$CI_DEFAULT_BRANCH";
|
|
283
302
|
const onMerge = `$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == ${target}`;
|
|
284
|
-
|
|
303
|
+
// A member's jobs start in its directory, after the image's install lines.
|
|
304
|
+
const enter = rooted ? [] : [`cd ${memberShellDir(member!)}`];
|
|
305
|
+
const script = (job: typeof plan): string[] => [...prLoopSetup(image), ...enter, ...beforeScript, ...(job.setup ?? []), job.command, ...extraScript];
|
|
285
306
|
const stages = ["plan", "apply"];
|
|
286
307
|
const doc: Record<string, unknown> = {
|
|
287
|
-
workflow: { name: `chant-pr-${env}`, rules: [{ if: onMergeRequest }, { if: onMerge }] },
|
|
308
|
+
workflow: { name: member ? `chant-pr-${member.name}-${env}` : `chant-pr-${env}`, rules: [{ if: onMergeRequest }, { if: onMerge }] },
|
|
288
309
|
stages,
|
|
289
310
|
variables: { ...options.variables, CHANT_ENV: env, GIT_DEPTH: "0" },
|
|
290
|
-
|
|
311
|
+
[planJob]: {
|
|
291
312
|
stage: "plan",
|
|
292
313
|
image,
|
|
293
314
|
rules: [{ if: onMergeRequest }],
|
|
294
315
|
variables: plan.env,
|
|
295
316
|
script: script(plan),
|
|
296
|
-
artifacts: { when: "always", paths: [
|
|
317
|
+
artifacts: { when: "always", paths: [reportPath] },
|
|
297
318
|
},
|
|
298
|
-
|
|
319
|
+
[applyJob]: {
|
|
299
320
|
stage: "apply",
|
|
300
321
|
image,
|
|
301
|
-
resource_group: prApplyGroup(env),
|
|
322
|
+
resource_group: prApplyGroup(env, member?.name),
|
|
302
323
|
rules: [{ if: onMerge }],
|
|
303
324
|
variables: apply.env,
|
|
304
325
|
script: script(apply),
|
|
305
|
-
|
|
326
|
+
// The attempt record (#3543), kept under the pushed commit even when
|
|
327
|
+
// the job fails, so a retry finishes the apply under the same approval.
|
|
328
|
+
cache: {
|
|
329
|
+
key: prApplyRecordKey(env, "$CI_COMMIT_SHA", member?.name),
|
|
330
|
+
paths: [member ? memberRepoPath(member, PR_APPLY_RECORD) : PR_APPLY_RECORD],
|
|
331
|
+
when: "always",
|
|
332
|
+
},
|
|
333
|
+
artifacts: { when: "always", paths: [reportPath] },
|
|
306
334
|
},
|
|
307
335
|
};
|
|
308
336
|
const jobs: GeneratedJob[] = [
|
|
309
|
-
{ jobName:
|
|
310
|
-
{ jobName:
|
|
337
|
+
{ jobName: planJob, component: "merge request plan", stage: "plan", needs: [] },
|
|
338
|
+
{ jobName: applyJob, component: "merge request apply", stage: "apply", needs: [] },
|
|
311
339
|
];
|
|
312
|
-
const sections = ["workflow", "stages", "variables",
|
|
340
|
+
const sections = ["workflow", "stages", "variables", planJob, applyJob].map((key) => emitYAMLEntry(key, doc[key]));
|
|
313
341
|
return { yaml: sections.join("\n\n") + "\n", stages, jobs, env };
|
|
314
342
|
}
|
|
315
343
|
|
|
@@ -12,6 +12,7 @@ import { describe, expect, test } from "vitest";
|
|
|
12
12
|
import { parseYAML } from "@intentius/chant/yaml";
|
|
13
13
|
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
14
14
|
import { generateGitlabPipeline } from "./generate-pipeline";
|
|
15
|
+
import { PR_LOOP_SETUP } from "@intentius/chant/components/pr-pipeline";
|
|
15
16
|
|
|
16
17
|
const ESTATE: DriverComponent[] = [
|
|
17
18
|
{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] },
|
|
@@ -26,7 +27,9 @@ interface Job {
|
|
|
26
27
|
variables: Record<string, string>;
|
|
27
28
|
script: string[];
|
|
28
29
|
resource_group?: string;
|
|
30
|
+
image?: string;
|
|
29
31
|
artifacts: Record<string, unknown>;
|
|
32
|
+
cache?: Record<string, unknown>;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
describe("the GitLab merge-request pipeline", () => {
|
|
@@ -50,21 +53,67 @@ describe("the GitLab merge-request pipeline", () => {
|
|
|
50
53
|
test("plans in a merge request pipeline from its diff base, applies on a push to the default branch, one at a time", () => {
|
|
51
54
|
expect(doc.plan.rules).toEqual([{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }]);
|
|
52
55
|
expect(doc.plan.variables).toEqual({ BASE_SHA: "$CI_MERGE_REQUEST_DIFF_BASE_SHA", PR_NUMBER: "$CI_MERGE_REQUEST_IID" });
|
|
53
|
-
expect(doc.plan.script).toEqual([
|
|
56
|
+
expect(doc.plan.script).toEqual([
|
|
57
|
+
...PR_LOOP_SETUP,'chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge gitlab']);
|
|
54
58
|
expect(doc.apply.rules).toEqual([{ if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' }]);
|
|
55
59
|
expect(doc.apply.variables).toEqual({ BASE_SHA: "$CI_COMMIT_BEFORE_SHA" });
|
|
56
|
-
expect(doc.apply.script
|
|
60
|
+
expect(doc.apply.script.at(-1)).toContain("--require-review");
|
|
57
61
|
expect(doc.apply.resource_group).toBe("chant-apply-prod");
|
|
58
62
|
expect(doc.variables.GIT_DEPTH).toBe("0");
|
|
59
63
|
expect(doc.apply.artifacts).toEqual({ when: "always", paths: [".chant/pr"] });
|
|
60
64
|
});
|
|
61
65
|
|
|
66
|
+
test("runs in an image with git and installs OpenTofu, unless the caller names an image", () => {
|
|
67
|
+
expect(doc.plan.image).toBe("node:22");
|
|
68
|
+
expect(doc.apply.script[0]).toBe(PR_LOOP_SETUP[0]);
|
|
69
|
+
const own = generateGitlabPipeline(ESTATE, { env: "prod", image: "example/ci:1", prLoop: {} }).yaml;
|
|
70
|
+
expect(own).toContain("image: example/ci:1");
|
|
71
|
+
expect(own).not.toContain("install-opentofu");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("an all-zero CI_COMMIT_BEFORE_SHA falls back to the merge request diff base, then the target branch", () => {
|
|
75
|
+
const line = doc.apply.script.find((l) => l.includes("CI_MERGE_REQUEST_DIFF_BASE_SHA"))!;
|
|
76
|
+
expect(line).toContain("0000000000000000000000000000000000000000");
|
|
77
|
+
expect(line).toContain("git fetch origin $CI_DEFAULT_BRANCH");
|
|
78
|
+
expect(doc.apply.script.indexOf(line)).toBeLessThan(doc.apply.script.length - 1);
|
|
79
|
+
expect(doc.plan.script.some((l) => l.includes("CI_MERGE_REQUEST_DIFF_BASE_SHA"))).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
62
82
|
test("a named branch replaces the default branch in the apply rule", () => {
|
|
63
83
|
const yaml = generateGitlabPipeline(ESTATE, { env: "prod", prLoop: { branch: "trunk" } }).yaml;
|
|
64
84
|
expect(yaml).toContain('$CI_COMMIT_BRANCH == "trunk"');
|
|
65
85
|
});
|
|
66
86
|
|
|
87
|
+
test("the apply resumes from a record the job cache keeps for the pushed commit, failed or not (#3543)", () => {
|
|
88
|
+
expect(doc.apply.script.at(-1)).toContain("--resume .chant/pr-resume/pr-apply.json");
|
|
89
|
+
expect(doc.apply.cache).toEqual({ key: "chant-apply-prod-$CI_COMMIT_SHA", paths: [".chant/pr-resume/pr-apply.json"], when: "always" });
|
|
90
|
+
// The plan runs the merge request's code: no cache, no record.
|
|
91
|
+
expect(doc.plan.cache).toBeUndefined();
|
|
92
|
+
expect(doc.plan.script.at(-1)).not.toContain("--resume");
|
|
93
|
+
});
|
|
94
|
+
|
|
67
95
|
test("refuses a wave or promote job beside it", () => {
|
|
68
96
|
expect(() => generateGitlabPipeline(ESTATE, { prLoop: {}, gatedWaves: { gate: "g" } })).toThrow(/pull-request pipeline has no wave or promote jobs/);
|
|
69
97
|
});
|
|
98
|
+
|
|
99
|
+
test("inside a workspace member: member-named jobs that cd into the member, with no changes rule (#3465)", () => {
|
|
100
|
+
const member = { name: "network", dir: "infra/network", file: ".gitlab/ci/chant-pr-network-prod.gitlab-ci.yml" };
|
|
101
|
+
const scoped = generateGitlabPipeline(ESTATE, { env: "prod", prLoop: {}, member });
|
|
102
|
+
const mdoc = parseYAML(scoped.yaml) as Record<string, unknown> & { workflow: { name: string } };
|
|
103
|
+
expect(scoped.jobs.map((j) => j.jobName)).toEqual(["network-plan", "network-apply"]);
|
|
104
|
+
expect(mdoc.plan).toBeUndefined();
|
|
105
|
+
expect(mdoc.workflow.name).toBe("chant-pr-network-prod");
|
|
106
|
+
const plan = mdoc["network-plan"] as Job;
|
|
107
|
+
const apply = mdoc["network-apply"] as Job;
|
|
108
|
+
expect(plan.script).toContain("cd infra/network");
|
|
109
|
+
expect(plan.script.indexOf("cd infra/network")).toBeLessThan(plan.script.findIndex((l) => l.includes("pr-plan")));
|
|
110
|
+
expect(plan.script.find((l) => l.includes("pr-plan"))).toContain("--member network");
|
|
111
|
+
expect(apply.script.find((l) => l.includes("pr-apply"))).toContain("--member network");
|
|
112
|
+
// A change outside the member can reach it, so the jobs keep only their pipeline-source rules.
|
|
113
|
+
expect(plan.rules).toEqual([{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }]);
|
|
114
|
+
expect(apply.resource_group).toBe("chant-apply-network-prod");
|
|
115
|
+
expect(plan.artifacts).toEqual({ when: "always", paths: ["infra/network/.chant/pr"] });
|
|
116
|
+
expect(apply.artifacts).toEqual({ when: "always", paths: ["infra/network/.chant/pr"] });
|
|
117
|
+
expect(apply.cache).toEqual({ key: "chant-apply-network-prod-$CI_COMMIT_SHA", paths: ["infra/network/.chant/pr-resume/pr-apply.json"], when: "always" });
|
|
118
|
+
});
|
|
70
119
|
});
|