@intentius/chant-lexicon-gitlab 0.0.22 → 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/src/plugin.ts CHANGED
@@ -6,10 +6,13 @@
6
6
  */
7
7
 
8
8
  import { createRequire } from "module";
9
- import type { LexiconPlugin, IntrinsicDef, SkillDefinition, InitTemplateSet } from "@intentius/chant/lexicon";
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 type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
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(): PostSynthCheck[] {
28
- const { wgl010 } = require("./lint/post-synth/wgl010");
29
- const { wgl011 } = require("./lint/post-synth/wgl011");
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(): SkillDefinition[] {
362
- const skills: SkillDefinition[] = [
363
- {
364
- name: "chant-gitlab",
365
- description: "GitLab CI/CD pipeline lifecycle — build, validate, deploy, monitor, rollback, and troubleshoot",
366
- content: `---
367
- skill: chant-gitlab
368
- description: Build, validate, and deploy GitLab CI pipelines from a chant project
369
- user-invocable: true
370
- ---
371
-
372
- # GitLab CI/CD Operational Playbook
373
-
374
- ## How chant and GitLab CI relate
375
-
376
- chant is a **synthesis compiler** — it compiles TypeScript source files into \`.gitlab-ci.yml\` (YAML). \`chant build\` does not call GitLab APIs; synthesis is pure and deterministic. Your job as an agent is to bridge synthesis and deployment:
377
-
378
- - Use **chant** for: build, lint, diff (local YAML comparison)
379
- - Use **git + GitLab API** for: push, merge requests, pipeline monitoring, job logs, rollback, and all deployment operations
380
-
381
- 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.
382
-
383
- ## Scaffolding a new project
384
-
385
- ### Initialize with a template
386
-
387
- \`\`\`bash
388
- chant init --lexicon gitlab # default: config.ts + pipeline.ts
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
- title: "Deploy pipeline update",
906
- description: "Build, validate, and deploy a pipeline change via MR workflow",
907
- input: "Deploy my pipeline changes to production",
908
- output: `chant lint src/
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
- title: "Preview pipeline changes",
918
- description: "Validate pipeline configuration via lint and CI Lint API before deploying",
919
- input: "Check if my pipeline changes are valid before pushing",
920
- output: `chant lint src/
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
- title: "Scaffold and deploy a Node.js pipeline",
930
- description: "Use --template to scaffold a Node.js project, build YAML, and push to GitLab",
931
- input: "Create a Node.js CI pipeline and deploy it to GitLab",
932
- output: `# Scaffold the project
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
- title: "Retry a failed pipeline",
952
- description: "Retry a failed pipeline and monitor its progress",
953
- input: "Pipeline 12345 failed, retry it",
954
- output: `# Retry the pipeline
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
- // Load file-based skills from src/skills/
966
- const { readFileSync } = require("fs");
967
- const { join, dirname } = require("path");
968
- const { fileURLToPath } = require("url");
969
- const dir = dirname(fileURLToPath(import.meta.url));
970
-
971
- const skillFiles = [
972
- {
973
- file: "gitlab-ci-patterns.md",
974
- name: "gitlab-ci-patterns",
975
- description: "GitLab CI/CD pipeline stages, caching, artifacts, includes, and advanced patterns",
976
- triggers: [
977
- { type: "context" as const, value: "gitlab pipeline" },
978
- { type: "context" as const, value: "gitlab cache" },
979
- { type: "context" as const, value: "gitlab artifacts" },
980
- { type: "context" as const, value: "gitlab include" },
981
- { type: "context" as const, value: "gitlab stages" },
982
- { type: "context" as const, value: "review app" },
983
- ],
984
- parameters: [],
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
  };