mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 10:12:50 +00:00
test: exercise CI trust and remote cache authority policies
This commit is contained in:
1 parent
38286d4bc3
commit
b05f472592
2 files changed
+104
-25
No files matched your search
@@ -10,6 +10,7 @@ import {
|
||||
writeFile,
|
||||
} from "node:fs/promises";
|
||||
import { delimiter, join } from "node:path";
|
||||
import { runInNewContext } from "node:vm";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
@@ -17,6 +18,69 @@ import { z } from "zod";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
// These policy expressions use only JS-compatible boolean operators, property
|
||||
// access, literals and format; exercise the parsed workflow rather than copies.
|
||||
const evaluatePolicy = (
|
||||
expression: string,
|
||||
context: Record<string, unknown>,
|
||||
): unknown =>
|
||||
runInNewContext(
|
||||
expression.replace(/^\$\{\{\s*|\s*\}\}$/gu, ""),
|
||||
{
|
||||
...context,
|
||||
format: (template: string, value: string) =>
|
||||
template.replace("{0}", value),
|
||||
},
|
||||
{ timeout: 100 },
|
||||
);
|
||||
|
||||
const assertRetentionPolicy = (expression: string): void => {
|
||||
for (const conclusion of [
|
||||
"success",
|
||||
"failure",
|
||||
"timed_out",
|
||||
"cancelled",
|
||||
"skipped",
|
||||
"action_required",
|
||||
]) {
|
||||
for (const event of [
|
||||
"push",
|
||||
"schedule",
|
||||
"workflow_dispatch",
|
||||
"pull_request",
|
||||
]) {
|
||||
for (const [repositoryName, branch] of [
|
||||
["owner/repo", "main"],
|
||||
["untrusted/fork", "main"],
|
||||
["owner/repo", "feature"],
|
||||
]) {
|
||||
const permitted =
|
||||
repositoryName === "owner/repo" &&
|
||||
branch === "main" &&
|
||||
event !== "pull_request" &&
|
||||
["success", "failure", "timed_out", "cancelled"].includes(conclusion);
|
||||
expect(
|
||||
evaluatePolicy(expression, {
|
||||
github: {
|
||||
repository: "owner/repo",
|
||||
event: {
|
||||
repository: { default_branch: "main" },
|
||||
workflow_run: {
|
||||
conclusion,
|
||||
event,
|
||||
head_repository: { full_name: repositoryName },
|
||||
head_branch: branch,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
`${repositoryName}:${branch}:${event}:${conclusion}`,
|
||||
).toBe(permitted);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const repository = process.cwd();
|
||||
const actionPath = join(repository, ".github/actions/npm-cache/action.yml");
|
||||
const readAction = async () =>
|
||||
@@ -512,30 +576,7 @@ it("deletes superseded caches only from the trusted follow-up workflow", async (
|
||||
actions: "write",
|
||||
});
|
||||
const job = workflow.jobs.retain;
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.conclusion == 'success'",
|
||||
);
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.conclusion == 'failure'",
|
||||
);
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.conclusion == 'timed_out'",
|
||||
);
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.conclusion == 'cancelled'",
|
||||
);
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.head_repository.full_name == github.repository",
|
||||
);
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.head_branch == github.event.repository.default_branch",
|
||||
);
|
||||
expect(job?.if).toContain("github.event.workflow_run.event == 'push'");
|
||||
expect(job?.if).toContain("github.event.workflow_run.event == 'schedule'");
|
||||
expect(job?.if).toContain(
|
||||
"github.event.workflow_run.event == 'workflow_dispatch'",
|
||||
);
|
||||
expect(job?.if).not.toContain("pull_request");
|
||||
assertRetentionPolicy(z.string().parse(job?.if));
|
||||
const steps = job?.steps ?? [];
|
||||
expect(steps.some((step) => step.run === "npm ci")).toBe(false);
|
||||
const checkouts = steps.filter(
|
||||
@@ -729,6 +770,43 @@ it("exports turbo remote-cache credentials wherever a workflow runs turbo", asyn
|
||||
expect(cache, `${file}:${name}`).not.toContain("local:rw,remote:r'");
|
||||
} else expect(cache, `${file}:${name}`).toContain("local:rw,remote:r'");
|
||||
expect(cache, `${file}:${name}`).toContain("local:rw,remote:rw");
|
||||
for (const event of [
|
||||
"push",
|
||||
"schedule",
|
||||
"workflow_dispatch",
|
||||
"pull_request",
|
||||
"workflow_call",
|
||||
]) {
|
||||
for (const ref of ["refs/heads/main", "refs/heads/feature"]) {
|
||||
for (const grant of [false, true]) {
|
||||
const permitted =
|
||||
(grantsReleaseWrite && grant) ||
|
||||
(ref === "refs/heads/main" &&
|
||||
(["push", "schedule", "workflow_dispatch"].includes(event) ||
|
||||
(grantsCalledEvent && event === "workflow_call")));
|
||||
const context = {
|
||||
github: {
|
||||
ref,
|
||||
event_name: event,
|
||||
event: { repository: { default_branch: "main" } },
|
||||
},
|
||||
inputs: { remote_cache_write: grant },
|
||||
secrets: { TURBO_TOKEN: "recording-token" },
|
||||
};
|
||||
const label = `${file}:${name}:${event}:${ref}:${grant}`;
|
||||
expect(evaluatePolicy(token, context), label).toBe(
|
||||
permitted ? "recording-token" : "",
|
||||
);
|
||||
expect(evaluatePolicy(cache, context), label).toBe(
|
||||
permitted
|
||||
? "local:rw,remote:rw"
|
||||
: grantsReleaseWrite
|
||||
? "local:rw"
|
||||
: "local:rw,remote:r",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(env.TURBO_TEAM, `${file}:${name}`).toBe("${{ vars.TURBO_TEAM }}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +501,7 @@ it("selects only a trusted successful ancestor from recent CI push runs", async
|
||||
}) => {
|
||||
skip(process.platform === "win32", "the fake gh is a POSIX shell script");
|
||||
const { directory, base } = await fixture();
|
||||
const foreignBase = await change(directory, ["README.md"]);
|
||||
const head = await change(directory, ["src/evm/client.ts"]);
|
||||
const bin = join(directory, "fake-bin");
|
||||
await mkdir(bin);
|
||||
@@ -517,7 +518,7 @@ it("selects only a trusted successful ancestor from recent CI push runs", async
|
||||
conclusion: "success",
|
||||
head_branch: "main",
|
||||
head_repository: { full_name: "untrusted/fork" },
|
||||
head_sha: base,
|
||||
head_sha: foreignBase,
|
||||
},
|
||||
{
|
||||
event: "push",
|
||||
|
||||
Reference in new issue
Block a user