@tablation/crew 0.18.0 → 0.19.0
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 +89 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2575,6 +2575,9 @@ function detectSystemd() {
|
|
|
2575
2575
|
return !res.error;
|
|
2576
2576
|
}
|
|
2577
2577
|
|
|
2578
|
+
// src/ship-attention.ts
|
|
2579
|
+
import { statfsSync } from "fs";
|
|
2580
|
+
|
|
2578
2581
|
// src/git.ts
|
|
2579
2582
|
import { execFileSync } from "child_process";
|
|
2580
2583
|
import {
|
|
@@ -3049,14 +3052,94 @@ function baseAttention(repo, outcome, remote, base, nowIso = (/* @__PURE__ */ ne
|
|
|
3049
3052
|
if (outcome.action === "level" || outcome.action === "fast-forwarded") return { clear: baseUnsafeKey(repo) };
|
|
3050
3053
|
return null;
|
|
3051
3054
|
}
|
|
3055
|
+
var diskLowKey = (repo) => `disk_low:${repo}`;
|
|
3056
|
+
var LOW_DISK_BYTES = 2 * 1024 ** 3;
|
|
3057
|
+
var gib = (bytes) => (bytes / 1024 ** 3).toFixed(1);
|
|
3058
|
+
function diskAttention(repo, freeBytes, thresholdBytes = LOW_DISK_BYTES, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3059
|
+
if (freeBytes >= thresholdBytes) return { clear: diskLowKey(repo) };
|
|
3060
|
+
return {
|
|
3061
|
+
raise: {
|
|
3062
|
+
kind: "disk_low",
|
|
3063
|
+
key: diskLowKey(repo),
|
|
3064
|
+
since: nowIso,
|
|
3065
|
+
message: `${repo}: only ${gib(freeBytes)} GiB free beside the checkout (need ${gib(thresholdBytes)}) \u2014 no worktree can be set up until space is freed. Resumes automatically once there is room`
|
|
3066
|
+
}
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
function freeBytesAt(dir) {
|
|
3070
|
+
try {
|
|
3071
|
+
const s = statfsSync(dir);
|
|
3072
|
+
return Number(s.bavail) * Number(s.bsize);
|
|
3073
|
+
} catch {
|
|
3074
|
+
return null;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
async function checkRepoDisks(o, freeAt = freeBytesAt, thresholdBytes = LOW_DISK_BYTES) {
|
|
3078
|
+
for (const target of reposOf(o.route)) {
|
|
3079
|
+
const free = freeAt(target.dir);
|
|
3080
|
+
if (free === null) continue;
|
|
3081
|
+
const att = diskAttention(target.name, free, thresholdBytes);
|
|
3082
|
+
if ("raise" in att) await raiseShipAttention(o, att.raise);
|
|
3083
|
+
else clearShipAttention(o.state, att.clear);
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
function parseHookMissing(key, routeName) {
|
|
3087
|
+
const prefix = `hook_missing:${routeName}/`;
|
|
3088
|
+
if (!key.startsWith(prefix)) return null;
|
|
3089
|
+
const rest = key.slice(prefix.length);
|
|
3090
|
+
const slash = rest.lastIndexOf("/");
|
|
3091
|
+
if (slash <= 0) return null;
|
|
3092
|
+
return { repo: rest.slice(0, slash), hook: rest.slice(slash + 1) };
|
|
3093
|
+
}
|
|
3052
3094
|
function repoStopFor(items, route) {
|
|
3053
|
-
const stops =
|
|
3095
|
+
const stops = /* @__PURE__ */ new Map();
|
|
3096
|
+
for (const i of items) {
|
|
3097
|
+
let repo = null;
|
|
3098
|
+
if (i.kind === "base_unsafe") repo = i.key.slice("base_unsafe:".length);
|
|
3099
|
+
else if (i.kind === "disk_low") repo = i.key.slice("disk_low:".length);
|
|
3100
|
+
else if (i.kind === "hook_missing") {
|
|
3101
|
+
const h = parseHookMissing(i.key, route.route);
|
|
3102
|
+
if (h?.hook === "setup") repo = h.repo;
|
|
3103
|
+
}
|
|
3104
|
+
if (repo && !stops.has(repo)) stops.set(repo, i.message);
|
|
3105
|
+
}
|
|
3054
3106
|
if (stops.size === 0) return () => null;
|
|
3055
3107
|
return (t) => {
|
|
3056
3108
|
const name = repoTargetFor(route, t.repo_id)?.name;
|
|
3057
|
-
return name ? stops.get(
|
|
3109
|
+
return name ? stops.get(name) ?? null : null;
|
|
3058
3110
|
};
|
|
3059
3111
|
}
|
|
3112
|
+
function recheckEnvironmentStops(state, route, ship, deps = {}) {
|
|
3113
|
+
const onPath = deps.onPath ?? findOnPath;
|
|
3114
|
+
const dockerProblem = deps.dockerProblem ?? dockerPathProblem;
|
|
3115
|
+
const cleared = [];
|
|
3116
|
+
for (const item of state.shipAttention().previous()) {
|
|
3117
|
+
if (item.kind === "docker_missing") {
|
|
3118
|
+
if (!dockerProblem(ship)) {
|
|
3119
|
+
clearShipAttention(state, item.key);
|
|
3120
|
+
cleared.push("docker");
|
|
3121
|
+
}
|
|
3122
|
+
continue;
|
|
3123
|
+
}
|
|
3124
|
+
if (item.kind !== "hook_missing") continue;
|
|
3125
|
+
const h = parseHookMissing(item.key, route.route);
|
|
3126
|
+
const target = h && reposOf(route).find((t) => t.name === h.repo);
|
|
3127
|
+
if (!h || !target) continue;
|
|
3128
|
+
let cfg;
|
|
3129
|
+
try {
|
|
3130
|
+
cfg = resolveRepoConfig(loadRepoConfig(target.dir), void 0, target.dir);
|
|
3131
|
+
} catch {
|
|
3132
|
+
continue;
|
|
3133
|
+
}
|
|
3134
|
+
const script = cfg.hooks[h.hook];
|
|
3135
|
+
const cmd = script ? hookCommand(script) : null;
|
|
3136
|
+
if (!cmd || onPath(cmd, pathFor(ship))) {
|
|
3137
|
+
clearShipAttention(state, item.key);
|
|
3138
|
+
cleared.push(`${h.repo}/${h.hook}`);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
return cleared;
|
|
3142
|
+
}
|
|
3060
3143
|
function recheckBaseStops(state, route) {
|
|
3061
3144
|
const open = state.shipAttention().previous().filter((i) => i.kind === "base_unsafe");
|
|
3062
3145
|
const cleared = [];
|
|
@@ -4695,6 +4778,10 @@ async function decideCycle(o) {
|
|
|
4695
4778
|
for (const repo of recheckBaseStops(state, route)) {
|
|
4696
4779
|
emit.emit(`${repo}: primary checkout is level again \u2014 building lanes resume for that repo`, { step: "select" });
|
|
4697
4780
|
}
|
|
4781
|
+
for (const what of recheckEnvironmentStops(state, route, ship)) {
|
|
4782
|
+
emit.emit(`${what}: no longer missing from the scheduler PATH \u2014 stop cleared`, { step: "select" });
|
|
4783
|
+
}
|
|
4784
|
+
await checkRepoDisks({ state, emit, route, ship });
|
|
4698
4785
|
const selectionInput = {
|
|
4699
4786
|
repoStop: repoStopFor(openShipAttention(state), route),
|
|
4700
4787
|
tickets,
|
package/package.json
CHANGED