@intentius/chant-lexicon-github 0.124.31 → 0.124.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/dist/action-pins.js +53 -0
  2. package/dist/codegen/docs-cli.js +2 -0
  3. package/dist/codegen/docs.js +142 -0
  4. package/dist/codegen/generate-cli.js +26 -0
  5. package/dist/codegen/generate-lexicon.js +36 -0
  6. package/dist/codegen/generate-typescript.js +108 -0
  7. package/dist/codegen/generate.js +111 -0
  8. package/dist/codegen/naming.js +49 -0
  9. package/dist/codegen/package.js +40 -0
  10. package/dist/codegen/parse.js +595 -0
  11. package/dist/codegen/patches.js +36 -0
  12. package/dist/components/ci-jobs.d.ts +7 -0
  13. package/dist/components/ci-jobs.d.ts.map +1 -1
  14. package/dist/components/ci-jobs.js +98 -0
  15. package/dist/components/generate-op-pipeline.js +440 -0
  16. package/dist/components/generate-op-waves-pipeline.js +158 -0
  17. package/dist/components/generate-pipeline.d.ts.map +1 -1
  18. package/dist/components/generate-pipeline.js +481 -0
  19. package/dist/composites/cache.js +17 -0
  20. package/dist/composites/catalog.js +859 -0
  21. package/dist/composites/checkout.js +25 -0
  22. package/dist/composites/dependabot.js +43 -0
  23. package/dist/composites/deploy-environment.js +38 -0
  24. package/dist/composites/docker-build.js +70 -0
  25. package/dist/composites/download-artifact.js +18 -0
  26. package/dist/composites/go-ci.js +62 -0
  27. package/dist/composites/index.js +15 -0
  28. package/dist/composites/node-ci.js +44 -0
  29. package/dist/composites/node-pipeline.js +96 -0
  30. package/dist/composites/pr-plan-report.js +129 -0
  31. package/dist/composites/python-ci.js +60 -0
  32. package/dist/composites/setup-go.js +18 -0
  33. package/dist/composites/setup-node.js +21 -0
  34. package/dist/composites/setup-python.js +18 -0
  35. package/dist/composites/upload-artifact.js +19 -0
  36. package/dist/coverage.js +32 -0
  37. package/dist/expression.js +177 -0
  38. package/dist/generated/index.js +26 -0
  39. package/dist/generated/runtime.js +4 -0
  40. package/dist/graph-edges.js +174 -0
  41. package/dist/import/generator.js +108 -0
  42. package/dist/import/parser.js +69 -0
  43. package/dist/index.js +22 -0
  44. package/dist/integrity.json +2 -2
  45. package/dist/lint/audit-catalog.js +69 -0
  46. package/dist/lint/audit-lineage.js +168 -0
  47. package/dist/lint/post-synth/gha006.js +50 -0
  48. package/dist/lint/post-synth/gha009.js +35 -0
  49. package/dist/lint/post-synth/gha011.js +34 -0
  50. package/dist/lint/post-synth/gha013.js +40 -0
  51. package/dist/lint/post-synth/gha017.js +25 -0
  52. package/dist/lint/post-synth/gha018.js +33 -0
  53. package/dist/lint/post-synth/gha019.js +58 -0
  54. package/dist/lint/post-synth/gha021.js +42 -0
  55. package/dist/lint/post-synth/gha022.js +44 -0
  56. package/dist/lint/post-synth/gha023.js +37 -0
  57. package/dist/lint/post-synth/gha024.js +32 -0
  58. package/dist/lint/post-synth/gha025.js +33 -0
  59. package/dist/lint/post-synth/gha026.js +32 -0
  60. package/dist/lint/post-synth/gha027.js +46 -0
  61. package/dist/lint/post-synth/gha028.js +28 -0
  62. package/dist/lint/post-synth/gha029.js +56 -0
  63. package/dist/lint/post-synth/gha030.js +35 -0
  64. package/dist/lint/post-synth/gha031.js +75 -0
  65. package/dist/lint/post-synth/gha032.js +35 -0
  66. package/dist/lint/post-synth/gha033.js +39 -0
  67. package/dist/lint/post-synth/gha034.js +35 -0
  68. package/dist/lint/post-synth/gha035.js +50 -0
  69. package/dist/lint/post-synth/gha036.js +37 -0
  70. package/dist/lint/post-synth/gha037.js +41 -0
  71. package/dist/lint/post-synth/gha038.js +35 -0
  72. package/dist/lint/post-synth/gha039.js +34 -0
  73. package/dist/lint/post-synth/gha040.js +36 -0
  74. package/dist/lint/post-synth/gha041.js +32 -0
  75. package/dist/lint/post-synth/gha042.js +33 -0
  76. package/dist/lint/post-synth/gha043.js +35 -0
  77. package/dist/lint/post-synth/gha044.js +36 -0
  78. package/dist/lint/post-synth/gha045.js +33 -0
  79. package/dist/lint/post-synth/gha046.js +55 -0
  80. package/dist/lint/post-synth/gha047.js +44 -0
  81. package/dist/lint/post-synth/gha048.js +34 -0
  82. package/dist/lint/post-synth/gha049.js +36 -0
  83. package/dist/lint/post-synth/gha050.js +37 -0
  84. package/dist/lint/post-synth/gha051.js +38 -0
  85. package/dist/lint/post-synth/gha052.js +35 -0
  86. package/dist/lint/post-synth/gha053.js +34 -0
  87. package/dist/lint/post-synth/gha054.js +35 -0
  88. package/dist/lint/post-synth/gha055.js +44 -0
  89. package/dist/lint/post-synth/gha056.js +32 -0
  90. package/dist/lint/post-synth/gha057.js +35 -0
  91. package/dist/lint/post-synth/gha058.js +42 -0
  92. package/dist/lint/post-synth/gha059.js +119 -0
  93. package/dist/lint/post-synth/gha060.js +129 -0
  94. package/dist/lint/post-synth/gha061.js +78 -0
  95. package/dist/lint/post-synth/gha062.js +65 -0
  96. package/dist/lint/post-synth/gha063.js +56 -0
  97. package/dist/lint/post-synth/gha064.js +47 -0
  98. package/dist/lint/post-synth/gha065.js +125 -0
  99. package/dist/lint/post-synth/gha066.js +40 -0
  100. package/dist/lint/post-synth/gha067.js +75 -0
  101. package/dist/lint/post-synth/gha068.js +47 -0
  102. package/dist/lint/post-synth/gha069.js +99 -0
  103. package/dist/lint/post-synth/index.js +114 -0
  104. package/dist/lint/post-synth/yaml-helpers.js +465 -0
  105. package/dist/lint/rules/data/action-usage-policy.js +16 -0
  106. package/dist/lint/rules/data/advisory-feed.js +15 -0
  107. package/dist/lint/rules/data/deprecated-versions.js +14 -0
  108. package/dist/lint/rules/data/flagged-actions.js +18 -0
  109. package/dist/lint/rules/data/known-action-slugs.js +38 -0
  110. package/dist/lint/rules/data/known-actions.js +12 -0
  111. package/dist/lint/rules/data/preinstalled-tools.js +36 -0
  112. package/dist/lint/rules/data/recommended-inputs.js +9 -0
  113. package/dist/lint/rules/data/risky-features.js +22 -0
  114. package/dist/lint/rules/data/secret-patterns.js +30 -0
  115. package/dist/lint/rules/data/trusted-action-owners.js +12 -0
  116. package/dist/lint/rules/data/untrusted-contexts.js +44 -0
  117. package/dist/lint/rules/deprecated-action-version.js +44 -0
  118. package/dist/lint/rules/detect-secrets.js +45 -0
  119. package/dist/lint/rules/extract-inline-structs.js +55 -0
  120. package/dist/lint/rules/file-job-limit.js +43 -0
  121. package/dist/lint/rules/index.js +16 -0
  122. package/dist/lint/rules/job-timeout.js +53 -0
  123. package/dist/lint/rules/missing-recommended-inputs.js +51 -0
  124. package/dist/lint/rules/no-hardcoded-secrets.js +39 -0
  125. package/dist/lint/rules/no-raw-expressions.js +44 -0
  126. package/dist/lint/rules/suggest-cache.js +60 -0
  127. package/dist/lint/rules/use-condition-builders.js +37 -0
  128. package/dist/lint/rules/use-matrix-builder.js +36 -0
  129. package/dist/lint/rules/use-typed-actions.js +41 -0
  130. package/dist/lint/rules/validate-concurrency.js +59 -0
  131. package/dist/lsp/completions.d.ts.map +1 -1
  132. package/dist/lsp/completions.js +18 -0
  133. package/dist/lsp/hover.d.ts.map +1 -1
  134. package/dist/lsp/hover.js +33 -0
  135. package/dist/manifest.json +1 -1
  136. package/dist/mcp/context-tools.js +152 -0
  137. package/dist/package-cli.js +20 -0
  138. package/dist/plugin.js +344 -0
  139. package/dist/serializer.js +572 -0
  140. package/dist/spec/fetch.js +46 -0
  141. package/dist/validate-cli.js +16 -0
  142. package/dist/validate.js +38 -0
  143. package/dist/variables.js +41 -0
  144. package/package.json +6 -6
  145. package/src/codegen/package.ts +2 -2
  146. package/src/components/__fixtures__/pr-loop-build.github.golden.yml +6 -2
  147. package/src/components/__fixtures__/pr-loop-record.github.golden.yml +74 -0
  148. package/src/components/__fixtures__/pr-loop-waves.github.golden.yml +178 -0
  149. package/src/components/__fixtures__/pr-loop.github.golden.yml +6 -2
  150. package/src/components/ci-jobs.ts +16 -2
  151. package/src/components/generate-op-pipeline.test.ts +11 -0
  152. package/src/components/generate-op-pipeline.ts +3 -3
  153. package/src/components/generate-pipeline.ts +74 -31
  154. package/src/components/generate-pr-pipeline.test.ts +99 -29
  155. package/src/components/generate-pr-waves-pipeline.test.ts +117 -0
  156. package/src/lsp/completions.ts +2 -1
  157. package/src/lsp/hover.ts +2 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * GitHub Actions predefined context variable references.
