fix(review): harden ce-review base resolution (#452)

This commit is contained in:
Trevin Chow
2026-03-30 01:10:45 -07:00
committed by GitHub
parent e872e15efa
commit 638b38abd2
5 changed files with 331 additions and 10 deletions

View File

@@ -0,0 +1,300 @@
import { describe, expect, test } from "bun:test"
import { promises as fs } from "fs"
import os from "os"
import path from "path"
import { pathToFileURL } from "url"
const gitEnv = {
...process.env,
GIT_AUTHOR_NAME: "Test",
GIT_AUTHOR_EMAIL: "test@example.com",
GIT_COMMITTER_NAME: "Test",
GIT_COMMITTER_EMAIL: "test@example.com",
}
const resolveBaseScript = path.join(
import.meta.dir,
"..",
"plugins",
"compound-engineering",
"skills",
"ce-review",
"references",
"resolve-base.sh",
)
type RunResult = {
exitCode: number
stderr: string
stdout: string
}
async function runCommand(
cmd: string[],
cwd: string,
env?: NodeJS.ProcessEnv,
): Promise<RunResult> {
const proc = Bun.spawn(cmd, {
cwd,
env: env ?? process.env,
stderr: "pipe",
stdout: "pipe",
})
const [exitCode, stdout, stderr] = await Promise.all([
proc.exited,
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
])
return { exitCode, stderr, stdout }
}
async function runGit(args: string[], cwd: string, env?: NodeJS.ProcessEnv): Promise<string> {
const result = await runCommand(["git", ...args], cwd, env ?? gitEnv)
if (result.exitCode !== 0) {
throw new Error(
`git ${args.join(" ")} failed (exit ${result.exitCode}).\nstdout: ${result.stdout}\nstderr: ${result.stderr}`,
)
}
return result.stdout.trim()
}
async function initRepo(initialBranch = "main"): Promise<string> {
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-repo-"))
await runGit(["init", "-b", initialBranch], repoRoot)
return repoRoot
}
async function commitFile(
repoRoot: string,
relativePath: string,
content: string,
message: string,
): Promise<string> {
const filePath = path.join(repoRoot, relativePath)
await fs.mkdir(path.dirname(filePath), { recursive: true })
await fs.writeFile(filePath, content)
await runGit(["add", relativePath], repoRoot)
await runGit(["commit", "-m", message], repoRoot)
return runGit(["rev-parse", "HEAD"], repoRoot)
}
async function writeExecutable(filePath: string, content: string): Promise<void> {
await fs.writeFile(filePath, content)
await fs.chmod(filePath, 0o755)
}
async function createStubBin(mode: "gh-fails" | "pr-metadata"): Promise<string> {
const binDir = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-bin-"))
if (mode === "gh-fails") {
await writeExecutable(path.join(binDir, "gh"), "#!/usr/bin/env bash\nexit 1\n")
return binDir
}
await writeExecutable(
path.join(binDir, "gh"),
`#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ge 2 ] && [ "$1" = "pr" ] && [ "$2" = "view" ]; then
printf '%s' '{"baseRefName":"main","url":"https://github.com/EveryInc/compound-engineering-plugin/pull/123"}'
exit 0
fi
exit 1
`,
)
await writeExecutable(
path.join(binDir, "jq"),
`#!/usr/bin/env bun
const args = process.argv.slice(2).filter((arg) => arg !== "-r")
const query = args[args.length - 1] ?? ""
const input = await new Response(Bun.stdin.stream()).text()
const data = input.trim() ? JSON.parse(input) : {}
let output = ""
if (query === ".baseRefName // empty") {
output = data.baseRefName ?? ""
} else if (query === ".url // empty") {
output = data.url ?? ""
} else if (query === ".defaultBranchRef.name") {
output = data.defaultBranchRef?.name ?? ""
} else {
console.error(\`unsupported jq query: \${query}\`)
process.exit(1)
}
process.stdout.write(String(output))
`,
)
return binDir
}
async function runResolveBase(
repoRoot: string,
stubBin: string,
extraEnv?: NodeJS.ProcessEnv,
): Promise<RunResult> {
return runCommand(["bash", resolveBaseScript], repoRoot, {
...gitEnv,
PATH: `${stubBin}:${process.env.PATH ?? ""}`,
...extraEnv,
})
}
describe("resolve-base.sh", () => {
test("prefers the PR base remote from gh metadata over origin", async () => {
const repoRoot = await initRepo()
const initialSha = await commitFile(repoRoot, "history.txt", "a\n", "initial")
const upstreamMainSha = await commitFile(repoRoot, "history.txt", "b\n", "main advance")
await runGit(["checkout", "-b", "feature"], repoRoot)
await commitFile(repoRoot, "feature.txt", "feature\n", "feature change")
await runGit(["checkout", "-b", "fork-main", initialSha], repoRoot)
const forkMainSha = await commitFile(repoRoot, "fork.txt", "fork\n", "fork main diverges")
await runGit(["checkout", "feature"], repoRoot)
await runGit(["remote", "add", "origin", "git@github.com:someone/fork.git"], repoRoot)
await runGit(
["remote", "add", "upstream", "git@github.com:EveryInc/compound-engineering-plugin.git"],
repoRoot,
)
await runGit(["update-ref", "refs/remotes/origin/main", forkMainSha], repoRoot)
await runGit(["update-ref", "refs/remotes/upstream/main", upstreamMainSha], repoRoot)
const stubBin = await createStubBin("pr-metadata")
const result = await runResolveBase(repoRoot, stubBin)
expect(result.exitCode).toBe(0)
expect(result.stdout.trim()).toBe(`BASE:${upstreamMainSha}`)
})
test("falls back to a local base branch when origin is absent", async () => {
const repoRoot = await initRepo()
await commitFile(repoRoot, "history.txt", "a\n", "initial")
const mainSha = await commitFile(repoRoot, "history.txt", "b\n", "main advance")
await runGit(["checkout", "-b", "feature"], repoRoot)
await commitFile(repoRoot, "feature.txt", "feature\n", "feature change")
const stubBin = await createStubBin("gh-fails")
const result = await runResolveBase(repoRoot, stubBin)
expect(result.exitCode).toBe(0)
expect(result.stdout.trim()).toBe(`BASE:${mainSha}`)
})
test("resolves against origin/HEAD in a detached shallow checkout", async () => {
const seedRepo = await initRepo()
await commitFile(seedRepo, "history.txt", "a\n", "initial")
const mainSha = await commitFile(seedRepo, "history.txt", "b\n", "main advance")
await runGit(["checkout", "-b", "feature"], seedRepo)
const featureSha = await commitFile(seedRepo, "feature.txt", "feature\n", "feature change")
await runGit(["checkout", "main"], seedRepo)
const bareRepo = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-remote-"))
await runGit(["init", "--bare", bareRepo], seedRepo)
const bareUrl = pathToFileURL(bareRepo).toString()
await runGit(["remote", "add", "origin", bareUrl], seedRepo)
await runGit(["push", "origin", "main", "feature"], seedRepo)
const checkoutRoot = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-checkout-"))
await runCommand(["git", "clone", "--depth", "1", bareUrl, checkoutRoot], os.tmpdir(), gitEnv)
await runGit(["config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"], checkoutRoot)
await runGit(
["fetch", "--depth", "1", "origin", "main:refs/remotes/origin/main", "feature:refs/remotes/origin/feature"],
checkoutRoot,
)
await runGit(["symbolic-ref", "refs/remotes/origin/HEAD", "refs/remotes/origin/main"], checkoutRoot)
await runGit(["checkout", "--detach", "origin/feature"], checkoutRoot)
const originMain = await runGit(["rev-parse", "--verify", "origin/main"], checkoutRoot)
expect(originMain).toBe(mainSha)
const originFeature = await runGit(["rev-parse", "--verify", "origin/feature"], checkoutRoot)
expect(originFeature).toBe(featureSha)
const originHead = await runGit(
["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"],
checkoutRoot,
)
expect(originHead).toBe("origin/main")
const stubBin = await createStubBin("gh-fails")
const result = await runResolveBase(checkoutRoot, stubBin)
expect(result.exitCode).toBe(0)
expect(result.stdout.trim()).toBe(`BASE:${mainSha}`)
})
test("unshallows the PR base remote in a detached shallow checkout", async () => {
const upstreamSeed = await initRepo()
const initialSha = await commitFile(upstreamSeed, "history.txt", "a\n", "initial")
const upstreamMainSha = await commitFile(upstreamSeed, "history.txt", "b\n", "upstream main")
await runGit(["checkout", "-b", "feature"], upstreamSeed)
const featureSha = await commitFile(upstreamSeed, "feature.txt", "feature\n", "feature change")
const forkSeed = await initRepo()
await commitFile(forkSeed, "history.txt", "a\n", "initial")
const forkMainSha = await commitFile(forkSeed, "fork.txt", "fork\n", "fork main diverges")
const remotesRoot = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-remotes-"))
const upstreamBare = path.join(
remotesRoot,
"github.com",
"EveryInc",
"compound-engineering-plugin.git",
)
await fs.mkdir(path.dirname(upstreamBare), { recursive: true })
await runGit(["init", "--bare", upstreamBare], upstreamSeed)
const upstreamUrl = pathToFileURL(upstreamBare).toString()
await runGit(["remote", "add", "origin", upstreamUrl], upstreamSeed)
await runGit(["push", "origin", "main", "feature"], upstreamSeed)
const forkBare = path.join(remotesRoot, "github.com", "someone", "fork.git")
await fs.mkdir(path.dirname(forkBare), { recursive: true })
await runGit(["init", "--bare", forkBare], forkSeed)
const forkUrl = pathToFileURL(forkBare).toString()
await runGit(["remote", "add", "origin", forkUrl], forkSeed)
await runGit(["push", "origin", "main"], forkSeed)
const checkoutParent = await fs.mkdtemp(path.join(os.tmpdir(), "resolve-base-pr-shallow-"))
const checkoutRoot = path.join(checkoutParent, "checkout")
await runCommand(
["git", "clone", "--depth", "1", "--branch", "feature", upstreamUrl, checkoutRoot],
os.tmpdir(),
gitEnv,
)
await runGit(["checkout", "--detach", featureSha], checkoutRoot)
await runGit(["remote", "rename", "origin", "upstream"], checkoutRoot)
await runGit(["remote", "add", "origin", forkUrl], checkoutRoot)
await runGit(["fetch", "--depth", "1", "origin", "main"], checkoutRoot)
await runGit(["update-ref", "refs/remotes/origin/main", forkMainSha], checkoutRoot)
await runGit(["branch", "-D", "feature"], checkoutRoot)
const stubBin = await createStubBin("pr-metadata")
const result = await runResolveBase(checkoutRoot, stubBin)
expect(result.exitCode).toBe(0)
expect(result.stdout.trim()).toBe(`BASE:${upstreamMainSha}`)
})
test("emits ERROR output when no base branch can be resolved", async () => {
const repoRoot = await initRepo("scratch")
await commitFile(repoRoot, "history.txt", "a\n", "initial")
const stubBin = await createStubBin("gh-fails")
const result = await runResolveBase(repoRoot, stubBin)
expect(result.exitCode).toBe(0)
expect(result.stdout.trim()).toBe(
"ERROR:Unable to resolve review base branch locally. Fetch the base branch and rerun, or provide a PR number so the review scope can be determined from PR metadata.",
)
})
})