@intentius/chant-lexicon-gitlab 0.0.1
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/package.json +27 -0
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +33 -0
- package/src/codegen/docs-cli.ts +3 -0
- package/src/codegen/docs.ts +962 -0
- package/src/codegen/fetch.ts +73 -0
- package/src/codegen/generate-cli.ts +41 -0
- package/src/codegen/generate-lexicon.ts +53 -0
- package/src/codegen/generate-typescript.ts +144 -0
- package/src/codegen/generate.ts +166 -0
- package/src/codegen/naming.ts +52 -0
- package/src/codegen/package.ts +64 -0
- package/src/codegen/parse.test.ts +195 -0
- package/src/codegen/parse.ts +531 -0
- package/src/codegen/patches.test.ts +99 -0
- package/src/codegen/patches.ts +100 -0
- package/src/codegen/rollback.ts +26 -0
- package/src/codegen/snapshot.test.ts +109 -0
- package/src/coverage.test.ts +39 -0
- package/src/coverage.ts +52 -0
- package/src/generated/index.d.ts +248 -0
- package/src/generated/index.ts +23 -0
- package/src/generated/lexicon-gitlab.json +77 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +151 -0
- package/src/import/generator.ts +173 -0
- package/src/import/parser.test.ts +160 -0
- package/src/import/parser.ts +282 -0
- package/src/import/roundtrip.test.ts +89 -0
- package/src/index.ts +25 -0
- package/src/intrinsics.test.ts +42 -0
- package/src/intrinsics.ts +40 -0
- package/src/lint/post-synth/post-synth.test.ts +155 -0
- package/src/lint/post-synth/wgl010.ts +41 -0
- package/src/lint/post-synth/wgl011.ts +54 -0
- package/src/lint/post-synth/yaml-helpers.ts +88 -0
- package/src/lint/rules/artifact-no-expiry.ts +62 -0
- package/src/lint/rules/deprecated-only-except.ts +53 -0
- package/src/lint/rules/index.ts +8 -0
- package/src/lint/rules/missing-script.ts +65 -0
- package/src/lint/rules/missing-stage.ts +62 -0
- package/src/lint/rules/rules.test.ts +146 -0
- package/src/lsp/completions.test.ts +85 -0
- package/src/lsp/completions.ts +18 -0
- package/src/lsp/hover.test.ts +60 -0
- package/src/lsp/hover.ts +36 -0
- package/src/plugin.test.ts +228 -0
- package/src/plugin.ts +380 -0
- package/src/serializer.test.ts +309 -0
- package/src/serializer.ts +226 -0
- package/src/testdata/ci-schema-fixture.json +2184 -0
- package/src/testdata/create-fixture.ts +46 -0
- package/src/testdata/load-fixtures.ts +23 -0
- package/src/validate-cli.ts +19 -0
- package/src/validate.test.ts +43 -0
- package/src/validate.ts +125 -0
- package/src/variables.ts +27 -0
|
@@ -0,0 +1,962 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation generation for GitLab CI lexicon.
|
|
3
|
+
*
|
|
4
|
+
* Generates Starlight MDX pages for CI entities using the core docs pipeline.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { docsPipeline, writeDocsSite, type DocsConfig } from "@intentius/chant/codegen/docs";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extract service name from GitLab CI type: "GitLab::CI::Job" → "CI"
|
|
13
|
+
*/
|
|
14
|
+
function serviceFromType(resourceType: string): string {
|
|
15
|
+
const parts = resourceType.split("::");
|
|
16
|
+
return parts.length >= 2 ? parts[1] : "CI";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const overview = `The **GitLab CI/CD** lexicon provides typed constructors for GitLab CI pipeline
|
|
20
|
+
configuration. It covers jobs, workflow settings, artifacts, caching, images,
|
|
21
|
+
rules, environments, triggers, and more.
|
|
22
|
+
|
|
23
|
+
Install it with:
|
|
24
|
+
|
|
25
|
+
\`\`\`bash
|
|
26
|
+
npm install --save-dev @intentius/chant-lexicon-gitlab
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
\`\`\`typescript
|
|
32
|
+
import { Job, Image, Cache, Artifacts, CI } from "@intentius/chant-lexicon-gitlab";
|
|
33
|
+
|
|
34
|
+
export const test = new Job({
|
|
35
|
+
stage: "test",
|
|
36
|
+
image: new Image({ name: "node:20" }),
|
|
37
|
+
cache: new Cache({ key: CI.CommitRef, paths: ["node_modules/"] }),
|
|
38
|
+
script: ["npm ci", "npm test"],
|
|
39
|
+
artifacts: new Artifacts({
|
|
40
|
+
paths: ["coverage/"],
|
|
41
|
+
expireIn: "1 week",
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
The lexicon provides **3 resources** (Job, Workflow, Default), **13 property types** (Image, Cache, Artifacts, Rule, Environment, Trigger, and more), the \`CI\` pseudo-parameter object for predefined variables, and the \`reference()\` intrinsic for YAML \`!reference\` tags.
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const outputFormat = `The GitLab lexicon serializes resources into **\`.gitlab-ci.yml\` YAML**. Keys are
|
|
50
|
+
converted to \`snake_case\` and jobs use kebab-case names. Stages are automatically
|
|
51
|
+
collected from all job definitions.
|
|
52
|
+
|
|
53
|
+
## Building
|
|
54
|
+
|
|
55
|
+
Run \`chant build\` to produce a \`.gitlab-ci.yml\` from your declarations:
|
|
56
|
+
|
|
57
|
+
\`\`\`bash
|
|
58
|
+
chant build
|
|
59
|
+
# Writes dist/.gitlab-ci.yml
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
The generated file includes:
|
|
63
|
+
|
|
64
|
+
- \`stages:\` list — automatically collected from all job \`stage\` properties
|
|
65
|
+
- \`default:\` section — if a \`Default\` resource is exported
|
|
66
|
+
- \`workflow:\` section — if a \`Workflow\` resource is exported
|
|
67
|
+
- Job definitions with \`snake_case\` keys and \`kebab-case\` job names
|
|
68
|
+
|
|
69
|
+
## Key conversions
|
|
70
|
+
|
|
71
|
+
| Chant (TypeScript) | YAML output | Rule |
|
|
72
|
+
|--------------------|-------------|------|
|
|
73
|
+
| \`export const buildApp = new Job({...})\` | \`build-app:\` | Export name → kebab-case job key |
|
|
74
|
+
| \`expireIn: "1 week"\` | \`expire_in: 1 week\` | camelCase → snake_case |
|
|
75
|
+
| \`ifCondition: ...\` | \`if: ...\` | Reserved word properties use suffixed names |
|
|
76
|
+
| \`new Image({ name: "node:20" })\` | \`image: node:20\` | Single-property objects are collapsed |
|
|
77
|
+
|
|
78
|
+
## Validating locally
|
|
79
|
+
|
|
80
|
+
The output is standard GitLab CI YAML. Validate with the GitLab CI Lint API or locally:
|
|
81
|
+
|
|
82
|
+
\`\`\`bash
|
|
83
|
+
# Using the GitLab API
|
|
84
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
85
|
+
--data-urlencode "content=$(cat dist/.gitlab-ci.yml)" \\
|
|
86
|
+
"https://gitlab.com/api/v4/ci/lint"
|
|
87
|
+
|
|
88
|
+
# Using the glab CLI
|
|
89
|
+
glab ci lint dist/.gitlab-ci.yml
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
## Compatibility
|
|
93
|
+
|
|
94
|
+
The output is compatible with:
|
|
95
|
+
- GitLab CI/CD (any recent GitLab version)
|
|
96
|
+
- GitLab CI Lint API
|
|
97
|
+
- \`glab\` CLI tool
|
|
98
|
+
- Any tool that processes \`.gitlab-ci.yml\` files`;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate documentation for the GitLab CI lexicon.
|
|
102
|
+
*/
|
|
103
|
+
export async function generateDocs(opts?: { verbose?: boolean }): Promise<void> {
|
|
104
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
105
|
+
|
|
106
|
+
const config: DocsConfig = {
|
|
107
|
+
name: "gitlab",
|
|
108
|
+
displayName: "GitLab CI/CD",
|
|
109
|
+
description: "Typed constructors for GitLab CI/CD pipeline configuration",
|
|
110
|
+
distDir: join(pkgDir, "dist"),
|
|
111
|
+
outDir: join(pkgDir, "docs"),
|
|
112
|
+
overview,
|
|
113
|
+
outputFormat,
|
|
114
|
+
serviceFromType,
|
|
115
|
+
suppressPages: ["intrinsics", "rules"],
|
|
116
|
+
extraPages: [
|
|
117
|
+
{
|
|
118
|
+
slug: "pipeline-concepts",
|
|
119
|
+
title: "Pipeline Concepts",
|
|
120
|
+
description: "Jobs, stages, artifacts, caching, images, rules, environments, and triggers in the GitLab CI/CD lexicon",
|
|
121
|
+
content: `Every exported \`Job\` declaration becomes a job entry in the generated \`.gitlab-ci.yml\`. The serializer handles the translation automatically:
|
|
122
|
+
|
|
123
|
+
- Converts camelCase property names to snake_case (\`expireIn\` → \`expire_in\`)
|
|
124
|
+
- Converts export names to kebab-case job keys (\`buildApp\` → \`build-app\`)
|
|
125
|
+
- Collects stages from all jobs into a \`stages:\` list
|
|
126
|
+
- Collapses single-property objects (\`new Image({ name: "node:20" })\` → \`image: node:20\`)
|
|
127
|
+
|
|
128
|
+
\`\`\`typescript
|
|
129
|
+
// This chant declaration...
|
|
130
|
+
export const buildApp = new Job({
|
|
131
|
+
stage: "build",
|
|
132
|
+
image: new Image({ name: "node:20" }),
|
|
133
|
+
script: ["npm ci", "npm run build"],
|
|
134
|
+
});
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
Produces this YAML:
|
|
138
|
+
|
|
139
|
+
\`\`\`yaml
|
|
140
|
+
stages:
|
|
141
|
+
- build
|
|
142
|
+
|
|
143
|
+
build-app:
|
|
144
|
+
stage: build
|
|
145
|
+
image: node:20
|
|
146
|
+
script:
|
|
147
|
+
- npm ci
|
|
148
|
+
- npm run build
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
## Resource types
|
|
152
|
+
|
|
153
|
+
The lexicon provides 3 resource types and 13 property types:
|
|
154
|
+
|
|
155
|
+
### Resources
|
|
156
|
+
|
|
157
|
+
| Type | Description |
|
|
158
|
+
|------|-------------|
|
|
159
|
+
| \`Job\` | A CI/CD job — the fundamental unit of a pipeline |
|
|
160
|
+
| \`Workflow\` | Top-level \`workflow:\` configuration (pipeline-level rules, name, auto_cancel) |
|
|
161
|
+
| \`Default\` | Top-level \`default:\` block (shared defaults inherited by all jobs) |
|
|
162
|
+
|
|
163
|
+
### Property types
|
|
164
|
+
|
|
165
|
+
| Type | Used in | Description |
|
|
166
|
+
|------|---------|-------------|
|
|
167
|
+
| \`Image\` | Job, Default | Docker image for the job runner |
|
|
168
|
+
| \`Cache\` | Job, Default | Files cached between pipeline runs |
|
|
169
|
+
| \`Artifacts\` | Job | Files passed between stages or stored after completion |
|
|
170
|
+
| \`Rule\` | Job, Workflow | Conditional execution rules (\`rules:\` entries) |
|
|
171
|
+
| \`Environment\` | Job | Deployment target environment |
|
|
172
|
+
| \`Trigger\` | Job | Downstream pipeline trigger |
|
|
173
|
+
| \`Include\` | Workflow | External YAML file inclusion |
|
|
174
|
+
| \`AllowFailure\` | Job | Failure tolerance configuration |
|
|
175
|
+
| \`Retry\` | Job | Automatic retry on failure |
|
|
176
|
+
| \`Parallel\` | Job | Job parallelization (matrix builds) |
|
|
177
|
+
| \`Release\` | Job | GitLab Release creation |
|
|
178
|
+
| \`AutoCancel\` | Workflow | Pipeline auto-cancellation settings |
|
|
179
|
+
|
|
180
|
+
## The barrel file
|
|
181
|
+
|
|
182
|
+
Every chant project has a barrel file (conventionally \`_.ts\`) that re-exports the lexicon:
|
|
183
|
+
|
|
184
|
+
\`\`\`typescript
|
|
185
|
+
// _.ts — the barrel file
|
|
186
|
+
export * from "@intentius/chant-lexicon-gitlab";
|
|
187
|
+
export * from "./config";
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
Other files import the barrel and use its exports:
|
|
191
|
+
|
|
192
|
+
\`\`\`typescript
|
|
193
|
+
// pipeline.ts
|
|
194
|
+
import * as _ from "./_";
|
|
195
|
+
|
|
196
|
+
export const build = new _.Job({
|
|
197
|
+
stage: "build",
|
|
198
|
+
image: _.nodeImage, // from config.ts via barrel
|
|
199
|
+
cache: _.npmCache, // from config.ts via barrel
|
|
200
|
+
script: ["npm ci", "npm run build"],
|
|
201
|
+
artifacts: _.buildArtifacts,
|
|
202
|
+
});
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
## Jobs
|
|
206
|
+
|
|
207
|
+
A \`Job\` is the fundamental unit. Every exported \`Job\` becomes a job entry in the YAML:
|
|
208
|
+
|
|
209
|
+
\`\`\`typescript
|
|
210
|
+
export const test = new Job({
|
|
211
|
+
stage: "test",
|
|
212
|
+
image: new Image({ name: "node:20-alpine" }),
|
|
213
|
+
script: ["npm ci", "npm test"],
|
|
214
|
+
artifacts: new Artifacts({
|
|
215
|
+
paths: ["coverage/"],
|
|
216
|
+
expireIn: "1 week",
|
|
217
|
+
reports: { junit: "coverage/junit.xml" },
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
220
|
+
\`\`\`
|
|
221
|
+
|
|
222
|
+
Key properties:
|
|
223
|
+
- \`script\` — **required** (or \`trigger\`/\`run\`). Array of shell commands to execute.
|
|
224
|
+
- \`stage\` — which pipeline stage this job belongs to. Defaults to \`test\` if omitted.
|
|
225
|
+
- \`image\` — Docker image. Use \`new Image({ name: "..." })\` or pass a string to the YAML.
|
|
226
|
+
- \`needs\` — job dependencies for DAG-mode execution (run before stage ordering).
|
|
227
|
+
|
|
228
|
+
## Stages
|
|
229
|
+
|
|
230
|
+
Stages define the execution order of a pipeline. The serializer automatically collects unique stage values from all jobs:
|
|
231
|
+
|
|
232
|
+
\`\`\`typescript
|
|
233
|
+
export const lint = new Job({ stage: "test", script: ["npm run lint"] });
|
|
234
|
+
export const test = new Job({ stage: "test", script: ["npm test"] });
|
|
235
|
+
export const build = new Job({ stage: "build", script: ["npm run build"] });
|
|
236
|
+
export const deploy = new Job({ stage: "deploy", script: ["npm run deploy"] });
|
|
237
|
+
\`\`\`
|
|
238
|
+
|
|
239
|
+
Produces:
|
|
240
|
+
|
|
241
|
+
\`\`\`yaml
|
|
242
|
+
stages:
|
|
243
|
+
- test
|
|
244
|
+
- build
|
|
245
|
+
- deploy
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
Jobs in the same stage run in parallel. Stages run sequentially in declaration order.
|
|
249
|
+
|
|
250
|
+
## Artifacts and caching
|
|
251
|
+
|
|
252
|
+
**Artifacts** are files produced by a job and passed to later stages or stored for download:
|
|
253
|
+
|
|
254
|
+
\`\`\`typescript
|
|
255
|
+
export const buildArtifacts = new Artifacts({
|
|
256
|
+
paths: ["dist/"],
|
|
257
|
+
expireIn: "1 hour", // always set expiry (WGL004 warns if missing)
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
export const testArtifacts = new Artifacts({
|
|
261
|
+
paths: ["coverage/"],
|
|
262
|
+
expireIn: "1 week",
|
|
263
|
+
reports: { junit: "coverage/junit.xml" }, // parsed by GitLab for MR display
|
|
264
|
+
});
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
**Caches** persist files between pipeline runs to speed up builds:
|
|
268
|
+
|
|
269
|
+
\`\`\`typescript
|
|
270
|
+
export const npmCache = new Cache({
|
|
271
|
+
key: "$CI_COMMIT_REF_SLUG", // cache per branch
|
|
272
|
+
paths: ["node_modules/"],
|
|
273
|
+
policy: "pull-push", // "pull" for read-only, "push" for write-only
|
|
274
|
+
});
|
|
275
|
+
\`\`\`
|
|
276
|
+
|
|
277
|
+
The key difference: artifacts are for passing files between **stages in the same pipeline**; caches are for speeding up **repeated pipeline runs**.
|
|
278
|
+
|
|
279
|
+
## Conditional execution with rules
|
|
280
|
+
|
|
281
|
+
\`Rule\` objects control when a job runs. They map to \`rules:\` entries in the YAML:
|
|
282
|
+
|
|
283
|
+
\`\`\`typescript
|
|
284
|
+
export const onMergeRequest = new Rule({
|
|
285
|
+
ifCondition: CI.MergeRequestIid, // → if: $CI_MERGE_REQUEST_IID
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
export const onDefaultBranch = new Rule({
|
|
289
|
+
ifCondition: \`\${CI.CommitBranch} == \${CI.DefaultBranch}\`,
|
|
290
|
+
when: "manual", // require manual trigger
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
export const deploy = new Job({
|
|
294
|
+
stage: "deploy",
|
|
295
|
+
script: ["npm run deploy"],
|
|
296
|
+
rules: [onDefaultBranch],
|
|
297
|
+
});
|
|
298
|
+
\`\`\`
|
|
299
|
+
|
|
300
|
+
Produces:
|
|
301
|
+
|
|
302
|
+
\`\`\`yaml
|
|
303
|
+
deploy:
|
|
304
|
+
stage: deploy
|
|
305
|
+
script:
|
|
306
|
+
- npm run deploy
|
|
307
|
+
rules:
|
|
308
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
309
|
+
when: manual
|
|
310
|
+
\`\`\`
|
|
311
|
+
|
|
312
|
+
The \`ifCondition\` property maps to \`if:\` in the YAML (since \`if\` is a reserved word in TypeScript).
|
|
313
|
+
|
|
314
|
+
## Environments
|
|
315
|
+
|
|
316
|
+
\`Environment\` defines a deployment target:
|
|
317
|
+
|
|
318
|
+
\`\`\`typescript
|
|
319
|
+
export const productionEnv = new Environment({
|
|
320
|
+
name: "production",
|
|
321
|
+
url: "https://example.com",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
export const deploy = new Job({
|
|
325
|
+
stage: "deploy",
|
|
326
|
+
script: ["npm run deploy"],
|
|
327
|
+
environment: productionEnv,
|
|
328
|
+
rules: [onDefaultBranch],
|
|
329
|
+
});
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
GitLab tracks deployments to environments and provides rollback capabilities in the UI.
|
|
333
|
+
|
|
334
|
+
## Images and services
|
|
335
|
+
|
|
336
|
+
\`Image\` specifies the Docker image for a job:
|
|
337
|
+
|
|
338
|
+
\`\`\`typescript
|
|
339
|
+
export const nodeImage = new Image({ name: "node:20-alpine" });
|
|
340
|
+
|
|
341
|
+
// With entrypoint override
|
|
342
|
+
export const customImage = new Image({
|
|
343
|
+
name: "registry.example.com/my-image:latest",
|
|
344
|
+
entrypoint: ["/bin/sh", "-c"],
|
|
345
|
+
});
|
|
346
|
+
\`\`\`
|
|
347
|
+
|
|
348
|
+
## Workflow
|
|
349
|
+
|
|
350
|
+
\`Workflow\` controls pipeline-level settings — when pipelines run, auto-cancellation, and global includes:
|
|
351
|
+
|
|
352
|
+
\`\`\`typescript
|
|
353
|
+
export const workflow = new Workflow({
|
|
354
|
+
name: "CI Pipeline for $CI_COMMIT_REF_NAME",
|
|
355
|
+
rules: [
|
|
356
|
+
new Rule({ ifCondition: CI.MergeRequestIid }),
|
|
357
|
+
new Rule({ ifCondition: CI.CommitBranch }),
|
|
358
|
+
],
|
|
359
|
+
autoCancel: new AutoCancel({
|
|
360
|
+
onNewCommit: "interruptible",
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
\`\`\`
|
|
364
|
+
|
|
365
|
+
## Default
|
|
366
|
+
|
|
367
|
+
\`Default\` sets shared configuration inherited by all jobs:
|
|
368
|
+
|
|
369
|
+
\`\`\`typescript
|
|
370
|
+
export const defaults = new Default({
|
|
371
|
+
image: new Image({ name: "node:20-alpine" }),
|
|
372
|
+
cache: new Cache({ key: CI.CommitRef, paths: ["node_modules/"] }),
|
|
373
|
+
retry: new Retry({ max: 2, when: ["runner_system_failure"] }),
|
|
374
|
+
});
|
|
375
|
+
\`\`\`
|
|
376
|
+
|
|
377
|
+
Jobs can override any default property individually.
|
|
378
|
+
|
|
379
|
+
## Triggers
|
|
380
|
+
|
|
381
|
+
\`Trigger\` creates downstream pipeline jobs:
|
|
382
|
+
|
|
383
|
+
\`\`\`typescript
|
|
384
|
+
export const deployInfra = new Job({
|
|
385
|
+
stage: "deploy",
|
|
386
|
+
trigger: new Trigger({
|
|
387
|
+
project: "my-group/infra-repo",
|
|
388
|
+
branch: "main",
|
|
389
|
+
strategy: "depend",
|
|
390
|
+
}),
|
|
391
|
+
});
|
|
392
|
+
\`\`\``,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
slug: "variables",
|
|
396
|
+
title: "Predefined Variables",
|
|
397
|
+
description: "GitLab CI/CD predefined variable references",
|
|
398
|
+
content: `The \`CI\` object provides type-safe access to GitLab CI/CD predefined variables. These map to \`$CI_*\` environment variables at runtime.
|
|
399
|
+
|
|
400
|
+
\`\`\`typescript
|
|
401
|
+
import { CI, Job, Rule } from "@intentius/chant-lexicon-gitlab";
|
|
402
|
+
|
|
403
|
+
// Use in rule conditions
|
|
404
|
+
const onDefault = new Rule({
|
|
405
|
+
ifCondition: \`\${CI.CommitBranch} == \${CI.DefaultBranch}\`,
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// Use in cache keys
|
|
409
|
+
const cache = new Cache({
|
|
410
|
+
key: CI.CommitRef, // → $CI_COMMIT_REF_NAME
|
|
411
|
+
paths: ["node_modules/"],
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Use in workflow names
|
|
415
|
+
const workflow = new Workflow({
|
|
416
|
+
name: \`Pipeline for \${CI.CommitRef}\`,
|
|
417
|
+
});
|
|
418
|
+
\`\`\`
|
|
419
|
+
|
|
420
|
+
## Variable reference
|
|
421
|
+
|
|
422
|
+
| Property | Variable | Description |
|
|
423
|
+
|----------|----------|-------------|
|
|
424
|
+
| \`CI.CommitBranch\` | \`$CI_COMMIT_BRANCH\` | Current branch name (not set for tag pipelines) |
|
|
425
|
+
| \`CI.CommitRef\` | \`$CI_COMMIT_REF_NAME\` | Branch or tag name |
|
|
426
|
+
| \`CI.CommitSha\` | \`$CI_COMMIT_SHA\` | Full commit SHA |
|
|
427
|
+
| \`CI.CommitTag\` | \`$CI_COMMIT_TAG\` | Tag name (only set for tag pipelines) |
|
|
428
|
+
| \`CI.DefaultBranch\` | \`$CI_DEFAULT_BRANCH\` | Default branch (usually \`main\`) |
|
|
429
|
+
| \`CI.Environment\` | \`$CI_ENVIRONMENT_NAME\` | Environment name (set during deploy jobs) |
|
|
430
|
+
| \`CI.JobId\` | \`$CI_JOB_ID\` | Unique job ID |
|
|
431
|
+
| \`CI.JobName\` | \`$CI_JOB_NAME\` | Job name |
|
|
432
|
+
| \`CI.JobStage\` | \`$CI_JOB_STAGE\` | Job stage name |
|
|
433
|
+
| \`CI.MergeRequestIid\` | \`$CI_MERGE_REQUEST_IID\` | MR internal ID (merge request pipelines only) |
|
|
434
|
+
| \`CI.PipelineId\` | \`$CI_PIPELINE_ID\` | Unique pipeline ID |
|
|
435
|
+
| \`CI.PipelineSource\` | \`$CI_PIPELINE_SOURCE\` | How the pipeline was triggered (\`push\`, \`merge_request_event\`, \`schedule\`, etc.) |
|
|
436
|
+
| \`CI.ProjectDir\` | \`$CI_PROJECT_DIR\` | Full path of the repository clone |
|
|
437
|
+
| \`CI.ProjectId\` | \`$CI_PROJECT_ID\` | Unique project ID |
|
|
438
|
+
| \`CI.ProjectName\` | \`$CI_PROJECT_NAME\` | Project name (URL-safe) |
|
|
439
|
+
| \`CI.ProjectPath\` | \`$CI_PROJECT_PATH\` | Project namespace with project name |
|
|
440
|
+
| \`CI.Registry\` | \`$CI_REGISTRY\` | Container registry URL |
|
|
441
|
+
| \`CI.RegistryImage\` | \`$CI_REGISTRY_IMAGE\` | Registry image path for the project |
|
|
442
|
+
|
|
443
|
+
## Common patterns
|
|
444
|
+
|
|
445
|
+
**Conditional on branch type:**
|
|
446
|
+
|
|
447
|
+
\`\`\`typescript
|
|
448
|
+
// Only on merge requests
|
|
449
|
+
new Rule({ ifCondition: CI.MergeRequestIid })
|
|
450
|
+
|
|
451
|
+
// Only on default branch
|
|
452
|
+
new Rule({ ifCondition: \`\${CI.CommitBranch} == \${CI.DefaultBranch}\` })
|
|
453
|
+
|
|
454
|
+
// Only on tags
|
|
455
|
+
new Rule({ ifCondition: CI.CommitTag })
|
|
456
|
+
\`\`\`
|
|
457
|
+
|
|
458
|
+
**Dynamic naming:**
|
|
459
|
+
|
|
460
|
+
\`\`\`typescript
|
|
461
|
+
export const deploy = new Job({
|
|
462
|
+
stage: "deploy",
|
|
463
|
+
environment: new Environment({
|
|
464
|
+
name: \`review/\${CI.CommitRef}\`,
|
|
465
|
+
url: \`https://\${CI.CommitRef}.preview.example.com\`,
|
|
466
|
+
}),
|
|
467
|
+
script: ["deploy-preview"],
|
|
468
|
+
});
|
|
469
|
+
\`\`\`
|
|
470
|
+
|
|
471
|
+
**Container registry:**
|
|
472
|
+
|
|
473
|
+
\`\`\`typescript
|
|
474
|
+
export const buildImage = new Job({
|
|
475
|
+
stage: "build",
|
|
476
|
+
image: new Image({ name: "docker:24" }),
|
|
477
|
+
script: [
|
|
478
|
+
\`docker build -t \${CI.RegistryImage}:\${CI.CommitSha} .\`,
|
|
479
|
+
\`docker push \${CI.RegistryImage}:\${CI.CommitSha}\`,
|
|
480
|
+
],
|
|
481
|
+
});
|
|
482
|
+
\`\`\`
|
|
483
|
+
`,
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
slug: "intrinsics",
|
|
487
|
+
title: "Intrinsic Functions",
|
|
488
|
+
description: "GitLab CI/CD intrinsic functions and their chant syntax",
|
|
489
|
+
content: `The GitLab lexicon provides one intrinsic function: \`reference()\`, which maps to GitLab's \`!reference\` YAML tag.
|
|
490
|
+
|
|
491
|
+
\`\`\`typescript
|
|
492
|
+
import { reference } from "@intentius/chant-lexicon-gitlab";
|
|
493
|
+
\`\`\`
|
|
494
|
+
|
|
495
|
+
## \`reference()\` — reuse job properties
|
|
496
|
+
|
|
497
|
+
The \`reference()\` intrinsic lets you reuse properties from other jobs or hidden keys. It produces the \`!reference\` YAML tag:
|
|
498
|
+
|
|
499
|
+
\`\`\`typescript
|
|
500
|
+
import { reference, Job } from "@intentius/chant-lexicon-gitlab";
|
|
501
|
+
|
|
502
|
+
export const deploy = new Job({
|
|
503
|
+
script: reference(".setup", "script"),
|
|
504
|
+
});
|
|
505
|
+
\`\`\`
|
|
506
|
+
|
|
507
|
+
Serializes to:
|
|
508
|
+
|
|
509
|
+
\`\`\`yaml
|
|
510
|
+
deploy:
|
|
511
|
+
script: !reference [.setup, script]
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
### Syntax
|
|
515
|
+
|
|
516
|
+
\`\`\`typescript
|
|
517
|
+
reference(jobName: string, property: string): ReferenceTag
|
|
518
|
+
\`\`\`
|
|
519
|
+
|
|
520
|
+
- \`jobName\` — the job or hidden key to reference (e.g. \`".setup"\`, \`"build"\`)
|
|
521
|
+
- \`property\` — the property to extract (e.g. \`"script"\`, \`"before_script"\`, \`"rules"\`)
|
|
522
|
+
|
|
523
|
+
### Use cases
|
|
524
|
+
|
|
525
|
+
**Shared setup scripts:**
|
|
526
|
+
|
|
527
|
+
\`\`\`typescript
|
|
528
|
+
// Hidden key with shared setup (defined in .gitlab-ci.yml or included)
|
|
529
|
+
// Reference its script from multiple jobs:
|
|
530
|
+
|
|
531
|
+
export const test = new Job({
|
|
532
|
+
stage: "test",
|
|
533
|
+
beforeScript: reference(".node-setup", "before_script"),
|
|
534
|
+
script: ["npm test"],
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
export const lint = new Job({
|
|
538
|
+
stage: "test",
|
|
539
|
+
beforeScript: reference(".node-setup", "before_script"),
|
|
540
|
+
script: ["npm run lint"],
|
|
541
|
+
});
|
|
542
|
+
\`\`\`
|
|
543
|
+
|
|
544
|
+
Produces:
|
|
545
|
+
|
|
546
|
+
\`\`\`yaml
|
|
547
|
+
test:
|
|
548
|
+
stage: test
|
|
549
|
+
before_script: !reference [.node-setup, before_script]
|
|
550
|
+
script:
|
|
551
|
+
- npm test
|
|
552
|
+
|
|
553
|
+
lint:
|
|
554
|
+
stage: test
|
|
555
|
+
before_script: !reference [.node-setup, before_script]
|
|
556
|
+
script:
|
|
557
|
+
- npm run lint
|
|
558
|
+
\`\`\`
|
|
559
|
+
|
|
560
|
+
**Shared rules:**
|
|
561
|
+
|
|
562
|
+
\`\`\`typescript
|
|
563
|
+
export const build = new Job({
|
|
564
|
+
stage: "build",
|
|
565
|
+
rules: reference(".default-rules", "rules"),
|
|
566
|
+
script: ["npm run build"],
|
|
567
|
+
});
|
|
568
|
+
\`\`\`
|
|
569
|
+
|
|
570
|
+
**Nested references (multi-level):**
|
|
571
|
+
|
|
572
|
+
\`\`\`typescript
|
|
573
|
+
// Reference a specific nested element
|
|
574
|
+
export const deploy = new Job({
|
|
575
|
+
script: reference(".setup", "script"),
|
|
576
|
+
environment: reference(".deploy-defaults", "environment"),
|
|
577
|
+
});
|
|
578
|
+
\`\`\`
|
|
579
|
+
|
|
580
|
+
### When to use \`reference()\` vs barrel imports
|
|
581
|
+
|
|
582
|
+
Use **barrel imports** (\`_.$\`) when referencing chant-managed objects — the serializer resolves them at build time:
|
|
583
|
+
|
|
584
|
+
\`\`\`typescript
|
|
585
|
+
// Preferred for chant-managed config
|
|
586
|
+
export const test = new Job({
|
|
587
|
+
cache: _.npmCache, // resolved at build time
|
|
588
|
+
artifacts: _.testArtifacts, // resolved at build time
|
|
589
|
+
});
|
|
590
|
+
\`\`\`
|
|
591
|
+
|
|
592
|
+
Use **\`reference()\`** when referencing jobs or hidden keys defined outside chant (e.g. in included YAML files or templates):
|
|
593
|
+
|
|
594
|
+
\`\`\`typescript
|
|
595
|
+
// For external/included YAML definitions
|
|
596
|
+
export const test = new Job({
|
|
597
|
+
beforeScript: reference(".ci-setup", "before_script"),
|
|
598
|
+
});
|
|
599
|
+
\`\`\`
|
|
600
|
+
`,
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
slug: "lint-rules",
|
|
604
|
+
title: "Lint Rules",
|
|
605
|
+
description: "Built-in lint rules and post-synth checks for GitLab CI/CD",
|
|
606
|
+
content: `The GitLab lexicon ships lint rules that run during \`chant lint\` and post-synth checks that validate the serialized YAML after \`chant build\`.
|
|
607
|
+
|
|
608
|
+
## Lint rules
|
|
609
|
+
|
|
610
|
+
Lint rules analyze your TypeScript source code before build.
|
|
611
|
+
|
|
612
|
+
### WGL001 — Deprecated only/except
|
|
613
|
+
|
|
614
|
+
**Severity:** warning | **Category:** style
|
|
615
|
+
|
|
616
|
+
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.
|
|
617
|
+
|
|
618
|
+
\`\`\`typescript
|
|
619
|
+
// Triggers WGL001
|
|
620
|
+
export const deploy = new Job({
|
|
621
|
+
stage: "deploy",
|
|
622
|
+
script: ["npm run deploy"],
|
|
623
|
+
only: ["main"], // deprecated
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// Fixed — use rules instead
|
|
627
|
+
export const deploy = new Job({
|
|
628
|
+
stage: "deploy",
|
|
629
|
+
script: ["npm run deploy"],
|
|
630
|
+
rules: [new Rule({
|
|
631
|
+
ifCondition: \`\${CI.CommitBranch} == \${CI.DefaultBranch}\`,
|
|
632
|
+
})],
|
|
633
|
+
});
|
|
634
|
+
\`\`\`
|
|
635
|
+
|
|
636
|
+
### WGL002 — Missing script
|
|
637
|
+
|
|
638
|
+
**Severity:** error | **Category:** correctness
|
|
639
|
+
|
|
640
|
+
A GitLab CI job must have \`script\`, \`trigger\`, or \`run\` defined. Jobs without any of these will fail pipeline validation.
|
|
641
|
+
|
|
642
|
+
\`\`\`typescript
|
|
643
|
+
// Triggers WGL002
|
|
644
|
+
export const build = new Job({
|
|
645
|
+
stage: "build",
|
|
646
|
+
image: new Image({ name: "node:20" }),
|
|
647
|
+
// Missing script!
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Fixed — add script
|
|
651
|
+
export const build = new Job({
|
|
652
|
+
stage: "build",
|
|
653
|
+
image: new Image({ name: "node:20" }),
|
|
654
|
+
script: ["npm run build"],
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Also valid — trigger job (no script needed)
|
|
658
|
+
export const downstream = new Job({
|
|
659
|
+
trigger: new Trigger({ project: "my-group/other-repo" }),
|
|
660
|
+
});
|
|
661
|
+
\`\`\`
|
|
662
|
+
|
|
663
|
+
### WGL003 — Missing stage
|
|
664
|
+
|
|
665
|
+
**Severity:** info | **Category:** style
|
|
666
|
+
|
|
667
|
+
Jobs should declare a \`stage\` property. Without it, the job defaults to the \`test\` stage, which may not be the intended behavior.
|
|
668
|
+
|
|
669
|
+
\`\`\`typescript
|
|
670
|
+
// Triggers WGL003
|
|
671
|
+
export const build = new Job({
|
|
672
|
+
script: ["npm run build"],
|
|
673
|
+
// No stage — defaults to "test"
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// Fixed — declare the stage
|
|
677
|
+
export const build = new Job({
|
|
678
|
+
stage: "build",
|
|
679
|
+
script: ["npm run build"],
|
|
680
|
+
});
|
|
681
|
+
\`\`\`
|
|
682
|
+
|
|
683
|
+
### WGL004 — Artifacts without expiry
|
|
684
|
+
|
|
685
|
+
**Severity:** warning | **Category:** performance
|
|
686
|
+
|
|
687
|
+
Flags \`Artifacts\` without \`expireIn\`. Artifacts without expiry are kept indefinitely, consuming storage. Always set an expiration.
|
|
688
|
+
|
|
689
|
+
\`\`\`typescript
|
|
690
|
+
// Triggers WGL004
|
|
691
|
+
export const build = new Job({
|
|
692
|
+
script: ["npm run build"],
|
|
693
|
+
artifacts: new Artifacts({
|
|
694
|
+
paths: ["dist/"],
|
|
695
|
+
// Missing expireIn!
|
|
696
|
+
}),
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
// Fixed — set expiry
|
|
700
|
+
export const build = new Job({
|
|
701
|
+
script: ["npm run build"],
|
|
702
|
+
artifacts: new Artifacts({
|
|
703
|
+
paths: ["dist/"],
|
|
704
|
+
expireIn: "1 hour",
|
|
705
|
+
}),
|
|
706
|
+
});
|
|
707
|
+
\`\`\`
|
|
708
|
+
|
|
709
|
+
## Post-synth checks
|
|
710
|
+
|
|
711
|
+
Post-synth checks run against the serialized YAML after build. They catch issues only visible in the final output.
|
|
712
|
+
|
|
713
|
+
### WGL010 — Undefined stage
|
|
714
|
+
|
|
715
|
+
**Severity:** error
|
|
716
|
+
|
|
717
|
+
Flags jobs that reference a stage not present in the collected stages list. This causes a pipeline validation error in GitLab.
|
|
718
|
+
|
|
719
|
+
### WGL011 — Unreachable job
|
|
720
|
+
|
|
721
|
+
**Severity:** warning
|
|
722
|
+
|
|
723
|
+
Flags jobs where all \`rules:\` entries have \`when: "never"\`, making the job unreachable. This usually indicates a configuration error.
|
|
724
|
+
|
|
725
|
+
\`\`\`typescript
|
|
726
|
+
// Triggers WGL011 — job can never run
|
|
727
|
+
export const noop = new Job({
|
|
728
|
+
script: ["echo unreachable"],
|
|
729
|
+
rules: [
|
|
730
|
+
new Rule({ ifCondition: CI.CommitBranch, when: "never" }),
|
|
731
|
+
new Rule({ ifCondition: CI.CommitTag, when: "never" }),
|
|
732
|
+
],
|
|
733
|
+
});
|
|
734
|
+
\`\`\`
|
|
735
|
+
|
|
736
|
+
## Running lint
|
|
737
|
+
|
|
738
|
+
\`\`\`bash
|
|
739
|
+
# Lint your chant project
|
|
740
|
+
chant lint
|
|
741
|
+
|
|
742
|
+
# Lint with auto-fix where supported
|
|
743
|
+
chant lint --fix
|
|
744
|
+
\`\`\`
|
|
745
|
+
|
|
746
|
+
To suppress a rule on a specific line:
|
|
747
|
+
|
|
748
|
+
\`\`\`typescript
|
|
749
|
+
// chant-disable-next-line WGL001
|
|
750
|
+
export const deploy = new Job({ only: ["main"], script: ["deploy"] });
|
|
751
|
+
\`\`\`
|
|
752
|
+
|
|
753
|
+
To suppress globally in \`chant.config.ts\`:
|
|
754
|
+
|
|
755
|
+
\`\`\`typescript
|
|
756
|
+
export default {
|
|
757
|
+
lint: {
|
|
758
|
+
rules: {
|
|
759
|
+
WGL003: "off", // don't require stage on every job
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
\`\`\`
|
|
764
|
+
`,
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
slug: "examples",
|
|
768
|
+
title: "Examples",
|
|
769
|
+
description: "Walkthrough of the getting-started GitLab CI/CD example",
|
|
770
|
+
content: `A runnable example lives in the lexicon's \`examples/\` directory. Clone the repo and try it:
|
|
771
|
+
|
|
772
|
+
\`\`\`bash
|
|
773
|
+
cd examples/getting-started
|
|
774
|
+
bun install
|
|
775
|
+
chant build # produces .gitlab-ci.yml
|
|
776
|
+
chant lint # runs lint rules
|
|
777
|
+
bun test # runs the example's tests
|
|
778
|
+
\`\`\`
|
|
779
|
+
|
|
780
|
+
## Getting Started
|
|
781
|
+
|
|
782
|
+
\`examples/getting-started/\` — a 3-stage Node.js pipeline with build, test, and deploy jobs.
|
|
783
|
+
|
|
784
|
+
\`\`\`
|
|
785
|
+
src/
|
|
786
|
+
├── _.ts # Barrel — re-exports lexicon + shared config
|
|
787
|
+
├── config.ts # Shared config: images, caches, artifacts, rules, environments
|
|
788
|
+
└── pipeline.ts # Job definitions: build, test, deploy
|
|
789
|
+
\`\`\`
|
|
790
|
+
|
|
791
|
+
### Barrel file
|
|
792
|
+
|
|
793
|
+
The barrel re-exports both the lexicon and shared config, so pipeline files only need one import:
|
|
794
|
+
|
|
795
|
+
\`\`\`typescript
|
|
796
|
+
// _.ts
|
|
797
|
+
export * from "@intentius/chant-lexicon-gitlab";
|
|
798
|
+
export * from "./config";
|
|
799
|
+
\`\`\`
|
|
800
|
+
|
|
801
|
+
### Shared configuration
|
|
802
|
+
|
|
803
|
+
\`config.ts\` extracts reusable objects — images, caches, artifacts, rules, and environments — so jobs stay concise:
|
|
804
|
+
|
|
805
|
+
\`\`\`typescript
|
|
806
|
+
// config.ts
|
|
807
|
+
import * as _ from "./_";
|
|
808
|
+
|
|
809
|
+
export const nodeImage = new _.Image({ name: "node:20-alpine" });
|
|
810
|
+
|
|
811
|
+
export const npmCache = new _.Cache({
|
|
812
|
+
key: "$CI_COMMIT_REF_SLUG",
|
|
813
|
+
paths: ["node_modules/"],
|
|
814
|
+
policy: "pull-push",
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
export const buildArtifacts = new _.Artifacts({
|
|
818
|
+
paths: ["dist/"],
|
|
819
|
+
expireIn: "1 hour",
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
export const testArtifacts = new _.Artifacts({
|
|
823
|
+
paths: ["coverage/"],
|
|
824
|
+
expireIn: "1 week",
|
|
825
|
+
reports: { junit: "coverage/junit.xml" },
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
export const onMergeRequest = new _.Rule({
|
|
829
|
+
ifCondition: _.CI.MergeRequestIid,
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
export const onCommit = new _.Rule({
|
|
833
|
+
ifCondition: _.CI.CommitBranch,
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
export const onDefaultBranch = new _.Rule({
|
|
837
|
+
ifCondition: \`\${_.CI.CommitBranch} == \${_.CI.DefaultBranch}\`,
|
|
838
|
+
when: "manual",
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
export const productionEnv = new _.Environment({
|
|
842
|
+
name: "production",
|
|
843
|
+
url: "https://example.com",
|
|
844
|
+
});
|
|
845
|
+
\`\`\`
|
|
846
|
+
|
|
847
|
+
### Pipeline jobs
|
|
848
|
+
|
|
849
|
+
\`pipeline.ts\` defines three jobs that reference shared config via the barrel:
|
|
850
|
+
|
|
851
|
+
\`\`\`typescript
|
|
852
|
+
// pipeline.ts
|
|
853
|
+
import * as _ from "./_";
|
|
854
|
+
|
|
855
|
+
export const build = new _.Job({
|
|
856
|
+
stage: "build",
|
|
857
|
+
image: _.nodeImage,
|
|
858
|
+
cache: _.npmCache,
|
|
859
|
+
script: ["npm ci", "npm run build"],
|
|
860
|
+
artifacts: _.buildArtifacts,
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
export const test = new _.Job({
|
|
864
|
+
stage: "test",
|
|
865
|
+
image: _.nodeImage,
|
|
866
|
+
cache: _.npmCache,
|
|
867
|
+
script: ["npm ci", "npm test"],
|
|
868
|
+
artifacts: _.testArtifacts,
|
|
869
|
+
rules: [_.onMergeRequest, _.onCommit],
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
export const deploy = new _.Job({
|
|
873
|
+
stage: "deploy",
|
|
874
|
+
image: _.nodeImage,
|
|
875
|
+
script: ["npm run deploy"],
|
|
876
|
+
environment: _.productionEnv,
|
|
877
|
+
rules: [_.onDefaultBranch],
|
|
878
|
+
});
|
|
879
|
+
\`\`\`
|
|
880
|
+
|
|
881
|
+
### Generated output
|
|
882
|
+
|
|
883
|
+
\`chant build\` produces this \`.gitlab-ci.yml\`:
|
|
884
|
+
|
|
885
|
+
\`\`\`yaml
|
|
886
|
+
stages:
|
|
887
|
+
- build
|
|
888
|
+
- test
|
|
889
|
+
- deploy
|
|
890
|
+
|
|
891
|
+
build:
|
|
892
|
+
stage: build
|
|
893
|
+
image: node:20-alpine
|
|
894
|
+
cache:
|
|
895
|
+
key: $CI_COMMIT_REF_SLUG
|
|
896
|
+
paths:
|
|
897
|
+
- node_modules/
|
|
898
|
+
policy: pull-push
|
|
899
|
+
script:
|
|
900
|
+
- npm ci
|
|
901
|
+
- npm run build
|
|
902
|
+
artifacts:
|
|
903
|
+
paths:
|
|
904
|
+
- dist/
|
|
905
|
+
expire_in: 1 hour
|
|
906
|
+
|
|
907
|
+
test:
|
|
908
|
+
stage: test
|
|
909
|
+
image: node:20-alpine
|
|
910
|
+
cache:
|
|
911
|
+
key: $CI_COMMIT_REF_SLUG
|
|
912
|
+
paths:
|
|
913
|
+
- node_modules/
|
|
914
|
+
policy: pull-push
|
|
915
|
+
script:
|
|
916
|
+
- npm ci
|
|
917
|
+
- npm test
|
|
918
|
+
artifacts:
|
|
919
|
+
paths:
|
|
920
|
+
- coverage/
|
|
921
|
+
expire_in: 1 week
|
|
922
|
+
reports:
|
|
923
|
+
junit: coverage/junit.xml
|
|
924
|
+
rules:
|
|
925
|
+
- if: $CI_MERGE_REQUEST_IID
|
|
926
|
+
- if: $CI_COMMIT_BRANCH
|
|
927
|
+
|
|
928
|
+
deploy:
|
|
929
|
+
stage: deploy
|
|
930
|
+
image: node:20-alpine
|
|
931
|
+
script:
|
|
932
|
+
- npm run deploy
|
|
933
|
+
environment:
|
|
934
|
+
name: production
|
|
935
|
+
url: https://example.com
|
|
936
|
+
rules:
|
|
937
|
+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
|
938
|
+
when: manual
|
|
939
|
+
\`\`\`
|
|
940
|
+
|
|
941
|
+
**Patterns demonstrated:**
|
|
942
|
+
|
|
943
|
+
1. **Barrel file** — single import point for lexicon types and shared config
|
|
944
|
+
2. **Shared config** — reusable images, caches, artifacts, and rules extracted into \`config.ts\`
|
|
945
|
+
3. **Conditional execution** — merge request and branch rules control when jobs run
|
|
946
|
+
4. **Manual deployment** — deploy requires manual trigger on the default branch
|
|
947
|
+
5. **JUnit reports** — test artifacts include JUnit XML for GitLab MR display
|
|
948
|
+
`,
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
basePath: "/lexicons/gitlab/",
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
const result = await docsPipeline(config);
|
|
955
|
+
writeDocsSite(config, result);
|
|
956
|
+
|
|
957
|
+
if (opts?.verbose) {
|
|
958
|
+
console.error(
|
|
959
|
+
`Generated docs: ${result.stats.resources} resources, ${result.stats.properties} properties, ${result.stats.services} services, ${result.stats.rules} rules`,
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
}
|