mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-06-19 15:41:46 +02:00
337c01ee38
The plugin convention places executable scripts in `scripts/` and reference prose/schemas in `references/`. resolve-base.sh was the only `.sh` file living in any `references/` directory across the plugin. Move it to the conventional location and update the four callsites in SKILL.md, the script's own usage comment, and the two test files that referenced the old path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
301 lines
11 KiB
TypeScript
301 lines
11 KiB
TypeScript
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-code-review",
|
|
"scripts",
|
|
"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.",
|
|
)
|
|
})
|
|
})
|