@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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- skill: gitlab-ci-patterns
2
+ skill: chant-gitlab-patterns
3
3
  description: GitLab CI/CD pipeline stages, caching, artifacts, includes, and advanced patterns
4
4
  user-invocable: true
5
5
  ---
@@ -0,0 +1,502 @@
1
+ ---
2
+ skill: chant-gitlab
3
+ description: Build, validate, and deploy GitLab CI pipelines from a chant project
4
+ user-invocable: true
5
+ ---
6
+
7
+ # GitLab CI/CD Operational Playbook
8
+
9
+ ## How chant and GitLab CI relate
10
+
11
+ 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:
12
+
13
+ - Use **chant** for: build, lint, diff (local YAML comparison)
14
+ - Use **git + GitLab API** for: push, merge requests, pipeline monitoring, job logs, rollback, and all deployment operations
15
+
16
+ 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.
17
+
18
+ ## Scaffolding a new project
19
+
20
+ ### Initialize with a template
21
+
22
+ ```bash
23
+ chant init --lexicon gitlab # default: config.ts + pipeline.ts
24
+ chant init --lexicon gitlab --template node-pipeline # NodePipeline composite
25
+ chant init --lexicon gitlab --template python-pipeline # PythonPipeline composite
26
+ chant init --lexicon gitlab --template docker-build # DockerBuild composite
27
+ chant init --lexicon gitlab --template review-app # ReviewApp composite
28
+ ```
29
+
30
+ This creates `src/` with chant pipeline definitions. It does NOT create application files — you bring your own app code.
31
+
32
+ ### Available templates
33
+
34
+ | Template | What it generates | Best for |
35
+ |----------|-------------------|----------|
36
+ | *(default)* | `config.ts` + `pipeline.ts` with build/test jobs | Custom pipelines from scratch |
37
+ | `node-pipeline` | `NodePipeline` composite with npm install/build/test | Node.js apps |
38
+ | `python-pipeline` | `PythonPipeline` composite with venv/pytest | Python apps |
39
+ | `docker-build` | `DockerBuild` composite + test job | Containerized apps |
40
+ | `review-app` | `ReviewApp` composite + test job | Apps needing per-MR environments |
41
+
42
+ ## Deploying to GitLab
43
+
44
+ ### What goes in the GitLab repo
45
+
46
+ The GitLab repo needs TWO things:
47
+ 1. **`.gitlab-ci.yml`** — generated by `chant build`
48
+ 2. **Your application files** — whatever the pipeline scripts reference
49
+
50
+ chant only generates the YAML. Application files (`package.json`, `Dockerfile`, `requirements.txt`, source code, tests) are your responsibility.
51
+
52
+ ### Typical project structure in the GitLab repo
53
+
54
+ **Node.js project:**
55
+ ```
56
+ .gitlab-ci.yml # generated by chant
57
+ package.json # your app's package.json with build/test scripts
58
+ index.js # your app code
59
+ test.js # your tests
60
+ ```
61
+
62
+ **Python project:**
63
+ ```
64
+ .gitlab-ci.yml
65
+ requirements.txt # must include pytest, pytest-cov if using PythonPipeline defaults
66
+ app.py
67
+ test_app.py
68
+ ```
69
+
70
+ **Docker project:**
71
+ ```
72
+ .gitlab-ci.yml
73
+ Dockerfile
74
+ src/ # your app source
75
+ ```
76
+
77
+ ### Important: npm ci vs npm install
78
+
79
+ 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:
80
+
81
+ ```typescript
82
+ NodePipeline({
83
+ installCommand: "npm install", // use instead of npm ci
84
+ ...
85
+ })
86
+ ```
87
+
88
+ Or generate a lockfile: `npm install && git add package-lock.json`.
89
+
90
+ ### Step-by-step: push to GitLab
91
+
92
+ ```bash
93
+ # 1. Build the YAML from chant source
94
+ chant build src/ --output .gitlab-ci.yml
95
+
96
+ # 2. Initialize git (if needed) and commit everything
97
+ git init -b main
98
+ git add .gitlab-ci.yml package.json index.js test.js # add your app files
99
+ git commit -m "Initial pipeline"
100
+
101
+ # 3. Push to GitLab
102
+ git remote add origin git@gitlab.com:YOUR_GROUP/YOUR_PROJECT.git
103
+ git push -u origin main
104
+ ```
105
+
106
+ 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.
107
+
108
+ ## Build and validate
109
+
110
+ ### Build the pipeline
111
+
112
+ ```bash
113
+ chant build src/ --output .gitlab-ci.yml
114
+ ```
115
+
116
+ Options:
117
+ - `--format yaml` — emit YAML (default for GitLab)
118
+ - `--watch` — rebuild on source changes
119
+ - `--output <path>` — write to a specific file
120
+
121
+ ### Lint the source
122
+
123
+ ```bash
124
+ chant lint src/
125
+ ```
126
+
127
+ Options:
128
+ - `--fix` — auto-fix violations where possible
129
+ - `--format sarif` — SARIF output for CI integration
130
+ - `--watch` — re-lint on changes
131
+
132
+ ### Validate with GitLab CI Lint API
133
+
134
+ ```bash
135
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
136
+ --header "Content-Type: application/json" \
137
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \
138
+ --data-binary @- <<EOF
139
+ {"content": $(cat .gitlab-ci.yml | jq -Rs .)}
140
+ EOF
141
+ ```
142
+
143
+ Add `"dry_run": true, "include_merged_yaml": true` for full expansion with includes resolved.
144
+
145
+ ### What each step catches
146
+
147
+ | Step | Catches | When to run |
148
+ |------|---------|-------------|
149
+ | `chant lint` | Deprecated only/except (WGL001), missing script (WGL002), missing stage (WGL003), artifacts without expiry (WGL004) | Every edit |
150
+ | `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 |
151
+ | CI Lint API | GitLab-specific validation: include resolution, variable expansion, YAML schema errors | Before merge to default branch |
152
+
153
+ Always run all three before deploying. Lint catches things the API cannot (and vice versa).
154
+
155
+ ## Diffing and change preview
156
+
157
+ ### Local diff
158
+
159
+ Compare generated `.gitlab-ci.yml` against the version on the remote branch:
160
+
161
+ ```bash
162
+ # Build the proposed config
163
+ chant build src/ --output .gitlab-ci.yml
164
+
165
+ # Diff against the remote version
166
+ git diff origin/main -- .gitlab-ci.yml
167
+ ```
168
+
169
+ ### MR pipeline preview
170
+
171
+ 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.
172
+
173
+ ### CI Lint API with dry run
174
+
175
+ ```bash
176
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
177
+ --header "Content-Type: application/json" \
178
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \
179
+ --data-binary @- <<EOF
180
+ {"content": $(cat .gitlab-ci.yml | jq -Rs .), "dry_run": true, "include_merged_yaml": true}
181
+ EOF
182
+ ```
183
+
184
+ This resolves all `include:` directives and expands the full pipeline — useful for catching issues with cross-file references.
185
+
186
+ ### Safe preview checklist
187
+
188
+ Before merging pipeline changes, verify:
189
+ 1. All jobs have valid `stage:` values and stages are defined
190
+ 2. `needs:` references point to existing job names
191
+ 3. `rules:` conditions match the intended branches/events
192
+ 4. Environment names and `on_stop` references are correct
193
+ 5. Docker images are accessible from your runners
194
+ 6. Secrets/variables referenced in scripts exist in project settings
195
+
196
+ ## Deploying pipeline changes
197
+
198
+ ### Safe path (production pipelines)
199
+
200
+ 1. Build: `chant build src/ --output .gitlab-ci.yml`
201
+ 2. Lint: `chant lint src/`
202
+ 3. Validate: CI Lint API (see above)
203
+ 4. Push to feature branch: `git push -u origin feature/pipeline-update`
204
+ 5. Open MR — review pipeline diff in the MR widget
205
+ 6. Merge — pipeline runs on the default branch
206
+
207
+ ### Fast path (dev/iteration)
208
+
209
+ ```bash
210
+ chant build src/ --output .gitlab-ci.yml
211
+ git add .gitlab-ci.yml && git commit -m "Update pipeline" && git push
212
+ ```
213
+
214
+ ### Which path to use
215
+
216
+ | Scenario | Path |
217
+ |----------|------|
218
+ | Production pipeline with deploy jobs | Safe path (MR review) |
219
+ | Pipeline with environment/secrets changes | Safe path (MR review) |
220
+ | Dev/test pipeline iteration | Fast path (direct push) |
221
+ | CI/CD with approval gates or protected environments | Safe path + protected branch |
222
+
223
+ ## Environment lifecycle
224
+
225
+ Environments are created by jobs with an `environment:` keyword. They track deployments and enable rollback.
226
+
227
+ ### Review apps pattern
228
+
229
+ Deploy on MR, auto-stop when MR is merged or closed:
230
+
231
+ ```typescript
232
+ new Job({
233
+ stage: "deploy",
234
+ environment: new Environment({
235
+ name: "review/$CI_COMMIT_REF_SLUG",
236
+ url: "https://$CI_COMMIT_REF_SLUG.example.com",
237
+ onStop: "stop_review",
238
+ autoStopIn: "1 week",
239
+ }),
240
+ script: ["./deploy-review.sh"],
241
+ rules: [{ if: "$CI_MERGE_REQUEST_IID" }],
242
+ });
243
+ ```
244
+
245
+ ### Environment promotion
246
+
247
+ Deploy through environments in order: dev -> staging -> production. Use `rules:` and `when: manual` to gate promotions.
248
+
249
+ ### Rollback to a previous deployment
250
+
251
+ ```bash
252
+ # List deployments for an environment
253
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
254
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/environments/$ENV_ID/deployments?order_by=created_at&sort=desc&per_page=5"
255
+
256
+ # Re-deploy a previous deployment's commit
257
+ curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
258
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/deployments" \
259
+ --data "environment=production&sha=$PREVIOUS_SHA&ref=main&tag=false&status=created"
260
+ ```
261
+
262
+ Alternatively, revert the MR that introduced the change and let the pipeline re-run.
263
+
264
+ ## Pipeline and job states
265
+
266
+ ### Pipeline states
267
+
268
+ | State | Meaning | Action |
269
+ |-------|---------|--------|
270
+ | `created` | Pipeline created, not yet started | Wait |
271
+ | `waiting_for_resource` | Waiting for runner | Check runner availability |
272
+ | `preparing` | Job is being prepared | Wait |
273
+ | `pending` | Waiting for runner to pick up | Check runner tags/availability |
274
+ | `running` | Pipeline is executing | Monitor |
275
+ | `success` | All jobs passed | None — healthy |
276
+ | `failed` | One or more jobs failed | Check failed job logs |
277
+ | `canceled` | Pipeline was canceled | Re-run if needed |
278
+ | `skipped` | Pipeline was skipped by rules | Check rules configuration |
279
+ | `manual` | Pipeline waiting for manual action | Trigger manual job or cancel |
280
+ | `scheduled` | Waiting for scheduled time | Wait |
281
+
282
+ ### Job states
283
+
284
+ | State | Meaning | Action |
285
+ |-------|---------|--------|
286
+ | `created` | Job created | Wait |
287
+ | `pending` | Waiting for runner | Check runner tags |
288
+ | `running` | Job executing | Monitor logs |
289
+ | `success` | Job passed | None |
290
+ | `failed` | Job failed | Read trace log |
291
+ | `canceled` | Job canceled | Re-run if needed |
292
+ | `skipped` | Job skipped by rules/needs | Check rules |
293
+ | `manual` | Waiting for manual trigger | Play or skip |
294
+ | `allowed_failure` | Failed but allowed | Review — may indicate flaky test |
295
+
296
+ ## Monitoring pipelines
297
+
298
+ ### Check pipeline status
299
+
300
+ ```bash
301
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
302
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
303
+ ```
304
+
305
+ ### List recent pipelines for a branch
306
+
307
+ ```bash
308
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
309
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?ref=main&per_page=5"
310
+ ```
311
+
312
+ ### Get jobs in a pipeline
313
+
314
+ ```bash
315
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
316
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
317
+ ```
318
+
319
+ ### Stream job logs
320
+
321
+ ```bash
322
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
323
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
324
+ ```
325
+
326
+ ### Download job artifacts
327
+
328
+ ```bash
329
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
330
+ --output artifacts.zip \
331
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/artifacts"
332
+ ```
333
+
334
+ ## Merge request pipeline workflow
335
+
336
+ ### How MR pipelines differ
337
+
338
+ 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.
339
+
340
+ ### Common rules patterns
341
+
342
+ ```yaml
343
+ # Run only on MR pipelines
344
+ rules:
345
+ - if: $CI_MERGE_REQUEST_IID
346
+
347
+ # Run only on the default branch
348
+ rules:
349
+ - if: $CI_COMMIT_BRANCH == "main"
350
+
351
+ # Run on MRs and the default branch (but not both at once)
352
+ rules:
353
+ - if: $CI_MERGE_REQUEST_IID
354
+ - if: $CI_COMMIT_BRANCH == "main"
355
+ ```
356
+
357
+ ### Merged results pipelines
358
+
359
+ 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.
360
+
361
+ ### Merge trains
362
+
363
+ 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.
364
+
365
+ ## Troubleshooting decision tree
366
+
367
+ ### Step 1: Check pipeline status
368
+
369
+ ```bash
370
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
371
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID" | jq '.status'
372
+ ```
373
+
374
+ ### Step 2: Branch on status
375
+
376
+ - **`running` / `pending` / `created`** -> Wait. Do not take action while the pipeline is in progress.
377
+ - **`failed`** -> Read the failed job logs (Step 3).
378
+ - **`success`** -> Pipeline is healthy. If behavior is wrong, check job scripts and configuration.
379
+ - **`canceled`** -> Re-run if needed: `curl --request POST ... /pipelines/$PIPELINE_ID/retry`
380
+ - **`skipped`** -> All jobs were filtered out by `rules:`. Check rule conditions.
381
+
382
+ ### Step 3: Read failed job logs
383
+
384
+ ```bash
385
+ # Get failed jobs
386
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
387
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs?scope=failed" | jq '.[].id'
388
+
389
+ # Read the trace for a failed job
390
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
391
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
392
+ ```
393
+
394
+ ### Step 4: Diagnose by error pattern
395
+
396
+ | Error pattern | Likely cause | Fix |
397
+ |---------------|-------------|-----|
398
+ | "no matching runner" | No runner with matching tags | Check runner tags, register a runner |
399
+ | "image pull failed" | Docker image not found or auth failed | Check image name, registry credentials |
400
+ | "script exit code 1" | Script command failed | Read job log for the failing command |
401
+ | "artifact upload failed" | Artifact path doesn't exist or too large | Check `artifacts.paths`, size limits |
402
+ | "cache not found" | Cache key mismatch or first run | Expected on first run; check `cache.key` |
403
+ | "yaml invalid" | Syntax error in generated YAML | Run `chant lint src/` and CI Lint API |
404
+ | "pipeline filtered out" | All jobs filtered by rules | Check `rules:` conditions |
405
+ | "job timed out" | Job exceeded timeout | Increase `timeout:` or optimize job |
406
+ | "stuck or pending" | No available runner | Check runner status, tags, executor capacity |
407
+ | "environment does not exist" | `on_stop` references non-existent job | Check `on_stop` job name matches expanded name |
408
+ | "needs job not found" | `needs:` references non-existent job | Check job names, stage ordering |
409
+
410
+ ## Variable management
411
+
412
+ ### Variable types and precedence
413
+
414
+ Variables are resolved in this order (highest priority first):
415
+ 1. Job-level `variables:`
416
+ 2. Project CI/CD variables (Settings -> CI/CD -> Variables)
417
+ 3. Group CI/CD variables
418
+ 4. Instance CI/CD variables
419
+
420
+ ### Protected and masked variables
421
+
422
+ - **Protected**: only available in pipelines on protected branches/tags
423
+ - **Masked**: hidden in job logs (value must meet masking requirements)
424
+
425
+ ### Managing variables via API
426
+
427
+ ```bash
428
+ # List project variables
429
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
430
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/variables"
431
+
432
+ # Create a variable
433
+ curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
434
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/variables" \
435
+ --form "key=DEPLOY_TOKEN" --form "value=secret" --form "masked=true" --form "protected=true"
436
+
437
+ # Update a variable
438
+ curl --request PUT --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
439
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN" \
440
+ --form "value=new-secret"
441
+
442
+ # Delete a variable
443
+ curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
444
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/variables/DEPLOY_TOKEN"
445
+ ```
446
+
447
+ ## Quick reference
448
+
449
+ ### Pipeline info commands
450
+
451
+ ```bash
452
+ # List recent pipelines
453
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
454
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines?per_page=5"
455
+
456
+ # Get pipeline status
457
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
458
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID"
459
+
460
+ # Get jobs in a pipeline
461
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
462
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs"
463
+
464
+ # Read job log
465
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
466
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace"
467
+
468
+ # Retry a failed pipeline
469
+ curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
470
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/retry"
471
+
472
+ # Cancel a running pipeline
473
+ curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
474
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/pipelines/$PIPELINE_ID/cancel"
475
+ ```
476
+
477
+ ### Full build-to-deploy pipeline
478
+
479
+ ```bash
480
+ # 1. Lint
481
+ chant lint src/
482
+
483
+ # 2. Build
484
+ chant build src/ --output .gitlab-ci.yml
485
+
486
+ # 3. Validate via API
487
+ curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
488
+ --header "Content-Type: application/json" \
489
+ "https://gitlab.com/api/v4/projects/$PROJECT_ID/ci/lint" \
490
+ --data-binary @- <<EOF
491
+ {"content": $(cat .gitlab-ci.yml | jq -Rs .)}
492
+ EOF
493
+
494
+ # 4. Push to feature branch
495
+ git checkout -b feature/pipeline-update
496
+ git add .gitlab-ci.yml
497
+ git commit -m "Update pipeline"
498
+ git push -u origin feature/pipeline-update
499
+
500
+ # 5. Open MR, review pipeline diff, merge
501
+ # Pipeline runs automatically on the default branch after merge
502
+ ```