@intentius/chant-lexicon-github 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.
Files changed (78) hide show
  1. package/dist/integrity.json +14 -4
  2. package/dist/manifest.json +1 -1
  3. package/dist/rules/gha020.ts +40 -0
  4. package/dist/rules/gha021.ts +48 -0
  5. package/dist/rules/gha022.ts +50 -0
  6. package/dist/rules/gha023.ts +44 -0
  7. package/dist/rules/gha024.ts +42 -0
  8. package/dist/rules/gha025.ts +42 -0
  9. package/dist/rules/gha026.ts +40 -0
  10. package/dist/rules/gha027.ts +57 -0
  11. package/dist/rules/gha028.ts +37 -0
  12. package/dist/skills/{github-actions-patterns.md → chant-github-patterns.md} +2 -1
  13. package/dist/skills/chant-github-security.md +88 -0
  14. package/dist/skills/chant-github.md +569 -4
  15. package/package.json +20 -2
  16. package/src/codegen/docs.test.ts +19 -0
  17. package/src/codegen/generate.test.ts +12 -0
  18. package/src/codegen/package.test.ts +8 -0
  19. package/src/composites/cache.ts +8 -3
  20. package/src/composites/checkout.ts +8 -3
  21. package/src/composites/composites.test.ts +106 -0
  22. package/src/composites/deploy-environment.ts +11 -5
  23. package/src/composites/docker-build.ts +11 -5
  24. package/src/composites/download-artifact.ts +8 -3
  25. package/src/composites/go-ci.ts +17 -9
  26. package/src/composites/node-ci.ts +11 -5
  27. package/src/composites/node-pipeline.ts +14 -7
  28. package/src/composites/python-ci.ts +14 -7
  29. package/src/composites/setup-go.ts +8 -3
  30. package/src/composites/setup-node.ts +8 -3
  31. package/src/composites/setup-python.ts +8 -3
  32. package/src/composites/upload-artifact.ts +8 -3
  33. package/src/coverage.test.ts +23 -0
  34. package/src/import/roundtrip.test.ts +206 -0
  35. package/src/lint/post-synth/gha006.test.ts +56 -0
  36. package/src/lint/post-synth/gha009.test.ts +56 -0
  37. package/src/lint/post-synth/gha011.test.ts +61 -0
  38. package/src/lint/post-synth/gha017.test.ts +51 -0
  39. package/src/lint/post-synth/gha018.test.ts +66 -0
  40. package/src/lint/post-synth/gha019.test.ts +66 -0
  41. package/src/lint/post-synth/gha020.test.ts +66 -0
  42. package/src/lint/post-synth/gha020.ts +40 -0
  43. package/src/lint/post-synth/gha021.test.ts +67 -0
  44. package/src/lint/post-synth/gha021.ts +48 -0
  45. package/src/lint/post-synth/gha022.test.ts +45 -0
  46. package/src/lint/post-synth/gha022.ts +50 -0
  47. package/src/lint/post-synth/gha023.test.ts +52 -0
  48. package/src/lint/post-synth/gha023.ts +44 -0
  49. package/src/lint/post-synth/gha024.test.ts +67 -0
  50. package/src/lint/post-synth/gha024.ts +42 -0
  51. package/src/lint/post-synth/gha025.test.ts +65 -0
  52. package/src/lint/post-synth/gha025.ts +42 -0
  53. package/src/lint/post-synth/gha026.test.ts +65 -0
  54. package/src/lint/post-synth/gha026.ts +40 -0
  55. package/src/lint/post-synth/gha027.test.ts +48 -0
  56. package/src/lint/post-synth/gha027.ts +57 -0
  57. package/src/lint/post-synth/gha028.test.ts +48 -0
  58. package/src/lint/post-synth/gha028.ts +37 -0
  59. package/src/lint/rules/deprecated-action-version.test.ts +26 -0
  60. package/src/lint/rules/detect-secrets.test.ts +25 -0
  61. package/src/lint/rules/extract-inline-structs.test.ts +25 -0
  62. package/src/lint/rules/file-job-limit.test.ts +28 -0
  63. package/src/lint/rules/job-timeout.test.ts +31 -0
  64. package/src/lint/rules/missing-recommended-inputs.test.ts +26 -0
  65. package/src/lint/rules/no-hardcoded-secrets.test.ts +31 -0
  66. package/src/lint/rules/no-raw-expressions.test.ts +31 -0
  67. package/src/lint/rules/suggest-cache.test.ts +25 -0
  68. package/src/lint/rules/use-condition-builders.test.ts +31 -0
  69. package/src/lint/rules/use-matrix-builder.test.ts +25 -0
  70. package/src/lint/rules/use-typed-actions.test.ts +39 -0
  71. package/src/lint/rules/validate-concurrency.test.ts +31 -0
  72. package/src/plugin.test.ts +1 -1
  73. package/src/plugin.ts +70 -145
  74. package/src/skills/{github-actions-patterns.md → chant-github-patterns.md} +2 -1
  75. package/src/skills/chant-github-security.md +88 -0
  76. package/src/skills/chant-github.md +594 -0
  77. package/src/validate.ts +14 -1
  78. package/src/variables.test.ts +48 -0
