@intentius/chant-lexicon-gitlab 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/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 +43 -0
- package/src/components/generate-pipeline.test.ts +33 -0
- package/src/components/generate-pipeline.ts +111 -0
- package/src/components/generate-pr-pipeline.test.ts +70 -0
|
@@ -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;
|
|
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,CAsJtB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "ef6e7a167965f693557802a8211365e1dee13712ef42ac41e276e9b3528dc307",
|
|
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": "01362067b7c359f45f632c89c2532c2f24ae13c6ee1b01f1fc501524df6383c1"
|
|
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.104.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.
|
|
71
|
-
"@intentius/chant-lexicon-github": "^0.
|
|
70
|
+
"@intentius/chant": "^0.104.0",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.104.0",
|
|
72
72
|
"typescript": "^5.9.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
workflow:
|
|
2
|
+
name: chant-pr-prod
|
|
3
|
+
rules:
|
|
4
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
5
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
6
|
+
|
|
7
|
+
stages:
|
|
8
|
+
- plan
|
|
9
|
+
- apply
|
|
10
|
+
|
|
11
|
+
variables:
|
|
12
|
+
CHANT_ENV: prod
|
|
13
|
+
GIT_DEPTH: '0'
|
|
14
|
+
|
|
15
|
+
plan:
|
|
16
|
+
stage: plan
|
|
17
|
+
image: node:22-slim
|
|
18
|
+
rules:
|
|
19
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
20
|
+
variables:
|
|
21
|
+
BASE_SHA: '$CI_MERGE_REQUEST_DIFF_BASE_SHA'
|
|
22
|
+
PR_NUMBER: '$CI_MERGE_REQUEST_IID'
|
|
23
|
+
script:
|
|
24
|
+
- chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge gitlab
|
|
25
|
+
artifacts:
|
|
26
|
+
when: always
|
|
27
|
+
paths:
|
|
28
|
+
- .chant/pr
|
|
29
|
+
|
|
30
|
+
apply:
|
|
31
|
+
stage: apply
|
|
32
|
+
image: node:22-slim
|
|
33
|
+
resource_group: chant-apply-prod
|
|
34
|
+
rules:
|
|
35
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
36
|
+
variables:
|
|
37
|
+
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
38
|
+
script:
|
|
39
|
+
- chant components pr-apply --base "$BASE_SHA" --env prod --gate pr-apply --output .chant/pr --forge gitlab --require-review
|
|
40
|
+
artifacts:
|
|
41
|
+
when: always
|
|
42
|
+
paths:
|
|
43
|
+
- .chant/pr
|
|
@@ -327,3 +327,36 @@ describe("generateGitlabPipeline: a promote job (#2575)", () => {
|
|
|
327
327
|
expect(() => generateGitlabPipeline(infraOnly)).not.toThrow();
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
|
+
|
|
331
|
+
describe("generateGitlabPipeline: gated waves (#3049)", () => {
|
|
332
|
+
/** net -> a, b -> app: three waves, four with a canary in front. */
|
|
333
|
+
const roots: DriverComponent[] = [
|
|
334
|
+
{ name: "net", deploy: [] },
|
|
335
|
+
{ name: "a", dependsOn: ["net"], deploy: [] },
|
|
336
|
+
{ name: "b", dependsOn: ["net"], deploy: [] },
|
|
337
|
+
{ name: "app", dependsOn: ["a"], deploy: [] },
|
|
338
|
+
];
|
|
339
|
+
|
|
340
|
+
test("one job per wave, in order, each running that wave of the gated fan-out", () => {
|
|
341
|
+
const result = generateGitlabPipeline(roots, { env: "prod", gatedWaves: { gate: "release" } });
|
|
342
|
+
const parsed = parseYAML(result.yaml) as Record<string, Record<string, unknown>>;
|
|
343
|
+
expect(result.stages).toEqual(["wave-1", "wave-2", "wave-3"]);
|
|
344
|
+
expect(parsed["wave-2"].needs).toEqual(["wave-1"]);
|
|
345
|
+
expect(parsed["wave-1"].needs).toBeUndefined();
|
|
346
|
+
expect(parsed["wave-3"].script).toEqual([
|
|
347
|
+
"chant components fan-out --base HEAD~1 --env prod --wave-gate release --wave 3 --resume .chant/fan-out.json",
|
|
348
|
+
]);
|
|
349
|
+
// The record goes to the next job even when this one stops at a gate.
|
|
350
|
+
expect(parsed["wave-1"].artifacts).toEqual({ when: "always", paths: [".chant/fan-out.json"] });
|
|
351
|
+
expect(parsed["wave-1"].variables).toEqual({ GIT_DEPTH: "0" });
|
|
352
|
+
// No per-component jobs.
|
|
353
|
+
expect(parsed.net).toBeUndefined();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test("a canary list adds a wave in front and is passed to every job", () => {
|
|
357
|
+
const result = generateGitlabPipeline(roots, { env: "prod", gatedWaves: { gate: "release", canary: ["b"], base: "$CI_COMMIT_BEFORE_SHA" } });
|
|
358
|
+
expect(result.stages).toEqual(["wave-1", "wave-2", "wave-3", "wave-4"]);
|
|
359
|
+
const parsed = parseYAML(result.yaml) as Record<string, Record<string, unknown>>;
|
|
360
|
+
expect((parsed["wave-1"].script as string[])[0]).toContain("--base $CI_COMMIT_BEFORE_SHA --env prod --wave-gate release --canary b --wave 1");
|
|
361
|
+
});
|
|
362
|
+
});
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
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
|
+
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";
|
|
38
40
|
import { memberGitlabChanges, memberRepoPath, memberShellDir } from "@intentius/chant/lexicon";
|
|
39
41
|
import type {
|
|
40
42
|
ComponentPipelineJob as GeneratedJob,
|
|
@@ -73,6 +75,19 @@ export function generateGitlabPipeline(
|
|
|
73
75
|
const { waves } = resolveComponentGraph(components);
|
|
74
76
|
const byName = new Map(components.map((c) => [c.name, c]));
|
|
75
77
|
|
|
78
|
+
if (options.prLoop) {
|
|
79
|
+
if (options.gatedWaves || options.promoteTo !== undefined) {
|
|
80
|
+
throw new Error("a pull-request pipeline has no wave or promote jobs; drop --wave-gate and --promote-to, or --pr-loop");
|
|
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
|
+
return prLoopGitlabPipeline(env, image, beforeScript, extraScript, options);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.gatedWaves) {
|
|
87
|
+
if (options.promoteTo !== undefined) throw new Error("a gated-wave pipeline has no promote job; drop --promote-to or --wave-gate");
|
|
88
|
+
return gatedWaveGitlabPipeline(components, env, image, beforeScript, extraScript, options);
|
|
89
|
+
}
|
|
90
|
+
|
|
76
91
|
// Components that something else depends on must hand their resolved outputs
|
|
77
92
|
// (stack outputs, published artifact refs) to their dependents, which run as
|
|
78
93
|
// separate jobs/processes. Each such producer dumps its outputs to a file and
|
|
@@ -202,6 +217,102 @@ export function generateGitlabPipeline(
|
|
|
202
217
|
return { yaml: sections.join("\n\n") + "\n", stages, jobs, env };
|
|
203
218
|
}
|
|
204
219
|
|
|
220
|
+
/**
|
|
221
|
+
* The gated-wave pipeline (#3049): one job per wave, each running one wave of
|
|
222
|
+
* `chant components fan-out --wave-gate`, in order through `needs:`. The
|
|
223
|
+
* attempt record is the artifact GitLab hands from job to job, kept even when
|
|
224
|
+
* a job stops at a gate (exit 3) so the re-run after `chant approve` reads it.
|
|
225
|
+
* `GIT_DEPTH: "0"` gives the job the history `--base` diffs against.
|
|
226
|
+
*/
|
|
227
|
+
function gatedWaveGitlabPipeline(
|
|
228
|
+
components: DriverComponent[],
|
|
229
|
+
env: string,
|
|
230
|
+
image: string,
|
|
231
|
+
beforeScript: string[],
|
|
232
|
+
extraScript: string[],
|
|
233
|
+
options: GenerateGitlabOptions,
|
|
234
|
+
): GenerateGitlabResult {
|
|
235
|
+
const waveJobs = gatedWaveJobs(components, env, options.gatedWaves!);
|
|
236
|
+
const stages = waveJobs.map((j) => j.jobName);
|
|
237
|
+
const doc: Record<string, unknown> = {
|
|
238
|
+
workflow: { name: `chant-components-${env}` },
|
|
239
|
+
stages,
|
|
240
|
+
variables: { ...options.variables, CHANT_ENV: env },
|
|
241
|
+
};
|
|
242
|
+
const jobs: GeneratedJob[] = [];
|
|
243
|
+
for (const job of waveJobs) {
|
|
244
|
+
jobs.push({ jobName: job.jobName, component: `wave ${job.wave}`, stage: job.jobName, needs: job.needs });
|
|
245
|
+
doc[job.jobName] = {
|
|
246
|
+
stage: job.jobName,
|
|
247
|
+
image,
|
|
248
|
+
variables: { GIT_DEPTH: "0" },
|
|
249
|
+
script: [...beforeScript, job.command.join(" "), ...extraScript],
|
|
250
|
+
...(job.needs.length > 0 ? { needs: job.needs } : {}),
|
|
251
|
+
artifacts: { when: "always", paths: [GATED_WAVE_RECORD] },
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (options.member) scopeToMember(doc, jobs, undefined, options.member, env);
|
|
255
|
+
const sections = [
|
|
256
|
+
emitYAMLEntry("workflow", doc.workflow),
|
|
257
|
+
emitYAMLEntry("stages", stages),
|
|
258
|
+
emitYAMLEntry("variables", doc.variables),
|
|
259
|
+
...jobs.map((job) => emitYAMLEntry(job.jobName, doc[job.jobName])),
|
|
260
|
+
];
|
|
261
|
+
return { yaml: sections.join("\n\n") + "\n", stages, jobs, env };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The merge-request pipeline (#3183): `plan` in each merge request pipeline,
|
|
266
|
+
* `apply` on each push to the target branch, one apply at a time per
|
|
267
|
+
* environment (`resource_group`). `GIT_DEPTH: "0"` gives both the history
|
|
268
|
+
* they measure the change with. The jobs talk to GitLab with
|
|
269
|
+
* `CHANT_FORGE_TOKEN`, a project or group access token with the `api` scope
|
|
270
|
+
* set as a masked CI/CD variable, since a job token cannot write notes.
|
|
271
|
+
*/
|
|
272
|
+
function prLoopGitlabPipeline(
|
|
273
|
+
env: string,
|
|
274
|
+
image: string,
|
|
275
|
+
beforeScript: string[],
|
|
276
|
+
extraScript: string[],
|
|
277
|
+
options: GenerateGitlabOptions,
|
|
278
|
+
): GenerateGitlabResult {
|
|
279
|
+
const loop = options.prLoop!;
|
|
280
|
+
const [plan, apply] = prLoopJobs("gitlab", env, loop);
|
|
281
|
+
const onMergeRequest = '$CI_PIPELINE_SOURCE == "merge_request_event"';
|
|
282
|
+
const target = loop.branch ? JSON.stringify(loop.branch) : "$CI_DEFAULT_BRANCH";
|
|
283
|
+
const onMerge = `$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == ${target}`;
|
|
284
|
+
const script = (job: typeof plan): string[] => [...beforeScript, job.command, ...extraScript];
|
|
285
|
+
const stages = ["plan", "apply"];
|
|
286
|
+
const doc: Record<string, unknown> = {
|
|
287
|
+
workflow: { name: `chant-pr-${env}`, rules: [{ if: onMergeRequest }, { if: onMerge }] },
|
|
288
|
+
stages,
|
|
289
|
+
variables: { ...options.variables, CHANT_ENV: env, GIT_DEPTH: "0" },
|
|
290
|
+
plan: {
|
|
291
|
+
stage: "plan",
|
|
292
|
+
image,
|
|
293
|
+
rules: [{ if: onMergeRequest }],
|
|
294
|
+
variables: plan.env,
|
|
295
|
+
script: script(plan),
|
|
296
|
+
artifacts: { when: "always", paths: [PR_LOOP_REPORT_DIR] },
|
|
297
|
+
},
|
|
298
|
+
apply: {
|
|
299
|
+
stage: "apply",
|
|
300
|
+
image,
|
|
301
|
+
resource_group: prApplyGroup(env),
|
|
302
|
+
rules: [{ if: onMerge }],
|
|
303
|
+
variables: apply.env,
|
|
304
|
+
script: script(apply),
|
|
305
|
+
artifacts: { when: "always", paths: [PR_LOOP_REPORT_DIR] },
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
const jobs: GeneratedJob[] = [
|
|
309
|
+
{ jobName: "plan", component: "merge request plan", stage: "plan", needs: [] },
|
|
310
|
+
{ jobName: "apply", component: "merge request apply", stage: "apply", needs: [] },
|
|
311
|
+
];
|
|
312
|
+
const sections = ["workflow", "stages", "variables", "plan", "apply"].map((key) => emitYAMLEntry(key, doc[key]));
|
|
313
|
+
return { yaml: sections.join("\n\n") + "\n", stages, jobs, env };
|
|
314
|
+
}
|
|
315
|
+
|
|
205
316
|
/**
|
|
206
317
|
* Scope a pipeline to one workspace member (#2542, #2524 D19), in place on
|
|
207
318
|
* `doc` and `jobs`. Returns the promote job's new name.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generated merge-request pipeline for GitLab CI (#3183): a plan job in
|
|
3
|
+
* each merge request pipeline, an apply job on each push to the default
|
|
4
|
+
* branch. The whole file has a golden, and it validates against the GitLab
|
|
5
|
+
* CI schema this lexicon vendors.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { describe, expect, test } from "vitest";
|
|
12
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
13
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
14
|
+
import { generateGitlabPipeline } from "./generate-pipeline";
|
|
15
|
+
|
|
16
|
+
const ESTATE: DriverComponent[] = [
|
|
17
|
+
{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] },
|
|
18
|
+
{ name: "app", dependsOn: ["net"], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "app" }] }] },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const GOLDEN = join(import.meta.dirname, "__fixtures__", "pr-loop.gitlab.golden.yml");
|
|
22
|
+
const SCHEMA = join(import.meta.dirname, "..", "codegen", "vendored", "gitlab-ci-schema.json");
|
|
23
|
+
|
|
24
|
+
interface Job {
|
|
25
|
+
rules: Array<{ if: string }>;
|
|
26
|
+
variables: Record<string, string>;
|
|
27
|
+
script: string[];
|
|
28
|
+
resource_group?: string;
|
|
29
|
+
artifacts: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("the GitLab merge-request pipeline", () => {
|
|
33
|
+
const result = generateGitlabPipeline(ESTATE, { env: "prod", prLoop: {} });
|
|
34
|
+
const doc = parseYAML(result.yaml) as { workflow: { rules: unknown[] }; variables: Record<string, string>; plan: Job; apply: Job };
|
|
35
|
+
|
|
36
|
+
test("matches its golden", () => {
|
|
37
|
+
if (process.env.UPDATE_GOLDEN) writeFileSync(GOLDEN, result.yaml);
|
|
38
|
+
expect(result.yaml).toBe(readFileSync(GOLDEN, "utf-8"));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("validates against the vendored GitLab CI schema", () => {
|
|
42
|
+
const req = createRequire(import.meta.url);
|
|
43
|
+
const mod = req("ajv") as { default?: unknown };
|
|
44
|
+
const Ajv = (mod.default ?? mod) as new (opts: object) => { compile(s: object): ((d: unknown) => boolean) & { errors?: unknown } };
|
|
45
|
+
const validate = new Ajv({ strict: false, allErrors: true }).compile(JSON.parse(readFileSync(SCHEMA, "utf-8")));
|
|
46
|
+
const ok = validate(doc);
|
|
47
|
+
expect(ok, JSON.stringify(validate.errors, null, 2)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("plans in a merge request pipeline from its diff base, applies on a push to the default branch, one at a time", () => {
|
|
51
|
+
expect(doc.plan.rules).toEqual([{ if: '$CI_PIPELINE_SOURCE == "merge_request_event"' }]);
|
|
52
|
+
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(['chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge gitlab']);
|
|
54
|
+
expect(doc.apply.rules).toEqual([{ if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' }]);
|
|
55
|
+
expect(doc.apply.variables).toEqual({ BASE_SHA: "$CI_COMMIT_BEFORE_SHA" });
|
|
56
|
+
expect(doc.apply.script[0]).toContain("--require-review");
|
|
57
|
+
expect(doc.apply.resource_group).toBe("chant-apply-prod");
|
|
58
|
+
expect(doc.variables.GIT_DEPTH).toBe("0");
|
|
59
|
+
expect(doc.apply.artifacts).toEqual({ when: "always", paths: [".chant/pr"] });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("a named branch replaces the default branch in the apply rule", () => {
|
|
63
|
+
const yaml = generateGitlabPipeline(ESTATE, { env: "prod", prLoop: { branch: "trunk" } }).yaml;
|
|
64
|
+
expect(yaml).toContain('$CI_COMMIT_BRANCH == "trunk"');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("refuses a wave or promote job beside it", () => {
|
|
68
|
+
expect(() => generateGitlabPipeline(ESTATE, { prLoop: {}, gatedWaves: { gate: "g" } })).toThrow(/pull-request pipeline has no wave or promote jobs/);
|
|
69
|
+
});
|
|
70
|
+
});
|