fix(ci): freeze lockfiles before pnpm bootstrap (#162862)

## What Problem This Solves

Older selected-source installers can invoke pnpm before their install-only frozen-lockfile flag takes effect, allowing package-manager bootstrap to rewrite the selected source lockfile.

## User Impact

The source receiver supplies frozen policy before starting the selected installer. Source verification, dependency pins, and false-input behavior stay unchanged. No schema or runtime storage change.

## Why This Change Was Made

Current main already fixes its shared installer with early native policy and an explicit install flag. This PR preserves that implementation and adds only the receiver process-boundary setting, plus distinct regression coverage. The explicit flag remains necessary for older pnpm workspace-configuration precedence. The final production/tooling delta is one added line.

## Evidence

Actual pnpm 12.5.1 originally reproduced the lockfile mutation. The repaired Linux receiver preserved the selected source through the unchanged post-install verifier; its later SQLite diagnostic failure remains separate and is not claimed fixed.

On the corrected composition, all 14 hydration cases passed in 38.273 seconds. Actual pnpm 10.23.0 conflicting-workspace and false/invalid-input cells preserved the expected lockfile behavior. Prior receiver boundary proof remains applicable to unchanged source. Independent full-diff P2 review is clean.

The earlier broad gate attempt and historical diagnostic failures remain recorded. The maintainer explicitly waived further test execution and approved landing this reviewed correction; no current-head full-suite or hosted-CI success is claimed. Independent code review, security review, and exact-head merge admission remain required.
This commit is contained in:
Peter Steinberger 2026-10-02 01:48:58 -05:00 • committed by GitHub
parent b80acea636
commit c4a1d35536
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 1 deletions

View file

@ -289,6 +289,7 @@ try {
// source's install owner before any caller payload can run.
const installEnv = { ...env, CI: "true", GITHUB_WORKSPACE: cwd,
NODE_BIN: path.dirname(process.execPath), FROZEN_LOCKFILE: "true",
PNPM_CONFIG_FROZEN_LOCKFILE: "true",
DEPENDENCY_CACHE: "false", DEPENDENCY_CACHE_HIT: "false" };
for (const key of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) delete installEnv[key];
process.stderr.write("[crabbox] reconciling selected-source dependencies\n");

View file

@ -47,6 +47,51 @@ function write(root: string, relative: string, contents: string, mode?: number)
}
describe.skipIf(process.platform === "win32")("Crabbox dependency hydration", () => {
it.each(["true", "false", "invalid"])(
"preserves frozen policy through pnpm bootstrap and workspace config (%s)",
(frozen) => {
const root = tempDirs.make("openclaw-frozen-bootstrap-");
const bin = path.join(root, "bin");
mkdirSync(bin);
symlinkSync(resolveTestNodeExecPath(), path.join(bin, "node"));
const lock = path.join(root, "pnpm-lock.yaml");
const calls = path.join(root, "pnpm-calls");
writeFileSync(lock, "original\n");
write(
bin,
"pnpm",
`#!/bin/bash
printf '%s\\n' "$*" >> "$PNPM_CALLS"
# pnpm 10 applies workspace frozenLockfile:false after its environment settings.
if [ "$1" = install ]; then
case " $* " in
*" --frozen-lockfile "*) ;;
*) printf 'rewritten\\n' > "$PNPM_LOCK" ;;
esac
elif [ "\${PNPM_CONFIG_FROZEN_LOCKFILE:-}" != true ]; then
printf 'rewritten\\n' > "$PNPM_LOCK"
fi
`,
0o755,
);
const result = spawnSync("bash", [".github/actions/setup-node-env/install-dependencies.sh"], {
encoding: "utf8",
env: {
PATH: process.env.PATH,
NODE_BIN: bin,
FROZEN_LOCKFILE: frozen,
DEPENDENCY_CACHE: "false",
DEPENDENCY_CACHE_HIT: "false",
PNPM_CALLS: calls,
PNPM_LOCK: lock,
},
});
expect(result.status, result.stderr).toBe(frozen === "invalid" ? 2 : 0);
expect(readFileSync(lock, "utf8")).toBe(frozen === "false" ? "rewritten\n" : "original\n");
expect(existsSync(calls)).toBe(frozen !== "invalid");
},
);
it.each([
["default hydration", "fresh"],
["shared setup action", "fresh"],

View file

@ -4742,7 +4742,11 @@ process.on("exit", () => {
const copyOptions = { recursive: true, verbatimSymlinks: true };
cpSync(selectedSource, producer, copyOptions);
mkdirSync(path.dirname(path.join(producer, installOwner)), { recursive: true });
writeFileSync(path.join(producer, installOwner), installer);
writeFileSync(
path.join(producer, installOwner),
// Older selected installers can invoke pnpm before applying their install-only flag.
'test "${PNPM_CONFIG_FROZEN_LOCKFILE:-}" = true || exit 73\n' + installer,
);
writeFileSync(
path.join(producer, sourceCommand),
[