@@ -0,0 +1,594 @@
1
+ ---
2
+ skill: chant-github
3
+ description: Build, validate, and deploy GitHub Actions workflows from a chant project
4
+ user-invocable: true
5
+ ---
6
+
7
+ # GitHub Actions Operational Playbook
8
+
9
+ ## How chant and GitHub Actions relate
10
+
11
+ chant is a **synthesis compiler** — it compiles TypeScript source files into `.github/workflows/*.yml` (YAML). `chant build` does not call GitHub 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 + gh CLI** for: push, pull requests, workflow monitoring, run logs, deployments, and all GitHub operations
15
+
16
+ The source of truth for workflow configuration is the TypeScript in `src/`. The generated `.github/workflows/*.yml` files are intermediate artifacts — never edit them by hand.
17
+
18
+ ## Scaffolding a new project
19
+
20
+ ### Initialize with a template
21
+
22
+ ```bash
23
+ chant init --lexicon github # default: Workflow + Job skeleton
24
+ chant init --lexicon github --template node-ci # NodeCI composite
25
+ chant init --lexicon github --template docker-build # DockerBuild composite
26
+ ```
27
+
28
+ This creates `src/` with chant workflow definitions. It does NOT create application files — you bring your own app code.
29
+
30
+ ### Available init templates
31
+
32
+ | Template | What it generates | Best for |
33
+ |----------|-------------------|----------|
34
+ | *(default)* | `pipeline.ts` with Workflow, Job, Checkout, SetupNode | Custom workflows from scratch |
35
+ | `node-ci` | `NodeCI` composite with checkout, setup-node, install, build, test | Node.js apps |
36
+ | `docker-build` | Workflow + Job with checkout, docker build step | Containerized apps |
37
+
38
+ ### Project layout after init
39
+
40
+ ```
41
+ src/
42
+ pipeline.ts # chant source — your workflow definition
43
+ .github/
44
+ workflows/
45
+ ci.yml # generated by chant build (do not edit)
46
+ package.json # your app code
47
+ ```
48
+
49
+ ## Building and validating
50
+
51
+ ### Build the workflow
52
+
53
+ ```bash
54
+ chant build src/ --output .github/workflows/ci.yml
55
+ ```
56
+
57
+ Options:
58
+ - `--format yaml` — emit YAML (default for GitHub)
59
+ - `--watch` — rebuild on source changes
60
+ - `--output <path>` — write to a specific file
61
+
62
+ ### Lint the source
63
+
64
+ ```bash
65
+ chant lint src/
66
+ ```
67
+
68
+ Options:
69
+ - `--fix` — auto-fix violations where possible
70
+ - `--format sarif` — SARIF output for CI integration
71
+ - `--watch` — re-lint on changes
72
+
73
+ ### Validate with actionlint
74
+
75
+ After `chant build`, run `actionlint` to catch GitHub-specific YAML issues that chant's lint rules do not cover:
76
+
77
+ ```bash
78
+ actionlint .github/workflows/ci.yml
79
+ ```
80
+
81
+ Install: `brew install actionlint` (macOS) or `go install github.com/rhysd/actionlint/cmd/actionlint@latest`.
82
+
83
+ ### What each step catches
84
+
85
+ | Step | Catches | When to run |
86
+ |------|---------|-------------|
87
+ | `chant lint` | Use typed composites (GHA001), use Expression helpers (GHA002), hardcoded tokens (GHA003), inline matrix extraction (GHA004), deep nesting (GHA005), raw expressions (GHA008), missing version inputs (GHA010), deprecated action versions (GHA012), missing timeout (GHA014), suggest caching (GHA015), concurrency without group (GHA016), potential secrets in source (GHA020) | Every edit |
88
+ | `chant build` | Post-synth checks: duplicate workflow names (GHA006), too many jobs per file (GHA007), empty matrix dimensions (GHA009), invalid needs references (GHA011), missing permissions (GHA017), checkout with pull_request_target (GHA018), circular needs chains (GHA019), checkout without pinned SHA (GHA021), job without timeout (GHA022), deprecated set-output (GHA023), deploy without concurrency (GHA024), pull_request_target without restrictions (GHA025), secrets without environment protection (GHA026), cleanup steps without `if: always()` (GHA027), workflow with no triggers (GHA028) | Before push |
89
+ | `actionlint` | GitHub-specific YAML schema errors, unknown action inputs, shell syntax, expression type errors, runner label validation | Before merge |
90
+
91
+ Always run all three before deploying. `chant lint` catches TypeScript-level issues, `chant build` catches structural problems in the synthesized YAML, and `actionlint` catches GitHub-specific schema violations.
92
+
93
+ ## Deploying to GitHub
94
+
95
+ ### What goes in the GitHub repo
96
+
97
+ The GitHub repo needs TWO things:
98
+ 1. **`.github/workflows/*.yml`** — generated by `chant build`
99
+ 2. **Your application files** — whatever the workflow scripts reference
100
+
101
+ chant only generates the YAML. Application files (`package.json`, `Dockerfile`, source code, tests) are your responsibility.
102
+
103
+ ### Typical project structure in the GitHub repo
104
+
105
+ **Node.js project:**
106
+ ```
107
+ .github/workflows/ci.yml # generated by chant
108
+ package.json # your app's package.json with build/test scripts
109
+ package-lock.json # required if using npm ci (NodeCI default)
110
+ src/ # your app code
111
+ test/ # your tests
112
+ ```
113
+
114
+ **Python project:**
115
+ ```
116
+ .github/workflows/ci.yml
117
+ requirements.txt # must include pytest, pytest-cov if using PythonCI defaults
118
+ app.py
119
+ test_app.py
120
+ ```
121
+
122
+ **Go project:**
123
+ ```
124
+ .github/workflows/ci.yml
125
+ go.mod
126
+ go.sum
127
+ main.go
128
+ main_test.go
129
+ ```
130
+
131
+ **Docker project:**
132
+ ```
133
+ .github/workflows/ci.yml
134
+ Dockerfile
135
+ src/ # your app source
136
+ ```
137
+
138
+ ### Important: npm ci vs npm install
139
+
140
+ The `NodeCI` composite defaults to `npm ci`, which requires a `package-lock.json` in the repo. If your repo does not have a lockfile, override with:
141
+
142
+ ```typescript
143
+ NodeCI({
144
+ installCommand: "npm install", // use instead of npm ci
145
+ ...
146
+ })
147
+ ```
148
+
149
+ Or generate a lockfile: `npm install && git add package-lock.json`.
150
+
151
+ ### Step-by-step: push to GitHub
152
+
153
+ ```bash
154
+ # 1. Build the YAML from chant source
155
+ chant build src/ --output .github/workflows/ci.yml
156
+
157
+ # 2. Initialize git (if needed) and commit everything
158
+ git init -b main
159
+ git add .github/workflows/ci.yml package.json src/ test/ # add your app files
160
+ git commit -m "Initial workflow"
161
+
162
+ # 3. Push to GitHub
163
+ git remote add origin git@github.com:YOUR_ORG/YOUR_REPO.git
164
+ git push -u origin main
165
+ ```
166
+
167
+ The workflow triggers automatically on push (if configured with a push trigger). Do NOT commit the chant `src/` directory, `node_modules/`, or `.chant/` to the application repo — those are local development files.
168
+
169
+ ## Diffing and change preview
170
+
171
+ ### Local diff
172
+
173
+ Compare generated workflow YAML against the version on the remote branch:
174
+
175
+ ```bash
176
+ # Build the proposed config
177
+ chant build src/ --output .github/workflows/ci.yml
178
+
179
+ # Diff against the remote version
180
+ git diff origin/main -- .github/workflows/ci.yml
181
+ ```
182
+
183
+ ### Pull request preview
184
+
185
+ Push to a branch and open a pull request. Reviewers can see the exact YAML diff in the PR files tab. This is the safest way to preview workflow changes for production:
186
+
187
+ ```bash
188
+ git checkout -b feature/workflow-update
189
+ chant build src/ --output .github/workflows/ci.yml
190
+ git add .github/workflows/ci.yml
191
+ git commit -m "Update workflow"
192
+ git push -u origin feature/workflow-update
193
+ gh pr create --title "Update CI workflow" --body "Workflow changes generated by chant"
194
+ ```
195
+
196
+ ### Safe preview checklist
197
+
198
+ Before merging workflow changes, verify:
199
+ 1. All jobs have valid `runs-on:` labels
200
+ 2. `needs:` references point to existing job names
201
+ 3. `on:` triggers match the intended branches/events
202
+ 4. Environment names match those configured in repo settings
203
+ 5. Docker images referenced in jobs are accessible from GitHub runners
204
+ 6. Secrets referenced in `${{ secrets.* }}` exist in repository or environment settings
205
+ 7. Permissions are scoped to least privilege
206
+
207
+ ## Composites reference
208
+
209
+ Composites are high-level building blocks that emit pre-wired Workflow + Job combinations.
210
+
211
+ | Composite | What it produces | Key props |
212
+ |-----------|-----------------|-----------|
213
+ | `NodeCI` | Workflow + job: checkout, setup-node, install, build, test | `nodeVersion`, `packageManager`, `buildScript`, `testScript`, `installCommand` |
214
+ | `NodePipeline` | Workflow + separate build/test jobs with artifact handoff | `nodeVersion`, `packageManager`, `buildScript`, `testScript`, `buildArtifactPaths`, `artifactName` |
215
+ | `BunPipeline` | NodePipeline preset with `packageManager: "bun"` | Same as NodePipeline |
216
+ | `PnpmPipeline` | NodePipeline preset with `packageManager: "pnpm"` | Same as NodePipeline |
217
+ | `YarnPipeline` | NodePipeline preset with `packageManager: "yarn"` | Same as NodePipeline |
218
+ | `PythonCI` | Workflow + test job + optional lint job (ruff) | `pythonVersion`, `testCommand`, `lintCommand`, `requirementsFile`, `usePoetry` |
219
+ | `GoCI` | Workflow + build/test/lint jobs | `goVersion`, `testCommand`, `buildCommand`, `lintCommand` |
220
+ | `DockerBuild` | Workflow + job: checkout, registry login, buildx, metadata, build-push | `tag`, `dockerfile`, `registry`, `imageName`, `platforms`, `push` |
221
+ | `DeployEnvironment` | Deploy job + cleanup job with concurrency control | `name`, `deployScript`, `cleanupScript`, `url`, `concurrencyGroup` |
222
+
223
+ ### Utility composites
224
+
225
+ | Composite | Purpose | Key props |
226
+ |-----------|---------|-----------|
227
+ | `Checkout` | `actions/checkout@v4` step | `fetchDepth`, `ref`, `token` |
228
+ | `SetupNode` | `actions/setup-node@v4` step | `nodeVersion`, `cache` |
229
+ | `SetupGo` | `actions/setup-go@v5` step | `goVersion` |
230
+ | `SetupPython` | `actions/setup-python@v5` step | `pythonVersion`, `cache` |
231
+ | `CacheAction` | `actions/cache` step | `path`, `key`, `restoreKeys` |
232
+ | `UploadArtifact` | `actions/upload-artifact` step | `name`, `path`, `retentionDays` |
233
+ | `DownloadArtifact` | `actions/download-artifact` step | `name`, `path` |
234
+
235
+ ### Composite usage example
236
+
237
+ ```typescript
238
+ import { NodeCI } from "@intentius/chant-lexicon-github";
239
+
240
+ export const app = NodeCI({
241
+ nodeVersion: "22",
242
+ installCommand: "npm ci",
243
+ buildScript: "build",
244
+ testScript: "test",
245
+ });
246
+ ```
247
+
248
+ ### Customizing composites with defaults
249
+
250
+ Every composite accepts a `defaults` prop to override job or workflow properties:
251
+
252
+ ```typescript
253
+ NodeCI({
254
+ nodeVersion: "22",
255
+ defaults: {
256
+ job: { "runs-on": "ubuntu-24.04", "timeout-minutes": 15 },
257
+ workflow: { name: "My Custom CI" },
258
+ },
259
+ })
260
+ ```
261
+
262
+ ## Workflow run monitoring
263
+
264
+ Use the `gh` CLI for all monitoring. Do not use raw API calls.
265
+
266
+ ### List recent workflow runs
267
+
268
+ ```bash
269
+ # All runs for the repo
270
+ gh run list --limit 10
271
+
272
+ # Runs for a specific workflow
273
+ gh run list --workflow ci.yml --limit 5
274
+
275
+ # Runs for a specific branch
276
+ gh run list --branch main --limit 5
277
+
278
+ # Filter by status
279
+ gh run list --status failure --limit 5
280
+ ```
281
+
282
+ ### View a specific run
283
+
284
+ ```bash
285
+ # Summary view
286
+ gh run view <run-id>
287
+
288
+ # Show job details
289
+ gh run view <run-id> --json jobs --jq '.jobs[] | {name, status, conclusion}'
290
+ ```
291
+
292
+ ### Watch a run in progress
293
+
294
+ ```bash
295
+ gh run watch <run-id>
296
+ ```
297
+
298
+ This streams status updates until the run completes. Useful after pushing a workflow change.
299
+
300
+ ### Read job logs
301
+
302
+ ```bash
303
+ # View logs for a specific run
304
+ gh run view <run-id> --log
305
+
306
+ # View logs for a failed run (filtered to failures)
307
+ gh run view <run-id> --log-failed
308
+
309
+ # View logs for a specific job within a run
310
+ gh run view <run-id> --job <job-id> --log
311
+ ```
312
+
313
+ ### Download artifacts from a run
314
+
315
+ ```bash
316
+ # Download all artifacts
317
+ gh run download <run-id>
318
+
319
+ # Download a specific artifact
320
+ gh run download <run-id> --name build-output
321
+ ```
322
+
323
+ ### Re-run a failed workflow
324
+
325
+ ```bash
326
+ # Re-run all jobs
327
+ gh run rerun <run-id>
328
+
329
+ # Re-run only failed jobs
330
+ gh run rerun <run-id> --failed
331
+ ```
332
+
333
+ ### Cancel a running workflow
334
+
335
+ ```bash
336
+ gh run cancel <run-id>
337
+ ```
338
+
339
+ ## Environment protection and deployment
340
+
341
+ ### Configuring environments
342
+
343
+ Environments are configured in the GitHub repo settings (Settings > Environments), not in YAML. The workflow references them by name:
344
+
345
+ ```typescript
346
+ DeployEnvironment({
347
+ name: "production",
348
+ url: "https://example.com",
349
+ deployScript: "./deploy.sh",
350
+ })
351
+ ```
352
+
353
+ ### Environment protection rules
354
+
355
+ Configure these in the GitHub UI (or via `gh api`):
356
+
357
+ - **Required reviewers** — one or more approvers must approve before deploy
358
+ - **Wait timer** — delay in minutes before deployment proceeds
359
+ - **Branch restrictions** — limit which branches can deploy to this environment
360
+ - **Deployment branch policy** — restrict to specific branches or tags
361
+
362
+ ### Deployment flow: dev -> staging -> production
363
+
364
+ ```typescript
365
+ import { DeployEnvironment } from "@intentius/chant-lexicon-github";
366
+
367
+ export const deployDev = DeployEnvironment({
368
+ name: "dev",
369
+ deployScript: "./deploy.sh dev",
370
+ url: "https://dev.example.com",
371
+ });
372
+
373
+ export const deployStaging = DeployEnvironment({
374
+ name: "staging",
375
+ deployScript: "./deploy.sh staging",
376
+ url: "https://staging.example.com",
377
+ });
378
+
379
+ export const deployProd = DeployEnvironment({
380
+ name: "production",
381
+ deployScript: "./deploy.sh production",
382
+ url: "https://example.com",
383
+ });
384
+ ```
385
+
386
+ Add `needs:` to chain the deploy jobs in sequence, gated by environment protection rules configured in the GitHub UI.
387
+
388
+ ### Checking deployment status
389
+
390
+ ```bash
391
+ # List deployments for an environment
392
+ gh api repos/{owner}/{repo}/deployments --jq '.[0:5] | .[] | {id, environment, sha: .sha[0:8], created_at}'
393
+
394
+ # Check deployment status
395
+ gh api repos/{owner}/{repo}/deployments/<deploy-id>/statuses --jq '.[0] | {state, description}'
396
+ ```
397
+
398
+ ## Deploying workflow changes
399
+
400
+ ### Safe path (production workflows)
401
+
402
+ 1. Build: `chant build src/ --output .github/workflows/ci.yml`
403
+ 2. Lint: `chant lint src/`
404
+ 3. Validate: `actionlint .github/workflows/ci.yml`
405
+ 4. Push to feature branch: `git push -u origin feature/workflow-update`
406
+ 5. Open PR: `gh pr create --title "Update CI workflow"`
407
+ 6. Review YAML diff in PR, wait for status checks
408
+ 7. Merge — workflow runs on the default branch
409
+
410
+ ### Fast path (dev/iteration)
411
+
412
+ ```bash
413
+ chant build src/ --output .github/workflows/ci.yml
414
+ git add .github/workflows/ci.yml && git commit -m "Update workflow" && git push
415
+ ```
416
+
417
+ ### Which path to use
418
+
419
+ | Scenario | Path |
420
+ |----------|------|
421
+ | Production workflow with deploy jobs | Safe path (PR review) |
422
+ | Workflow with environment/secrets changes | Safe path (PR review) |
423
+ | Dev/test workflow iteration | Fast path (direct push) |
424
+ | Workflow with protected environments | Safe path + branch protection |
425
+
426
+ ## Rollback patterns
427
+
428
+ ### Revert the workflow change
429
+
430
+ The simplest rollback: revert the commit that introduced the bad workflow.
431
+
432
+ ```bash
433
+ # Find the commit that broke things
434
+ gh run list --status failure --limit 5
435
+ git log --oneline -10 -- .github/workflows/
436
+
437
+ # Revert it
438
+ git revert <bad-commit-sha>
439
+ git push
440
+ ```
441
+
442
+ ### Re-run a previous successful workflow
443
+
444
+ If the workflow YAML was fine but the run failed due to a transient issue:
445
+
446
+ ```bash
447
+ # Find the last successful run
448
+ gh run list --status success --workflow ci.yml --limit 1
449
+
450
+ # Re-run it
451
+ gh run rerun <run-id>
452
+ ```
453
+
454
+ ### Rebuild from a previous chant source version
455
+
456
+ If you version-control the chant `src/` alongside the generated YAML:
457
+
458
+ ```bash
459
+ # Check out the previous working version of chant source
460
+ git checkout <good-commit-sha> -- src/
461
+
462
+ # Rebuild
463
+ chant build src/ --output .github/workflows/ci.yml
464
+
465
+ # Commit the rollback
466
+ git add .github/workflows/ci.yml
467
+ git commit -m "Rollback workflow to known-good version"
468
+ git push
469
+ ```
470
+
471
+ ### Emergency: disable a workflow
472
+
473
+ ```bash
474
+ gh workflow disable ci.yml
475
+ ```
476
+
477
+ Re-enable later: `gh workflow enable ci.yml`.
478
+
479
+ ## Troubleshooting decision tree
480
+
481
+ ### Step 1: Check the run status
482
+
483
+ ```bash
484
+ gh run list --limit 5
485
+ ```
486
+
487
+ ### Step 2: Branch on status
488
+
489
+ - **`in_progress` / `queued`** — Wait. Do not take action while the run is in progress.
490
+ - **`failure`** — Read the failed job logs (Step 3).
491
+ - **`success`** — Run is healthy. If behavior is wrong, check job scripts and workflow triggers.
492
+ - **`cancelled`** — Re-run if needed: `gh run rerun <run-id>`
493
+ - **No runs appear** — Workflow was not triggered. Check `on:` triggers and branch filters.
494
+
495
+ ### Step 3: Read failed job logs
496
+
497
+ ```bash
498
+ # View failure output
499
+ gh run view <run-id> --log-failed
500
+ ```
501
+
502
+ ### Step 4: Diagnose by error pattern
503
+
504
+ | Error pattern | Likely cause | Fix |
505
+ |---------------|-------------|-----|
506
+ | `no matching runner` | No runner with matching `runs-on` label | Check runner label, use `ubuntu-latest` for hosted |
507
+ | `image not found` / `pull access denied` | Docker image not found or auth failed | Check image name, registry credentials |
508
+ | `Process completed with exit code 1` | Script command failed | Read full job log for the failing command |
509
+ | `Error: ENOENT` / `file not found` | Missing application file | Ensure app files are committed to the repo |
510
+ | `npm ERR! could not determine executable` | Missing `package-lock.json` for `npm ci` | Run `npm install` and commit lockfile, or use `installCommand: "npm install"` |
511
+ | `Error: Input required and not supplied` | Action input missing | Check `with:` values for the action step |
512
+ | `Node.js 16 actions are deprecated` | Action uses Node 16 runtime | Upgrade to latest action version (GHA012) |
513
+ | `Unrecognized named-value: 'env'` | Expression context error | Use correct context (`github`, `secrets`, `env`, `steps`) |
514
+ | `This request was denied` / `403` | Insufficient GITHUB_TOKEN permissions | Add explicit `permissions:` block to workflow or job |
515
+ | `annotations` / `reviewdog` errors | Status check misconfiguration | Check `pull-requests: write` permission |
516
+ | `all jobs filtered` | All jobs skipped by `if:` conditions | Check `on:` triggers and job-level `if:` expressions |
517
+ | `job needs non-existent job` | `needs:` points to wrong job name | Check job names in the synthesized YAML (GHA011) |
518
+ | `circular dependency detected` | Jobs form a needs cycle | Break the cycle in chant source (GHA019) |
519
+ | `yaml: line N: ...` | YAML syntax error | Run `actionlint` and `chant lint` to find the issue |
520
+ | `::set-output` deprecation warning | Using old output mechanism | Switch to `$GITHUB_OUTPUT` (GHA023) |
521
+ | `timeout` / `The job running on runner ... has exceeded the maximum execution time` | Job exceeded timeout | Add or increase `timeout-minutes` |
522
+
523
+ ## Quick reference commands
524
+
525
+ ### Full build-to-deploy pipeline
526
+
527
+ ```bash
528
+ # 1. Lint
529
+ chant lint src/
530
+
531
+ # 2. Build
532
+ chant build src/ --output .github/workflows/ci.yml
533
+
534
+ # 3. Validate with actionlint
535
+ actionlint .github/workflows/ci.yml
536
+
537
+ # 4. Push to feature branch
538
+ git checkout -b feature/workflow-update
539
+ git add .github/workflows/ci.yml
540
+ git commit -m "Update workflow"
541
+ git push -u origin feature/workflow-update
542
+
543
+ # 5. Open PR
544
+ gh pr create --title "Update CI workflow" --body "Workflow changes generated by chant"
545
+
546
+ # 6. Watch the PR checks
547
+ gh pr checks
548
+
549
+ # 7. Merge when green
550
+ gh pr merge --squash
551
+ ```
552
+
553
+ ### Monitoring commands
554
+
555
+ ```bash
556
+ gh run list --limit 10 # recent runs
557
+ gh run list --workflow ci.yml --limit 5 # runs for a specific workflow
558
+ gh run view <run-id> # run summary
559
+ gh run view <run-id> --log-failed # failed job logs
560
+ gh run watch <run-id> # stream live status
561
+ gh run rerun <run-id> --failed # re-run failed jobs
562
+ gh run cancel <run-id> # cancel a run
563
+ gh run download <run-id> --name <artifact> # download artifact
564
+ ```
565
+
566
+ ### Workflow management commands
567
+
568
+ ```bash
569
+ gh workflow list # list all workflows
570
+ gh workflow view ci.yml # view workflow details
571
+ gh workflow run ci.yml # manually trigger (needs workflow_dispatch)
572
+ gh workflow disable ci.yml # disable a workflow
573
+ gh workflow enable ci.yml # re-enable a workflow
574
+ ```
575
+
576
+ ### Environment and deployment commands
577
+
578
+ ```bash
579
+ gh api repos/{owner}/{repo}/environments --jq '.environments[].name' # list environments
580
+ gh api repos/{owner}/{repo}/deployments --jq '.[0:5] | .[].environment' # recent deployments
581
+ ```
582
+
583
+ ### chant commands summary
584
+
585
+ ```bash
586
+ chant init --lexicon github # scaffold a new project
587
+ chant init --lexicon github --template node-ci # scaffold with NodeCI composite
588
+ chant build src/ --output .github/workflows/ci.yml # synthesize YAML
589
+ chant build src/ --output .github/workflows/ci.yml --watch # watch mode
590
+ chant lint src/ # lint chant source
591
+ chant lint src/ --fix # auto-fix lint violations
592
+ chant lint src/ --format sarif # SARIF output for CI
593
+ chant diff src/ # diff current vs last build
594
+ ```
package/src/validate.ts CHANGED
@@ -2,7 +2,8 @@
2
2
  * Validate generated lexicon-github artifacts.
