@tablation/crew 0.127.3 → 0.127.5
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 +25 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9683,18 +9683,28 @@ async function missingAcceptance(i, t) {
|
|
|
9683
9683
|
if (lines2.length === 0 || !i.writer.comments) return null;
|
|
9684
9684
|
const cc = i.contract.comments;
|
|
9685
9685
|
const mine = (await i.writer.comments()).map((c) => c).filter((r) => r[cc.parent] === t.id && r[cc.author] === i.seatId).sort((a, b) => String(a[cc.createdAt] ?? "").localeCompare(String(b[cc.createdAt] ?? "")));
|
|
9686
|
-
const
|
|
9687
|
-
const
|
|
9688
|
-
|
|
9686
|
+
const from = i.since ? Date.parse(i.since) : NaN;
|
|
9687
|
+
const recent = Number.isNaN(from) ? [] : mine.filter((r) => Date.parse(String(r[cc.createdAt] ?? "")) >= from);
|
|
9688
|
+
const candidates2 = recent.length > 0 ? recent : mine.slice(-1);
|
|
9689
|
+
if (candidates2.length === 0) return { missing: lines2, judged: null };
|
|
9690
|
+
let best = null;
|
|
9691
|
+
for (const c of candidates2) {
|
|
9692
|
+
const missing = unquotedAcceptance(lines2, String(c[cc.body] ?? ""));
|
|
9693
|
+
if (missing.length === 0) return null;
|
|
9694
|
+
if (!best || missing.length <= best.missing.length) best = { missing, judged: String(c[cc.createdAt] ?? "") || null };
|
|
9695
|
+
}
|
|
9696
|
+
return best;
|
|
9689
9697
|
}
|
|
9690
|
-
function acceptanceBounceComment(missing, shipName2) {
|
|
9698
|
+
function acceptanceBounceComment(missing, shipName2, judged) {
|
|
9691
9699
|
return `${ACCEPTANCE_BOUNCED_MARKER}
|
|
9692
9700
|
|
|
9693
9701
|
**Hand-off bounced: the hand-off comment does not quote every line of the ticket's Acceptance section.**
|
|
9694
9702
|
|
|
9695
9703
|
${shipName2 ? `Ship: ${shipName2}
|
|
9696
9704
|
|
|
9697
|
-
` : ""}
|
|
9705
|
+
` : ""}${judged ? `Judged the comment posted at ${judged}; no comment this run quoted every line.
|
|
9706
|
+
|
|
9707
|
+
` : "This seat has no comment on the ticket.\n\n"}Not quoted verbatim:
|
|
9698
9708
|
${missing.map((l) => `- ${l}`).join("\n")}
|
|
9699
9709
|
|
|
9700
9710
|
Set back to \`in_progress\`. Post a comment that lists every Acceptance line verbatim, each followed by the test, command or manual check that exercised it in this run and its result (a line you could not exercise is listed with the reason, never omitted), then set \`fixed\` again. The branch is already pushed.`;
|
|
@@ -9715,8 +9725,8 @@ async function enforceHandoff(i) {
|
|
|
9715
9725
|
return { kind: "unverified", why: v.why };
|
|
9716
9726
|
}
|
|
9717
9727
|
if (v.kind === "pushed" && !claimed) {
|
|
9718
|
-
const
|
|
9719
|
-
if (
|
|
9728
|
+
const gap = await missingAcceptance(i, t);
|
|
9729
|
+
if (gap) {
|
|
9720
9730
|
const heldNow = i.hold ? t[i.hold.column] : void 0;
|
|
9721
9731
|
if (i.hold && heldNow && heldNow !== i.hold.shipId || await takenOverSince(i, t.id)) {
|
|
9722
9732
|
i.log.warn(`${t.issue_id} is held by another ship now \u2014 the acceptance check is stale, left alone`, log);
|
|
@@ -9724,10 +9734,11 @@ async function enforceHandoff(i) {
|
|
|
9724
9734
|
}
|
|
9725
9735
|
await i.writer.updateTicket(t.id, {
|
|
9726
9736
|
status: i.contract.statuses.building,
|
|
9727
|
-
|
|
9737
|
+
// CREW-2962: the hand-off may have assigned QA's seat; a bounce is the building seat's again.
|
|
9738
|
+
assignee_id: i.seatId,
|
|
9728
9739
|
...i.hold && !heldNow ? { [i.hold.column]: i.hold.shipId } : {}
|
|
9729
9740
|
});
|
|
9730
|
-
await i.writer.postEvent(t.id, acceptanceBounceComment(missing, i.shipName), i.seatId, "bounced").catch(() => {
|
|
9741
|
+
await i.writer.postEvent(t.id, acceptanceBounceComment(gap.missing, i.shipName, gap.judged), i.seatId, "bounced").catch(() => {
|
|
9731
9742
|
i.log.warn("bounced, but the note failed to post", log);
|
|
9732
9743
|
});
|
|
9733
9744
|
i.log.warn(`${t.issue_id} bounced back to ${i.contract.statuses.building}: hand-off comment lacks the acceptance checklist`, log);
|
|
@@ -9781,7 +9792,7 @@ async function enforceHandoff(i) {
|
|
|
9781
9792
|
}
|
|
9782
9793
|
await i.writer.updateTicket(t.id, {
|
|
9783
9794
|
status: i.contract.statuses.building,
|
|
9784
|
-
|
|
9795
|
+
assignee_id: i.seatId,
|
|
9785
9796
|
...i.hold && !heldBy ? { [i.hold.column]: i.hold.shipId } : {}
|
|
9786
9797
|
});
|
|
9787
9798
|
await i.writer.postEvent(t.id, bounceComment(v, i.shipName, pushError), i.seatId, "bounced").catch(() => {
|
|
@@ -9921,7 +9932,7 @@ ${i.shipName ? `${i.shipName}'s ` : "A "}${i.role} run set it to \`${st.handoff}
|
|
|
9921
9932
|
i.log.warn(`${t.issue_id}: \`${v.remote}/${v.branch}\` is at ${v.origin.slice(0, 7)}, not a tip this checkout built \u2014 left alone`, at);
|
|
9922
9933
|
continue;
|
|
9923
9934
|
}
|
|
9924
|
-
await i.writer.updateTicket(t.id, { status: st.building,
|
|
9935
|
+
await i.writer.updateTicket(t.id, { status: st.building, assignee_id: i.seatId });
|
|
9925
9936
|
await i.writer.postEvent(t.id, bounceComment(v, i.shipName), i.seatId, "bounced").catch(() => {
|
|
9926
9937
|
});
|
|
9927
9938
|
}
|
|
@@ -13134,12 +13145,14 @@ function baseLayer() {
|
|
|
13134
13145
|
].join("\n");
|
|
13135
13146
|
return { name: "base", tag: `crew-sandbox-base:${hash({ SANDBOX_BASE_IMAGE, SANDBOX_OPENSHELL_VERSION })}`, dockerfile, files: {} };
|
|
13136
13147
|
}
|
|
13148
|
+
var APT_HTTPS_COMMAND = "sed -i -E 's#http://([a-z0-9.-]*\\.)?ubuntu\\.com#https://\\1ubuntu.com#g' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null || true";
|
|
13137
13149
|
function crewLayer(base, intel, bridge) {
|
|
13138
13150
|
const files = { "credential-helper": CREDENTIAL_HELPER, "mcp.json": MCP_CONFIG, ...bridge ? { "intel-bridge": bridge } : {} };
|
|
13139
13151
|
const env = Object.entries(CREW_ENV).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
13140
13152
|
const dockerfile = [
|
|
13141
13153
|
`FROM ${base.tag}`,
|
|
13142
13154
|
"USER root",
|
|
13155
|
+
run2(APT_HTTPS_COMMAND),
|
|
13143
13156
|
run2(`apt-get update && apt-get install -y --no-install-recommends ca-certificates curl git${intel ? " python3 python3-venv nodejs" : ""} && rm -rf /var/lib/apt/lists/*`),
|
|
13144
13157
|
`COPY credential-helper ${CREW_PREFIX}/bin/credential-helper`,
|
|
13145
13158
|
`COPY mcp.json ${CREW_PREFIX}/mcp/mcp.json`,
|
|
@@ -13188,6 +13201,7 @@ function repoLayer(repo, decl, crew, warm) {
|
|
|
13188
13201
|
lines2.push(`FROM ${crew.tag}`, "USER root");
|
|
13189
13202
|
}
|
|
13190
13203
|
if (decl.packages.length > 0) {
|
|
13204
|
+
lines2.push(run2(APT_HTTPS_COMMAND));
|
|
13191
13205
|
lines2.push(run2(`apt-get update && apt-get install -y --no-install-recommends ${decl.packages.join(" ")} && rm -rf /var/lib/apt/lists/*`));
|
|
13192
13206
|
}
|
|
13193
13207
|
lines2.push(
|
package/package.json
CHANGED