@intentius/chant-lexicon-github 0.124.31 → 0.124.32

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 (151) 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.js +84 -0
  13. package/dist/components/generate-op-pipeline.js +440 -0
  14. package/dist/components/generate-op-waves-pipeline.js +158 -0
  15. package/dist/components/generate-pipeline.d.ts.map +1 -1
  16. package/dist/components/generate-pipeline.js +476 -0
  17. package/dist/composites/cache.js +17 -0
  18. package/dist/composites/catalog.js +859 -0
  19. package/dist/composites/checkout.js +25 -0
  20. package/dist/composites/dependabot.js +43 -0
  21. package/dist/composites/deploy-environment.js +38 -0
  22. package/dist/composites/docker-build.js +70 -0
  23. package/dist/composites/download-artifact.js +18 -0
  24. package/dist/composites/go-ci.js +62 -0
  25. package/dist/composites/index.js +15 -0
  26. package/dist/composites/node-ci.js +44 -0
  27. package/dist/composites/node-pipeline.js +96 -0
  28. package/dist/composites/pr-plan-report.js +129 -0
  29. package/dist/composites/python-ci.js +60 -0
  30. package/dist/composites/setup-go.js +18 -0
  31. package/dist/composites/setup-node.js +21 -0
  32. package/dist/composites/setup-python.js +18 -0
  33. package/dist/composites/upload-artifact.js +19 -0
  34. package/dist/coverage.js +32 -0
  35. package/dist/expression.js +177 -0
  36. package/dist/generated/index.js +26 -0
  37. package/dist/generated/runtime.js +4 -0
  38. package/dist/graph-edges.js +174 -0
  39. package/dist/import/generator.js +108 -0
  40. package/dist/import/parser.js +69 -0
  41. package/dist/index.js +22 -0
  42. package/dist/integrity.json +2 -2
  43. package/dist/lint/audit-catalog.js +69 -0
  44. package/dist/lint/audit-lineage.js +168 -0
  45. package/dist/lint/post-synth/gha006.js +50 -0
  46. package/dist/lint/post-synth/gha009.js +35 -0
  47. package/dist/lint/post-synth/gha011.js +34 -0
  48. package/dist/lint/post-synth/gha013.js +40 -0
  49. package/dist/lint/post-synth/gha017.js +25 -0
  50. package/dist/lint/post-synth/gha018.js +33 -0
  51. package/dist/lint/post-synth/gha019.js +58 -0
  52. package/dist/lint/post-synth/gha021.js +42 -0
  53. package/dist/lint/post-synth/gha022.js +44 -0
  54. package/dist/lint/post-synth/gha023.js +37 -0
  55. package/dist/lint/post-synth/gha024.js +32 -0
  56. package/dist/lint/post-synth/gha025.js +33 -0
  57. package/dist/lint/post-synth/gha026.js +32 -0
  58. package/dist/lint/post-synth/gha027.js +46 -0
  59. package/dist/lint/post-synth/gha028.js +28 -0
  60. package/dist/lint/post-synth/gha029.js +56 -0
  61. package/dist/lint/post-synth/gha030.js +35 -0
  62. package/dist/lint/post-synth/gha031.js +75 -0
  63. package/dist/lint/post-synth/gha032.js +35 -0
  64. package/dist/lint/post-synth/gha033.js +39 -0
  65. package/dist/lint/post-synth/gha034.js +35 -0
  66. package/dist/lint/post-synth/gha035.js +50 -0
  67. package/dist/lint/post-synth/gha036.js +37 -0
  68. package/dist/lint/post-synth/gha037.js +41 -0
  69. package/dist/lint/post-synth/gha038.js +35 -0
  70. package/dist/lint/post-synth/gha039.js +34 -0
  71. package/dist/lint/post-synth/gha040.js +36 -0
  72. package/dist/lint/post-synth/gha041.js +32 -0
  73. package/dist/lint/post-synth/gha042.js +33 -0
  74. package/dist/lint/post-synth/gha043.js +35 -0
  75. package/dist/lint/post-synth/gha044.js +36 -0
  76. package/dist/lint/post-synth/gha045.js +33 -0
  77. package/dist/lint/post-synth/gha046.js +55 -0
  78. package/dist/lint/post-synth/gha047.js +44 -0
  79. package/dist/lint/post-synth/gha048.js +34 -0
  80. package/dist/lint/post-synth/gha049.js +36 -0
  81. package/dist/lint/post-synth/gha050.js +37 -0
  82. package/dist/lint/post-synth/gha051.js +38 -0
  83. package/dist/lint/post-synth/gha052.js +35 -0
  84. package/dist/lint/post-synth/gha053.js +34 -0
  85. package/dist/lint/post-synth/gha054.js +35 -0
  86. package/dist/lint/post-synth/gha055.js +44 -0
  87. package/dist/lint/post-synth/gha056.js +32 -0
  88. package/dist/lint/post-synth/gha057.js +35 -0
  89. package/dist/lint/post-synth/gha058.js +42 -0
  90. package/dist/lint/post-synth/gha059.js +119 -0
  91. package/dist/lint/post-synth/gha060.js +129 -0
  92. package/dist/lint/post-synth/gha061.js +78 -0
  93. package/dist/lint/post-synth/gha062.js +65 -0
  94. package/dist/lint/post-synth/gha063.js +56 -0
  95. package/dist/lint/post-synth/gha064.js +47 -0
  96. package/dist/lint/post-synth/gha065.js +125 -0
  97. package/dist/lint/post-synth/gha066.js +40 -0
  98. package/dist/lint/post-synth/gha067.js +75 -0
  99. package/dist/lint/post-synth/gha068.js +47 -0
  100. package/dist/lint/post-synth/gha069.js +99 -0
  101. package/dist/lint/post-synth/index.js +114 -0
  102. package/dist/lint/post-synth/yaml-helpers.js +465 -0
  103. package/dist/lint/rules/data/action-usage-policy.js +16 -0
  104. package/dist/lint/rules/data/advisory-feed.js +15 -0
  105. package/dist/lint/rules/data/deprecated-versions.js +14 -0
  106. package/dist/lint/rules/data/flagged-actions.js +18 -0
  107. package/dist/lint/rules/data/known-action-slugs.js +38 -0
  108. package/dist/lint/rules/data/known-actions.js +12 -0
  109. package/dist/lint/rules/data/preinstalled-tools.js +36 -0
  110. package/dist/lint/rules/data/recommended-inputs.js +9 -0
  111. package/dist/lint/rules/data/risky-features.js +22 -0
  112. package/dist/lint/rules/data/secret-patterns.js +30 -0
  113. package/dist/lint/rules/data/trusted-action-owners.js +12 -0
  114. package/dist/lint/rules/data/untrusted-contexts.js +44 -0
  115. package/dist/lint/rules/deprecated-action-version.js +44 -0
  116. package/dist/lint/rules/detect-secrets.js +45 -0
  117. package/dist/lint/rules/extract-inline-structs.js +55 -0
  118. package/dist/lint/rules/file-job-limit.js +43 -0
  119. package/dist/lint/rules/index.js +16 -0
  120. package/dist/lint/rules/job-timeout.js +53 -0
  121. package/dist/lint/rules/missing-recommended-inputs.js +51 -0
  122. package/dist/lint/rules/no-hardcoded-secrets.js +39 -0
  123. package/dist/lint/rules/no-raw-expressions.js +44 -0
  124. package/dist/lint/rules/suggest-cache.js +60 -0
  125. package/dist/lint/rules/use-condition-builders.js +37 -0
  126. package/dist/lint/rules/use-matrix-builder.js +36 -0
  127. package/dist/lint/rules/use-typed-actions.js +41 -0
  128. package/dist/lint/rules/validate-concurrency.js +59 -0
  129. package/dist/lsp/completions.d.ts.map +1 -1
  130. package/dist/lsp/completions.js +18 -0
  131. package/dist/lsp/hover.d.ts.map +1 -1
  132. package/dist/lsp/hover.js +33 -0
  133. package/dist/manifest.json +1 -1
  134. package/dist/mcp/context-tools.js +152 -0
  135. package/dist/package-cli.js +20 -0
  136. package/dist/plugin.js +344 -0
  137. package/dist/serializer.js +572 -0
  138. package/dist/spec/fetch.js +46 -0
  139. package/dist/validate-cli.js +16 -0
  140. package/dist/validate.js +38 -0
  141. package/dist/variables.js +41 -0
  142. package/package.json +6 -6
  143. package/src/codegen/package.ts +2 -2
  144. package/src/components/__fixtures__/pr-loop-build.github.golden.yml +6 -2
  145. package/src/components/__fixtures__/pr-loop-waves.github.golden.yml +178 -0
  146. package/src/components/__fixtures__/pr-loop.github.golden.yml +6 -2
  147. package/src/components/generate-pipeline.ts +66 -29
  148. package/src/components/generate-pr-pipeline.test.ts +44 -20
  149. package/src/components/generate-pr-waves-pipeline.test.ts +117 -0
  150. package/src/lsp/completions.ts +2 -1
  151. package/src/lsp/hover.ts +2 -1
