@intentius/chant-lexicon-github 0.44.14 → 0.45.0

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 +1 @@
1
- {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8EH;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2zC9E"}
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/codegen/docs.ts"],"names":[],"mappings":"AAAA;;GAEG;AA8EH;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B9E"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "4128be61f2c88b9e4f768cce38ebcc9cf20fa289de404300758e369c8ac367a3",
4
+ "manifest.json": "e30d7f473b8a2e2f5043a3cb45b1904b7a3863a8a114d534e0fe87147352f632",
5
5
  "meta.json": "798c5f5f37b0174756d2451299acfdc95c014f11d06d32b9153bbdfa9f3a580c",
6
6
  "types/index.d.ts": "39c0791f0e58025bcef82e0f7a92240508f899fc2771d5dd51b2369b4d330f13",
7
7
  "rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
@@ -67,5 +67,5 @@
67
67
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
68
68
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
69
69
  },
70
- "composite": "9b2a9d396094838146c39a7e9fda48ab7777292413121dcdd2f736f4e8c02bd6"
70
+ "composite": "77a265cdec5d2163e1ce49b12ef260aaa7e23b3d8989fcb6de23a90ce9612227"
71
71
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github",
3
- "version": "0.44.14",
3
+ "version": "0.45.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitHub",
6
6
  "intrinsics": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.44.14",
3
+ "version": "0.45.0",
4
4
  "description": "GitHub Actions lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -61,7 +61,7 @@
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@intentius/chant": "^0.44.14",
64
+ "@intentius/chant": "^0.45.0",
65
65
  "typescript": "^5.9.3"
66
66
  }
67
67
  }
@@ -101,1315 +101,7 @@ export async function generateDocs(opts?: { verbose?: boolean }): Promise<void>
101
101
  content: `The GitLab lexicon ships a typed-compiler migration tool that translates \`.github/workflows/*.yml\` into \`.gitlab-ci.yml\` (or chant TypeScript) with provenance, 33 curated marketplace-action mappings, and optional composite recognition. See [GitLab → Migration from GitHub Actions](/chant/lexicons/gitlab/migration/) or the [\`chant migrate\` CLI reference](/chant/cli/migrate/).`,
102
102
  },
103
103
  ],
