Skip to content

Commit 41937c4

Browse files
committed
claude publish fix
1 parent fda4953 commit 41937c4

2 files changed

Lines changed: 70 additions & 22 deletions

File tree

packages/beachball/src/packageManager/packageManager.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@ import path from 'path';
44
export type PackageManagerResult = execa.ExecaReturnValue & { success: boolean };
55
export type PackageManagerOptions = execa.Options & { cwd: string };
66

7+
/**
8+
* Filter PATH for running npm commands, removing entries that contain shell-script node wrappers.
9+
* Package managers inject temp directories into PATH with node shims — yarn classic uses `yarn--*`
10+
* dirs, yarn berry uses `xfs-*` dirs. These shims are POSIX shell scripts, and POSIX sh drops env
11+
* vars whose names contain characters like / and :, which npm auth token env vars do contain.
12+
* See: https://github.com/yarnpkg/yarn/issues/6685
13+
*
14+
* Workaround for an issue on certain platforms/shells(?) if the parent command was run VIA yarn:
15+
* The auth environment variable (e.g. `npm_config_//someRegistry/:_authToken`) was not being
16+
* passed through to the child process. This might be because:
17+
* - Special characters such as / and : aren't valid in env var names for certain shells/platforms
18+
* - On every `yarn run ...` command, yarn makes temp directories like /<temp>/yarn--1776822418161-0.7992675923334178
19+
* with aliases for `node` and `yarn`. On Linux (and Mac), the `node` alias looks something like:
20+
* #!/bin/sh
21+
* exec "/path/to/node" "$@"
22+
* (see https://github.com/yarnpkg/yarn/issues/6685 for context)
23+
* - Best guess: invalid environment variable names are dropped by this extra `exec` step??
24+
* (This consistently reproed on Ubuntu+bash, but not Mac+zsh or bash. The clue was that the
25+
* tests passed even on Linux when run via debugTests.js, but failed when run via yarn test.)
26+
*
27+
* Removing yarn temp directories from the PATH seems to consistently fix this issue.
28+
* yarn classic (v1) uses `yarn--*` directories; yarn berry (v4) uses `xfs-*` directories.
29+
* Use `checkNpmAuthEnvPassthrough` after this filter to detect unknown variants of this issue.
30+
*/
31+
export function filterNpmPath(pathEnv: string): string {
32+
return pathEnv
33+
.split(path.delimiter)
34+
.filter(p => {
35+
const base = path.basename(p);
36+
return !base.startsWith('yarn--') && !base.startsWith('xfs-');
37+
})
38+
.join(path.delimiter);
39+
}
40+
741
/**
842
* Run a package manager command. Returns the error result instead of throwing on failure.
943
* @param manager The package manager to use
@@ -18,28 +52,7 @@ export async function packageManager(
1852
): Promise<PackageManagerResult> {
1953
let pathEnv = options.env?.PATH || process.env.PATH;
2054
if (manager === 'npm' && pathEnv) {
21-
// Workaround for an issue on certain platforms/shells(?) if the parent command was run VIA yarn:
22-
// The auth environment variable (e.g. `npm_config_//someRegistry/:_authToken`) was not being
23-
// passed through to the child process. This might be because:
24-
// - Special characters such as / and : aren't valid in env var names for certain shells/platforms
25-
// - On every `yarn run ...` command, yarn makes temp directories like /<temp>/yarn--1776822418161-0.7992675923334178
26-
// with aliases for `node` and `yarn`. On Linux (and Mac), the `node` alias looks something like:
27-
// #!/bin/sh
28-
// exec "/path/to/node" "$@"
29-
// (see https://github.com/yarnpkg/yarn/issues/6685 for context)
30-
// - Best guess: invalid environment variable names are dropped by this extra `exec` step??
31-
// (This consistently reproed on Ubuntu+bash, but not Mac+zsh or bash. The clue was that the
32-
// tests passed even on Linux when run via debugTests.js, but failed when run via yarn test.)
33-
//
34-
// Removing yarn temp directories from the PATH seems to consistently fix this issue.
35-
// yarn classic (v1) uses `yarn--*` directories; yarn berry (v4) uses `xfs-*` directories.
36-
pathEnv = pathEnv
37-
.split(path.delimiter)
38-
.filter(p => {
39-
const base = path.basename(p);
40-
return !base.startsWith('yarn--') && !base.startsWith('xfs-');
41-
})
42-
.join(path.delimiter);
55+
pathEnv = filterNpmPath(pathEnv);
4356
}
4457

4558
try {

packages/beachball/src/packageManager/packagePublish.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
import type { PackageInfo } from '../types/PackageInfo';
22
import path from 'path';
3+
import execa from 'execa';
34
import { npm, type NpmResult } from './npm';
45
import type { BeachballOptions } from '../types/BeachballOptions';
56
import { getNpmAuthEnv, getNpmPublishArgs } from './npmArgs';
67
import type { NpmOptions } from '../types/NpmOptions';
8+
import { filterNpmPath } from './packageManager';
9+
10+
/**
11+
* Check whether env vars with special characters in their names (like npm auth token env vars,
12+
* which contain // and :) will be passed through to npm subprocesses. Runs a test using the same
13+
* PATH filtering applied to actual npm commands, so a failure here means the fix in
14+
* `filterNpmPath` doesn't cover this platform/environment variant.
15+
*/
16+
async function checkNpmAuthEnvPassthrough(authEnvKey: string): Promise<boolean> {
17+
const sentinel = 'beachball-auth-sentinel';
18+
const filteredPath = process.env.PATH ? filterNpmPath(process.env.PATH) : undefined;
19+
try {
20+
const result = await execa(
21+
'node',
22+
['-e', `process.stdout.write(process.env[${JSON.stringify(authEnvKey)}] || '')`],
23+
{ env: { [authEnvKey]: sentinel, ...(filteredPath && { PATH: filteredPath }) }, extendEnv: true }
24+
);
25+
return result.stdout === sentinel;
26+
} catch {
27+
return false;
28+
}
29+
}
730

831
/**
932
* Attempt to publish the package with retries. Returns the result of the final npm publish call
@@ -24,6 +47,18 @@ export async function packagePublish(
2447
console.log(` publish command: ${publishArgs.join(' ')}`);
2548
console.log(` (cwd: ${packageRoot}${authEnv ? `, auth env var: ${Object.keys(authEnv)[0]}=****` : ''})\n`);
2649

50+
if (authEnv) {
51+
const [authEnvKey] = Object.keys(authEnv);
52+
if (!(await checkNpmAuthEnvPassthrough(authEnvKey))) {
53+
console.error(
54+
`Auth token env var "${authEnvKey}" is not being passed to npm subprocesses. ` +
55+
`This is typically caused by a shell script node wrapper in your PATH (e.g. injected by a ` +
56+
`package manager). Check that your PATH resolves "node" to a native binary, not a shell script.`
57+
);
58+
return { success: false } as NpmResult;
59+
}
60+
}
61+
2762
let result: NpmResult;
2863

2964
// Unclear whether `options.retries` should be interpreted as "X attempts" or "initial attempt + X retries"...

0 commit comments

Comments
 (0)