@intentius/chant-lexicon-github 0.124.45 → 0.124.48
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/action-pins.d.ts +17 -27
- package/dist/action-pins.d.ts.map +1 -1
- package/dist/action-pins.js +39 -27
- package/dist/components/ci-jobs.d.ts +5 -2
- package/dist/components/ci-jobs.d.ts.map +1 -1
- package/dist/components/ci-jobs.js +8 -5
- package/dist/components/generate-op-pipeline.d.ts +7 -0
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-pipeline.js +26 -3
- package/dist/components/generate-op-waves-pipeline.d.ts +13 -3
- package/dist/components/generate-op-waves-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-waves-pipeline.js +24 -5
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +55 -10
- package/dist/components/review-jobs.d.ts +55 -0
- package/dist/components/review-jobs.d.ts.map +1 -0
- package/dist/components/review-jobs.js +130 -0
- package/dist/composites/cache.d.ts.map +1 -1
- package/dist/composites/cache.js +2 -1
- package/dist/composites/checkout.d.ts +3 -3
- package/dist/composites/checkout.js +1 -1
- package/dist/composites/docker-build.js +4 -4
- package/dist/composites/download-artifact.d.ts.map +1 -1
- package/dist/composites/download-artifact.js +2 -1
- package/dist/composites/go-ci.js +4 -4
- package/dist/composites/node-pipeline.js +5 -5
- package/dist/composites/python-ci.js +2 -2
- package/dist/composites/setup-go.d.ts.map +1 -1
- package/dist/composites/setup-go.js +2 -1
- package/dist/composites/setup-node.d.ts +3 -3
- package/dist/composites/setup-node.js +1 -1
- package/dist/composites/setup-python.d.ts.map +1 -1
- package/dist/composites/setup-python.js +2 -1
- package/dist/composites/upload-artifact.d.ts.map +1 -1
- package/dist/composites/upload-artifact.js +2 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/action-pins.test.ts +29 -6
- package/src/action-pins.ts +48 -34
- package/src/components/__fixtures__/pr-loop-build.github.golden.yml +8 -9
- package/src/components/__fixtures__/pr-loop-record.github.golden.yml +4 -5
- package/src/components/__fixtures__/pr-loop-waves.github.golden.yml +16 -17
- package/src/components/__fixtures__/pr-loop.github.golden.yml +8 -9
- package/src/components/__golden__/op-waves-jobs.yml +71 -9
- package/src/components/ci-jobs.ts +8 -5
- package/src/components/generate-op-pipeline.review.test.ts +152 -0
- package/src/components/generate-op-pipeline.test.ts +5 -4
- package/src/components/generate-op-pipeline.ts +33 -3
- package/src/components/generate-op-waves-pipeline.test.ts +2 -1
- package/src/components/generate-op-waves-pipeline.ts +25 -5
- package/src/components/generate-pipeline.test.ts +10 -9
- package/src/components/generate-pipeline.ts +57 -9
- package/src/components/generate-pr-pipeline.locks.test.ts +68 -0
- package/src/components/generate-pr-pipeline.test.ts +6 -4
- package/src/components/generate-pr-waves-pipeline.test.ts +3 -2
- package/src/components/review-jobs.ts +165 -0
- package/src/composites/cache.ts +2 -1
- package/src/composites/checkout.ts +4 -4
- package/src/composites/composites.test.ts +23 -22
- package/src/composites/docker-build.ts +4 -4
- package/src/composites/download-artifact.ts +2 -1
- package/src/composites/go-ci.ts +4 -4
- package/src/composites/node-pipeline.ts +5 -5
- package/src/composites/python-ci.ts +2 -2
- package/src/composites/setup-go.ts +2 -1
- package/src/composites/setup-node.ts +4 -4
- package/src/composites/setup-python.ts +2 -1
- package/src/composites/upload-artifact.ts +2 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pull-request workflow with `ci.locks: plan` (#4327): a lock job after
|
|
3
|
+
* the plan runs `chant lock --from-report` and an unlock job runs `chant
|
|
4
|
+
* unlock` when the pull request closes. Both check out the target branch's
|
|
5
|
+
* commit. Without the option the workflow is unchanged.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, test } from "vitest";
|
|
9
|
+
import { parseYAML } from "@intentius/chant/yaml";
|
|
10
|
+
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
11
|
+
import { generateGithubPipeline } from "./generate-pipeline";
|
|
12
|
+
|
|
13
|
+
const ESTATE: DriverComponent[] = [
|
|
14
|
+
{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
interface Step { name?: string; uses?: string; run?: string; env?: Record<string, string>; with?: Record<string, unknown> }
|
|
18
|
+
interface Job { if?: string; needs?: string[]; permissions: Record<string, string>; steps: Step[] }
|
|
19
|
+
type Doc = { on: Record<string, unknown>; jobs: Record<string, Job> };
|
|
20
|
+
|
|
21
|
+
const gen = (prLoop: Record<string, unknown>) => parseYAML(generateGithubPipeline(ESTATE, { env: "prod", prLoop }).yaml) as Doc;
|
|
22
|
+
|
|
23
|
+
describe("the GitHub workflow with ci.locks: plan", () => {
|
|
24
|
+
const doc = gen({ locks: "plan" });
|
|
25
|
+
|
|
26
|
+
test("adds a lock job and an unlock job, and fires on close", () => {
|
|
27
|
+
expect(Object.keys(doc.jobs)).toEqual(["plan", "plan-note", "lock", "unlock", "record"]);
|
|
28
|
+
expect(doc.on.pull_request).toEqual({ branches: ["main"], types: ["opened", "synchronize", "reopened", "closed"] });
|
|
29
|
+
expect(doc.jobs.plan!.if).toBe("github.event_name == 'pull_request' && github.event.action != 'closed'");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("the lock job needs the plan, takes its report and runs chant lock --from-report from the base commit", () => {
|
|
33
|
+
const job = doc.jobs.lock!;
|
|
34
|
+
expect(job.needs).toEqual(["plan"]);
|
|
35
|
+
expect(job.steps[0]!.with!.ref).toBe("${{ github.event.pull_request.base.sha }}");
|
|
36
|
+
expect(job.steps.some((s) => s.run?.includes("needs.plan.outputs"))).toBe(true);
|
|
37
|
+
const lock = job.steps.find((s) => s.run?.startsWith("chant lock"))!;
|
|
38
|
+
expect(lock.run).toBe('chant lock --from-report .chant/pr/pr-plan.json --pr "$PR_NUMBER" --head "$HEAD_SHA" --stage plan --forge github');
|
|
39
|
+
expect(lock.env!.PR_NUMBER).toBe("${{ github.event.pull_request.number }}");
|
|
40
|
+
expect(job.permissions).toEqual({ contents: "write", "pull-requests": "read", statuses: "write" });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("the unlock job runs chant unlock on close, from the base commit, and runs no head code", () => {
|
|
44
|
+
const job = doc.jobs.unlock!;
|
|
45
|
+
expect(job.if).toBe("github.event_name == 'pull_request' && github.event.action == 'closed'");
|
|
46
|
+
expect(job.steps[0]!.with!.ref).toBe("${{ github.event.pull_request.base.sha }}");
|
|
47
|
+
expect(job.steps.find((s) => s.run?.startsWith("chant unlock"))!.run).toBe('chant unlock --pr "$PR_NUMBER"');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("without ci.locks the workflow has neither job and fires on the default pull request types", () => {
|
|
51
|
+
const plain = generateGithubPipeline(ESTATE, { env: "prod", prLoop: {} }).yaml;
|
|
52
|
+
expect(parseYAML(plain)).toMatchObject({ on: { pull_request: { branches: ["main"] } } });
|
|
53
|
+
expect(plain).not.toContain("closed");
|
|
54
|
+
expect(plain).not.toContain("chant lock");
|
|
55
|
+
expect(plain).not.toContain("chant unlock");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("with a pull request environment too", () => {
|
|
60
|
+
test("the pull_request types and the plan condition are the same as with the environment alone", () => {
|
|
61
|
+
const both = gen({ locks: "plan", prEnvironment: true });
|
|
62
|
+
const env = gen({ prEnvironment: true });
|
|
63
|
+
expect(both.on.pull_request).toEqual(env.on.pull_request);
|
|
64
|
+
expect(both.jobs.plan!.if).toBe(env.jobs.plan!.if);
|
|
65
|
+
expect(Object.keys(both.jobs)).toContain("lock");
|
|
66
|
+
expect(Object.keys(both.jobs)).toContain("env-down");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -14,6 +14,7 @@ import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
|
14
14
|
import { githubBaseFallback, greenCheckout, prApplyGroup, prApplyRecordKey, prLoopJobs } from "@intentius/chant/components/pr-pipeline";
|
|
15
15
|
import { dropForgeTokens } from "@intentius/chant/ci-handover";
|
|
16
16
|
import { generateGithubPipeline } from "./generate-pipeline";
|
|
17
|
+
import { ACTION_PINS, usesRef } from "@intentius/chant/action-pins";
|
|
17
18
|
|
|
18
19
|
const ESTATE: DriverComponent[] = [
|
|
19
20
|
{ name: "net", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "terraform-apply", root: "net" }] }] },
|
|
@@ -80,7 +81,8 @@ describe("the GitHub pull-request workflow", () => {
|
|
|
80
81
|
expect(lastLine(plan.run)).toBe('chant ci run plan --in-place --no-push --base "$BASE_SHA" --pr "$PR_NUMBER" --env prod --gate pr-apply --forge github');
|
|
81
82
|
expect(plan.env).toMatchObject({ BASE_SHA: "${{ github.event.pull_request.base.sha }}", PR_NUMBER: "${{ github.event.pull_request.number }}" });
|
|
82
83
|
// The runner key is an optional secret: unset, the expression is empty and the record is unsigned.
|
|
83
|
-
|
|
84
|
+
// #4328: the plan runs the pull request's code, so its job never gets the runner key.
|
|
85
|
+
expect(plan.env?.CHANT_RUNNER_KEY).toBeUndefined();
|
|
84
86
|
// #4295: the plan job only reads; the plan-note job writes the note and the status.
|
|
85
87
|
expect(doc.jobs.plan.permissions).toEqual({ contents: "read" });
|
|
86
88
|
expect(doc.jobs["plan-note"].permissions).toEqual({ actions: "read", contents: "read", "pull-requests": "write", statuses: "write" });
|
|
@@ -123,9 +125,9 @@ describe("the GitHub pull-request workflow", () => {
|
|
|
123
125
|
|
|
124
126
|
test("the apply resumes from a record the cache keeps for the pushed commit across a re-run (#3543)", () => {
|
|
125
127
|
const steps = doc.jobs.apply.steps;
|
|
126
|
-
const restore = steps.findIndex((s) => s.uses ===
|
|
128
|
+
const restore = steps.findIndex((s) => s.uses === `${usesRef(ACTION_PINS.cacheRestore4)}`);
|
|
127
129
|
const run = steps.findIndex((s) => s.run?.includes("ci run apply"));
|
|
128
|
-
const save = steps.findIndex((s) => s.uses ===
|
|
130
|
+
const save = steps.findIndex((s) => s.uses === `${usesRef(ACTION_PINS.cacheSave4)}`);
|
|
129
131
|
expect(restore).toBeGreaterThan(-1);
|
|
130
132
|
expect(restore).toBeLessThan(run);
|
|
131
133
|
expect(save).toBeGreaterThan(run);
|
|
@@ -199,7 +201,7 @@ describe("the GitHub pull-request workflow", () => {
|
|
|
199
201
|
expect(mdoc.jobs.apply.concurrency).toEqual({ group: "chant-apply.network.prod", "cancel-in-progress": false });
|
|
200
202
|
// The record path is relative to the member for --resume and to the repository for the cache.
|
|
201
203
|
expect(apply.run).toContain("--in-place");
|
|
202
|
-
const restore = mdoc.jobs.apply.steps.find((s) => s.uses ===
|
|
204
|
+
const restore = mdoc.jobs.apply.steps.find((s) => s.uses === `${usesRef(ACTION_PINS.cacheRestore4)}`)!;
|
|
203
205
|
expect(restore.with).toMatchObject({ path: "infra/network/.chant/pr-resume/pr-apply.json", "restore-keys": "chant-apply.network.prod-${{ github.sha }}-" });
|
|
204
206
|
// #4295: the plan job uploads nothing; the plan-note job keeps its report.
|
|
205
207
|
expect(mdoc.jobs.plan.steps.some((s) => s.uses?.startsWith("actions/upload-artifact"))).toBe(false);
|
|
@@ -14,6 +14,7 @@ import { describe, expect, test } from "vitest";
|
|
|
14
14
|
import { parseYAML } from "@intentius/chant/yaml";
|
|
15
15
|
import type { DriverComponent } from "@intentius/chant/components/driver";
|
|
16
16
|
import { generateGithubPipeline } from "./generate-pipeline";
|
|
17
|
+
import { ACTION_PINS, usesRef } from "@intentius/chant/action-pins";
|
|
17
18
|
|
|
18
19
|
const root = (name: string, dependsOn: string[] = []): DriverComponent => ({
|
|
19
20
|
name,
|
|
@@ -112,8 +113,8 @@ describe("the GitHub pull-request workflow with a gate per wave (#4003)", () =>
|
|
|
112
113
|
test("each wave restores the commit's newest record and saves its own, so wave k reads what wave k-1 left", () => {
|
|
113
114
|
for (const k of [1, 2, 3]) {
|
|
114
115
|
const steps = doc.jobs[`apply-wave-${k}`]!.steps;
|
|
115
|
-
const restore = steps.find((s) => s.uses ===
|
|
116
|
-
const save = steps.find((s) => s.uses ===
|
|
116
|
+
const restore = steps.find((s) => s.uses === `${usesRef(ACTION_PINS.cacheRestore4)}`)!;
|
|
117
|
+
const save = steps.find((s) => s.uses === `${usesRef(ACTION_PINS.cacheSave4)}`)!;
|
|
117
118
|
const key = `chant-apply-prod-\${{ github.sha }}-\${{ github.run_id }}-\${{ github.run_attempt }}-wave-${k}`;
|
|
118
119
|
expect(restore.with).toEqual({ path: ".chant/pr-resume/pr-apply.json", key, "restore-keys": "chant-apply-prod-${{ github.sha }}-" });
|
|
119
120
|
expect(save).toMatchObject({ if: "always()", with: { path: ".chant/pr-resume/pr-apply.json", key } });
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two jobs of a split review (#4326): `ScheduledOpSpec.reviewAgent`.
|
|
3
|
+
*
|
|
4
|
+
* An Op that runs a `reviewAgent` step in `handoff` mode runs the change's
|
|
5
|
+
* code (`chant run` loads the pull request's Op files) beside a model. This
|
|
6
|
+
* generator keeps the forge write token out of that job and posts the note
|
|
7
|
+
* from another one, as terragucci's review does (`render-review.ts`: the
|
|
8
|
+
* `review` and `review-note` jobs):
|
|
9
|
+
*
|
|
10
|
+
* - the review job holds `contents: read` and no forge token in any step's
|
|
11
|
+
* environment, bar the checkout script's fetches (`checkoutScript`,
|
|
12
|
+
* ../../../../packages/core/src/ci-handover.ts). Every later step starts
|
|
13
|
+
* without the token variables (`dropForgeTokens`). The `chant run` step is
|
|
14
|
+
* its last, and it hands the review directory on as the job's outputs
|
|
15
|
+
* (`handoverTrap`), in place of an artifact upload that would start beside
|
|
16
|
+
* whatever the change's code left running.
|
|
17
|
+
* - the note job checks nothing out. It takes the outputs back as data
|
|
18
|
+
* (`takeOverScript`), installs the chant release this lexicon belongs to
|
|
19
|
+
* and runs `postReviewNote` with the job's token. The Op's name comes from
|
|
20
|
+
* this file, not from the review, which the change's code wrote.
|
|
21
|
+
*
|
|
22
|
+
* Forgejo's runner ignores `permissions:` and may put its token in a step's
|
|
23
|
+
* environment, so the review job drops the token variables on both forges.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { readFileSync } from "node:fs";
|
|
27
|
+
import { GITHUB_JOB_TOKEN, GITHUB_RUNNER } from "@intentius/chant/ci-jobs";
|
|
28
|
+
import { CHECKOUT_TOKEN_ENV, checkoutScript, dropForgeTokens, handoverOutputs, handoverTrap, takeOverScript } from "@intentius/chant/ci-handover";
|
|
29
|
+
import type { OpTrigger, PipelineMember } from "@intentius/chant/lexicon";
|
|
30
|
+
|
|
31
|
+
/** The variable `reviewAgent` reads its handoff directory from (`REVIEW_DIR_ENV`, review-agent.ts). */
|
|
32
|
+
const REVIEW_DIR_ENV = "CHANT_REVIEW_DIR";
|
|
33
|
+
|
|
34
|
+
/** Where the review job writes the review, from the directory the step starts in. */
|
|
35
|
+
const REVIEW_DIR = ".chant/review";
|
|
36
|
+
|
|
37
|
+
/** The id of the review job's `chant run` step, which its outputs come from. */
|
|
38
|
+
const REVIEW_STEP_ID = "chant-review";
|
|
39
|
+
|
|
40
|
+
/** The name of the note job beside the Op's own job. */
|
|
41
|
+
export function reviewNoteJobName(jobName: string): string {
|
|
42
|
+
return `${jobName}-note`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Refuse a spec whose review cannot be split the way this shape needs:
|
|
47
|
+
* a trigger with no pull request to post on, a finding-mode that would give
|
|
48
|
+
* the review job a write scope, or an additive scope that would.
|
|
49
|
+
*/
|
|
50
|
+
export function assertReviewSplit(name: string, trigger: OpTrigger, findingMode: string, permissions: Record<string, string> | undefined): void {
|
|
51
|
+
if (trigger.kind !== "pull_request") {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Scheduled Op "${name}" sets reviewAgent, which posts the review as a note on the pull request that triggered the run, but its trigger is "${trigger.kind}". Give it a { kind: "pull_request" } trigger.`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (findingMode !== "report") {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Scheduled Op "${name}" sets reviewAgent with findingMode "${findingMode}". The review job holds no forge write token, and the note job posts instead, so the Op's own finding-mode must be "report".`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
for (const [scope, level] of Object.entries(permissions ?? {})) {
|
|
62
|
+
if (level === "write" && scope !== "id-token") {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Scheduled Op "${name}" sets reviewAgent and adds the permission "${scope}: write". The review job runs the change's code and may hold only "id-token: write" beyond contents: read.`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The chant release the note job installs: this lexicon's own, which releases in lockstep with `@intentius/chant`. */
|
|
71
|
+
function chantVersion(): string {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")) as { version?: unknown };
|
|
73
|
+
if (typeof pkg.version !== "string" || pkg.version === "") {
|
|
74
|
+
throw new Error("the github lexicon's package.json has no version to pin the review note job's chant to");
|
|
75
|
+
}
|
|
76
|
+
return pkg.version;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** What the note job runs under node: `postReviewNote` over the review the review job handed on. Plain JavaScript, no `${{ }}`. */
|
|
80
|
+
const NOTE_SCRIPT = [
|
|
81
|
+
'import { postReviewNote } from "@intentius/chant/op/activities/review-agent";',
|
|
82
|
+
`const { CHANT_OP: op, ${REVIEW_DIR_ENV}: dir } = process.env;`,
|
|
83
|
+
"const result = await postReviewNote({ op, dir });",
|
|
84
|
+
"console.log(result.reason);",
|
|
85
|
+
"if (!result.posted) process.exitCode = 1;",
|
|
86
|
+
].join("\n");
|
|
87
|
+
|
|
88
|
+
/** Where the note job puts the review it took back: outside any checkout, which it has none of. */
|
|
89
|
+
const NOTE_DIR = '"${RUNNER_TEMP:-/tmp}/chant-review"';
|
|
90
|
+
|
|
91
|
+
/** The note step's shell: install the pinned chant beside the review and run {@link NOTE_SCRIPT} with tsx. */
|
|
92
|
+
function noteScript(version: string): string {
|
|
93
|
+
return [
|
|
94
|
+
`review=${NOTE_DIR}`,
|
|
95
|
+
'dir="${RUNNER_TEMP:-/tmp}/chant-review-note"',
|
|
96
|
+
'mkdir -p "$dir"',
|
|
97
|
+
'cd "$dir"',
|
|
98
|
+
"echo '{\"private\":true}' > package.json",
|
|
99
|
+
`npm install --omit=peer --no-audit --no-fund --loglevel=error @intentius/chant@${version}`,
|
|
100
|
+
"cat > note.mjs <<'NOTE'",
|
|
101
|
+
NOTE_SCRIPT,
|
|
102
|
+
"NOTE",
|
|
103
|
+
`${REVIEW_DIR_ENV}="$review" node --import tsx note.mjs`,
|
|
104
|
+
].join("\n");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ReviewJobsInput {
|
|
108
|
+
/** The Op's name: what `chant run` targets and what keys the note. */
|
|
109
|
+
name: string;
|
|
110
|
+
/** The Op's job name. */
|
|
111
|
+
jobName: string;
|
|
112
|
+
image: string;
|
|
113
|
+
/** The invocation of the Op, `chant run <name>` by default. */
|
|
114
|
+
invocation: string;
|
|
115
|
+
/** Setup steps, as built by `setupStepDoc`. */
|
|
116
|
+
setup: Array<Record<string, unknown>>;
|
|
117
|
+
beforeScript: readonly string[];
|
|
118
|
+
extraScript: readonly string[];
|
|
119
|
+
/** The Op's own per-job environment (`ScheduledOpSpec.variables`). */
|
|
120
|
+
variables?: Record<string, string>;
|
|
121
|
+
member?: PipelineMember;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The review job and the note job of one split review. */
|
|
125
|
+
export function reviewJobs(i: ReviewJobsInput): { review: Record<string, unknown>; note: Record<string, unknown> } {
|
|
126
|
+
const drop = dropForgeTokens("sh");
|
|
127
|
+
const rooted = !i.member || i.member.dir === "." || i.member.dir === "";
|
|
128
|
+
const headStep = (line: string): Record<string, unknown> => ({ run: `${drop}\n${line}` });
|
|
129
|
+
const review = {
|
|
130
|
+
"runs-on": GITHUB_RUNNER,
|
|
131
|
+
container: i.image,
|
|
132
|
+
...(i.variables && Object.keys(i.variables).length > 0 ? { env: i.variables } : {}),
|
|
133
|
+
outputs: handoverOutputs(REVIEW_STEP_ID, "github"),
|
|
134
|
+
steps: [
|
|
135
|
+
// A script, not actions/checkout: the token reaches the fetches alone, and the base branch's history comes with them for the diff.
|
|
136
|
+
{
|
|
137
|
+
name: "Check out the change",
|
|
138
|
+
...(rooted ? {} : { "working-directory": "." }),
|
|
139
|
+
env: { [CHECKOUT_TOKEN_ENV]: GITHUB_JOB_TOKEN },
|
|
140
|
+
run: checkoutScript(true),
|
|
141
|
+
},
|
|
142
|
+
...i.setup,
|
|
143
|
+
...i.beforeScript.map(headStep),
|
|
144
|
+
// The job's last step: it hands the review directory on as the job's outputs when it ends, however it ends.
|
|
145
|
+
{
|
|
146
|
+
id: REVIEW_STEP_ID,
|
|
147
|
+
name: "Run the Op and keep its review",
|
|
148
|
+
env: { [REVIEW_DIR_ENV]: REVIEW_DIR },
|
|
149
|
+
run: [drop, ...handoverTrap(REVIEW_DIR, "github"), i.invocation, ...i.extraScript].join("\n"),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
};
|
|
153
|
+
const note = {
|
|
154
|
+
needs: i.jobName,
|
|
155
|
+
if: `needs.${i.jobName}.result == 'success'`,
|
|
156
|
+
"runs-on": GITHUB_RUNNER,
|
|
157
|
+
container: i.image,
|
|
158
|
+
permissions: { "pull-requests": "write" },
|
|
159
|
+
steps: [
|
|
160
|
+
{ name: "Take the review from the review job", run: takeOverScript(i.jobName, NOTE_DIR, "github") },
|
|
161
|
+
{ name: "Post the review as a note", env: { GITHUB_TOKEN: GITHUB_JOB_TOKEN, CHANT_OP: i.name }, run: noteScript(chantVersion()) },
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
return { review, note };
|
|
165
|
+
}
|
package/src/composites/cache.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
2
|
+
import { actionRef } from "../action-pins";
|
|
2
3
|
import { Step } from "../generated/index";
|
|
3
4
|
|
|
4
5
|
export interface CacheActionProps {
|
|
@@ -20,7 +21,7 @@ export const CacheAction = Composite((props: CacheActionProps) => {
|
|
|
20
21
|
|
|
21
22
|
const step = new Step(mergeDefaults({
|
|
22
23
|
name: "Cache",
|
|
23
|
-
uses: "actions/cache
|
|
24
|
+
uses: actionRef("actions/cache"),
|
|
24
25
|
with: withObj,
|
|
25
26
|
}, defaults?.step));
|
|
26
27
|
|
|
@@ -10,9 +10,9 @@ export interface CheckoutProps {
|
|
|
10
10
|
submodules?: boolean | string;
|
|
11
11
|
sshKey?: string;
|
|
12
12
|
/**
|
|
13
|
-
* `"
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* `"sha"` (default) emits the commit SHA from the pin table with the version as
|
|
14
|
+
* a YAML comment, which passes GHA021, GHA029 and GHA059. `"tag"` emits the
|
|
15
|
+
* action's major tag.
|
|
16
16
|
*/
|
|
17
17
|
pin?: ActionPinMode;
|
|
18
18
|
defaults?: {
|
|
@@ -32,7 +32,7 @@ export const Checkout = Composite((props: CheckoutProps) => {
|
|
|
32
32
|
|
|
33
33
|
const step = new Step(mergeDefaults({
|
|
34
34
|
name: "Checkout",
|
|
35
|
-
uses: actionRef("actions/checkout", props.pin),
|
|
35
|
+
uses: actionRef("actions/checkout", props.pin ?? "sha"),
|
|
36
36
|
...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
|
|
37
37
|
}, defaults?.step));
|
|
38
38
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { actionRef, type PinnedAction } from "../action-pins";
|
|
2
3
|
import { Checkout } from "./checkout";
|
|
3
4
|
import { SetupNode } from "./setup-node";
|
|
4
5
|
import { SetupGo } from "./setup-go";
|
|
@@ -22,7 +23,7 @@ describe("Checkout", () => {
|
|
|
22
23
|
test("returns step with checkout action", () => {
|
|
23
24
|
const result = Checkout({});
|
|
24
25
|
expect(result.step).toBeDefined();
|
|
25
|
-
expect(propsOf(result.step).uses).toBe("actions/checkout
|
|
26
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/checkout" as PinnedAction));
|
|
26
27
|
expect(propsOf(result.step).name).toBe("Checkout");
|
|
27
28
|
});
|
|
28
29
|
|
|
@@ -48,7 +49,7 @@ describe("Checkout", () => {
|
|
|
48
49
|
describe("SetupNode", () => {
|
|
49
50
|
test("returns step with setup-node action", () => {
|
|
50
51
|
const result = SetupNode({});
|
|
51
|
-
expect(propsOf(result.step).uses).toBe("actions/setup-node
|
|
52
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/setup-node" as PinnedAction));
|
|
52
53
|
expect(propsOf(result.step).name).toBe("Setup Node.js");
|
|
53
54
|
});
|
|
54
55
|
|
|
@@ -68,7 +69,7 @@ describe("SetupNode", () => {
|
|
|
68
69
|
describe("SetupGo", () => {
|
|
69
70
|
test("returns step with setup-go action", () => {
|
|
70
71
|
const result = SetupGo({});
|
|
71
|
-
expect(propsOf(result.step).uses).toBe("actions/setup-go
|
|
72
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/setup-go" as PinnedAction));
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
test("passes goVersion", () => {
|
|
@@ -82,7 +83,7 @@ describe("SetupGo", () => {
|
|
|
82
83
|
describe("SetupPython", () => {
|
|
83
84
|
test("returns step with setup-python action", () => {
|
|
84
85
|
const result = SetupPython({});
|
|
85
|
-
expect(propsOf(result.step).uses).toBe("actions/setup-python
|
|
86
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/setup-python" as PinnedAction));
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
test("passes pythonVersion", () => {
|
|
@@ -96,7 +97,7 @@ describe("SetupPython", () => {
|
|
|
96
97
|
describe("CacheAction", () => {
|
|
97
98
|
test("returns step with cache action", () => {
|
|
98
99
|
const result = CacheAction({ path: "node_modules", key: "npm-${{ hashFiles('package-lock.json') }}" });
|
|
99
|
-
expect(propsOf(result.step).uses).toBe("actions/cache
|
|
100
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/cache" as PinnedAction));
|
|
100
101
|
expect((propsOf(result.step).with as Record<string, string>).path).toBe("node_modules");
|
|
101
102
|
expect((propsOf(result.step).with as Record<string, string>).key).toBe("npm-${{ hashFiles('package-lock.json') }}");
|
|
102
103
|
});
|
|
@@ -107,7 +108,7 @@ describe("CacheAction", () => {
|
|
|
107
108
|
describe("UploadArtifact", () => {
|
|
108
109
|
test("returns step with upload action", () => {
|
|
109
110
|
const result = UploadArtifact({ name: "build", path: "dist/" });
|
|
110
|
-
expect(propsOf(result.step).uses).toBe("actions/upload-artifact
|
|
111
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/upload-artifact" as PinnedAction));
|
|
111
112
|
expect((propsOf(result.step).with as Record<string, string>).name).toBe("build");
|
|
112
113
|
expect((propsOf(result.step).with as Record<string, string>).path).toBe("dist/");
|
|
113
114
|
});
|
|
@@ -123,7 +124,7 @@ describe("UploadArtifact", () => {
|
|
|
123
124
|
describe("DownloadArtifact", () => {
|
|
124
125
|
test("returns step with download action", () => {
|
|
125
126
|
const result = DownloadArtifact({ name: "build" });
|
|
126
|
-
expect(propsOf(result.step).uses).toBe("actions/download-artifact
|
|
127
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/download-artifact" as PinnedAction));
|
|
127
128
|
expect((propsOf(result.step).with as Record<string, string>).name).toBe("build");
|
|
128
129
|
});
|
|
129
130
|
});
|
|
@@ -228,7 +229,7 @@ describe("NodePipeline", () => {
|
|
|
228
229
|
test("bun uses oven-sh/setup-bun instead of setup-node", () => {
|
|
229
230
|
const result = NodePipeline({ packageManager: "bun" });
|
|
230
231
|
const steps = propsOf(result.buildJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
231
|
-
expect(steps[1].props.uses).toBe("oven-sh/setup-bun
|
|
232
|
+
expect(steps[1].props.uses).toBe(actionRef("oven-sh/setup-bun" as PinnedAction));
|
|
232
233
|
expect(steps[1].props.name).toBe("Setup Bun");
|
|
233
234
|
});
|
|
234
235
|
|
|
@@ -247,14 +248,14 @@ describe("NodePipeline", () => {
|
|
|
247
248
|
test("upload step uses upload-artifact with configured name", () => {
|
|
248
249
|
const result = NodePipeline({ artifactName: "my-build" });
|
|
249
250
|
const steps = propsOf(result.buildJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
250
|
-
expect(steps[4].props.uses).toBe("actions/upload-artifact
|
|
251
|
+
expect(steps[4].props.uses).toBe(actionRef("actions/upload-artifact" as PinnedAction));
|
|
251
252
|
expect((steps[4].props.with as Record<string, string>).name).toBe("my-build");
|
|
252
253
|
});
|
|
253
254
|
|
|
254
255
|
test("download step in testJob uses configured artifact name", () => {
|
|
255
256
|
const result = NodePipeline({ artifactName: "my-build" });
|
|
256
257
|
const steps = propsOf(result.testJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
257
|
-
expect(steps[3].props.uses).toBe("actions/download-artifact
|
|
258
|
+
expect(steps[3].props.uses).toBe(actionRef("actions/download-artifact" as PinnedAction));
|
|
258
259
|
expect((steps[3].props.with as Record<string, string>).name).toBe("my-build");
|
|
259
260
|
});
|
|
260
261
|
|
|
@@ -289,7 +290,7 @@ describe("BunPipeline", () => {
|
|
|
289
290
|
test("defaults to bun package manager", () => {
|
|
290
291
|
const result = BunPipeline({});
|
|
291
292
|
const steps = propsOf(result.buildJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
292
|
-
expect(steps[1].props.uses).toBe("oven-sh/setup-bun
|
|
293
|
+
expect(steps[1].props.uses).toBe(actionRef("oven-sh/setup-bun" as PinnedAction));
|
|
293
294
|
expect(steps[2].props.run).toBe("bun install --frozen-lockfile");
|
|
294
295
|
});
|
|
295
296
|
});
|
|
@@ -340,7 +341,7 @@ describe("PythonCI", () => {
|
|
|
340
341
|
const result = PythonCI({});
|
|
341
342
|
const steps = propsOf(result.testJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
342
343
|
const setupStep = steps[1];
|
|
343
|
-
expect(propsOf(setupStep).uses).toBe("actions/setup-python
|
|
344
|
+
expect(propsOf(setupStep).uses).toBe(actionRef("actions/setup-python" as PinnedAction));
|
|
344
345
|
expect((propsOf(setupStep).with as Record<string, string>)["python-version"]).toBe("3.12");
|
|
345
346
|
});
|
|
346
347
|
|
|
@@ -430,25 +431,25 @@ describe("DockerBuild", () => {
|
|
|
430
431
|
test("uses docker/login-action", () => {
|
|
431
432
|
const result = DockerBuild({});
|
|
432
433
|
const steps = propsOf(result.job).steps as Array<{ props: Record<string, unknown> }>;
|
|
433
|
-
expect(steps[1].props.uses).toBe("docker/login-action
|
|
434
|
+
expect(steps[1].props.uses).toBe(actionRef("docker/login-action" as PinnedAction));
|
|
434
435
|
});
|
|
435
436
|
|
|
436
437
|
test("uses docker/setup-buildx-action", () => {
|
|
437
438
|
const result = DockerBuild({});
|
|
438
439
|
const steps = propsOf(result.job).steps as Array<{ props: Record<string, unknown> }>;
|
|
439
|
-
expect(steps[2].props.uses).toBe("docker/setup-buildx-action
|
|
440
|
+
expect(steps[2].props.uses).toBe(actionRef("docker/setup-buildx-action" as PinnedAction));
|
|
440
441
|
});
|
|
441
442
|
|
|
442
443
|
test("uses docker/metadata-action", () => {
|
|
443
444
|
const result = DockerBuild({});
|
|
444
445
|
const steps = propsOf(result.job).steps as Array<{ props: Record<string, unknown> }>;
|
|
445
|
-
expect(steps[3].props.uses).toBe("docker/metadata-action
|
|
446
|
+
expect(steps[3].props.uses).toBe(actionRef("docker/metadata-action" as PinnedAction));
|
|
446
447
|
});
|
|
447
448
|
|
|
448
449
|
test("uses docker/build-push-action", () => {
|
|
449
450
|
const result = DockerBuild({});
|
|
450
451
|
const steps = propsOf(result.job).steps as Array<{ props: Record<string, unknown> }>;
|
|
451
|
-
expect(steps[4].props.uses).toBe("docker/build-push-action
|
|
452
|
+
expect(steps[4].props.uses).toBe(actionRef("docker/build-push-action" as PinnedAction));
|
|
452
453
|
});
|
|
453
454
|
|
|
454
455
|
test("login step uses ghcr.io by default", () => {
|
|
@@ -543,7 +544,7 @@ describe("DeployEnvironment", () => {
|
|
|
543
544
|
const result = DeployEnvironment({ name: "staging", deployScript: "npm run deploy" });
|
|
544
545
|
const steps = propsOf(result.deployJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
545
546
|
expect(steps.length).toBe(2);
|
|
546
|
-
expect(steps[0].props.uses).toBe("actions/checkout
|
|
547
|
+
expect(steps[0].props.uses).toBe(actionRef("actions/checkout" as PinnedAction));
|
|
547
548
|
expect(steps[1].props.run).toBe("npm run deploy");
|
|
548
549
|
});
|
|
549
550
|
|
|
@@ -630,7 +631,7 @@ describe("GoCI", () => {
|
|
|
630
631
|
test("buildJob uses setup-go with default version", () => {
|
|
631
632
|
const result = GoCI({});
|
|
632
633
|
const steps = propsOf(result.buildJob).steps as Array<{ props: Record<string, unknown> }>;
|
|
633
|
-
expect(steps[1].props.uses).toBe("actions/setup-go
|
|
634
|
+
expect(steps[1].props.uses).toBe(actionRef("actions/setup-go" as PinnedAction));
|
|
634
635
|
expect((steps[1].props.with as Record<string, string>)["go-version"]).toBe("1.22");
|
|
635
636
|
});
|
|
636
637
|
|
|
@@ -667,7 +668,7 @@ describe("GoCI", () => {
|
|
|
667
668
|
test("lintJob uses golangci-lint-action", () => {
|
|
668
669
|
const result = GoCI({});
|
|
669
670
|
const steps = propsOf(result.lintJob!).steps as Array<{ props: Record<string, unknown> }>;
|
|
670
|
-
expect(steps[2].props.uses).toBe("golangci/golangci-lint-action
|
|
671
|
+
expect(steps[2].props.uses).toBe(actionRef("golangci/golangci-lint-action" as PinnedAction));
|
|
671
672
|
});
|
|
672
673
|
|
|
673
674
|
test("uses custom runsOn", () => {
|
|
@@ -686,7 +687,7 @@ describe("defaults overrides", () => {
|
|
|
686
687
|
defaults: { step: { id: "my-checkout" } },
|
|
687
688
|
});
|
|
688
689
|
expect(propsOf(result.step).id).toBe("my-checkout");
|
|
689
|
-
expect(propsOf(result.step).uses).toBe("actions/checkout
|
|
690
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/checkout" as PinnedAction));
|
|
690
691
|
});
|
|
691
692
|
|
|
692
693
|
test("SetupNode defaults override step props", () => {
|
|
@@ -769,7 +770,7 @@ describe("defaults overrides", () => {
|
|
|
769
770
|
defaults: { step: { id: "upload" } },
|
|
770
771
|
});
|
|
771
772
|
expect(propsOf(result.step).id).toBe("upload");
|
|
772
|
-
expect(propsOf(result.step).uses).toBe("actions/upload-artifact
|
|
773
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/upload-artifact" as PinnedAction));
|
|
773
774
|
});
|
|
774
775
|
|
|
775
776
|
test("CacheAction defaults override step props", () => {
|
|
@@ -779,6 +780,6 @@ describe("defaults overrides", () => {
|
|
|
779
780
|
defaults: { step: { id: "cache-step" } },
|
|
780
781
|
});
|
|
781
782
|
expect(propsOf(result.step).id).toBe("cache-step");
|
|
782
|
-
expect(propsOf(result.step).uses).toBe("actions/cache
|
|
783
|
+
expect(propsOf(result.step).uses).toBe(actionRef("actions/cache" as PinnedAction));
|
|
783
784
|
});
|
|
784
785
|
});
|
|
@@ -49,7 +49,7 @@ export const DockerBuild = Composite((props: DockerBuildProps) => {
|
|
|
49
49
|
|
|
50
50
|
const login = new Step({
|
|
51
51
|
name: "Log in to container registry",
|
|
52
|
-
uses: "docker/login-action
|
|
52
|
+
uses: actionRef("docker/login-action"),
|
|
53
53
|
with: {
|
|
54
54
|
registry,
|
|
55
55
|
username: "${{ github.actor }}",
|
|
@@ -59,7 +59,7 @@ export const DockerBuild = Composite((props: DockerBuildProps) => {
|
|
|
59
59
|
|
|
60
60
|
const setupBuildx = new Step({
|
|
61
61
|
name: "Set up Docker Buildx",
|
|
62
|
-
uses: "docker/setup-buildx-action
|
|
62
|
+
uses: actionRef("docker/setup-buildx-action"),
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
// Build tags for metadata action
|
|
@@ -71,7 +71,7 @@ export const DockerBuild = Composite((props: DockerBuildProps) => {
|
|
|
71
71
|
const metadata = new Step({
|
|
72
72
|
name: "Extract metadata",
|
|
73
73
|
id: "meta",
|
|
74
|
-
uses: "docker/metadata-action
|
|
74
|
+
uses: actionRef("docker/metadata-action"),
|
|
75
75
|
with: {
|
|
76
76
|
images: imageName,
|
|
77
77
|
tags: tags.join("\n"),
|
|
@@ -98,7 +98,7 @@ export const DockerBuild = Composite((props: DockerBuildProps) => {
|
|
|
98
98
|
|
|
99
99
|
const buildPush = new Step({
|
|
100
100
|
name: "Build and push",
|
|
101
|
-
uses: "docker/build-push-action
|
|
101
|
+
uses: actionRef("docker/build-push-action"),
|
|
102
102
|
with: buildPushWith,
|
|
103
103
|
});
|
|
104
104
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
2
|
+
import { actionRef } from "../action-pins";
|
|
2
3
|
import { Step } from "../generated/index";
|
|
3
4
|
|
|
4
5
|
export interface DownloadArtifactProps {
|
|
@@ -19,7 +20,7 @@ export const DownloadArtifact = Composite((props: DownloadArtifactProps) => {
|
|
|
19
20
|
|
|
20
21
|
const step = new Step(mergeDefaults({
|
|
21
22
|
name: "Download Artifact",
|
|
22
|
-
uses: "actions/download-artifact
|
|
23
|
+
uses: actionRef("actions/download-artifact"),
|
|
23
24
|
...(Object.keys(withObj).length > 0 ? { with: withObj } : {}),
|
|
24
25
|
}, defaults?.step));
|
|
25
26
|
|
package/src/composites/go-ci.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const GoCI = Composite((props: GoCIProps) => {
|
|
|
38
38
|
new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
|
|
39
39
|
new Step({
|
|
40
40
|
name: "Setup Go",
|
|
41
|
-
uses: "actions/setup-go
|
|
41
|
+
uses: actionRef("actions/setup-go"),
|
|
42
42
|
with: { "go-version": goVersion },
|
|
43
43
|
}),
|
|
44
44
|
new Step({ name: "Build", run: buildCommand }),
|
|
@@ -52,7 +52,7 @@ export const GoCI = Composite((props: GoCIProps) => {
|
|
|
52
52
|
new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
|
|
53
53
|
new Step({
|
|
54
54
|
name: "Setup Go",
|
|
55
|
-
uses: "actions/setup-go
|
|
55
|
+
uses: actionRef("actions/setup-go"),
|
|
56
56
|
with: { "go-version": goVersion },
|
|
57
57
|
}),
|
|
58
58
|
new Step({ name: "Test", run: testCommand }),
|
|
@@ -68,12 +68,12 @@ export const GoCI = Composite((props: GoCIProps) => {
|
|
|
68
68
|
new Step({ name: "Checkout", uses: actionRef("actions/checkout") }),
|
|
69
69
|
new Step({
|
|
70
70
|
name: "Setup Go",
|
|
71
|
-
uses: "actions/setup-go
|
|
71
|
+
uses: actionRef("actions/setup-go"),
|
|
72
72
|
with: { "go-version": goVersion },
|
|
73
73
|
}),
|
|
74
74
|
new Step({
|
|
75
75
|
name: "Lint",
|
|
76
|
-
uses: "golangci/golangci-lint-action
|
|
76
|
+
uses: actionRef("golangci/golangci-lint-action"),
|
|
77
77
|
with: { args: lintCommand },
|
|
78
78
|
}),
|
|
79
79
|
],
|
|
@@ -51,7 +51,7 @@ const cacheConfig = {
|
|
|
51
51
|
cache: undefined,
|
|
52
52
|
installCmd: "bun install --frozen-lockfile",
|
|
53
53
|
runPrefix: "bun run",
|
|
54
|
-
setupAction: "oven-sh/setup-bun
|
|
54
|
+
setupAction: actionRef("oven-sh/setup-bun"),
|
|
55
55
|
},
|
|
56
56
|
} as const;
|
|
57
57
|
|
|
@@ -79,7 +79,7 @@ export const NodePipeline = Composite((props: NodePipelineProps) => {
|
|
|
79
79
|
const buildCheckout = new Step({ name: "Checkout", uses: actionRef("actions/checkout") });
|
|
80
80
|
|
|
81
81
|
const buildSetup = isBun
|
|
82
|
-
? new Step({ name: "Setup Bun", uses: "oven-sh/setup-bun
|
|
82
|
+
? new Step({ name: "Setup Bun", uses: actionRef("oven-sh/setup-bun") })
|
|
83
83
|
: new Step({
|
|
84
84
|
name: "Setup Node.js",
|
|
85
85
|
uses: actionRef("actions/setup-node"),
|
|
@@ -92,7 +92,7 @@ export const NodePipeline = Composite((props: NodePipelineProps) => {
|
|
|
92
92
|
|
|
93
93
|
const buildUpload = new Step({
|
|
94
94
|
name: "Upload build artifacts",
|
|
95
|
-
uses: "actions/upload-artifact
|
|
95
|
+
uses: actionRef("actions/upload-artifact"),
|
|
96
96
|
with: {
|
|
97
97
|
name: artifactName,
|
|
98
98
|
path: buildArtifactPaths.join("\n"),
|
|
@@ -109,7 +109,7 @@ export const NodePipeline = Composite((props: NodePipelineProps) => {
|
|
|
109
109
|
const testCheckout = new Step({ name: "Checkout", uses: actionRef("actions/checkout") });
|
|
110
110
|
|
|
111
111
|
const testSetup = isBun
|
|
112
|
-
? new Step({ name: "Setup Bun", uses: "oven-sh/setup-bun
|
|
112
|
+
? new Step({ name: "Setup Bun", uses: actionRef("oven-sh/setup-bun") })
|
|
113
113
|
: new Step({
|
|
114
114
|
name: "Setup Node.js",
|
|
115
115
|
uses: actionRef("actions/setup-node"),
|
|
@@ -120,7 +120,7 @@ export const NodePipeline = Composite((props: NodePipelineProps) => {
|
|
|
120
120
|
|
|
121
121
|
const testDownload = new Step({
|
|
122
122
|
name: "Download build artifacts",
|
|
123
|
-
uses: "actions/download-artifact
|
|
123
|
+
uses: actionRef("actions/download-artifact"),
|
|
124
124
|
with: { name: artifactName },
|
|
125
125
|
});
|
|
126
126
|
|