3
+ *
4
+ * These provide type-safe access to GitHub and Runner context values
5
+ * that expand to `${{ context.property }}` expressions in YAML.
6
+ */
7
+ import { Expression } from "./expression.js";
8
+ export const GitHub = {
9
+ Ref: /* @__PURE__ */ new Expression("github.ref"),
10
+ RefName: /* @__PURE__ */ new Expression("github.ref_name"),
11
+ RefType: /* @__PURE__ */ new Expression("github.ref_type"),
12
+ Sha: /* @__PURE__ */ new Expression("github.sha"),
13
+ Actor: /* @__PURE__ */ new Expression("github.actor"),
14
+ TriggeringActor: /* @__PURE__ */ new Expression("github.triggering_actor"),
15
+ Repository: /* @__PURE__ */ new Expression("github.repository"),
16
+ RepositoryOwner: /* @__PURE__ */ new Expression("github.repository_owner"),
17
+ EventName: /* @__PURE__ */ new Expression("github.event_name"),
18
+ Event: /* @__PURE__ */ new Expression("github.event"),
19
+ RunId: /* @__PURE__ */ new Expression("github.run_id"),
20
+ RunNumber: /* @__PURE__ */ new Expression("github.run_number"),
21
+ RunAttempt: /* @__PURE__ */ new Expression("github.run_attempt"),
22
+ Workflow: /* @__PURE__ */ new Expression("github.workflow"),
23
+ WorkflowRef: /* @__PURE__ */ new Expression("github.workflow_ref"),
24
+ Workspace: /* @__PURE__ */ new Expression("github.workspace"),
25
+ Token: /* @__PURE__ */ new Expression("github.token"),
26
+ Job: /* @__PURE__ */ new Expression("github.job"),
27
+ HeadRef: /* @__PURE__ */ new Expression("github.head_ref"),
28
+ BaseRef: /* @__PURE__ */ new Expression("github.base_ref"),
29
+ ServerUrl: /* @__PURE__ */ new Expression("github.server_url"),
30
+ ApiUrl: /* @__PURE__ */ new Expression("github.api_url"),
31
+ GraphqlUrl: /* @__PURE__ */ new Expression("github.graphql_url"),
32
+ Action: /* @__PURE__ */ new Expression("github.action"),
33
+ ActionPath: /* @__PURE__ */ new Expression("github.action_path"),
34
+ };
35
+ export const Runner = {
36
+ Os: /* @__PURE__ */ new Expression("runner.os"),
37
+ Arch: /* @__PURE__ */ new Expression("runner.arch"),
38
+ Name: /* @__PURE__ */ new Expression("runner.name"),
39
+ Temp: /* @__PURE__ */ new Expression("runner.temp"),
40
+ ToolCache: /* @__PURE__ */ new Expression("runner.tool_cache"),
41
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.124.31",
3
+ "version": "0.124.33",
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",
@@ -32,17 +32,17 @@
32
32
  ".": {
33
33
  "development": "./src/index.ts",
34
34
  "types": "./dist/index.d.ts",
35
- "default": "./src/index.ts"
35
+ "default": "./dist/index.js"
36
36
  },
37
37
  "./*": {
38
38
  "development": "./src/*.ts",
39
39
  "types": "./dist/*.d.ts",
40
- "default": "./src/*.ts"
40
+ "default": "./dist/*.js"
41
41
  },
42
42
  "./lint/post-synth": {
43
43
  "development": "./src/lint/post-synth/index.ts",
44
44
  "types": "./dist/lint/post-synth/index.d.ts",
45
- "default": "./src/lint/post-synth/index.ts"
45
+ "default": "./dist/lint/post-synth/index.js"
46
46
  },
47
47
  "./manifest": "./dist/manifest.json",
48
48
  "./meta": "./dist/meta.json",
@@ -54,13 +54,13 @@
54
54
  "validate": "tsx src/validate-cli.ts",
55
55
  "docs": "tsx src/codegen/docs-cli.ts",
56
56
  "prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
57
- "build": "../../scripts/tsc7.sh -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete"
57
+ "build": "../../scripts/tsc7.sh -p tsconfig.build.json --sourceMap false && tsc-alias -p tsconfig.build.json"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@intentius/chant": "*",
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@intentius/chant": "^0.124.31"
64
+ "@intentius/chant": "^0.124.33"
65
65
  }
