@intentius/chant-lexicon-gitlab 0.44.14 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiFH;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAu2B9E"}
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiFH;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC9E"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "ece61e80a4c35316d0c5ee47c20b7252a13298b0a36d5b5ce307c396addb366d",
4
+ "manifest.json": "c3703cd4ebf4f650408c0bd187c1a242041dfdb0461b52d67db198d2cf486c67",
5
5
  "meta.json": "931fc3246a55645b1493080bbeb160e5d205e42349e8bdca96ca243adb5f0da3",
6
6
  "types/index.d.ts": "5cd2e99f135a929b72bdd822d00d780d39b1cd407ba0cfb3d511c7ac9d667b58",
7
7
  "rules/artifact-no-expiry.ts": "3f3cabf9792cbf8207e53a25f506715466b19ec25e9c3b4d0d77fed6b2eb4542",
@@ -52,5 +52,5 @@
52
52
  "skills/chant-gitlab-migrate.md": "4853d04980560b379e5b0f7267dcb0a1e5d5879cb9e9931681ce76b2a5fe69d8",
53
53
  "skills/chant-gitlab-patterns.md": "6d9a44e9e8de4c3820be9d65381b0d2ede4cb3626aaaf0011e21838a9fbf556e"
54
54
  },
55
- "composite": "b3af21ff51af16abb0cc25d0f518173e63572c231ac9e8ff41ddd067a25b68f6"
55
+ "composite": "b01961b1bd64c168d5d5d28d9d6cd160745f706b69b7451fe5b9218dff4c1d66"
56
56
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitlab",
3
- "version": "0.44.14",
3
+ "version": "0.46.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitLab",
6
6
  "intrinsics": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-gitlab",
3
- "version": "0.44.14",
3
+ "version": "0.46.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.44.14",
71
- "@intentius/chant-lexicon-github": "^0.44.14",
70
+ "@intentius/chant": "^0.46.0",
71
+ "@intentius/chant-lexicon-github": "^0.46.0",
72
72
  "typescript": "^5.9.3"
73
73
  },