104
- extraPages: [
105
- {
106
- slug: "getting-started",
107
- title: "Getting Started",
108
- description: "Step-by-step guide to building your first GitHub Actions workflow with chant",
109
- content: `## 1. Install
110
-
111
- \`\`\`bash
112
- mkdir my-project && cd my-project
113
- npm init -y
114
- npm install --save-dev @intentius/chant @intentius/chant-lexicon-github typescript
115
- \`\`\`
116
-
117
- ## 2. Create your workflow
118
-
119
- Create \`src/ci.ts\`:
120
-
121
- {{file:docs-snippets/src/quickstart.ts}}
122
-
123
- ## 3. Build
124
-
125
- \`\`\`bash
126
- chant build src/ --output .github/workflows/ci.yml
127
- \`\`\`
128
-
129
- This produces \`.github/workflows/ci.yml\`:
130
-
131
- \`\`\`yaml
132
- name: CI
133
- on:
134
- push:
135
- branches:
136
- - main
137
- pull_request:
138
- branches:
139
- - main
140
- permissions:
141
- contents: read
142
- jobs:
143
- build:
144
- runs-on: ubuntu-latest
145
- timeout-minutes: 15
146
- steps:
147
- - name: Checkout
148
- uses: actions/checkout@v4
149
- - name: Setup Node.js
150
- uses: actions/setup-node@v4
151
- with:
152
- node-version: '22'
153
- cache: npm
154
- - name: Install
155
- run: npm ci
156
- - name: Build
157
- run: npm run build
158
- - name: Test
159
- run: npm test
160
- \`\`\`
161
-
162
- ## 4. Lint
163
-
164
- \`\`\`bash
165
- chant lint src/
166
- \`\`\`
167
-
168
- Lint checks for common issues — missing timeouts, hardcoded secrets, raw expression strings, and more. See the [Lint Rules](/chant/lexicons/github/lint-rules/) page for the full list.
169
-
170
- ## 5. Deploy
171
-
172
- Commit the generated YAML and push:
173
-
174
- \`\`\`bash
175
- git add .github/workflows/ci.yml
176
- git commit -m "Add CI workflow"
177
- git push
178
- \`\`\`
179
-
180
- GitHub automatically picks up any \`.github/workflows/*.yml\` files and runs them on the configured triggers.
181
-
182
- ## Next steps
183
-
184
- - [Workflow Concepts](/chant/lexicons/github/workflow-concepts/) — resource types, triggers, permissions
185
- - [Expressions](/chant/lexicons/github/expressions/) — typed expression system and condition helpers
186
- - [Composites](/chant/lexicons/github/composites/) — pre-built action wrappers (Checkout, SetupNode, etc.)
187
- - [Lint Rules](/chant/lexicons/github/lint-rules/) — 13 lint rules and 45 post-synth checks`,
188
- },
189
- {
190
- slug: "workflow-concepts",
191
- title: "Workflow Concepts",
192
- description: "Resource types, triggers, jobs, steps, permissions, and strategy in the GitHub Actions lexicon",
193
- content: `## Resource types
194
-
195
- The lexicon provides 2 resource types and several property types:
196
-
197
- ### Resources
198
-
199
- | Type | Description |
200
- |------|-------------|
201
- | \`Workflow\` | Top-level workflow configuration — name, triggers, permissions, concurrency |
202
- | \`Job\` | A job within a workflow — runs-on, steps, strategy, needs, outputs |
203
-
204
- ### Property types
205
-
206
- | Type | Used in | Description |
207
- |------|---------|-------------|
208
- | \`Step\` | Job | A single step — run command or action usage |
209
- | \`Strategy\` | Job | Matrix strategy for parallel job execution |
210
- | \`Permissions\` | Workflow, Job | GITHUB_TOKEN permission scopes |
211
- | \`Concurrency\` | Workflow, Job | Concurrency group and cancel-in-progress settings |
212
- | \`PushTrigger\` | Workflow (on) | Push event trigger with branch/tag/path filters |
213
- | \`PullRequestTrigger\` | Workflow (on) | Pull request event trigger with filters |
214
- | \`ScheduleTrigger\` | Workflow (on) | Cron-based schedule trigger |
215
- | \`WorkflowDispatchTrigger\` | Workflow (on) | Manual dispatch with typed inputs |
216
- | \`Environment\` | Job | Deployment environment with protection rules |
217
- | \`Output\` | Job | Job output values for downstream jobs |
218
-
219
- ## Triggers
220
-
221
- Triggers define when a workflow runs. Pass them in the \`on:\` field:
222
-
223
- \`\`\`typescript
224
- import { Workflow } from "@intentius/chant-lexicon-github";
225
-
226
- export const workflow = new Workflow({
227
- name: "CI",
228
- on: {
229
- push: { branches: ["main", "release/*"] },
230
- pull_request: {
231
- branches: ["main"],
232
- types: ["opened", "synchronize"],
233
- },
234
- schedule: [{ cron: "0 0 * * 1" }], // Weekly on Monday
235
- workflow_dispatch: { // Manual trigger
236
- inputs: {
237
- environment: {
238
- description: "Deploy target",
239
- required: true,
240
- type: "choice",
241
- options: ["staging", "production"],
242
- },
243
- },
244
- },
245
- },
246
- });
247
- \`\`\`
248
-
249
- ## Jobs and steps
250
-
251
- Each exported \`Job\` becomes a job entry under \`jobs:\`. Steps run sequentially within a job:
252
-
253
- \`\`\`typescript
254
- import { Job, Step, Checkout, SetupNode } from "@intentius/chant-lexicon-github";
255
-
256
- export const test = new Job({
257
- "runs-on": "ubuntu-latest",
258
- timeoutMinutes: 10,
259
- steps: [
260
- Checkout({}).step,
261
- SetupNode({ nodeVersion: "22", cache: "npm" }).step,
262
- new Step({ name: "Install", run: "npm ci" }),
263
- new Step({ name: "Test", run: "npm test" }),
264
- ],
265
- });
266
- \`\`\`
267
-
268
- Key job properties:
269
- - \`runs-on\` — **required**. Runner label (\`ubuntu-latest\`, \`macos-latest\`, \`windows-latest\`).
270
- - \`steps\` — **required**. Array of \`Step\` objects.
271
- - \`timeoutMinutes\` — maximum job duration (recommended, flagged by GHA014 if missing).
272
- - \`needs\` — job dependencies for execution ordering.
273
- - \`if\` — conditional execution using Expressions.
274
- - \`strategy\` — matrix builds for parallel execution.
275
-
276
- ## Matrix strategy
277
-
278
- Run a job across multiple configurations:
279
-
280
- \`\`\`typescript
281
- import { Job, Step, Checkout, SetupNode } from "@intentius/chant-lexicon-github";
282
-
283
- export const test = new Job({
284
- "runs-on": "ubuntu-latest",
285
- strategy: {
286
- matrix: {
287
- "node-version": ["18", "20", "22"],
288
- os: ["ubuntu-latest", "macos-latest"],
289
- },
290
- "fail-fast": false,
291
- },
292
- steps: [
293
- Checkout({}).step,
294
- SetupNode({ nodeVersion: "\${{ matrix.node-version }}", cache: "npm" }).step,
295
- new Step({ name: "Test", run: "npm test" }),
296
- ],
297
- });
298
- \`\`\`
299
-
300
- ## Permissions
301
-
302
- Control GITHUB_TOKEN permissions at the workflow or job level:
303
-
304
- \`\`\`typescript
305
- import { Workflow, Job, Step } from "@intentius/chant-lexicon-github";
306
-
307
- // Workflow-level (applies to all jobs)
308
- export const workflow = new Workflow({
309
- name: "Release",
310
- on: { push: { tags: ["v*"] } },
311
- permissions: {
312
- contents: "write",
313
- packages: "write",
314
- "id-token": "write",
315
- },
316
- });
317
-
318
- // Job-level (overrides workflow permissions for this job)
319
- export const publish = new Job({
320
- "runs-on": "ubuntu-latest",
321
- permissions: { contents: "read", packages: "write" },
322
- steps: [
323
- new Step({ name: "Publish", run: "npm publish" }),
324
- ],
325
- });
326
- \`\`\`
327
-
328
- Available permission scopes: \`actions\`, \`checks\`, \`contents\`, \`deployments\`, \`id-token\`, \`issues\`, \`packages\`, \`pages\`, \`pull-requests\`, \`repository-projects\`, \`security-events\`, \`statuses\`. Values: \`"read"\`, \`"write"\`, \`"none"\`.
329
-
330
- ## Concurrency
331
-
332
- Prevent concurrent runs of the same workflow or job:
333
-
334
- \`\`\`typescript
335
- import { Workflow } from "@intentius/chant-lexicon-github";
336
-
337
- export const workflow = new Workflow({
338
- name: "Deploy",
339
- on: { push: { branches: ["main"] } },
340
- concurrency: {
341
- group: "deploy-\${{ github.ref }}",
342
- "cancel-in-progress": true,
343
- },
344
- });
345
- \`\`\`
346
-
347
- ## Job dependencies
348
-
349
- Use \`needs\` to order jobs and pass outputs between them:
350
-
351
- \`\`\`typescript
352
- import { Job, Step } from "@intentius/chant-lexicon-github";
353
-
354
- export const build = new Job({
355
- "runs-on": "ubuntu-latest",
356
- outputs: { version: "\${{ steps.version.outputs.value }}" },
357
- steps: [
358
- new Step({
359
- id: "version",
360
- name: "Get version",
361
- run: 'echo "value=$(node -p \\"require(\'./package.json\').version\\")" >> $GITHUB_OUTPUT',
362
- }),
363
- ],
364
- });
365
-
366
- export const deploy = new Job({
367
- "runs-on": "ubuntu-latest",
368
- needs: ["build"],
369
- steps: [
370
- new Step({
371
- name: "Deploy",
372
- run: "echo Deploying version \${{ needs.build.outputs.version }}",
373
- }),
374
- ],
375
- });
376
- \`\`\``,
377
- },
378
- {
379
- slug: "expressions",
380
- title: "Expressions",
381
- description: "Typed expression system, condition helpers, and utility functions for GitHub Actions",
382
- content: `The Expression system provides type-safe access to GitHub Actions \`\${{ }}\` expressions. Instead of writing raw strings, use the typed helpers for better IDE support and lint coverage.
383
-
384
- {{file:docs-snippets/src/expressions-usage.ts}}
385
-
386
- ## Expression class
387
-
388
- The \`Expression\` class wraps a raw expression string and provides operator methods:
389
-
390
- | Method | Example | Result |
391
- |--------|---------|--------|
392
- | \`.and(other)\` | \`github.ref.eq("refs/heads/main").and(isPR)\` | \`\${{ github.ref == 'refs/heads/main' && ... }}\` |
393
- | \`.or(other)\` | \`isMain.or(isDev)\` | \`\${{ ... \\|\\| ... }}\` |
394
- | \`.not()\` | \`isPR.not()\` | \`\${{ !(...) }}\` |
395
- | \`.eq(value)\` | \`github.ref.eq("refs/heads/main")\` | \`\${{ github.ref == 'refs/heads/main' }}\` |
396
- | \`.ne(value)\` | \`github.eventName.ne("schedule")\` | \`\${{ github.event_name != 'schedule' }}\` |
397
-
398
- ## Context accessors
399
-
400
- The \`github\` and \`runner\` objects provide typed access to context properties:
401
-
402
- \`\`\`typescript
403
- import { github, runner } from "@intentius/chant-lexicon-github";
404
-
405
- github.ref // \${{ github.ref }}
406
- github.sha // \${{ github.sha }}
407
- github.actor // \${{ github.actor }}
408
- runner.os // \${{ runner.os }}
409
- runner.arch // \${{ runner.arch }}
410
- \`\`\`
411
-
412
- See [Variables](/chant/lexicons/github/variables/) for the full reference table.
413
-
414
- ## Dynamic context accessors
415
-
416
- Access dynamic context values — secrets, matrix, step outputs, job outputs, inputs, vars, env:
417
-
418
- \`\`\`typescript
419
- import { secrets, matrix, steps, needs, inputs, vars, env } from "@intentius/chant-lexicon-github";
420
-
421
- secrets("NPM_TOKEN") // \${{ secrets.NPM_TOKEN }}
422
- matrix("node-version") // \${{ matrix.node-version }}
423
- steps("build").outputs("path") // \${{ steps.build.outputs.path }}
424
- needs("build").outputs("version") // \${{ needs.build.outputs.version }}
425
- inputs("environment") // \${{ inputs.environment }}
426
- vars("API_URL") // \${{ vars.API_URL }}
427
- env("NODE_ENV") // \${{ env.NODE_ENV }}
428
- \`\`\`
429
-
430
- ## Condition helpers
431
-
432
- Status check functions for job and step \`if:\` fields:
433
-
434
- | Function | Expression | Description |
435
- |----------|-----------|-------------|
436
- | \`always()\` | \`\${{ always() }}\` | Always run, regardless of status |
437
- | \`failure()\` | \`\${{ failure() }}\` | Run only if a previous step failed |
438
- | \`success()\` | \`\${{ success() }}\` | Run only if all previous steps succeeded (default) |
439
- | \`cancelled()\` | \`\${{ cancelled() }}\` | Run only if the workflow was cancelled |
440
-
441
- Use them in \`if:\` conditions:
442
-
443
- \`\`\`typescript
444
- import { Job, Step, failure, always } from "@intentius/chant-lexicon-github";
445
-
446
- export const test = new Job({
447
- "runs-on": "ubuntu-latest",
448
- steps: [
449
- new Step({ name: "Test", run: "npm test" }),
450
- new Step({
451
- name: "Upload coverage",
452
- if: always(),
453
- run: "npx codecov",
454
- }),
455
- new Step({
456
- name: "Notify failure",
457
- if: failure(),
458
- run: "curl -X POST $SLACK_WEBHOOK",
459
- }),
460
- ],
461
- });
462
- \`\`\`
463
-
464
- ## Function helpers
465
-
466
- Utility functions that produce expression strings:
467
-
468
- | Function | Example | Expression |
469
- |----------|---------|-----------|
470
- | \`contains(haystack, needle)\` | \`contains(github.event, "bug")\` | \`\${{ contains(github.event, 'bug') }}\` |
471
- | \`startsWith(value, prefix)\` | \`startsWith(github.ref, "refs/tags/")\` | \`\${{ startsWith(github.ref, 'refs/tags/') }}\` |
472
- | \`toJSON(value)\` | \`toJSON(github.event)\` | \`\${{ toJSON(github.event) }}\` |
473
- | \`fromJSON(json)\` | \`fromJSON(steps("meta").outputs("matrix"))\` | \`\${{ fromJSON(steps.meta.outputs.matrix) }}\` |
474
- | \`format(template, ...args)\` | \`format("v{0}.{1}", major, minor)\` | \`\${{ format('v{0}.{1}', ...) }}\` |
475
-
476
- ## Convenience helpers
477
-
478
- Shorthand functions for common checks:
479
-
480
- \`\`\`typescript
481
- import { branch, tag } from "@intentius/chant-lexicon-github";
482
-
483
- branch("main") // github.ref == 'refs/heads/main'
484
- tag("v1") // startsWith(github.ref, 'refs/tags/v1')
485
- \`\`\`
486
-
487
- These are useful in job \`if:\` conditions:
488
-
489
- \`\`\`typescript
490
- import { Job, Step, branch } from "@intentius/chant-lexicon-github";
491
-
492
- export const deploy = new Job({
493
- "runs-on": "ubuntu-latest",
494
- if: branch("main"),
495
- steps: [
496
- new Step({ name: "Deploy", run: "npm run deploy" }),
497
- ],
498
- });
499
- \`\`\``,
500
- },
501
- {
502
- slug: "variables",
503
- title: "Variables",
504
- description: "GitHub and Runner context variable references for GitHub Actions workflows",
505
- content: `The \`GitHub\` and \`Runner\` objects provide type-safe access to GitHub Actions context variables. These expand to \`\${{ github.* }}\` and \`\${{ runner.* }}\` expressions in the generated YAML.
506
-
507
- {{file:docs-snippets/src/variables-usage.ts}}
508
-
509
- ## GitHub context
510
-
511
- | Property | Expression | Description |
512
- |----------|-----------|-------------|
513
- | \`GitHub.Ref\` | \`\${{ github.ref }}\` | Full ref (e.g. \`refs/heads/main\`, \`refs/tags/v1.0\`) |
514
- | \`GitHub.RefName\` | \`\${{ github.ref_name }}\` | Short ref name (e.g. \`main\`, \`v1.0\`) |
515
- | \`GitHub.RefType\` | \`\${{ github.ref_type }}\` | Ref type: \`branch\` or \`tag\` |
516
- | \`GitHub.Sha\` | \`\${{ github.sha }}\` | Full commit SHA |
517
- | \`GitHub.Actor\` | \`\${{ github.actor }}\` | Username that triggered the workflow |
518
- | \`GitHub.TriggeringActor\` | \`\${{ github.triggering_actor }}\` | Username that triggered the workflow run |
519
- | \`GitHub.Repository\` | \`\${{ github.repository }}\` | Owner/repo (e.g. \`octocat/hello-world\`) |
520
- | \`GitHub.RepositoryOwner\` | \`\${{ github.repository_owner }}\` | Repository owner (e.g. \`octocat\`) |
521
- | \`GitHub.EventName\` | \`\${{ github.event_name }}\` | Triggering event name (\`push\`, \`pull_request\`, etc.) |
522
- | \`GitHub.Event\` | \`\${{ github.event }}\` | Full event payload object |
523
- | \`GitHub.RunId\` | \`\${{ github.run_id }}\` | Unique run ID |
524
- | \`GitHub.RunNumber\` | \`\${{ github.run_number }}\` | Run number for this workflow |
525
- | \`GitHub.RunAttempt\` | \`\${{ github.run_attempt }}\` | Attempt number for this run |
526
- | \`GitHub.Workflow\` | \`\${{ github.workflow }}\` | Workflow name |
527
- | \`GitHub.WorkflowRef\` | \`\${{ github.workflow_ref }}\` | Workflow ref path |
528
- | \`GitHub.Workspace\` | \`\${{ github.workspace }}\` | Default working directory on the runner |
529
- | \`GitHub.Token\` | \`\${{ github.token }}\` | Automatically-generated GITHUB_TOKEN |
530
- | \`GitHub.Job\` | \`\${{ github.job }}\` | Current job ID |
531
- | \`GitHub.HeadRef\` | \`\${{ github.head_ref }}\` | PR head branch (pull_request events only) |
532
- | \`GitHub.BaseRef\` | \`\${{ github.base_ref }}\` | PR base branch (pull_request events only) |
533
- | \`GitHub.ServerUrl\` | \`\${{ github.server_url }}\` | GitHub server URL |
534
- | \`GitHub.ApiUrl\` | \`\${{ github.api_url }}\` | GitHub API URL |
535
- | \`GitHub.GraphqlUrl\` | \`\${{ github.graphql_url }}\` | GitHub GraphQL API URL |
536
- | \`GitHub.Action\` | \`\${{ github.action }}\` | Action name or step ID |
537
- | \`GitHub.ActionPath\` | \`\${{ github.action_path }}\` | Path where the action is located |
538
-
539
- ## Runner context
540
-
541
- | Property | Expression | Description |
542
- |----------|-----------|-------------|
543
- | \`Runner.Os\` | \`\${{ runner.os }}\` | Runner operating system (\`Linux\`, \`Windows\`, \`macOS\`) |
544
- | \`Runner.Arch\` | \`\${{ runner.arch }}\` | Runner architecture (\`X86\`, \`X64\`, \`ARM\`, \`ARM64\`) |
545
- | \`Runner.Name\` | \`\${{ runner.name }}\` | Runner name |
546
- | \`Runner.Temp\` | \`\${{ runner.temp }}\` | Path to a temporary directory on the runner |
547
- | \`Runner.ToolCache\` | \`\${{ runner.tool_cache }}\` | Path to the tool cache directory |
548
-
549
- ## Common patterns
550
-
551
- ### Branch checks
552
-
553
- \`\`\`typescript
554
- import { Job, Step, GitHub } from "@intentius/chant-lexicon-github";
555
- import { branch } from "@intentius/chant-lexicon-github";
556
-
557
- // Deploy only on main
558
- export const deploy = new Job({
559
- "runs-on": "ubuntu-latest",
560
- if: branch("main"),
561
- steps: [
562
- new Step({ name: "Deploy", run: "npm run deploy" }),
563
- ],
564
- });
565
- \`\`\`
566
-
567
- ### Event filtering
568
-
569
- \`\`\`typescript
570
- import { Job, Step, GitHub } from "@intentius/chant-lexicon-github";
571
- import { github } from "@intentius/chant-lexicon-github";
572
-
573
- // Skip scheduled runs
574
- export const test = new Job({
575
- "runs-on": "ubuntu-latest",
576
- if: github.eventName.ne("schedule"),
577
- steps: [
578
- new Step({ name: "Test", run: "npm test" }),
579
- ],
580
- });
581
- \`\`\`
582
-
583
- ### Runner-specific logic
584
-
585
- \`\`\`typescript
586
- import { Job, Step, runner } from "@intentius/chant-lexicon-github";
587
-
588
- export const build = new Job({
589
- "runs-on": "ubuntu-latest",
590
- steps: [
591
- new Step({
592
- name: "Platform-specific setup",
593
- if: runner.os.eq("Linux"),
594
- run: "sudo apt-get update && sudo apt-get install -y jq",
595
- }),
596
- new Step({ name: "Build", run: "npm run build" }),
597
- ],
598
- });
599
- \`\`\``,
600
- },
601
- {
602
- slug: "composites",
603
- title: "Composites",
604
- description: "Pre-built composites — action wrappers, CI pipelines, Docker builds, and deploy environments",
605
- content: `Composites are pre-built abstractions that produce typed GitHub Actions resources. They range from single-action wrappers to full multi-job workflow pipelines.
606
-
607
- {{file:docs-snippets/src/composites-usage.ts}}
608
-
609
- **A note on folding.** These two composite shapes fold differently (see [Folded vs Run](/chant/concepts/typescript-as-data/#folded-vs-run)). A single-action wrapper like \`Checkout({...})\` is normally embedded inline as \`checkout.step\` inside a \`Job\`'s \`steps\` array — a call nested as a value inside another resource's own properties, which is outside the fold subset, so a file using it falls back to the normal run path. A multi-job pipeline composite like \`NodePipeline({...})\` is normally consumed the other way: bound to a local \`const\`, then each member re-exported separately (\`export const nodeWorkflow = node.workflow\`) — a top-level export whose value is a call result's member access, which is exactly the composite-call shape folding resolves (chant #1023).
610
-
611
- ## Checkout
612
-
613
- Wraps \`actions/checkout@v4\`. Clones the repository.
614
-
615
- \`\`\`typescript
616
- import { Checkout } from "@intentius/chant-lexicon-github";
617
-
618
- Checkout({}).step // Default checkout
619
- Checkout({ fetchDepth: 0 }).step // Full history
620
- Checkout({ ref: "develop" }).step // Specific branch
621
- Checkout({ submodules: "recursive" }).step // With submodules
622
- \`\`\`
623
-
624
- **Props:** \`ref?\`, \`repository?\`, \`fetchDepth?\`, \`token?\`, \`submodules?\`, \`sshKey?\`
625
-
626
- ## SetupNode
627
-
628
- Wraps \`actions/setup-node@v4\`. Installs Node.js with optional dependency caching.
629
-
630
- \`\`\`typescript
631
- import { SetupNode } from "@intentius/chant-lexicon-github";
632
-
633
- SetupNode({ nodeVersion: "22" }).step // Node 22
634
- SetupNode({ nodeVersion: "22", cache: "npm" }).step // With npm cache
635
- SetupNode({ nodeVersion: "20", cache: "pnpm" }).step // pnpm cache
636
- \`\`\`
637
-
638
- **Props:** \`nodeVersion?\`, \`cache?\` (\`"npm"\` | \`"pnpm"\` | \`"yarn"\`), \`registryUrl?\`
639
-
640
- ## SetupGo
641
-
642
- Wraps \`actions/setup-go@v5\`. Installs Go.
643
-
644
- \`\`\`typescript
645
- import { SetupGo } from "@intentius/chant-lexicon-github";
646
-
647
- SetupGo({ goVersion: "1.22" }).step
648
- SetupGo({ goVersion: "stable" }).step
649
- \`\`\`
650
-
651
- **Props:** \`goVersion?\`, \`cache?\`
652
-
653
- ## SetupPython
654
-
655
- Wraps \`actions/setup-python@v5\`. Installs Python.
656
-
657
- \`\`\`typescript
658
- import { SetupPython } from "@intentius/chant-lexicon-github";
659
-
660
- SetupPython({ pythonVersion: "3.12" }).step
661
- SetupPython({ pythonVersion: "3.12", cache: "pip" }).step
662
- \`\`\`
663
-
664
- **Props:** \`pythonVersion?\`, \`cache?\` (\`"pip"\` | \`"pipenv"\` | \`"poetry"\`), \`architecture?\`
665
-
666
- ## CacheAction
667
-
668
- Wraps \`actions/cache@v4\`. Caches files between workflow runs.
669
-
670
- \`\`\`typescript
671
- import { CacheAction } from "@intentius/chant-lexicon-github";
672
-
673
- CacheAction({
674
- path: "~/.npm",
675
- key: "npm-\${{ runner.os }}-\${{ hashFiles('**/package-lock.json') }}",
676
- restoreKeys: ["npm-\${{ runner.os }}-"],
677
- }).step
678
- \`\`\`
679
-
680
- **Props:** \`path\`, \`key\`, \`restoreKeys?\`
681
-
682
- ## UploadArtifact
683
-
684
- Wraps \`actions/upload-artifact@v4\`. Uploads files as workflow artifacts.
685
-
686
- \`\`\`typescript
687
- import { UploadArtifact } from "@intentius/chant-lexicon-github";
688
-
689
- UploadArtifact({
690
- name: "build-output",
691
- path: "dist/",
692
- retentionDays: 7,
693
- }).step
694
- \`\`\`
695
-
696
- **Props:** \`name\`, \`path\`, \`retentionDays?\`, \`ifNoFilesFound?\`
697
-
698
- ## DownloadArtifact
699
-
700
- Wraps \`actions/download-artifact@v4\`. Downloads previously uploaded artifacts.
701
-
702
- \`\`\`typescript
703
- import { DownloadArtifact } from "@intentius/chant-lexicon-github";
704
-
705
- DownloadArtifact({
706
- name: "build-output",
707
- path: "dist/",
708
- }).step
709
- \`\`\`
710
-
711
- **Props:** \`name\`, \`path?\`
712
-
713
- ## NodeCI
714
-
715
- A batteries-included composite that produces a full CI workflow and job. Generates a \`Workflow\` (push + PR on main) and a \`Job\` with checkout, setup-node, install, build, and test steps.
716
-
717
- \`\`\`typescript
718
- import { NodeCI } from "@intentius/chant-lexicon-github";
719
-
720
- // Default: Node 22, npm, "build" + "test" scripts
721
- const { workflow, job } = NodeCI({});
722
-
723
- // Customized
724
- const { workflow: w, job: j } = NodeCI({
725
- nodeVersion: "20",
726
- packageManager: "pnpm",
727
- buildScript: "compile",
728
- testScript: "test:ci",
729
- });
730
- \`\`\`
731
-
732
- **Props:** \`nodeVersion?\`, \`packageManager?\` (\`"npm"\` | \`"pnpm"\` | \`"yarn"\` | \`"bun"\`), \`buildScript?\`, \`testScript?\`, \`installCommand?\`
733
-
734
- The returned \`workflow\` and \`job\` can be exported directly:
735
-
736
- \`\`\`typescript
737
- const ci = NodeCI({ nodeVersion: "22", packageManager: "npm" });
738
- export const workflow = ci.workflow;
739
- export const build = ci.job;
740
- \`\`\`
741
-
742
- ## NodePipeline
743
-
744
- A production-grade Node pipeline with separate build and test jobs connected by artifact passing. The build job uploads artifacts; the test job downloads them and runs tests with \`needs: ["build"]\`.
745
-
746
- \`\`\`typescript
747
- import { NodePipeline } from "@intentius/chant-lexicon-github";
748
-
749
- const { workflow, buildJob, testJob } = NodePipeline({
750
- nodeVersion: "22",
751
- packageManager: "pnpm",
752
- buildScript: "build",
753
- testScript: "test:ci",
754
- buildArtifactPaths: ["dist/", "lib/"],
755
- });
756
- \`\`\`
757
-
758
- **Props:** \`nodeVersion?\`, \`packageManager?\` (\`"npm"\` | \`"pnpm"\` | \`"yarn"\` | \`"bun"\`), \`buildScript?\`, \`testScript?\`, \`installCommand?\`, \`buildArtifactPaths?\`, \`artifactName?\`, \`artifactRetentionDays?\`, \`runsOn?\`
759
-
760
- ### Presets
761
-
762
- \`\`\`typescript
763
- import { BunPipeline, PnpmPipeline, YarnPipeline } from "@intentius/chant-lexicon-github";
764
-
765
- const bun = BunPipeline({}); // packageManager: "bun", uses oven-sh/setup-bun@v2
766
- const pnpm = PnpmPipeline({}); // packageManager: "pnpm"
767
- const yarn = YarnPipeline({}); // packageManager: "yarn"
768
- \`\`\`
769
-
770
- ## PythonCI
771
-
772
- Python CI with test and optional lint jobs. Supports pip and Poetry workflows.
773
-
774
- \`\`\`typescript
775
- import { PythonCI } from "@intentius/chant-lexicon-github";
776
-
777
- const { workflow, testJob, lintJob } = PythonCI({
778
- pythonVersion: "3.12",
779
- testCommand: "pytest --junitxml=report.xml --cov",
780
- lintCommand: "ruff check .",
781
- });
782
-
783
- // Omit lint job
784
- const { workflow: w, testJob: t } = PythonCI({ lintCommand: null });
785
-
786
- // Poetry mode
787
- const poetry = PythonCI({ usePoetry: true });
788
- \`\`\`
789
-
790
- **Props:** \`pythonVersion?\`, \`testCommand?\`, \`lintCommand?\` (null to omit), \`requirementsFile?\`, \`usePoetry?\`, \`runsOn?\`
791
-
792
- ## DockerBuild
793
-
794
- Docker build and push using official Docker actions (login, setup-buildx, metadata, build-push). Configured for GitHub Container Registry by default.
795
-
796
- \`\`\`typescript
797
- import { DockerBuild } from "@intentius/chant-lexicon-github";
798
-
799
- const { workflow, job } = DockerBuild({
800
- registry: "ghcr.io",
801
- imageName: "ghcr.io/my-org/my-app",
802
- dockerfile: "Dockerfile",
803
- platforms: ["linux/amd64", "linux/arm64"],
804
- });
805
- \`\`\`
806
-
807
- **Props:** \`tag?\`, \`dockerfile?\`, \`context?\`, \`registry?\`, \`imageName?\`, \`tagLatest?\`, \`buildArgs?\`, \`push?\`, \`platforms?\`, \`runsOn?\`
808
-
809
- ## DeployEnvironment
810
-
811
- Deploy and cleanup job pair using GitHub Environments with concurrency control.
812
-
813
- \`\`\`typescript
814
- import { DeployEnvironment } from "@intentius/chant-lexicon-github";
815
-
816
- const { deployJob, cleanupJob } = DeployEnvironment({
817
- name: "staging",
818
- deployScript: ["npm run build", "npm run deploy"],
819
- cleanupScript: "npm run teardown",
820
- url: "https://staging.example.com",
821
- });
822
- \`\`\`
823
-
824
- **Props:** \`name\` (required), \`deployScript\` (required), \`cleanupScript?\`, \`url?\`, \`concurrencyGroup?\`, \`cancelInProgress?\`, \`runsOn?\`
825
-
826
- ## GoCI
827
-
828
- Go CI with build, test, and optional lint jobs. Uses \`golangci-lint-action\` for linting.
829
-
830
- \`\`\`typescript
831
- import { GoCI } from "@intentius/chant-lexicon-github";
832
-
833
- const { workflow, buildJob, testJob, lintJob } = GoCI({
834
- goVersion: "1.22",
835
- buildCommand: "go build ./...",
836
- testCommand: "go test ./... -v -race",
837
- });
838
-
839
- // Without lint
840
- const noLint = GoCI({ lintCommand: null });
841
- \`\`\`
842
-
843
- **Props:** \`goVersion?\`, \`testCommand?\`, \`buildCommand?\`, \`lintCommand?\` (null to omit), \`runsOn?\`
844
-
845
- ## Dependabot
846
-
847
- Models the repository's dependency-update configuration (\`.github/dependabot.yml\`) as a chant resource, so it is emitted and lintable like a workflow. The composite ships **safe defaults**: a cooldown window on every ecosystem (so a version published moments ago — including a compromised one — is not adopted before anyone can react) and external code execution explicitly denied.
848
-
849
- \`\`\`typescript
850
- import { Dependabot } from "@intentius/chant-lexicon-github";
851
-
852
- export const dependabot = Dependabot({
853
- ecosystems: [
854
- { packageEcosystem: "npm", directory: "/" },
855
- { packageEcosystem: "github-actions", directory: "/" },
856
- ],
857
- // cooldownDays defaults to 7, openPullRequestsLimit to 5
858
- });
859
- \`\`\`
860
-
861
- **Props:** \`ecosystems\` (required — each \`{ packageEcosystem, directory?, interval? }\`), \`cooldownDays?\` (default 7), \`openPullRequestsLimit?\` (default 5)
862
-
863
- For full control, construct the \`DependabotConfig\` resource directly with raw \`updates:\` entries. Two post-synth checks validate the emitted config: **GHA057** (\`insecure-external-code-execution: allow\`) and **GHA058** (no cooldown). See [Lint Rules](/chant/lexicons/github/lint-rules/).`,
864
- },
865
- {
866
- slug: "lint-rules",
867
- title: "Lint Rules",
868
- description: "Built-in lint rules and post-synth checks for GitHub Actions workflows",
869
- content: `The GitHub Actions lexicon ships lint rules that run during \`chant lint\` and post-synth checks that validate the serialized YAML after \`chant build\`.
870
-
871
- ## Lint rules
872
-
873
- Lint rules analyze your TypeScript source code before build.
874
-
875
- ### GHA001 — Use typed action composites
876
-
877
- **Severity:** warning | **Category:** style
878
-
879
- Flags raw \`uses:\` strings when a typed composite wrapper is available (e.g. \`actions/checkout@v4\` → \`Checkout({})\`). Typed composites provide better IDE support and catch configuration errors at compile time.
880
-
881
- ### GHA002 — Use Expression helpers
882
-
883
- **Severity:** warning | **Category:** style
884
-
885
- Flags raw \`\${{ }}\` strings in \`if:\` conditions. Use the typed Expression helpers (\`github.ref.eq(...)\`, \`branch("main")\`, \`failure()\`) instead for type safety and lint coverage.
886
-
887
- ### GHA003 — No hardcoded secrets
888
-
889
- **Severity:** error | **Category:** security
890
-
891
- Flags hardcoded GitHub tokens, AWS keys, and other secret patterns in source code. Use \`secrets("...")\` expressions instead.
892
-
893
- {{file:docs-snippets/src/lint-gha003.ts}}
894
-
895
- ### GHA004 — Extract inline matrix
896
-
897
- **Severity:** info | **Category:** style
898
-
899
- Flags inline matrix objects and suggests extracting them to named constants for readability.
900
-
901
- ### GHA005 — Extract deeply nested objects
902
-
903
- **Severity:** info | **Category:** style
904
-
905
- Flags deeply nested inline objects and suggests extracting them to named constants.
906
-
907
- ### GHA007 — Too many jobs per file
908
-
909
- **Severity:** warning | **Category:** style
910
-
911
- Flags files with more than 10 job exports. Split large workflows into separate files for maintainability.
912
-
913
- ### GHA008 — Avoid raw expression strings
914
-
915
- **Severity:** info | **Category:** style
916
-
917
- Flags raw \`\${{ }}\` expression strings outside of \`if:\` fields. Use the typed expression helpers (\`github.*\`, \`secrets()\`, \`matrix()\`) for better type safety.
918
-
919
- ### GHA010 — Setup action missing version
920
-
921
- **Severity:** warning | **Category:** correctness
922
-
923
- Flags setup action composites (SetupNode, SetupGo, SetupPython) without a version input. Pinning the version ensures reproducible builds.
924
-
925
- ### GHA012 — Deprecated action version
926
-
927
- **Severity:** warning | **Category:** correctness
928
-
929
- Flags action \`uses:\` references that point to deprecated versions. Upgrade to the recommended version.
930
-
931
- ### GHA014 — Missing timeout
932
-
933
- **Severity:** warning | **Category:** correctness
934
-
935
- Flags jobs without \`timeoutMinutes\`. Jobs without a timeout default to 360 minutes (6 hours), which can waste runner minutes if stuck.
936
-
937
- ### GHA015 — Setup without cache
938
-
939
- **Severity:** warning | **Category:** performance
940
-
941
- Flags setup action composites (SetupNode, SetupGo, SetupPython) without a paired \`CacheAction\` or built-in \`cache\` option. Caching dependencies significantly speeds up builds.
942
-
943
- ### GHA016 — Concurrency missing group
944
-
945
- **Severity:** warning | **Category:** correctness
946
-
947
- Flags \`concurrency\` with \`cancel-in-progress: true\` but no \`group\` specified. Without a group, all runs of the workflow share a single concurrency slot.
948
-
949
- ### GHA020 — Potential secret detected
950
-
951
- **Severity:** error | **Category:** security
952
-
953
- Flags string literals that match common secret patterns (API keys, tokens, passwords). Use \`secrets()\` or environment variables instead.
954
-
955
- ## Post-synth checks
956
-
957
- Post-synth checks run against the serialized YAML after build. They catch issues only visible in the final output.
958
-
959
- ### GHA006 — Duplicate workflow names
960
-
961
- **Severity:** error
962
-
963
- Flags multiple workflows that share the same \`name:\` value. Duplicate names cause confusion in the GitHub Actions UI.
964
-
965
- ### GHA009 — Empty matrix dimension
966
-
967
- **Severity:** error
968
-
969
- Flags matrix strategy dimensions with an empty values array. An empty dimension causes the job to be skipped entirely.
970
-
971
- ### GHA011 — Invalid needs target
972
-
973
- **Severity:** error
974
-
975
- Flags jobs whose \`needs:\` entries reference a job not defined in the workflow. This causes a workflow validation error on GitHub.
976
-
977
- ### GHA013 — Missing job-level permissions for sensitive triggers
978
-
979
- **Severity:** warning
980
-
981
- Flags jobs without an explicit \`permissions:\` block when the workflow uses a sensitive trigger (\`pull_request_target\` or \`workflow_dispatch\`). Declaring job-level permissions keeps least-privilege scope on workflows that run with elevated context.
982
-
983
- ### GHA017 — Missing permissions block
984
-
985
- **Severity:** info
986
-
987
- Flags workflows without an explicit \`permissions:\` block. Omitting permissions uses the repository default (often overly broad). Following least-privilege by declaring explicit permissions is a security best practice.
988
-
989
- ### GHA018 — pull_request_target with checkout
990
-
991
- **Severity:** warning
992
-
993
- Flags workflows triggered by \`pull_request_target\` that include \`actions/checkout\`. This combination can be a security risk because the workflow runs with write permissions in the context of the base branch while checking out potentially untrusted PR code.
994
-
995
- ### GHA019 — Circular needs chain
996
-
997
- **Severity:** error
998
-
999
- Detects cycles in the \`needs:\` dependency graph. If job A needs B and B needs A (directly or transitively), GitHub rejects the workflow. Reports the full cycle chain in the diagnostic message.
1000
-
1001
- ### GHA021 — Checkout action not pinned to a SHA
1002
-
1003
- **Severity:** warning
1004
-
1005
- Flags \`actions/checkout\` referenced by a tag (e.g. \`@v4\`) instead of a pinned commit SHA. The narrower precursor to GHA029, kept because checkout is the most common unpinned action.
1006
-
1007
- ### GHA022 — Job without timeout-minutes
1008
-
1009
- **Severity:** info
1010
-
1011
- Flags jobs that omit \`timeout-minutes\`. A hung step otherwise runs to the runner's default cap, burning minutes — set an explicit ceiling.
1012
-
1013
- ### GHA023 — Deprecated set-output command
1014
-
1015
- **Severity:** warning
1016
-
1017
- Flags \`::set-output\` in \`run:\` steps. The workflow command is deprecated and disabled on current runners — write to \`$GITHUB_OUTPUT\` instead.
1018
-
1019
- ### GHA024 — Missing concurrency for deploy workflows
1020
-
1021
- **Severity:** info
1022
-
1023
- Flags deploy workflows without a \`concurrency:\` block. Without one, two pushes can deploy concurrently and race — add a concurrency group to serialize them.
1024
-
1025
- ### GHA025 — pull_request_target without restrictions
1026
-
1027
- **Severity:** warning
1028
-
1029
- Flags \`pull_request_target\` used without a \`types:\` filter. The trigger runs with repository secrets in the base-branch context, so it should be scoped to the specific PR events that need it.
1030
-
1031
- ### GHA026 — Secret used without environment protection
1032
-
1033
- **Severity:** info
1034
-
1035
- Flags workflows that reference \`secrets.\` in steps but declare no \`environment:\` on any job, so the secret skips the approval and scoping rules an environment gate provides.
1036
-
1037
- ### GHA027 — Cleanup step missing if: always()
1038
-
1039
- **Severity:** info
1040
-
1041
- Flags steps named "cleanup" / "teardown" / "clean up" that lack an \`if:\` condition. Cleanup should run even when a prior step fails — add \`if: always()\`.
1042
-
1043
- ### GHA028 — Workflow with no on: triggers
1044
-
1045
- **Severity:** error
1046
-
1047
- Flags a workflow file with no top-level \`on:\` key. Without a trigger the workflow can never run.
1048
-
1049
- ## Supply-chain security pass (GHA029–058)
1050
-
1051
- GHA029 onward are a CI/CD supply-chain security pass: pin & vet external references, enforce least-privilege token scopes, guard trust boundaries against untrusted input, contain secrets, reject unsound expressions, and keep artifacts/caches honest. They run statically on the emitted YAML — everything answerable without leaving the build.
1052
-
1053
- The checks that need a *moving external truth* — whether a pinned SHA still maps to a real upstream tag, whether a ref still exists, whether a new advisory now covers an action in use — can't be deterministic, so they live in the operational layer instead. Schedule the [\`WorkflowAuditOp\`](/chant/guide/ops/#audit-supply-chain-drift) (temporal lexicon) for that live, always-fresh half; it reads the same emitted workflow references and reports drift via \`report | issue | pull-request\`.
1054
-
1055
- ### GHA029 — Action or reusable workflow not pinned to a commit SHA
1056
-
1057
- **Severity:** warning
1058
-
1059
- Flags any \`uses:\` — step action or job-level reusable workflow — pinned to a mutable tag or branch instead of a full commit SHA. Tags can be repointed to malicious code after review, so every external reference should be pinned. \`actions/checkout\` is covered by the more specific GHA021; local (\`./\`) and \`docker://\` references are out of scope. Owners in the vendored trusted allowlist are exempt.
1060
-
1061
- ### GHA030 — Container image not pinned to a digest
1062
-
1063
- **Severity:** warning
1064
-
1065
- Flags job \`container:\` images, \`services:\` images, and \`docker://\` step references that are not pinned to an immutable \`@sha256:\` digest. A mutable tag can be repointed to a different image after review.
1066
-
1067
- ### GHA031 — Action resembles a well-known action
1068
-
1069
- **Severity:** warning
1070
-
1071
- Flags a \`uses:\` slug that is a near-miss (edit distance 1–2) of a popular action but not an exact match — a likely typo or impersonation under different ownership. Advisory; backed by a vendored reference list.
1072
-
1073
- ### GHA032 — Archived or compromised action
1074
-
1075
- **Severity:** warning
1076
-
1077
- Flags a \`uses:\` slug that a vendored snapshot marks as archived/abandoned or carrying a disclosed security issue, with remediation. Advisory and necessarily incomplete.
1078
-
1079
- ### GHA033 — Blanket write-all permissions
1080
-
1081
- **Severity:** warning
1082
-
1083
- Flags \`permissions: write-all\` at the workflow or job level. It grants the \`GITHUB_TOKEN\` every write scope regardless of need — replace it with the specific scopes the job uses.
1084
-
1085
- ### GHA034 — Write permissions granted workflow-wide
1086
-
1087
- **Severity:** warning
1088
-
1089
- Flags individual write scopes declared at the workflow level, which apply to every job even when only one needs them. Move each write scope onto the specific job that uses it. (The \`write-all\` preset is covered by GHA033.)
1090
-
1091
- ### GHA035 — Elevated scope on an untrusted-code trigger
1092
-
1093
- **Severity:** error
1094
-
1095
- Flags a workflow that grants the token write access while using a trigger that can run untrusted code (\`pull_request_target\`, \`workflow_run\`). An injected step would run with standing write credentials — drop the write scope or isolate the privileged work in a separate trusted workflow.
1096
-
1097
- ### GHA036 — Untrusted input in a run: command
1098
-
1099
- **Severity:** error
1100
-
1101
- Flags an attacker-controllable expression context (PR title, branch name, issue/comment body, commit message) interpolated directly into a \`run:\` script — a script-injection sink. Pass the value through an \`env:\` variable and reference it quoted instead.
1102
-
1103
- ### GHA037 — Untrusted input written to GITHUB_ENV / GITHUB_PATH
1104
-
1105
- **Severity:** error
1106
-
1107
- Flags a \`run:\` step that writes untrusted input into \`$GITHUB_ENV\` or \`$GITHUB_PATH\`, which set environment/PATH state for later steps and can escalate into takeover of a subsequent privileged step.
1108
-
1109
- ### GHA038 — workflow_run trigger checking out untrusted code
1110
-
1111
- **Severity:** warning
1112
-
1113
- Generalizes GHA018 to the \`workflow_run\` trigger, which runs with repo write scope and secrets. Checking out the head/artifact of the triggering run pulls untrusted code into that privileged context.
1114
-
1115
- ### GHA039 — Authorization gate on a spoofable identity
1116
-
1117
- **Severity:** warning
1118
-
1119
- Flags an \`if:\` condition that gates on a commit-author identity field (\`author.name\` / \`author.email\`). Those come from git metadata the committer sets freely and can be spoofed — gate on a verified signal (environment protection, CODEOWNERS, verified actor).
1120
-
1121
- ### GHA040 — Self-hosted runner on an untrusted-code trigger
1122
-
1123
- **Severity:** warning
1124
-
1125
- Flags a job on a self-hosted runner under a trigger a fork can reach (\`pull_request\`, \`pull_request_target\`, \`workflow_run\`). Self-hosted runners are non-ephemeral and shared, so untrusted code can persist and compromise later jobs.
1126
-
1127
- ### GHA041 — Blanket secrets: inherit into a reusable workflow
1128
-
1129
- **Severity:** warning
1130
-
1131
- Flags a reusable-workflow call passing \`secrets: inherit\`, which hands the called workflow every caller secret. Pass through only the specific secrets it needs.
1132
-
1133
- ### GHA042 — Entire secrets context passed
1134
-
1135
- **Severity:** warning
1136
-
1137
- Flags \`toJSON(secrets)\` passed into a step or reusable workflow, serializing every secret where one or two specific references would do.
1138
-
1139
- ### GHA043 — Secret consumed without an environment gate
1140
-
1141
- **Severity:** warning
1142
-
1143
- Extends GHA026: when a workflow gates some jobs with an \`environment:\`, flags the specific secret-using jobs that have none — the inconsistent-gating case where a job skips the approval/scoping applied elsewhere.
1144
-
1145
- ### GHA044 — Hardcoded registry/container credential
1146
-
1147
- **Severity:** error
1148
-
1149
- Flags a \`password:\` / \`token:\` / \`registry-password:\` set to a literal rather than a \`\${{ secrets.* }}\` reference. Move the credential into a secret.
1150
-
1151
- ### GHA045 — Secret interpolated into a run: command
1152
-
1153
- **Severity:** warning
1154
-
1155
- Flags \`\${{ secrets.* }}\` expanded directly into a \`run:\` script, where a transform can defeat log masking and the raw value is exposed to argument injection. Pass it through an \`env:\` variable and reference \`"$VAR"\` quoted.
1156
-
1157
- ### GHA046 — Logically unsound guard condition
1158
-
1159
- **Severity:** warning
1160
-
1161
- Flags an \`if:\` condition that reads like a gate but evaluates to a constant — \`true\`/\`false\` literals, an \`X == X\` tautology, or a collapse via \`|| true\` / \`&& false\`. A gate that constrains nothing is misleading.
1162
-
1163
- ### GHA047 — Ineffective contains() guard
1164
-
1165
- **Severity:** warning
1166
-
1167
- Flags \`contains('literal', <dynamic>)\` — a constant haystack with a dynamic needle. \`contains(search, item)\` tests whether \`item\` is in \`search\`, so reversed arguments make the result depend on a fixed string. Swap them.
1168
-
1169
- ### GHA048 — Obfuscated guard condition
1170
-
1171
- **Severity:** warning
1172
-
1173
- Flags an \`if:\` gate whose compared operand is built through \`format()\` / \`join()\` / \`fromJSON()\` indirection. Constructing the operand at evaluation time hides what the gate checks — compare against the value directly.
1174
-
1175
- ### GHA049 — Persisted checkout credentials reachable by an artifact
1176
-
1177
- **Severity:** warning
1178
-
1179
- Flags a job that checks out with persisted credentials (the default) and uploads an artifact — the token in \`.git/config\` can be swept into the artifact. Set \`persist-credentials: false\` on the checkout.
1180
-
1181
- ### GHA050 — Cache populated in a privileged context
1182
-
1183
- **Severity:** warning
1184
-
1185
- Flags \`actions/cache\` under a privileged trigger (\`pull_request_target\`, \`workflow_run\`). A cache entry influenced by a fork can be restored and executed by a later trusted run (cache poisoning) — restrict caching to trusted triggers.
1186
-
1187
- ### GHA051 — Publish step using a long-lived token instead of OIDC
1188
-
1189
- **Severity:** info
1190
-
1191
- Flags a publish/release job that uses a long-lived token secret while requesting no \`id-token: write\`. If the registry supports OIDC, mint a short-lived federated credential per run instead of holding a standing token.
1192
-
1193
- ### GHA052 — Software fetched and piped to a shell
1194
-
1195
- **Severity:** warning
1196
-
1197
- Flags a \`run:\` step that pipes a network download straight into a shell (\`curl ... | bash\`). The fetched code is unpinned and unverified — download to a file, verify a checksum/signature, then run it.
1198
-
1199
- ### GHA053 — Unsafe set-env / add-path opt-in
1200
-
1201
- **Severity:** error
1202
-
1203
- Flags re-enabling the \`set-env\` / \`add-path\` workflow commands removed for security (CVE-2020-15228) via \`ACTIONS_ALLOW_UNSECURE_COMMANDS\` or direct \`::set-env::\` / \`::add-path::\`. Use the \`$GITHUB_ENV\` / \`$GITHUB_PATH\` files instead.
1204
-
1205
- ### GHA054 — Known-bad feature usage
1206
-
1207
- **Severity:** warning
1208
-
1209
- Catch-all, data-driven check flagging emitted content matching a vendored snapshot of risky features (deprecated workflow commands, unsafe runtime opt-ins). Advisory and necessarily incomplete.
1210
-
1211
- ### GHA055 — Redundant runtime tool install
1212
-
1213
- **Severity:** info
1214
-
1215
- Flags a \`run:\` step that installs a tool GitHub-hosted runners already ship. The redundant install adds supply-chain surface for no benefit (irrelevant on self-hosted runners that may lack the tool).
1216
-
1217
- ### GHA056 — Workflow without a name
1218
-
1219
- **Severity:** info
1220
-
1221
- Flags a workflow with no top-level \`name:\`. Without one, GitHub falls back to the file path in the UI and audit logs, making runs harder to identify.
1222
-
1223
- > Two items from issue #295 are intentionally not implemented at the post-synth layer: over-broad/unrevoked app-installation tokens (whether a minted token is scoped wider than used cannot be determined from emitted YAML) and a reference allowlist/denylist policy (configuration, overlapping the GHA029 trusted-owner allowlist).
1224
-
1225
- ### GHA057 — Dependency update executes untrusted code
1226
-
1227
- **Severity:** error
1228
-
1229
- Flags a Dependabot \`updates:\` entry with \`insecure-external-code-execution: allow\`, which runs a freshly-pulled dependency's lifecycle scripts during the update itself — a compromised release executes before any review. Set it to \`deny\`. Requires the \`Dependabot\` resource so the config is emitted (\`.github/dependabot.yml\`).
1230
-
1231
- ### GHA058 — Dependency update without a cooldown
1232
-
1233
- **Severity:** warning
1234
-
1235
- Flags a Dependabot \`updates:\` entry with no cooldown — a version published moments ago (including a compromised one) is adopted immediately. Configure a \`cooldown:\` window. The \`Dependabot\` composite ships a 7-day default.
1236
-
1237
- ## Running lint
1238
-
1239
- \`\`\`bash
1240
- # Lint your chant project
1241
- chant lint src/
1242
-
1243
- # Lint with auto-fix where supported
1244
- chant lint --fix src/
1245
- \`\`\`
1246
-
1247
- To suppress a rule on a specific line:
1248
-
1249
- \`\`\`typescript
1250
- // chant-disable-next-line GHA001
1251
- new Step({ uses: "actions/checkout@v4" });
1252
- \`\`\`
1253
-
1254
- To suppress globally in \`chant.config.ts\`:
1255
-
1256
- \`\`\`typescript
1257
- export default {
1258
- lint: {
1259
- rules: {
1260
- GHA014: "off",
1261
- },
1262
- },
1263
- };
1264
- \`\`\``,
1265
- },
1266
- {
1267
- slug: "examples",
1268
- title: "Examples",
1269
- description: "Walkthrough of GitHub Actions examples — workflows, composites, and patterns",
1270
- content: `Runnable examples live in the lexicon's \`examples/\` directory. Clone the repo and try them:
1271
-
1272
- \`\`\`bash
1273
- cd examples/getting-started
1274
- npm install
1275
- chant build # produces .github/workflows/ci.yml
1276
- chant lint # runs lint rules
1277
- npx vitest run # run the tests
1278
- \`\`\`
1279
-
1280
- ## Getting Started
1281
-
1282
- \`examples/getting-started/\` — a CI workflow with build and test steps for a Node.js project.
1283
-
1284
- \`\`\`
1285
- src/
1286
- └── ci.ts # Workflow + Job definitions
1287
- \`\`\`
1288
-
1289
- ### Source
1290
-
1291
- {{file:getting-started/src/ci.ts}}
1292
-
1293
- ### Generated output
1294
-
1295
- \`chant build\` produces \`.github/workflows/ci.yml\`:
1296
-
1297
- \`\`\`yaml
1298
- name: CI
1299
- on:
1300
- push:
1301
- branches:
1302
- - main
1303
- pull_request:
1304
- branches:
1305
- - main
1306
- permissions:
1307
- contents: read
1308
- jobs:
1309
- build:
1310
- runs-on: ubuntu-latest
1311
- timeout-minutes: 15
1312
- steps:
1313
- - name: Checkout
1314
- uses: actions/checkout@v4
1315
- - name: Setup Node.js
1316
- uses: actions/setup-node@v4
1317
- with:
1318
- node-version: '22'
1319
- cache: npm
1320
- - name: Install
1321
- run: npm ci
1322
- - name: Build
1323
- run: npm run build
1324
- - name: Test
1325
- run: npm test
1326
- \`\`\`
1327
-
1328
- **Patterns demonstrated:**
1329
-
1330
- 1. **Typed composites** — \`Checkout({})\` and \`SetupNode({...})\` instead of raw \`uses:\` strings
1331
- 2. **Permissions** — explicit \`contents: read\` following least-privilege
1332
- 3. **Timeout** — \`timeoutMinutes: 15\` prevents runaway jobs
1333
- 4. **Trigger scoping** — push and PR on \`main\` only`,
1334
- },
1335
- {
1336
- slug: "skills",
1337
- title: "AI Skills",
1338
- description: "AI agent skills bundled with the GitHub Actions lexicon",
1339
- content: `The GitHub Actions lexicon ships AI skills that teach AI coding agents (like Claude Code) how to build, validate, and deploy GitHub Actions workflows from a chant project.
1340
-
1341
- ## What are skills?
1342
-
1343
- Skills are structured markdown documents bundled with a lexicon. When an AI agent works in a chant project, it discovers and loads relevant skills automatically — giving it operational knowledge about the deployment workflow without requiring the user to explain each step.
1344
-
1345
- ## Installation
1346
-
1347
- When you scaffold a new project with \`chant init --lexicon github\`, skills are installed to \`.claude/skills/\` for automatic discovery by Claude Code.
1348
-
1349
- For existing projects, create the files manually:
1350
-
1351
- \`\`\`
1352
- .claude/
1353
- skills/
1354
- chant-github/
1355
- SKILL.md # skill content
1356
- \`\`\`
1357
-
1358
- ## Skill: chant-github
1359
-
1360
- The inline \`chant-github\` skill covers the full workflow lifecycle:
1361
-
1362
- - **Build** — \`chant build src/ --output .github/workflows/ci.yml\`
1363
- - **Validate** — \`chant lint src/\`
1364
- - **Deploy** — commit and push the generated YAML
1365
- - **Status** — GitHub Actions UI or \`gh run list\`
1366
- - **Troubleshooting** — lint rule codes (GHA001–GHA020), post-synth checks (GHA006–GHA058)
1367
-
1368
- The skill is invocable as a slash command: \`/chant-github\`
1369
-
1370
- ## Skill: github-actions-patterns
1371
-
1372
- A file-based skill loaded from \`src/skills/github-actions-patterns.md\`. It provides pattern knowledge for:
1373
-
1374
- - **Workflow structure** — name, on, permissions, jobs
1375
- - **Trigger patterns** — push, pull_request, schedule, workflow_dispatch
1376
- - **Matrix strategy** — multi-OS, multi-version builds
1377
- - **Caching** — SetupNode cache option, CacheAction composite
1378
- - **Permissions** — least-privilege patterns
1379
- - **Reusable workflows** — ReusableWorkflowCallJob
1380
- - **Artifacts** — upload/download between jobs
1381
- - **Concurrency** — group + cancel-in-progress
1382
-
1383
- ## MCP integration
1384
-
1385
- The lexicon provides MCP (Model Context Protocol) tools and resources that AI agents can use programmatically:
1386
-
1387
- | MCP tool | Description |
1388
- |----------|-------------|
1389
- | \`diff\` | Compare current build output against previous output |
1390
- | \`github:checks\` | Build the workflow and return its security findings (the GHA checks) |
1391
- | \`github:workflow\` | Triggers and jobs as written — name, run order, step count |
1392
- | \`github:references\` | Actions and images pulled in, and whether each is pinned to a commit SHA |
1393
- | \`github:affected\` | Given a job, the jobs that would re-run because they depend on it |
1394
- | \`github:workflow-yaml\` | The generated workflow YAML |
1395
-
1396
- The \`github:*\` tools are **read-only context tools** (#327): each builds from your source and returns what chant already computes — before the workflow runs or merges. None touch the live GitHub instance, run history, or write anything.
1397
-
1398
- | MCP resource | Description |
1399
- |--------------|-------------|
1400
- | \`resource-catalog\` | JSON list of all supported GitHub Actions entity types |
1401
- | \`examples/basic-ci\` | Example CI workflow with TypeScript source |`,
1402
- },
1403
- ],
1404
104
  basePath: "/chant/lexicons/github/",
1405
- // Hand-written pages under docs/src/content/docs/ that no sidebar entry
1406
- // pointed at (#1312).
1407
- sidebarExtra: [
1408
- { label: "Workflows", slug: "workflows" },
1409
- { label: "Actions & Composites", slug: "actions" },
1410
- { label: "Matrix Strategies", slug: "matrix" },
1411
- { label: "Multiple Workflows", slug: "multi-workflow" },
1412
- ],
1413
105
  };
1414
106
 
1415
107
  const result = await docsPipeline(config);