| 1 | import { expect, test } from "bun:test"; |
| 2 | import { |
| 3 | mkdtempSync, |
| 4 | mkdirSync, |
| 5 | writeFileSync, |
| 6 | readFileSync, |
| 7 | existsSync, |
| 8 | rmSync, |
| 9 | } from "node:fs"; |
| 10 | import { tmpdir } from "node:os"; |
| 11 | import { join, resolve } from "node:path"; |
| 12 | import type { Manifest } from "../src/Manifest.ts"; |
| 13 | |
| 14 | const cli = resolve(import.meta.dir, "../src/cli/bin.ts"); |
| 15 | |
| 16 | test("CLI selects PR packages, honors overrides, and clears empty output", async () => { |
| 17 | const cwd = mkdtempSync(join(tmpdir(), "pkg-selection-")); |
| 18 | const env = { ...process.env }; |
| 19 | delete env.GITHUB_EVENT_NAME; |
| 20 | delete env.GITHUB_OUTPUT; |
| 21 | const run = async (args: string[]) => { |
| 22 | const child = Bun.spawn(args, { cwd, env, stdout: "pipe", stderr: "pipe" }); |
| 23 | const [stdout, stderr, code] = await Promise.all([ |
| 24 | new Response(child.stdout).text(), |
| 25 | new Response(child.stderr).text(), |
| 26 | child.exited, |
| 27 | ]); |
| 28 | if (code !== 0) throw new Error(`${args.join(" ")}: ${stdout}\n${stderr}`); |
| 29 | return stdout.trim(); |
| 30 | }; |
| 31 | try { |
| 32 | await run(["git", "init", "-q"]); |
| 33 | await run(["git", "config", "user.email", "test@example.com"]); |
| 34 | await run(["git", "config", "user.name", "Test"]); |
| 35 | for (const name of ["core", "aws", "cloudflare"]) { |
| 36 | const dir = join(cwd, "packages", name); |
| 37 | mkdirSync(dir, { recursive: true }); |
| 38 | writeFileSync( |
| 39 | join(dir, "package.json"), |
| 40 | JSON.stringify({ |
| 41 | name, |
| 42 | version: "1.0.0", |
| 43 | dependencies: name === "core" ? {} : { core: "1.0.0" }, |
| 44 | }), |
| 45 | ); |
| 46 | writeFileSync(join(dir, "index.js"), "export const value = 1;"); |
| 47 | } |
| 48 | await run(["git", "add", "."]); |
| 49 | await run(["git", "commit", "-qm", "base"]); |
| 50 | const base = await run(["git", "rev-parse", "HEAD"]); |
| 51 | writeFileSync( |
| 52 | join(cwd, "packages/cloudflare/index.js"), |
| 53 | "export const value = 2;", |
| 54 | ); |
| 55 | await run(["git", "add", "."]); |
| 56 | await run(["git", "commit", "-qm", "change"]); |
| 57 | const head = await run(["git", "rev-parse", "HEAD"]); |
| 58 | const pack = async (...flags: string[]) => { |
| 59 | await run([ |
| 60 | process.execPath, |
| 61 | cli, |
| 62 | "pack", |
| 63 | "--group", |
| 64 | "SDK=./packages/*", |
| 65 | ...flags, |
| 66 | ]); |
| 67 | const manifest: Manifest = JSON.parse( |
| 68 | readFileSync(join(cwd, ".pkg/pkg-manifest.json"), "utf8"), |
| 69 | ); |
| 70 | return manifest.packages.map((pkg) => pkg.name); |
| 71 | }; |
| 72 | expect(await pack("--since", base)).toEqual(["cloudflare", "core"]); |
| 73 | expect(await pack("--since", "HEAD")).toEqual([]); |
| 74 | expect(existsSync(join(cwd, ".pkg/cloudflare.tgz"))).toBe(false); |
| 75 | |
| 76 | env.GITHUB_EVENT_NAME = "pull_request"; |
| 77 | env.GITHUB_EVENT_PATH = join(cwd, "event.json"); |
| 78 | env.GITHUB_OUTPUT = join(cwd, "output"); |
| 79 | const event = { |
| 80 | pull_request: { |
| 81 | base: { sha: base }, |
| 82 | head: { sha: head }, |
| 83 | labels: [] as { name: string }[], |
| 84 | }, |
| 85 | }; |
| 86 | writeFileSync(env.GITHUB_EVENT_PATH, JSON.stringify(event)); |
| 87 | expect(await pack()).toEqual(["cloudflare", "core"]); |
| 88 | expect(readFileSync(env.GITHUB_OUTPUT, "utf8")).toContain( |
| 89 | "package-count=2", |
| 90 | ); |
| 91 | expect(await pack("--all")).toEqual(["aws", "cloudflare", "core"]); |
| 92 | event.pull_request.labels.push({ name: "force-ci" }); |
| 93 | writeFileSync(env.GITHUB_EVENT_PATH, JSON.stringify(event)); |
| 94 | // Repository-specific labels have no meaning to the CLI. |
| 95 | expect(await pack()).toEqual(["cloudflare", "core"]); |
| 96 | } finally { |
| 97 | rmSync(cwd, { recursive: true, force: true }); |
| 98 | } |
| 99 | }, 120_000); |