@intentius/chant-lexicon-gitlab 0.79.0 → 0.80.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.
|
@@ -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;AAEjG,OAAO,KAAK,EACV,oBAAoB,IAAI,YAAY,EACpC,wBAAwB,IAAI,qBAAqB,EACjD,uBAAuB,IAAI,oBAAoB,EAChD,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,CAyHtB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "073de2644bb1121ba60a3afcbffb9c821b972968c75e1b966446fe5a1833bd29",
|
|
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": "163341752b7f5181bd0682992b19dd2242df3f5b977dd5c6c64303b4ebc11f7e"
|
|
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.80.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.80.0",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.80.0",
|
|
72
72
|
"typescript": "^5.9.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|
|
@@ -257,3 +257,40 @@ describe("generateGitlabPipeline: options", () => {
|
|
|
257
257
|
expect((job.script as string[])[0]).toContain("--env production");
|
|
258
258
|
});
|
|
259
259
|
});
|
|
260
|
+
|
|
261
|
+
describe("generateGitlabPipeline: a promote job (#2575)", () => {
|
|
262
|
+
const components = (): DriverComponent[] => [
|
|
263
|
+
{ name: "shared-alb", dependsOn: [], deploy: [] },
|
|
264
|
+
{
|
|
265
|
+
name: "api",
|
|
266
|
+
dependsOn: ["shared-alb"],
|
|
267
|
+
deploy: [
|
|
268
|
+
{ phase: "Build", steps: [{ kind: "docker-build", context: ".", into: "dist/api.tar" }] },
|
|
269
|
+
{ phase: "Publish", steps: [{ kind: "publish-image", from: "archive:dist/api.tar" }] },
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
test("without promoteTo the pipeline is unchanged", () => {
|
|
275
|
+
const yaml = generateGitlabPipeline(components(), { env: "staging" }).yaml;
|
|
276
|
+
expect(yaml).not.toContain("promote");
|
|
277
|
+
expect(yaml).not.toContain("dist/api.tar");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("building jobs keep their archive as artifacts, and a promote stage job needs every component job", () => {
|
|
281
|
+
const result = generateGitlabPipeline(components(), { env: "staging", promoteTo: "prod" });
|
|
282
|
+
const parsed = parseYAML(result.yaml);
|
|
283
|
+
|
|
284
|
+
expect((parsed["api"] as Record<string, unknown>).artifacts).toEqual({ paths: ["dist/api.tar"] });
|
|
285
|
+
expect((parsed["shared-alb"] as Record<string, unknown>).artifacts).toEqual({ paths: ["shared-alb.outputs.json"] });
|
|
286
|
+
|
|
287
|
+
expect(parsed.stages).toEqual(["wave-1", "wave-2", "promote"]);
|
|
288
|
+
expect(result.stages).toEqual(["wave-1", "wave-2"]);
|
|
289
|
+
expect(parsed["promote-prod"]).toEqual({
|
|
290
|
+
stage: "promote",
|
|
291
|
+
image: "node:22-slim",
|
|
292
|
+
script: ["chant components promote --from staging --to prod"],
|
|
293
|
+
needs: ["api", "shared-alb"],
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
});
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
36
36
|
import { resolveComponentGraph, type DriverComponent } from "@intentius/chant/components/driver";
|
|
37
|
+
import { promoteArchivePaths } from "@intentius/chant/components/promote";
|
|
37
38
|
import type {
|
|
38
39
|
ComponentPipelineJob as GeneratedJob,
|
|
39
40
|
ComponentPipelineOptions as GenerateGitlabOptions,
|
|
@@ -82,6 +83,19 @@ export function generateGitlabPipeline(
|
|
|
82
83
|
for (const c of components) for (const dep of c.dependsOn ?? []) dependedUpon.add(dep);
|
|
83
84
|
const outputsFile = (name: string) => `${name}.outputs.json`;
|
|
84
85
|
|
|
86
|
+
// A promote job (#2575) runs apart from the deploy jobs, and a promote
|
|
87
|
+
// publishes from the build archive on disk, so each component job keeps the
|
|
88
|
+
// files its build steps wrote as artifacts. GitLab hands them to the promote
|
|
89
|
+
// job across its `needs:` edges.
|
|
90
|
+
const promoteTo = options.promoteTo;
|
|
91
|
+
const archives = new Map<string, string[]>();
|
|
92
|
+
if (promoteTo !== undefined) {
|
|
93
|
+
for (const c of components) {
|
|
94
|
+
const paths = promoteArchivePaths(c);
|
|
95
|
+
if (paths.length > 0) archives.set(c.name, paths);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
85
99
|
const stages = waves.map((_, i) => `wave-${i + 1}`);
|
|
86
100
|
const jobs: GeneratedJob[] = [];
|
|
87
101
|
const jobNameByComponent = new Map<string, string>();
|
|
@@ -125,11 +139,30 @@ export function generateGitlabPipeline(
|
|
|
125
139
|
};
|
|
126
140
|
if (needs.length > 0) jobProps.needs = needs;
|
|
127
141
|
// Publish this component's dumped outputs so dependent jobs receive it.
|
|
128
|
-
|
|
142
|
+
const artifactPaths = [
|
|
143
|
+
...(dependedUpon.has(name) ? [outputsFile(name)] : []),
|
|
144
|
+
...(archives.get(name) ?? []),
|
|
145
|
+
];
|
|
146
|
+
if (artifactPaths.length > 0) jobProps.artifacts = { paths: artifactPaths };
|
|
129
147
|
doc[jobName] = jobProps;
|
|
130
148
|
}
|
|
131
149
|
});
|
|
132
150
|
|
|
151
|
+
let promoteJob: string | undefined;
|
|
152
|
+
if (promoteTo !== undefined) {
|
|
153
|
+
promoteJob = `promote-${toJobName(promoteTo)}`;
|
|
154
|
+
if (promoteJob in doc) {
|
|
155
|
+
throw new Error(`the promote job "${promoteJob}" has the same name as a component job; rename the component`);
|
|
156
|
+
}
|
|
157
|
+
const command = options.promoteCommand ?? ["chant", "components", "promote", "--from", env, "--to", promoteTo];
|
|
158
|
+
doc[promoteJob] = {
|
|
159
|
+
stage: "promote",
|
|
160
|
+
image,
|
|
161
|
+
script: [...beforeScript, command.join(" "), ...extraScript],
|
|
162
|
+
needs: [...jobNameByComponent.values()].sort(),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
133
166
|
const sections: string[] = [];
|
|
134
167
|
// `emitYAML` returns a `\n`-led block for a non-empty sequence and an inline
|
|
135
168
|
// `[]` for an empty one, so the header needs a space in the second case.
|
|
@@ -137,12 +170,16 @@ export function generateGitlabPipeline(
|
|
|
137
170
|
// when whitespace or the line's end follows it — which is how GitLab's own
|
|
138
171
|
// reader takes it, and now how `parseYAML` does too (chant #2013).
|
|
139
172
|
sections.push("workflow:" + emitYAML(doc.workflow, 1));
|
|
140
|
-
|
|
173
|
+
// The promote stage is YAML only: `stages` in the result stays one entry
|
|
174
|
+
// per graph wave.
|
|
175
|
+
const yamlStages = promoteJob ? [...stages, "promote"] : stages;
|
|
176
|
+
sections.push(yamlStages.length > 0 ? "stages:" + emitYAML(yamlStages, 1) : "stages: []");
|
|
141
177
|
if (doc.variables) sections.push("variables:" + emitYAML(doc.variables, 1));
|
|
142
178
|
for (const job of jobs) {
|
|
143
179
|
const props = doc[job.jobName] as Record<string, unknown>;
|
|
144
180
|
sections.push(`${job.jobName}:` + emitYAML(props, 1));
|
|
145
181
|
}
|
|
182
|
+
if (promoteJob) sections.push(`${promoteJob}:` + emitYAML(doc[promoteJob], 1));
|
|
146
183
|
|
|
147
184
|
return { yaml: sections.join("\n\n") + "\n", stages, jobs, env };
|
|
148
185
|
}
|