From ed4d39fd6f1d8652a77efeb7c6c7791b2cdc73cb Mon Sep 17 00:00:00 2001 From: Laith Al-Saadoon <9553966+theagenticguy@users.noreply.github.com> Date: Fri, 29 May 2026 08:09:56 -0500 Subject: [PATCH] fix(ci): isolate verify-global-install into a per-run npm prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Volta macOS leg failed gate 2 (GHCR/postinstall fetch) + gate 4 (install > 60s) intermittently, even though NO OpenCodeHub package depends on node-pty anymore (dep removed in the graphty-Leiden vendoring; lockfile + every packed tarball are clean — verified). Root cause: `npm install -g` installed into whatever global prefix the node manager provides, and Volta persists its global package dir across runs on the hosted runner. A node-pty left behind by a pre-removal run re-ran its `prebuild-install` GHCR fetch on the next install (and bloated install time to 75-95s vs 25-50s elsewhere). arm64-nvm on the SAME run passed gate 2 — proof it's cached runner state, not the dep graph. Install into a fresh `mktemp -d` prefix per cell (npm_config_prefix + PATH), removed on the EXIT trap. Each cell is now hermetic: the gates see only what THIS run's tarballs actually pull, immune to cross-run global pollution. Validated locally end-to-end (`bash scripts/verify-global-install.sh local`): 9/9 gates+smokes pass, gate 2 clean, install 12s. --- scripts/verify-global-install.sh | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/scripts/verify-global-install.sh b/scripts/verify-global-install.sh index a171ede..40605fb 100755 --- a/scripts/verify-global-install.sh +++ b/scripts/verify-global-install.sh @@ -62,9 +62,14 @@ note() { printf ' ...... %s\n' "$1"; } # -------------------------------------------------------------------- cleanup # shellcheck disable=SC2329 # invoked indirectly via the EXIT trap below. cleanup() { - # Drop the global install we created so re-runs are idempotent. Errors - # are tolerated — the install may have failed before the binary landed. - npm uninstall -g @opencodehub/cli @opencodehub/ingestion >/dev/null 2>&1 || true + # The install lives in a per-run isolated prefix (set below as + # $ISOLATED_PREFIX); removing it drops the whole global tree at once, which + # is fully hermetic and avoids depending on `npm uninstall -g` resolving the + # right prefix. Guarded because the var is set after arg parsing — an early + # exit (bad MODE) may run cleanup before it exists. + if [ -n "${ISOLATED_PREFIX:-}" ] && [ -d "$ISOLATED_PREFIX" ]; then + rm -rf "$ISOLATED_PREFIX" + fi if [ "$MODE" = "local" ] && [ -d "$TARBALL_DIR" ]; then rm -rf "$TARBALL_DIR" fi @@ -128,6 +133,21 @@ else exit 1 fi +# -------------------------------------------------------------------- hermetic global prefix +# Install into a FRESH, per-run npm global prefix instead of whatever the node +# manager provides. Some managers (notably Volta) persist their global package +# dir across runs on the hosted runner, so a node-pty left behind by a +# pre-fix run re-runs its `prebuild-install` GHCR fetch on the next +# `npm install -g` — tripping gate 2 even though NO OpenCodeHub package depends +# on node-pty (the dep was removed; the lockfile + every tarball are clean). +# A clean prefix makes each cell hermetic: gates see only what THIS run's +# tarballs actually pull, immune to cached cross-run global state. Prepend its +# bin dir to PATH so the `codehub` shim resolves from here. +ISOLATED_PREFIX=$(mktemp -d -t verify-global-install-prefix.XXXXXX) +export npm_config_prefix="$ISOLATED_PREFIX" +export PATH="$ISOLATED_PREFIX/bin:$PATH" +note "isolated npm global prefix: $ISOLATED_PREFIX" + # -------------------------------------------------------------------- install + capture INSTALL_LOG=$(mktemp -t verify-global-install-log.XXXXXX) log "running: npm install -g ${INSTALL_ARGS[*]}"