Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/commands/bench-kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ export async function runBenchKitInit(
const templateVersion = readTemplateVersion(ctx, scratch);
mkdirSync(targetDir, { recursive: true });
const copied = materialize(scratch, targetDir, { skipExisting: repair });
installWorkflows(targetDir, { skipExisting: repair });

// Running init from inside a product repo is the common flow — register
// that repo as the first base repo instead of leaving the placeholder.
Expand Down Expand Up @@ -291,6 +292,27 @@ function readTemplateVersion(ctx: OutputContext, cloneDir: string): string {
return readFileSync(versionFile, "utf8").trim();
}

/**
* GitHub only runs workflows from .github/workflows/, so the template's
* .bench-kit/workflows/ files are copied there. In repair mode existing
* files are kept — the company may have customized triggers or secrets.
*/
function installWorkflows(
targetDir: string,
opts: { skipExisting: boolean },
): void {
const srcDir = join(targetDir, ".bench-kit", "workflows");
if (!existsSync(srcDir)) return;
const destDir = join(targetDir, ".github", "workflows");
mkdirSync(destDir, { recursive: true });
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
if (!entry.isFile()) continue;
const to = join(destDir, entry.name);
if (opts.skipExisting && existsSync(to)) continue;
cpSync(join(srcDir, entry.name), to);
}
}

/**
* Copies the clone into the target without git history. In repair mode
* existing files are never overwritten — company content is untouchable.
Expand Down
12 changes: 12 additions & 0 deletions tests/bench-kit-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ function buildTemplateFixture(version = "0.1.0"): string {
writeFileSync(join(dir, ".git", "HEAD"), "ref: refs/heads/main\n");
mkdirSync(join(dir, ".bench-kit"), { recursive: true });
writeFileSync(join(dir, ".bench-kit", "VERSION"), `${version}\n`);
mkdirSync(join(dir, ".bench-kit", "workflows"), { recursive: true });
writeFileSync(join(dir, ".bench-kit", "workflows", "bench-run.yaml"), "name: bench-run\n");
mkdirSync(join(dir, "tasks", "demo"), { recursive: true });
writeFileSync(join(dir, "tasks", "demo", "prompt.md"), "demo prompt\n");
writeFileSync(
Expand Down Expand Up @@ -151,6 +153,10 @@ describe("10x bench-kit init", () => {
expect(existsSync(join(target, ".bench-kit", "VERSION"))).toBe(true);
expect(existsSync(join(target, "tasks", "demo", "prompt.md"))).toBe(true);
expect(existsSync(join(target, ".git", "HEAD"))).toBe(false);
// GitHub only runs workflows from .github/workflows/ — init installs them there.
expect(readFileSync(join(target, ".github", "workflows", "bench-run.yaml"), "utf8")).toBe(
"name: bench-run\n",
);

const manifest = JSON.parse(readFileSync(join(target, ".bench-kit", "instance.json"), "utf8"));
expect(manifest.templateVersion).toBe("0.1.0");
Expand Down Expand Up @@ -248,6 +254,9 @@ describe("10x bench-kit init", () => {
mkdirSync(join(target, ".bench-kit"), { recursive: true });
writeFileSync(join(target, ".bench-kit", "VERSION"), "0.1.0\n");
writeFileSync(join(target, "bench.config.yaml"), "base_repos: [edited by company]\n");
// Workflow customized by the company (e.g. triggers/secrets) — repair keeps it.
mkdirSync(join(target, ".github", "workflows"), { recursive: true });
writeFileSync(join(target, ".github", "workflows", "bench-run.yaml"), "name: customized\n");
const { deps, gitCalls } = fakeDeps(template);

const result = await captureStreams(() =>
Expand All @@ -259,6 +268,9 @@ describe("10x bench-kit init", () => {
expect(existsSync(join(target, "tasks", "demo", "prompt.md"))).toBe(true);
// …company content untouched…
expect(readFileSync(join(target, "bench.config.yaml"), "utf8")).toContain("edited by company");
expect(readFileSync(join(target, ".github", "workflows", "bench-run.yaml"), "utf8")).toBe(
"name: customized\n",
);
// …and no fresh git init in repair mode.
expect(gitCalls.length).toBe(0);

Expand Down