74
74
  "peerDependenciesMeta": {
@@ -103,849 +103,16 @@ export async function generateDocs(opts?: { verbose?: boolean }): Promise<void>
103
103
  // `intrinsics()` registration (docsPipeline's generateIntrinsics, same
104
104
  // mechanism azure/helm already use), so its "Folds?" column can never
105
105
  // drift from the registration the way #1062's foldability matrix
106
- // depends on. The hand-written `reference()` usage guide moves to a
107
- // separate "intrinsics-guide" page below — content unchanged, just no
108
- // longer sharing a slug with generated data.
106
+ // depends on. The hand-written `reference()` usage guide lives in a
107
+ // separate "intrinsics-guide" page under docs/pages/ — content unchanged,
108
+ // just no longer sharing a slug with generated data.
109
109
  examplesDir: join(pkgDir, "examples"),
110
- extraPages: [
111
- {
112
- slug: "pipeline-concepts",
113
- title: "Pipeline Concepts",
114
- description: "Jobs, stages, artifacts, caching, images, rules, environments, and triggers in the GitLab CI/CD lexicon",
115
- content: `import Diagram from '../../components/Diagram.astro';
116
-
117
- Every exported \`Job\` declaration becomes a job entry in the generated \`.gitlab-ci.yml\`. The serializer handles the translation automatically:
118
-
119
- <Diagram name="pipeline-hierarchy" alt="GitLab Pipeline with stages (build, test, deploy) each containing jobs, with stage dependencies flowing left to right" caption="GitLab CI pipeline hierarchy" />
120
-
121
- - Property names use spec-native snake_case (\`expire_in\`, \`allow_failure\`)
122
- - Converts export names to kebab-case job keys (\`buildApp\` → \`build-app\`)
123
- - Collects stages from all jobs into a \`stages:\` list
124
- - Collapses single-property objects (\`new Image({ name: "node:20" })\` → \`image: node:20\`)
125
-
126
- {{file:docs-snippets/src/job-basic.ts}}
127
-
128
- Produces this YAML:
129
-
130
- \`\`\`yaml
131
- stages:
132
- - build
133
-
134
- build-app:
135
- stage: build
136
- image: node:20
137
- script:
138
- - npm ci
139
- - npm run build
140
- \`\`\`
141
-
142
- ## Resource types
143
-
144
- The lexicon provides 3 resource types and 16 property types:
145
-
146
- ### Resources
147
-
148
- | Type | Description |
149
- |------|-------------|
150
- | \`Job\` | A CI/CD job — the fundamental unit of a pipeline |
151
- | \`Workflow\` | Top-level \`workflow:\` configuration (pipeline-level rules, name, auto_cancel) |
152
- | \`Default\` | Top-level \`default:\` block (shared defaults inherited by all jobs) |
153
-
154
- ### Property types
155
-
156
- | Type | Used in | Description |
157
- |------|---------|-------------|
158
- | \`Image\` | Job, Default | Docker image for the job runner |
159
- | \`Cache\` | Job, Default | Files cached between pipeline runs |
160
- | \`Artifacts\` | Job | Files passed between stages or stored after completion |
161
- | \`Rule\` | Job, Workflow | Conditional execution rules (\`rules:\` entries) |
162
- | \`Environment\` | Job | Deployment target environment |
163
- | \`Trigger\` | Job | Downstream pipeline trigger |
164
- | \`Include\` | Workflow | External YAML file inclusion |
165
- | \`AllowFailure\` | Job | Failure tolerance configuration |
166
- | \`Retry\` | Job | Automatic retry on failure |
167
- | \`Parallel\` | Job | Job parallelization (matrix builds) |
168
- | \`Release\` | Job | GitLab Release creation |
169
- | \`AutoCancel\` | Workflow | Pipeline auto-cancellation settings |
170
- | \`Need\` | Job | Job dependency for DAG-mode execution |
171
- | \`Inherit\` | Job | Controls which global defaults a job inherits |
172
- | \`Service\` | Job, Default | Sidecar service container (e.g. Docker-in-Docker, databases) |
173
- | \`WorkflowRule\` | Workflow | Conditional rules for pipeline-level execution |
174
-
175
- ## Shared config
176
-
177
- Extract reusable objects into a shared config file and import them across your pipeline files:
178
-
179
- {{file:docs-snippets/src/pipeline-shared-config.ts}}
180
-
181
- ## Jobs
182
-
183
- A \`Job\` is the fundamental unit. Every exported \`Job\` becomes a job entry in the YAML:
184
-
185
- {{file:docs-snippets/src/job-test.ts}}
186
-
187
- Key properties:
188
- - \`script\` — **required** (or \`trigger\`/\`run\`). Array of shell commands to execute.
189
- - \`stage\` — which pipeline stage this job belongs to. Defaults to \`test\` if omitted.
190
- - \`image\` — Docker image. Use \`new Image({ name: "..." })\` or pass a string to the YAML.
191
- - \`needs\` — job dependencies for DAG-mode execution (run before stage ordering).
192
-
193
- ## Stages
194
-
195
- Stages define the execution order of a pipeline. The serializer automatically collects unique stage values from all jobs:
196
-
197
- {{file:docs-snippets/src/stages.ts}}
198
-
199
- Produces:
200
-
201
- \`\`\`yaml
202
- stages:
203
- - test
204
- - build
205
- - deploy
206
- \`\`\`
207
-
208
- Jobs in the same stage run in parallel. Stages run sequentially in declaration order.
209
-
210
- ## Artifacts and caching
211
-
212
- **Artifacts** are files produced by a job and passed to later stages or stored for download. **Caches** persist files between pipeline runs to speed up builds. Both are shown in the shared config:
213
-
214
- {{file:docs-snippets/src/config.ts:4-22}}
215
-
216
- The key difference: artifacts are for passing files between **stages in the same pipeline**; caches are for speeding up **repeated pipeline runs**.
217
-
218
- ## Conditional execution with rules
219
-
220
- \`Rule\` objects control when a job runs. They map to \`rules:\` entries in the YAML:
221
-
222
- {{file:docs-snippets/src/rules-conditions.ts}}
223
-
224
- Produces:
225
-
226
- \`\`\`yaml
227
- deploy:
228
- stage: deploy
229
- script:
230
- - npm run deploy
231
- rules:
232
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
233
- when: manual
234
- \`\`\`
235
-
236
- The \`if\` property maps directly to \`if:\` in the YAML. Use the \`CI\` pseudo-parameter object for type-safe variable references.
237
-
238
- ## Environments
239
-
240
- \`Environment\` defines a deployment target:
241
-
242
- {{file:docs-snippets/src/environment.ts}}
243
-
244
- GitLab tracks deployments to environments and provides rollback capabilities in the UI.
245
-
246
- ## Images and services
247
-
248
- \`Image\` specifies the Docker image for a job:
249
-
250
- {{file:docs-snippets/src/images.ts}}
251
-
252
- ## Workflow
253
-
254
- \`Workflow\` controls pipeline-level settings — when pipelines run, auto-cancellation, and global includes:
255
-
256
- {{file:docs-snippets/src/workflow.ts}}
257
-
258
- ## Default
259
-
260
- \`Default\` sets shared configuration inherited by all jobs:
261
-
262
- {{file:docs-snippets/src/defaults.ts}}
263
-
264
- Jobs can override any default property individually.
265
-
266
- ## Triggers
267
-
268
- \`Trigger\` creates downstream pipeline jobs:
269
-
270
- {{file:docs-snippets/src/trigger.ts}}`,
271
- },
272
- {
273
- slug: "variables",
274
- title: "Predefined Variables",
275
- description: "GitLab CI/CD predefined variable references",
276
- content: `The \`CI\` object provides type-safe access to GitLab CI/CD predefined variables. These map to \`$CI_*\` environment variables at runtime.
277
-
278
- {{file:docs-snippets/src/variables-usage.ts}}
279
-
280
- ## Variable reference
281
-
282
- | Property | Variable | Description |
283
- |----------|----------|-------------|
284
- | \`CI.CommitBranch\` | \`$CI_COMMIT_BRANCH\` | Current branch name (not set for tag pipelines) |
285
- | \`CI.CommitRef\` | \`$CI_COMMIT_REF_NAME\` | Branch or tag name |
286
- | \`CI.CommitSha\` | \`$CI_COMMIT_SHA\` | Full commit SHA |
287
- | \`CI.CommitTag\` | \`$CI_COMMIT_TAG\` | Tag name (only set for tag pipelines) |
288
- | \`CI.DefaultBranch\` | \`$CI_DEFAULT_BRANCH\` | Default branch (usually \`main\`) |
289
- | \`CI.Environment\` | \`$CI_ENVIRONMENT_NAME\` | Environment name (set during deploy jobs) |
290
- | \`CI.JobId\` | \`$CI_JOB_ID\` | Unique job ID |
291
- | \`CI.JobName\` | \`$CI_JOB_NAME\` | Job name |
292
- | \`CI.JobStage\` | \`$CI_JOB_STAGE\` | Job stage name |
293
- | \`CI.MergeRequestIid\` | \`$CI_MERGE_REQUEST_IID\` | MR internal ID (merge request pipelines only) |
294
- | \`CI.PipelineId\` | \`$CI_PIPELINE_ID\` | Unique pipeline ID |
295
- | \`CI.PipelineSource\` | \`$CI_PIPELINE_SOURCE\` | How the pipeline was triggered (\`push\`, \`merge_request_event\`, \`schedule\`, etc.) |
296
- | \`CI.ProjectDir\` | \`$CI_PROJECT_DIR\` | Full path of the repository clone |
297
- | \`CI.ProjectId\` | \`$CI_PROJECT_ID\` | Unique project ID |
298
- | \`CI.ProjectName\` | \`$CI_PROJECT_NAME\` | Project name (URL-safe) |
299
- | \`CI.ProjectPath\` | \`$CI_PROJECT_PATH\` | Project namespace with project name |
300
- | \`CI.Registry\` | \`$CI_REGISTRY\` | Container registry URL |
301
- | \`CI.RegistryImage\` | \`$CI_REGISTRY_IMAGE\` | Registry image path for the project |
302
-
303
- ## Common patterns
304
-
305
- {{file:docs-snippets/src/variables-patterns.ts}}
306
- `,
307
- },
308
- {
309
- slug: "intrinsics-guide",
310
- title: "Intrinsics Guide",
311
- description: "Worked examples for the GitLab reference() intrinsic and its chant syntax",
312
- content: `See [Intrinsic Functions](../intrinsics/) for the generated reference table (name, description, output key, whether it's a tagged template, whether folding can reduce it). This page is the worked-example companion.
313
-
314
- The GitLab lexicon provides one intrinsic function: \`reference()\`, which maps to GitLab's \`!reference\` YAML tag.
315
-
316
- \`reference()\` is a plain function call, not a TypeScript tagged template — like any function call used as a value, it's outside the subset [folding](/chant/concepts/typescript-as-data/#folded-vs-run) can reduce. A file that calls \`reference()\` anywhere in a resource's properties falls back to the normal run path.
317
-
318
- ## \`reference()\` — reuse job properties
319
-
320
- The \`reference()\` intrinsic lets you reuse properties from other jobs or hidden keys. It produces the \`!reference\` YAML tag:
321
-
322
- {{file:docs-snippets/src/reference-basic.ts}}
323
-
324
- Serializes to:
325
-
326
- \`\`\`yaml
327
- deploy:
328
- script: !reference [.setup, script]
329
- \`\`\`
330
-
331
- ### Syntax
332
-
333
- \`\`\`typescript
334
- reference(jobName: string, property: string): ReferenceTag
335
- \`\`\`
336
-
337
- - \`jobName\` — the job or hidden key to reference (e.g. \`".setup"\`, \`"build"\`)
338
- - \`property\` — the property to extract (e.g. \`"script"\`, \`"before_script"\`, \`"rules"\`)
339
-
340
- ### Use cases
341
-
342
- {{file:docs-snippets/src/reference-shared.ts}}
343
-
344
- Produces:
345
-
346
- \`\`\`yaml
347
- test:
348
- stage: test
349
- before_script: !reference [.node-setup, before_script]
350
- script:
351
- - npm test
352
-
353
- lint:
354
- stage: test
355
- before_script: !reference [.node-setup, before_script]
356
- script:
357
- - npm run lint
358
- \`\`\`
359
-
360
- ### When to use \`reference()\` vs direct imports
361
-
362
- {{file:docs-snippets/src/reference-vs-import.ts}}
363
- `,
364
- },
365
- {
366
- slug: "lint-rules",
367
- title: "Lint Rules",
368
- description: "Built-in lint rules and post-synth checks for GitLab CI/CD",
369
- content: `The GitLab lexicon ships lint rules that run during \`chant lint\` and post-synth checks that validate the serialized YAML after \`chant build\`.
370
-
371
- ## Lint rules
372
-
373
- Lint rules analyze your TypeScript source code before build.
374
-
375
- ### WGL001 — Deprecated only/except
376
-
377
- **Severity:** warning | **Category:** style
378
-
379
- Flags usage of \`only:\` and \`except:\` keywords, which are deprecated in favor of \`rules:\`. The \`rules:\` syntax is more flexible and is the recommended approach.
380
-
381
- {{file:docs-snippets/src/lint-wgl001.ts}}
382
-
383
- ### WGL002 — Missing script
384
-
385
- **Severity:** error | **Category:** correctness
386
-
387
- A GitLab CI job must have \`script\`, \`trigger\`, or \`run\` defined. Jobs without any of these will fail pipeline validation.
388
-
389
- {{file:docs-snippets/src/lint-wgl002.ts}}
390
-
391
- ### WGL003 — Missing stage
392
-
393
- **Severity:** info | **Category:** style
394
-
395
- Jobs should declare a \`stage\` property. Without it, the job defaults to the \`test\` stage, which may not be the intended behavior.
396
-
397
- {{file:docs-snippets/src/lint-wgl003.ts}}
398
-
399
- ### WGL004 — Artifacts without expiry
400
-
401
- **Severity:** warning | **Category:** performance
402
-
403
- Flags \`Artifacts\` without \`expireIn\`. Artifacts without expiry are kept indefinitely, consuming storage. Always set an expiration.
404
-
405
- {{file:docs-snippets/src/lint-wgl004.ts}}
406
-
407
- ## Post-synth checks
408
-
409
- Post-synth checks run against the serialized YAML after build. They catch issues only visible in the final output.
410
-
411
- ### WGL010 — Undefined stage
412
-
413
- **Severity:** error
414
-
415
- Flags jobs that reference a stage not present in the collected stages list. This causes a pipeline validation error in GitLab.
416
-
417
- ### WGL011 — Unreachable job
418
-
419
- **Severity:** warning
420
-
421
- Flags jobs where all \`rules:\` entries have \`when: "never"\`, making the job unreachable. This usually indicates a configuration error.
422
-
423
- {{file:docs-snippets/src/lint-wgl011.ts}}
424
-
425
- ### WGL012 — Deprecated property usage
426
-
427
- **Severity:** warning
428
-
429
- Flags properties marked as deprecated in the GitLab CI schema. Deprecation signals are mined from property descriptions (keywords like "deprecated", "legacy", "no longer available"). Using deprecated properties may cause unexpected behavior in future GitLab versions.
430
-
431
- ### WGL013 — Invalid \`needs:\` target
432
-
433
- **Severity:** error
434
-
435
- Flags jobs whose \`needs:\` entries reference a job not defined in the pipeline, or reference themselves. Both cause GitLab pipeline validation failures. When \`include:\` is present, the check is skipped since needed jobs may come from included files.
436
-
437
- ### WGL014 — Invalid \`extends:\` target
438
-
439
- **Severity:** error
440
-
441
- Flags jobs whose \`extends:\` references a template or hidden job not defined in the pipeline. GitLab rejects pipelines with unresolved extends references. When \`include:\` is present, the check is skipped since templates may come from included files.
442
-
443
- ### WGL015 — Circular \`needs:\` chain
444
-
445
- **Severity:** error
446
-
447
- Detects cycles in the \`needs:\` dependency graph. If job A needs B and B needs A (directly or transitively), GitLab rejects the pipeline. Reports the full cycle chain in the diagnostic message.
448
-
449
- ### WGL016 — Secret in a variables block
450
-
451
- **Severity:** error
452
-
453
- Detects hardcoded passwords, tokens, or keys in a \`variables:\` block. Move them to CI/CD masked variables instead of committing them to the pipeline. The precursor to the WGL038–040 secret-scoping checks.
454
-
455
- ### WGL017 — Insecure registry
456
-
457
- **Severity:** warning
458
-
459
- Flags Docker push/pull to a non-HTTPS registry in a job script. HTTP gives the registry traffic no transport integrity.
460
-
461
- ### WGL018 — Missing timeout
462
-
463
- **Severity:** warning
464
-
465
- Flags jobs without an explicit \`timeout:\`. The instance default (often 1 hour) is too long for most jobs and lets a hung job hold a runner.
466
-
467
- ### WGL019 — Missing retry on deploy jobs
468
-
469
- **Severity:** info
470
-
471
- Deploy-stage jobs benefit from a \`retry:\` strategy to ride out transient infrastructure failures. Informational, not required.
472
-
473
- ### WGL020 — Duplicate job names
474
-
475
- **Severity:** error
476
-
477
- Detects multiple jobs that resolve to the same kebab-case key in the serialized YAML. GitLab silently merges duplicate keys, so one job's config quietly overwrites the other.
478
-
479
- ### WGL021 — Unused variables
480
-
481
- **Severity:** warning
482
-
483
- Flags global \`variables:\` not referenced by any job script — usually stale configuration adding noise.
484
-
485
- ### WGL022 — Missing artifacts expiry
486
-
487
- **Severity:** warning
488
-
489
- Flags \`artifacts:\` without \`expire_in:\`. Depending on instance config the default is "never expire," which bloats storage.
490
-
491
- ### WGL023 — Overly broad rules
492
-
493
- **Severity:** info
494
-
495
- Flags a job whose only rule is \`when: always\` with no conditions (\`if:\`, \`changes:\`, …). That disables all pipeline filtering for the job, which is usually unintended.
496
-
497
- ### WGL024 — Manual without allow_failure
498
-
499
- **Severity:** warning
500
-
501
- Flags \`when: manual\` jobs that don't set \`allow_failure: true\`. Without it the manual job blocks the pipeline from progressing past its stage until someone triggers it.
502
-
503
- ### WGL025 — Missing cache key
504
-
505
- **Severity:** warning
506
-
507
- Flags \`cache:\` without a \`key:\`. GitLab falls back to the \`default\` key, causing cache collisions between unrelated jobs on the same runner.
508
-
509
- ### WGL026 — Privileged services without TLS
510
-
511
- **Severity:** warning
512
-
513
- Flags Docker-in-Docker (DinD) services that don't set \`DOCKER_TLS_CERTDIR\`, leaving the Docker daemon on an unencrypted socket. Extended by WGL036 for the merge-request-reachable case.
514
-
515
- ### WGL027 — Empty script
516
-
517
- **Severity:** error
518
-
519
- Detects jobs with \`script: []\` or only empty strings. GitLab rejects empty scripts at pipeline validation time.
520
-
521
- ### WGL028 — Redundant needs
522
-
523
- **Severity:** info
524
-
525
- Detects \`needs:\` entries already implied by stage ordering. Not incorrect, but redundant needs add noise and make the pipeline harder to maintain.
526
-
527
- ## Supply-chain security pass (WGL029–048)
528
-
529
- WGL029 onward are a CI/CD supply-chain security pass, the GitLab counterpart to the github lexicon's GHA029–058: pin & vet includes/components/images, scope \`CI_JOB_TOKEN\` and OIDC, guard trust boundaries against untrusted CI input, mask/protect/scope secrets, reject unsound \`rules:\` expressions, and keep artifacts/caches honest. They run statically on the emitted \`.gitlab-ci.yml\`.
530
-
531
- The checks that need a *moving external truth* — whether a pinned component/include ref still resolves, whether an upstream was archived or moved, whether a new advisory covers a component in use — live in the operational layer instead. Schedule the [\`PipelineAuditOp\`](/chant/guide/ops/#audit-supply-chain-drift) (temporal lexicon) for that live half; it reads the emitted \`include:\` / \`component:\` / \`image:\` references and reports drift via \`report | issue | merge-request\`.
532
-
533
- ### WGL029 — Unpinned include:project / component
534
-
535
- **Severity:** warning
536
-
537
- Flags an \`include:project\` or CI/CD \`component:\` resolved by a moving ref — a branch, a missing \`ref:\` (defaults to the default branch), or a floating component version — instead of a pinned tag or commit SHA.
538
-
539
- ### WGL030 — Mutable or insecure include:remote
540
-
541
- **Severity:** error (HTTP) / warning (HTTPS)
542
-
543
- Flags \`include:remote\` URLs fetched over HTTP (no transport integrity) or over HTTPS but inherently mutable. Prefer a pinned \`include:project\` or component. Generalizes WGL017 to includes.
544
-
545
- ### WGL031 — Container image without a digest
546
-
547
- **Severity:** warning
548
-
549
- Flags \`image:\` and \`services:\` references not pinned to an immutable \`@sha256:\` digest. Variable-based references (e.g. \`$CI_REGISTRY_IMAGE:tag\`) are skipped.
550
-
551
- ### WGL032 — Look-alike include/component source
552
-
553
- **Severity:** warning
554
-
555
- Flags an \`include:project\` / \`component:\` source that is a near-miss (edit distance 1–2) of a well-known GitLab CI source but not an exact match — a likely typo or impersonation. Backed by a vendored reference list.
556
-
557
- ### WGL033 — OIDC id_token without a scoped audience
558
-
559
- **Severity:** warning
560
-
561
- Flags an \`id_tokens:\` declaration with no \`aud:\` or a wildcard audience. The audience binds the minted OIDC token to a relying party; without it a leaked token is accepted anywhere.
562
-
563
- ### WGL034 — OIDC id_token mintable from a merge-request pipeline
564
-
565
- **Severity:** warning
566
-
567
- Flags a job that declares \`id_tokens:\` and is reachable from merge-request pipelines, which outside contributors can trigger. Restrict OIDC jobs to protected refs or require approval.
568
-
569
- > The project-level \`CI_JOB_TOKEN\` allowlist and a variable's protected status are project settings, not emitted pipeline YAML, so they are out of scope for these post-synth checks (see issue #298's caveat).
570
-
571
- ### WGL035 — Untrusted CI variable in a script
572
-
573
- **Severity:** warning
574
-
575
- Flags an attacker-controllable predefined variable (branch/tag name, commit or MR title/description, author) referenced in a \`script:\` command, where a crafted value can inject shell commands. Quote it and avoid using it in sensitive commands.
576
-
577
- ### WGL036 — Privileged DinD reachable from merge requests
578
-
579
- **Severity:** warning
580
-
581
- Flags a Docker-in-Docker (privileged) job reachable from merge-request pipelines, which outside contributors can trigger. Restrict privileged services to protected refs. Complements WGL026.
582
-
583
- ### WGL037 — Regex gate on an untrusted ref
584
-
585
- **Severity:** warning
586
-
587
- Flags a \`rules:if\` that gates on a regex match (\`=~\`) over an attacker-controllable ref variable — a crafted branch/tag name can satisfy the pattern. Match the full ref with \`==\` or gate on a protected condition.
588
-
589
- ### WGL038 — Secret reachable from a merge-request pipeline
590
-
591
- **Severity:** warning
592
-
593
- Flags a user-defined secret-like variable read by a job reachable from merge-request pipelines, which can run untrusted code. Gate the job to protected refs or mark the variable protected. (Built-in \`CI_*\` variables are excluded.)
594
-
595
- ### WGL039 — Secret echoed to job logs
596
-
597
- **Severity:** warning
598
-
599
- Flags a \`script:\` command that prints a secret-like variable (\`echo\`/\`printf\`/\`cat\`). Logs are broadly readable and masking can be defeated by transforms.
600
-
601
- ### WGL040 — Hardcoded registry credential
602
-
603
- **Severity:** error
604
-
605
- Flags a \`docker login\` (or compatible) passing a literal password via \`-p\`/\`--password\` instead of a variable or \`--password-stdin\`. Extends WGL016 to scripts.
606
-
607
- > Variable *masking* and *protected* status are GitLab project/CI-settings, not emitted YAML — WGL016 already nudges toward masked variables; this group covers the exposure paths visible in the pipeline.
608
-
609
- ### WGL041 — Unsound rules:if condition
610
-
611
- **Severity:** warning
612
-
613
- Flags a tautological \`rules:if\` where both sides of \`==\`/\`!=\` are identical, so the condition is always true or always false. Generalizes WGL011 to conditions presented as gates.
614
-
615
- ### WGL042 — Unreachable rules after an unconditional match
616
-
617
- **Severity:** warning
618
-
619
- Flags rules listed after an unconditional rule (no \`if:\`, not \`when: never\`). GitLab takes the first matching rule, so a catch-all makes everything after it dead. Put specific rules first.
620
-
621
- ### WGL043 — Match-anything regex gate
622
-
623
- **Severity:** warning
624
-
625
- Flags a \`rules:if\` whose \`=~\` regex matches every value (empty, dot-star, anchored dot-star) — a filter that admits everything. Tighten the pattern or remove the gate.
626
-
627
- > The auto-fix acceptance item is N/A at the post-synth layer (PostSynthDiagnostic has no fix channel); it belongs to the declarative source-lint rules.
628
-
629
- ### WGL044 — Public artifacts
630
-
631
- **Severity:** warning
632
-
633
- Flags \`artifacts:public: true\`, which makes build output downloadable by anyone. Keep artifacts private unless they are meant to be world-readable.
634
-
635
- ### WGL045 — Credential-bearing artifact path
636
-
637
- **Severity:** error
638
-
639
- Flags an \`artifacts:paths:\` entry that looks like a credential or sensitive file (\`.env\`, \`*.pem\`, \`id_rsa\`, \`*.key\`, \`.npmrc\`, \`.netrc\`, \`credentials\`). Artifacts flow downstream and are downloadable — exclude the file.
640
-
641
- ### WGL046 — Cache poisoning from a merge-request pipeline
642
-
643
- **Severity:** warning
644
-
645
- Flags a job that writes a cache (push policy) and is reachable from merge-request pipelines. An MR can poison the cache for a later protected run that restores the same key. Restrict cache writes to protected refs or scope the key.
646
-
647
- > Artifact / \`dependencies:\` flow across a *protected* boundary depends on a ref's protected status, a project setting not present in emitted YAML — out of scope here.
648
-
649
- ### WGL047 — Software fetched and piped to a shell
650
-
651
- **Severity:** warning
652
-
653
- Flags a \`script:\` command that pipes a network download straight into a shell (\`curl ... | bash\`). The fetched code is unpinned and unverified — download to a file, verify a checksum/signature, then run it.
654
-
655
- ### WGL048 — Pipeline without a name
656
-
657
- **Severity:** info
658
-
659
- Flags a pipeline that defines a \`workflow:\` block but no \`workflow:name\`. A pipeline name aids identification in the GitLab UI and audit output.
660
-
661
- > Two #304 items stay out of scope: a broad privileged-service / DinD check (already covered by WGL026 for DinD-TLS and WGL036 for MR-reachable DinD; a runner's privileged isolation is not in emitted YAML) and an optional include/component allowlist policy (configuration, overlapping WGL032's vendored source list).
662
-
663
- ## Running lint
664
-
665
- \`\`\`bash
666
- # Lint your chant project
667
- chant lint
668
-
669
- # Lint with auto-fix where supported
670
- chant lint --fix
671
- \`\`\`
672
-
673
- To suppress a rule on a specific line:
674
-
675
- \`\`\`typescript
676
- // chant-disable-next-line WGL001
677
- export const deploy = new Job({ only: ["main"], script: ["deploy"] });
678
- \`\`\`
679
-
680
- To suppress globally in \`chant.config.ts\`:
681
-
682
- \`\`\`typescript
683
- export default {
684
- lint: {
685
- rules: {
686
- WGL003: "off",
687
- },
688
- },
689
- };
690
- \`\`\`
691
- `,
692
- },
693
- {
694
- slug: "examples",
695
- title: "Examples",
696
- description: "Walkthrough of GitLab CI/CD examples — pipelines, composites, and cross-lexicon patterns",
697
- content: `Runnable examples live in the lexicon's \`examples/\` directory. Clone the repo and try them:
698
-
699
- \`\`\`bash
700
- cd examples/getting-started
701
- npm install
702
- chant build # produces .gitlab-ci.yml
703
- chant lint # runs lint rules
704
- npx vitest run # run the tests
705
- \`\`\`
706
-
707
- ## Getting Started
708
-
709
- \`examples/getting-started/\` — a 2-stage Node.js pipeline with build and test jobs.
710
-
711
- \`\`\`
712
- src/
713
- ├── config.ts # Shared config: image, cache
714
- └── pipeline.ts # Job definitions: build, test
715
- \`\`\`
716
-
717
- ### Shared configuration
718
-
719
- \`config.ts\` extracts reusable objects — image and cache — so jobs stay concise:
720
-
721
- {{file:getting-started/src/config.ts}}
722
-
723
- ### Pipeline jobs
724
-
725
- \`pipeline.ts\` defines two jobs that import shared config:
726
-
727
- {{file:getting-started/src/pipeline.ts}}
728
-
729
- ### Generated output
730
-
731
- \`chant build\` produces this \`.gitlab-ci.yml\`:
732
-
733
- \`\`\`yaml
734
- stages:
735
- - build
736
- - test
737
-
738
- build:
739
- stage: build
740
- image:
741
- name: node:20-alpine
742
- cache:
743
- key: '$CI_COMMIT_REF_SLUG'
744
- paths:
745
- - node_modules/
746
- policy: pull-push
747
- script:
748
- - npm install
749
- - npm run build
750
-
751
- test:
752
- stage: test
753
- image:
754
- name: node:20-alpine
755
- cache:
756
- key: '$CI_COMMIT_REF_SLUG'
757
- paths:
758
- - node_modules/
759
- policy: pull-push
760
- script:
761
- - npm install
762
- - npm test
763
- artifacts:
764
- reports:
765
- junit: coverage/junit.xml
766
- paths:
767
- - coverage/
768
- expire_in: '1 week'
769
- \`\`\`
770
-
771
- **Patterns demonstrated:**
772
-
773
- 1. **Shared config** — reusable image and cache extracted into \`config.ts\`
774
- 2. **JUnit reports** — test artifacts include JUnit XML for GitLab MR display
775
- 3. **Stage ordering** — stages collected automatically from job declarations
776
-
777
- ## Docker Build
778
-
779
- \`examples/docker-build/\` — builds and pushes a Docker image using the \`DockerBuild\` composite.
780
-
781
- {{file:docker-build/src/pipeline.ts}}
782
-
783
- The \`DockerBuild\` composite expands to a job with Docker-in-Docker service, registry login, build, and push steps.
784
-
785
- ## Node Pipeline
786
-
787
- \`examples/node-pipeline/\` — a full Node.js CI pipeline using the \`NodePipeline\` composite.
788
-
789
- {{file:node-pipeline/src/pipeline.ts}}
790
-
791
- ## Python Pipeline
792
-
793
- \`examples/python-pipeline/\` — a Python CI pipeline using the \`PythonPipeline\` composite.
794
-
795
- {{file:python-pipeline/src/pipeline.ts}}
796
-
797
- ## Review App
798
-
799
- \`examples/review-app/\` — deploys a review environment per merge request using the \`ReviewApp\` composite.
800
-
801
- {{file:review-app/src/pipeline.ts}}
802
-
803
- ## Merge-request plan widget
804
-
805
- The \`MrPlanReport\` composite turns \`chant lifecycle plan\` into the GitLab merge-request plan widget — the "N to add, M to change, K to delete" summary GitLab renders from an \`artifacts:reports:terraform\` artifact:
806
-
807
- \`\`\`typescript
808
- import { MrPlanReport } from "@intentius/chant-lexicon-gitlab";
809
-
810
- export const plan = MrPlanReport({
811
- environment: "prod",
812
- // credential setup — the plan queries the live system to classify drift
813
- before: ["aws sts get-caller-identity"],
814
- });
815
- \`\`\`
816
-
817
- The job runs \`chant lifecycle plan prod --report gitlab-mr\`, writes the count JSON to \`tfplan.json\`, and declares it as \`artifacts:reports:terraform\`. On the merge request, GitLab shows the plan summary inline.
818
-
819
- Caveats worth knowing:
820
-
821
- - The widget label always reads **"Terraform"** — that is GitLab's fixed string for this report type, not a claim chant makes.
822
- - It is **counts only** (create/update/delete). \`adopt\` and \`noop\` are excluded, since the widget has no column for live-but-undeclared or no-change. There is no per-resource breakdown — run \`chant lifecycle plan\` in the job log for that.
823
- - It is a **GitLab-only** surface. The same plan JSON is portable, but the widget is GitLab's.
824
- - The plan reads the live system, so the job needs cloud credentials — wire them via \`before\` or CI variables.
825
-
826
- ## AWS ALB Deployment
827
-
828
- A cross-lexicon example showing how to deploy AWS CloudFormation stacks from GitLab CI. Three separate pipelines mirror the separate-project AWS ALB pattern:
829
-
830
- ### Infra pipeline
831
-
832
- Deploys the shared ALB stack (VPC, ALB, ECS cluster, ECR repos):
833
-
834
- \`\`\`typescript
835
- import { Job, Image, Rule } from "@intentius/chant-lexicon-gitlab";
836
-
837
- const awsImage = new Image({ name: "amazon/aws-cli:latest" });
838
-
839
- export const deployInfra = new Job({
840
- stage: "deploy",
841
- image: awsImage,
842
- script: [
843
- "aws cloudformation deploy --template-file templates/template.json --stack-name shared-alb --capabilities CAPABILITY_IAM --no-fail-on-empty-changeset",
844
- ],
845
- rules: [
846
- new Rule({ if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" }),
847
- ],
848
- });
849
- \`\`\`
850
-
851
- ### Service pipeline (API)
852
-
853
- Builds a Docker image, pushes to ECR, and deploys the API service stack with cross-stack parameter passing. The full source lives in the cross-lexicon example \`examples/gitlab-aws-alb-api/\`:
854
-
855
- {{file:../../../examples/gitlab-aws-alb-api/src/pipeline.ts}}
856
-
857
- **Key patterns:**
858
-
859
- 1. **ECR login** — uses \`aws ecr get-login-password\` instead of GitLab registry credentials
860
- 2. **Cross-stack parameter passing** — \`describe-stacks\` fetches outputs from the infra stack, \`jq\` maps them to \`--parameter-overrides\`
861
- 3. **Job naming** — \`buildImage\` serializes to \`build-image\` in YAML; \`Need\` references must use kebab-case
862
- 4. **Docker-in-Docker** — \`docker:27-cli\` image with \`docker:27-dind\` service for container builds
863
-
864
- The full examples live in \`examples/gitlab-aws-alb-infra/\`, \`examples/gitlab-aws-alb-api/\`, and \`examples/gitlab-aws-alb-ui/\`.
865
- `,
866
- },
867
- {
868
- slug: "skills",
869
- title: "AI Skills",
870
- description: "AI agent skills bundled with the GitLab CI/CD lexicon",
871
- content: `The GitLab lexicon ships an AI skill called **chant-gitlab** that teaches AI coding agents (like Claude Code) how to build, validate, and deploy GitLab CI pipelines from a chant project.
872
-
873
- ## What are skills?
874
-
875
- Skills are structured markdown documents bundled with a lexicon. When an AI agent works in a chant project, it discovers and loads relevant skills automatically — giving it operational knowledge about the deployment workflow without requiring the user to explain each step.
876
-
877
- ## Installation
878
-
879
- When you scaffold a new project with \`chant init --lexicon gitlab\`, the skill is installed to \`skills/chant-gitlab/SKILL.md\` for automatic discovery by Claude Code.
880
-
881
- For existing projects, create the file manually:
882
-
883
- \`\`\`
884
- .claude/
885
- skills/
886
- chant-gitlab/
887
- SKILL.md # skill content (see below)
888
- \`\`\`
889
-
890
- ## Skill: chant-gitlab
891
-
892
- The \`chant-gitlab\` skill covers the full deployment lifecycle:
893
-
894
- - **Build** — \`chant build src/ --output .gitlab-ci.yml\`
895
- - **Validate** — \`chant lint src/\` + GitLab CI Lint API
896
- - **Deploy** — commit and push the generated YAML
897
- - **Status** — GitLab UI or pipelines API
898
- - **Retry** — retry failed jobs via UI or API
899
- - **Cancel** — cancel running pipelines via API
900
- - **Troubleshooting** — job logs, lint rule codes (WGL001–WGL004), post-synth checks (WGL010–WGL048)
901
-
902
- The skill is invocable as a slash command: \`/chant-gitlab\`
903
-
904
- ## Skill: chant-gitlab-migrate
905
-
906
- Operational glue for translating GitHub Actions workflows to GitLab CI/CD. The skill detects the user's intent (paste a \`.github/workflows/*.yml\`, ask about migrating, etc.), invokes \`chant migrate\`, surfaces the report, and suggests GitLab-native upgrade moments like \`--use-composites\`.
907
-
908
- See [Migration](../migration) for the full CLI surface, supported translations, and limitations.
909
-
910
- ## MCP integration
911
-
912
- The lexicon also provides MCP (Model Context Protocol) tools and resources that AI agents can use programmatically:
913
-
914
- | MCP tool | Description |
915
- |----------|-------------|
916
- | \`build\` | Build the chant project |
917
- | \`lint\` | Run lint rules |
918
- | \`explain\` | Summarize project resources |
919
- | \`scaffold\` | Generate starter files |
920
- | \`search\` | Search available resource types |
921
- | \`gitlab:diff\` | Compare current build output against previous |
922
- | \`gitlab:migrate\` | Translate a GitHub Actions workflow into GitLab CI/CD (see [Migration](../migration)) |
923
- | \`gitlab:checks\` | Build and return the pipeline's security/correctness findings (the WGL checks) |
924
- | \`gitlab:pipeline\` | Build and return the pipeline's stages and jobs (name, stage, run order), as written |
925
- | \`gitlab:references\` | Build and list what the pipeline pulls in (includes, components, images) and whether each is pinned |
926
- | \`gitlab:affected\` | Given a job, list the jobs that would re-run because they depend on it |
927
- | \`gitlab:pipeline-yaml\` | Build and return the generated \`.gitlab-ci.yml\` |
928
- | \`gitlab:source\` | Given a job, where it came from in the TypeScript — the declaring file and the composite that expanded it, if any (entity-level, not a YAML-line source map) |
929
- | \`gitlab:owns\` | Given a job, whether it is declared (owned) by chant in this project. Pipeline jobs are not taggable cloud resources, so ownership here means "declared here" — live ownership markers apply to cloud lexicons |
930
- | \`gitlab:compare\` | Given a GitHub Actions workflow file, migrate it to GitLab CI and report which security properties survive (translated/approximated/needs-review/lost) — the migration safety view |
931
-
932
- The \`gitlab:checks\` / \`gitlab:pipeline\` / \`gitlab:references\` / \`gitlab:affected\` / \`gitlab:pipeline-yaml\` / \`gitlab:source\` / \`gitlab:owns\` / \`gitlab:compare\` tools are **read-only**: they build (or migrate) from source and never touch the live GitLab instance. They give an agent a *before-it-runs* view of the pipeline — what it does, what it pulls in, whether it is safe, where it came from, and what survives a migration — to complement the *after-it-ran* view it gets from the instance.
933
-
934
- > **Why no \`github:compare\`?** The GitHub → GitLab migration lives in the GitLab lexicon (the GitHub lexicon does not depend on GitLab). A \`github:compare\` would invert that dependency, so the migration safety view is exposed once, here, as \`gitlab:compare\`.
935
-
936
- | MCP resource | Description |
937
- |--------------|-------------|
938
- | \`resource-catalog\` | JSON list of all supported GitLab CI entity types |
939
- | \`examples/basic-pipeline\` | Example pipeline with build, test, and deploy jobs |`,
940
- },
941
- ],
942
110
  extraSections: [
943
111
  {
944
112
  title: "Migrating from GitHub Actions",
945
113
  content: `\`chant migrate\` translates GitHub Actions workflows into GitLab CI/CD pipelines or typed chant source. See [Migration](./migration) for the full CLI surface, supported translations, and limitations.`,
946
114
  },
947
115
  ],
948
- sidebarExtra: [{ label: "Migration", slug: "migration" }],
949
116
  basePath: "/chant/lexicons/gitlab/",
950
117
  };
951
118
 
@@ -107,7 +107,7 @@ describe("gitlabSerializer.serialize", () => {
107
107
  entities.set("jobA", new MockJob({ stage: "build", script: ["build1"] }));
108
108
  entities.set("jobB", new MockJob({ stage: "build", script: ["build2"] }));
109
109
 
110
- const output = gitlabSerializer.serialize(entities);
110
+ const output = gitlabSerializer.serialize(entities) as string;
111
111
  // "build" should appear only once in the stages list
112
112
  const stagesSection = output.split("\n\n")[0]; // stages is the first section
113
113
  const stagesMatch = stagesSection.match(/- build/g);