@intentius/chant-lexicon-github 0.62.0 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EACnD,aAAa,EAEb,aAAa,EACb,gBAAgB,IAAI,sBAAsB,EAC1C,WAAW,EACX,SAAS,EACT,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,mBAAmB,CAAC;CAC1B;AAqTD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CA8BzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,GAAG,IAAI,CAwBhF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAC1C,OAAO,EAAE,SAAS,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAqClC;AA6BD;;;;;GAKG;AACH,MAAM,WAAW,wCAAwC;IACvD;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,EACrC,eAAe,GAAE,wCAA6C,GAC7D;IAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,IAAI,EAAE,aAAa,EAAE,CAAA;CAAE,CA6F1D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAenE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAMxB"}
1
+ {"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EACnD,aAAa,EAEb,aAAa,EACb,gBAAgB,IAAI,sBAAsB,EAC1C,WAAW,EACX,SAAS,EACT,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,mBAAmB,CAAC;CAC1B;AAmUD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CA8BzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,GAAG,IAAI,CAwBhF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAC1C,OAAO,EAAE,SAAS,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAqClC;AA6BD;;;;;GAKG;AACH,MAAM,WAAW,wCAAwC;IACvD;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,EACrC,eAAe,GAAE,wCAA6C,GAC7D;IAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,IAAI,EAAE,aAAa,EAAE,CAAA;CAAE,CA6F1D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAenE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAMxB"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "1c9c51090ae4df5488b7a558b098177d5f40d9efccc4ab8b89bb5caf91d1b953",
4
+ "manifest.json": "ee70227500205251bb2525d85be15c82185d173f1942686400135abc08e56786",
5
5
  "meta.json": "dc3977afc2b6ddc4904de906d1b1e448b786b2aced9d82f8eade2ff915203344",
6
6
  "types/index.d.ts": "f207946d7ab52f712d0c09995366440a6bba8b39358057b0da38ff9c0acca429",
7
7
  "rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
@@ -77,5 +77,5 @@
77
77
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
78
78
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
79
79
  },
80
- "composite": "92101a972617cf05fe02d8c0ff00f3ef87c8b769e768e1da63a6ed7730df17af"
80
+ "composite": "241cea113916c7340ca4cb0383b1e1c909eff2a6d4ffa0bb99b957499e41c71c"
81
81
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitHub",
6
6
  "intrinsics": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "description": "GitHub Actions lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -61,7 +61,7 @@
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@intentius/chant": "^0.62.0",
64
+ "@intentius/chant": "^0.63.0",
65
65
  "typescript": "^5.9.3"
66
66
  }
67
67
  }
