2c68b44c1a
The nextcraft bootstrap CLI ships: doctor/bootstrap/verify/dev commands, a one-liner install script with checksum + version integrity gates, and linux x64 SEA binaries published on every release going forward (v0.3.2 onward). Fresh-clone E2E proven; 34 CLI tests + full monorepo gates green. Escalation note: merge_to_main hook — proceeding per full autonomy + founder directive D-016 (streamlined install + bootstrap CLI + ongoing binaries, recorded at P0 SPECIFY). ---ci--- phase: 4 milestone: v0.4 status: complete requirements: covered: [REQ-4-001, REQ-4-002, REQ-4-003, REQ-4-004, REQ-4-005] partial: [] ---/ci---
72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdtempSync, existsSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const cliDir = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const repoRoot = join(cliDir, "..", "..");
|
|
|
|
test("fresh-clone E2E: clone -> doctor -> bootstrap -> verify (happy path, transcript)", async () => {
|
|
const work = mkdtempSync(join(tmpdir(), "nc-e2e-"));
|
|
const cloneDir = join(work, "nextcraft");
|
|
|
|
const clone = spawnSync(
|
|
"git",
|
|
["clone", "--quiet", "--no-hardlinks", repoRoot, cloneDir],
|
|
{ encoding: "utf8", timeout: 120000 },
|
|
);
|
|
assert.equal(clone.status, 0, `clone failed: ${clone.stderr}`);
|
|
|
|
const binary = join(repoRoot, "apps", "cli", "dist", "nextcraft-linux-x64");
|
|
const cli = existsSync(binary)
|
|
? binary
|
|
: join(cliDir, "node_modules", ".bin", "tsx");
|
|
|
|
const transcript: string[] = [];
|
|
const step = (name: string, args: string[], expect: number, timeoutMs: number) => {
|
|
const { NODE_TEST_CONTEXT, ...restEnv } = process.env as Record<string, string | undefined>;
|
|
const env = {
|
|
...restEnv,
|
|
PATH: [join(process.env.HOME ?? "/home", ".local/bin"), process.env.PATH].filter(Boolean).join(":"),
|
|
NEXTCRAFT_VERSION: "v-e2e",
|
|
} as Record<string, string | undefined>;
|
|
const run = spawnSync(cli, existsSync(binary) ? args : [join(cliDir, "src", "index.ts"), ...args], {
|
|
cwd: cloneDir,
|
|
encoding: "utf8",
|
|
timeout: timeoutMs,
|
|
env,
|
|
});
|
|
transcript.push(`$ nextcraft ${args.join(" ")} -> exit ${run.status}`);
|
|
if (run.status !== expect) {
|
|
transcript.push(run.stdout, run.stderr);
|
|
assert.fail(
|
|
`${name} exited ${run.status} (expected ${expect})\nTRANSCRIPT:\n${transcript.join("\n")}\nstderr: ${run.stderr}`,
|
|
);
|
|
}
|
|
return run.stdout + run.stderr;
|
|
};
|
|
|
|
const doctorOut = step("doctor", ["doctor"], 0, 60000);
|
|
assert.ok(doctorOut.includes("node"), "doctor mentions node");
|
|
assert.ok(doctorOut.includes("pnpm"), "doctor mentions pnpm");
|
|
|
|
step("bootstrap", ["bootstrap"], 0, 600000);
|
|
|
|
const verifyOut = step("verify", ["verify"], 0, 120000);
|
|
assert.ok(verifyOut.includes("venv"), "verify covers venv");
|
|
assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".env")), ".env created in clone");
|
|
assert.ok(existsSync(join(cloneDir, "apps", "ai-service", ".venv")), "venv created in clone");
|
|
assert.ok(
|
|
existsSync(join(cloneDir, "node_modules", ".pnpm")),
|
|
"workspace node_modules present",
|
|
);
|
|
|
|
const envExample = readFileSync(join(repoRoot, "apps", "ai-service", ".env.example"), "utf8");
|
|
const envClone = readFileSync(join(cloneDir, "apps", "ai-service", ".env"), "utf8");
|
|
assert.equal(envClone, envExample, ".env content matches template");
|
|
|
|
console.log("E2E TRANSCRIPT:\n" + transcript.join("\n"));
|
|
}); |