@@ -0,0 +1,25 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const Checkout = Composite((props) => {
5
+ const { defaults } = props;
6
+ const withObj = {};
7
+ if (props.ref !== undefined)
8
+ withObj.ref = props.ref;
9
+ if (props.repository !== undefined)
10
+ withObj.repository = props.repository;
11
+ if (props.fetchDepth !== undefined)
12
+ withObj["fetch-depth"] = String(props.fetchDepth);
13
+ if (props.token !== undefined)
14
+ withObj.token = props.token;
15
+ if (props.submodules !== undefined)
16
+ withObj.submodules = String(props.submodules);
17
+ if (props.sshKey !== undefined)
18
+ withObj["ssh-key"] = props.sshKey;
19
+ const step = new Step(mergeDefaults({
20
+ name: "Checkout",
21
+ uses: actionRef("actions/checkout", props.pin),
22
+ ...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
23
+ }, defaults?.step));
24
+ return { step };
25
+ }, "Checkout");
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Dependency-update configuration (`.github/dependabot.yml`) as a chant
3
+ * resource, so it is emitted and lintable like workflows (#294).
4
+ *
5
+ * Two failure modes the post-synth checks target:
6
+ * - an update that executes untrusted code from a freshly-pulled dependency
7
+ * during the update itself (`insecure-external-code-execution: allow`), and
8
+ * - a configuration with no cooldown — a version published seconds ago
9
+ * (including a compromised one) is adopted before anyone can react.
10
+ *
11
+ * The `Dependabot` composite ships safe defaults: a cooldown window and an
12
+ * explicit deny on external code execution.
13
+ */
14
+ var _a;
15
+ import { DECLARABLE_MARKER } from "@intentius/chant/declarable";
16
+ /** The `.github/dependabot.yml` resource. */
17
+ export class DependabotConfig {
18
+ constructor(props) {
19
+ this[_a] = true;
20
+ this.lexicon = "github";
21
+ this.entityType = "GitHub::Dependabot::Config";
22
+ this.kind = "resource";
23
+ this.props = { version: 2, ...props };
24
+ }
25
+ }
26
+ _a = DECLARABLE_MARKER;
27
+ /**
28
+ * Build a `DependabotConfig` with safe defaults: a cooldown window on every
29
+ * ecosystem and external code execution explicitly denied.
30
+ */
31
+ export function Dependabot(props) {
32
+ const cooldownDays = props.cooldownDays ?? 7;
33
+ const limit = props.openPullRequestsLimit ?? 5;
34
+ const updates = props.ecosystems.map((e) => ({
35
+ packageEcosystem: e.packageEcosystem,
36
+ directory: e.directory ?? "/",
37
+ schedule: { interval: e.interval ?? "weekly" },
38
+ cooldown: { defaultDays: cooldownDays },
39
+ openPullRequestsLimit: limit,
40
+ insecureExternalCodeExecution: "deny",
41
+ }));
42
+ return new DependabotConfig({ version: 2, updates });
43
+ }
@@ -0,0 +1,38 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const DeployEnvironment = Composite((props) => {
5
+ const { name, deployScript, cleanupScript = 'echo "Cleaning up..."', url, concurrencyGroup, cancelInProgress = true, runsOn = "ubuntu-latest", defaults, } = props;
6
+ const group = concurrencyGroup ?? `deploy-${name}`;
7
+ const deployScriptArr = Array.isArray(deployScript) ? deployScript : [deployScript];
8
+ const cleanupScriptArr = Array.isArray(cleanupScript) ? cleanupScript : [cleanupScript];
9
+ // ── Deploy job ─────────────────────────────────────────────────────
10
+ const deploySteps = [
11
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
12
+ ...deployScriptArr.map((cmd) => new Step({ name: "Deploy", run: cmd })),
13
+ ];
14
+ const environment = { name };
15
+ if (url) {
16
+ environment.url = url;
17
+ }
18
+ const deployJob = new Job(mergeDefaults({
19
+ "runs-on": runsOn,
20
+ environment,
21
+ concurrency: {
22
+ group,
23
+ "cancel-in-progress": cancelInProgress,
24
+ },
25
+ steps: deploySteps,
26
+ }, defaults?.deployJob));
27
+ // ── Cleanup job ────────────────────────────────────────────────────
28
+ const cleanupSteps = [
29
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
30
+ ...cleanupScriptArr.map((cmd) => new Step({ name: "Cleanup", run: cmd })),
31
+ ];
32
+ const cleanupJob = new Job(mergeDefaults({
33
+ "runs-on": runsOn,
34
+ environment: { name },
35
+ steps: cleanupSteps,
36
+ }, defaults?.cleanupJob));
37
+ return { deployJob, cleanupJob };
38
+ }, "DeployEnvironment");
@@ -0,0 +1,70 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Workflow } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const DockerBuild = Composite((props) => {
5
+ const { tag = "${{ github.sha }}", dockerfile = "Dockerfile", context = ".", registry = "ghcr.io", imageName = "ghcr.io/${{ github.repository }}", tagLatest = true, buildArgs, push = true, platforms, runsOn = "ubuntu-latest", defaults, } = props;
6
+ // ── Steps ──────────────────────────────────────────────────────────
7
+ const checkout = new Step({ name: "Checkout", uses: actionRef("actions/checkout") });
8
+ const login = new Step({
9
+ name: "Log in to container registry",
10
+ uses: "docker/login-action@v3",
11
+ with: {
12
+ registry,
13
+ username: "${{ github.actor }}",
14
+ password: "${{ secrets.GITHUB_TOKEN }}",
15
+ },
16
+ });
17
+ const setupBuildx = new Step({
18
+ name: "Set up Docker Buildx",
19
+ uses: "docker/setup-buildx-action@v3",
20
+ });
21
+ // Build tags for metadata action
22
+ const tags = [`${imageName}:${tag}`];
23
+ if (tagLatest) {
24
+ tags.push(`${imageName}:latest`);
25
+ }
26
+ const metadata = new Step({
27
+ name: "Extract metadata",
28
+ id: "meta",
29
+ uses: "docker/metadata-action@v5",
30
+ with: {
31
+ images: imageName,
32
+ tags: tags.join("\n"),
33
+ },
34
+ });
35
+ const buildPushWith = {
36
+ context,
37
+ file: dockerfile,
38
+ push: String(push),
39
+ tags: "${{ steps.meta.outputs.tags }}",
40
+ labels: "${{ steps.meta.outputs.labels }}",
41
+ };
42
+ if (platforms && platforms.length > 0) {
43
+ buildPushWith.platforms = platforms.join(",");
44
+ }
45
+ if (buildArgs) {
46
+ buildPushWith["build-args"] = Object.entries(buildArgs)
47
+ .map(([k, v]) => `${k}=${v}`)
48
+ .join("\n");
49
+ }
50
+ const buildPush = new Step({
51
+ name: "Build and push",
52
+ uses: "docker/build-push-action@v6",
53
+ with: buildPushWith,
54
+ });
55
+ const job = new Job(mergeDefaults({
56
+ "runs-on": runsOn,
57
+ steps: [checkout, login, setupBuildx, metadata, buildPush],
58
+ }, defaults?.job));
59
+ const workflow = new Workflow(mergeDefaults({
60
+ name: "Docker Build",
61
+ on: {
62
+ push: { branches: ["main"] },
63
+ },
64
+ permissions: {
65
+ contents: "read",
66
+ packages: "write",
67
+ },
68
+ }, defaults?.workflow));
69
+ return { workflow, job };
70
+ }, "DockerBuild");
@@ -0,0 +1,18 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ export const DownloadArtifact = Composite((props) => {
4
+ const { defaults } = props;
5
+ const withObj = {};
6
+ if (props.name !== undefined)
7
+ withObj.name = props.name;
8
+ if (props.path !== undefined)
9
+ withObj.path = props.path;
10
+ if (props.mergeMultiple !== undefined)
11
+ withObj["merge-multiple"] = String(props.mergeMultiple);
12
+ const step = new Step(mergeDefaults({
13
+ name: "Download Artifact",
14
+ uses: "actions/download-artifact@v4",
15
+ ...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
16
+ }, defaults?.step));
17
+ return { step };
18
+ }, "DownloadArtifact");
@@ -0,0 +1,62 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Workflow } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const GoCI = Composite((props) => {
5
+ const { goVersion = "1.22", testCommand = "go test ./... -v -race", buildCommand = "go build ./...", lintCommand = "golangci-lint run", runsOn = "ubuntu-latest", defaults, } = props;
6
+ // ── Build job ──────────────────────────────────────────────────────
7
+ const buildJob = new Job(mergeDefaults({
8
+ "runs-on": runsOn,
9
+ steps: [
10
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
11
+ new Step({
12
+ name: "Setup Go",
13
+ uses: "actions/setup-go@v5",
14
+ with: { "go-version": goVersion },
15
+ }),
16
+ new Step({ name: "Build", run: buildCommand }),
17
+ ],
18
+ }, defaults?.buildJob));
19
+ // ── Test job ───────────────────────────────────────────────────────
20
+ const testJob = new Job(mergeDefaults({
21
+ "runs-on": runsOn,
22
+ steps: [
23
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
24
+ new Step({
25
+ name: "Setup Go",
26
+ uses: "actions/setup-go@v5",
27
+ with: { "go-version": goVersion },
28
+ }),
29
+ new Step({ name: "Test", run: testCommand }),
30
+ ],
31
+ }, defaults?.testJob));
32
+ // ── Lint job (optional) ────────────────────────────────────────────
33
+ const lintJob = lintCommand !== null
34
+ ? new Job(mergeDefaults({
35
+ "runs-on": runsOn,
36
+ steps: [
37
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
38
+ new Step({
39
+ name: "Setup Go",
40
+ uses: "actions/setup-go@v5",
41
+ with: { "go-version": goVersion },
42
+ }),
43
+ new Step({
44
+ name: "Lint",
45
+ uses: "golangci/golangci-lint-action@v6",
46
+ with: { args: lintCommand },
47
+ }),
48
+ ],
49
+ }, defaults?.lintJob))
50
+ : undefined;
51
+ const workflow = new Workflow(mergeDefaults({
52
+ name: "Go CI",
53
+ on: {
54
+ push: { branches: ["main"] },
55
+ pull_request: { branches: ["main"] },
56
+ },
57
+ }, defaults?.workflow));
58
+ if (lintJob) {
59
+ return { workflow, buildJob, testJob, lintJob };
60
+ }
61
+ return { workflow, buildJob, testJob };
62
+ }, "GoCI");
@@ -0,0 +1,15 @@
1
+ export { Checkout } from "./checkout.js";
2
+ export { SetupNode } from "./setup-node.js";
3
+ export { SetupGo } from "./setup-go.js";
4
+ export { SetupPython } from "./setup-python.js";
5
+ export { CacheAction } from "./cache.js";
6
+ export { UploadArtifact } from "./upload-artifact.js";
7
+ export { DownloadArtifact } from "./download-artifact.js";
8
+ export { NodeCI } from "./node-ci.js";
9
+ export { NodePipeline, BunPipeline, PnpmPipeline, YarnPipeline } from "./node-pipeline.js";
10
+ export { PythonCI } from "./python-ci.js";
11
+ export { DockerBuild } from "./docker-build.js";
12
+ export { DeployEnvironment } from "./deploy-environment.js";
13
+ export { GoCI } from "./go-ci.js";
14
+ export { Dependabot, DependabotConfig } from "./dependabot.js";
15
+ export { PrPlanReport } from "./pr-plan-report.js";
@@ -0,0 +1,44 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Workflow } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const NodeCI = Composite((props) => {
5
+ const { nodeVersion = "22", packageManager = "npm", buildScript = "build", testScript = "test", installCommand, defaults, } = props;
6
+ const install = installCommand ?? (packageManager === "npm" ? "npm ci" : `${packageManager} install`);
7
+ const run = packageManager === "npm" ? "npm run" : packageManager;
8
+ const checkoutStep = new Step({
9
+ name: "Checkout",
10
+ uses: actionRef("actions/checkout"),
11
+ });
12
+ const setupNodeStep = new Step({
13
+ name: "Setup Node.js",
14
+ uses: actionRef("actions/setup-node"),
15
+ with: {
16
+ "node-version": nodeVersion,
17
+ cache: packageManager === "bun" ? undefined : packageManager,
18
+ },
19
+ });
20
+ const installStep = new Step({
21
+ name: "Install dependencies",
22
+ run: install,
23
+ });
24
+ const buildStep = new Step({
25
+ name: "Build",
26
+ run: `${run} ${buildScript}`,
27
+ });
28
+ const testStep = new Step({
29
+ name: "Test",
30
+ run: `${run} ${testScript}`,
31
+ });
32
+ const job = new Job(mergeDefaults({
33
+ "runs-on": "ubuntu-latest",
34
+ steps: [checkoutStep, setupNodeStep, installStep, buildStep, testStep],
35
+ }, defaults?.job));
36
+ const workflow = new Workflow(mergeDefaults({
37
+ name: "CI",
38
+ on: {
39
+ push: { branches: ["main"] },
40
+ pull_request: { branches: ["main"] },
41
+ },
42
+ }, defaults?.workflow));
43
+ return { workflow, job };
44
+ }, "NodeCI");
@@ -0,0 +1,96 @@
1
+ import { Composite, withDefaults, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Workflow } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ const cacheConfig = {
5
+ npm: {
6
+ cache: "npm",
7
+ installCmd: "npm ci",
8
+ runPrefix: "npm run",
9
+ setupAction: actionRef("actions/setup-node"),
10
+ },
11
+ pnpm: {
12
+ cache: "pnpm",
13
+ installCmd: "pnpm install --frozen-lockfile",
14
+ runPrefix: "pnpm run",
15
+ setupAction: actionRef("actions/setup-node"),
16
+ },
17
+ yarn: {
18
+ cache: "yarn",
19
+ installCmd: "yarn install --frozen-lockfile",
20
+ runPrefix: "yarn",
21
+ setupAction: actionRef("actions/setup-node"),
22
+ },
23
+ bun: {
24
+ cache: undefined,
25
+ installCmd: "bun install --frozen-lockfile",
26
+ runPrefix: "bun run",
27
+ setupAction: "oven-sh/setup-bun@v2",
28
+ },
29
+ };
30
+ export const NodePipeline = Composite((props) => {
31
+ const { nodeVersion = "22", packageManager = "npm", buildScript = "build", testScript = "test", installCommand, buildArtifactPaths = ["dist/"], artifactName = "build-output", artifactRetentionDays = 1, runsOn = "ubuntu-latest", defaults, } = props;
32
+ const pm = cacheConfig[packageManager];
33
+ const install = installCommand ?? pm.installCmd;
34
+ const run = pm.runPrefix;
35
+ const isBun = packageManager === "bun";
36
+ // ── Build job steps ────────────────────────────────────────────────
37
+ const buildCheckout = new Step({ name: "Checkout", uses: actionRef("actions/checkout") });
38
+ const buildSetup = isBun
39
+ ? new Step({ name: "Setup Bun", uses: "oven-sh/setup-bun@v2" })
40
+ : new Step({
41
+ name: "Setup Node.js",
42
+ uses: actionRef("actions/setup-node"),
43
+ with: { "node-version": nodeVersion, cache: pm.cache },
44
+ });
45
+ const buildInstall = new Step({ name: "Install dependencies", run: install });
46
+ const buildRun = new Step({ name: "Build", run: `${run} ${buildScript}` });
47
+ const buildUpload = new Step({
48
+ name: "Upload build artifacts",
49
+ uses: "actions/upload-artifact@v4",
50
+ with: {
51
+ name: artifactName,
52
+ path: buildArtifactPaths.join("\n"),
53
+ "retention-days": String(artifactRetentionDays),
54
+ },
55
+ });
56
+ const buildJob = new Job(mergeDefaults({
57
+ "runs-on": runsOn,
58
+ steps: [buildCheckout, buildSetup, buildInstall, buildRun, buildUpload],
59
+ }, defaults?.buildJob));
60
+ // ── Test job steps ─────────────────────────────────────────────────
61
+ const testCheckout = new Step({ name: "Checkout", uses: actionRef("actions/checkout") });
62
+ const testSetup = isBun
63
+ ? new Step({ name: "Setup Bun", uses: "oven-sh/setup-bun@v2" })
64
+ : new Step({
65
+ name: "Setup Node.js",
66
+ uses: actionRef("actions/setup-node"),
67
+ with: { "node-version": nodeVersion, cache: pm.cache },
68
+ });
69
+ const testInstall = new Step({ name: "Install dependencies", run: install });
70
+ const testDownload = new Step({
71
+ name: "Download build artifacts",
72
+ uses: "actions/download-artifact@v4",
73
+ with: { name: artifactName },
74
+ });
75
+ const testRun = new Step({ name: "Test", run: `${run} ${testScript}` });
76
+ const testJob = new Job(mergeDefaults({
77
+ "runs-on": runsOn,
78
+ needs: ["build"],
79
+ steps: [testCheckout, testSetup, testInstall, testDownload, testRun],
80
+ }, defaults?.testJob));
81
+ // ── Workflow ───────────────────────────────────────────────────────
82
+ const workflow = new Workflow(mergeDefaults({
83
+ name: "Node Pipeline",
84
+ on: {
85
+ push: { branches: ["main"] },
86
+ pull_request: { branches: ["main"] },
87
+ },
88
+ }, defaults?.workflow));
89
+ return { workflow, buildJob, testJob };
90
+ }, "NodePipeline");
91
+ /** NodePipeline preset for Bun projects. */
92
+ export const BunPipeline = withDefaults(NodePipeline, { packageManager: "bun" });
93
+ /** NodePipeline preset for pnpm projects. */
94
+ export const PnpmPipeline = withDefaults(NodePipeline, { packageManager: "pnpm" });
95
+ /** NodePipeline preset for Yarn projects. */
96
+ export const YarnPipeline = withDefaults(NodePipeline, { packageManager: "yarn" });
@@ -0,0 +1,129 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Permissions } from "../generated/index.js";
3
+ import { Checkout } from "./checkout.js";
4
+ import { SetupNode } from "./setup-node.js";
5
+ /**
6
+ * Sticky-comment script (#1223's mechanism), find the comment whose body
7
+ * starts with `$MARKER`, PATCH it if found, POST otherwise. No marketplace
8
+ * action, nothing extra to pin — `gh` ships on GitHub's hosted runners. The
9
+ * flag is `-F`, not `-f`: `gh api`'s `-F/--field` is the typed form that
10
+ * reads the value from a file when it starts with `@`, while `-f/--raw-field`
11
+ * adds the parameter as a literal string, so the `-f` form posted the eight
12
+ * characters `@plan.md` (#2236). Reading the body from the file the plan step
13
+ * wrote means a large or multi-line plan never has to survive shell quoting.
14
+ * `-F`'s type coercion of `true`/`false`/`null`/integers does not reach the
15
+ * body: gh resolves the leading `@` first and hands back the file's bytes as
16
+ * a string.
17
+ *
18
+ * `$api_base` (chant #2305) is `$GITHUB_API_URL` with any trailing slash
19
+ * trimmed, falling back to `https://api.github.com` when it is unset —
20
+ * `$GITHUB_API_URL` is already correct on every GitHub Actions and Forgejo
21
+ * Actions job (`https://api.github.com` on github.com, `<host>/api/v3` on
22
+ * GitHub Enterprise Server, `<host>/api/v1` on Forgejo), the same fact
23
+ * `githubApiBaseFrom` in `packages/core/src/op/activities/reconcile.ts`
24
+ * builds on for `reconcilePr`'s `postOrUpdateComment` (#2291). This script
25
+ * cannot import that helper — it is a shell string emitted into YAML, not a
26
+ * TypeScript call — so the equivalent base-URL construction is inlined here
27
+ * instead. Without it, every `gh api` call below took a bare relative path,
28
+ * which `gh` resolves against `/api/v3` for any host but github.com; that
29
+ * guess is right for GitHub Enterprise Server but wrong for Forgejo, which
30
+ * does not serve `/api/v3` and answered 404 for GET and POST alike. On
31
+ * github.com and GHES the resolved URL is byte-identical to `gh`'s own
32
+ * guess, so this changes nothing there.
33
+ *
34
+ * The step that runs it sets the token twice, as `GH_TOKEN` and as
35
+ * `GH_ENTERPRISE_TOKEN` (chant #2352). `gh` sends `GH_TOKEN` only to
36
+ * github.com and `ghe.com` hosts and reads `GH_ENTERPRISE_TOKEN` for every
37
+ * other host, Forgejo and GitHub Enterprise Server included. With `GH_TOKEN`
38
+ * alone, this script's POST to a real Forgejo 16.0.5 instance went out with
39
+ * no `Authorization` header and answered 401 `token is required`; with both
40
+ * set it answered 201, and a second run PATCHed the same comment. On
41
+ * github.com `GH_TOKEN` still wins, so nothing changes there. The same fix
42
+ * `ghCredentialEnv` made for `reconcilePr` at runtime (#2333).
43
+ */
44
+ const stickyCommentScript = [
45
+ 'api_base="${GITHUB_API_URL%/}"',
46
+ 'api_base="${api_base:-https://api.github.com}"',
47
+ 'comment_id=$(gh api "$api_base/repos/$REPO/issues/$PR_NUMBER/comments" --paginate ' +
48
+ '--jq "map(select(.body | startswith(\\"$MARKER\\"))) | .[0].id // empty")',
49
+ 'if [ -n "$comment_id" ]; then',
50
+ ' gh api -X PATCH "$api_base/repos/$REPO/issues/comments/$comment_id" -F body=@plan.md > /dev/null',
51
+ "else",
52
+ ' gh api -X POST "$api_base/repos/$REPO/issues/$PR_NUMBER/comments" -F body=@plan.md > /dev/null',
53
+ "fi",
54
+ ].join("\n");
55
+ /**
56
+ * A CI job that builds, plans, and posts `chant lifecycle plan --report
57
+ * markdown` as a sticky PR comment (#1983) — the compiled diff reaches the
58
+ * reviewer without being asked for.
59
+ *
60
+ * A sibling of the gitlab lexicon's `MrPlanReport`, but where GitLab renders
61
+ * a native merge-request widget from a counts artifact, GitHub has no such
62
+ * widget: the mechanism is the sticky-comment recipe `examples/github-pr-preview`
63
+ * proved (#1223), reused here rather than reinvented. The forgejo lexicon
64
+ * inherits this composite through its github re-export, since Forgejo Actions
65
+ * runs the same workflow shape and the Forgejo API accepts the same `gh api`
66
+ * calls against its GitHub-compatible surface — once they target a full URL
67
+ * rather than a bare relative path, which `stickyCommentScript` now does
68
+ * (#2305, the same fix #2291 made for `reconcilePr`'s `postOrUpdateComment`).
69
+ *
70
+ * Only meaningful on a `pull_request`-triggered workflow — the job guards on
71
+ * `github.event_name` since the comment targets `github.event.number`, which
72
+ * a push-triggered run does not have. The plan reads the live system, so wire
73
+ * cloud credentials via `before` or CI variables.
74
+ */
75
+ export const PrPlanReport = Composite((props) => {
76
+ const { environment, lexicon, ownedOnly = false, runsOn = "ubuntu-latest", nodeVersion = "22", installCommand = "npm ci", before, postComment = true, marker = `<!-- chant-pr-plan-report:${environment} -->`, defaults: defs, } = props;
77
+ const planArgs = [
78
+ "lifecycle",
79
+ "plan",
80
+ environment,
81
+ ...(lexicon ? [lexicon] : []),
82
+ ...(ownedOnly ? ["--owned"] : []),
83
+ "--report",
84
+ "markdown",
85
+ ].join(" ");
86
+ const steps = [
87
+ Checkout({}).step,
88
+ SetupNode({ nodeVersion, cache: "npm" }).step,
89
+ new Step({ name: "Install", run: installCommand }),
90
+ ...(before ?? []).map((cmd) => new Step({ name: "Live credentials", run: cmd })),
91
+ new Step({ name: "Build", run: "npx chant build" }),
92
+ new Step({
93
+ name: `Plan ${environment}`,
94
+ // The marker (and an "updated for" footer, same spirit as the preview
95
+ // tutorial's) is prepended to the rendered plan before anything is
96
+ // posted, so the comment body itself carries the identity the search
97
+ // step below keys on.
98
+ run: [
99
+ `npx chant ${planArgs} > plan-body.md`,
100
+ '{ printf \'%s\\n\\n\' "$MARKER"; printf \'_Updated for %s._\\n\\n\' "$HEAD_SHA"; cat plan-body.md; } > plan.md',
101
+ ].join("\n"),
102
+ }),
103
+ ...(postComment
104
+ ? [
105
+ new Step({
106
+ name: "Post or update PR comment",
107
+ env: {
108
+ GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}",
109
+ GH_ENTERPRISE_TOKEN: "${{ secrets.GITHUB_TOKEN }}",
110
+ },
111
+ run: stickyCommentScript,
112
+ }),
113
+ ]
114
+ : []),
115
+ ];
116
+ const job = new Job(mergeDefaults({
117
+ "runs-on": runsOn,
118
+ if: "github.event_name == 'pull_request'",
119
+ permissions: new Permissions({ contents: "read", "pull-requests": "write" }),
120
+ env: {
121
+ MARKER: marker,
122
+ HEAD_SHA: "${{ github.event.pull_request.head.sha }}",
123
+ PR_NUMBER: "${{ github.event.number }}",
124
+ REPO: "${{ github.repository }}",
125
+ },
126
+ steps,
127
+ }, defs?.job));
128
+ return { job };
129
+ }, "PrPlanReport");
@@ -0,0 +1,60 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Job, Step, Workflow } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const PythonCI = Composite((props) => {
5
+ const { pythonVersion = "3.12", testCommand = "pytest --junitxml=report.xml --cov", lintCommand = "ruff check .", requirementsFile = "requirements.txt", usePoetry = false, runsOn = "ubuntu-latest", defaults, } = props;
6
+ const cacheType = usePoetry ? "poetry" : "pip";
7
+ const installSteps = usePoetry
8
+ ? [
9
+ new Step({ name: "Install Poetry", run: "pip install poetry" }),
10
+ new Step({ name: "Install dependencies", run: "poetry install" }),
11
+ ]
12
+ : [
13
+ new Step({ name: "Install dependencies", run: `pip install -r ${requirementsFile}` }),
14
+ ];
15
+ // ── Test job ───────────────────────────────────────────────────────
16
+ const testJob = new Job(mergeDefaults({
17
+ "runs-on": runsOn,
18
+ steps: [
19
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
20
+ new Step({
21
+ name: "Setup Python",
22
+ uses: "actions/setup-python@v5",
23
+ with: { "python-version": pythonVersion, cache: cacheType },
24
+ }),
25
+ ...installSteps,
26
+ new Step({ name: "Test", run: testCommand }),
27
+ ],
28
+ }, defaults?.testJob));
29
+ // ── Lint job (optional) ────────────────────────────────────────────
30
+ const lintJob = lintCommand !== null
31
+ ? new Job(mergeDefaults({
32
+ "runs-on": runsOn,
33
+ steps: [
34
+ new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
35
+ new Step({
36
+ name: "Setup Python",
37
+ uses: "actions/setup-python@v5",
38
+ with: { "python-version": pythonVersion, cache: cacheType },
39
+ }),
40
+ ...installSteps.map((s) => {
41
+ // Create fresh step instances for the lint job
42
+ return new Step({ name: s.props.name, run: s.props.run });
43
+ }),
44
+ new Step({ name: "Lint", run: lintCommand }),
45
+ ],
46
+ }, defaults?.lintJob))
47
+ : undefined;
48
+ // ── Workflow ───────────────────────────────────────────────────────
49
+ const workflow = new Workflow(mergeDefaults({
50
+ name: "Python CI",
51
+ on: {
52
+ push: { branches: ["main"] },
53
+ pull_request: { branches: ["main"] },
54
+ },
55
+ }, defaults?.workflow));
56
+ if (lintJob) {
57
+ return { workflow, testJob, lintJob };
58
+ }
59
+ return { workflow, testJob };
60
+ }, "PythonCI");
@@ -0,0 +1,18 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ export const SetupGo = Composite((props) => {
4
+ const { defaults } = props;
5
+ const withObj = {};
6
+ if (props.goVersion !== undefined)
7
+ withObj["go-version"] = props.goVersion;
8
+ if (props.goVersionFile !== undefined)
9
+ withObj["go-version-file"] = props.goVersionFile;
10
+ if (props.cache !== undefined)
11
+ withObj.cache = String(props.cache);
12
+ const step = new Step(mergeDefaults({
13
+ name: "Setup Go",
14
+ uses: "actions/setup-go@v5",
15
+ ...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
16
+ }, defaults?.step));
17
+ return { step };
18
+ }, "SetupGo");
@@ -0,0 +1,21 @@
1
+ import { Composite, mergeDefaults } from "@intentius/chant";
2
+ import { Step } from "../generated/index.js";
3
+ import { actionRef } from "../action-pins.js";
4
+ export const SetupNode = Composite((props) => {
5
+ const { defaults } = props;
6
+ const withObj = {};
7
+ if (props.nodeVersion !== undefined)
8
+ withObj["node-version"] = props.nodeVersion;
9
+ if (props.registryUrl !== undefined)
10
+ withObj["registry-url"] = props.registryUrl;
11
+ if (props.cache !== undefined)
12
+ withObj.cache = props.cache;
13
+ if (props.cacheFilePath !== undefined)
14
+ withObj["cache-dependency-path"] = props.cacheFilePath;
15
+ const step = new Step(mergeDefaults({
16
+ name: "Setup Node.js",
17
+ uses: actionRef("actions/setup-node", props.pin),
18
+ ...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
19
+ }, defaults?.step));
20
+ return { step };
21
+ }, "SetupNode");