@@ -535,7 +535,11 @@ describe("generateGithubOpPipeline: the gated apply on push (#2243)", () => {
535
535
 
536
536
  test("a failing run stays a failing job: the pipe cannot swallow its exit code", () => {
537
537
  const step = pushDoc().jobs!["app-apply"].steps.find((s) => s.id === "chant-run");
538
- expect(step?.run).toContain("set -o pipefail");
538
+ expect(step?.run).toContain(
539
+ '{ chant run app-apply --gated-exit 0 --json; echo "$?" >"$status"; } | tee "$json"',
540
+ );
541
+ expect(step?.run).toContain('code=$(cat "$status")');
542
+ expect(step?.run).toContain('[ "$code" -eq 0 ] || exit "$code"');
539
543
  });
540
544
 
541
545
  /**
@@ -544,16 +548,36 @@ describe("generateGithubOpPipeline: the gated apply on push (#2243)", () => {
544
548
  * shell on GitHub Actions is `sh`, not bash. `sh` rejects `set -o pipefail`
545
549
  * outright (`Illegal option -o pipefail`) and fails the step before `chant`
546
550
  * is ever reached, which is exactly what happened on a real run (choudoufu
547
- * #1026, run 34312967579). The step that emits `pipefail` must declare
548
- * `shell: bash` itself.
551
+ * #1026, run 34312967579). The exit-code capture above needs neither
552
+ * `pipefail` nor a `shell:` override to survive that — it is plain POSIX
553
+ * `sh`, so the default shell a `container:` job gets already runs it.
549
554
  */
550
- test("the pipefail step declares shell: bash — its job runs in a container, whose default shell is sh (#2299)", () => {
555
+ test("the run step needs no shell override — its script is POSIX sh, not bash (#2299)", () => {
551
556
  const doc = pushDoc();
552
557
  const job = doc.jobs!["app-apply"];
553
558
  expect(job.container).toBe("node:22-slim");
554
559
  const step = job.steps.find((s) => s.id === "chant-run");
555
- expect(step?.run).toContain("set -o pipefail");
556
- expect(step?.shell).toBe("bash");
560
+ expect(step?.shell).toBeUndefined();
561
+ expect(step?.run).not.toContain("pipefail");
562
+ });
563
+
564
+ /**
565
+ * chant #2321 — #2307 fixed #2299 by pairing the `pipefail` line with an
566
+ * unconditional `shell: "bash"` on this step, which then broke every
567
+ * consumer whose `options.image` has no bash on it at all (alpine,
568
+ * distroless, …): `shell: bash` on such an image fails with `bash: not
569
+ * found` before `chant` is ever reached, a failure busybox `ash` never had
570
+ * before #2307 introduced the unconditional `shell: bash`. The fix removes
571
+ * the bash-ism instead of moving it, so a non-bash image's own default
572
+ * `sh` runs the step unchanged.
573
+ */
574
+ test("a consumer-set non-bash image's gated job can still start (#2321)", () => {
575
+ const doc = parseFile(generateGithubOpPipeline([pushSpec], { image: "alpine:3.20" }).files[0].yaml);
576
+ const job = doc.jobs!["app-apply"];
577
+ expect(job.container).toBe("alpine:3.20");
578
+ const step = job.steps.find((s) => s.id === "chant-run");
579
+ expect(step?.shell).toBeUndefined();
580
+ expect(step?.run).not.toContain("pipefail");
557
581
  });
558
582
  });
559
583
 
@@ -245,26 +245,37 @@ const GATE_OUTPUT_SCRIPT =
245
245
  * `--json`, tee'd so the record is both in the log and on disk, then read for
246
246
  * the job's outputs.
247
247
  *
248
- * `set -o pipefail` is not decoration. Without it, a failing `chant run`
249
- * piped into `tee` comes back as `tee`'s own zero and turns a broken apply
250
- * green the exact thing this whole change must not do. But `pipefail` is a
251
- * bash-ism: GitHub's (and Forgejo's) default shell for a job with no
252
- * `container:` is `bash --noprofile --norc -eo pipefail`, which already sets
253
- * it, but the default for a job that *does* carry `container:` which every
254
- * Op job does, this one included (#2299) is plain `sh`, which rejects `set
255
- * -o pipefail` outright (`Illegal option -o pipefail`) and fails the step
256
- * before `chant` ever runs. This generator's own Op job always sets
257
- * `container:` (see {@link buildGithubOpPipelineDocs}'s `container: image`),
258
- * so the failure is not a corner case it is what every gated apply and
259
- * gated adopt does today.
248
+ * The exit-code capture below is not decoration. Piping straight into `tee`
249
+ * `chant run ... | tee "$json"` reports `tee`'s own zero as the step's
250
+ * result no matter what `chant run` exited with, and turns a broken apply
251
+ * green, which is the exact thing this step must not do. bash's fix for that
252
+ * is `set -o pipefail`, but `pipefail` is a bash-ism, and this generator's
253
+ * own Op job always sets `container:` (see {@link buildGithubOpPipelineDocs}'s
254
+ * `container: image`) GitHub's (and Forgejo's) default shell for a
255
+ * `container:` job is plain `sh`, which rejects `set -o pipefail` outright
256
+ * (`Illegal option -o pipefail`) and fails the step before `chant` ever runs
257
+ * (#2299). #2307 first fixed that by pairing the `pipefail` line with an
258
+ * unconditional `shell: "bash"` on the stepwhich then broke every
259
+ * consumer whose `options.image` has no bash on it at all, e.g. an
260
+ * `alpine:*` or distroless image (#2321): busybox `ash` ran the old
261
+ * plain-pipe script fine, so pinning `shell: bash` regressed exactly the
262
+ * images that never needed pipefail's workaround in the first place.
260
263
  *
261
- * That is why this function returns the whole step record rather than just
262
- * the script text: `shell: "bash"` is bundled into the same return value as
263
- * the `pipefail` line, so nothing that calls it can get one without the
264
- * other. A future edit that inlines another `pipefail`-bearing script has to
265
- * either reuse this function or open a new call site that a reviewer can see
266
- * declares no shell the property that made #2299 possible in the first
267
- * place is no longer available by accident.
264
+ * So this step needs no shell declaration and runs under whatever `sh` the
265
+ * consumer's image provides. It gets the same failure-cannot-hide guarantee
266
+ * `pipefail` gave, without the bash-ism, by capturing the invocation's own
267
+ * exit code into a file from *inside* the pipeline's first stage — where
268
+ * `tee` can never see it and checking that file once the pipe finishes:
269
+ * `{ invocation; echo "$?" >status; } | tee "$json"` followed by `exit
270
+ * "$(cat "$status")"` when that is non-zero. `set +e` up front makes this
271
+ * work under any default shell a container job might get, `-e` included:
272
+ * GitHub's documented `sh` template is `sh -e {0}`, and without `set +e`
273
+ * `-e` would abort the brace group at the first failing command — before
274
+ * `echo "$?"` ever runs — which was verified by running both this script and
275
+ * that failure mode under bash and dash directly, `-e` on. Everything past
276
+ * that point (`set +e`, `{ }`, `$(...)`, `[ ]`) is POSIX `sh`, so it needs no
277
+ * `shell:` field to run correctly under `ash`, `dash`, or bash alike, and
278
+ * #2299 does not reopen: nothing here is a bash-only construct.
268
279
  *
269
280
  * The `node -e` line and the job's own `outputs:` mapping exist for exactly
270
281
  * one reader: the gate-notice job beside it, which is `needs:`-only readable
@@ -283,12 +294,15 @@ function gatedRunStep(
283
294
  env: Record<string, string>,
284
295
  ): Record<string, unknown> {
285
296
  const lines = [
286
- "set -o pipefail",
297
+ "set +e",
287
298
  'json="${RUNNER_TEMP:-/tmp}/chant-run-' + op + '.json"',
288
- `${invocation} | tee "$json"`,
299
+ 'status="${RUNNER_TEMP:-/tmp}/chant-run-' + op + '.status"',
300
+ `{ ${invocation}; echo "$?" >"$status"; } | tee "$json"`,
301
+ 'code=$(cat "$status")',
302
+ '[ "$code" -eq 0 ] || exit "$code"',
289
303
  ];
290
304
  if (emitOutputs) lines.push(`node -e '${GATE_OUTPUT_SCRIPT}' "$json"`);
291
- return { id, run: lines.join("\n"), shell: "bash", env };
305
+ return { id, run: lines.join("\n"), env };
292
306
  }
293
307
 
294
308
  /**