diff --git a/.changeset/fix-gridland-react-singleton.md b/.changeset/fix-gridland-react-singleton.md new file mode 100644 index 00000000..9e75c36d --- /dev/null +++ b/.changeset/fix-gridland-react-singleton.md @@ -0,0 +1,5 @@ +--- +"@prover-coder-ai/docker-git": patch +--- + +Pin React and React DOM to the Gridland renderer-compatible 19.2.4 release so the CLI menu keeps a single valid React hook dispatcher. diff --git a/bun.lock b/bun.lock index 8fad0eec..a6bccdd7 100644 --- a/bun.lock +++ b/bun.lock @@ -41,7 +41,7 @@ }, "packages/app": { "name": "@prover-coder-ai/docker-git", - "version": "1.1.50", + "version": "1.1.54", "bin": { "docker-git": "dist/src/docker-git/main.js", }, @@ -62,8 +62,8 @@ "@gridland/web": "0.4.3", "@prover-coder-ai/docker-git-session-sync": "workspace:*", "effect": "^3.21.2", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "19.2.4", + "react-dom": "19.2.4", "react-reconciler": "^0.33.0", "ts-morph": "^28.0.0", }, @@ -110,7 +110,7 @@ }, "packages/docker-git-session-sync": { "name": "@prover-coder-ai/docker-git-session-sync", - "version": "1.0.53", + "version": "1.0.57", "bin": { "docker-git-session-sync": "dist/docker-git-session-sync.js", }, @@ -184,7 +184,7 @@ "@effect/platform-node": "^0.107.0", "@effect/schema": "^0.75.5", "effect": "^3.21.2", - "react": "^19.2.7", + "react": "19.2.4", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", }, @@ -217,7 +217,7 @@ "fast-check": "^4.8.0", "globals": "^17.6.0", "jscpd": "^4.2.4", - "react-dom": "^19.2.7", + "react-dom": "19.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", "vite": "^8.0.16", @@ -233,7 +233,8 @@ "@parcel/watcher", ], "overrides": { - "react": "19.2.7", + "react": "19.2.4", + "react-dom": "19.2.4", }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "7.28.5", "js-tokens": "4.0.0", "picocolors": "1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -1566,9 +1567,9 @@ "rdf-canonize": ["rdf-canonize@5.0.0", "", { "dependencies": { "setimmediate": "^1.0.5" } }, "sha512-g8OUrgMXAR9ys/ZuJVfBr05sPPoMA7nHIVs8VEvg9QwM5W4GR2qSFEEHjsyHF1eWlBaf8Ev40WNjQFQ+nJTO3w=="], - "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], - "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], diff --git a/package.json b/package.json index b75a268f..e4fbdf45 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ "unrs-resolver" ], "overrides": { - "react": "19.2.7" + "react": "19.2.4", + "react-dom": "19.2.4" }, "repository": { "type": "git", diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index 726a05ab..3a96ca24 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -46,11 +46,32 @@ RUN set -eu; \ && nvidia-ctk runtime configure --runtime=docker \ && rm -rf /var/lib/apt/lists/* -RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \ - && apt-get install -y --no-install-recommends nodejs \ - && curl -fsSL https://bun.sh/install | bash \ - && npm i -g node-gyp \ - && rm -rf /var/lib/apt/lists/* +RUN set -eu; \ + installed=0; \ + for attempt in 1 2 3 4 5; do \ + rm -rf /var/lib/apt/lists/* /opt/bun; \ + if curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 https://deb.nodesource.com/setup_24.x -o /tmp/nodesource-setup.sh \ + && bash /tmp/nodesource-setup.sh \ + && apt-get -o Acquire::Retries=3 install -y --no-install-recommends nodejs \ + && npm install -g --prefix /opt/bun --no-audit --no-fund bun@1.3.11 node-gyp \ + && node -v \ + && npm -v \ + && bun --version \ + && test "$(bun --version)" = "1.3.11" \ + && node-gyp --version; then \ + installed=1; \ + break; \ + fi; \ + echo "controller tooling install attempt ${attempt} failed; retrying..." >&2; \ + rm -f /tmp/nodesource-setup.sh; \ + sleep $((attempt * 2)); \ + done; \ + if [ "$installed" != "1" ]; then \ + echo "controller tooling install failed after retries" >&2; \ + exit 1; \ + fi; \ + rm -f /tmp/nodesource-setup.sh; \ + rm -rf /var/lib/apt/lists/* FROM controller-base AS workspace-deps diff --git a/packages/app/package.json b/packages/app/package.json index 7da1314a..4c6ec213 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -80,8 +80,8 @@ "@gridland/bun": "0.4.3", "@gridland/web": "0.4.3", "effect": "^3.21.2", - "react": "^19.2.7", - "react-dom": "^19.2.7", + "react": "19.2.4", + "react-dom": "19.2.4", "react-reconciler": "^0.33.0", "ts-morph": "^28.0.0" }, diff --git a/packages/app/tests/docker-git/controller-resource-limits.test.ts b/packages/app/tests/docker-git/controller-resource-limits.test.ts index 9b03314a..400198fc 100644 --- a/packages/app/tests/docker-git/controller-resource-limits.test.ts +++ b/packages/app/tests/docker-git/controller-resource-limits.test.ts @@ -108,6 +108,34 @@ describe("API Dockerfile Electron materialization", () => { })) }) +describe("API Dockerfile controller tooling install", () => { + it.effect("retries network-bound controller tooling downloads", () => + Effect.gen(function*(_) { + const contents = yield* _(readComposeFile("packages/api/Dockerfile")) + expect(contents).toContain("https://deb.nodesource.com/setup_24.x -o /tmp/nodesource-setup.sh") + expect(contents).toContain("npm install -g --prefix /opt/bun --no-audit --no-fund bun@1.3.11 node-gyp") + expect(contents).toContain("curl -fsSL --retry 5 --retry-all-errors --retry-delay 2") + expect(contents).toContain("controller tooling install failed after retries") + expect(contents).toContain("test \"$(bun --version)\" = \"1.3.11\"") + expect(contents).toContain("node-gyp --version") + })) +}) + +describe("OpenCode E2E auth bootstrap", () => { + it.effect("retries controller auth commands before the clone scenario", () => + Effect.gen(function*(_) { + const contents = yield* _(readComposeFile("scripts/e2e/opencode-autoconnect.sh")) + expect(contents).toContain("auth_attempts=3") + expect(contents).toContain(": > \"$AUTH_LOG\"") + expect(contents).toContain("if (") + expect(contents).toContain("dg_run_docker_git \"$REPO_ROOT\" auth codex import") + expect(contents).toContain("dg_run_docker_git \"$REPO_ROOT\" auth codex status") + expect(contents).toContain(") >>\"$AUTH_LOG\" 2>&1") + expect(contents).toContain("auth bootstrap attempt $auth_attempt/$auth_attempts failed") + expect(contents).toContain("docker-git auth bootstrap failed after $auth_attempts attempts") + })) +}) + describe("controller resource limit resolution", () => { it.effect("resolves CPU and RAM defaults to 90% of host resources", () => Effect.sync(() => { diff --git a/packages/app/tests/docker-git/gridland-react-singleton.test.ts b/packages/app/tests/docker-git/gridland-react-singleton.test.ts index 076fa822..2532729a 100644 --- a/packages/app/tests/docker-git/gridland-react-singleton.test.ts +++ b/packages/app/tests/docker-git/gridland-react-singleton.test.ts @@ -2,11 +2,31 @@ import { describe, expect, it } from "@effect/vitest" import { Effect } from "effect" import rootPackage from "../../../../package.json" with { type: "json" } +import terminalPackage from "../../../terminal/package.json" with { type: "json" } import appPackage from "../../package.json" with { type: "json" } +// CHANGE: encode the React version resolved for the Gridland Bun renderer. +// WHY: @gridland/bun embeds react-reconciler@0.33.0; Bun resolves that renderer contract to React 19.2.4. +// QUOTE(ISSUE): "TypeError: null is not an object (evaluating 'resolveDispatcher().useCallback')" +// REF: issue-385 +// SOURCE: n/a +// FORMAT THEOREM: workspaceReactVersion = rendererReactVersion -> dispatcher(hookCall) != null +// PURITY: CORE +// EFFECT: n/a +// INVARIANT: every workspace React entry resolves to the renderer-compatible singleton version. +// COMPLEXITY: O(1) +const gridlandRendererReactVersion = "19.2.4" + +const stripCaret = (value: string): string => value.replace(/^\^/u, "") + describe("Gridland React singleton contract", () => { it.effect("pins React across workspace dependencies for the Gridland renderer", () => Effect.sync(() => { - expect(rootPackage.overrides.react).toBe(appPackage.dependencies.react.replace(/^\^/u, "")) + expect(rootPackage.overrides.react).toBe(gridlandRendererReactVersion) + expect(rootPackage.overrides["react-dom"]).toBe(gridlandRendererReactVersion) + expect(stripCaret(appPackage.dependencies.react)).toBe(gridlandRendererReactVersion) + expect(stripCaret(appPackage.dependencies["react-dom"])).toBe(gridlandRendererReactVersion) + expect(stripCaret(terminalPackage.dependencies.react)).toBe(gridlandRendererReactVersion) + expect(stripCaret(terminalPackage.devDependencies["react-dom"])).toBe(gridlandRendererReactVersion) })) }) diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 3840bd72..3860b46a 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -22,7 +22,7 @@ "@effect/platform-node": "^0.107.0", "@effect/schema": "^0.75.5", "effect": "^3.21.2", - "react": "^19.2.7", + "react": "19.2.4", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0" }, @@ -55,7 +55,7 @@ "fast-check": "^4.8.0", "globals": "^17.6.0", "jscpd": "^4.2.4", - "react-dom": "^19.2.7", + "react-dom": "19.2.4", "typescript": "^6.0.3", "typescript-eslint": "^8.60.1", "vite": "^8.0.16", diff --git a/scripts/e2e/opencode-autoconnect.sh b/scripts/e2e/opencode-autoconnect.sh index c2b4e103..f8127b23 100755 --- a/scripts/e2e/opencode-autoconnect.sh +++ b/scripts/e2e/opencode-autoconnect.sh @@ -124,11 +124,26 @@ OPENCODE_AUTO_CONNECT=1 EOF_ENV AUTH_LOG="$ROOT/codex-auth.log" -( - cd "$REPO_ROOT" - dg_run_docker_git "$REPO_ROOT" auth codex import --codex-auth "$ROOT/.orch/auth/codex" - dg_run_docker_git "$REPO_ROOT" auth codex status --codex-auth "$ROOT/.orch/auth/codex" -) >"$AUTH_LOG" 2>&1 +auth_attempts=3 +auth_attempt=1 +auth_exit=0 +: > "$AUTH_LOG" +while [[ "$auth_attempt" -le "$auth_attempts" ]]; do + if ( + cd "$REPO_ROOT" + dg_run_docker_git "$REPO_ROOT" auth codex import --codex-auth "$ROOT/.orch/auth/codex" + dg_run_docker_git "$REPO_ROOT" auth codex status --codex-auth "$ROOT/.orch/auth/codex" + ) >>"$AUTH_LOG" 2>&1; then + auth_exit=0 + break + else + auth_exit=$? + fi + echo "e2e/opencode-autoconnect: auth bootstrap attempt $auth_attempt/$auth_attempts failed (exit: $auth_exit); retrying..." >&2 + auth_attempt="$((auth_attempt + 1))" + sleep 2 +done +[[ "$auth_exit" -eq 0 ]] || fail "docker-git auth bootstrap failed after $auth_attempts attempts (last exit: $auth_exit)" auth_confirmation_count="$(grep -Fc -- "Codex auth imported into controller state (account: ci@example.com)." "$AUTH_LOG" || true)" [[ "$auth_confirmation_count" -ge 2 ]] \