@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.
- package/dist/integrity.json +14 -4
- package/dist/manifest.json +1 -1
- package/dist/rules/gha020.ts +40 -0
- package/dist/rules/gha021.ts +48 -0
- package/dist/rules/gha022.ts +50 -0
- package/dist/rules/gha023.ts +44 -0
- package/dist/rules/gha024.ts +42 -0
- package/dist/rules/gha025.ts +42 -0
- package/dist/rules/gha026.ts +40 -0
- package/dist/rules/gha027.ts +57 -0
- package/dist/rules/gha028.ts +37 -0
- package/dist/skills/{github-actions-patterns.md → chant-github-patterns.md} +2 -1
- package/dist/skills/chant-github-security.md +88 -0
- package/dist/skills/chant-github.md +569 -4
- package/package.json +20 -2
- package/src/codegen/docs.test.ts +19 -0
- package/src/codegen/generate.test.ts +12 -0
- package/src/codegen/package.test.ts +8 -0
- package/src/composites/cache.ts +8 -3
- package/src/composites/checkout.ts +8 -3
- package/src/composites/composites.test.ts +106 -0
- package/src/composites/deploy-environment.ts +11 -5
- package/src/composites/docker-build.ts +11 -5
- package/src/composites/download-artifact.ts +8 -3
- package/src/composites/go-ci.ts +17 -9
- package/src/composites/node-ci.ts +11 -5
- package/src/composites/node-pipeline.ts +14 -7
- package/src/composites/python-ci.ts +14 -7
- package/src/composites/setup-go.ts +8 -3
- package/src/composites/setup-node.ts +8 -3
- package/src/composites/setup-python.ts +8 -3
- package/src/composites/upload-artifact.ts +8 -3
- package/src/coverage.test.ts +23 -0
- package/src/import/roundtrip.test.ts +206 -0
- package/src/lint/post-synth/gha006.test.ts +56 -0
- package/src/lint/post-synth/gha009.test.ts +56 -0
- package/src/lint/post-synth/gha011.test.ts +61 -0
- package/src/lint/post-synth/gha017.test.ts +51 -0
- package/src/lint/post-synth/gha018.test.ts +66 -0
- package/src/lint/post-synth/gha019.test.ts +66 -0
- package/src/lint/post-synth/gha020.test.ts +66 -0
- package/src/lint/post-synth/gha020.ts +40 -0
- package/src/lint/post-synth/gha021.test.ts +67 -0
- package/src/lint/post-synth/gha021.ts +48 -0
- package/src/lint/post-synth/gha022.test.ts +45 -0
- package/src/lint/post-synth/gha022.ts +50 -0
- package/src/lint/post-synth/gha023.test.ts +52 -0
- package/src/lint/post-synth/gha023.ts +44 -0
- package/src/lint/post-synth/gha024.test.ts +67 -0
- package/src/lint/post-synth/gha024.ts +42 -0
- package/src/lint/post-synth/gha025.test.ts +65 -0
- package/src/lint/post-synth/gha025.ts +42 -0
- package/src/lint/post-synth/gha026.test.ts +65 -0
- package/src/lint/post-synth/gha026.ts +40 -0
- package/src/lint/post-synth/gha027.test.ts +48 -0
- package/src/lint/post-synth/gha027.ts +57 -0
- package/src/lint/post-synth/gha028.test.ts +48 -0
- package/src/lint/post-synth/gha028.ts +37 -0
- package/src/lint/rules/deprecated-action-version.test.ts +26 -0
- package/src/lint/rules/detect-secrets.test.ts +25 -0
- package/src/lint/rules/extract-inline-structs.test.ts +25 -0
- package/src/lint/rules/file-job-limit.test.ts +28 -0
- package/src/lint/rules/job-timeout.test.ts +31 -0
- package/src/lint/rules/missing-recommended-inputs.test.ts +26 -0
- package/src/lint/rules/no-hardcoded-secrets.test.ts +31 -0
- package/src/lint/rules/no-raw-expressions.test.ts +31 -0
- package/src/lint/rules/suggest-cache.test.ts +25 -0
- package/src/lint/rules/use-condition-builders.test.ts +31 -0
- package/src/lint/rules/use-matrix-builder.test.ts +25 -0
- package/src/lint/rules/use-typed-actions.test.ts +39 -0
- package/src/lint/rules/validate-concurrency.test.ts +31 -0
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +70 -145
- package/src/skills/{github-actions-patterns.md → chant-github-patterns.md} +2 -1
- package/src/skills/chant-github-security.md +88 -0
- package/src/skills/chant-github.md +594 -0
- package/src/validate.ts +14 -1
- 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
|
+
});
|