@intentius/chant-lexicon-gitlab 0.0.8 → 0.0.9
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/integrity.json +10 -6
- package/dist/manifest.json +1 -1
- package/dist/meta.json +186 -8
- package/dist/rules/wgl012.ts +86 -0
- package/dist/rules/wgl013.ts +62 -0
- package/dist/rules/wgl014.ts +51 -0
- package/dist/rules/wgl015.ts +85 -0
- package/dist/rules/yaml-helpers.ts +65 -3
- package/dist/skills/chant-gitlab.md +467 -24
- package/dist/types/index.d.ts +55 -16
- package/package.json +2 -2
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +58 -0
- package/src/codegen/docs.ts +32 -9
- package/src/codegen/generate-lexicon.ts +6 -1
- package/src/codegen/generate.ts +45 -50
- package/src/codegen/naming.ts +3 -0
- package/src/codegen/parse.test.ts +154 -4
- package/src/codegen/parse.ts +161 -49
- package/src/codegen/snapshot.test.ts +7 -5
- package/src/composites/composites.test.ts +452 -0
- package/src/composites/docker-build.ts +81 -0
- package/src/composites/index.ts +8 -0
- package/src/composites/node-pipeline.ts +104 -0
- package/src/composites/python-pipeline.ts +75 -0
- package/src/composites/review-app.ts +63 -0
- package/src/generated/index.d.ts +55 -16
- package/src/generated/index.ts +3 -0
- package/src/generated/lexicon-gitlab.json +186 -8
- package/src/import/generator.ts +3 -2
- package/src/index.ts +4 -0
- package/src/lint/post-synth/wgl012.test.ts +131 -0
- package/src/lint/post-synth/wgl012.ts +86 -0
- package/src/lint/post-synth/wgl013.test.ts +164 -0
- package/src/lint/post-synth/wgl013.ts +62 -0
- package/src/lint/post-synth/wgl014.test.ts +97 -0
- package/src/lint/post-synth/wgl014.ts +51 -0
- package/src/lint/post-synth/wgl015.test.ts +139 -0
- package/src/lint/post-synth/wgl015.ts +85 -0
- package/src/lint/post-synth/yaml-helpers.ts +65 -3
- package/src/plugin.test.ts +39 -13
- package/src/plugin.ts +636 -40
- package/src/serializer.test.ts +140 -0
- package/src/serializer.ts +63 -5
- package/src/validate.ts +1 -0
- package/src/variables.ts +4 -0
package/src/plugin.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import type { LexiconPlugin, IntrinsicDef, SkillDefinition } from "@intentius/chant/lexicon";
|
|
9
|
+
import type { LexiconPlugin, IntrinsicDef, SkillDefinition, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
12
|
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
@@ -27,7 +27,11 @@ export const gitlabPlugin: LexiconPlugin = {
|
|
|
27
27
|
postSynthChecks(): PostSynthCheck[] {
|
|
28
28
|
const { wgl010 } = require("./lint/post-synth/wgl010");
|
|
29
29
|
const { wgl011 } = require("./lint/post-synth/wgl011");
|
|
30
|
-
|
|
30
|
+
const { wgl012 } = require("./lint/post-synth/wgl012");
|
|
31
|
+
const { wgl013 } = require("./lint/post-synth/wgl013");
|
|
32
|
+
const { wgl014 } = require("./lint/post-synth/wgl014");
|
|
33
|
+
const { wgl015 } = require("./lint/post-synth/wgl015");
|
|
34
|
+
return [wgl010, wgl011, wgl012, wgl013, wgl014, wgl015];
|
|
31
35
|
},
|
|
32
36
|
|
|
33
37
|
intrinsics(): IntrinsicDef[] {
|
|
@@ -41,9 +45,81 @@ export const gitlabPlugin: LexiconPlugin = {
|
|
|
41
45
|
];
|
|
42
46
|
},
|
|
43
47
|
|
|
44
|
-
initTemplates():
|
|
48
|
+
initTemplates(template?: string): InitTemplateSet {
|
|
49
|
+
if (template === "node-pipeline") {
|
|
50
|
+
return {
|
|
51
|
+
src: {
|
|
52
|
+
"pipeline.ts": `import { NodePipeline } from "@intentius/chant-lexicon-gitlab";
|
|
53
|
+
|
|
54
|
+
export const app = NodePipeline({
|
|
55
|
+
nodeVersion: "22",
|
|
56
|
+
installCommand: "npm install",
|
|
57
|
+
buildScript: "build",
|
|
58
|
+
testScript: "test",
|
|
59
|
+
});
|
|
60
|
+
`,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (template === "python-pipeline") {
|
|
66
|
+
return {
|
|
67
|
+
src: {
|
|
68
|
+
"pipeline.ts": `import { PythonPipeline } from "@intentius/chant-lexicon-gitlab";
|
|
69
|
+
|
|
70
|
+
export const app = PythonPipeline({
|
|
71
|
+
pythonVersion: "3.12",
|
|
72
|
+
lintCommand: null,
|
|
73
|
+
});
|
|
74
|
+
`,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (template === "docker-build") {
|
|
80
|
+
return {
|
|
81
|
+
src: {
|
|
82
|
+
"pipeline.ts": `import { DockerBuild, Job, Image } from "@intentius/chant-lexicon-gitlab";
|
|
83
|
+
|
|
84
|
+
export const docker = DockerBuild({
|
|
85
|
+
dockerfile: "Dockerfile",
|
|
86
|
+
tagLatest: true,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export const test = new Job({
|
|
90
|
+
stage: "test",
|
|
91
|
+
image: new Image({ name: "node:22-alpine" }),
|
|
92
|
+
script: ["node test.js"],
|
|
93
|
+
});
|
|
94
|
+
`,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (template === "review-app") {
|
|
100
|
+
return {
|
|
101
|
+
src: {
|
|
102
|
+
"pipeline.ts": `import { ReviewApp, Job, Image } from "@intentius/chant-lexicon-gitlab";
|
|
103
|
+
|
|
104
|
+
export const review = ReviewApp({
|
|
105
|
+
name: "review",
|
|
106
|
+
deployScript: "echo deploy",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export const test = new Job({
|
|
110
|
+
stage: "test",
|
|
111
|
+
image: new Image({ name: "node:22-alpine" }),
|
|
112
|
+
script: ["node test.js"],
|
|
113
|
+
});
|
|
114
|
+
`,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Default template — basic pipeline with shared config
|
|
45
120
|
return {
|
|
46
|
-
|
|
121
|
+
src: {
|
|
122
|
+
"config.ts": `/**
|
|
47
123
|
* Shared pipeline configuration
|
|
48
124
|
*/
|
|
49
125
|
|
|
@@ -61,25 +137,33 @@ export const npmCache = new Cache({
|
|
|
61
137
|
policy: "pull-push",
|
|
62
138
|
});
|
|
63
139
|
`,
|
|
64
|
-
|
|
65
|
-
* Test job
|
|
66
|
-
*/
|
|
67
|
-
|
|
68
|
-
import { Job, Artifacts } from "@intentius/chant-lexicon-gitlab";
|
|
140
|
+
"pipeline.ts": `import { Job, Artifacts } from "@intentius/chant-lexicon-gitlab";
|
|
69
141
|
import { defaultImage, npmCache } from "./config";
|
|
70
142
|
|
|
143
|
+
export const junitReports = { junit: "coverage/junit.xml" };
|
|
144
|
+
|
|
145
|
+
export const testArtifacts = new Artifacts({
|
|
146
|
+
reports: junitReports,
|
|
147
|
+
paths: ["coverage/"],
|
|
148
|
+
expire_in: "1 week",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
export const build = new Job({
|
|
152
|
+
stage: "build",
|
|
153
|
+
image: defaultImage,
|
|
154
|
+
cache: npmCache,
|
|
155
|
+
script: ["npm install", "npm run build"],
|
|
156
|
+
});
|
|
157
|
+
|
|
71
158
|
export const test = new Job({
|
|
72
159
|
stage: "test",
|
|
73
160
|
image: defaultImage,
|
|
74
161
|
cache: npmCache,
|
|
75
|
-
script: ["npm
|
|
76
|
-
artifacts:
|
|
77
|
-
reports: { junit: "coverage/junit.xml" },
|
|
78
|
-
paths: ["coverage/"],
|
|
79
|
-
expireIn: "1 week",
|
|
80
|
-
}),
|
|
162
|
+
script: ["npm install", "npm test"],
|
|
163
|
+
artifacts: testArtifacts,
|
|
81
164
|
});
|
|
82
165
|
`,
|
|
166
|
+
},
|
|
83
167
|
};
|
|
84
168
|
},
|
|
85
169
|
|
|
@@ -261,70 +345,525 @@ export const deploy = new Job({
|
|
|
261
345
|
return [
|
|
262
346
|
{
|
|
263
347
|
name: "chant-gitlab",
|
|
264
|
-
description: "GitLab CI/CD pipeline
|
|
348
|
+
description: "GitLab CI/CD pipeline lifecycle — build, validate, deploy, monitor, rollback, and troubleshoot",
|
|
265
349
|
content: `---
|
|
266
350
|
skill: chant-gitlab
|
|
267
351
|
description: Build, validate, and deploy GitLab CI pipelines from a chant project
|
|
268
352
|
user-invocable: true
|
|
269
353
|
---
|
|
270
354
|
|
|
271
|
-
#
|
|
355
|
+
# GitLab CI/CD Operational Playbook
|
|
356
|
+
|
|
357
|
+
## How chant and GitLab CI relate
|
|
358
|
+
|
|
359
|
+
chant is a **synthesis-only** tool — it compiles TypeScript source files into \`.gitlab-ci.yml\` (YAML). chant does NOT call GitLab APIs. Your job as an agent is to bridge the two:
|
|
360
|
+
|
|
361
|
+
- Use **chant** for: build, lint, diff (local YAML comparison)
|
|
362
|
+
- Use **git + GitLab API** for: push, merge requests, pipeline monitoring, job logs, rollback, and all deployment operations
|
|
272
363
|
|
|
273
|
-
|
|
364
|
+
The source of truth for pipeline configuration is the TypeScript in \`src/\`. The generated \`.gitlab-ci.yml\` is an intermediate artifact — never edit it by hand.
|
|
274
365
|
|
|
275
|
-
##
|
|
366
|
+
## Scaffolding a new project
|
|
367
|
+
|
|
368
|
+
### Initialize with a template
|
|
276
369
|
|
|
277
370
|
\`\`\`bash
|
|
371
|
+
chant init --lexicon gitlab # default: config.ts + pipeline.ts
|
|
372
|
+
chant init --lexicon gitlab --template node-pipeline # NodePipeline composite
|
|
373
|
+
chant init --lexicon gitlab --template python-pipeline # PythonPipeline composite
|
|
374
|
+
chant init --lexicon gitlab --template docker-build # DockerBuild composite
|
|
375
|
+
chant init --lexicon gitlab --template review-app # ReviewApp composite
|
|
376
|
+
\`\`\`
|
|
377
|
+
|
|
378
|
+
This creates \`src/\` with chant pipeline definitions. It does NOT create application files — you bring your own app code.
|
|
379
|
+
|
|
380
|
+
### Available templates
|
|
381
|
+
|
|
382
|
+
| Template | What it generates | Best for |
|
|
383
|
+
|----------|-------------------|----------|
|
|
384
|
+
| *(default)* | \`config.ts\` + \`pipeline.ts\` with build/test jobs | Custom pipelines from scratch |
|
|
385
|
+
| \`node-pipeline\` | \`NodePipeline\` composite with npm install/build/test | Node.js apps |
|
|
386
|
+
| \`python-pipeline\` | \`PythonPipeline\` composite with venv/pytest | Python apps |
|
|
387
|
+
| \`docker-build\` | \`DockerBuild\` composite + test job | Containerized apps |
|
|
388
|
+
| \`review-app\` | \`ReviewApp\` composite + test job | Apps needing per-MR environments |
|
|
389
|
+
|
|
390
|
+
## Deploying to GitLab
|
|
391
|
+
|
|
392
|
+
### What goes in the GitLab repo
|
|
393
|
+
|
|
394
|
+
The GitLab repo needs TWO things:
|
|
395
|
+
1. **\`.gitlab-ci.yml\`** — generated by \`chant build\`
|
|
396
|
+
2. **Your application files** — whatever the pipeline scripts reference
|
|
397
|
+
|
|
398
|
+
chant only generates the YAML. Application files (\`package.json\`, \`Dockerfile\`, \`requirements.txt\`, source code, tests) are your responsibility.
|
|
399
|
+
|
|
400
|
+
### Typical project structure in the GitLab repo
|
|
401
|
+
|
|
402
|
+
**Node.js project:**
|
|
403
|
+
\`\`\`
|
|
404
|
+
.gitlab-ci.yml # generated by chant
|
|
405
|
+
package.json # your app's package.json with build/test scripts
|
|
406
|
+
index.js # your app code
|
|
407
|
+
test.js # your tests
|
|
408
|
+
\`\`\`
|
|
409
|
+
|
|
410
|
+
**Python project:**
|
|
411
|
+
\`\`\`
|
|
412
|
+
.gitlab-ci.yml
|
|
413
|
+
requirements.txt # must include pytest, pytest-cov if using PythonPipeline defaults
|
|
414
|
+
app.py
|
|
415
|
+
test_app.py
|
|
416
|
+
\`\`\`
|
|
417
|
+
|
|
418
|
+
**Docker project:**
|
|
419
|
+
\`\`\`
|
|
420
|
+
.gitlab-ci.yml
|
|
421
|
+
Dockerfile
|
|
422
|
+
src/ # your app source
|
|
423
|
+
\`\`\`
|
|
424
|
+
|
|
425
|
+
### Important: npm ci vs npm install
|
|
426
|
+
|
|
427
|
+
The \`NodePipeline\` composite defaults to \`npm ci\`, which requires a \`package-lock.json\` in the repo. If your repo does not have a lockfile, override with:
|
|
428
|
+
|
|
429
|
+
\`\`\`typescript
|
|
430
|
+
NodePipeline({
|
|
431
|
+
installCommand: "npm install", // use instead of npm ci
|
|
432
|
+
...
|
|
433
|
+
})
|
|
434
|
+
\`\`\`
|
|
435
|
+
|
|
436
|
+
Or generate a lockfile: \`npm install && git add package-lock.json\`.
|
|
437
|
+
|
|
438
|
+
### Step-by-step: push to GitLab
|
|
439
|
+
|
|
440
|
+
\`\`\`bash
|
|
441
|
+
# 1. Build the YAML from chant source
|
|
278
442
|
chant build src/ --output .gitlab-ci.yml
|
|
443
|
+
|
|
444
|
+
# 2. Initialize git (if needed) and commit everything
|
|
445
|
+
git init -b main
|
|
446
|
+
git add .gitlab-ci.yml package.json index.js test.js # add your app files
|
|
447
|
+
git commit -m "Initial pipeline"
|
|
448
|
+
|
|
449
|
+
# 3. Push to GitLab
|
|
450
|
+
git remote add origin git@gitlab.com:YOUR_GROUP/YOUR_PROJECT.git
|
|
451
|
+
git push -u origin main
|
|
279
452
|
\`\`\`
|
|
280
453
|
|
|
281
|
-
|
|
454
|
+
The pipeline triggers automatically on push. Do NOT commit the chant \`src/\` directory, \`node_modules/\`, or \`.chant/\` to the GitLab repo — those are local development files.
|
|
455
|
+
|
|
456
|
+
## Build and validate
|
|
457
|
+
|
|
458
|
+
### Build the pipeline
|
|
459
|
+
|
|
460
|
+
\`\`\`bash
|
|
461
|
+
chant build src/ --output .gitlab-ci.yml
|
|
462
|
+
\`\`\`
|
|
463
|
+
|
|
464
|
+
Options:
|
|
465
|
+
- \`--format yaml\` — emit YAML (default for GitLab)
|
|
466
|
+
- \`--watch\` — rebuild on source changes
|
|
467
|
+
- \`--output <path>\` — write to a specific file
|
|
468
|
+
|
|
469
|
+
### Lint the source
|
|
282
470
|
|
|
283
471
|
\`\`\`bash
|
|
284
472
|
chant lint src/
|
|
285
473
|
\`\`\`
|
|
286
474
|
|
|
287
|
-
|
|
475
|
+
Options:
|
|
476
|
+
- \`--fix\` — auto-fix violations where possible
|
|
477
|
+
- \`--format sarif\` — SARIF output for CI integration
|
|
478
|
+
- \`--watch\` — re-lint on changes
|
|
479
|
+
|
|
480
|
+
### Validate with GitLab CI Lint API
|
|
481
|
+
|
|
288
482
|
\`\`\`bash
|
|
289
483
|
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
290
|
-
"
|
|
291
|
-
|
|
484
|
+
--header "Content-Type: application/json" \\
|
|
485
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
486
|
+
--data-binary @- <<EOF
|
|
487
|
+
{"content": $(cat .gitlab-ci.yml | jq -Rs .)}
|
|
488
|
+
EOF
|
|
292
489
|
\`\`\`
|
|
293
490
|
|
|
294
|
-
|
|
491
|
+
Add \`"dry_run": true, "include_merged_yaml": true\` for full expansion with includes resolved.
|
|
492
|
+
|
|
493
|
+
### What each step catches
|
|
494
|
+
|
|
495
|
+
| Step | Catches | When to run |
|
|
496
|
+
|------|---------|-------------|
|
|
497
|
+
| \`chant lint\` | Deprecated only/except (WGL001), missing script (WGL002), missing stage (WGL003), artifacts without expiry (WGL004) | Every edit |
|
|
498
|
+
| \`chant build\` | Post-synth checks: undefined stages (WGL010), unreachable jobs (WGL011), deprecated properties (WGL012), invalid needs targets (WGL013), invalid extends targets (WGL014), circular needs chains (WGL015) | Before push |
|
|
499
|
+
| CI Lint API | GitLab-specific validation: include resolution, variable expansion, YAML schema errors | Before merge to default branch |
|
|
500
|
+
|
|
501
|
+
Always run all three before deploying. Lint catches things the API cannot (and vice versa).
|
|
295
502
|
|
|
296
|
-
|
|
503
|
+
## Diffing and change preview
|
|
504
|
+
|
|
505
|
+
### Local diff
|
|
506
|
+
|
|
507
|
+
Compare generated \`.gitlab-ci.yml\` against the version on the remote branch:
|
|
297
508
|
|
|
298
509
|
\`\`\`bash
|
|
510
|
+
# Build the proposed config
|
|
299
511
|
chant build src/ --output .gitlab-ci.yml
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
git
|
|
512
|
+
|
|
513
|
+
# Diff against the remote version
|
|
514
|
+
git diff origin/main -- .gitlab-ci.yml
|
|
515
|
+
\`\`\`
|
|
516
|
+
|
|
517
|
+
### MR pipeline preview
|
|
518
|
+
|
|
519
|
+
Push to a branch and open a merge request — GitLab shows the pipeline that would run without executing it. This is the safest way to preview pipeline changes for production.
|
|
520
|
+
|
|
521
|
+
### CI Lint API with dry run
|
|
522
|
+
|
|
523
|
+
\`\`\`bash
|
|
524
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
525
|
+
--header "Content-Type: application/json" \\
|
|
526
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
527
|
+
--data-binary @- <<EOF
|
|
528
|
+
{"content": $(cat .gitlab-ci.yml | jq -Rs .), "dry_run": true, "include_merged_yaml": true}
|
|
529
|
+
EOF
|
|
530
|
+
\`\`\`
|
|
531
|
+
|
|
532
|
+
This resolves all \`include:\` directives and expands the full pipeline — useful for catching issues with cross-file references.
|
|
533
|
+
|
|
534
|
+
### Safe preview checklist
|
|
535
|
+
|
|
536
|
+
Before merging pipeline changes, verify:
|
|
537
|
+
1. All jobs have valid \`stage:\` values and stages are defined
|
|
538
|
+
2. \`needs:\` references point to existing job names
|
|
539
|
+
3. \`rules:\` conditions match the intended branches/events
|
|
540
|
+
4. Environment names and \`on_stop\` references are correct
|
|
541
|
+
5. Docker images are accessible from your runners
|
|
542
|
+
6. Secrets/variables referenced in scripts exist in project settings
|
|
543
|
+
|
|
544
|
+
## Deploying pipeline changes
|
|
545
|
+
|
|
546
|
+
### Safe path (production pipelines)
|
|
547
|
+
|
|
548
|
+
1. Build: \`chant build src/ --output .gitlab-ci.yml\`
|
|
549
|
+
2. Lint: \`chant lint src/\`
|
|
550
|
+
3. Validate: CI Lint API (see above)
|
|
551
|
+
4. Push to feature branch: \`git push -u origin feature/pipeline-update\`
|
|
552
|
+
5. Open MR — review pipeline diff in the MR widget
|
|
553
|
+
6. Merge — pipeline runs on the default branch
|
|
554
|
+
|
|
555
|
+
### Fast path (dev/iteration)
|
|
556
|
+
|
|
557
|
+
\`\`\`bash
|
|
558
|
+
chant build src/ --output .gitlab-ci.yml
|
|
559
|
+
git add .gitlab-ci.yml && git commit -m "Update pipeline" && git push
|
|
560
|
+
\`\`\`
|
|
561
|
+
|
|
562
|
+
### Which path to use
|
|
563
|
+
|
|
564
|
+
| Scenario | Path |
|
|
565
|
+
|----------|------|
|
|
566
|
+
| Production pipeline with deploy jobs | Safe path (MR review) |
|
|
567
|
+
| Pipeline with environment/secrets changes | Safe path (MR review) |
|
|
568
|
+
| Dev/test pipeline iteration | Fast path (direct push) |
|
|
569
|
+
| CI/CD with approval gates or protected environments | Safe path + protected branch |
|
|
570
|
+
|
|
571
|
+
## Environment lifecycle
|
|
572
|
+
|
|
573
|
+
Environments are created by jobs with an \`environment:\` keyword. They track deployments and enable rollback.
|
|
574
|
+
|
|
575
|
+
### Review apps pattern
|
|
576
|
+
|
|
577
|
+
Deploy on MR, auto-stop when MR is merged or closed:
|
|
578
|
+
|
|
579
|
+
\`\`\`typescript
|
|
580
|
+
new Job({
|
|
581
|
+
stage: "deploy",
|
|
582
|
+
environment: new Environment({
|
|
583
|
+
name: "review/$CI_COMMIT_REF_SLUG",
|
|
584
|
+
url: "https://$CI_COMMIT_REF_SLUG.example.com",
|
|
585
|
+
onStop: "stop_review",
|
|
586
|
+
autoStopIn: "1 week",
|
|
587
|
+
}),
|
|
588
|
+
script: ["./deploy-review.sh"],
|
|
589
|
+
rules: [{ if: "$CI_MERGE_REQUEST_IID" }],
|
|
590
|
+
});
|
|
591
|
+
\`\`\`
|
|
592
|
+
|
|
593
|
+
### Environment promotion
|
|
594
|
+
|
|
595
|
+
Deploy through environments in order: dev → staging → production. Use \`rules:\` and \`when: manual\` to gate promotions.
|
|
596
|
+
|
|
597
|
+
### Rollback to a previous deployment
|
|
598
|
+
|
|
599
|
+
\`\`\`bash
|
|
600
|
+
# List deployments for an environment
|
|
601
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
602
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/environments/$ENV_ID/deployments?order_by=created_at&sort=desc&per_page=5"
|
|
603
|
+
|
|
604
|
+
# Re-deploy a previous deployment's commit
|
|
605
|
+
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
606
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/deployments" \\
|
|
607
|
+
--data "environment=production&sha=$PREVIOUS_SHA&ref=main&tag=false&status=created"
|
|
608
|
+
\`\`\`
|
|
609
|
+
|
|
610
|
+
Alternatively, revert the MR that introduced the change and let the pipeline re-run.
|
|
611
|
+
|
|
612
|
+
## Pipeline and job states
|
|
613
|
+
|
|
614
|
+
### Pipeline states
|
|
615
|
+
|
|
616
|
+
| State | Meaning | Action |
|
|
617
|
+
|-------|---------|--------|
|
|
618
|
+
| \`created\` | Pipeline created, not yet started | Wait |
|
|
619
|
+
| \`waiting_for_resource\` | Waiting for runner | Check runner availability |
|
|
620
|
+
| \`preparing\` | Job is being prepared | Wait |
|
|
621
|
+
| \`pending\` | Waiting for runner to pick up | Check runner tags/availability |
|
|
622
|
+
| \`running\` | Pipeline is executing | Monitor |
|
|
623
|
+
| \`success\` | All jobs passed | None — healthy |
|
|
624
|
+
| \`failed\` | One or more jobs failed | Check failed job logs |
|
|
625
|
+
| \`canceled\` | Pipeline was canceled | Re-run if needed |
|
|
626
|
+
| \`skipped\` | Pipeline was skipped by rules | Check rules configuration |
|
|
627
|
+
| \`manual\` | Pipeline waiting for manual action | Trigger manual job or cancel |
|
|
628
|
+
| \`scheduled\` | Waiting for scheduled time | Wait |
|
|
629
|
+
|
|
630
|
+
### Job states
|
|
631
|
+
|
|
632
|
+
| State | Meaning | Action |
|
|
633
|
+
|-------|---------|--------|
|
|
634
|
+
| \`created\` | Job created | Wait |
|
|
635
|
+
| \`pending\` | Waiting for runner | Check runner tags |
|
|
636
|
+
| \`running\` | Job executing | Monitor logs |
|
|
637
|
+
| \`success\` | Job passed | None |
|
|
638
|
+
| \`failed\` | Job failed | Read trace log |
|
|
639
|
+
| \`canceled\` | Job canceled | Re-run if needed |
|
|
640
|
+
| \`skipped\` | Job skipped by rules/needs | Check rules |
|
|
641
|
+
| \`manual\` | Waiting for manual trigger | Play or skip |
|
|
642
|
+
| \`allowed_failure\` | Failed but allowed | Review — may indicate flaky test |
|
|
643
|
+
|
|
644
|
+
## Monitoring pipelines
|
|
645
|
+
|
|
646
|
+
### Check pipeline status
|
|
647
|
+
|
|
648
|
+
\`\`\`bash
|
|
649
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
650
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
|
|
651
|
+
\`\`\`
|
|
652
|
+
|
|
653
|
+
### List recent pipelines for a branch
|
|
654
|
+
|
|
655
|
+
\`\`\`bash
|
|
656
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
657
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?ref=main&per_page=5"
|
|
658
|
+
\`\`\`
|
|
659
|
+
|
|
660
|
+
### Get jobs in a pipeline
|
|
661
|
+
|
|
662
|
+
\`\`\`bash
|
|
663
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
664
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
|
|
665
|
+
\`\`\`
|
|
666
|
+
|
|
667
|
+
### Stream job logs
|
|
668
|
+
|
|
669
|
+
\`\`\`bash
|
|
670
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
671
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
303
672
|
\`\`\`
|
|
304
673
|
|
|
305
|
-
|
|
674
|
+
### Download job artifacts
|
|
306
675
|
|
|
307
|
-
|
|
308
|
-
|
|
676
|
+
\`\`\`bash
|
|
677
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
678
|
+
--output artifacts.zip \\
|
|
679
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/artifacts"
|
|
680
|
+
\`\`\`
|
|
681
|
+
|
|
682
|
+
## Merge request pipeline workflow
|
|
309
683
|
|
|
310
|
-
|
|
684
|
+
### How MR pipelines differ
|
|
311
685
|
|
|
312
|
-
|
|
313
|
-
- API: \`curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/retry"\`
|
|
686
|
+
MR pipelines run in a merge request context with \`CI_MERGE_REQUEST_IID\` available. Branch pipelines run on push with \`CI_COMMIT_BRANCH\`. A job cannot have both — use \`rules:\` to target one or the other.
|
|
314
687
|
|
|
315
|
-
|
|
688
|
+
### Common rules patterns
|
|
689
|
+
|
|
690
|
+
\`\`\`yaml
|
|
691
|
+
# Run only on MR pipelines
|
|
692
|
+
rules:
|
|
693
|
+
- if: $CI_MERGE_REQUEST_IID
|
|
694
|
+
|
|
695
|
+
# Run only on the default branch
|
|
696
|
+
rules:
|
|
697
|
+
- if: $CI_COMMIT_BRANCH == "main"
|
|
698
|
+
|
|
699
|
+
# Run on MRs and the default branch (but not both at once)
|
|
700
|
+
rules:
|
|
701
|
+
- if: $CI_MERGE_REQUEST_IID
|
|
702
|
+
- if: $CI_COMMIT_BRANCH == "main"
|
|
703
|
+
\`\`\`
|
|
316
704
|
|
|
317
|
-
|
|
705
|
+
### Merged results pipelines
|
|
318
706
|
|
|
319
|
-
|
|
707
|
+
Enable in project settings → CI/CD → General pipelines → "Merged results pipelines". These test the result of merging your branch into the target — catching integration issues before merge.
|
|
320
708
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
709
|
+
### Merge trains
|
|
710
|
+
|
|
711
|
+
Merge trains queue MRs and test each one merged on top of the previous. Enable in project settings → Merge requests → "Merge trains". Requires merged results pipelines.
|
|
712
|
+
|
|
713
|
+
## Troubleshooting decision tree
|
|
714
|
+
|
|
715
|
+
### Step 1: Check pipeline status
|
|
716
|
+
|
|
717
|
+
\`\`\`bash
|
|
718
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
719
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID" | jq '.status'
|
|
720
|
+
\`\`\`
|
|
721
|
+
|
|
722
|
+
### Step 2: Branch on status
|
|
723
|
+
|
|
724
|
+
- **\`running\` / \`pending\` / \`created\`** → Wait. Do not take action while the pipeline is in progress.
|
|
725
|
+
- **\`failed\`** → Read the failed job logs (Step 3).
|
|
726
|
+
- **\`success\`** → Pipeline is healthy. If behavior is wrong, check job scripts and configuration.
|
|
727
|
+
- **\`canceled\`** → Re-run if needed: \`curl --request POST ... /pipelines/$PIPELINE_ID/retry\`
|
|
728
|
+
- **\`skipped\`** → All jobs were filtered out by \`rules:\`. Check rule conditions.
|
|
729
|
+
|
|
730
|
+
### Step 3: Read failed job logs
|
|
731
|
+
|
|
732
|
+
\`\`\`bash
|
|
733
|
+
# Get failed jobs
|
|
734
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
735
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs?scope=failed" | jq '.[].id'
|
|
736
|
+
|
|
737
|
+
# Read the trace for a failed job
|
|
738
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
739
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
740
|
+
\`\`\`
|
|
741
|
+
|
|
742
|
+
### Step 4: Diagnose by error pattern
|
|
743
|
+
|
|
744
|
+
| Error pattern | Likely cause | Fix |
|
|
745
|
+
|---------------|-------------|-----|
|
|
746
|
+
| "no matching runner" | No runner with matching tags | Check runner tags, register a runner |
|
|
747
|
+
| "image pull failed" | Docker image not found or auth failed | Check image name, registry credentials |
|
|
748
|
+
| "script exit code 1" | Script command failed | Read job log for the failing command |
|
|
749
|
+
| "artifact upload failed" | Artifact path doesn't exist or too large | Check \`artifacts.paths\`, size limits |
|
|
750
|
+
| "cache not found" | Cache key mismatch or first run | Expected on first run; check \`cache.key\` |
|
|
751
|
+
| "yaml invalid" | Syntax error in generated YAML | Run \`chant lint src/\` and CI Lint API |
|
|
752
|
+
| "pipeline filtered out" | All jobs filtered by rules | Check \`rules:\` conditions |
|
|
753
|
+
| "job timed out" | Job exceeded timeout | Increase \`timeout:\` or optimize job |
|
|
754
|
+
| "stuck or pending" | No available runner | Check runner status, tags, executor capacity |
|
|
755
|
+
| "environment does not exist" | \`on_stop\` references non-existent job | Check \`on_stop\` job name matches expanded name |
|
|
756
|
+
| "needs job not found" | \`needs:\` references non-existent job | Check job names, stage ordering |
|
|
757
|
+
|
|
758
|
+
## Variable management
|
|
759
|
+
|
|
760
|
+
### Variable types and precedence
|
|
761
|
+
|
|
762
|
+
Variables are resolved in this order (highest priority first):
|
|
763
|
+
1. Job-level \`variables:\`
|
|
764
|
+
2. Project CI/CD variables (Settings → CI/CD → Variables)
|
|
765
|
+
3. Group CI/CD variables
|
|
766
|
+
4. Instance CI/CD variables
|
|
767
|
+
|
|
768
|
+
### Protected and masked variables
|
|
769
|
+
|
|
770
|
+
- **Protected**: only available in pipelines on protected branches/tags
|
|
771
|
+
- **Masked**: hidden in job logs (value must meet masking requirements)
|
|
772
|
+
|
|
773
|
+
### Managing variables via API
|
|
774
|
+
|
|
775
|
+
\`\`\`bash
|
|
776
|
+
# List project variables
|
|
777
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
778
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables"
|
|
779
|
+
|
|
780
|
+
# Create a variable
|
|
781
|
+
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
782
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables" \\
|
|
783
|
+
--form "key=DEPLOY_TOKEN" --form "value=secret" --form "masked=true" --form "protected=true"
|
|
784
|
+
|
|
785
|
+
# Update a variable
|
|
786
|
+
curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
787
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN" \\
|
|
788
|
+
--form "value=new-secret"
|
|
789
|
+
|
|
790
|
+
# Delete a variable
|
|
791
|
+
curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
792
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN"
|
|
793
|
+
\`\`\`
|
|
794
|
+
|
|
795
|
+
## Quick reference
|
|
796
|
+
|
|
797
|
+
### Pipeline info commands
|
|
798
|
+
|
|
799
|
+
\`\`\`bash
|
|
800
|
+
# List recent pipelines
|
|
801
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
802
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?per_page=5"
|
|
803
|
+
|
|
804
|
+
# Get pipeline status
|
|
805
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
806
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
|
|
807
|
+
|
|
808
|
+
# Get jobs in a pipeline
|
|
809
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
810
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
|
|
811
|
+
|
|
812
|
+
# Read job log
|
|
813
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
814
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
815
|
+
|
|
816
|
+
# Retry a failed pipeline
|
|
817
|
+
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
818
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/retry"
|
|
819
|
+
|
|
820
|
+
# Cancel a running pipeline
|
|
821
|
+
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
822
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/cancel"
|
|
823
|
+
\`\`\`
|
|
824
|
+
|
|
825
|
+
### Full build-to-deploy pipeline
|
|
826
|
+
|
|
827
|
+
\`\`\`bash
|
|
828
|
+
# 1. Lint
|
|
829
|
+
chant lint src/
|
|
830
|
+
|
|
831
|
+
# 2. Build
|
|
832
|
+
chant build src/ --output .gitlab-ci.yml
|
|
833
|
+
|
|
834
|
+
# 3. Validate via API
|
|
835
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
836
|
+
--header "Content-Type: application/json" \\
|
|
837
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
838
|
+
--data-binary @- <<EOF
|
|
839
|
+
{"content": $(cat .gitlab-ci.yml | jq -Rs .)}
|
|
840
|
+
EOF
|
|
841
|
+
|
|
842
|
+
# 4. Push to feature branch
|
|
843
|
+
git checkout -b feature/pipeline-update
|
|
844
|
+
git add .gitlab-ci.yml
|
|
845
|
+
git commit -m "Update pipeline"
|
|
846
|
+
git push -u origin feature/pipeline-update
|
|
847
|
+
|
|
848
|
+
# 5. Open MR, review pipeline diff, merge
|
|
849
|
+
# Pipeline runs automatically on the default branch after merge
|
|
850
|
+
\`\`\`
|
|
324
851
|
`,
|
|
325
852
|
triggers: [
|
|
326
853
|
{ type: "file-pattern", value: "**/*.gitlab.ts" },
|
|
854
|
+
{ type: "file-pattern", value: "**/.gitlab-ci.yml" },
|
|
327
855
|
{ type: "context", value: "gitlab" },
|
|
856
|
+
{ type: "context", value: "pipeline" },
|
|
857
|
+
{ type: "context", value: "deploy" },
|
|
858
|
+
],
|
|
859
|
+
preConditions: [
|
|
860
|
+
"chant CLI is installed (chant --version succeeds)",
|
|
861
|
+
"git is configured and can push to the remote",
|
|
862
|
+
"Project has chant source files in src/",
|
|
863
|
+
],
|
|
864
|
+
postConditions: [
|
|
865
|
+
"Pipeline is in a stable state (success/manual/scheduled)",
|
|
866
|
+
"No failed jobs in the pipeline",
|
|
328
867
|
],
|
|
329
868
|
parameters: [],
|
|
330
869
|
examples: [
|
|
@@ -345,6 +884,63 @@ git push
|
|
|
345
884
|
}),
|
|
346
885
|
})`,
|
|
347
886
|
},
|
|
887
|
+
{
|
|
888
|
+
title: "Deploy pipeline update",
|
|
889
|
+
description: "Build, validate, and deploy a pipeline change via MR workflow",
|
|
890
|
+
input: "Deploy my pipeline changes to production",
|
|
891
|
+
output: `chant lint src/
|
|
892
|
+
chant build src/ --output .gitlab-ci.yml
|
|
893
|
+
git checkout -b feature/pipeline-update
|
|
894
|
+
git add .gitlab-ci.yml
|
|
895
|
+
git commit -m "Update pipeline"
|
|
896
|
+
git push -u origin feature/pipeline-update
|
|
897
|
+
# Open MR in GitLab, review pipeline diff, then merge`,
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
title: "Preview pipeline changes",
|
|
901
|
+
description: "Validate pipeline configuration via lint and CI Lint API before deploying",
|
|
902
|
+
input: "Check if my pipeline changes are valid before pushing",
|
|
903
|
+
output: `chant lint src/
|
|
904
|
+
chant build src/ --output .gitlab-ci.yml
|
|
905
|
+
# Validate via GitLab CI Lint API
|
|
906
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
907
|
+
--header "Content-Type: application/json" \\
|
|
908
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
909
|
+
--data-binary '{"content": "'$(cat .gitlab-ci.yml | jq -Rs .)'", "dry_run": true}'`,
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
title: "Scaffold and deploy a Node.js pipeline",
|
|
913
|
+
description: "Use --template to scaffold a Node.js project, build YAML, and push to GitLab",
|
|
914
|
+
input: "Create a Node.js CI pipeline and deploy it to GitLab",
|
|
915
|
+
output: `# Scaffold the project
|
|
916
|
+
chant init --lexicon gitlab --template node-pipeline my-node-app
|
|
917
|
+
cd my-node-app
|
|
918
|
+
|
|
919
|
+
# Build the YAML
|
|
920
|
+
chant build src/ --output .gitlab-ci.yml
|
|
921
|
+
|
|
922
|
+
# The GitLab repo needs app files — create them
|
|
923
|
+
echo '{"scripts":{"build":"echo build","test":"node test.js"}}' > package.json
|
|
924
|
+
echo 'console.log("ok")' > test.js
|
|
925
|
+
|
|
926
|
+
# Push to GitLab
|
|
927
|
+
git init -b main
|
|
928
|
+
git add .gitlab-ci.yml package.json test.js
|
|
929
|
+
git commit -m "Initial pipeline"
|
|
930
|
+
git remote add origin git@gitlab.com:YOUR_GROUP/YOUR_PROJECT.git
|
|
931
|
+
git push -u origin main`,
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
title: "Retry a failed pipeline",
|
|
935
|
+
description: "Retry a failed pipeline and monitor its progress",
|
|
936
|
+
input: "Pipeline 12345 failed, retry it",
|
|
937
|
+
output: `# Retry the pipeline
|
|
938
|
+
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
939
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/12345/retry"
|
|
940
|
+
# Monitor status
|
|
941
|
+
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
942
|
+
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/12345"`,
|
|
943
|
+
},
|
|
348
944
|
],
|
|
349
945
|
},
|
|
350
946
|
];
|