66
66
  }
@@ -5,7 +5,7 @@
5
5
  import { readFileSync } from "fs";
6
6
  import { githubPlugin } from "../plugin";
7
7
  import { join, dirname } from "path";
8
- import { fileURLToPath } from "url";
8
+ import { moduleSourceDir } from "@intentius/chant/module-source";
9
9
  import type { IntrinsicDef } from "@intentius/chant/lexicon";
10
10
  import {
11
11
  packagePipeline,
@@ -17,7 +17,7 @@ import { generate } from "./generate";
17
17
 
18
18
  export type { PackageOptions, PackageResult };
19
19
 
20
- const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
20
+ const pkgDir = dirname(moduleSourceDir(import.meta.url));
21
21
 
22
22
  /**
23
23
  * Package the GitHub Actions lexicon into a distributable BundleSpec.
@@ -34,7 +34,8 @@ jobs:
34
34
  BASE_SHA: '${{ github.event.pull_request.base.sha }}'
35
35
  PR_NUMBER: '${{ github.event.pull_request.number }}'
36
36
  GITHUB_TOKEN: '${{ github.token }}'
37
- run: chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge github
37
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
38
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge github
38
39
  - name: Keep the plan report
39
40
  if: always()
40
41
  uses: actions/upload-artifact@v4
@@ -73,7 +74,10 @@ jobs:
73
74
  env:
74
75
  BASE_SHA: '${{ github.event.before }}'
75
76
  GITHUB_TOKEN: '${{ github.token }}'
76
- run: chant components pr-apply --base "$BASE_SHA" --env prod --gate pr-apply --output .chant/pr --forge github --require-review --resume .chant/pr-resume/pr-apply.json
77
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
78
+ run: |
79
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
80
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github
77
81
  - name: Keep the record for a re-run
78
82
  if: always()
79
83
  uses: actions/cache/save@v4
@@ -0,0 +1,74 @@
1
+ name: chant-pr-prod
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ env:
12
+ CHANT_ENV: prod
13
+
14
+ jobs:
15
+ plan:
16
+ if: github.event_name == 'pull_request'
17
+ runs-on: ubuntu-latest
18
+ container: node:22
19
+ permissions:
20
+ contents: read
21
+ pull-requests: write
22
+ statuses: write
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ with:
26
+ fetch-depth: 0
27
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
28
+ - name: Plan the members this pull request reaches
29
+ env:
30
+ BASE_SHA: '${{ github.event.pull_request.base.sha }}'
31
+ PR_NUMBER: '${{ github.event.pull_request.number }}'
32
+ GITHUB_TOKEN: '${{ github.token }}'
33
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
34
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge github
35
+ - name: Keep the plan report
36
+ if: always()
37
+ uses: actions/upload-artifact@v4
38
+ with:
39
+ name: chant-pr-plan
40
+ path: .chant/pr
41
+ if-no-files-found: ignore
42
+ include-hidden-files: true
43
+ record:
44
+ if: github.event_name == 'push'
45
+ runs-on: ubuntu-latest
46
+ container: node:22
47
+ concurrency:
48
+ group: chant-record-prod
49
+ cancel-in-progress: false
50
+ permissions:
51
+ contents: write
52
+ pull-requests: write
53
+ statuses: write
54
+ steps:
55
+ - uses: actions/checkout@v7
56
+ with:
57
+ fetch-depth: 0
58
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
59
+ - name: Record the plan on the merged commit; nothing applies
60
+ env:
61
+ BASE_SHA: '${{ github.event.before }}'
62
+ GITHUB_TOKEN: '${{ github.token }}'
63
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
64
+ run: |
65
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
66
+ chant ci run record --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github
67
+ - name: Keep the record report
68
+ if: always()
69
+ uses: actions/upload-artifact@v4
70
+ with:
71
+ name: chant-pr-record
72
+ path: .chant/pr
73
+ if-no-files-found: ignore
74
+ include-hidden-files: true
@@ -0,0 +1,178 @@
1
+ name: chant-pr-prod
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+ push:
8
+ branches:
9
+ - main
10
+
11
+ env:
12
+ CHANT_ENV: prod
13
+
14
+ jobs:
15
+ plan:
16
+ if: github.event_name == 'pull_request'
17
+ runs-on: ubuntu-latest
18
+ container: node:22
19
+ permissions:
20
+ contents: read
21
+ pull-requests: write
22
+ statuses: write
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ with:
26
+ fetch-depth: 0
27
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
28
+ - name: Plan the members this pull request reaches
29
+ env:
30
+ BASE_SHA: '${{ github.event.pull_request.base.sha }}'
31
+ PR_NUMBER: '${{ github.event.pull_request.number }}'
32
+ GITHUB_TOKEN: '${{ github.token }}'
33
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
34
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge github --wave-gate release
35
+ - name: Keep the plan report
36
+ if: always()
37
+ uses: actions/upload-artifact@v4
38
+ with:
39
+ name: chant-pr-plan
40
+ path: .chant/pr
41
+ if-no-files-found: ignore
42
+ include-hidden-files: true
43
+ apply-wave-1:
44
+ if: github.event_name == 'push'
45
+ runs-on: ubuntu-latest
46
+ container: node:22
47
+ concurrency:
48
+ group: chant-apply-prod
49
+ cancel-in-progress: false
50
+ permissions:
51
+ contents: write
52
+ pull-requests: write
53
+ statuses: write
54
+ steps:
55
+ - uses: actions/checkout@v7
56
+ with:
57
+ fetch-depth: 0
58
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
59
+ - name: Restore the newest record of this commit's apply
60
+ uses: actions/cache/restore@v4
61
+ with:
62
+ path: .chant/pr-resume/pr-apply.json
63
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-1
64
+ restore-keys: chant-apply-prod-${{ github.sha }}-
65
+ - name: Apply wave 1, under the approval its plan has
66
+ env:
67
+ BASE_SHA: '${{ github.event.before }}'
68
+ GITHUB_TOKEN: '${{ github.token }}'
69
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
70
+ run: |
71
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
72
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github --wave-gate release --wave 1
73
+ - name: Keep the record for the next wave and a re-run
74
+ if: always()
75
+ uses: actions/cache/save@v4
76
+ with:
77
+ path: .chant/pr-resume/pr-apply.json
78
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-1
79
+ - name: Keep the wave 1 apply report
80
+ if: always()
81
+ uses: actions/upload-artifact@v4
82
+ with:
83
+ name: chant-pr-apply-wave-1
84
+ path: .chant/pr
85
+ if-no-files-found: ignore
86
+ include-hidden-files: true
87
+ apply-wave-2:
88
+ if: github.event_name == 'push'
89
+ runs-on: ubuntu-latest
90
+ needs:
91
+ - apply-wave-1
92
+ container: node:22
93
+ concurrency:
94
+ group: chant-apply-prod
95
+ cancel-in-progress: false
96
+ permissions:
97
+ contents: write
98
+ pull-requests: write
99
+ statuses: write
100
+ steps:
101
+ - uses: actions/checkout@v7
102
+ with:
103
+ fetch-depth: 0
104
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
105
+ - name: Restore the newest record of this commit's apply
106
+ uses: actions/cache/restore@v4
107
+ with:
108
+ path: .chant/pr-resume/pr-apply.json
109
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-2
110
+ restore-keys: chant-apply-prod-${{ github.sha }}-
111
+ - name: Apply wave 2, under the approval its plan has
112
+ env:
113
+ BASE_SHA: '${{ github.event.before }}'
114
+ GITHUB_TOKEN: '${{ github.token }}'
115
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
116
+ run: |
117
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
118
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github --wave-gate release --wave 2
119
+ - name: Keep the record for the next wave and a re-run
120
+ if: always()
121
+ uses: actions/cache/save@v4
122
+ with:
123
+ path: .chant/pr-resume/pr-apply.json
124
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-2
125
+ - name: Keep the wave 2 apply report
126
+ if: always()
127
+ uses: actions/upload-artifact@v4
128
+ with:
129
+ name: chant-pr-apply-wave-2
130
+ path: .chant/pr
131
+ if-no-files-found: ignore
132
+ include-hidden-files: true
133
+ apply-wave-3:
134
+ if: github.event_name == 'push'
135
+ runs-on: ubuntu-latest
136
+ needs:
137
+ - apply-wave-2
138
+ container: node:22
139
+ concurrency:
140
+ group: chant-apply-prod
141
+ cancel-in-progress: false
142
+ permissions:
143
+ contents: write
144
+ pull-requests: write
145
+ statuses: write
146
+ steps:
147
+ - uses: actions/checkout@v7
148
+ with:
149
+ fetch-depth: 0
150
+ - run: command -v tofu >/dev/null 2>&1 || (curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh && sh /tmp/install-opentofu.sh --install-method standalone)
151
+ - name: Restore the newest record of this commit's apply
152
+ uses: actions/cache/restore@v4
153
+ with:
154
+ path: .chant/pr-resume/pr-apply.json
155
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-3
156
+ restore-keys: chant-apply-prod-${{ github.sha }}-
157
+ - name: Apply wave 3, under the approval its plan has
158
+ env:
159
+ BASE_SHA: '${{ github.event.before }}'
160
+ GITHUB_TOKEN: '${{ github.token }}'
161
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
162
+ run: |
163
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
164
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github --wave-gate release --wave 3
165
+ - name: Keep the record for the next wave and a re-run
166
+ if: always()
167
+ uses: actions/cache/save@v4
168
+ with:
169
+ path: .chant/pr-resume/pr-apply.json
170
+ key: chant-apply-prod-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-wave-3
171
+ - name: Keep the wave 3 apply report
172
+ if: always()
173
+ uses: actions/upload-artifact@v4
174
+ with:
175
+ name: chant-pr-apply-wave-3
176
+ path: .chant/pr
177
+ if-no-files-found: ignore
178
+ include-hidden-files: true
@@ -30,7 +30,8 @@ jobs:
30
30
  BASE_SHA: '${{ github.event.pull_request.base.sha }}'
31
31
  PR_NUMBER: '${{ github.event.pull_request.number }}'
32
32
  GITHUB_TOKEN: '${{ github.token }}'
33
- run: chant components pr-plan --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --output .chant/pr --forge github
33
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
34
+ run: chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge github
34
35
  - name: Keep the plan report
35
36
  if: always()
36
37
  uses: actions/upload-artifact@v4
@@ -65,7 +66,10 @@ jobs:
65
66
  env:
66
67
  BASE_SHA: '${{ github.event.before }}'
67
68
  GITHUB_TOKEN: '${{ github.token }}'
68
- run: chant components pr-apply --base "$BASE_SHA" --env prod --gate pr-apply --output .chant/pr --forge github --require-review --resume .chant/pr-resume/pr-apply.json
69
+ CHANT_RUNNER_KEY: '${{ secrets.CHANT_RUNNER_KEY }}'
70
+ run: |
71
+ if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then git fetch origin "main" && BASE_SHA="$(git merge-base FETCH_HEAD "$GITHUB_SHA")"; if [ "$BASE_SHA" = "$GITHUB_SHA" ]; then BASE_SHA="$(git rev-parse --verify --quiet "$GITHUB_SHA^" || echo 4b825dc642cb6eb9a060e54bf8d69288fbee4904)"; fi; fi; export BASE_SHA
72
+ chant ci run apply --in-place --base "$BASE_SHA" --env prod --gate pr-apply --forge github
69
73
  - name: Keep the record for a re-run
70
74
  if: always()
71
75
  uses: actions/cache/save@v4
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { emitYAML, emitYAMLEntry } from "@intentius/chant/yaml";
12
- import { GITHUB_JOB_TOKEN, pushBranches } from "@intentius/chant/ci-jobs";
12
+ import { BUILD_REQUEST_TAGS, GITHUB_JOB_TOKEN, pushBranches } from "@intentius/chant/ci-jobs";
13
13
  import type { OpTrigger } from "@intentius/chant/lexicon";
14
14
  import { actionRef } from "../action-pins";
15
15
 
@@ -70,10 +70,24 @@ export function onTrigger(trigger: OpTrigger): Record<string, unknown> {
70
70
  case "pull_request":
71
71
  return { pull_request: trigger.branches && trigger.branches.length > 0 ? { branches: trigger.branches } : {} };
72
72
  case "push":
73
- return { push: { branches: pushBranches(trigger.branches) } };
73
+ // #4169: a newest build runs at each build request's tag too, and by hand.
74
+ return trigger.build === "newest"
75
+ ? { push: { branches: pushBranches(trigger.branches), tags: [BUILD_REQUEST_TAGS] }, workflow_dispatch: {} }
76
+ : { push: { branches: pushBranches(trigger.branches) } };
74
77
  }
75
78
  }
76
79
 
80
+ /**
81
+ * A push Op's concurrency group (#4169): one per commit under `build:
82
+ * "every"`, so no push cancels or replaces another's run; one per ref under
83
+ * `newest`, so the newest push waits and each build request's tag runs on its
84
+ * own; and the Op's own name otherwise, one run at a time.
85
+ */
86
+ export function opConcurrencyGroup(jobName: string, trigger: OpTrigger): string {
87
+ if (trigger.kind !== "push" || !trigger.build) return jobName;
88
+ return trigger.build === "every" ? `${jobName}-\${{ github.sha }}` : `${jobName}-\${{ github.ref }}`;
89
+ }
90
+
77
91
  /**
78
92
  * A workflow document before emission. Every generator's document is one of
79
93
  * these; each leaves out the sections it has no use for.
@@ -208,6 +208,17 @@ describe("generateGithubOpPipeline: trigger kinds (#2084)", () => {
208
208
  expect(result.jobs[0].trigger).toEqual({ kind: "push", branches: ["release"] });
209
209
  });
210
210
 
211
+ test("a push trigger's build (#4169): newest runs at build request tags and by hand, one group per ref; every, one group per commit", () => {
212
+ const newest = parseFile(generateGithubOpPipeline([{ name: "suites", trigger: { kind: "push", build: "newest" } }]).files[0].yaml);
213
+ expect(newest.on).toEqual({ push: { branches: ["main"], tags: ["ci/build/**"] }, workflow_dispatch: {} });
214
+ expect(newest.concurrency).toEqual({ group: "suites-${{ github.ref }}", "cancel-in-progress": false });
215
+ const every = parseFile(generateGithubOpPipeline([{ name: "suites", trigger: { kind: "push", build: "every" } }]).files[0].yaml);
216
+ expect(every.on).toEqual({ push: { branches: ["main"] } });
217
+ expect(every.concurrency).toEqual({ group: "suites-${{ github.sha }}", "cancel-in-progress": false });
218
+ const plain = parseFile(generateGithubOpPipeline([{ name: "suites", trigger: { kind: "push" } }]).files[0].yaml);
219
+ expect(plain.concurrency).toEqual({ group: "suites", "cancel-in-progress": false });
220
+ });
221
+
211
222
  test("push trigger defaults to the repository default branch (main) when branches is omitted", () => {
212
223
  const result = generateGithubOpPipeline([{ name: "tf-apply", trigger: { kind: "push" } }]);
213
224
  const doc = parseFile(result.files[0].yaml);
@@ -95,7 +95,7 @@ import type {
95
95
  PipelineMember,
96
96
  ScheduledOpSpec,
97
97
  } from "@intentius/chant/lexicon";
98
- import { checkoutStep, concurrency, emitWorkflowYAML, jobTokenEnv, onTrigger, runSteps } from "./ci-jobs";
98
+ import { checkoutStep, concurrency, emitWorkflowYAML, jobTokenEnv, onTrigger, opConcurrencyGroup, runSteps } from "./ci-jobs";
99
99
 
100
100
  export type { GenerateGithubOpOptions, GenerateGithubOpResult };
101
101
 
@@ -455,8 +455,8 @@ export function buildGithubOpPipelineDocs(
455
455
  on: onTrigger(trigger),
456
456
  ...nonEmpty("env", options.variables),
457
457
  // One run at a time per Op — a slow audit must not overlap its own next
458
- // scheduled trigger.
459
- concurrency: concurrency(jobName),
458
+ // scheduled trigger. A push Op's `build` (#4169) widens the group.
459
+ concurrency: concurrency(opConcurrencyGroup(jobName, trigger)),
460
460
  permissions: mergePermissions(
461
461
  spec.name,
462
462
  permissionsFor(findingMode, trigger),