mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(install): avoid success after incomplete lifecycle changes (#125992)
* fix(install): make lifecycle mutations transactional Standalone installers now apply npm-version-aware lifecycle approval. Updates verify and repair the installation before reporting success and preserve the prior install owner during method switches. Uninstall now exits nonzero when requested cleanup is only partially completed. Plugin update behavior is unchanged. Closes #125925 * test(uninstall): assert aggregated live-owner failure * fix(install): satisfy standalone shell checks * fix(update): scan PATH for prior Git wrapper * test(hooks): await Gmail watcher descendant exit * fix(install): verify Windows npm candidate * fix(ci): normalize package acceptance version * fix(update): preserve staged local package links * test(update): fold staged symlink coverage * fix(update): retire every legacy Git wrapper * test(docs): align consolidated ownership checks
This commit is contained in:
committed by
GitHub
parent
a441431896
commit
7bc994aee8
@@ -23,6 +23,7 @@ import {
|
||||
writeNpmBeforePolicyFixture,
|
||||
writeNpmFreshnessConflictFixture,
|
||||
writeNpmInstallRetryFixture,
|
||||
writeNpmLifecycleFixture,
|
||||
} from "./install-npm-fixtures.js";
|
||||
|
||||
const SCRIPT_PATH = "scripts/install-cli.sh";
|
||||
@@ -604,7 +605,10 @@ describe("install-cli.sh", () => {
|
||||
const dependencyInstallIndex = script.indexOf(
|
||||
'CI="${CI:-true}" run_pnpm -C "$repo_dir" install "$install_lockfile_flag"',
|
||||
);
|
||||
const wrapperIndex = script.indexOf('cat > "${PREFIX}/bin/openclaw"', compatibilityIndex);
|
||||
const wrapperIndex = script.indexOf(
|
||||
'publish_executable_wrapper "${PREFIX}/bin/openclaw"',
|
||||
compatibilityIndex,
|
||||
);
|
||||
|
||||
expect(checkoutIndex).toBeGreaterThan(-1);
|
||||
expect(compatibilityIndex).toBeGreaterThan(checkoutIndex);
|
||||
@@ -660,6 +664,7 @@ describe("install-cli.sh", () => {
|
||||
"set -euo pipefail",
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"npm_lifecycle_allow_arg() { :; }",
|
||||
'install_node() { mkdir -p "$(node_dir)/lib/node_modules/openclaw/dist"; : > "$(node_dir)/lib/node_modules/openclaw/dist/entry.js"; }',
|
||||
"ensure_git() { :; }",
|
||||
'npm_bin() { printf "/usr/bin/true\\n"; }',
|
||||
@@ -773,7 +778,7 @@ describe("install-cli.sh", () => {
|
||||
symlinkSync("node-v24.15.0", join(prefix, "tools", "node"));
|
||||
writeFileSync(
|
||||
join(nodeDir, "bin", "npm"),
|
||||
'#!/bin/bash\nif [[ "$1" == "config" ]]; then printf "null\\n"; fi\n',
|
||||
'#!/bin/bash\nif [[ "$1" == "--version" ]]; then printf "11.15.0\\n"; elif [[ "$1" == "config" ]]; then printf "null\\n"; fi\n',
|
||||
);
|
||||
chmodSync(join(nodeDir, "bin", "npm"), 0o755);
|
||||
for (const entry of [
|
||||
@@ -1599,6 +1604,100 @@ describe("install-cli.sh", () => {
|
||||
expect(script).toContain("env -u NPM_CONFIG_BEFORE -u npm_config_before");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ expected: "", version: "11.15.0" },
|
||||
{ expected: "--allow-scripts=openclaw", version: "11.16.0" },
|
||||
{ expected: "--allow-scripts=openclaw", version: "12.0.0" },
|
||||
])("resolves canonical npm lifecycle policy for npm $version", ({ expected, version }) => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-lifecycle-"));
|
||||
const npm = join(tmp, "npm");
|
||||
writeNpmLifecycleFixture(npm);
|
||||
try {
|
||||
const result = runInstallCliShell(
|
||||
[
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
`result="$(npm_lifecycle_allow_arg ${JSON.stringify(npm)} openclaw@latest)"`,
|
||||
`printf '%s' "$result"`,
|
||||
].join("\n"),
|
||||
{ NPM_FAKE_VERSION: version },
|
||||
);
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toBe(expected);
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an invalid npm version before mutation", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-lifecycle-invalid-"));
|
||||
const npm = join(tmp, "npm");
|
||||
const args = join(tmp, "args");
|
||||
writeNpmLifecycleFixture(npm);
|
||||
try {
|
||||
const result = runInstallCliShell(
|
||||
[
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
`npm_lifecycle_allow_arg ${JSON.stringify(npm)} openclaw@latest`,
|
||||
].join("\n"),
|
||||
{ NPM_FAKE_ARGS: args, NPM_FAKE_VERSION: "invalid" },
|
||||
);
|
||||
expect(result.status).not.toBe(0);
|
||||
expect(existsSync(args)).toBe(false);
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["openclaw@npm:@scope/candidate@1.0.0", "--allow-scripts=@scope/candidate"],
|
||||
["file:/tmp/openclaw.tgz", "--allow-scripts=file:/tmp/openclaw.tgz"],
|
||||
[
|
||||
"https://example.invalid/openclaw.tgz",
|
||||
"--allow-scripts=https://example.invalid/openclaw.tgz",
|
||||
],
|
||||
])("uses npm-resolved lifecycle identity for %s", (spec, expected) => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-identity-"));
|
||||
const npm = join(tmp, "npm");
|
||||
writeNpmLifecycleFixture(npm);
|
||||
try {
|
||||
const result = runInstallCliShell(
|
||||
[
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
`npm_lifecycle_allow_arg ${JSON.stringify(npm)} ${JSON.stringify(spec)}`,
|
||||
].join("\n"),
|
||||
{ NPM_FAKE_VERSION: "12.0.0" },
|
||||
);
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout.trim()).toBe(expected);
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("relativizes absolute npm path identities against the command cwd", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-identity-comma,"));
|
||||
const npm = join(tmp, "npm");
|
||||
const commandCwd = join(tmp, "safe");
|
||||
const candidate = join(tmp, "candidate.tgz");
|
||||
mkdirSync(commandCwd);
|
||||
writeNpmLifecycleFixture(npm);
|
||||
try {
|
||||
const result = runInstallCliShell(
|
||||
[
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
`node_bin() { printf '%s\\n' ${JSON.stringify(process.execPath)}; }`,
|
||||
`cd ${JSON.stringify(commandCwd)}`,
|
||||
`npm_lifecycle_allow_arg ${JSON.stringify(npm)} ${JSON.stringify(candidate)} "$PWD"`,
|
||||
].join("\n"),
|
||||
{ NPM_FAKE_VERSION: "12.0.0" },
|
||||
);
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout.trim()).toBe("--allow-scripts=../candidate.tgz");
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not emit --before when raw user npmrc config contains min-release-age", () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-cli-npmrc-"));
|
||||
const bin = join(tmp, "bin");
|
||||
@@ -1638,6 +1737,7 @@ describe("install-cli.sh", () => {
|
||||
"set -euo pipefail",
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"npm_lifecycle_allow_arg() { :; }",
|
||||
`npm_bin() { printf '%s\\n' ${JSON.stringify(fakeNpm)}; }`,
|
||||
`node_dir() { printf '%s\\n' ${JSON.stringify(nodeDir)}; }`,
|
||||
"emit_json() { :; }",
|
||||
@@ -1722,6 +1822,7 @@ describe("install-cli.sh", () => {
|
||||
"set -euo pipefail",
|
||||
`cd ${JSON.stringify(process.cwd())}`,
|
||||
`source ${JSON.stringify(SCRIPT_PATH)}`,
|
||||
"npm_lifecycle_allow_arg() { :; }",
|
||||
`npm_bin() { printf '%s\\n' ${JSON.stringify(fakeNpm)}; }`,
|
||||
`node_dir() { printf '%s\\n' ${JSON.stringify(nodeDir)}; }`,
|
||||
"emit_json() { :; }",
|
||||
|
||||
Reference in New Issue
Block a user