@intentius/chant-lexicon-gitlab 0.57.0 → 0.59.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 +33 -5
- 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 +79 -2
- package/src/components/generate-op-pipeline.ts +92 -10
- 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 →
|
|
@@ -20,10 +22,36 @@
|
|
|
20
22
|
* inlined audit/reconcile logic. The finding-mode itself is already baked
|
|
21
23
|
* into the Op's own activity args at build time by the composite that
|
|
22
24
|
* created it; GitLab has no per-job `permissions:` concept (unlike GitHub
|
|
23
|
-
* Actions),
|
|
25
|
+
* Actions), and `findingMode: "comment"` is refused by name here because it
|
|
26
|
+
* posts onto a GitHub pull-request event GitLab does not have (#2231), so a
|
|
27
|
+
* non-`report` mode's write access comes from whatever
|
|
24
28
|
* `GITLAB_TOKEN`/CI-CD-variable configuration the project already has —
|
|
25
29
|
* this generator documents the requirement rather than fabricating a
|
|
26
30
|
* variable nothing reads.
|
|
31
|
+
*
|
|
32
|
+
* Neither of #2242's two per-Op options survives the crossing, and both are
|
|
33
|
+
* refused by name rather than dropped. A `setup` entry spelled `{ uses }` is
|
|
34
|
+
* a GitHub Actions marketplace action; GitLab CI has `script` and nothing
|
|
35
|
+
* else, so there is no shape to translate it into and no way to approximate
|
|
36
|
+
* `aws-actions/configure-aws-credentials` in a shell line. A `{ run }` entry
|
|
37
|
+
* translates exactly, and is emitted ahead of the `beforeScript` lines, the
|
|
38
|
+
* same position the github generator gives it. An additive `permissions` map
|
|
39
|
+
* is refused for the same reason `permissions:` is absent here at all: GitLab
|
|
40
|
+
* has no per-job token-scope mapping, and its OIDC surface is a different
|
|
41
|
+
* declaration (`id_tokens:` with an `aud`, exchanged for cloud credentials by
|
|
42
|
+
* the job itself) that chant does not generate. Ignoring the map would emit a
|
|
43
|
+
* job that reads as having OIDC and runs with no credentials.
|
|
44
|
+
*
|
|
45
|
+
* The gated-apply mapping (#2243) has nothing to attach to here. It exists
|
|
46
|
+
* because a push-to-main apply that stops at its gate exits 3 and paints the
|
|
47
|
+
* branch red on every merge; this generator has no push pipeline at all,
|
|
48
|
+
* refusing a `push` trigger by name (#2084) because a GitLab schedule is a
|
|
49
|
+
* project-level cron object rather than an event. GitLab does have its own
|
|
50
|
+
* equivalent of the mapping should one ever be wanted — `allow_failure:
|
|
51
|
+
* { exit_codes: [3] }` turns one exit code into a warning rather than a
|
|
52
|
+
* failure, without a flag on the invocation — so the day this generator
|
|
53
|
+
* grows a push trigger, that is the shape to reach for rather than
|
|
54
|
+
* `--gated-exit`.
|
|
27
55
|
*/
|
|
28
56
|
import type { ComponentPipelineOptions as GenerateGitlabOpOptions, OpPipelineResult as GenerateGitlabOpResult, ScheduledOpSpec } from "@intentius/chant/lexicon";
|
|
29
57
|
export type { GenerateGitlabOpOptions, GenerateGitlabOpResult };
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAGnD,gBAAgB,IAAI,sBAAsB,EAC1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAiClC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAmBhE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAmExB"}
|
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": "05eca8e8e24c1045cd8d1b9903dfa59661449d40b52b9d2f880eb2a5a00c67d2",
|
|
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": "5ba4e358f25944a07f0966355b274aebb3c199d11fc997aa7713830c7ff5320e"
|
|
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.59.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.59.0",
|
|
71
|
+
"@intentius/chant-lexicon-github": "^0.59.0",
|
|
72
72
|
"typescript": "^5.9.3"
|
|
73
73
|
},
|
|
74
74
|
"peerDependenciesMeta": {
|
|
@@ -70,6 +70,36 @@ 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
|
+
|
|
85
|
+
describe("generateGitlabOpPipeline: no comment finding mode (#2231)", () => {
|
|
86
|
+
test("findingMode comment is refused by name, even on a cron trigger GitLab does support", () => {
|
|
87
|
+
const specs: ScheduledOpSpec[] = [
|
|
88
|
+
{ name: "app-plan", schedule: "0 6 * * *", findingMode: "comment" },
|
|
89
|
+
];
|
|
90
|
+
expect(() => generateGitlabOpPipeline(specs)).toThrow(
|
|
91
|
+
/Scheduled Op "app-plan".*findingMode "comment".*GitLab has no pull_request event/s,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("the refusal names the modes GitLab does have", () => {
|
|
96
|
+
const specs: ScheduledOpSpec[] = [{ name: "app-plan", schedule: "0 6 * * *", findingMode: "comment" }];
|
|
97
|
+
expect(() => generateGitlabOpPipeline(specs)).toThrow(
|
|
98
|
+
/findingMode "issue" or "merge-request"/,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
73
103
|
describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit, not per-job", () => {
|
|
74
104
|
test("runCommand/beforeScript/extraScript apply uniformly across every job", () => {
|
|
75
105
|
const specs: ScheduledOpSpec[] = [
|
|
@@ -77,7 +107,7 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
|
|
|
77
107
|
{ name: "prod-reconcile", schedule: "0 * * * *" },
|
|
78
108
|
];
|
|
79
109
|
const result = generateGitlabOpPipeline(specs, {
|
|
80
|
-
runCommand: ["chant", "run", "{name}", "--
|
|
110
|
+
runCommand: ["chant", "run", "{name}", "--json"],
|
|
81
111
|
beforeScript: ["npm ci"],
|
|
82
112
|
extraScript: ["echo done"],
|
|
83
113
|
});
|
|
@@ -87,8 +117,55 @@ describe("generateGitlabOpPipeline: a cross-cutting change is one generator edit
|
|
|
87
117
|
const job = parsed[spec.name] as Record<string, unknown>;
|
|
88
118
|
const script = job.script as string[];
|
|
89
119
|
expect(script[0]).toBe("npm ci");
|
|
90
|
-
expect(script[1]).toContain("--
|
|
120
|
+
expect(script[1]).toContain("--json");
|
|
91
121
|
expect(script[2]).toBe("echo done");
|
|
92
122
|
}
|
|
93
123
|
});
|
|
94
124
|
});
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* #2242's two per-Op options against a provider that has neither concept.
|
|
128
|
+
* Both degrade by name at build time rather than being dropped into a job
|
|
129
|
+
* that would run without the thing the option asked for.
|
|
130
|
+
*/
|
|
131
|
+
describe("generateGitlabOpPipeline: setup steps and additive permissions (#2242)", () => {
|
|
132
|
+
test("refuses a `uses` setup step by name, and names the action", () => {
|
|
133
|
+
expect(() =>
|
|
134
|
+
generateGitlabOpPipeline([
|
|
135
|
+
{
|
|
136
|
+
name: "actions-audit",
|
|
137
|
+
schedule: "0 6 * * *",
|
|
138
|
+
setup: [{ uses: "aws-actions/configure-aws-credentials@v6" }],
|
|
139
|
+
},
|
|
140
|
+
]),
|
|
141
|
+
).toThrow(/setup step 1 is `uses: "aws-actions\/configure-aws-credentials@v6"`.*GitLab CI jobs run/s);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("emits a `run` setup entry ahead of the beforeScript lines", () => {
|
|
145
|
+
const result = generateGitlabOpPipeline(
|
|
146
|
+
[
|
|
147
|
+
{
|
|
148
|
+
name: "actions-audit",
|
|
149
|
+
schedule: "0 6 * * *",
|
|
150
|
+
setup: [{ run: "aws sts get-caller-identity" }],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
{ beforeScript: ["npm ci"], extraScript: ["echo done"] },
|
|
154
|
+
);
|
|
155
|
+
const job = parseYAML(result.files[0].yaml)["actions-audit"] as { script: string[] };
|
|
156
|
+
expect(job.script).toEqual([
|
|
157
|
+
"aws sts get-caller-identity",
|
|
158
|
+
"npm ci",
|
|
159
|
+
"chant run actions-audit",
|
|
160
|
+
"echo done",
|
|
161
|
+
]);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("refuses additive permissions by name, and points at GitLab's own OIDC surface", () => {
|
|
165
|
+
expect(() =>
|
|
166
|
+
generateGitlabOpPipeline([
|
|
167
|
+
{ name: "actions-audit", schedule: "0 6 * * *", permissions: { "id-token": "write" } },
|
|
168
|
+
]),
|
|
169
|
+
).toThrow(/adds permissions \{ id-token: write \}.*id_tokens:/s);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -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 →
|
|
@@ -20,13 +22,40 @@
|
|
|
20
22
|
* inlined audit/reconcile logic. The finding-mode itself is already baked
|
|
21
23
|
* into the Op's own activity args at build time by the composite that
|
|
22
24
|
* created it; GitLab has no per-job `permissions:` concept (unlike GitHub
|
|
23
|
-
* Actions),
|
|
25
|
+
* Actions), and `findingMode: "comment"` is refused by name here because it
|
|
26
|
+
* posts onto a GitHub pull-request event GitLab does not have (#2231), so a
|
|
27
|
+
* non-`report` mode's write access comes from whatever
|
|
24
28
|
* `GITLAB_TOKEN`/CI-CD-variable configuration the project already has —
|
|
25
29
|
* this generator documents the requirement rather than fabricating a
|
|
26
30
|
* variable nothing reads.
|
|
31
|
+
*
|
|
32
|
+
* Neither of #2242's two per-Op options survives the crossing, and both are
|
|
33
|
+
* refused by name rather than dropped. A `setup` entry spelled `{ uses }` is
|
|
34
|
+
* a GitHub Actions marketplace action; GitLab CI has `script` and nothing
|
|
35
|
+
* else, so there is no shape to translate it into and no way to approximate
|
|
36
|
+
* `aws-actions/configure-aws-credentials` in a shell line. A `{ run }` entry
|
|
37
|
+
* translates exactly, and is emitted ahead of the `beforeScript` lines, the
|
|
38
|
+
* same position the github generator gives it. An additive `permissions` map
|
|
39
|
+
* is refused for the same reason `permissions:` is absent here at all: GitLab
|
|
40
|
+
* has no per-job token-scope mapping, and its OIDC surface is a different
|
|
41
|
+
* declaration (`id_tokens:` with an `aud`, exchanged for cloud credentials by
|
|
42
|
+
* the job itself) that chant does not generate. Ignoring the map would emit a
|
|
43
|
+
* job that reads as having OIDC and runs with no credentials.
|
|
44
|
+
*
|
|
45
|
+
* The gated-apply mapping (#2243) has nothing to attach to here. It exists
|
|
46
|
+
* because a push-to-main apply that stops at its gate exits 3 and paints the
|
|
47
|
+
* branch red on every merge; this generator has no push pipeline at all,
|
|
48
|
+
* refusing a `push` trigger by name (#2084) because a GitLab schedule is a
|
|
49
|
+
* project-level cron object rather than an event. GitLab does have its own
|
|
50
|
+
* equivalent of the mapping should one ever be wanted — `allow_failure:
|
|
51
|
+
* { exit_codes: [3] }` turns one exit code into a warning rather than a
|
|
52
|
+
* failure, without a flag on the invocation — so the day this generator
|
|
53
|
+
* grows a push trigger, that is the shape to reach for rather than
|
|
54
|
+
* `--gated-exit`.
|
|
27
55
|
*/
|
|
28
56
|
|
|
29
57
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
58
|
+
import { resolveOpTrigger } from "@intentius/chant/lexicon";
|
|
30
59
|
import type {
|
|
31
60
|
ComponentPipelineOptions as GenerateGitlabOpOptions,
|
|
32
61
|
OpFindingMode,
|
|
@@ -35,6 +64,37 @@ import type {
|
|
|
35
64
|
ScheduledOpSpec,
|
|
36
65
|
} from "@intentius/chant/lexicon";
|
|
37
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Refuse the two #2242 options GitLab cannot honour, by name and before any
|
|
69
|
+
* YAML exists, and return the `run` setup lines that do translate. See the
|
|
70
|
+
* module doc for why each one is a refusal rather than a silent drop.
|
|
71
|
+
*/
|
|
72
|
+
function gitlabSetupScript(spec: ScheduledOpSpec): string[] {
|
|
73
|
+
if (spec.permissions && Object.keys(spec.permissions).length > 0) {
|
|
74
|
+
const scopes = Object.entries(spec.permissions)
|
|
75
|
+
.map(([scope, value]) => `${scope}: ${value}`)
|
|
76
|
+
.join(", ");
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Scheduled Op "${spec.name}" adds permissions { ${scopes} }, but GitLab CI has no per-job token-scope ` +
|
|
79
|
+
`mapping — there is no \`permissions:\` key to add them to (#2242). Its OIDC surface is a separate ` +
|
|
80
|
+
`\`id_tokens:\` declaration the job exchanges for cloud credentials itself, which chant does not ` +
|
|
81
|
+
`generate. Drop the option here, or generate this Op for github.`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const lines: string[] = [];
|
|
85
|
+
(spec.setup ?? []).forEach((step, index) => {
|
|
86
|
+
if ("uses" in step) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Scheduled Op "${spec.name}" setup step ${index + 1} is \`uses: "${step.uses}"\`, a GitHub Actions ` +
|
|
89
|
+
`marketplace action. GitLab CI jobs run \`script\` lines only, so there is nothing to translate it ` +
|
|
90
|
+
`into (#2242). Express the setup as a \`{ run }\` entry, or generate this Op for github/forgejo.`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
lines.push(step.run);
|
|
94
|
+
});
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
97
|
+
|
|
38
98
|
export type { GenerateGitlabOpOptions, GenerateGitlabOpResult };
|
|
39
99
|
|
|
40
100
|
/** GitLab CI job names must be safe YAML keys; Op names are already kebab-case in every fixture, but normalize defensively (mirrors `./generate-pipeline.ts`'s `toJobName`). */
|
|
@@ -49,9 +109,9 @@ const STAGE = "scheduled-ops";
|
|
|
49
109
|
const SELECTOR_VAR = "CHANT_SCHEDULED_OP";
|
|
50
110
|
|
|
51
111
|
/** One setup line per Op in the generated file's header comment. */
|
|
52
|
-
function setupLine(spec: ScheduledOpSpec, jobName: string, mode: OpFindingMode): string {
|
|
112
|
+
function setupLine(spec: ScheduledOpSpec, cron: string, jobName: string, mode: OpFindingMode): string {
|
|
53
113
|
const tokenNote = mode === "report" ? "" : " — needs a GITLAB_TOKEN CI/CD variable (masked, scope: api)";
|
|
54
|
-
return `# ${jobName}: cron "${
|
|
114
|
+
return `# ${jobName}: cron "${cron}", ${SELECTOR_VAR}="${spec.name}", finding-mode ${mode}${tokenNote}`;
|
|
55
115
|
}
|
|
56
116
|
|
|
57
117
|
/**
|
|
@@ -82,13 +142,35 @@ export function generateGitlabOpPipeline(
|
|
|
82
142
|
];
|
|
83
143
|
|
|
84
144
|
for (const spec of ops) {
|
|
145
|
+
const setupScript = gitlabSetupScript(spec);
|
|
85
146
|
const findingMode = spec.findingMode ?? "report";
|
|
147
|
+
if (findingMode === "comment") {
|
|
148
|
+
// The mode posts onto the pull request that triggered the run (#2231),
|
|
149
|
+
// read out of the GitHub Actions event payload by `reconcilePr`. GitLab
|
|
150
|
+
// has neither that event model nor that payload, and core carries no
|
|
151
|
+
// GitLab API client that would post the merge-request note instead, so
|
|
152
|
+
// this refuses the mode by name rather than emitting a job whose finding
|
|
153
|
+
// step fails on every pipeline.
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Scheduled Op "${spec.name}" has findingMode "comment", which posts its finding on the pull request ` +
|
|
156
|
+
`that triggered the run. GitLab has no pull_request event and chant has no GitLab merge-request ` +
|
|
157
|
+
`note activity (#2231). Use findingMode "issue" or "merge-request" here, or generate this Op for ` +
|
|
158
|
+
`github.`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const trigger = resolveOpTrigger(spec);
|
|
162
|
+
if (trigger.kind !== "cron") {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Scheduled Op "${spec.name}" has a "${trigger.kind}" trigger, but GitLab has no pull_request/push event model ` +
|
|
165
|
+
`(#2084) — only a project-level Pipeline Schedule (cron). Give it a cron trigger, or generate it for github/forgejo instead.`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
86
168
|
const jobName = toJobName(spec.name);
|
|
87
|
-
jobs.push({ jobName, op: spec.name,
|
|
88
|
-
headerLines.push(setupLine(spec, jobName, findingMode));
|
|
169
|
+
jobs.push({ jobName, op: spec.name, trigger, findingMode });
|
|
170
|
+
headerLines.push(setupLine(spec, trigger.schedule, jobName, findingMode));
|
|
89
171
|
|
|
90
172
|
const runParts = runCommand.map((part) => part.replace("{name}", spec.name));
|
|
91
|
-
const script = [...beforeScript, runParts.join(" "), ...extraScript];
|
|
173
|
+
const script = [...setupScript, ...beforeScript, runParts.join(" "), ...extraScript];
|
|
92
174
|
|
|
93
175
|
doc[jobName] = {
|
|
94
176
|
stage: STAGE,
|
|
@@ -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
|
|