Skip to content

Commit 5962636

Browse files
authored
ci: check yarnrc if it exists (#2608)
## Summary: Our `prepublish-check` assumed we used a `.npmrc` file to publish. However, we're now using Yarn V4 which uses its' `.yarnrc.yml` to set your registry and auth token. Update the check to handle both.
1 parent 2e39616 commit 5962636

File tree

1 file changed

+77
-27
lines changed

1 file changed

+77
-27
lines changed

.ado/scripts/prepublish-check.mjs

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -88,44 +88,94 @@ function loadNxConfig(configFile) {
8888
return JSON.parse(nx);
8989
}
9090

91+
/**
92+
* Detects whether to use npm or yarn to publish based on .npmrc existence
93+
* @returns {boolean} true if npm should be used, false if yarn should be used
94+
* @throws {Error} if neither .npmrc nor .yarnrc.yml exists
95+
*/
96+
function shouldUseNpm() {
97+
const hasNpmrc = fs.existsSync('.npmrc');
98+
const hasYarnrc = fs.existsSync('.yarnrc.yml');
99+
100+
if (!hasNpmrc && !hasYarnrc) {
101+
error('No package manager configuration found. Expected either .npmrc or .yarnrc.yml file.');
102+
throw new Error('No package manager configuration found');
103+
}
104+
105+
if (hasNpmrc && hasYarnrc) {
106+
// If both exist, prefer npm (could be changed based on project preference)
107+
info('Both .npmrc and .yarnrc.yml found, using npm configuration');
108+
return true;
109+
}
110+
111+
return hasNpmrc;
112+
}
113+
91114
function verifyNpmAuth(registry = NPM_DEFEAULT_REGISTRY) {
92-
const npmErrorRegex = /npm error code (\w+)/;
115+
const useNpm = shouldUseNpm();
93116
const spawnOptions = {
94117
stdio: /** @type {const} */ ("pipe"),
95118
shell: true,
96119
windowsVerbatimArguments: true,
97120
};
98121

99-
const whoamiArgs = ["whoami", "--registry", registry];
100-
const whoami = spawnSync("npm", whoamiArgs, spawnOptions);
101-
if (whoami.status !== 0) {
102-
const error = whoami.stderr.toString();
103-
const m = error.match(npmErrorRegex);
104-
const errorCode = m && m[1];
105-
switch (errorCode) {
106-
case "EINVALIDNPMTOKEN":
107-
throw new Error(`Invalid auth token for npm registry: ${registry}`);
108-
case "ENEEDAUTH":
109-
throw new Error(`Missing auth token for npm registry: ${registry}`);
110-
default:
111-
throw new Error(error);
122+
if (useNpm) {
123+
info("Using npm for authentication (found .npmrc)");
124+
const npmErrorRegex = /npm error code (\w+)/;
125+
126+
const whoamiArgs = ["whoami", "--registry", registry];
127+
const whoami = spawnSync("npm", whoamiArgs, spawnOptions);
128+
if (whoami.status !== 0) {
129+
const error = whoami.stderr.toString();
130+
const m = error.match(npmErrorRegex);
131+
const errorCode = m && m[1];
132+
switch (errorCode) {
133+
case "EINVALIDNPMTOKEN":
134+
throw new Error(`Invalid auth token for npm registry: ${registry}`);
135+
case "ENEEDAUTH":
136+
throw new Error(`Missing auth token for npm registry: ${registry}`);
137+
default:
138+
throw new Error(error);
139+
}
112140
}
113-
}
114141

115-
const tokenArgs = ["token", "list", "--registry", registry];
116-
const token = spawnSync("npm", tokenArgs, spawnOptions);
117-
if (token.status !== 0) {
118-
const error = token.stderr.toString();
119-
const m = error.match(npmErrorRegex);
120-
const errorCode = m && m[1];
142+
const tokenArgs = ["token", "list", "--registry", registry];
143+
const token = spawnSync("npm", tokenArgs, spawnOptions);
144+
if (token.status !== 0) {
145+
const error = token.stderr.toString();
146+
const m = error.match(npmErrorRegex);
147+
const errorCode = m && m[1];
148+
149+
// E403 means the token doesn't have permission to list tokens, but that's
150+
// not required for publishing. Only fail for other error codes.
151+
if (errorCode === "E403") {
152+
info(`Token verification skipped: token doesn't have permission to list tokens (${errorCode})`);
153+
} else {
154+
throw new Error(m ? `Auth token for '${registry}' returned error code ${errorCode}` : error);
155+
}
156+
}
157+
} else {
158+
info("Using yarn for authentication (no .npmrc found)");
121159

122-
// E403 means the token doesn't have permission to list tokens, but that's
123-
// not required for publishing. Only fail for other error codes.
124-
if (errorCode === "E403") {
125-
info(`Token verification skipped: token doesn't have permission to list tokens (${errorCode})`);
126-
} else {
127-
throw new Error(m ? `Auth token for '${registry}' returned error code ${errorCode}` : error);
160+
const whoamiArgs = ["npm", "whoami", "--publish"];
161+
const whoami = spawnSync("yarn", whoamiArgs, spawnOptions);
162+
if (whoami.status !== 0) {
163+
const stderr = whoami.stderr.toString().trim();
164+
const stdout = whoami.stdout.toString().trim();
165+
const errorOutput = stderr || stdout || 'No error message available';
166+
167+
// Yarn uses different error format
168+
if (errorOutput.includes("Invalid authentication") || errorOutput.includes("Failed with errors")) {
169+
throw new Error(`Invalid or missing auth token for registry: ${registry}`);
170+
}
171+
172+
// Provide more context about the yarn authentication failure
173+
throw new Error(`Yarn authentication failed (exit code ${whoami.status}): ${errorOutput}`);
128174
}
175+
176+
// Skip token listing for yarn since it doesn't support npm token commands
177+
// The whoami check above is sufficient to verify authentication
178+
info("Skipping token list check when using yarn (not required for publishing)");
129179
}
130180
}
131181

0 commit comments

Comments
 (0)