@intentius/chant-lexicon-gitlab 0.124.31 → 0.124.33
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/codegen/docs-cli.js +2 -0
- package/dist/codegen/docs.js +157 -0
- package/dist/codegen/fetch.js +93 -0
- package/dist/codegen/generate-cli.js +30 -0
- package/dist/codegen/generate-lexicon.js +36 -0
- package/dist/codegen/generate-typescript.js +105 -0
- package/dist/codegen/generate.js +125 -0
- package/dist/codegen/naming.js +48 -0
- package/dist/codegen/package.js +41 -0
- package/dist/codegen/parse.js +539 -0
- package/dist/codegen/patches.js +84 -0
- package/dist/codegen/typecheck.js +4 -0
- package/dist/components/ci-jobs.js +16 -0
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-pipeline.js +294 -0
- package/dist/components/generate-op-waves-pipeline.js +125 -0
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +376 -0
- package/dist/composites/catalog.js +418 -0
- package/dist/composites/docker-build.js +38 -0
- package/dist/composites/index.js +5 -0
- package/dist/composites/mr-plan-report.js +42 -0
- package/dist/composites/node-pipeline.js +67 -0
- package/dist/composites/python-pipeline.js +45 -0
- package/dist/composites/review-app.js +32 -0
- package/dist/coverage.js +35 -0
- package/dist/generated/index.js +25 -0
- package/dist/generated/runtime.js +4 -0
- package/dist/graph-edges.js +202 -0
- package/dist/import/generator.js +196 -0
- package/dist/import/parser.js +111 -0
- package/dist/index.js +26 -0
- package/dist/integrity.json +3 -3
- package/dist/intrinsics.js +33 -0
- package/dist/lint/audit-catalog.js +53 -0
- package/dist/lint/audit-lineage.js +64 -0
- package/dist/lint/post-synth/index.js +84 -0
- package/dist/lint/post-synth/wgl010.js +34 -0
- package/dist/lint/post-synth/wgl011.js +48 -0
- package/dist/lint/post-synth/wgl012.d.ts.map +1 -1
- package/dist/lint/post-synth/wgl012.js +68 -0
- package/dist/lint/post-synth/wgl013.js +57 -0
- package/dist/lint/post-synth/wgl014.js +43 -0
- package/dist/lint/post-synth/wgl015.js +70 -0
- package/dist/lint/post-synth/wgl016.js +70 -0
- package/dist/lint/post-synth/wgl017.js +38 -0
- package/dist/lint/post-synth/wgl018.js +34 -0
- package/dist/lint/post-synth/wgl019.js +43 -0
- package/dist/lint/post-synth/wgl020.js +47 -0
- package/dist/lint/post-synth/wgl021.js +52 -0
- package/dist/lint/post-synth/wgl022.js +38 -0
- package/dist/lint/post-synth/wgl023.js +44 -0
- package/dist/lint/post-synth/wgl024.js +41 -0
- package/dist/lint/post-synth/wgl025.js +42 -0
- package/dist/lint/post-synth/wgl026.js +58 -0
- package/dist/lint/post-synth/wgl027.js +49 -0
- package/dist/lint/post-synth/wgl028.js +58 -0
- package/dist/lint/post-synth/wgl029.js +55 -0
- package/dist/lint/post-synth/wgl030.js +43 -0
- package/dist/lint/post-synth/wgl031.js +33 -0
- package/dist/lint/post-synth/wgl032.js +64 -0
- package/dist/lint/post-synth/wgl033.js +32 -0
- package/dist/lint/post-synth/wgl034.js +37 -0
- package/dist/lint/post-synth/wgl035.js +40 -0
- package/dist/lint/post-synth/wgl036.js +38 -0
- package/dist/lint/post-synth/wgl037.js +41 -0
- package/dist/lint/post-synth/wgl038.js +43 -0
- package/dist/lint/post-synth/wgl039.js +34 -0
- package/dist/lint/post-synth/wgl040.js +35 -0
- package/dist/lint/post-synth/wgl041.js +53 -0
- package/dist/lint/post-synth/wgl042.js +41 -0
- package/dist/lint/post-synth/wgl043.js +39 -0
- package/dist/lint/post-synth/wgl044.js +35 -0
- package/dist/lint/post-synth/wgl045.js +54 -0
- package/dist/lint/post-synth/wgl046.js +40 -0
- package/dist/lint/post-synth/wgl047.js +34 -0
- package/dist/lint/post-synth/wgl048.js +32 -0
- package/dist/lint/post-synth/wgl049.js +53 -0
- package/dist/lint/post-synth/wgl050.js +42 -0
- package/dist/lint/post-synth/yaml-helpers.js +432 -0
- package/dist/lint/rules/artifact-no-expiry.js +53 -0
- package/dist/lint/rules/data/known-include-sources.js +44 -0
- package/dist/lint/rules/data/untrusted-variables.js +30 -0
- package/dist/lint/rules/deprecated-only-except.js +46 -0
- package/dist/lint/rules/import-source.js +61 -0
- package/dist/lint/rules/index.js +7 -0
- package/dist/lint/rules/missing-script.js +67 -0
- package/dist/lint/rules/missing-stage.js +60 -0
- package/dist/lsp/completions.d.ts.map +1 -1
- package/dist/lsp/completions.js +18 -0
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/lsp/hover.js +33 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/context-tools.js +262 -0
- package/dist/migrate/from-github/actions/index.js +17 -0
- package/dist/migrate/from-github/actions/registry.js +61 -0
- package/dist/migrate/from-github/actions/tier-1.js +298 -0
- package/dist/migrate/from-github/actions/tier-2.js +241 -0
- package/dist/migrate/from-github/actions/tier-3.js +96 -0
- package/dist/migrate/from-github/composites/patterns.js +151 -0
- package/dist/migrate/from-github/composites/rewriter.js +22 -0
- package/dist/migrate/from-github/diagnostics.js +53 -0
- package/dist/migrate/from-github/emit-yaml.js +121 -0
- package/dist/migrate/from-github/expressions.js +275 -0
- package/dist/migrate/from-github/index.js +96 -0
- package/dist/migrate/from-github/provenance.js +35 -0
- package/dist/migrate/from-github/rules.js +74 -0
- package/dist/migrate/from-github/security.js +189 -0
- package/dist/migrate/from-github/stages.js +156 -0
- package/dist/migrate/from-github/transformer.js +633 -0
- package/dist/op/activities/gitlab.js +38 -0
- package/dist/op/activities/index.js +10 -0
- package/dist/op/builders.js +26 -0
- package/dist/package-cli.js +21 -0
- package/dist/plugin.js +536 -0
- package/dist/rules/wgl012.ts +2 -2
- package/dist/serializer.js +197 -0
- package/dist/testdata/create-fixture.js +40 -0
- package/dist/testdata/load-fixtures.js +20 -0
- package/dist/validate-cli.js +16 -0
- package/dist/validate.js +29 -0
- package/dist/variables.js +30 -0
- package/package.json +8 -8
- package/src/codegen/fetch.ts +2 -2
- package/src/codegen/package.ts +2 -2
- package/src/components/__fixtures__/pr-loop-build.gitlab.golden.yml +3 -3
- package/src/components/__fixtures__/pr-loop-record.gitlab.golden.yml +46 -0
- package/src/components/__fixtures__/pr-loop-waves.gitlab.golden.yml +101 -0
- package/src/components/__fixtures__/pr-loop.gitlab.golden.yml +3 -3
- package/src/components/generate-op-pipeline.test.ts +10 -0
- package/src/components/generate-op-pipeline.ts +8 -1
- package/src/components/generate-pipeline.ts +71 -30
- package/src/components/generate-pr-pipeline.test.ts +61 -18
- package/src/components/generate-pr-waves-pipeline.test.ts +88 -0
- package/src/lint/post-synth/wgl012.ts +2 -2
- package/src/lsp/completions.ts +2 -1
- package/src/lsp/hover.ts +2 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitLab CI YAML serializer.
|
|
3
|
+
*
|
|
4
|
+
* Converts Chant declarables to .gitlab-ci.yml YAML output.
|
|
5
|
+
* Uses snake_case keys (GitLab CI convention) and produces
|
|
6
|
+
* valid YAML without a library dependency — the CI schema is
|
|
7
|
+
* simple enough for a direct emitter.
|
|
8
|
+
*/
|
|
9
|
+
import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
10
|
+
import { walkValue } from "@intentius/chant/serializer-walker";
|
|
11
|
+
import { INTRINSIC_MARKER } from "@intentius/chant/intrinsic";
|
|
12
|
+
import { emitYAMLEntry } from "@intentius/chant/yaml";
|
|
13
|
+
/**
|
|
14
|
+
* `#` lines for a comment, one per line of the string (#3669). Only the file
|
|
15
|
+
* header and job comments exist, and both sit at column 0.
|
|
16
|
+
*/
|
|
17
|
+
function commentLines(comment) {
|
|
18
|
+
return comment.split("\n").map((l) => `#${l.trimEnd() ? ` ${l.trimEnd()}` : ""}`).join("\n");
|
|
19
|
+
}
|
|
20
|
+
/** An entity's props without `comment`, and the comment if it is a non-empty string. */
|
|
21
|
+
function splitComment(entity) {
|
|
22
|
+
const props = isResourceDeclarable(entity) ? entity.props : undefined;
|
|
23
|
+
if (!props || !("comment" in props))
|
|
24
|
+
return { props };
|
|
25
|
+
const { comment, ...rest } = props;
|
|
26
|
+
return { props: rest, comment: typeof comment === "string" && comment !== "" ? comment : undefined };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* GitLab CI visitor for the generic serializer walker.
|
|
30
|
+
*/
|
|
31
|
+
function gitlabVisitor(entityNames) {
|
|
32
|
+
return {
|
|
33
|
+
attrRef: (name, _attr) => name,
|
|
34
|
+
resourceRef: (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(),
|
|
35
|
+
propertyDeclarable: (entity, walk) => {
|
|
36
|
+
if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const props = entity.props;
|
|
40
|
+
const result = {};
|
|
41
|
+
for (const [key, value] of Object.entries(props)) {
|
|
42
|
+
if (value !== undefined) {
|
|
43
|
+
result[key] = walk(value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Pre-process values to convert intrinsics to their YAML representation
|
|
52
|
+
* before the walker (which would call toJSON instead of toYAML).
|
|
53
|
+
*
|
|
54
|
+
* IMPORTANT: Must not touch Declarable objects — their identity markers
|
|
55
|
+
* (DECLARABLE_MARKER, entityType, kind, props) are non-enumerable and
|
|
56
|
+
* would be stripped by Object.entries(), producing empty `{}` output.
|
|
57
|
+
*/
|
|
58
|
+
function preprocessIntrinsics(value) {
|
|
59
|
+
if (value === null || value === undefined)
|
|
60
|
+
return value;
|
|
61
|
+
if (typeof value === "object" && INTRINSIC_MARKER in value) {
|
|
62
|
+
if ("toYAML" in value && typeof value.toYAML === "function") {
|
|
63
|
+
return value.toYAML();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Leave Declarables untouched — the walker handles them
|
|
67
|
+
if (typeof value === "object" && value !== null && "entityType" in value) {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value.map(preprocessIntrinsics);
|
|
72
|
+
}
|
|
73
|
+
if (typeof value === "object") {
|
|
74
|
+
const result = {};
|
|
75
|
+
for (const [k, v] of Object.entries(value)) {
|
|
76
|
+
result[k] = preprocessIntrinsics(v);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Convert a value to YAML-compatible form using the walker.
|
|
84
|
+
*/
|
|
85
|
+
function toYAMLValue(value, entityNames) {
|
|
86
|
+
const preprocessed = preprocessIntrinsics(value);
|
|
87
|
+
return walkValue(preprocessed, entityNames, gitlabVisitor(entityNames));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* GitLab CI YAML serializer implementation.
|
|
91
|
+
*/
|
|
92
|
+
export const gitlabSerializer = {
|
|
93
|
+
name: "gitlab",
|
|
94
|
+
rulePrefix: "WGL",
|
|
95
|
+
serialize(entities, _outputs) {
|
|
96
|
+
// Build reverse map: entity → name
|
|
97
|
+
const entityNames = new Map();
|
|
98
|
+
for (const [name, entity] of entities) {
|
|
99
|
+
entityNames.set(entity, name);
|
|
100
|
+
}
|
|
101
|
+
const sections = [];
|
|
102
|
+
// Separate entities by type
|
|
103
|
+
const jobs = [];
|
|
104
|
+
const pipelines = [];
|
|
105
|
+
const defaults = [];
|
|
106
|
+
const workflows = [];
|
|
107
|
+
const others = [];
|
|
108
|
+
for (const [name, entity] of entities) {
|
|
109
|
+
if (isPropertyDeclarable(entity))
|
|
110
|
+
continue; // Skip property-only entities
|
|
111
|
+
const entityType = entity.entityType;
|
|
112
|
+
if (entityType === "GitLab::CI::Job") {
|
|
113
|
+
jobs.push([name, entity]);
|
|
114
|
+
}
|
|
115
|
+
else if (entityType === "GitLab::CI::Default") {
|
|
116
|
+
defaults.push([name, entity]);
|
|
117
|
+
}
|
|
118
|
+
else if (entityType === "GitLab::CI::Workflow") {
|
|
119
|
+
workflows.push([name, entity]);
|
|
120
|
+
}
|
|
121
|
+
else if (entityType === "GitLab::CI::Pipeline") {
|
|
122
|
+
pipelines.push([name, entity]);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
others.push([name, entity]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// The Pipeline entity's top-level keys (#3669). More than one is merged
|
|
129
|
+
// in declaration order: comments joined, includes concatenated, stages
|
|
130
|
+
// in first-listed order, later variables overriding earlier ones.
|
|
131
|
+
const headers = [];
|
|
132
|
+
const includes = [];
|
|
133
|
+
const declaredStages = [];
|
|
134
|
+
let variables;
|
|
135
|
+
for (const [, entity] of pipelines) {
|
|
136
|
+
const { props, comment } = splitComment(entity);
|
|
137
|
+
if (comment)
|
|
138
|
+
headers.push(comment);
|
|
139
|
+
const converted = toYAMLValue(props, entityNames);
|
|
140
|
+
if (!converted)
|
|
141
|
+
continue;
|
|
142
|
+
if (converted.include !== undefined) {
|
|
143
|
+
includes.push(...(Array.isArray(converted.include) ? converted.include : [converted.include]));
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(converted.stages))
|
|
146
|
+
declaredStages.push(...converted.stages.map(String));
|
|
147
|
+
if (converted.variables && typeof converted.variables === "object") {
|
|
148
|
+
variables = { ...variables, ...converted.variables };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (headers.length > 0)
|
|
152
|
+
sections.push(commentLines(headers.join("\n\n")));
|
|
153
|
+
if (includes.length > 0)
|
|
154
|
+
sections.push(emitYAMLEntry("include", includes));
|
|
155
|
+
// Stages: a Pipeline's explicit list first, in its order, then any stage
|
|
156
|
+
// a job names that the list leaves out, in first use. Without a list,
|
|
157
|
+
// that is the jobs' stages alone.
|
|
158
|
+
const stages = new Set(declaredStages);
|
|
159
|
+
for (const [, entity] of jobs) {
|
|
160
|
+
const props = isResourceDeclarable(entity) ? entity.props : undefined;
|
|
161
|
+
if (props?.stage && typeof props.stage === "string") {
|
|
162
|
+
stages.add(props.stage);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (stages.size > 0) {
|
|
166
|
+
sections.push(emitYAMLEntry("stages", [...stages]));
|
|
167
|
+
}
|
|
168
|
+
if (variables && Object.keys(variables).length > 0)
|
|
169
|
+
sections.push(emitYAMLEntry("variables", variables));
|
|
170
|
+
// Emit defaults
|
|
171
|
+
for (const [, entity] of defaults) {
|
|
172
|
+
const converted = toYAMLValue((isResourceDeclarable(entity) ? entity.props : undefined), entityNames);
|
|
173
|
+
if (converted) {
|
|
174
|
+
sections.push(emitYAMLEntry("default", converted));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Emit workflow
|
|
178
|
+
for (const [, entity] of workflows) {
|
|
179
|
+
const converted = toYAMLValue((isResourceDeclarable(entity) ? entity.props : undefined), entityNames);
|
|
180
|
+
if (converted) {
|
|
181
|
+
sections.push(emitYAMLEntry("workflow", converted));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Emit jobs
|
|
185
|
+
for (const [name, entity] of jobs) {
|
|
186
|
+
const { props, comment } = splitComment(entity);
|
|
187
|
+
const converted = toYAMLValue(props, entityNames);
|
|
188
|
+
if (converted) {
|
|
189
|
+
// Convert job name from camelCase to kebab-case for YAML
|
|
190
|
+
const yamlName = name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
191
|
+
const entry = emitYAMLEntry(yamlName, converted);
|
|
192
|
+
sections.push(comment ? `${commentLines(comment)}\n${entry}` : entry);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return sections.join("\n\n") + "\n";
|
|
196
|
+
},
|
|
197
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Script to create a minimal CI schema fixture for tests.
|
|
4
|
+
* Run once: npx tsx src/testdata/create-fixture.ts
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
const schema = JSON.parse(readFileSync(join(homedir(), ".chant", "gitlab-ci-schema.json"), "utf-8"));
|
|
10
|
+
const neededDefs = [
|
|
11
|
+
"job_template", "image", "services", "artifacts", "cache_item",
|
|
12
|
+
"rules", "retry", "allow_failure", "parallel", "include_item",
|
|
13
|
+
"when", "retry_errors", "workflowAutoCancel",
|
|
14
|
+
"before_script", "after_script", "script", "optional_script",
|
|
15
|
+
"globalVariables", "jobVariables", "rulesVariables",
|
|
16
|
+
"id_tokens", "secrets", "timeout", "start_in",
|
|
17
|
+
];
|
|
18
|
+
const defs = {};
|
|
19
|
+
for (const name of neededDefs) {
|
|
20
|
+
if (schema.definitions?.[name]) {
|
|
21
|
+
defs[name] = schema.definitions[name];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const keepProps = ["default", "workflow", "stages", "variables", "include"];
|
|
25
|
+
const filteredProps = {};
|
|
26
|
+
for (const key of keepProps) {
|
|
27
|
+
if (schema.properties?.[key])
|
|
28
|
+
filteredProps[key] = schema.properties[key];
|
|
29
|
+
}
|
|
30
|
+
const minimal = {
|
|
31
|
+
definitions: defs,
|
|
32
|
+
properties: filteredProps,
|
|
33
|
+
patternProperties: schema.patternProperties || {},
|
|
34
|
+
required: schema.required || [],
|
|
35
|
+
};
|
|
36
|
+
const outPath = join(import.meta.dirname, "ci-schema-fixture.json");
|
|
37
|
+
writeFileSync(outPath, JSON.stringify(minimal, null, 2));
|
|
38
|
+
console.log(`Fixture written to ${outPath}`);
|
|
39
|
+
console.log(`Definitions: ${Object.keys(defs).length}`);
|
|
40
|
+
console.log(`Missing: ${neededDefs.filter(n => !defs[n]).join(", ") || "none"}`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test fixture loader for the GitLab CI lexicon.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
/**
|
|
7
|
+
* Load the minimal CI schema fixture for offline testing.
|
|
8
|
+
*/
|
|
9
|
+
export function loadSchemaFixture() {
|
|
10
|
+
const fixturePath = join(import.meta.dirname, "ci-schema-fixture.json");
|
|
11
|
+
return Buffer.from(readFileSync(fixturePath));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Load the schema fixture as a Map compatible with generatePipeline.
|
|
15
|
+
*/
|
|
16
|
+
export function loadSchemaFixtureMap() {
|
|
17
|
+
const schemas = new Map();
|
|
18
|
+
schemas.set("GitLab::CI::Pipeline", loadSchemaFixture());
|
|
19
|
+
return schemas;
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Thin entry point for `npm run validate` in lexicon-gitlab.
|
|
4
|
+
*/
|
|
5
|
+
import { validate } from "./validate.js";
|
|
6
|
+
const result = await validate();
|
|
7
|
+
for (const check of result.checks) {
|
|
8
|
+
const status = check.ok ? "OK" : "FAIL";
|
|
9
|
+
const msg = check.error ? ` — ${check.error}` : "";
|
|
10
|
+
console.error(` [${status}] ${check.name}${msg}`);
|
|
11
|
+
}
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
console.error("Validation failed");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
console.error("All validation checks passed.");
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate generated lexicon-gitlab artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the core validation framework
|
|
5
|
+
* with GitLab-specific configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { dirname } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { validateLexiconArtifacts } from "@intentius/chant/codegen/validate";
|
|
10
|
+
const REQUIRED_NAMES = [
|
|
11
|
+
"Job", "Default", "Workflow",
|
|
12
|
+
"Artifacts", "Cache", "Image", "Service", "Rule", "Retry",
|
|
13
|
+
"AllowFailure", "Parallel", "Include", "Release",
|
|
14
|
+
"Environment", "Trigger", "AutoCancel",
|
|
15
|
+
"WorkflowRule", "Need", "Inherit",
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Validate the generated lexicon-gitlab artifacts.
|
|
19
|
+
*
|
|
20
|
+
* @param opts.basePath - Override the package directory (defaults to lexicon-gitlab package root)
|
|
21
|
+
*/
|
|
22
|
+
export async function validate(opts) {
|
|
23
|
+
const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
|
|
24
|
+
return validateLexiconArtifacts({
|
|
25
|
+
lexiconJsonFilename: "lexicon-gitlab.json",
|
|
26
|
+
requiredNames: REQUIRED_NAMES,
|
|
27
|
+
basePath,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitLab CI/CD predefined variable references.
|
|
3
|
+
*
|
|
4
|
+
* These expand to $CI_* variable strings in the serialized YAML.
|
|
5
|
+
* Provided for type-safe access and autocompletion.
|
|
6
|
+
*/
|
|
7
|
+
export const CI = {
|
|
8
|
+
CommitBranch: "$CI_COMMIT_BRANCH",
|
|
9
|
+
CommitRef: "$CI_COMMIT_REF_NAME",
|
|
10
|
+
CommitRefSlug: "$CI_COMMIT_REF_SLUG",
|
|
11
|
+
CommitSha: "$CI_COMMIT_SHA",
|
|
12
|
+
CommitTag: "$CI_COMMIT_TAG",
|
|
13
|
+
DefaultBranch: "$CI_DEFAULT_BRANCH",
|
|
14
|
+
Environment: "$CI_ENVIRONMENT_NAME",
|
|
15
|
+
EnvironmentSlug: "$CI_ENVIRONMENT_SLUG",
|
|
16
|
+
JobId: "$CI_JOB_ID",
|
|
17
|
+
JobName: "$CI_JOB_NAME",
|
|
18
|
+
JobStage: "$CI_JOB_STAGE",
|
|
19
|
+
MergeRequestIid: "$CI_MERGE_REQUEST_IID",
|
|
20
|
+
PipelineId: "$CI_PIPELINE_ID",
|
|
21
|
+
PipelineSource: "$CI_PIPELINE_SOURCE",
|
|
22
|
+
ProjectDir: "$CI_PROJECT_DIR",
|
|
23
|
+
ProjectId: "$CI_PROJECT_ID",
|
|
24
|
+
ProjectName: "$CI_PROJECT_NAME",
|
|
25
|
+
ProjectPath: "$CI_PROJECT_PATH",
|
|
26
|
+
Registry: "$CI_REGISTRY",
|
|
27
|
+
RegistryImage: "$CI_REGISTRY_IMAGE",
|
|
28
|
+
RegistryUser: "$CI_REGISTRY_USER",
|
|
29
|
+
RegistryPassword: "$CI_REGISTRY_PASSWORD",
|
|
30
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gitlab",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.33",
|
|
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",
|
|
@@ -32,22 +32,22 @@
|
|
|
32
32
|
".": {
|
|
33
33
|
"development": "./src/index.ts",
|
|
34
34
|
"types": "./dist/index.d.ts",
|
|
35
|
-
"default": "./
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
36
|
},
|
|
37
37
|
"./*": {
|
|
38
38
|
"development": "./src/*.ts",
|
|
39
39
|
"types": "./dist/*.d.ts",
|
|
40
|
-
"default": "./
|
|
40
|
+
"default": "./dist/*.js"
|
|
41
41
|
},
|
|
42
42
|
"./lint/post-synth": {
|
|
43
43
|
"development": "./src/lint/post-synth/index.ts",
|
|
44
44
|
"types": "./dist/lint/post-synth/index.d.ts",
|
|
45
|
-
"default": "./
|
|
45
|
+
"default": "./dist/lint/post-synth/index.js"
|
|
46
46
|
},
|
|
47
47
|
"./op/activities": {
|
|
48
48
|
"development": "./src/op/activities/index.ts",
|
|
49
49
|
"types": "./dist/op/activities/index.d.ts",
|
|
50
|
-
"default": "./
|
|
50
|
+
"default": "./dist/op/activities/index.js"
|
|
51
51
|
},
|
|
52
52
|
"./manifest": "./dist/manifest.json",
|
|
53
53
|
"./meta": "./dist/meta.json",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"validate": "tsx src/validate-cli.ts",
|
|
60
60
|
"docs": "tsx src/codegen/docs-cli.ts",
|
|
61
61
|
"prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
|
|
62
|
-
"build": "../../scripts/tsc7.sh -p tsconfig.build.json && tsc-alias -p tsconfig.build.json
|
|
62
|
+
"build": "../../scripts/tsc7.sh -p tsconfig.build.json --sourceMap false && tsc-alias -p tsconfig.build.json"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@intentius/chant": "*",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"typescript": "^5.9.3"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@intentius/chant": "^0.124.
|
|
71
|
-
"@intentius/chant-lexicon-github": "^0.124.
|
|
70
|
+
"@intentius/chant": "^0.124.33",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.124.33"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
74
74
|
"@intentius/chant-lexicon-github": {
|
package/src/codegen/fetch.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { join, dirname } from "path";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { readFileSync, writeFileSync } from "fs";
|
|
9
|
-
import {
|
|
9
|
+
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
10
10
|
import { fetchWithCache, clearCacheFile } from "@intentius/chant/codegen/fetch";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -41,7 +41,7 @@ export function getCachePath(): string {
|
|
|
41
41
|
* committed copy is equivalent and network-free.
|
|
42
42
|
*/
|
|
43
43
|
export function getVendoredSchemaPath(): string {
|
|
44
|
-
return join(
|
|
44
|
+
return join(moduleSourceDir(import.meta.url), "vendored", "gitlab-ci-schema.json");
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
package/src/codegen/package.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { readFileSync } from "fs";
|
|
7
7
|
import { gitlabPlugin } from "../plugin";
|
|
8
8
|
import { join, dirname } from "path";
|
|
9
|
-
import {
|
|
9
|
+
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
10
10
|
import type { IntrinsicDef } from "@intentius/chant/lexicon";
|
|
11
11
|
import {
|
|
12
12
|
packagePipeline,
|
|
@@ -18,7 +18,7 @@ import { generate } from "./generate";
|
|
|
18
18
|
|
|
19
19
|
export type { PackageOptions, PackageResult };
|
|
20
20
|
|
|
21
|
-
const pkgDir = dirname(
|
|
21
|
+
const pkgDir = dirname(moduleSourceDir(import.meta.url));
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Package the GitLab lexicon into a distributable BundleSpec.
|
|
@@ -24,7 +24,7 @@ plan:
|
|
|
24
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)
|
|
25
25
|
- chant build --env prod
|
|
26
26
|
- chant lint
|
|
27
|
-
- chant
|
|
27
|
+
- chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge gitlab
|
|
28
28
|
artifacts:
|
|
29
29
|
when: always
|
|
30
30
|
paths:
|
|
@@ -40,10 +40,10 @@ apply:
|
|
|
40
40
|
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
41
41
|
script:
|
|
42
42
|
- 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)
|
|
43
|
-
- 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
|
|
43
|
+
- 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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
44
44
|
- chant build --env prod
|
|
45
45
|
- chant lint
|
|
46
|
-
- chant
|
|
46
|
+
- chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab
|
|
47
47
|
cache:
|
|
48
48
|
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
49
49
|
paths:
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
- record
|
|
10
|
+
|
|
11
|
+
variables:
|
|
12
|
+
CHANT_ENV: prod
|
|
13
|
+
GIT_DEPTH: '0'
|
|
14
|
+
|
|
15
|
+
plan:
|
|
16
|
+
stage: plan
|
|
17
|
+
image: node:22
|
|
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
|
+
- 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)
|
|
25
|
+
- chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge gitlab
|
|
26
|
+
artifacts:
|
|
27
|
+
when: always
|
|
28
|
+
paths:
|
|
29
|
+
- .chant/pr
|
|
30
|
+
|
|
31
|
+
record:
|
|
32
|
+
stage: record
|
|
33
|
+
image: node:22
|
|
34
|
+
resource_group: chant-record-prod
|
|
35
|
+
rules:
|
|
36
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
37
|
+
variables:
|
|
38
|
+
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
39
|
+
script:
|
|
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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
42
|
+
- chant ci run record --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab
|
|
43
|
+
artifacts:
|
|
44
|
+
when: always
|
|
45
|
+
paths:
|
|
46
|
+
- .chant/pr
|
|
@@ -0,0 +1,101 @@
|
|
|
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-wave-1
|
|
10
|
+
- apply-wave-2
|
|
11
|
+
- apply-wave-3
|
|
12
|
+
|
|
13
|
+
variables:
|
|
14
|
+
CHANT_ENV: prod
|
|
15
|
+
GIT_DEPTH: '0'
|
|
16
|
+
|
|
17
|
+
plan:
|
|
18
|
+
stage: plan
|
|
19
|
+
image: node:22
|
|
20
|
+
rules:
|
|
21
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
22
|
+
variables:
|
|
23
|
+
BASE_SHA: '$CI_MERGE_REQUEST_DIFF_BASE_SHA'
|
|
24
|
+
PR_NUMBER: '$CI_MERGE_REQUEST_IID'
|
|
25
|
+
script:
|
|
26
|
+
- 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)
|
|
27
|
+
- chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge gitlab --wave-gate release
|
|
28
|
+
artifacts:
|
|
29
|
+
when: always
|
|
30
|
+
paths:
|
|
31
|
+
- .chant/pr
|
|
32
|
+
|
|
33
|
+
apply-wave-1:
|
|
34
|
+
stage: apply-wave-1
|
|
35
|
+
image: node:22
|
|
36
|
+
resource_group: chant-apply-prod
|
|
37
|
+
rules:
|
|
38
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
39
|
+
variables:
|
|
40
|
+
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
41
|
+
script:
|
|
42
|
+
- 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)
|
|
43
|
+
- 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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
44
|
+
- chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab --wave-gate release --wave 1
|
|
45
|
+
cache:
|
|
46
|
+
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
47
|
+
paths:
|
|
48
|
+
- .chant/pr-resume/pr-apply.json
|
|
49
|
+
when: always
|
|
50
|
+
artifacts:
|
|
51
|
+
when: always
|
|
52
|
+
paths:
|
|
53
|
+
- .chant/pr
|
|
54
|
+
|
|
55
|
+
apply-wave-2:
|
|
56
|
+
stage: apply-wave-2
|
|
57
|
+
image: node:22
|
|
58
|
+
needs:
|
|
59
|
+
- apply-wave-1
|
|
60
|
+
resource_group: chant-apply-prod
|
|
61
|
+
rules:
|
|
62
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
63
|
+
variables:
|
|
64
|
+
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
65
|
+
script:
|
|
66
|
+
- 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)
|
|
67
|
+
- 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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
68
|
+
- chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab --wave-gate release --wave 2
|
|
69
|
+
cache:
|
|
70
|
+
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
71
|
+
paths:
|
|
72
|
+
- .chant/pr-resume/pr-apply.json
|
|
73
|
+
when: always
|
|
74
|
+
artifacts:
|
|
75
|
+
when: always
|
|
76
|
+
paths:
|
|
77
|
+
- .chant/pr
|
|
78
|
+
|
|
79
|
+
apply-wave-3:
|
|
80
|
+
stage: apply-wave-3
|
|
81
|
+
image: node:22
|
|
82
|
+
needs:
|
|
83
|
+
- apply-wave-2
|
|
84
|
+
resource_group: chant-apply-prod
|
|
85
|
+
rules:
|
|
86
|
+
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
87
|
+
variables:
|
|
88
|
+
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
89
|
+
script:
|
|
90
|
+
- 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)
|
|
91
|
+
- 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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
92
|
+
- chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab --wave-gate release --wave 3
|
|
93
|
+
cache:
|
|
94
|
+
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
95
|
+
paths:
|
|
96
|
+
- .chant/pr-resume/pr-apply.json
|
|
97
|
+
when: always
|
|
98
|
+
artifacts:
|
|
99
|
+
when: always
|
|
100
|
+
paths:
|
|
101
|
+
- .chant/pr
|
|
@@ -22,7 +22,7 @@ plan:
|
|
|
22
22
|
PR_NUMBER: '$CI_MERGE_REQUEST_IID'
|
|
23
23
|
script:
|
|
24
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)
|
|
25
|
-
- chant
|
|
25
|
+
- chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge gitlab
|
|
26
26
|
artifacts:
|
|
27
27
|
when: always
|
|
28
28
|
paths:
|
|
@@ -38,8 +38,8 @@ apply:
|
|
|
38
38
|
BASE_SHA: '$CI_COMMIT_BEFORE_SHA'
|
|
39
39
|
script:
|
|
40
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
|
|
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 --verify --quiet "$CI_COMMIT_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; fi; export BASE_SHA
|
|
42
|
+
- chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge gitlab
|
|
43
43
|
cache:
|
|
44
44
|
key: chant-apply-prod-$CI_COMMIT_SHA
|
|
45
45
|
paths:
|
|
@@ -185,6 +185,16 @@ describe("generateGitlabOpPipeline: the merge_request and push triggers (#2084,
|
|
|
185
185
|
]);
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
+
test("a newest push build also runs at a build request's tag, and the header says how auto-cancel must be set (#4169)", () => {
|
|
189
|
+
const newest = generateGitlabOpPipeline([{ name: "suites", trigger: { kind: "push", build: "newest" } }]);
|
|
190
|
+
const job = parseYAML(newest.files[0].yaml)["suites"] as { rules: Array<{ if: string }> };
|
|
191
|
+
expect(job.rules.map((r) => r.if)).toEqual(['$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"', "$CI_COMMIT_TAG =~ /^ci\\/build\\//"]);
|
|
192
|
+
expect(newest.files[0].yaml).toContain("Auto-cancel redundant pipelines on");
|
|
193
|
+
const every = generateGitlabOpPipeline([{ name: "suites", trigger: { kind: "push", build: "every" } }]);
|
|
194
|
+
expect((parseYAML(every.files[0].yaml)["suites"] as { rules: unknown[] }).rules).toHaveLength(1);
|
|
195
|
+
expect(every.files[0].yaml).toContain("Auto-cancel redundant pipelines off");
|
|
196
|
+
});
|
|
197
|
+
|
|
188
198
|
test("an unfiltered push trigger defaults to main, the same branch github's generator assumes", () => {
|
|
189
199
|
const result = generateGitlabOpPipeline([{ name: "tf-apply", trigger: { kind: "push" } }]);
|
|
190
200
|
const job = parseYAML(result.files[0].yaml)["tf-apply"] as { rules: Array<{ if: string }> };
|
|
@@ -239,9 +239,16 @@ function headerLineFor(
|
|
|
239
239
|
}
|
|
240
240
|
case "push": {
|
|
241
241
|
const branches = pushBranches(trigger.branches);
|
|
242
|
+
// #4169: GitLab cancels a waiting pipeline when a newer commit's starts, under the project's auto-cancel setting.
|
|
243
|
+
const build =
|
|
244
|
+
trigger.build === "newest"
|
|
245
|
+
? "; builds the newest commit and each ci/build/<sha> tag: keep Settings > CI/CD > Auto-cancel redundant pipelines on"
|
|
246
|
+
: trigger.build === "every"
|
|
247
|
+
? "; builds every commit: turn Settings > CI/CD > Auto-cancel redundant pipelines off"
|
|
248
|
+
: "";
|
|
242
249
|
return (
|
|
243
250
|
`# ${jobName}: push to ${branches.join(", ")}, finding-mode ${mode}${tokenNote}` +
|
|
244
|
-
` — a gated apply stays green, its pending gate in the log and in ${gateSummaryPath(jobName)}`
|
|
251
|
+
` — a gated apply stays green, its pending gate in the log and in ${gateSummaryPath(jobName)}${build}`
|
|
245
252
|
);
|
|
246
253
|
}
|
|
247
254
|
}
|