Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,12 @@ is_reusable_managed_nemoclaw_install() {
[[ "$version_output" == "${_CLI_BIN} v${identity_version}" ]]
}

restore_managed_source_lockfile() {
local source_root="$1"
[[ -d "${source_root}/.git" && ! -L "${source_root}/.git" ]] || return 0
git -C "$source_root" checkout -- package-lock.json 2>/dev/null || true
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

finish_nemoclaw_install() {
# A backup-preparation pass defers OpenShell but still prepares the CLI. The
# later install pass completes only the OpenShell policy for that source mode.
Expand Down Expand Up @@ -2141,6 +2147,7 @@ install_nemoclaw() {
spin "Building ${_CLI_DISPLAY} CLI modules" bash -c "cd \"$nemoclaw_src\" && npm run --if-present build:cli"
spin "Building ${_CLI_DISPLAY} plugin" bash -c "cd \"$nemoclaw_src\"/nemoclaw && npm ci --ignore-scripts && npm run build"
spin "Linking ${_CLI_DISPLAY} CLI" bash -c "cd \"$nemoclaw_src\" && npm link"
restore_managed_source_lockfile "$nemoclaw_src"
fi
_NEMOCLAW_CLI_INSTALL_MODE=managed
fi
Expand Down
36 changes: 35 additions & 1 deletion test/install-managed-cli-reuse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function writeManagedSource(root: string, revision: string) {
function runManagedCliInstallTwice(
initialRevision = INSTALL_REUSE_REVISION,
forceCliReinstall = false,
separateInstallerRuns = false,
) {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-install-reuse-"));
const home = path.join(tmp, "home");
Expand Down Expand Up @@ -92,14 +93,21 @@ case "\${1:-}" in
printf '%s\n' "$EXPECTED_REVISION"
fi
;;
diff) exit 0 ;;
diff)
if [ -n "$repo" ] && [ -f "$repo/.fixture-lockfile-dirty" ]; then exit 1; fi
exit 0
;;
checkout)
if [ -n "$repo" ]; then rm -f "$repo/.fixture-lockfile-dirty"; fi
;;
init)
target="\${@: -1}"
mkdir -p "$target/.git" "$target/bin" "$target/dist/lib/onboard" "$target/node_modules" \
"$target/nemoclaw/dist" "$target/nemoclaw/node_modules"
printf '%s' "$EXPECTED_REVISION" > "$target/.fixture-revision"
printf '%s\n' '{"name":"nemoclaw","dependencies":{"openclaw":"2026.7.1"}}' > "$target/package.json"
printf '%s\n' '{"name":"nemoclaw-plugin"}' > "$target/nemoclaw/package.json"
printf '%s\n' '{"lockfileVersion":3}' > "$target/package-lock.json"
;;
describe) printf '%s\n' 'v0.0.99' ;;
esac
Expand All @@ -117,6 +125,21 @@ if [ "\${1:-}" = "config" ] && [ "\${2:-}" = "get" ] && [ "\${3:-}" = "prefix" ]
exit 0
fi
if [ "\${1:-}" = "pack" ]; then exit 1; fi
if [ "\${1:-}" = "install" ]; then : > "$PWD/.fixture-lockfile-dirty"; fi
if [ "\${1:-}" = "run" ]; then
case "$*" in
*build:cli*)
mkdir -p "$PWD/dist/lib/onboard"
printf '%s\n' 'module.exports = {};' > "$PWD/dist/lib/onboard/preflight.js"
printf '{\n "nemoclawVersion": "0.0.99",\n "sourceRevision": "%s"\n}\n' "$EXPECTED_REVISION" \
> "$PWD/dist/build-identity.json"
;;
*build*)
mkdir -p "$PWD/dist"
printf '%s\n' 'module.exports = {};' > "$PWD/dist/index.js"
;;
esac
fi
if [ "\${1:-}" = "link" ]; then
mkdir -p "$PWD/bin" "$NPM_PREFIX/bin"
cat > "$PWD/bin/nemoclaw.js" <<'CLI'
Expand All @@ -140,6 +163,7 @@ SCRIPT_DIR="$PAYLOAD_SCRIPTS"
NEMOCLAW_BOOTSTRAP_PAYLOAD=1
NEMOCLAW_DEFER_OPENSHELL_INSTALL=1
install_nemoclaw
${separateInstallerRuns ? "_NEMOCLAW_CLI_INSTALL_PREPARED=false" : ""}
install_nemoclaw
printf 'PREPARED=%s MODE=%s SOURCE=%s\n' \
"$_NEMOCLAW_CLI_INSTALL_PREPARED" "$_NEMOCLAW_CLI_INSTALL_MODE" "$NEMOCLAW_SOURCE_ROOT"`,
Expand Down Expand Up @@ -196,6 +220,16 @@ describe("installer-managed CLI reuse", () => {
expect(npmLog.match(/\|link$/gm)).toHaveLength(1);
});

it("reuses the managed checkout on a later installer run after its own dependency install (#8305)", () => {
const { result, gitLog, npmLog } = runManagedCliInstallTwice("b".repeat(40), false, true);

expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0);
expect(result.stdout).toContain("Reusing the installed NemoClaw CLI at the selected revision");
expect(gitLog.match(/^init\b/gm)).toHaveLength(1);
expect(npmLog.match(/\|install --ignore-scripts$/gm)).toHaveLength(1);
expect(npmLog.match(/\|link$/gm)).toHaveLength(1);
});

it("reinstalls the exact managed CLI once when update --fresh requests repair (#7898)", () => {
const { result, gitLog, npmLog } = runManagedCliInstallTwice(INSTALL_REUSE_REVISION, true);

Expand Down
Loading