@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
package/src/plugin.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createRequire } from "module";
|
|
9
|
-
import type { LexiconPlugin, IntrinsicDef,
|
|
9
|
+
import type { LexiconPlugin, IntrinsicDef, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
|
-
import
|
|
12
|
+
import { discoverPostSynthChecks } from "@intentius/chant/lint/discover";
|
|
13
|
+
import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
|
|
14
|
+
import { join, dirname } from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
13
16
|
import { githubSerializer } from "./serializer";
|
|
14
17
|
|
|
15
18
|
export const githubPlugin: LexiconPlugin = {
|
|
@@ -47,14 +50,9 @@ export const githubPlugin: LexiconPlugin = {
|
|
|
47
50
|
];
|
|
48
51
|
},
|
|
49
52
|
|
|
50
|
-
postSynthChecks()
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
const { gha011 } = require("./lint/post-synth/gha011");
|
|
54
|
-
const { gha017 } = require("./lint/post-synth/gha017");
|
|
55
|
-
const { gha018 } = require("./lint/post-synth/gha018");
|
|
56
|
-
const { gha019 } = require("./lint/post-synth/gha019");
|
|
57
|
-
return [gha006, gha009, gha011, gha017, gha018, gha019];
|
|
53
|
+
postSynthChecks() {
|
|
54
|
+
const postSynthDir = join(dirname(fileURLToPath(import.meta.url)), "lint", "post-synth");
|
|
55
|
+
return discoverPostSynthChecks(postSynthDir, import.meta.url);
|
|
58
56
|
},
|
|
59
57
|
|
|
60
58
|
intrinsics(): IntrinsicDef[] {
|
|
@@ -228,48 +226,12 @@ export const build = new Job({
|
|
|
228
226
|
},
|
|
229
227
|
|
|
230
228
|
mcpTools() {
|
|
231
|
-
return [
|
|
232
|
-
{
|
|
233
|
-
name: "diff",
|
|
234
|
-
description: "Compare current build output against previous output for GitHub Actions",
|
|
235
|
-
inputSchema: {
|
|
236
|
-
type: "object" as const,
|
|
237
|
-
properties: {
|
|
238
|
-
path: {
|
|
239
|
-
type: "string",
|
|
240
|
-
description: "Path to the infrastructure project directory",
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
async handler(params: Record<string, unknown>): Promise<unknown> {
|
|
245
|
-
const { diffCommand } = await import("@intentius/chant/cli/commands/diff");
|
|
246
|
-
const result = await diffCommand({
|
|
247
|
-
path: (params.path as string) ?? ".",
|
|
248
|
-
serializers: [githubSerializer],
|
|
249
|
-
});
|
|
250
|
-
return result;
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
];
|
|
229
|
+
return [createDiffTool(githubSerializer, "Compare current build output against previous output for GitHub Actions")];
|
|
254
230
|
},
|
|
255
231
|
|
|
256
232
|
mcpResources() {
|
|
257
233
|
return [
|
|
258
|
-
|
|
259
|
-
uri: "resource-catalog",
|
|
260
|
-
name: "GitHub Actions Entity Catalog",
|
|
261
|
-
description: "JSON list of all supported GitHub Actions entity types",
|
|
262
|
-
mimeType: "application/json",
|
|
263
|
-
async handler(): Promise<string> {
|
|
264
|
-
const lexicon = require("./generated/lexicon-github.json") as Record<string, { resourceType: string; kind: string }>;
|
|
265
|
-
const entries = Object.entries(lexicon).map(([className, entry]) => ({
|
|
266
|
-
className,
|
|
267
|
-
resourceType: entry.resourceType,
|
|
268
|
-
kind: entry.kind,
|
|
269
|
-
}));
|
|
270
|
-
return JSON.stringify(entries);
|
|
271
|
-
},
|
|
272
|
-
},
|
|
234
|
+
createCatalogResource(import.meta.url, "GitHub Actions Entity Catalog", "JSON list of all supported GitHub Actions entity types", "lexicon-github.json"),
|
|
273
235
|
{
|
|
274
236
|
uri: "examples/basic-ci",
|
|
275
237
|
name: "Basic CI Example",
|
|
@@ -308,101 +270,64 @@ export const build = new Job({
|
|
|
308
270
|
await generateDocs(options);
|
|
309
271
|
},
|
|
310
272
|
|
|
311
|
-
skills
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
chant
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
{
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
{
|
|
372
|
-
file: "github-actions-patterns.md",
|
|
373
|
-
name: "github-actions-patterns",
|
|
374
|
-
description: "GitHub Actions workflow patterns — triggers, jobs, matrix, caching, artifacts",
|
|
375
|
-
triggers: [
|
|
376
|
-
{ type: "context" as const, value: "github actions" },
|
|
377
|
-
{ type: "context" as const, value: "workflow" },
|
|
378
|
-
{ type: "context" as const, value: "matrix" },
|
|
379
|
-
{ type: "context" as const, value: "cache" },
|
|
380
|
-
],
|
|
381
|
-
parameters: [],
|
|
382
|
-
examples: [
|
|
383
|
-
{
|
|
384
|
-
title: "Matrix strategy",
|
|
385
|
-
input: "Set up a Node.js matrix build",
|
|
386
|
-
output: `new Strategy({ matrix: { "node-version": ["18", "20", "22"] } })`,
|
|
387
|
-
},
|
|
388
|
-
],
|
|
389
|
-
},
|
|
390
|
-
];
|
|
391
|
-
|
|
392
|
-
for (const skill of skillFiles) {
|
|
393
|
-
try {
|
|
394
|
-
const content = readFileSync(join(dir, "skills", skill.file), "utf-8");
|
|
395
|
-
skills.push({
|
|
396
|
-
name: skill.name,
|
|
397
|
-
description: skill.description,
|
|
398
|
-
content,
|
|
399
|
-
triggers: skill.triggers,
|
|
400
|
-
parameters: skill.parameters,
|
|
401
|
-
examples: skill.examples,
|
|
402
|
-
});
|
|
403
|
-
} catch { /* skip missing skills */ }
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
return skills;
|
|
407
|
-
},
|
|
273
|
+
skills: createSkillsLoader(import.meta.url, [
|
|
274
|
+
{
|
|
275
|
+
file: "chant-github.md",
|
|
276
|
+
name: "chant-github",
|
|
277
|
+
description: "GitHub Actions workflow lifecycle — build, validate, deploy",
|
|
278
|
+
triggers: [
|
|
279
|
+
{ type: "file-pattern", value: "**/*.github.ts" },
|
|
280
|
+
{ type: "file-pattern", value: "**/.github/workflows/*.yml" },
|
|
281
|
+
{ type: "context", value: "github actions" },
|
|
282
|
+
{ type: "context", value: "workflow" },
|
|
283
|
+
],
|
|
284
|
+
parameters: [],
|
|
285
|
+
examples: [
|
|
286
|
+
{
|
|
287
|
+
title: "Basic CI workflow",
|
|
288
|
+
description: "Create a CI workflow with build and test",
|
|
289
|
+
input: "Create a CI workflow",
|
|
290
|
+
output: `new Workflow({ name: "CI", on: { push: { branches: ["main"] }, pull_request: { branches: ["main"] } } })`,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
file: "chant-github-patterns.md",
|
|
296
|
+
name: "chant-github-patterns",
|
|
297
|
+
description: "GitHub Actions workflow patterns — triggers, jobs, matrix, caching, artifacts",
|
|
298
|
+
triggers: [
|
|
299
|
+
{ type: "context", value: "github actions" },
|
|
300
|
+
{ type: "context", value: "workflow" },
|
|
301
|
+
{ type: "context", value: "matrix" },
|
|
302
|
+
{ type: "context", value: "cache" },
|
|
303
|
+
],
|
|
304
|
+
parameters: [],
|
|
305
|
+
examples: [
|
|
306
|
+
{
|
|
307
|
+
title: "Matrix strategy",
|
|
308
|
+
input: "Set up a Node.js matrix build",
|
|
309
|
+
output: `new Strategy({ matrix: { "node-version": ["18", "20", "22"] } })`,
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
file: "chant-github-security.md",
|
|
315
|
+
name: "chant-github-security",
|
|
316
|
+
description: "GitHub Actions security — secret scanning, OIDC, permissions hardening, supply chain",
|
|
317
|
+
triggers: [
|
|
318
|
+
{ type: "context", value: "github security" },
|
|
319
|
+
{ type: "context", value: "workflow security" },
|
|
320
|
+
{ type: "context", value: "oidc" },
|
|
321
|
+
{ type: "context", value: "permissions" },
|
|
322
|
+
],
|
|
323
|
+
parameters: [],
|
|
324
|
+
examples: [
|
|
325
|
+
{
|
|
326
|
+
title: "Permissions hardening",
|
|
327
|
+
input: "Lock down workflow permissions",
|
|
328
|
+
output: `new Workflow({ permissions: { contents: "read" } })`,
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
]),
|
|
408
333
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: chant-github-security
|
|
3
|
+
description: GitHub Actions security best practices — secret scanning, OIDC, permissions hardening, supply chain security
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# GitHub Actions Security Playbook
|
|
8
|
+
|
|
9
|
+
## Permissions Hardening
|
|
10
|
+
|
|
11
|
+
Always set the minimum required permissions at the workflow level:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
new Workflow({
|
|
15
|
+
name: "CI",
|
|
16
|
+
on: { push: { branches: ["main"] } },
|
|
17
|
+
permissions: { contents: "read" },
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For deployments that need write access, scope it to the job:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
new Job({
|
|
25
|
+
"runs-on": "ubuntu-latest",
|
|
26
|
+
permissions: { contents: "read", "id-token": "write" },
|
|
27
|
+
steps: [...],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Pin Actions by SHA
|
|
32
|
+
|
|
33
|
+
Never use mutable tags like `@v4`. Pin to a full commit SHA:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
new Step({
|
|
37
|
+
uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11", // v4.1.1
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## OIDC for Cloud Providers
|
|
42
|
+
|
|
43
|
+
Use OpenID Connect instead of long-lived secrets:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// AWS
|
|
47
|
+
new Step({
|
|
48
|
+
uses: "aws-actions/configure-aws-credentials@v4",
|
|
49
|
+
with: {
|
|
50
|
+
"role-to-assume": "arn:aws:iam::123456789012:role/deploy",
|
|
51
|
+
"aws-region": "us-east-1",
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Secret Scanning
|
|
57
|
+
|
|
58
|
+
- Never echo secrets in `run:` steps
|
|
59
|
+
- Use `environment` protection rules for production secrets
|
|
60
|
+
- Rotate secrets regularly and audit access logs
|
|
61
|
+
|
|
62
|
+
## Supply Chain Security
|
|
63
|
+
|
|
64
|
+
- Use `permissions: {}` (empty) as a baseline, then grant only what each job needs
|
|
65
|
+
- Avoid `pull_request_target` with `actions/checkout` (code injection risk)
|
|
66
|
+
- Use Dependabot or Renovate to keep action versions current
|
|
67
|
+
- Add `concurrency` blocks to prevent parallel deploys
|
|
68
|
+
|
|
69
|
+
## Concurrency Control
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
new Concurrency({
|
|
73
|
+
group: "${{ github.workflow }}-${{ github.ref }}",
|
|
74
|
+
"cancel-in-progress": true,
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Job Timeouts
|
|
79
|
+
|
|
80
|
+
Always set timeouts to prevent runaway jobs:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
new Job({
|
|
84
|
+
"runs-on": "ubuntu-latest",
|
|
85
|
+
"timeout-minutes": 30,
|
|
86
|
+
steps: [...],
|
|
87
|
+
});
|
|
88
|
+
```
|