mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
fix(install): share arguments between the plan and npm execution
Include the Linux user prefix in the planned command. Build the install arguments once and shell-escape the printed arguments so paths with spaces or metacharacters remain reusable. Cover dry-run admission, exact argument round-trip and the unprefixed macOS command with native Bash 3.2. Refs: #1417
This commit is contained in:
+7
-4
@@ -85,19 +85,22 @@ if [[ "$version" =~ \+ ]]; then
|
||||
fail "npm cannot install an exact version with build metadata. Pass a version without build metadata, then retry."
|
||||
fi
|
||||
|
||||
prefix_args=()
|
||||
install_args=(install --global)
|
||||
if [[ "$platform" == "Linux" ]]; then
|
||||
prefix_args=(--prefix "$HOME/.local")
|
||||
install_args+=(--prefix "$HOME/.local")
|
||||
install_bin="$HOME/.local/bin/rea"
|
||||
else
|
||||
npm_prefix="$(npm prefix --global)" || fail "the npm global prefix could not be read. Repair the npm configuration, then retry."
|
||||
install_bin="$npm_prefix/bin/rea"
|
||||
fi
|
||||
install_args+=("$PACKAGE@$version")
|
||||
|
||||
printf 'REA install plan\n'
|
||||
printf ' Version: %s\n' "$version"
|
||||
printf ' Runtime: Node.js %s\n' "$node_version"
|
||||
printf ' Command: npm install --global %s@%s\n' "$PACKAGE" "$version"
|
||||
printf ' Command:'
|
||||
printf ' %q' npm "${install_args[@]}"
|
||||
printf '\n'
|
||||
printf ' Binary: %s\n' "$install_bin"
|
||||
printf ' Setup: %s\n' "$([[ "$start_setup" == true ]] && printf 'start when a terminal is available' || printf 'skipped')"
|
||||
|
||||
@@ -107,7 +110,7 @@ if [[ "$dry_run" == true ]]; then
|
||||
fi
|
||||
|
||||
printf 'Installing %s@%s...\n' "$PACKAGE" "$version"
|
||||
npm install --global ${prefix_args[@]+"${prefix_args[@]}"} "$PACKAGE@$version" || fail "npm could not install REA. Check registry access and npm permissions, then retry."
|
||||
npm "${install_args[@]}" || fail "npm could not install REA. Check registry access and npm permissions, then retry."
|
||||
[[ -x "$install_bin" ]] || fail "npm completed without installing the rea command. Check the npm global bin directory and PATH, then retry."
|
||||
installed_version="$("$install_bin" --version 2>/dev/null | tr -d '[:space:]')" || fail "the installed REA version could not be read. Reinstall the requested version, then retry."
|
||||
[[ "$installed_version" == "$version" ]] || fail "installed version $installed_version does not match $version. Reinstall the requested version, then retry."
|
||||
|
||||
@@ -172,6 +172,42 @@ describe("curl installer scenarios", { timeout: 20_000 }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Linux user-prefix installer", { timeout: 20_000 }, () => {
|
||||
it("prints the executed arguments as a reusable command with shell metacharacters", async () => {
|
||||
const fixture = await createFixture();
|
||||
const home = join(fixture.home, "home with 'quotes' $cash; [glob]");
|
||||
const argvLog = join(fixture.home, "npm-argv.log");
|
||||
const overrides = { HOME: home, FAKE_NPM_ARGV_LOG: argvLog };
|
||||
const plan = await runInstaller(
|
||||
fixture,
|
||||
["--version", "0.3.0", "--dry-run"],
|
||||
overrides,
|
||||
);
|
||||
await expect(readFile(argvLog)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
const command = /^ Command: (.+)$/mu.exec(plan.stdout)?.[1];
|
||||
expect(command).toBeDefined();
|
||||
const printed = await execFileAsync("/bin/bash", [
|
||||
"-c",
|
||||
`npm() { printf '%s\\0' "$@"; }\n${command}`,
|
||||
]);
|
||||
const installed = await runInstaller(
|
||||
fixture,
|
||||
["--version", "0.3.0"],
|
||||
overrides,
|
||||
);
|
||||
expect(installed.stdout).toContain(` Command: ${command}\n`);
|
||||
expect(printed.stdout.split("\0")).toEqual([
|
||||
"install",
|
||||
"--global",
|
||||
"--prefix",
|
||||
join(home, ".local"),
|
||||
"[email protected]",
|
||||
"",
|
||||
]);
|
||||
expect((await readFile(argvLog)).toString()).toBe(printed.stdout);
|
||||
});
|
||||
});
|
||||
|
||||
describe("native macOS installer", { timeout: 20_000 }, () => {
|
||||
it("installs on Darwin with an empty prefix argument array", async () => {
|
||||
const fixture = await createFixture();
|
||||
@@ -179,6 +215,9 @@ describe("native macOS installer", { timeout: 20_000 }, () => {
|
||||
FAKE_PLATFORM: "Darwin",
|
||||
FAKE_NPM_PREFIX: join(fixture.home, "npm global prefix"),
|
||||
});
|
||||
expect(result.stdout).toContain(
|
||||
" Command: npm install --global [email protected]\n",
|
||||
);
|
||||
expect(result.stdout).toContain("REA 0.3.0 is installed");
|
||||
expect(await readFile(fixture.npmLog, "utf8")).toBe(
|
||||
"prefix --global\ninstall --global [email protected]\n",
|
||||
@@ -322,6 +361,7 @@ if [ "$1" = "-p" ]; then printf '%s\n' "\${FAKE_NODE_VERSION:-24.18.0}"; else ex
|
||||
join(bin, "npm"),
|
||||
`#!/bin/sh
|
||||
printf '%s\n' "$*" >> "$FAKE_NPM_LOG"
|
||||
if [ -n "\${FAKE_NPM_ARGV_LOG:-}" ]; then printf '%s\\0' "$@" > "$FAKE_NPM_ARGV_LOG"; fi
|
||||
[ "$1" = "prefix" ] && { [ "\${FAKE_NPM_PREFIX_FAIL:-}" = "1" ] && exit 1; printf '%s\n' "$FAKE_NPM_PREFIX"; exit 0; }
|
||||
[ "\${FAKE_NPM_FAIL:-}" = "1" ] && exit 1
|
||||
prefix="$HOME/.local"
|
||||
|
||||
Reference in New Issue
Block a user