@tablation/crew 0.79.0 → 0.79.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.
Files changed (2) hide show
  1. package/dist/cli.js +27 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -11388,14 +11388,14 @@ function dedupeChangelogEntries(entries, ticket) {
11388
11388
  return out;
11389
11389
  }
11390
11390
  var BUMP_LINE = /^Bump:[ \t]*(major|minor|patch)\b/gim;
11391
- function planMerge(cwd, tickets, contract, lastReleased, branchFor = (t) => branchForIssue(cwd, t.issue_id), base = "main", blockerInfo) {
11391
+ function planMerge(cwd, tickets, contract, lastReleased, branchFor = (t) => branchForIssue(cwd, t.issue_id), base = "main", blockerInfo, ref = "HEAD") {
11392
11392
  const held = new Set(heldByBlockers(tickets, contract.statuses.verified, blockerInfo).map((h) => h.ticket.id));
11393
11393
  const verified = tickets.filter((t) => t.status === contract.statuses.verified && !held.has(t.id)).sort((a, b) => Number.parseInt(a.issue_id.replace(/^\D+/, ""), 10) - Number.parseInt(b.issue_id.replace(/^\D+/, ""), 10));
11394
11394
  return verified.map((ticket) => {
11395
11395
  const branch = branchFor(ticket);
11396
11396
  if (!branch) {
11397
11397
  const keys = referenceKeys(ticket);
11398
- const unreleased = lastReleased ? findKeyInRange(cwd, keys, `${lastReleased}..HEAD`) : null;
11398
+ const unreleased = lastReleased ? findKeyInRange(cwd, keys, `${lastReleased}..${ref}`) : null;
11399
11399
  if (unreleased) {
11400
11400
  return {
11401
11401
  ticket,
@@ -11407,7 +11407,7 @@ function planMerge(cwd, tickets, contract, lastReleased, branchFor = (t) => bran
11407
11407
  skipReason: "already-merged"
11408
11408
  };
11409
11409
  }
11410
- const anywhere = findKeyInRange(cwd, keys, "HEAD");
11410
+ const anywhere = findKeyInRange(cwd, keys, ref);
11411
11411
  if (anywhere) {
11412
11412
  return {
11413
11413
  ticket,
@@ -11504,21 +11504,22 @@ ${section}`;
11504
11504
  return [...lines.slice(0, anchor), section, ...lines.slice(anchor)].join("\n");
11505
11505
  }
11506
11506
  var RELEASE_COMMIT_SUBJECT = /^Release \S+$/;
11507
- function subjectsSince(cwd, sha) {
11508
- if (!isAncestor(cwd, sha, "HEAD")) return null;
11509
- return (gitOk(cwd, ["log", "--format=%h %s", `${sha}..HEAD`]) ?? "").split("\n").filter(Boolean);
11507
+ function subjectsSince(cwd, sha, ref = "HEAD") {
11508
+ if (!isAncestor(cwd, sha, ref)) return null;
11509
+ return (gitOk(cwd, ["log", "--format=%h %s", `${sha}..${ref}`]) ?? "").split("\n").filter(Boolean);
11510
11510
  }
11511
11511
  var isReleaseCommitLine = (line) => RELEASE_COMMIT_SUBJECT.test(line.replace(/^\S+ /, ""));
11512
11512
  function decideRelease(cwd, tickets, contract, opts = {}) {
11513
11513
  const pattern = opts.tagPattern ?? "v*";
11514
11514
  const base = opts.base ?? "main";
11515
- const block = checkGuards(cwd, base);
11516
- const lastTag = latestReleaseTag(cwd, pattern, "HEAD");
11515
+ const ref = opts.ref ?? "HEAD";
11516
+ const block = opts.guard === false ? null : checkGuards(cwd, base);
11517
+ const lastTag = latestReleaseTag(cwd, pattern, ref);
11517
11518
  const lastReleased = lastTag ? tagCommit(cwd, lastTag) : null;
11518
- const head = resolve2(cwd, "HEAD") ?? "";
11519
- const merges = block ? [] : planMerge(cwd, tickets, contract, lastReleased, opts.branchFor ?? ((t) => branchForIssue(cwd, t.issue_id)), base, opts.blockerInfo);
11520
- const unreleasedCommits = lastReleased ? countCommits(cwd, `${lastReleased}..HEAD`) : 0;
11521
- const unreleasedWork = lastReleased ? (gitOk(cwd, ["log", "--format=%s", `${lastReleased}..HEAD`]) ?? "").split("\n").filter((l) => l && !RELEASE_COMMIT_SUBJECT.test(l)).length : 0;
11519
+ const head = resolve2(cwd, ref) ?? "";
11520
+ const merges = block ? [] : planMerge(cwd, tickets, contract, lastReleased, opts.branchFor ?? ((t) => branchForIssue(cwd, t.issue_id)), opts.ref ?? base, opts.blockerInfo, ref);
11521
+ const unreleasedCommits = lastReleased ? countCommits(cwd, `${lastReleased}..${ref}`) : 0;
11522
+ const unreleasedWork = lastReleased ? (gitOk(cwd, ["log", "--format=%s", `${lastReleased}..${ref}`]) ?? "").split("\n").filter((l) => l && !RELEASE_COMMIT_SUBJECT.test(l)).length : 0;
11522
11523
  return {
11523
11524
  block,
11524
11525
  merges,
@@ -12233,10 +12234,14 @@ async function runReleaseCycle(o, movedPast2, localRefSkips) {
12233
12234
  }
12234
12235
  if (drop.size) releasable = releasable.filter((t) => !drop.has(t.id));
12235
12236
  }
12237
+ const { remote, base } = o.repo.branch;
12238
+ const inPlace = o.dryRun || !!o.mergeOnly || !remoteBranchExists(o.cwd, remote, base);
12239
+ const remoteRef = `${remote}/${base}`;
12236
12240
  const decision = decideRelease(o.cwd, releasable, o.contract, {
12237
12241
  tagPattern,
12238
- base: o.repo.branch.base,
12242
+ base,
12239
12243
  blockerInfo: o.blockerInfo,
12244
+ ...inPlace ? {} : { ref: remoteRef, guard: false },
12240
12245
  // CREW-1364: a branch another ship built and pushed at `fixed` exists
12241
12246
  // here only on the remote. Materialize it so the squash below has a ref.
12242
12247
  // A dry run only looks.
@@ -12265,7 +12270,7 @@ async function runReleaseCycle(o, movedPast2, localRefSkips) {
12265
12270
  o.state?.clearBlocked();
12266
12271
  const failedAt = o.state?.deployFailedSha();
12267
12272
  if (!o.force && failedAt) {
12268
- const since2 = failedAt === decision.head ? [] : subjectsSince(o.cwd, failedAt);
12273
+ const since2 = failedAt === decision.head ? [] : subjectsSince(o.cwd, failedAt, inPlace ? "HEAD" : remoteRef);
12269
12274
  const real = since2?.filter((l) => !isReleaseCommitLine(l)) ?? null;
12270
12275
  if (real && real.length === 0) {
12271
12276
  o.emit.warn(
@@ -12292,10 +12297,13 @@ async function runReleaseCycle(o, movedPast2, localRefSkips) {
12292
12297
  const review = await reviewHandoff(o, externalClosures, decision.merges, reviewing);
12293
12298
  return { merged: [], deployed: false, stopped: "external", decision, externalClosures, review };
12294
12299
  }
12295
- const { remote, base } = o.repo.branch;
12296
- if (o.dryRun || o.mergeOnly || !remoteBranchExists(o.cwd, remote, base)) {
12300
+ if (inPlace) {
12297
12301
  return mergeAndRelease(o, decision, fresh);
12298
12302
  }
12303
+ const primary = checkGuards(o.cwd, base);
12304
+ if (primary) {
12305
+ o.emit.emit(scoped(`primary checkout not fast-forwarded: ${primary.detail}; its dist/ is stale until rebuilt`), { step: "release" });
12306
+ }
12299
12307
  let checkout;
12300
12308
  const stranded = recoverStrandedReleaseCheckouts(o.cwd);
12301
12309
  if (stranded.length > 0) o.emit.warn(scoped(`dropped release checkout(s) stranded by a crash: ${stranded.join(", ")}`));
@@ -17952,9 +17960,11 @@ ${changed2.map((c) => ` - ${c}
17952
17960
  preModes
17953
17961
  );
17954
17962
  reportUnplaceable(c.route, preUnplaceable, tracker.contract.statuses.verified, remit);
17963
+ const remoteBase = remoteBranchExists(target.dir, repo.branch.remote, repo.branch.base);
17955
17964
  const d = decideRelease(target.dir, pre, tracker.contract, {
17956
17965
  tagPattern: repo.release.tagPattern ?? "v*",
17957
- base: repo.branch.base
17966
+ base: repo.branch.base,
17967
+ ...remoteBase ? { ref: `${repo.branch.remote}/${repo.branch.base}`, guard: false } : {}
17958
17968
  });
17959
17969
  const unstamped = planReleasedBranches(
17960
17970
  target.dir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tablation/crew",
3
- "version": "0.79.0",
3
+ "version": "0.79.1",
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",