Honor workspace npm config for CLI installs (#443)

## Summary
- Run npm commands from the caller workspace so npm remains the source
of truth for `.npmrc` parsing, auth, delegated config files, env
expansion, quoting, and precedence.
- Keep the CLI install isolated with `--prefix`, while explicitly
overriding action-owned npm policy such as `offline=false`,
`bin-links=true`, and no package lock.
- Treat existing `node` and `npm` commands on musl as sufficient only
when they actually run, probing Alpine `/usr/bin` binaries with
`/usr/bin` first on PATH.
- Prefer Alpine `/usr/bin` runtime binaries when an earlier PATH entry
shadows them, and keep apk installation for missing runtime libraries or
missing commands.

## Validation
- `bun run ci`
- Real npm sanity check for workspace `.npmrc` with quoted/env delegated
config, `globalconfig`, relative `cafile`, mTLS path keys,
`offline=true`, `bin-links=false`, and package-lock policy
- Fixture workflow:
https://github.com/jgoux/setup-cli-testing/actions/runs/28663875606

Addresses
https://github.com/supabase/setup-cli/pull/442#discussion_r3519786253
Addresses
https://github.com/supabase/setup-cli/pull/442#discussion_r3519786255
This commit is contained in:
Julien Goux
2026-07-07 13:11:42 +02:00
committed by GitHub
parent 23ef4b0416
commit 4c16bf7a1f
3 changed files with 177 additions and 6 deletions

View File

@@ -252,6 +252,7 @@ async function getPackageMetadata(resolution: PackageResolution): Promise<Packag
"scripts",
"dist.integrity",
"--json",
"--offline=false",
]);
const metadata = JSON.parse(output) as unknown;
@@ -293,6 +294,7 @@ function createInstallRoot(): string {
async function runNpm(args: string[]): Promise<string> {
const executable = process.env[NPM_EXECUTABLE_ENV]?.trim() || "npm";
const proc = Bun.spawn([executable, ...args], {
cwd: process.env.GITHUB_WORKSPACE?.trim() ?? process.cwd(),
env: process.env,
stderr: "pipe",
stdout: "pipe",
@@ -311,12 +313,12 @@ async function runNpm(args: string[]): Promise<string> {
}
export async function installCli(resolution: PackageResolution): Promise<string> {
const installRoot = createInstallRoot();
const metadata = await getPackageMetadata(resolution);
verifyPackageMetadata(resolution, metadata);
verifyPackageIntegrity(resolution, metadata);
const installRoot = createInstallRoot();
await runNpm([
"install",
"--prefix",
@@ -326,6 +328,8 @@ export async function installCli(resolution: PackageResolution): Promise<string>
"--no-audit",
"--no-fund",
"--no-package-lock",
"--offline=false",
"--bin-links=true",
`--ignore-scripts=${shouldIgnoreInstallScripts(metadata)}`,
resolution.spec,
]);