mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-09-19 01:09:55 +08:00
119 lines
5.7 KiB
TypeScript
119 lines
5.7 KiB
TypeScript
import { spawnSync } from "node:child_process"
|
|
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from "node:fs"
|
|
import { tmpdir } from "node:os"
|
|
import path from "node:path"
|
|
import { describe, expect, setDefaultTimeout, test } from "bun:test"
|
|
|
|
// Executes the scratch-root preamble exactly as it ships in each skill, instead of only
|
|
// string-matching it (#1285).
|
|
//
|
|
// scratch-root-contract.test.ts pins the *shape* of these blocks. That is what let
|
|
// `install -d -m 700` survive: the string was present and correct-looking, and on POSIX it
|
|
// worked, so nothing was red — while on native Windows Git Bash (a supported shell per
|
|
// AGENTS.md) `install -d` fails with "cannot change permissions", tripping the block's own
|
|
// `|| exit 1` and aborting scratch setup for all 12 skills before any of them did work.
|
|
//
|
|
// The repo already learned this once: "Verify the literal documented invocation, not a
|
|
// hand-adjusted variant" (docs/solutions/conventions/resolve-python-interpreter-not-python3.md).
|
|
// A shape assertion cannot catch a primitive that is merely unavailable on one host; only
|
|
// running it can. This file runs on every platform, and CI runs it under real win32.
|
|
|
|
const SKILLS_ROOT = path.join(process.cwd(), "skills")
|
|
const ROOT_ASSIGNMENT = 'SCRATCH_ROOT="/tmp/compound-engineering-$(id -u)"'
|
|
const GUARD_END = 'chmod 700 "$SCRATCH_ROOT"'
|
|
|
|
/** A POSIX shell, which these skills require (AGENTS.md: bash on macOS/Linux, Git Bash on
|
|
* Windows). `sh`/`bash` are not on PATH for a non-shell process on Windows, so fall back to
|
|
* the Git for Windows install paths before giving up. */
|
|
function posixShell(): string {
|
|
const candidates = process.platform === "win32"
|
|
? ["bash", "sh", "C:/Program Files/Git/bin/bash.exe", "C:/Program Files/Git/usr/bin/sh.exe"]
|
|
: ["sh"]
|
|
for (const candidate of candidates) {
|
|
if (spawnSync(candidate, ["-c", "exit 0"]).status === 0) return candidate
|
|
}
|
|
throw new Error(`no POSIX shell found (tried ${candidates.join(", ")})`)
|
|
}
|
|
|
|
const SHELL = posixShell()
|
|
|
|
// One shell spawn per shipped block (16 and growing). That is ~2s alone but well past bun's
|
|
// 5s default once `bun run test --parallel` has every worker competing for process creation,
|
|
// which is slowest exactly where this test matters most (Windows).
|
|
setDefaultTimeout(120_000)
|
|
|
|
function contractFiles(root: string): string[] {
|
|
return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
|
|
const absolute = path.join(root, entry.name)
|
|
if (entry.isDirectory()) return contractFiles(absolute)
|
|
return entry.isFile() && /\.(md|py|sh)$/.test(entry.name) ? [absolute] : []
|
|
})
|
|
}
|
|
|
|
/** Every shipped preamble: from the root assignment through the end of its closing chmod line. */
|
|
function preambles(): { file: string; script: string }[] {
|
|
const found: { file: string; script: string }[] = []
|
|
for (const file of contractFiles(SKILLS_ROOT)) {
|
|
const content = readFileSync(file, "utf8")
|
|
let offset = content.indexOf(ROOT_ASSIGNMENT)
|
|
while (offset >= 0) {
|
|
const guard = content.indexOf(GUARD_END, offset)
|
|
expect(guard, `${file}: no closing ${GUARD_END} after the root assignment`).toBeGreaterThan(-1)
|
|
const lineEnd = content.indexOf("\n", guard)
|
|
found.push({
|
|
file: path.relative(process.cwd(), file),
|
|
script: content.slice(offset, lineEnd === -1 ? undefined : lineEnd),
|
|
})
|
|
offset = content.indexOf(ROOT_ASSIGNMENT, offset + ROOT_ASSIGNMENT.length)
|
|
}
|
|
}
|
|
return found
|
|
}
|
|
|
|
describe("scratch-root preamble executes on this host", () => {
|
|
test("every shipped preamble creates its root and exits 0", () => {
|
|
const blocks = preambles()
|
|
// Guard the guard: an extractor that quietly matches nothing is the failure mode here, and
|
|
// it would look identical to a clean pass. Pin the count of *blocks* (16 across 12 files),
|
|
// not files — a floor of 12 would let four blocks disappear before this went red. `>=` so
|
|
// adding a skill does not break the suite; a drop means the extractor or the prose moved.
|
|
expect(blocks.length).toBeGreaterThanOrEqual(16)
|
|
|
|
const parent = mkdtempSync(path.join(tmpdir(), "ce-preamble-"))
|
|
try {
|
|
const failures: string[] = []
|
|
blocks.forEach(({ file, script }, index) => {
|
|
// Redirect the real root into a disposable one; everything else runs verbatim.
|
|
const rooted = script.replaceAll("/tmp/compound-engineering-$(id -u)", `$CE_ROOT/${index}`)
|
|
expect(rooted, `${file}: root redirect did not apply`).not.toContain("$(id -u)")
|
|
const r = spawnSync(SHELL, ["-c", `CE_ROOT="$1"\n${rooted}\nprintf %s "$SCRATCH_ROOT"`,
|
|
"sh", parent], { encoding: "utf8" })
|
|
if (r.status !== 0 || !r.stdout || !existsSync(r.stdout)) {
|
|
failures.push(`${file} [${index}] status=${r.status} stderr=${(r.stderr || "").trim()}`)
|
|
}
|
|
})
|
|
expect(failures).toEqual([])
|
|
} finally {
|
|
spawnSync("chmod", ["-R", "u+rwx", parent])
|
|
rmSync(parent, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
test("the preamble is re-entrant over an existing root", () => {
|
|
// Skills run back to back as the same user; the second invocation must not abort on a
|
|
// root the first one already created.
|
|
const blocks = preambles()
|
|
const parent = mkdtempSync(path.join(tmpdir(), "ce-preamble-reentrant-"))
|
|
try {
|
|
const rooted = blocks[0].script.replaceAll("/tmp/compound-engineering-$(id -u)", "$CE_ROOT/r")
|
|
const run = () => spawnSync(SHELL, ["-c", `CE_ROOT="$1"\n${rooted}`, "sh", parent],
|
|
{ encoding: "utf8" })
|
|
expect(run().status, "first run").toBe(0)
|
|
expect(run().status, "second run over an existing root").toBe(0)
|
|
} finally {
|
|
spawnSync("chmod", ["-R", "u+rwx", parent])
|
|
rmSync(parent, { recursive: true, force: true })
|
|
}
|
|
})
|
|
})
|