@tablation/crew 0.54.4 → 0.55.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.
Files changed (2) hide show
  1. package/dist/cli.js +87 -0
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3283,6 +3283,7 @@ var DEFAULT_CONTRACT = {
3283
3283
  author: "filed_by_id",
3284
3284
  ship: "ship_id",
3285
3285
  heldBy: "held_by_ship_id",
3286
+ resolutionNote: "resolution_note",
3286
3287
  verifiedSha: "verified_sha",
3287
3288
  prRef: "pr_ref",
3288
3289
  position: "position"
@@ -3623,6 +3624,30 @@ ${describeComposition(o.composition)}` : "the target stays on the previous relea
3623
3624
  }
3624
3625
  return null;
3625
3626
  }
3627
+ function recoveredFailureHeadlines(o, route) {
3628
+ const setup = `${route}: release blocked \u2014 setup failed`;
3629
+ const tests = `${route}: release blocked \u2014 tests failed`;
3630
+ const build = `${route}: build FAILED`;
3631
+ const deploy = `${route}: deploy FAILED`;
3632
+ if (o.deployed || o.integrated) {
3633
+ return [
3634
+ setup,
3635
+ tests,
3636
+ build,
3637
+ deploy,
3638
+ `${route}: release blocked \u2014 checkout is not on the base branch`,
3639
+ `${route}: release blocked \u2014 working tree is not clean`
3640
+ ];
3641
+ }
3642
+ switch (o.stopped) {
3643
+ case "build failed":
3644
+ case "deploy failed":
3645
+ case "tag push failed":
3646
+ return o.stopped === "deploy failed" ? [setup, tests, build] : [setup, tests];
3647
+ default:
3648
+ return [];
3649
+ }
3650
+ }
3626
3651
 
3627
3652
  // src/install.ts
3628
3653
  import { dirname as dirname4, join as join9, extname } from "path";
@@ -10955,6 +10980,52 @@ Filed with the evidence above. ${headLine(n.head)}`, memberId, "failure_alert").
10955
10980
  return { kind: "failed", why: e.message };
10956
10981
  }
10957
10982
  }
10983
+ function alertResolvedStatus(c) {
10984
+ return c.statuses.resolved.find((s) => s === "closed_completed");
10985
+ }
10986
+ async function applyFailureRecovery(writer, headlines, openTickets, contract, memberId, passed, log, dryRun) {
10987
+ const out = { closed: [], noted: [], failed: [] };
10988
+ const closedStatus = alertResolvedStatus(contract);
10989
+ const closable = /* @__PURE__ */ new Set(["new", contract.statuses.approved, contract.statuses.needsHuman]);
10990
+ for (const headline of headlines) {
10991
+ const alert = findOpenFailureAlert(openTickets, headline);
10992
+ if (!alert) continue;
10993
+ const label = displayKey(alert);
10994
+ const willClose = closedStatus !== void 0 && closable.has(alert.status);
10995
+ if (dryRun) {
10996
+ log.emit(`would ${willClose ? "close" : "note on"} ${label} \u2014 ${passed}`, { step: "release" });
10997
+ continue;
10998
+ }
10999
+ try {
11000
+ await writer.postEvent(
11001
+ alert.id,
11002
+ `${FAILURE_ALERT_MARKER}
11003
+
11004
+ Passed: ${passed}.${willClose ? " Closing this alert." : ""}`,
11005
+ memberId,
11006
+ "failure_alert"
11007
+ );
11008
+ if (willClose) {
11009
+ const col = contract.columns;
11010
+ await writer.updateTicket(alert.id, {
11011
+ [contract.columns.status]: closedStatus,
11012
+ [col.resolutionNote]: `Resolved automatically: ${passed}.`,
11013
+ [col.assignee]: null,
11014
+ [col.heldBy]: null
11015
+ });
11016
+ out.closed.push(label);
11017
+ log.emit(`${label} closed \u2014 ${passed}`, { step: "release" });
11018
+ } else {
11019
+ out.noted.push(label);
11020
+ log.emit(`${label} left open \u2014 ${passed}`, { step: "release" });
11021
+ }
11022
+ } catch (e) {
11023
+ out.failed.push(label);
11024
+ log.warn(`could not resolve failure alert ${label}: ${e.message}`, { step: "release" });
11025
+ }
11026
+ }
11027
+ return out;
11028
+ }
10958
11029
 
10959
11030
  // src/projects-cmd.ts
10960
11031
  async function buildProjectsReport(routes, open) {
@@ -14852,6 +14923,22 @@ ${changed2.map((c) => ` - ${c}
14852
14923
  });
14853
14924
  }
14854
14925
  }
14926
+ const recovered = recoveredFailureHeadlines(outcome, scope);
14927
+ if (recovered.length) {
14928
+ const passed = outcome.version ? `${scope} released ${outcome.version}` : outcome.deployed || outcome.integrated ? `${scope} release passed` : `${scope} release got past this stage`;
14929
+ await applyFailureRecovery(
14930
+ tracker,
14931
+ recovered,
14932
+ all,
14933
+ tracker.contract,
14934
+ releaseSeat(c),
14935
+ passed,
14936
+ remit,
14937
+ dryRun
14938
+ ).catch((e) => {
14939
+ remit.warn(`could not resolve failure tickets: ${e.message}`, { step: "release" });
14940
+ });
14941
+ }
14855
14942
  if (outcome.buildCulprits?.length) {
14856
14943
  const seat = releaseSeat(c);
14857
14944
  for (const cu of outcome.buildCulprits) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.54.4",
3
+ "version": "0.55.0",
4
4
  "description": "A standing team of headless agents that picks work off a Tablation board, does it on a machine you control, and reports back.",
5
5
  "type": "module",
6
6
  "license": "MIT",