@intentius/chant-lexicon-gitlab 0.0.18 → 0.0.24
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 +4 -4
- package/dist/manifest.json +1 -1
- package/dist/skills/{gitlab-ci-patterns.md → chant-gitlab-patterns.md} +1 -1
- package/dist/skills/chant-gitlab.md +10 -10
- package/package.json +20 -2
- package/src/composites/composites.test.ts +93 -0
- package/src/composites/docker-build.ts +8 -3
- package/src/composites/node-pipeline.ts +14 -7
- package/src/composites/python-pipeline.ts +14 -7
- package/src/composites/review-app.ts +11 -5
- package/src/plugin.ts +87 -671
- package/src/skills/{gitlab-ci-patterns.md → chant-gitlab-patterns.md} +1 -1
- package/src/skills/chant-gitlab.md +502 -0
package/src/plugin.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import type { LexiconPlugin, IntrinsicDef,
|
|
9
|
+
import type { LexiconPlugin, IntrinsicDef, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
|
-
import
|
|
12
|
+
import { discoverPostSynthChecks } from "@intentius/chant/lint/discover";
|
|
13
|
+
import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
|
|
14
|
+
import { join, dirname } from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
13
16
|
import { gitlabSerializer } from "./serializer";
|
|
14
17
|
|
|
15
18
|
export const gitlabPlugin: LexiconPlugin = {
|
|
@@ -24,31 +27,9 @@ export const gitlabPlugin: LexiconPlugin = {
|
|
|
24
27
|
return [deprecatedOnlyExceptRule, missingScriptRule, missingStageRule, artifactNoExpiryRule];
|
|
25
28
|
},
|
|
26
29
|
|
|
27
|
-
postSynthChecks()
|
|
28
|
-
const
|
|
29
|
-
|
|
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
|
-
const { wgl016 } = require("./lint/post-synth/wgl016");
|
|
35
|
-
const { wgl017 } = require("./lint/post-synth/wgl017");
|
|
36
|
-
const { wgl018 } = require("./lint/post-synth/wgl018");
|
|
37
|
-
const { wgl019 } = require("./lint/post-synth/wgl019");
|
|
38
|
-
const { wgl020 } = require("./lint/post-synth/wgl020");
|
|
39
|
-
const { wgl021 } = require("./lint/post-synth/wgl021");
|
|
40
|
-
const { wgl022 } = require("./lint/post-synth/wgl022");
|
|
41
|
-
const { wgl023 } = require("./lint/post-synth/wgl023");
|
|
42
|
-
const { wgl024 } = require("./lint/post-synth/wgl024");
|
|
43
|
-
const { wgl025 } = require("./lint/post-synth/wgl025");
|
|
44
|
-
const { wgl026 } = require("./lint/post-synth/wgl026");
|
|
45
|
-
const { wgl027 } = require("./lint/post-synth/wgl027");
|
|
46
|
-
const { wgl028 } = require("./lint/post-synth/wgl028");
|
|
47
|
-
return [
|
|
48
|
-
wgl010, wgl011, wgl012, wgl013, wgl014, wgl015,
|
|
49
|
-
wgl016, wgl017, wgl018, wgl019, wgl020, wgl021,
|
|
50
|
-
wgl022, wgl023, wgl024, wgl025, wgl026, wgl027, wgl028,
|
|
51
|
-
];
|
|
30
|
+
postSynthChecks() {
|
|
31
|
+
const postSynthDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "post-synth");
|
|
32
|
+
return discoverPostSynthChecks(postSynthDir, import.meta.url);
|
|
52
33
|
},
|
|
53
34
|
|
|
54
35
|
intrinsics(): IntrinsicDef[] {
|
|
@@ -273,48 +254,12 @@ export const test = new Job({
|
|
|
273
254
|
},
|
|
274
255
|
|
|
275
256
|
mcpTools() {
|
|
276
|
-
return [
|
|
277
|
-
{
|
|
278
|
-
name: "diff",
|
|
279
|
-
description: "Compare current build output against previous output for GitLab CI",
|
|
280
|
-
inputSchema: {
|
|
281
|
-
type: "object" as const,
|
|
282
|
-
properties: {
|
|
283
|
-
path: {
|
|
284
|
-
type: "string",
|
|
285
|
-
description: "Path to the infrastructure project directory",
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
async handler(params: Record<string, unknown>): Promise<unknown> {
|
|
290
|
-
const { diffCommand } = await import("@intentius/chant/cli/commands/diff");
|
|
291
|
-
const result = await diffCommand({
|
|
292
|
-
path: (params.path as string) ?? ".",
|
|
293
|
-
serializers: [gitlabSerializer],
|
|
294
|
-
});
|
|
295
|
-
return result;
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
];
|
|
257
|
+
return [createDiffTool(gitlabSerializer, "Compare current build output against previous output for GitLab CI")];
|
|
299
258
|
},
|
|
300
259
|
|
|
301
260
|
mcpResources() {
|
|
302
261
|
return [
|
|
303
|
-
|
|
304
|
-
uri: "resource-catalog",
|
|
305
|
-
name: "GitLab CI Entity Catalog",
|
|
306
|
-
description: "JSON list of all supported GitLab CI entity types",
|
|
307
|
-
mimeType: "application/json",
|
|
308
|
-
async handler(): Promise<string> {
|
|
309
|
-
const lexicon = require("./generated/lexicon-gitlab.json") as Record<string, { resourceType: string; kind: string }>;
|
|
310
|
-
const entries = Object.entries(lexicon).map(([className, entry]) => ({
|
|
311
|
-
className,
|
|
312
|
-
resourceType: entry.resourceType,
|
|
313
|
-
kind: entry.kind,
|
|
314
|
-
}));
|
|
315
|
-
return JSON.stringify(entries);
|
|
316
|
-
},
|
|
317
|
-
},
|
|
262
|
+
createCatalogResource(import.meta.url, "GitLab CI Entity Catalog", "JSON list of all supported GitLab CI entity types", "lexicon-gitlab.json"),
|
|
318
263
|
{
|
|
319
264
|
uri: "examples/basic-pipeline",
|
|
320
265
|
name: "Basic Pipeline Example",
|
|
@@ -358,537 +303,34 @@ export const deploy = new Job({
|
|
|
358
303
|
await generateDocs(options);
|
|
359
304
|
},
|
|
360
305
|
|
|
361
|
-
skills
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
chant init --lexicon gitlab --template node-pipeline # NodePipeline composite
|
|
390
|
-
chant init --lexicon gitlab --template python-pipeline # PythonPipeline composite
|
|
391
|
-
chant init --lexicon gitlab --template docker-build # DockerBuild composite
|
|
392
|
-
chant init --lexicon gitlab --template review-app # ReviewApp composite
|
|
393
|
-
\`\`\`
|
|
394
|
-
|
|
395
|
-
This creates \`src/\` with chant pipeline definitions. It does NOT create application files — you bring your own app code.
|
|
396
|
-
|
|
397
|
-
### Available templates
|
|
398
|
-
|
|
399
|
-
| Template | What it generates | Best for |
|
|
400
|
-
|----------|-------------------|----------|
|
|
401
|
-
| *(default)* | \`config.ts\` + \`pipeline.ts\` with build/test jobs | Custom pipelines from scratch |
|
|
402
|
-
| \`node-pipeline\` | \`NodePipeline\` composite with npm install/build/test | Node.js apps |
|
|
403
|
-
| \`python-pipeline\` | \`PythonPipeline\` composite with venv/pytest | Python apps |
|
|
404
|
-
| \`docker-build\` | \`DockerBuild\` composite + test job | Containerized apps |
|
|
405
|
-
| \`review-app\` | \`ReviewApp\` composite + test job | Apps needing per-MR environments |
|
|
406
|
-
|
|
407
|
-
## Deploying to GitLab
|
|
408
|
-
|
|
409
|
-
### What goes in the GitLab repo
|
|
410
|
-
|
|
411
|
-
The GitLab repo needs TWO things:
|
|
412
|
-
1. **\`.gitlab-ci.yml\`** — generated by \`chant build\`
|
|
413
|
-
2. **Your application files** — whatever the pipeline scripts reference
|
|
414
|
-
|
|
415
|
-
chant only generates the YAML. Application files (\`package.json\`, \`Dockerfile\`, \`requirements.txt\`, source code, tests) are your responsibility.
|
|
416
|
-
|
|
417
|
-
### Typical project structure in the GitLab repo
|
|
418
|
-
|
|
419
|
-
**Node.js project:**
|
|
420
|
-
\`\`\`
|
|
421
|
-
.gitlab-ci.yml # generated by chant
|
|
422
|
-
package.json # your app's package.json with build/test scripts
|
|
423
|
-
index.js # your app code
|
|
424
|
-
test.js # your tests
|
|
425
|
-
\`\`\`
|
|
426
|
-
|
|
427
|
-
**Python project:**
|
|
428
|
-
\`\`\`
|
|
429
|
-
.gitlab-ci.yml
|
|
430
|
-
requirements.txt # must include pytest, pytest-cov if using PythonPipeline defaults
|
|
431
|
-
app.py
|
|
432
|
-
test_app.py
|
|
433
|
-
\`\`\`
|
|
434
|
-
|
|
435
|
-
**Docker project:**
|
|
436
|
-
\`\`\`
|
|
437
|
-
.gitlab-ci.yml
|
|
438
|
-
Dockerfile
|
|
439
|
-
src/ # your app source
|
|
440
|
-
\`\`\`
|
|
441
|
-
|
|
442
|
-
### Important: npm ci vs npm install
|
|
443
|
-
|
|
444
|
-
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:
|
|
445
|
-
|
|
446
|
-
\`\`\`typescript
|
|
447
|
-
NodePipeline({
|
|
448
|
-
installCommand: "npm install", // use instead of npm ci
|
|
449
|
-
...
|
|
450
|
-
})
|
|
451
|
-
\`\`\`
|
|
452
|
-
|
|
453
|
-
Or generate a lockfile: \`npm install && git add package-lock.json\`.
|
|
454
|
-
|
|
455
|
-
### Step-by-step: push to GitLab
|
|
456
|
-
|
|
457
|
-
\`\`\`bash
|
|
458
|
-
# 1. Build the YAML from chant source
|
|
459
|
-
chant build src/ --output .gitlab-ci.yml
|
|
460
|
-
|
|
461
|
-
# 2. Initialize git (if needed) and commit everything
|
|
462
|
-
git init -b main
|
|
463
|
-
git add .gitlab-ci.yml package.json index.js test.js # add your app files
|
|
464
|
-
git commit -m "Initial pipeline"
|
|
465
|
-
|
|
466
|
-
# 3. Push to GitLab
|
|
467
|
-
git remote add origin git@gitlab.com:YOUR_GROUP/YOUR_PROJECT.git
|
|
468
|
-
git push -u origin main
|
|
469
|
-
\`\`\`
|
|
470
|
-
|
|
471
|
-
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.
|
|
472
|
-
|
|
473
|
-
## Build and validate
|
|
474
|
-
|
|
475
|
-
### Build the pipeline
|
|
476
|
-
|
|
477
|
-
\`\`\`bash
|
|
478
|
-
chant build src/ --output .gitlab-ci.yml
|
|
479
|
-
\`\`\`
|
|
480
|
-
|
|
481
|
-
Options:
|
|
482
|
-
- \`--format yaml\` — emit YAML (default for GitLab)
|
|
483
|
-
- \`--watch\` — rebuild on source changes
|
|
484
|
-
- \`--output <path>\` — write to a specific file
|
|
485
|
-
|
|
486
|
-
### Lint the source
|
|
487
|
-
|
|
488
|
-
\`\`\`bash
|
|
489
|
-
chant lint src/
|
|
490
|
-
\`\`\`
|
|
491
|
-
|
|
492
|
-
Options:
|
|
493
|
-
- \`--fix\` — auto-fix violations where possible
|
|
494
|
-
- \`--format sarif\` — SARIF output for CI integration
|
|
495
|
-
- \`--watch\` — re-lint on changes
|
|
496
|
-
|
|
497
|
-
### Validate with GitLab CI Lint API
|
|
498
|
-
|
|
499
|
-
\`\`\`bash
|
|
500
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
501
|
-
--header "Content-Type: application/json" \\
|
|
502
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
503
|
-
--data-binary @- <<EOF
|
|
504
|
-
{"content": $(cat .gitlab-ci.yml | jq -Rs .)}
|
|
505
|
-
EOF
|
|
506
|
-
\`\`\`
|
|
507
|
-
|
|
508
|
-
Add \`"dry_run": true, "include_merged_yaml": true\` for full expansion with includes resolved.
|
|
509
|
-
|
|
510
|
-
### What each step catches
|
|
511
|
-
|
|
512
|
-
| Step | Catches | When to run |
|
|
513
|
-
|------|---------|-------------|
|
|
514
|
-
| \`chant lint\` | Deprecated only/except (WGL001), missing script (WGL002), missing stage (WGL003), artifacts without expiry (WGL004) | Every edit |
|
|
515
|
-
| \`chant build\` | Post-synth checks: undefined stages (WGL010), unreachable jobs (WGL011), deprecated properties (WGL012), invalid needs/extends targets (WGL013-014), circular needs (WGL015), secrets in variables (WGL016), insecure registry (WGL017), missing timeout/retry (WGL018-019), duplicate jobs (WGL020), unused variables (WGL021), missing artifacts expiry (WGL022), overly broad rules (WGL023), manual without allow_failure (WGL024), missing cache key (WGL025), DinD without TLS (WGL026), empty script (WGL027), redundant needs (WGL028) | Before push |
|
|
516
|
-
| CI Lint API | GitLab-specific validation: include resolution, variable expansion, YAML schema errors | Before merge to default branch |
|
|
517
|
-
|
|
518
|
-
Always run all three before deploying. Lint catches things the API cannot (and vice versa).
|
|
519
|
-
|
|
520
|
-
## Diffing and change preview
|
|
521
|
-
|
|
522
|
-
### Local diff
|
|
523
|
-
|
|
524
|
-
Compare generated \`.gitlab-ci.yml\` against the version on the remote branch:
|
|
525
|
-
|
|
526
|
-
\`\`\`bash
|
|
527
|
-
# Build the proposed config
|
|
528
|
-
chant build src/ --output .gitlab-ci.yml
|
|
529
|
-
|
|
530
|
-
# Diff against the remote version
|
|
531
|
-
git diff origin/main -- .gitlab-ci.yml
|
|
532
|
-
\`\`\`
|
|
533
|
-
|
|
534
|
-
### MR pipeline preview
|
|
535
|
-
|
|
536
|
-
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.
|
|
537
|
-
|
|
538
|
-
### CI Lint API with dry run
|
|
539
|
-
|
|
540
|
-
\`\`\`bash
|
|
541
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
542
|
-
--header "Content-Type: application/json" \\
|
|
543
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
544
|
-
--data-binary @- <<EOF
|
|
545
|
-
{"content": $(cat .gitlab-ci.yml | jq -Rs .), "dry_run": true, "include_merged_yaml": true}
|
|
546
|
-
EOF
|
|
547
|
-
\`\`\`
|
|
548
|
-
|
|
549
|
-
This resolves all \`include:\` directives and expands the full pipeline — useful for catching issues with cross-file references.
|
|
550
|
-
|
|
551
|
-
### Safe preview checklist
|
|
552
|
-
|
|
553
|
-
Before merging pipeline changes, verify:
|
|
554
|
-
1. All jobs have valid \`stage:\` values and stages are defined
|
|
555
|
-
2. \`needs:\` references point to existing job names
|
|
556
|
-
3. \`rules:\` conditions match the intended branches/events
|
|
557
|
-
4. Environment names and \`on_stop\` references are correct
|
|
558
|
-
5. Docker images are accessible from your runners
|
|
559
|
-
6. Secrets/variables referenced in scripts exist in project settings
|
|
560
|
-
|
|
561
|
-
## Deploying pipeline changes
|
|
562
|
-
|
|
563
|
-
### Safe path (production pipelines)
|
|
564
|
-
|
|
565
|
-
1. Build: \`chant build src/ --output .gitlab-ci.yml\`
|
|
566
|
-
2. Lint: \`chant lint src/\`
|
|
567
|
-
3. Validate: CI Lint API (see above)
|
|
568
|
-
4. Push to feature branch: \`git push -u origin feature/pipeline-update\`
|
|
569
|
-
5. Open MR — review pipeline diff in the MR widget
|
|
570
|
-
6. Merge — pipeline runs on the default branch
|
|
571
|
-
|
|
572
|
-
### Fast path (dev/iteration)
|
|
573
|
-
|
|
574
|
-
\`\`\`bash
|
|
575
|
-
chant build src/ --output .gitlab-ci.yml
|
|
576
|
-
git add .gitlab-ci.yml && git commit -m "Update pipeline" && git push
|
|
577
|
-
\`\`\`
|
|
578
|
-
|
|
579
|
-
### Which path to use
|
|
580
|
-
|
|
581
|
-
| Scenario | Path |
|
|
582
|
-
|----------|------|
|
|
583
|
-
| Production pipeline with deploy jobs | Safe path (MR review) |
|
|
584
|
-
| Pipeline with environment/secrets changes | Safe path (MR review) |
|
|
585
|
-
| Dev/test pipeline iteration | Fast path (direct push) |
|
|
586
|
-
| CI/CD with approval gates or protected environments | Safe path + protected branch |
|
|
587
|
-
|
|
588
|
-
## Environment lifecycle
|
|
589
|
-
|
|
590
|
-
Environments are created by jobs with an \`environment:\` keyword. They track deployments and enable rollback.
|
|
591
|
-
|
|
592
|
-
### Review apps pattern
|
|
593
|
-
|
|
594
|
-
Deploy on MR, auto-stop when MR is merged or closed:
|
|
595
|
-
|
|
596
|
-
\`\`\`typescript
|
|
597
|
-
new Job({
|
|
598
|
-
stage: "deploy",
|
|
599
|
-
environment: new Environment({
|
|
600
|
-
name: "review/$CI_COMMIT_REF_SLUG",
|
|
601
|
-
url: "https://$CI_COMMIT_REF_SLUG.example.com",
|
|
602
|
-
onStop: "stop_review",
|
|
603
|
-
autoStopIn: "1 week",
|
|
604
|
-
}),
|
|
605
|
-
script: ["./deploy-review.sh"],
|
|
606
|
-
rules: [{ if: "$CI_MERGE_REQUEST_IID" }],
|
|
607
|
-
});
|
|
608
|
-
\`\`\`
|
|
609
|
-
|
|
610
|
-
### Environment promotion
|
|
611
|
-
|
|
612
|
-
Deploy through environments in order: dev → staging → production. Use \`rules:\` and \`when: manual\` to gate promotions.
|
|
613
|
-
|
|
614
|
-
### Rollback to a previous deployment
|
|
615
|
-
|
|
616
|
-
\`\`\`bash
|
|
617
|
-
# List deployments for an environment
|
|
618
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
619
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/environments/$ENV_ID/deployments?order_by=created_at&sort=desc&per_page=5"
|
|
620
|
-
|
|
621
|
-
# Re-deploy a previous deployment's commit
|
|
622
|
-
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
623
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/deployments" \\
|
|
624
|
-
--data "environment=production&sha=$PREVIOUS_SHA&ref=main&tag=false&status=created"
|
|
625
|
-
\`\`\`
|
|
626
|
-
|
|
627
|
-
Alternatively, revert the MR that introduced the change and let the pipeline re-run.
|
|
628
|
-
|
|
629
|
-
## Pipeline and job states
|
|
630
|
-
|
|
631
|
-
### Pipeline states
|
|
632
|
-
|
|
633
|
-
| State | Meaning | Action |
|
|
634
|
-
|-------|---------|--------|
|
|
635
|
-
| \`created\` | Pipeline created, not yet started | Wait |
|
|
636
|
-
| \`waiting_for_resource\` | Waiting for runner | Check runner availability |
|
|
637
|
-
| \`preparing\` | Job is being prepared | Wait |
|
|
638
|
-
| \`pending\` | Waiting for runner to pick up | Check runner tags/availability |
|
|
639
|
-
| \`running\` | Pipeline is executing | Monitor |
|
|
640
|
-
| \`success\` | All jobs passed | None — healthy |
|
|
641
|
-
| \`failed\` | One or more jobs failed | Check failed job logs |
|
|
642
|
-
| \`canceled\` | Pipeline was canceled | Re-run if needed |
|
|
643
|
-
| \`skipped\` | Pipeline was skipped by rules | Check rules configuration |
|
|
644
|
-
| \`manual\` | Pipeline waiting for manual action | Trigger manual job or cancel |
|
|
645
|
-
| \`scheduled\` | Waiting for scheduled time | Wait |
|
|
646
|
-
|
|
647
|
-
### Job states
|
|
648
|
-
|
|
649
|
-
| State | Meaning | Action |
|
|
650
|
-
|-------|---------|--------|
|
|
651
|
-
| \`created\` | Job created | Wait |
|
|
652
|
-
| \`pending\` | Waiting for runner | Check runner tags |
|
|
653
|
-
| \`running\` | Job executing | Monitor logs |
|
|
654
|
-
| \`success\` | Job passed | None |
|
|
655
|
-
| \`failed\` | Job failed | Read trace log |
|
|
656
|
-
| \`canceled\` | Job canceled | Re-run if needed |
|
|
657
|
-
| \`skipped\` | Job skipped by rules/needs | Check rules |
|
|
658
|
-
| \`manual\` | Waiting for manual trigger | Play or skip |
|
|
659
|
-
| \`allowed_failure\` | Failed but allowed | Review — may indicate flaky test |
|
|
660
|
-
|
|
661
|
-
## Monitoring pipelines
|
|
662
|
-
|
|
663
|
-
### Check pipeline status
|
|
664
|
-
|
|
665
|
-
\`\`\`bash
|
|
666
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
667
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
|
|
668
|
-
\`\`\`
|
|
669
|
-
|
|
670
|
-
### List recent pipelines for a branch
|
|
671
|
-
|
|
672
|
-
\`\`\`bash
|
|
673
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
674
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?ref=main&per_page=5"
|
|
675
|
-
\`\`\`
|
|
676
|
-
|
|
677
|
-
### Get jobs in a pipeline
|
|
678
|
-
|
|
679
|
-
\`\`\`bash
|
|
680
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
681
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
|
|
682
|
-
\`\`\`
|
|
683
|
-
|
|
684
|
-
### Stream job logs
|
|
685
|
-
|
|
686
|
-
\`\`\`bash
|
|
687
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
688
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
689
|
-
\`\`\`
|
|
690
|
-
|
|
691
|
-
### Download job artifacts
|
|
692
|
-
|
|
693
|
-
\`\`\`bash
|
|
694
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
695
|
-
--output artifacts.zip \\
|
|
696
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/artifacts"
|
|
697
|
-
\`\`\`
|
|
698
|
-
|
|
699
|
-
## Merge request pipeline workflow
|
|
700
|
-
|
|
701
|
-
### How MR pipelines differ
|
|
702
|
-
|
|
703
|
-
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.
|
|
704
|
-
|
|
705
|
-
### Common rules patterns
|
|
706
|
-
|
|
707
|
-
\`\`\`yaml
|
|
708
|
-
# Run only on MR pipelines
|
|
709
|
-
rules:
|
|
710
|
-
- if: $CI_MERGE_REQUEST_IID
|
|
711
|
-
|
|
712
|
-
# Run only on the default branch
|
|
713
|
-
rules:
|
|
714
|
-
- if: $CI_COMMIT_BRANCH == "main"
|
|
715
|
-
|
|
716
|
-
# Run on MRs and the default branch (but not both at once)
|
|
717
|
-
rules:
|
|
718
|
-
- if: $CI_MERGE_REQUEST_IID
|
|
719
|
-
- if: $CI_COMMIT_BRANCH == "main"
|
|
720
|
-
\`\`\`
|
|
721
|
-
|
|
722
|
-
### Merged results pipelines
|
|
723
|
-
|
|
724
|
-
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.
|
|
725
|
-
|
|
726
|
-
### Merge trains
|
|
727
|
-
|
|
728
|
-
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.
|
|
729
|
-
|
|
730
|
-
## Troubleshooting decision tree
|
|
731
|
-
|
|
732
|
-
### Step 1: Check pipeline status
|
|
733
|
-
|
|
734
|
-
\`\`\`bash
|
|
735
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
736
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID" | jq '.status'
|
|
737
|
-
\`\`\`
|
|
738
|
-
|
|
739
|
-
### Step 2: Branch on status
|
|
740
|
-
|
|
741
|
-
- **\`running\` / \`pending\` / \`created\`** → Wait. Do not take action while the pipeline is in progress.
|
|
742
|
-
- **\`failed\`** → Read the failed job logs (Step 3).
|
|
743
|
-
- **\`success\`** → Pipeline is healthy. If behavior is wrong, check job scripts and configuration.
|
|
744
|
-
- **\`canceled\`** → Re-run if needed: \`curl --request POST ... /pipelines/$PIPELINE_ID/retry\`
|
|
745
|
-
- **\`skipped\`** → All jobs were filtered out by \`rules:\`. Check rule conditions.
|
|
746
|
-
|
|
747
|
-
### Step 3: Read failed job logs
|
|
748
|
-
|
|
749
|
-
\`\`\`bash
|
|
750
|
-
# Get failed jobs
|
|
751
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
752
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs?scope=failed" | jq '.[].id'
|
|
753
|
-
|
|
754
|
-
# Read the trace for a failed job
|
|
755
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
756
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
757
|
-
\`\`\`
|
|
758
|
-
|
|
759
|
-
### Step 4: Diagnose by error pattern
|
|
760
|
-
|
|
761
|
-
| Error pattern | Likely cause | Fix |
|
|
762
|
-
|---------------|-------------|-----|
|
|
763
|
-
| "no matching runner" | No runner with matching tags | Check runner tags, register a runner |
|
|
764
|
-
| "image pull failed" | Docker image not found or auth failed | Check image name, registry credentials |
|
|
765
|
-
| "script exit code 1" | Script command failed | Read job log for the failing command |
|
|
766
|
-
| "artifact upload failed" | Artifact path doesn't exist or too large | Check \`artifacts.paths\`, size limits |
|
|
767
|
-
| "cache not found" | Cache key mismatch or first run | Expected on first run; check \`cache.key\` |
|
|
768
|
-
| "yaml invalid" | Syntax error in generated YAML | Run \`chant lint src/\` and CI Lint API |
|
|
769
|
-
| "pipeline filtered out" | All jobs filtered by rules | Check \`rules:\` conditions |
|
|
770
|
-
| "job timed out" | Job exceeded timeout | Increase \`timeout:\` or optimize job |
|
|
771
|
-
| "stuck or pending" | No available runner | Check runner status, tags, executor capacity |
|
|
772
|
-
| "environment does not exist" | \`on_stop\` references non-existent job | Check \`on_stop\` job name matches expanded name |
|
|
773
|
-
| "needs job not found" | \`needs:\` references non-existent job | Check job names, stage ordering |
|
|
774
|
-
|
|
775
|
-
## Variable management
|
|
776
|
-
|
|
777
|
-
### Variable types and precedence
|
|
778
|
-
|
|
779
|
-
Variables are resolved in this order (highest priority first):
|
|
780
|
-
1. Job-level \`variables:\`
|
|
781
|
-
2. Project CI/CD variables (Settings → CI/CD → Variables)
|
|
782
|
-
3. Group CI/CD variables
|
|
783
|
-
4. Instance CI/CD variables
|
|
784
|
-
|
|
785
|
-
### Protected and masked variables
|
|
786
|
-
|
|
787
|
-
- **Protected**: only available in pipelines on protected branches/tags
|
|
788
|
-
- **Masked**: hidden in job logs (value must meet masking requirements)
|
|
789
|
-
|
|
790
|
-
### Managing variables via API
|
|
791
|
-
|
|
792
|
-
\`\`\`bash
|
|
793
|
-
# List project variables
|
|
794
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
795
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables"
|
|
796
|
-
|
|
797
|
-
# Create a variable
|
|
798
|
-
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
799
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables" \\
|
|
800
|
-
--form "key=DEPLOY_TOKEN" --form "value=secret" --form "masked=true" --form "protected=true"
|
|
801
|
-
|
|
802
|
-
# Update a variable
|
|
803
|
-
curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
804
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN" \\
|
|
805
|
-
--form "value=new-secret"
|
|
806
|
-
|
|
807
|
-
# Delete a variable
|
|
808
|
-
curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
809
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN"
|
|
810
|
-
\`\`\`
|
|
811
|
-
|
|
812
|
-
## Quick reference
|
|
813
|
-
|
|
814
|
-
### Pipeline info commands
|
|
815
|
-
|
|
816
|
-
\`\`\`bash
|
|
817
|
-
# List recent pipelines
|
|
818
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
819
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?per_page=5"
|
|
820
|
-
|
|
821
|
-
# Get pipeline status
|
|
822
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
823
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
|
|
824
|
-
|
|
825
|
-
# Get jobs in a pipeline
|
|
826
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
827
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
|
|
828
|
-
|
|
829
|
-
# Read job log
|
|
830
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
831
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
|
|
832
|
-
|
|
833
|
-
# Retry a failed pipeline
|
|
834
|
-
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
835
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/retry"
|
|
836
|
-
|
|
837
|
-
# Cancel a running pipeline
|
|
838
|
-
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
839
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/cancel"
|
|
840
|
-
\`\`\`
|
|
841
|
-
|
|
842
|
-
### Full build-to-deploy pipeline
|
|
843
|
-
|
|
844
|
-
\`\`\`bash
|
|
845
|
-
# 1. Lint
|
|
846
|
-
chant lint src/
|
|
847
|
-
|
|
848
|
-
# 2. Build
|
|
849
|
-
chant build src/ --output .gitlab-ci.yml
|
|
850
|
-
|
|
851
|
-
# 3. Validate via API
|
|
852
|
-
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
853
|
-
--header "Content-Type: application/json" \\
|
|
854
|
-
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
855
|
-
--data-binary @- <<EOF
|
|
856
|
-
{"content": $(cat .gitlab-ci.yml | jq -Rs .)}
|
|
857
|
-
EOF
|
|
858
|
-
|
|
859
|
-
# 4. Push to feature branch
|
|
860
|
-
git checkout -b feature/pipeline-update
|
|
861
|
-
git add .gitlab-ci.yml
|
|
862
|
-
git commit -m "Update pipeline"
|
|
863
|
-
git push -u origin feature/pipeline-update
|
|
864
|
-
|
|
865
|
-
# 5. Open MR, review pipeline diff, merge
|
|
866
|
-
# Pipeline runs automatically on the default branch after merge
|
|
867
|
-
\`\`\`
|
|
868
|
-
`,
|
|
869
|
-
triggers: [
|
|
870
|
-
{ type: "file-pattern", value: "**/*.gitlab.ts" },
|
|
871
|
-
{ type: "file-pattern", value: "**/.gitlab-ci.yml" },
|
|
872
|
-
{ type: "context", value: "gitlab" },
|
|
873
|
-
{ type: "context", value: "pipeline" },
|
|
874
|
-
{ type: "context", value: "deploy" },
|
|
875
|
-
],
|
|
876
|
-
preConditions: [
|
|
877
|
-
"chant CLI is installed (chant --version succeeds)",
|
|
878
|
-
"git is configured and can push to the remote",
|
|
879
|
-
"Project has chant source files in src/",
|
|
880
|
-
],
|
|
881
|
-
postConditions: [
|
|
882
|
-
"Pipeline is in a stable state (success/manual/scheduled)",
|
|
883
|
-
"No failed jobs in the pipeline",
|
|
884
|
-
],
|
|
885
|
-
parameters: [],
|
|
886
|
-
examples: [
|
|
887
|
-
{
|
|
888
|
-
title: "Basic test job",
|
|
889
|
-
description: "Create a test job with caching and artifacts",
|
|
890
|
-
input: "Create a test job",
|
|
891
|
-
output: `new Job({
|
|
306
|
+
skills: createSkillsLoader(import.meta.url, [
|
|
307
|
+
{
|
|
308
|
+
file: "chant-gitlab.md",
|
|
309
|
+
name: "chant-gitlab",
|
|
310
|
+
description: "GitLab CI/CD pipeline lifecycle — build, validate, deploy, monitor, rollback, and troubleshoot",
|
|
311
|
+
triggers: [
|
|
312
|
+
{ type: "file-pattern", value: "**/*.gitlab.ts" },
|
|
313
|
+
{ type: "file-pattern", value: "**/.gitlab-ci.yml" },
|
|
314
|
+
{ type: "context", value: "gitlab" },
|
|
315
|
+
{ type: "context", value: "pipeline" },
|
|
316
|
+
{ type: "context", value: "deploy" },
|
|
317
|
+
],
|
|
318
|
+
preConditions: [
|
|
319
|
+
"chant CLI is installed (chant --version succeeds)",
|
|
320
|
+
"git is configured and can push to the remote",
|
|
321
|
+
"Project has chant source files in src/",
|
|
322
|
+
],
|
|
323
|
+
postConditions: [
|
|
324
|
+
"Pipeline is in a stable state (success/manual/scheduled)",
|
|
325
|
+
"No failed jobs in the pipeline",
|
|
326
|
+
],
|
|
327
|
+
parameters: [],
|
|
328
|
+
examples: [
|
|
329
|
+
{
|
|
330
|
+
title: "Basic test job",
|
|
331
|
+
description: "Create a test job with caching and artifacts",
|
|
332
|
+
input: "Create a test job",
|
|
333
|
+
output: `new Job({
|
|
892
334
|
stage: "test",
|
|
893
335
|
image: new Image({ name: "node:20" }),
|
|
894
336
|
script: ["npm ci", "npm test"],
|
|
@@ -900,36 +342,36 @@ git push -u origin feature/pipeline-update
|
|
|
900
342
|
reports: { junit: "coverage/junit.xml" },
|
|
901
343
|
}),
|
|
902
344
|
})`,
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
title: "Deploy pipeline update",
|
|
348
|
+
description: "Build, validate, and deploy a pipeline change via MR workflow",
|
|
349
|
+
input: "Deploy my pipeline changes to production",
|
|
350
|
+
output: `chant lint src/
|
|
909
351
|
chant build src/ --output .gitlab-ci.yml
|
|
910
352
|
git checkout -b feature/pipeline-update
|
|
911
353
|
git add .gitlab-ci.yml
|
|
912
354
|
git commit -m "Update pipeline"
|
|
913
355
|
git push -u origin feature/pipeline-update
|
|
914
356
|
# Open MR in GitLab, review pipeline diff, then merge`,
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
title: "Preview pipeline changes",
|
|
360
|
+
description: "Validate pipeline configuration via lint and CI Lint API before deploying",
|
|
361
|
+
input: "Check if my pipeline changes are valid before pushing",
|
|
362
|
+
output: `chant lint src/
|
|
921
363
|
chant build src/ --output .gitlab-ci.yml
|
|
922
364
|
# Validate via GitLab CI Lint API
|
|
923
365
|
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
924
366
|
--header "Content-Type: application/json" \\
|
|
925
367
|
"https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \\
|
|
926
368
|
--data-binary '{"content": "'$(cat .gitlab-ci.yml | jq -Rs .)'", "dry_run": true}'`,
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
title: "Scaffold and deploy a Node.js pipeline",
|
|
372
|
+
description: "Use --template to scaffold a Node.js project, build YAML, and push to GitLab",
|
|
373
|
+
input: "Create a Node.js CI pipeline and deploy it to GitLab",
|
|
374
|
+
output: `# Scaffold the project
|
|
933
375
|
chant init --lexicon gitlab --template node-pipeline my-node-app
|
|
934
376
|
cd my-node-app
|
|
935
377
|
|
|
@@ -946,66 +388,40 @@ git add .gitlab-ci.yml package.json test.js
|
|
|
946
388
|
git commit -m "Initial pipeline"
|
|
947
389
|
git remote add origin git@gitlab.com:YOUR_GROUP/YOUR_PROJECT.git
|
|
948
390
|
git push -u origin main`,
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
title: "Retry a failed pipeline",
|
|
394
|
+
description: "Retry a failed pipeline and monitor its progress",
|
|
395
|
+
input: "Pipeline 12345 failed, retry it",
|
|
396
|
+
output: `# Retry the pipeline
|
|
955
397
|
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
956
398
|
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/12345/retry"
|
|
957
399
|
# Monitor status
|
|
958
400
|
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \\
|
|
959
401
|
"https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/12345"`,
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
{
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
examples: [
|
|
986
|
-
{
|
|
987
|
-
title: "Pipeline with caching",
|
|
988
|
-
input: "Set up a Node.js pipeline with proper caching",
|
|
989
|
-
output: "import { Job, Cache } from \"@intentius/chant-lexicon-gitlab\";\n\nconst cache = new Cache({ key: { files: [\"package-lock.json\"] }, paths: [\"node_modules/\"] });",
|
|
990
|
-
},
|
|
991
|
-
],
|
|
992
|
-
},
|
|
993
|
-
];
|
|
994
|
-
|
|
995
|
-
for (const skill of skillFiles) {
|
|
996
|
-
try {
|
|
997
|
-
const content = readFileSync(join(dir, "skills", skill.file), "utf-8");
|
|
998
|
-
skills.push({
|
|
999
|
-
name: skill.name,
|
|
1000
|
-
description: skill.description,
|
|
1001
|
-
content,
|
|
1002
|
-
triggers: skill.triggers,
|
|
1003
|
-
parameters: skill.parameters,
|
|
1004
|
-
examples: skill.examples,
|
|
1005
|
-
});
|
|
1006
|
-
} catch { /* skip missing skills */ }
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
return skills;
|
|
1010
|
-
},
|
|
402
|
+
},
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
file: "chant-gitlab-patterns.md",
|
|
407
|
+
name: "chant-gitlab-patterns",
|
|
408
|
+
description: "GitLab CI/CD pipeline stages, caching, artifacts, includes, and advanced patterns",
|
|
409
|
+
triggers: [
|
|
410
|
+
{ type: "context", value: "gitlab pipeline" },
|
|
411
|
+
{ type: "context", value: "gitlab cache" },
|
|
412
|
+
{ type: "context", value: "gitlab artifacts" },
|
|
413
|
+
{ type: "context", value: "gitlab include" },
|
|
414
|
+
{ type: "context", value: "gitlab stages" },
|
|
415
|
+
{ type: "context", value: "review app" },
|
|
416
|
+
],
|
|
417
|
+
parameters: [],
|
|
418
|
+
examples: [
|
|
419
|
+
{
|
|
420
|
+
title: "Pipeline with caching",
|
|
421
|
+
input: "Set up a Node.js pipeline with proper caching",
|
|
422
|
+
output: "import { Job, Cache } from \"@intentius/chant-lexicon-gitlab\";\n\nconst cache = new Cache({ key: { files: [\"package-lock.json\"] }, paths: [\"node_modules/\"] });",
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
]),
|
|
1011
427
|
};
|