Skip to content
Merged
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
29 changes: 22 additions & 7 deletions utils/binary-build.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { basename, dirname } from "jsr:@std/path@^1.0.0";
import { basename, dirname, resolve } from "jsr:@std/path@^1.0.0";
import type { BinaryBuildSpec } from "./types.ts";

import { DECKER_ROOT } from "./root.ts";
Expand All @@ -17,7 +17,8 @@ function outputPath(spec: BinaryBuildSpec): string {
}

function relDir(spec: BinaryBuildSpec): string {
return `${repoBasename(spec.repo)}-${slug(spec.ref)}`;
if (spec.path) return `local-${slug(resolve(spec.path))}`;
return `${repoBasename(spec.repo!)}-${slug(spec.ref!)}`;
}

function repoBasename(repo: string): string {
Expand Down Expand Up @@ -56,7 +57,19 @@ async function run(cmd: string[], opts: { cwd?: string } = {}): Promise<void> {
if (proc.code !== 0) throw new Error(`${cmd.join(" ")} exited with code ${proc.code}`);
}

async function ensureClone(spec: BinaryBuildSpec): Promise<string> {
// The directory `spec.cmd` runs in: an existing working tree for a path spec,
// otherwise a clone pinned to `spec.ref`.
async function ensureSource(spec: BinaryBuildSpec): Promise<string> {
if (spec.path) {
const dir = resolve(spec.path);
if (!(await dirExists(dir))) {
throw new Error(`binary build source ${dir} does not exist`);
}
return dir;
}
if (!spec.repo || !spec.ref) {
throw new Error("binary build needs either `path` or both `repo` and `ref`");
}
const cloneDir = `${SRC_CACHE}/${repoBasename(spec.repo)}`;
await Deno.mkdir(SRC_CACHE, { recursive: true });
if (!(await dirExists(`${cloneDir}/.git`))) {
Expand All @@ -72,9 +85,9 @@ async function ensureClone(spec: BinaryBuildSpec): Promise<string> {
}

async function buildOne(spec: BinaryBuildSpec): Promise<void> {
const cloneDir = await ensureClone(spec);
await run(["sh", "-c", spec.cmd], { cwd: cloneDir });
const artifact = `${cloneDir}/${spec.artifact}`;
const srcDir = await ensureSource(spec);
await run(["sh", "-c", spec.cmd], { cwd: srcDir });
const artifact = `${srcDir}/${spec.artifact}`;
if (!(await exists(artifact))) {
throw new Error(`build for ${basename(spec.artifact)} ran but ${spec.artifact} is missing`);
}
Expand All @@ -89,7 +102,9 @@ export async function ensureBinaries(
): Promise<string[]> {
const built: string[] = [];
for (const spec of specs.values()) {
if (await exists(outputPath(spec))) continue;
// A pinned ref builds the same bytes every time, so a cached artifact is
// final. A working tree does not, so it is rebuilt on every up.
if (!spec.path && (await exists(outputPath(spec)))) continue;
await buildOne(spec);
built.push(outputPath(spec));
}
Expand Down
1 change: 1 addition & 0 deletions utils/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export async function emit(
if (existing) {
if (
existing.repo !== spec.repo || existing.ref !== spec.ref ||
existing.path !== spec.path ||
existing.cmd !== spec.cmd || existing.artifact !== spec.artifact
) {
throw new Error(`binary ${path} produced by conflicting BinaryBuildSpec`);
Expand Down
20 changes: 15 additions & 5 deletions utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,22 @@ export type ImageBuildSpec = {
cmd: string;
};

// A host binary built from a git source, the process-side analogue of
// ImageBuildSpec. `cmd` runs in the clone root; `artifact` is the built
// binary's path within the clone (e.g. "target/release/reth-rbuilder").
// A host binary built from source, the process-side analogue of ImageBuildSpec.
// `cmd` runs in the source root; `artifact` is the built binary's path within it
// (e.g. "target/release/reth-rbuilder").
//
// Two source forms:
// • `repo` + `ref` — cloned and pinned under the decker cache. Built once and
// reused, since a pinned ref cannot change.
// • `path` — an existing working tree, built in place. Used when the thing
// being tested is the uncommitted state of a repo, which a pinned ref cannot
// name. Always rebuilt: the tree changes between runs and there is no ref to
// key a cache on. Cheap in practice — the build tool's own incremental cache
// does the real work.
export type BinaryBuildSpec = {
repo: string;
ref: string;
repo?: string;
ref?: string;
path?: string;
cmd: string;
artifact: string;
};
Expand Down
Loading