@tablation/crew 0.125.0 → 0.126.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +88 -10
- package/dist/sandbox-intel-bridge.cjs +4 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -588,7 +588,7 @@ function startSerena(d, s) {
|
|
|
588
588
|
const dest = join5(p.home, "language_servers");
|
|
589
589
|
if (!d.exists(dest)) d.link(s.languageServers, dest);
|
|
590
590
|
}
|
|
591
|
-
const mainCache = join5(p.mainProject, "cache");
|
|
591
|
+
const mainCache = s.seedCache ?? join5(p.mainProject, "cache");
|
|
592
592
|
const cache = join5(p.project, "cache");
|
|
593
593
|
if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
|
|
594
594
|
try {
|
|
@@ -11001,10 +11001,14 @@ function codeBridgeFor(o, harness) {
|
|
|
11001
11001
|
const number = /(\d+)$/.exec(o.ticket)?.[1];
|
|
11002
11002
|
if (!number) return void 0;
|
|
11003
11003
|
let intel;
|
|
11004
|
+
let seed;
|
|
11004
11005
|
try {
|
|
11005
11006
|
const repos = reposOf(o.route);
|
|
11006
11007
|
const target = repos.find((r) => r.name === o.repo) ?? (repos.length === 1 ? repos[0] : void 0);
|
|
11007
11008
|
intel = target ? loadRepoConfig(target.dir)?.hooks?.intel : void 0;
|
|
11009
|
+
const origin = target && o.ship.runner === "sandbox" && intelFlavor(intel) === "serena" ? originUrlOf(target.dir, "origin") : null;
|
|
11010
|
+
const remote = origin ? parseRemote(origin) : null;
|
|
11011
|
+
if (remote) seed = `${o.ship.sandbox?.mirrorDir ?? "/cache"}/${intelRelDir(remote)}/cache`;
|
|
11008
11012
|
} catch {
|
|
11009
11013
|
}
|
|
11010
11014
|
if (!wantsCodeBridge(intel)) return void 0;
|
|
@@ -11020,7 +11024,8 @@ function codeBridgeFor(o, harness) {
|
|
|
11020
11024
|
"--state-dir",
|
|
11021
11025
|
dirname13(SANDBOX_INTEL_DIR),
|
|
11022
11026
|
"--ticket",
|
|
11023
|
-
number
|
|
11027
|
+
number,
|
|
11028
|
+
...seed ? ["--seed-cache", seed] : []
|
|
11024
11029
|
] : [...process.execArgv, process.argv[1] ?? "crew", "intel", "bridge", o.route.route, number, "--state-dir", o.stateDir];
|
|
11025
11030
|
const bridge = { command: sandbox ? "node" : process.execPath, args };
|
|
11026
11031
|
if (harness === "gemini") {
|
|
@@ -12345,6 +12350,9 @@ function mirrorRelPath(r) {
|
|
|
12345
12350
|
function depsRelDir(r) {
|
|
12346
12351
|
return `cache/deps/${r.host}/${r.path}`;
|
|
12347
12352
|
}
|
|
12353
|
+
function intelRelDir(r) {
|
|
12354
|
+
return `intel/${r.host}/${r.path}`;
|
|
12355
|
+
}
|
|
12348
12356
|
var q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
12349
12357
|
function cloneScript(o) {
|
|
12350
12358
|
const mirror = `${o.mirrorDir}/${mirrorRelPath(o.repo)}`;
|
|
@@ -12395,6 +12403,30 @@ find . -name node_modules -type d -prune | while read -r p; do
|
|
|
12395
12403
|
done
|
|
12396
12404
|
rm -rf "$droot/$h"; mv "$droot/$h.tmp" "$droot/$h"
|
|
12397
12405
|
`;
|
|
12406
|
+
var INTEL_SCRIPT = `
|
|
12407
|
+
set -e
|
|
12408
|
+
m=${VOLUME_ROOT}/"$1"; iroot=${VOLUME_ROOT}/"$2"; config="$3"; ls="\${CREW_SERENA_LANGUAGE_SERVERS:-}"
|
|
12409
|
+
export GIT_TERMINAL_PROMPT=0
|
|
12410
|
+
git config --global --add safe.directory '*'
|
|
12411
|
+
tip=$(git -C "$m" rev-parse --verify --quiet "HEAD^{commit}") || { echo "the mirror has no default branch" >&2; exit 3; }
|
|
12412
|
+
[ "$(cat "$iroot/indexed-commit" 2>/dev/null)" = "$tip" ] && { echo "unchanged $tip"; exit 0; }
|
|
12413
|
+
w=$(mktemp -d); trap 'rm -rf "$w"' EXIT
|
|
12414
|
+
git clone --quiet --shared "$m" "$w/src"
|
|
12415
|
+
git -C "$w/src" checkout --quiet "$tip"
|
|
12416
|
+
mkdir -p "$w/home" "$w/project"
|
|
12417
|
+
printf '%s' "$config" | sed "s#@PROJECT@#$w/project#" > "$w/home/serena_config.yml"
|
|
12418
|
+
git -C "$w/src" show "$tip:.serena/project.yml" > "$w/project/project.yml" 2>/dev/null || : > "$w/project/project.yml"
|
|
12419
|
+
if grep -q '^read_only' "$w/project/project.yml"; then sed -i 's/^read_only.*/read_only: true/' "$w/project/project.yml"; else echo 'read_only: true' >> "$w/project/project.yml"; fi
|
|
12420
|
+
[ -n "$ls" ] && [ -d "$ls" ] && ln -s "$ls" "$w/home/language_servers"
|
|
12421
|
+
SERENA_HOME="$w/home" serena project index "$w/src"
|
|
12422
|
+
cd "$w/project"; mkdir -p cache
|
|
12423
|
+
mkdir -p "$(dirname "$iroot")"; rm -rf "$iroot.tmp"; mkdir -p "$iroot.tmp"
|
|
12424
|
+
cp -R -P cache "$iroot.tmp/"
|
|
12425
|
+
printf '%s
|
|
12426
|
+
' "$tip" > "$iroot.tmp/indexed-commit"
|
|
12427
|
+
rm -rf "$iroot"; mv "$iroot.tmp" "$iroot"
|
|
12428
|
+
echo "updated $tip"
|
|
12429
|
+
`;
|
|
12398
12430
|
var MATERIALIZE_SCRIPT = `
|
|
12399
12431
|
set -e
|
|
12400
12432
|
export GIT_TERMINAL_PROMPT=0
|
|
@@ -12473,6 +12505,14 @@ var SandboxCache = class {
|
|
|
12473
12505
|
continue;
|
|
12474
12506
|
}
|
|
12475
12507
|
}
|
|
12508
|
+
if (r.intelImage) {
|
|
12509
|
+
try {
|
|
12510
|
+
await this.helper(r.intelImage, INTEL_SCRIPT, [mirrorRelPath(r), intelRelDir(r), serenaConfigYaml("@PROJECT@")]);
|
|
12511
|
+
} catch (e) {
|
|
12512
|
+
out.push({ repo: r, ok: false, detail: `intel index: ${describe(e)}` });
|
|
12513
|
+
continue;
|
|
12514
|
+
}
|
|
12515
|
+
}
|
|
12476
12516
|
out.push({ repo: r, ok: true });
|
|
12477
12517
|
}
|
|
12478
12518
|
return out;
|
|
@@ -12498,13 +12538,13 @@ var SandboxCache = class {
|
|
|
12498
12538
|
} catch {
|
|
12499
12539
|
return [];
|
|
12500
12540
|
}
|
|
12501
|
-
const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do printf '%s\\t%s\\n' "
|
|
12541
|
+
const script = `cd ${VOLUME_ROOT}/mirrors 2>/dev/null || exit 0; find . -name '*.git' -type d -prune | while read -r d; do r="\${d#./}"; i=${VOLUME_ROOT}/intel/"\${r%.git}"; printf '%s\\t%s\\t%s\\t%s\\n' "$r" "$(cat "$d/crew-last-refresh" 2>/dev/null)" "$(cat "$i/indexed-commit" 2>/dev/null)" "$(stat -c %y "$i/indexed-commit" 2>/dev/null | cut -c1-19)"; done`;
|
|
12502
12542
|
const { stdout } = await this.helper(this.s.helperImage, script, []);
|
|
12503
12543
|
const rows = [];
|
|
12504
12544
|
for (const line of stdout.split("\n").filter(Boolean)) {
|
|
12505
|
-
const [rel, when] = line.split(" ");
|
|
12545
|
+
const [rel, when, commit, indexedAt] = line.split(" ");
|
|
12506
12546
|
const m = /^([^/]+)\/(.+)\.git$/.exec(rel ?? "");
|
|
12507
|
-
if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null });
|
|
12547
|
+
if (m) rows.push({ repo: { host: m[1], path: m[2] }, lastRefresh: when?.trim() || null, ...commit?.trim() ? { intel: { commit: commit.trim(), indexedAt: indexedAt?.trim() || null } } : {} });
|
|
12508
12548
|
}
|
|
12509
12549
|
return rows;
|
|
12510
12550
|
}
|
|
@@ -12513,7 +12553,7 @@ var SandboxCache = class {
|
|
|
12513
12553
|
const have = new Set(keep.map((r) => `${r.host}/${r.path}`));
|
|
12514
12554
|
const drop = (await this.list()).map((m) => m.repo).filter((r) => !have.has(`${r.host}/${r.path}`));
|
|
12515
12555
|
for (const r of drop) {
|
|
12516
|
-
await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))}`, []);
|
|
12556
|
+
await this.helper(this.s.helperImage, `rm -rf ${VOLUME_ROOT}/${q(mirrorRelPath(r))} ${VOLUME_ROOT}/${q(depsRelDir(r))} ${VOLUME_ROOT}/${q(intelRelDir(r))}`, []);
|
|
12517
12557
|
}
|
|
12518
12558
|
return drop;
|
|
12519
12559
|
}
|
|
@@ -12955,6 +12995,9 @@ function writeSandboxState(stateDir, state) {
|
|
|
12955
12995
|
`);
|
|
12956
12996
|
renameSync3(`${file}.tmp`, file);
|
|
12957
12997
|
}
|
|
12998
|
+
function sandboxImageFor(state, repo) {
|
|
12999
|
+
return state[repo]?.tags[0]?.tag ?? null;
|
|
13000
|
+
}
|
|
12958
13001
|
function sandboxImageStatus(state, plan) {
|
|
12959
13002
|
const s = state[plan.repo];
|
|
12960
13003
|
const current = s?.tags[0];
|
|
@@ -18450,6 +18493,20 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
|
|
|
18450
18493
|
}
|
|
18451
18494
|
merged.push(c);
|
|
18452
18495
|
}
|
|
18496
|
+
if (applied > 0 && !o.dryRun && !o.mergeOnly && o.reloadRepo) {
|
|
18497
|
+
const changed2 = gitOk(o.cwd, ["diff", "--name-only", preMerge, "HEAD", "--", ...REPO_CONFIG_FILENAMES]);
|
|
18498
|
+
if (changed2) {
|
|
18499
|
+
try {
|
|
18500
|
+
const next = o.reloadRepo(o.cwd);
|
|
18501
|
+
if (next) {
|
|
18502
|
+
o = { ...o, repo: next };
|
|
18503
|
+
o.emit.emit(`${changed2.split("\n")[0]} changed in this release \u2014 continuing under the merged contract`);
|
|
18504
|
+
}
|
|
18505
|
+
} catch (e) {
|
|
18506
|
+
o.emit.warn(`could not re-read the merged repo config: ${e.message}; releasing under the earlier one`);
|
|
18507
|
+
}
|
|
18508
|
+
}
|
|
18509
|
+
}
|
|
18453
18510
|
if (o.mergeOnly) {
|
|
18454
18511
|
o.emit.emit(`merge only: ${merged.length} branch(es) merged, not releasing`);
|
|
18455
18512
|
return { merged, conflicts, unbuildable, deployed: false, stopped: "merge only", decision };
|
|
@@ -18474,7 +18531,8 @@ async function mergeAndRelease(o, decision, fresh, releaseBranch, isolated = fal
|
|
|
18474
18531
|
o.emit.emit("nothing to release");
|
|
18475
18532
|
return { merged, conflicts, unbuildable, deployed: false, stopped: "nothing to release", decision };
|
|
18476
18533
|
}
|
|
18477
|
-
|
|
18534
|
+
const waiting = decision.merges.some((c) => c.skipReason === "already-merged" && c.mergedSha);
|
|
18535
|
+
if (merged.length === 0 && decision.unseeded && !waiting && !o.force) {
|
|
18478
18536
|
o.emit.emit(
|
|
18479
18537
|
`nothing merged, and ${o.repo.branch.base} carries no release tag to compare against \u2014 nothing to release`
|
|
18480
18538
|
);
|
|
@@ -25018,6 +25076,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
25018
25076
|
return { action: out.action, unsafe, detail: unsafe ? describeUnsafeBase(out, remote, base) : out.detail };
|
|
25019
25077
|
}, sandboxCacheRepos = function(c, ship) {
|
|
25020
25078
|
const out = [];
|
|
25079
|
+
let images;
|
|
25021
25080
|
for (const t of reposOf(c)) {
|
|
25022
25081
|
const origin = originUrlOf(t.dir, "origin");
|
|
25023
25082
|
const repo = origin ? parseRemote(origin) : null;
|
|
@@ -25027,7 +25086,12 @@ if (command === "passenger-sync-daemon") {
|
|
|
25027
25086
|
token = readForgeToken(ship.forges, repo.host, ship.stateDir);
|
|
25028
25087
|
} catch {
|
|
25029
25088
|
}
|
|
25030
|
-
|
|
25089
|
+
let intelImage = null;
|
|
25090
|
+
try {
|
|
25091
|
+
if (intelFlavor(loadRepoConfig(t.dir)?.hooks.intel) === "serena") intelImage = sandboxImageFor(images ??= readSandboxState(ship.stateDir), t.name);
|
|
25092
|
+
} catch {
|
|
25093
|
+
}
|
|
25094
|
+
out.push({ ...repo, dir: t.dir, token, username: forgeFor(ship.forges, repo.host)?.entry.username, intelImage });
|
|
25031
25095
|
}
|
|
25032
25096
|
return out;
|
|
25033
25097
|
}, buildSandboxCachePass = function(c, ship, state2, emit2) {
|
|
@@ -25779,6 +25843,13 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25779
25843
|
const outcome = await runRelease({
|
|
25780
25844
|
cwd: target.dir,
|
|
25781
25845
|
repo,
|
|
25846
|
+
reloadRepo: (dir) => resolveRepoConfig(loadRepoConfig(dir), {
|
|
25847
|
+
hooks: { ...c.hooks, ...o?.hooks },
|
|
25848
|
+
labels: { ...c.labels, ...o?.labels },
|
|
25849
|
+
release: mergeRouteRelease(c.release, o?.release),
|
|
25850
|
+
branch: { ...c.branch, ...o?.branch },
|
|
25851
|
+
worktrees: { prefix: shipWorktreePrefixFor(c), nameOverride: o?.worktreeName }
|
|
25852
|
+
}, dir),
|
|
25782
25853
|
contract: tracker.contract,
|
|
25783
25854
|
tickets,
|
|
25784
25855
|
blockerInfo,
|
|
@@ -25823,7 +25894,12 @@ ${changed2.map((c) => ` - ${c}
|
|
|
25823
25894
|
tracker.contract,
|
|
25824
25895
|
outcome.stampFrom ?? outcome.decision.lastReleased,
|
|
25825
25896
|
outcome.decision.head,
|
|
25826
|
-
new Map(
|
|
25897
|
+
new Map([
|
|
25898
|
+
// Squashed onto an untagged base by an earlier cycle: no release range
|
|
25899
|
+
// names it, so the first release carries it out here (CREW-2925).
|
|
25900
|
+
...outcome.decision.merges.filter((m) => m.skipReason === "already-merged" && m.mergedSha).map((m) => [m.ticket.issue_id, m.mergedSha]),
|
|
25901
|
+
...outcome.merged.map((m) => [m.ticket.issue_id, m.sha])
|
|
25902
|
+
])
|
|
25827
25903
|
);
|
|
25828
25904
|
if (plan.length) await applyStamp(tracker, plan, outcome.version, tracker.contract, remit, dryRun, target.dir);
|
|
25829
25905
|
} else if (outcome.stopped) {
|
|
@@ -28368,8 +28444,10 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
28368
28444
|
`);
|
|
28369
28445
|
if (mirrors.length === 0) process.stdout.write(`volume ${settings.volume}: no mirrors yet
|
|
28370
28446
|
`);
|
|
28371
|
-
for (const m of mirrors)
|
|
28447
|
+
for (const m of mirrors) {
|
|
28448
|
+
process.stdout.write(`${m.repo.host}/${m.repo.path} last refresh ${m.lastRefresh ?? "never"}${m.intel ? ` intel ${m.intel.commit.slice(0, 12)} (${m.intel.indexedAt ?? "age unknown"})` : ""}
|
|
28372
28449
|
`);
|
|
28450
|
+
}
|
|
28373
28451
|
}
|
|
28374
28452
|
} catch (e) {
|
|
28375
28453
|
process.stderr.write(`crew sandbox cache: ${e instanceof Error ? e.message : String(e)}
|
|
@@ -24529,7 +24529,7 @@ function startSerena(d, s) {
|
|
|
24529
24529
|
const dest = (0, import_node_path4.join)(p.home, "language_servers");
|
|
24530
24530
|
if (!d.exists(dest)) d.link(s.languageServers, dest);
|
|
24531
24531
|
}
|
|
24532
|
-
const mainCache = (0, import_node_path4.join)(p.mainProject, "cache");
|
|
24532
|
+
const mainCache = s.seedCache ?? (0, import_node_path4.join)(p.mainProject, "cache");
|
|
24533
24533
|
const cache = (0, import_node_path4.join)(p.project, "cache");
|
|
24534
24534
|
if (!d.exists(cache) && d.exists(mainCache)) d.copyDir(mainCache, cache);
|
|
24535
24535
|
try {
|
|
@@ -25113,12 +25113,13 @@ function parseSandboxBridgeArgs(argv) {
|
|
|
25113
25113
|
const attachFile = get("--attach-file");
|
|
25114
25114
|
const start = get("--start");
|
|
25115
25115
|
const clone2 = get("--clone");
|
|
25116
|
-
if (!attachFile || !start || !clone2) throw new Error("usage: intel-bridge --attach-file FILE --start FLAVOR|COMMAND --clone DIR [--state-dir DIR] [--ticket N] [--startup-seconds S]");
|
|
25116
|
+
if (!attachFile || !start || !clone2) throw new Error("usage: intel-bridge --attach-file FILE --start FLAVOR|COMMAND --clone DIR [--state-dir DIR] [--seed-cache DIR] [--ticket N] [--startup-seconds S]");
|
|
25117
25117
|
return {
|
|
25118
25118
|
attachFile,
|
|
25119
25119
|
start,
|
|
25120
25120
|
clone: clone2,
|
|
25121
25121
|
stateDir: get("--state-dir") ?? "/sandbox/.crew",
|
|
25122
|
+
...get("--seed-cache") ? { seedCache: get("--seed-cache") } : {},
|
|
25122
25123
|
ticket: Number(get("--ticket")) || 0,
|
|
25123
25124
|
startupSeconds: Number(get("--startup-seconds")) || 90
|
|
25124
25125
|
};
|
|
@@ -25151,6 +25152,7 @@ function sandboxStarter(a, run = runScript) {
|
|
|
25151
25152
|
worktree: a.clone,
|
|
25152
25153
|
mainCheckout: a.clone,
|
|
25153
25154
|
noMainIndex: true,
|
|
25155
|
+
seedCache: a.seedCache,
|
|
25154
25156
|
languageServers: process.env.CREW_SERENA_LANGUAGE_SERVERS
|
|
25155
25157
|
});
|
|
25156
25158
|
}
|
package/package.json
CHANGED