forgedock 1.24.4 → 1.24.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/bin/lean/lean.mjs CHANGED
@@ -366,8 +366,23 @@ export async function runCore(o) {
366
366
  }
367
367
  }
368
368
  };
369
- if (resumed) {
370
- await io.git(["fetch", "origin", branch], { cwd });
369
+ if (resumed) { try { await io.git(["fetch", "origin", branch], { cwd }); } catch { /* remote branch may not exist (PR-less resume) */ } }
370
+ // #4118: a local lane branch with commits not yet pushed (e.g. a reviewed commit whose push failed) must never be reset.
371
+ // Reuse it when it is ahead of the base and contains everything the remote branch has.
372
+ let keepLocal = false;
373
+ try {
374
+ const ahead = Number(String(await io.git(["rev-list", "--count", `${base}..refs/heads/${branch}`], { cwd })).trim());
375
+ if (ahead > 0) {
376
+ let containsRemote = true;
377
+ if (resumed) { try { await io.git(["merge-base", "--is-ancestor", `origin/${branch}`, `refs/heads/${branch}`], { cwd }); } catch { containsRemote = false; } }
378
+ keepLocal = containsRemote;
379
+ }
380
+ } catch { /* no local branch */ }
381
+ if (keepLocal) {
382
+ await addWorktree(["worktree", "add", wt, branch]);
383
+ resumed = resumed ?? { pr: null, local: true };
384
+ ev({ event: "LOCAL_BRANCH_REUSED", branch });
385
+ } else if (resumed) {
371
386
  await addWorktree(["worktree", "add", "-B", branch, wt, `origin/${branch}`]);
372
387
  } else {
373
388
  await addWorktree(["worktree", "add", "-B", branch, wt, base]);
@@ -1116,6 +1116,26 @@ describe("batch robustness (#4112-#4115)", () => {
1116
1116
  }
1117
1117
  });
1118
1118
 
1119
+ it("#4118 GitHub's transient '[remote rejected] ... (failed)' and pack write errors are retried, then merge", async () => {
1120
+ for (const msg of ["To github.com:o/r.git\n ! [remote rejected] feat/issue-7-lean -> feat/issue-7-lean (failed)",
1121
+ "remote: error: unable to write file ./objects/pack/pack-1.pack: No such file or directory\n ! [remote rejected] x -> x (failed)"]) {
1122
+ const w = world({ pushErrors: [msg] });
1123
+ assert.equal((await run(w)).terminalReason, "merged", msg);
1124
+ assert.equal(w.pushes, 2, msg);
1125
+ }
1126
+ });
1127
+
1128
+ it("#4118 a rerun reuses a local lane branch with unpushed commits instead of resetting it to the base", async () => {
1129
+ const w = world();
1130
+ const git0 = w.io.git;
1131
+ w.io.git = async (a, o) => (a[0] === "rev-list" && a[1] === "--count" && String(a[2]).includes("refs/heads/feat/issue-7-lean")) ? "1\n" : git0(a, o);
1132
+ await run(w);
1133
+ const adds = w.calls.filter((c) => c[0] === "git" && c[1] === "worktree" && c[2] === "add");
1134
+ assert.ok(adds.length >= 1);
1135
+ assert.ok(adds.every((c) => !c.includes("-B")), JSON.stringify(adds));
1136
+ assert.ok(w.calls.some((c) => c[0] === "git" && c[1] === "worktree" && c[2] === "add" && c.at(-1) === "feat/issue-7-lean"));
1137
+ });
1138
+
1119
1139
  it("#4114 gh writes (comment) retry on 5xx and secondary rate limits; non-transient errors do not", async () => {
1120
1140
  const w = world({ commentErrors: ["HTTP 502: Bad Gateway", "You have exceeded a secondary rate limit. Please wait a few minutes"] });
1121
1141
  assert.equal((await run(w)).terminalReason, "merged");
@@ -2,8 +2,8 @@
2
2
  // SPDX-License-Identifier: AGPL-3.0-or-later
3
3
  /** Transient-failure retry for the core work-on's network writes (git push, gh writes, gh pr merge). */
4
4
 
5
- export const PUSH_TRANSIENT_RE = /remote unpack failed|index-pack failed|RPC failed|unexpected disconnect|early EOF|the remote end hung up|HTTP\/?\s?5\d\d|returned error: 5\d\d|\b50[0234]\b|internal server error|bad gateway|service unavailable|gateway time-?out|connection (?:reset|timed out|refused)|timed out|could not resolve host|broken pipe|ETIMEDOUT|ECONNRESET|temporarily unavailable/i;
6
- export const PUSH_PERMANENT_RE = /protected branch|GH006|GH013|repository rule violations|permission (?:denied|to .* denied)|denied to|\b403\b|authentication failed|not allowed|hook declined|pre-receive hook declined|\[remote rejected\]|\[rejected\]|non-fast-forward|fetch first/i;
5
+ export const PUSH_TRANSIENT_RE = /remote unpack failed|index-pack failed|RPC failed|unexpected disconnect|early EOF|the remote end hung up|HTTP\/?\s?5\d\d|returned error: 5\d\d|\b50[0234]\b|internal server error|bad gateway|service unavailable|gateway time-?out|connection (?:reset|timed out|refused)|timed out|could not resolve host|broken pipe|ETIMEDOUT|ECONNRESET|temporarily unavailable|\[remote rejected\][^\n]*\(failed\)|unable to write file|unable to create temporary object|unpacker error|failed to write object/i;
6
+ export const PUSH_PERMANENT_RE = /protected branch|GH006|GH013|repository rule violations|permission (?:denied|to .* denied)|denied to|\b403\b|authentication failed|not allowed|hook declined|pre-receive hook declined|\[rejected\]|non-fast-forward|fetch first/i;
7
7
  export const GH_TRANSIENT_RE = /HTTP 5\d\d|\b50[0234]\b|secondary rate limit|rate limit exceeded|abuse detection|something went wrong|internal server error|bad gateway|service unavailable|ETIMEDOUT|ECONNRESET|connection reset|timed out|\bEOF\b|temporarily unavailable/i;
8
8
  export const MERGE_TRANSIENT_RE = new RegExp(`${GH_TRANSIENT_RE.source}|base branch was modified|head branch was modified|mergeability.*unknown|try again`, "i");
9
9
  export const MERGE_CONFLICT_RE = /not mergeable|merge conflict|conflict/i;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgedock",
3
- "version": "1.24.4",
3
+ "version": "1.24.5",
4
4
  "description": "Autonomous development pipeline for coding agents. GitHub as institutional memory, with native Claude Code and OpenCode entrypoints.",
5
5
  "bin": {
6
6
  "forgedock": "bin/forgedock.mjs"