3
3
  */
4
4
 
5
- import { dirname } from "path";
5
+ import { dirname, join } from "path";
6
+ import { existsSync } from "fs";
6
7
  import { fileURLToPath } from "url";
7
8
  import { validateLexiconArtifacts, type ValidateResult } from "@intentius/chant/codegen/validate";
8
9
 
@@ -23,6 +24,18 @@ const REQUIRED_NAMES = [
23
24
  */
24
25
  export async function validate(opts?: { basePath?: string }): Promise<ValidateResult> {
25
26
  const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
27
+ const generatedDir = join(basePath, "src", "generated");
28
+
29
+ if (!existsSync(generatedDir)) {
30
+ return {
31
+ success: false,
32
+ checks: [{
33
+ name: "generated-dir",
34
+ ok: false,
35
+ error: 'src/generated/ not found — run "chant dev generate" first',
36
+ }],
37
+ };
38
+ }
26
39
 
27
40
  return validateLexiconArtifacts({
28
41
  lexiconJsonFilename: "lexicon-github.json",
@@ -0,0 +1,48 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { GitHub, Runner } from "./variables";
3
+
4
+ describe("GitHub context variables", () => {
5
+ test("GitHub.Ref resolves to github.ref expression", () => {
6
+ expect(GitHub.Ref.toString()).toBe("${{ github.ref }}");
7
+ });
8
+
9
+ test("GitHub.Sha resolves to github.sha expression", () => {
10
+ expect(GitHub.Sha.toString()).toBe("${{ github.sha }}");
11
+ });
12
+
13
+ test("GitHub.Actor resolves to github.actor expression", () => {
14
+ expect(GitHub.Actor.toString()).toBe("${{ github.actor }}");
15
+ });
16
+
17
+ test("GitHub has all expected properties", () => {
18
+ const keys = Object.keys(GitHub);
19
+ expect(keys).toContain("Ref");
20
+ expect(keys).toContain("Sha");
21
+ expect(keys).toContain("Actor");
22
+ expect(keys).toContain("Repository");
23
+ expect(keys).toContain("EventName");
24
+ expect(keys).toContain("Token");
25
+ expect(keys).toContain("Workspace");
26
+ expect(keys.length).toBe(25);
27
+ });
28
+ });
29
+
30
+ describe("Runner context variables", () => {
31
+ test("Runner.Os resolves to runner.os expression", () => {
32
+ expect(Runner.Os.toString()).toBe("${{ runner.os }}");
33
+ });
34
+
35
+ test("Runner.Arch resolves to runner.arch expression", () => {
36
+ expect(Runner.Arch.toString()).toBe("${{ runner.arch }}");
37
+ });
38
+
39
+ test("Runner has all expected properties", () => {
40
+ const keys = Object.keys(Runner);
41
+ expect(keys).toContain("Os");
42
+ expect(keys).toContain("Arch");
43
+ expect(keys).toContain("Name");
44
+ expect(keys).toContain("Temp");
45
+ expect(keys).toContain("ToolCache");
46
+ expect(keys.length).toBe(5);
47
+ });
48
+ });