Let a bed's forge take the lab's shallow checkouts (issue 689)
mesh/merge-gate head 7ed7da1398df merged into trunk 307dd23d6d2a — pass: the change touches no module of the mesh's graph
mesh/repo-check head 7ed7da1398df merged into trunk 307dd23d6d2a — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript)…
mesh/delivery delivered
mesh/merge-gate head 7ed7da1398df merged into trunk 307dd23d6d2a — pass: the change touches no module of the mesh's graph
mesh/repo-check head 7ed7da1398df merged into trunk 307dd23d6d2a — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (typescript)…
mesh/delivery delivered
The lab module clones every repository with --depth 50, and a fresh bare repository refuses a push whose history ends in a cut, so the genesis bed raised its anchor and forge and then skipped. The three beds that carry a checkout into a forge now share one helper that sets receive.shallowUpdate on the bare repository. Unshallowing instead cannot work in a lab run: the forge credential is removed once the run's clones are done.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { carry } from "./integration/forge.ts";
|
||||
|
||||
/**
|
||||
* A bed's forge takes the lab's checkouts as they are: shallow (novox/hq issue 689). The lab module clones
|
||||
* every repository with `--depth`, and the genesis bed's forge refused the push ("shallow update not
|
||||
* allowed"), so the bed never reached its installer. Held here with a real git and no lab.
|
||||
*/
|
||||
|
||||
const git = (cwd: string, ...args: string[]) =>
|
||||
execFileSync("git", ["-C", cwd, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],
|
||||
env: { ...process.env, GIT_AUTHOR_NAME: "lab", GIT_AUTHOR_EMAIL: "lab@example.org",
|
||||
GIT_COMMITTER_NAME: "lab", GIT_COMMITTER_EMAIL: "lab@example.org" } }).trim();
|
||||
|
||||
test("a shallow checkout, as the lab clones it, is carried into the bed's forge and served from it", () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "mesh-lab-689-"));
|
||||
try {
|
||||
const origin = join(dir, "origin");
|
||||
execFileSync("git", ["init", "-q", "-b", "main", origin]);
|
||||
for (let i = 0; i < 5; i++) {
|
||||
writeFileSync(join(origin, "file"), `${i}\n`);
|
||||
git(origin, "add", "file");
|
||||
git(origin, "commit", "-q", "-m", `commit ${i}`);
|
||||
}
|
||||
// As the lab module clones: shallow, over a transport that honours the depth.
|
||||
const checkout = join(dir, "checkout");
|
||||
execFileSync("git", ["clone", "-q", "--depth", "2", "--branch", "main", `file://${origin}`, checkout]);
|
||||
assert.equal(git(checkout, "rev-parse", "--is-shallow-repository"), "true");
|
||||
|
||||
const bare = join(dir, "forge", "repo.git");
|
||||
const head = carry(checkout, bare, ["main", "lab"]);
|
||||
assert.equal(head, git(origin, "rev-parse", "HEAD"));
|
||||
assert.equal(git(bare, "rev-parse", "refs/heads/main"), head);
|
||||
assert.equal(git(bare, "rev-parse", "refs/heads/lab"), head);
|
||||
|
||||
// What the bed's machines do with it: clone over git's protocol, and find the commit under test.
|
||||
const served = join(dir, "served");
|
||||
execFileSync("git", ["clone", "-q", "--branch", "main", `file://${bare}`, served]);
|
||||
assert.equal(git(served, "rev-parse", "HEAD"), head);
|
||||
assert.equal(git(served, "show", "HEAD:file"), "4");
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
@@ -55,6 +55,7 @@ import { catalogueRoot } from "../../src/repos.ts";
|
||||
import { ready, returnTo, keep } from "../../src/warm.ts";
|
||||
import { labIsUsable, destroyAll, foundationBundle, catalogueModule, catalogueManifest } from "./harness.ts";
|
||||
import { genesis, type GenesisOptions } from "./genesis.ts";
|
||||
import { carry } from "./forge.ts";
|
||||
|
||||
const SCENARIO = "adoption";
|
||||
const CONTROL = "anchor";
|
||||
@@ -246,11 +247,7 @@ async function raiseForge(): Promise<Record<string, string>> {
|
||||
const heads: Record<string, string> = {};
|
||||
try {
|
||||
for (const [name, checkout] of Object.entries(REPOS)) {
|
||||
const bare = join(dir, `${name}.git`);
|
||||
execFileSync("git", ["init", "-q", "--bare", bare]);
|
||||
execFileSync("git", ["-C", checkout, "push", "-q", "--force", bare,
|
||||
"HEAD:refs/heads/main", "HEAD:refs/heads/lab"], { stdio: "pipe" });
|
||||
heads[name] = execFileSync("git", ["-C", checkout, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
||||
heads[name] = carry(checkout, join(dir, `${name}.git`), ["main", "lab"]);
|
||||
}
|
||||
const tar = join(tmpdir(), `mesh-lab-forge-${process.pid}.tar`);
|
||||
execFileSync("tar", ["-cf", tar, "-C", dir, "."]);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* A bed's forge: a checkout's HEAD carried into a fresh bare repository, which a git daemon then serves.
|
||||
*
|
||||
* **A shallow checkout is carried as it is** (novox/hq issue 689). The lab module checks every repository
|
||||
* out shallow, and a fresh bare repository refuses a push whose history ends in a cut ("shallow update not
|
||||
* allowed"), so the genesis bed raised its anchor and forge and then skipped. The bare repository accepts
|
||||
* the cut (`receive.shallowUpdate`) and serves the history the checkout holds: the bed serves the commit
|
||||
* under test, and nothing it raises reads further back than the run's checkout does. Unshallowing first is
|
||||
* not an option in a lab run: the forge credential is gone once the run's clones are done, so a private
|
||||
* repository could not be fetched again.
|
||||
*/
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
/**
|
||||
* Initialise `bare` and push `checkout`'s HEAD into it under each of `branches`. Returns the commit served.
|
||||
*/
|
||||
export function carry(checkout: string, bare: string, branches: string[] = ["main"]): string {
|
||||
execFileSync("git", ["init", "-q", "--bare", bare]);
|
||||
// No collection after the push: one run in the background rewrites the objects while they are carried.
|
||||
execFileSync("git", ["-C", bare, "config", "receive.autogc", "false"]);
|
||||
// The lab's checkouts are shallow, and a bare repository refuses a shallow push without this.
|
||||
execFileSync("git", ["-C", bare, "config", "receive.shallowUpdate", "true"]);
|
||||
const [first, ...rest] = branches;
|
||||
execFileSync("git", ["-C", checkout, "push", "-q", "--force", bare, `HEAD:refs/heads/${first}`], { stdio: "pipe" });
|
||||
const head = execFileSync("git", ["-C", checkout, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
||||
// The other branches are set in the bare repository itself: a shallow push of several refs at once has
|
||||
// receive-pack fail every ref after the first ("remote failed to report status").
|
||||
for (const branch of rest) execFileSync("git", ["-C", bare, "update-ref", `refs/heads/${branch}`, head]);
|
||||
return head;
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import { destroy, exec, push } from "../../src/lifecycle/operate.ts";
|
||||
import { bootstrapBinaryPath, hostBinaryPath } from "../../src/lifecycle/place.ts";
|
||||
import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts";
|
||||
import { genesis, type GenesisResult } from "./genesis.ts";
|
||||
import { carry } from "./forge.ts";
|
||||
import { BUS, busContainers, busNotKept, runningFrom, streamsFrom, written, type Seen } from "./bus-kept.ts";
|
||||
|
||||
const SCENARIO = "genesis-keeps-its-bus";
|
||||
@@ -107,13 +108,7 @@ async function raiseForge(): Promise<void> {
|
||||
const dir = mkdtempSync(join(tmpdir(), "mesh-lab-590-forge-"));
|
||||
try {
|
||||
for (const repo of REPOS) {
|
||||
const checkout = resolve(repos, repo);
|
||||
heads[repo] = execFileSync("git", ["-C", checkout, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
||||
const bare = join(dir, `${repo}.git`);
|
||||
execFileSync("git", ["init", "-q", "--bare", bare]);
|
||||
// No collection after the push: one run in the background rewrites the objects while they are carried.
|
||||
execFileSync("git", ["-C", bare, "config", "receive.autogc", "false"]);
|
||||
execFileSync("git", ["-C", checkout, "push", "-q", "--force", bare, "HEAD:refs/heads/main"], { stdio: "pipe" });
|
||||
heads[repo] = carry(resolve(repos, repo), join(dir, `${repo}.git`));
|
||||
}
|
||||
const tar = join(tmpdir(), `mesh-lab-590-forge-${process.pid}.tar`);
|
||||
execFileSync("tar", ["-cf", tar, "-C", dir, "."]);
|
||||
|
||||
@@ -43,6 +43,7 @@ import { destroy, exec, push } from "../../src/lifecycle/operate.ts";
|
||||
import { bootstrapBinaryPath, hostBinaryPath } from "../../src/lifecycle/place.ts";
|
||||
import { labIsUsable, destroyAll, foundationBundle } from "./harness.ts";
|
||||
import { genesis, type GenesisResult } from "./genesis.ts";
|
||||
import { carry } from "./forge.ts";
|
||||
|
||||
const SCENARIO = "genesis-untrusted";
|
||||
const ANCHOR = "anchor";
|
||||
@@ -98,13 +99,9 @@ async function must(machine: string, command: string, timeoutMs?: number): Promi
|
||||
/** The control plane's repository at the checkout's HEAD, served from the forge machine over git's protocol. */
|
||||
async function raiseForge(): Promise<string> {
|
||||
const dir = mkdtempSync(join(tmpdir(), "mesh-lab-r92-forge-"));
|
||||
const head = execFileSync("git", ["-C", controller, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
||||
let head: string;
|
||||
try {
|
||||
const bare = join(dir, "mesh-controller.git");
|
||||
execFileSync("git", ["init", "-q", "--bare", bare]);
|
||||
// No collection after the push: one run in the background rewrites the objects while they are carried.
|
||||
execFileSync("git", ["-C", bare, "config", "receive.autogc", "false"]);
|
||||
execFileSync("git", ["-C", controller, "push", "-q", "--force", bare, "HEAD:refs/heads/main"], { stdio: "pipe" });
|
||||
head = carry(controller, join(dir, "mesh-controller.git"));
|
||||
const tar = join(tmpdir(), `mesh-lab-r92-forge-${process.pid}.tar`);
|
||||
execFileSync("tar", ["-cf", tar, "-C", dir, "."]);
|
||||
await push(instanceId, FORGE, tar, "/tmp/forge.tar");
|
||||
|
||||
Reference in New Issue
Block a user