@intentius/chant-lexicon-gitlab 0.57.0 → 0.58.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-op-pipeline.d.ts +6 -4
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +10 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/op/activities/gitlab.d.ts +1 -1
- package/dist/op/activities/gitlab.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +5 -4
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/builders.d.ts +4 -5
- package/dist/op/builders.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/generate-op-pipeline.test.ts +14 -2
- package/src/components/generate-op-pipeline.ts +18 -8
- package/src/components/generate-pipeline.test.ts +2 -2
- package/src/index.ts +2 -3
- package/src/lint/audit-catalog.ts +5 -1
- package/src/lint/audit-lineage.ts +73 -0
- package/src/op/activities/gitlab.ts +2 -3
- package/src/op/activities/index.ts +5 -4
- package/src/op/builders.ts +4 -5
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The Op counterpart to `./generate-pipeline.ts` (#563): that module
|
|
5
5
|
* synthesizes a deploy-time component graph as one `.gitlab-ci.yml`; this one
|
|
6
|
-
* synthesizes a cron-triggered job per stateless Op
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* synthesizes a cron-triggered job per stateless Op. An Op's cadence is an
|
|
7
|
+
* `OpSchedule` on the Op itself (`packages/core/src/op/types.ts`),
|
|
8
|
+
* runtime-neutral data each reader interprets; this module is the reader that
|
|
9
|
+
* turns it into a GitLab pipeline schedule (`WorkflowAuditOp`/
|
|
10
|
+
* `PipelineAuditOp`/`ReconcileOp` all accept an optional `schedule` precisely
|
|
11
|
+
* for this).
|
|
10
12
|
*
|
|
11
13
|
* Unlike GitHub Actions' per-workflow `on.schedule`, GitLab has no in-file
|
|
12
14
|
* cron at all — a schedule is a project-level object (Settings → CI/CD →
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAGnD,gBAAgB,IAAI,sBAAsB,EAC1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAmBhE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAoDxB"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOxC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAG7D,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAIjC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnI,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGtI,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGzH,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "35158c32b55ed8f86ec453b7575d06fd8005641a5edb2ad972f0f4cf06fccd7e",
|
|
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": "59706e7a6edf95b2ecec5fe2220d0899b0282322ef921583a940a03fb623e7c0"
|
|
59
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA0E,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA0E,KAAK,QAAQ,EAAgB,MAAM,gCAAgC,CAAC;AAGrJ,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6CvD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for these audit rules: the open-source tools whose checks cover the
|
|
3
|
+
* same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
|
|
4
|
+
* the registry, the relation vocabulary, and why this is credit rather than
|
|
5
|
+
* authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
|
|
6
|
+
* when a credited tool's index no longer lists a rule cited here.
|
|
7
|
+
*/
|
|
8
|
+
import type { Lineage } from "@intentius/chant/audit/catalog";
|
|
9
|
+
export declare const gitlabAuditLineage: Record<string, Lineage[]>;
|
|
10
|
+
//# sourceMappingURL=audit-lineage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-lineage.d.ts","sourceRoot":"","sources":["../../src/lint/audit-lineage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CA+DxD,CAAC"}
|
package/dist/manifest.json
CHANGED
|
@@ -9,7 +9,7 @@ export interface GitlabPipelineArgs {
|
|
|
9
9
|
/**
|
|
10
10
|
* Trigger a GitLab CI pipeline and wait for it to complete successfully.
|
|
11
11
|
* Requires `glab` CLI authenticated in the environment.
|
|
12
|
-
* Uses longInfra profile
|
|
12
|
+
* Uses the longInfra profile: 20m timeout, three attempts backing off from 30s.
|
|
13
13
|
*/
|
|
14
14
|
export declare function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSignal): Promise<void>;
|
|
15
15
|
//# sourceMappingURL=gitlab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gitlab.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gitlab.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAqClG"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* gitlab Op activities — resolved by the core activity registry when a project's
|
|
3
3
|
* `chant.config.ts` lists the `gitlab` lexicon. `gitlabPipeline` triggers a
|
|
4
|
-
* pipeline over the GitLab CLI
|
|
5
|
-
*
|
|
6
|
-
* The `gitlabPipeline` step builder stays in
|
|
7
|
-
*
|
|
4
|
+
* pipeline over the GitLab CLI and polls it to completion under the step's
|
|
5
|
+
* profile; relocated from the hosting lexicon (#809) so gitlab's imperative
|
|
6
|
+
* activity lives with its product. The `gitlabPipeline` step builder stays in
|
|
7
|
+
* core and reaches authors through `@intentius/chant/op` like the other core
|
|
8
|
+
* builders.
|
|
8
9
|
*/
|
|
9
10
|
export { gitlabPipeline } from "./gitlab.js";
|
|
10
11
|
export type { GitlabPipelineArgs } from "./gitlab.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/op/builders.d.ts
CHANGED
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
* `./activities/gitlab.ts`. `opts`'s type below IS `GitlabPipelineArgs`
|
|
8
8
|
* itself (via `Omit`/`WithStepRefs`) — never a hand-restated mirror.
|
|
9
9
|
*
|
|
10
|
-
* `core`'s own `gitlabPipeline` (in `@intentius/chant/op
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* deliberately not swapped into the temporal barrel (see
|
|
10
|
+
* `core`'s own `gitlabPipeline` (in `@intentius/chant/op`) is UNCHANGED and
|
|
11
|
+
* produces a byte-identical `ActivityStep` for the same inputs — purely
|
|
12
|
+
* additive, and deliberately not swapped into core's barrel (see
|
|
14
13
|
* `lexicons/k8s/src/op/builders.ts`'s module doc for why: that would make
|
|
15
|
-
*
|
|
14
|
+
* core depend on this package at runtime). An author who wants the typed
|
|
16
15
|
* surface imports it from here — `@intentius/chant-lexicon-gitlab`.
|
|
17
16
|
*/
|
|
18
17
|
import { type ActivityStep, type NamedActivityStep, type WithStepRefs } from "@intentius/chant/op";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../src/op/builders.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,OAAO,YAAY,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,KACzG,iBAGF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gitlab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.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.58.0",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.58.0",
|
|
72
72
|
"typescript": "^5.9.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|
|
@@ -70,6 +70,18 @@ describe("generateGitlabOpPipeline: one file, one job per scheduled Op", () => {
|
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
describe("generateGitlabOpPipeline: no pull_request/push event model (#2084)", () => {
|
|
74
|
+
test("a pull_request trigger throws a clear error instead of silently ignoring it", () => {
|
|
75
|
+
const specs: ScheduledOpSpec[] = [{ name: "tf-plan", trigger: { kind: "pull_request" } }];
|
|
76
|
+
expect(() => generateGitlabOpPipeline(specs)).toThrow(/pull_request.*GitLab has no pull_request\/push event model/s);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("a push trigger throws a clear error instead of silently ignoring it", () => {
|
|
80
|
+
const specs: ScheduledOpSpec[] = [{ name: "tf-apply", trigger: { kind: "push" } }];
|
|
81
|
+
expect(() => generateGitlabOpPipeline(specs)).toThrow(/push.*GitLab has no pull_request\/push event model/s);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
73
85
|
describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit, not per-job", () => {
|
|
74
86
|
test("runCommand/beforeScript/extraScript apply uniformly across every job", () => {
|
|
75
87
|
const specs: ScheduledOpSpec[] = [
|
|
@@ -77,7 +89,7 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
|
|
|
77
89
|
{ name: "prod-reconcile", schedule: "0 * * * *" },
|
|
78
90
|
];
|
|
79
91
|
const result = generateGitlabOpPipeline(specs, {
|
|
80
|
-
runCommand: ["chant", "run", "{name}", "--
|
|
92
|
+
runCommand: ["chant", "run", "{name}", "--json"],
|
|
81
93
|
beforeScript: ["npm ci"],
|
|
82
94
|
extraScript: ["echo done"],
|
|
83
95
|
});
|
|
@@ -87,7 +99,7 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
|
|
|
87
99
|
const job = parsed[spec.name] as Record<string, unknown>;
|
|
88
100
|
const script = job.script as string[];
|
|
89
101
|
expect(script[0]).toBe("npm ci");
|
|
90
|
-
expect(script[1]).toContain("--
|
|
102
|
+
expect(script[1]).toContain("--json");
|
|
91
103
|
expect(script[2]).toBe("echo done");
|
|
92
104
|
}
|
|
93
105
|
});
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The Op counterpart to `./generate-pipeline.ts` (#563): that module
|
|
5
5
|
* synthesizes a deploy-time component graph as one `.gitlab-ci.yml`; this one
|
|
6
|
-
* synthesizes a cron-triggered job per stateless Op
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* synthesizes a cron-triggered job per stateless Op. An Op's cadence is an
|
|
7
|
+
* `OpSchedule` on the Op itself (`packages/core/src/op/types.ts`),
|
|
8
|
+
* runtime-neutral data each reader interprets; this module is the reader that
|
|
9
|
+
* turns it into a GitLab pipeline schedule (`WorkflowAuditOp`/
|
|
10
|
+
* `PipelineAuditOp`/`ReconcileOp` all accept an optional `schedule` precisely
|
|
11
|
+
* for this).
|
|
10
12
|
*
|
|
11
13
|
* Unlike GitHub Actions' per-workflow `on.schedule`, GitLab has no in-file
|
|
12
14
|
* cron at all — a schedule is a project-level object (Settings → CI/CD →
|
|
@@ -27,6 +29,7 @@
|
|
|
27
29
|
*/
|
|
28
30
|
|
|
29
31
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
32
|
+
import { resolveOpTrigger } from "@intentius/chant/lexicon";
|
|
30
33
|
import type {
|
|
31
34
|
ComponentPipelineOptions as GenerateGitlabOpOptions,
|
|
32
35
|
OpFindingMode,
|
|
@@ -49,9 +52,9 @@ const STAGE = "scheduled-ops";
|
|
|
49
52
|
const SELECTOR_VAR = "CHANT_SCHEDULED_OP";
|
|
50
53
|
|
|
51
54
|
/** One setup line per Op in the generated file's header comment. */
|
|
52
|
-
function setupLine(spec: ScheduledOpSpec, jobName: string, mode: OpFindingMode): string {
|
|
55
|
+
function setupLine(spec: ScheduledOpSpec, cron: string, jobName: string, mode: OpFindingMode): string {
|
|
53
56
|
const tokenNote = mode === "report" ? "" : " — needs a GITLAB_TOKEN CI/CD variable (masked, scope: api)";
|
|
54
|
-
return `# ${jobName}: cron "${
|
|
57
|
+
return `# ${jobName}: cron "${cron}", ${SELECTOR_VAR}="${spec.name}", finding-mode ${mode}${tokenNote}`;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/**
|
|
@@ -83,9 +86,16 @@ export function generateGitlabOpPipeline(
|
|
|
83
86
|
|
|
84
87
|
for (const spec of ops) {
|
|
85
88
|
const findingMode = spec.findingMode ?? "report";
|
|
89
|
+
const trigger = resolveOpTrigger(spec);
|
|
90
|
+
if (trigger.kind !== "cron") {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Scheduled Op "${spec.name}" has a "${trigger.kind}" trigger, but GitLab has no pull_request/push event model ` +
|
|
93
|
+
`(#2084) — only a project-level Pipeline Schedule (cron). Give it a cron trigger, or generate it for github/forgejo instead.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
86
96
|
const jobName = toJobName(spec.name);
|
|
87
|
-
jobs.push({ jobName, op: spec.name,
|
|
88
|
-
headerLines.push(setupLine(spec, jobName, findingMode));
|
|
97
|
+
jobs.push({ jobName, op: spec.name, trigger, findingMode });
|
|
98
|
+
headerLines.push(setupLine(spec, trigger.schedule, jobName, findingMode));
|
|
89
99
|
|
|
90
100
|
const runParts = runCommand.map((part) => part.replace("{name}", spec.name));
|
|
91
101
|
const script = [...beforeScript, runParts.join(" "), ...extraScript];
|
|
@@ -186,7 +186,7 @@ describe("generateGitlabPipeline: a cross-cutting change is one generator edit,
|
|
|
186
186
|
test("changing the trigger command (runCommand) updates every job's script uniformly", () => {
|
|
187
187
|
const components = pilotComponents();
|
|
188
188
|
const result = generateGitlabPipeline(components, {
|
|
189
|
-
runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--
|
|
189
|
+
runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--verbose"],
|
|
190
190
|
});
|
|
191
191
|
const parsed = parseYAML(result.yaml);
|
|
192
192
|
|
|
@@ -194,7 +194,7 @@ describe("generateGitlabPipeline: a cross-cutting change is one generator edit,
|
|
|
194
194
|
const script = (parsed[job.jobName] as Record<string, unknown>).script as string[];
|
|
195
195
|
// The runCommand prefix reflects in every job; output-threading flags
|
|
196
196
|
// (--seed-outputs/--dump-outputs) may be appended per the dependency graph.
|
|
197
|
-
expect(script[0].startsWith(`chant run --components ${job.component} --env staging --
|
|
197
|
+
expect(script[0].startsWith(`chant run --components ${job.component} --env staging --verbose`)).toBe(true);
|
|
198
198
|
}
|
|
199
199
|
});
|
|
200
200
|
|
package/src/index.ts
CHANGED
|
@@ -7,9 +7,8 @@ export { gitlabPlugin } from "./plugin";
|
|
|
7
7
|
// Typed Op step-builder wrapper (chant #1288 Stage 2) — gitlabPipeline with
|
|
8
8
|
// authoring-time types derived from this lexicon's own GitlabPipelineArgs
|
|
9
9
|
// (see lexicons/k8s/src/op/builders.ts's module doc for why this lives here
|
|
10
|
-
// rather than in core
|
|
11
|
-
//
|
|
12
|
-
// untyped builder, unchanged, for cloud-agnostic authoring.
|
|
10
|
+
// rather than in core). Opt-in: `@intentius/chant/op`'s same-named export is
|
|
11
|
+
// core's original untyped builder, unchanged, for cloud-agnostic authoring.
|
|
13
12
|
export { gitlabPipeline } from "./op/builders";
|
|
14
13
|
|
|
15
14
|
// Intrinsics
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The gitlab lexicon's chant audit catalog — metadata for its post-synth rules
|
|
3
3
|
* (WGL GitLab CI rules). Contributed via gitlabPlugin.auditCatalog() (#687).
|
|
4
4
|
*/
|
|
5
|
-
import { auditRule, GH_INJECTION, GH_OIDC, GH_PWN, GH_SECRETS, SCORECARD_PINNED, type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
5
|
+
import { auditRule, GH_INJECTION, GH_OIDC, GH_PWN, GH_SECRETS, SCORECARD_PINNED, type RuleMeta, applyLineage } from "@intentius/chant/audit/catalog";
|
|
6
|
+
import { gitlabAuditLineage } from "./audit-lineage";
|
|
6
7
|
|
|
7
8
|
export const gitlabAuditCatalog: Record<string, RuleMeta> = {
|
|
8
9
|
WGL010: auditRule("WGL010", "merge-worthy", "guidance", "Job references an undefined stage", "Add the stage to `stages:` or fix the job's `stage:`.", { category: "correctness" }),
|
|
@@ -50,3 +51,6 @@ export const gitlabAuditCatalog: Record<string, RuleMeta> = {
|
|
|
50
51
|
WGL049: auditRule("WGL049", "report-only", "guidance", "Dependency install without a cache", "Add a `cache:` covering the dependency directory.", { category: "efficiency" }),
|
|
51
52
|
WGL050: auditRule("WGL050", "report-only", "guidance", "Merge-request job missing interruptible", "Add `interruptible: true` so a superseded pipeline can be cancelled.", { category: "efficiency" }),
|
|
52
53
|
};
|
|
54
|
+
|
|
55
|
+
// Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
|
|
56
|
+
applyLineage(gitlabAuditCatalog, gitlabAuditLineage);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for these audit rules: the open-source tools whose checks cover the
|
|
3
|
+
* same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
|
|
4
|
+
* the registry, the relation vocabulary, and why this is credit rather than
|
|
5
|
+
* authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
|
|
6
|
+
* when a credited tool's index no longer lists a rule cited here.
|
|
7
|
+
*/
|
|
8
|
+
import type { Lineage } from "@intentius/chant/audit/catalog";
|
|
9
|
+
|
|
10
|
+
export const gitlabAuditLineage: Record<string, Lineage[]> = {
|
|
11
|
+
WGL010: [
|
|
12
|
+
{ tool: "gitlab-docs", rule: "stage: supported values are the default stages or stages listed in `stages`", url: "https://docs.gitlab.com/ci/yaml/#stage", relation: "equivalent" },
|
|
13
|
+
],
|
|
14
|
+
WGL012: [
|
|
15
|
+
{ tool: "gitlab-docs", rule: "Deprecated keywords (only/except, globally-defined image/services/cache/before_script/after_script)", url: "https://docs.gitlab.com/ci/yaml/deprecated_keywords/#only--except", relation: "equivalent" },
|
|
16
|
+
],
|
|
17
|
+
WGL013: [
|
|
18
|
+
{ tool: "gitlab-docs", rule: "Error: 'job' does not exist in the pipeline (needs)", url: "https://docs.gitlab.com/ci/yaml/needs/#error-job-does-not-exist-in-the-pipeline", relation: "equivalent" },
|
|
19
|
+
],
|
|
20
|
+
WGL014: [
|
|
21
|
+
{ tool: "gitlab-docs", rule: "extends: supported values are the name(s) of another job in the pipeline", url: "https://docs.gitlab.com/ci/yaml/#extends", relation: "equivalent" },
|
|
22
|
+
],
|
|
23
|
+
WGL016: [
|
|
24
|
+
{ tool: "gitlab-docs", rule: "CI/CD variable security (secrets belong in masked/protected variables, not in .gitlab-ci.yml)", url: "https://docs.gitlab.com/ci/variables/#cicd-variable-security", relation: "overlaps" },
|
|
25
|
+
],
|
|
26
|
+
WGL023: [
|
|
27
|
+
{ tool: "gitlab-docs", rule: "Pipeline warning: Job may allow multiple pipelines to run for a single action (final `when` rule without conditions)", url: "https://docs.gitlab.com/ci/debugging/#job-may-allow-multiple-pipelines-to-run-for-a-single-action-warning", relation: "overlaps" },
|
|
28
|
+
],
|
|
29
|
+
WGL024: [
|
|
30
|
+
{ tool: "gitlab-docs", rule: "allow_failure default: true for manual jobs, false for rules:when: manual", url: "https://docs.gitlab.com/ci/yaml/#allow_failure", relation: "overlaps" },
|
|
31
|
+
],
|
|
32
|
+
WGL025: [
|
|
33
|
+
{ tool: "gitlab-docs", rule: "cache:key: all jobs with `cache` but no `cache:key` share the `default` cache", url: "https://docs.gitlab.com/ci/yaml/#cachekey", relation: "overlaps" },
|
|
34
|
+
],
|
|
35
|
+
WGL026: [
|
|
36
|
+
{ tool: "gitlab-docs", rule: "Docker-in-Docker with TLS enabled (recommended); DOCKER_TLS_CERTDIR", url: "https://docs.gitlab.com/ci/docker/docker_in_docker/#docker-in-docker-with-tls-enabled-in-the-docker-executor-recommended", relation: "overlaps" },
|
|
37
|
+
],
|
|
38
|
+
WGL029: [
|
|
39
|
+
{ tool: "gitlab-docs", rule: "include:project ref guidance: use a specific SHA hash, protected branch/tag rules", url: "https://docs.gitlab.com/ci/yaml/#includeproject", relation: "overlaps" },
|
|
40
|
+
{ tool: "gitlab-docs", rule: "CI/CD component security best practices: use pinned versions (commit SHA preferred), avoid `latest`", url: "https://docs.gitlab.com/ci/components/#for-component-users", relation: "overlaps" },
|
|
41
|
+
],
|
|
42
|
+
WGL030: [
|
|
43
|
+
{ tool: "gitlab-docs", rule: "include:remote: treat as a third-party dependency; verify integrity with include:integrity", url: "https://docs.gitlab.com/ci/yaml/#includeremote", relation: "overlaps" },
|
|
44
|
+
],
|
|
45
|
+
WGL031: [
|
|
46
|
+
{ tool: "gitlab-docs", rule: "Use checksum to keep your image secure (image@sha256:digest)", url: "https://docs.gitlab.com/ci/docker/using_docker_images/#use-checksum-to-keep-your-image-secure", relation: "overlaps" },
|
|
47
|
+
],
|
|
48
|
+
WGL033: [
|
|
49
|
+
{ tool: "gitlab-docs", rule: "id_tokens: the required `aud` sub-keyword configures the aud claim", url: "https://docs.gitlab.com/ci/yaml/#id_tokens", relation: "overlaps" },
|
|
50
|
+
],
|
|
51
|
+
WGL035: [
|
|
52
|
+
{ tool: "poutine", rule: "injection", url: "https://boostsecurityio.github.io/poutine/rules/injection/", relation: "overlaps" },
|
|
53
|
+
],
|
|
54
|
+
WGL038: [
|
|
55
|
+
{ tool: "gitlab-docs", rule: "Protect a CI/CD variable (available only to protected branches/tags)", url: "https://docs.gitlab.com/ci/variables/#protect-a-cicd-variable", relation: "overlaps" },
|
|
56
|
+
{ tool: "gitlab-docs", rule: "Merge request pipelines from forked projects can steal secrets in the parent project", url: "https://docs.gitlab.com/ci/pipelines/merge_request_pipelines/#use-with-forked-projects", relation: "overlaps" },
|
|
57
|
+
],
|
|
58
|
+
WGL039: [
|
|
59
|
+
{ tool: "gitlab-docs", rule: "CI/CD variable security: accidental-leak-job example (echo of $PASSWORD); mask sensitive variables", url: "https://docs.gitlab.com/ci/variables/#mask-a-cicd-variable", relation: "overlaps" },
|
|
60
|
+
],
|
|
61
|
+
WGL044: [
|
|
62
|
+
{ tool: "gitlab-docs", rule: "artifacts:public (default true: downloadable by anonymous users in public pipelines)", url: "https://docs.gitlab.com/ci/yaml/#artifactspublic", relation: "overlaps" },
|
|
63
|
+
],
|
|
64
|
+
WGL046: [
|
|
65
|
+
{ tool: "gitlab-docs", rule: "Cache key names: protected and non-protected branches do not share the cache by default", url: "https://docs.gitlab.com/ci/caching/#cache-key-names", relation: "overlaps" },
|
|
66
|
+
],
|
|
67
|
+
WGL047: [
|
|
68
|
+
{ tool: "poutine", rule: "unverified_script_exec", url: "https://boostsecurityio.github.io/poutine/rules/unverified_script_exec/", relation: "equivalent" },
|
|
69
|
+
],
|
|
70
|
+
WGL048: [
|
|
71
|
+
{ tool: "gitlab-docs", rule: "Use pipeline names (workflow:name)", url: "https://docs.gitlab.com/ci/debugging/#use-pipeline-names", relation: "overlaps" },
|
|
72
|
+
],
|
|
73
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
|
-
import {
|
|
3
|
+
import { sleep } from "@intentius/chant/op";
|
|
4
4
|
|
|
5
5
|
const execAsync = promisify(exec);
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ export interface GitlabPipelineArgs {
|
|
|
16
16
|
/**
|
|
17
17
|
* Trigger a GitLab CI pipeline and wait for it to complete successfully.
|
|
18
18
|
* Requires `glab` CLI authenticated in the environment.
|
|
19
|
-
* Uses longInfra profile
|
|
19
|
+
* Uses the longInfra profile: 20m timeout, three attempts backing off from 30s.
|
|
20
20
|
*/
|
|
21
21
|
export async function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSignal): Promise<void> {
|
|
22
22
|
const ref = args.ref ?? "HEAD";
|
|
@@ -34,7 +34,6 @@ export async function gitlabPipeline(args: GitlabPipelineArgs, signal?: AbortSig
|
|
|
34
34
|
while (true) {
|
|
35
35
|
if (signal?.aborted) throw new Error("gitlabPipeline aborted");
|
|
36
36
|
attempt++;
|
|
37
|
-
safeHeartbeat({ step: "gitlabPipeline", project: args.name, attempt });
|
|
38
37
|
|
|
39
38
|
const { stdout } = await execAsync(
|
|
40
39
|
`glab ci status --project ${args.name} --format json`,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* gitlab Op activities — resolved by the core activity registry when a project's
|
|
3
3
|
* `chant.config.ts` lists the `gitlab` lexicon. `gitlabPipeline` triggers a
|
|
4
|
-
* pipeline over the GitLab CLI
|
|
5
|
-
*
|
|
6
|
-
* The `gitlabPipeline` step builder stays in
|
|
7
|
-
*
|
|
4
|
+
* pipeline over the GitLab CLI and polls it to completion under the step's
|
|
5
|
+
* profile; relocated from the hosting lexicon (#809) so gitlab's imperative
|
|
6
|
+
* activity lives with its product. The `gitlabPipeline` step builder stays in
|
|
7
|
+
* core and reaches authors through `@intentius/chant/op` like the other core
|
|
8
|
+
* builders.
|
|
8
9
|
*/
|
|
9
10
|
export { gitlabPipeline } from "./gitlab";
|
|
10
11
|
export type { GitlabPipelineArgs } from "./gitlab";
|
package/src/op/builders.ts
CHANGED
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
* `./activities/gitlab.ts`. `opts`'s type below IS `GitlabPipelineArgs`
|
|
8
8
|
* itself (via `Omit`/`WithStepRefs`) — never a hand-restated mirror.
|
|
9
9
|
*
|
|
10
|
-
* `core`'s own `gitlabPipeline` (in `@intentius/chant/op
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* deliberately not swapped into the temporal barrel (see
|
|
10
|
+
* `core`'s own `gitlabPipeline` (in `@intentius/chant/op`) is UNCHANGED and
|
|
11
|
+
* produces a byte-identical `ActivityStep` for the same inputs — purely
|
|
12
|
+
* additive, and deliberately not swapped into core's barrel (see
|
|
14
13
|
* `lexicons/k8s/src/op/builders.ts`'s module doc for why: that would make
|
|
15
|
-
*
|
|
14
|
+
* core depend on this package at runtime). An author who wants the typed
|
|
16
15
|
* surface imports it from here — `@intentius/chant-lexicon-gitlab`.
|
|
17
16
|
*/
|
|
18
17
|
|