Skip to content

[rush] Add provideNpmrcCredentialsViaEnvironment experiment to restore ${VAR} credentials with PNPM >= 10.34.2 - #5995

Open
Ian Clanton-Thuon (iclanton) with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-npm-auth-token-issue
Open

[rush] Add provideNpmrcCredentialsViaEnvironment experiment to restore ${VAR} credentials with PNPM >= 10.34.2#5995
Ian Clanton-Thuon (iclanton) with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-npm-auth-token-issue

Conversation

Copilot AI commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

PNPM 10.34.2 added dropUntrustedEnvExpansions, which silently discards any auth setting or request destination containing a ${VAR} token when it comes from a project or workspace .npmrc. Rush generates common/temp/.npmrc, which PNPM loads as both layers, so the documented practice of supplying private-feed credentials via environment variables in CI now fails with a pair of WARN Ignored project-level auth setting ... messages per credential and unauthenticated requests.

Only the cli, env (npm_config_*), and user config sources are still trusted by PNPM, so this change has Rush do the expansion itself and hand credentials to PNPM through npm_config_* variables.

Behavior (PNPM only, when the experiment is enabled)

syncNpmrc classifies each line containing a ${VAR} token the same way PNPM does:

  • Credentials (_authToken, _auth, _password, username, tokenHelper, cert, key — bare or registry-scoped) are replaced with a ; PROVIDED VIA ENVIRONMENT: marker that retains the unexpanded original line, and the value is passed via npm_config_*. The secret never touches disk, and rotating a token doesn't change the file (so cleanInstallAfterNpmrcChanges doesn't spuriously trigger).
  • Request destinations (registry, @scope:registry, proxy, http-proxy, https-proxy, and //... key names) are expanded literally into the generated file — they aren't secret, and the generated file isn't committed.
  • Everything else is left untouched for PNPM to expand as before.

Given common/config/rush/.npmrc:

registry=https://pkgs.dev.azure.com/azdo-org/_packaging/package-feed/npm/registry
//pkgs.dev.azure.com/azdo-org/_packaging/package-feed/npm/registry/:_authToken=${NPM_AUTH_TOKEN}

the generated common/temp/.npmrc becomes:

registry=https://pkgs.dev.azure.com/azdo-org/_packaging/package-feed/npm/registry
; PROVIDED VIA ENVIRONMENT: //pkgs.dev.azure.com/azdo-org/_packaging/package-feed/npm/registry/:_authToken=${NPM_AUTH_TOKEN}

and PNPM is invoked with npm_config_//pkgs.dev.azure.com/.../npm/registry/:_authToken set to the resolved token.

Changes

  • npmrcUtilities.ts — sensitive-setting classification mirroring PNPM's own lists; consolidated ${VAR} expansion into _expandEnvironmentVariables(); new moveSensitiveSettingsToEnvironment option on syncNpmrc/trimNpmrcFileLines; new getNpmrcEnvironmentVariables() that reconstructs the npm_config_* map by re-reading the marker lines.
  • Experiment plumbingprovideNpmrcCredentialsViaEnvironment in IExperimentsJson, experiments.schema.json, the rush init template, and the API report. InstallHelpers.shouldProvideNpmrcCredentialsViaEnvironment() is the single read point and also gates on isPnpm.
  • Call sitesBaseInstallManager passes the new sync option; WorkspaceInstallManager and Autoinstaller inject the variables into the package manager's environment; rush-pnpm recomputes them from the already-generated file so it stays authenticated without re-syncing.

Notes for reviewers

  • Stateless by design. syncNpmrc runs during prepareAsync while the install happens later, and rush-pnpm never syncs at all. Recomputing from the marker comments rather than threading state through keeps all consumers uniform.
  • Name round-tripping. PNPM's envKeyToSetting() splits an npm_config_* name at its first colon and normalizes the remainder, so //registry.example.com:8080/:_authToken would be mangled into :-authtoken. Such settings fall back to literal expansion in the generated file rather than the environment.
  • Windows. Environment variable names are case-insensitive there; URI casing in an npm_config_//... name could in principle be altered. Unverified, and part of why this is behind an experiment.
  • SetupPackageRegistry, rush publish (.npmrc-publish), and install-run.js are unchanged — the option defaults to false.

Verified against pnpm 10.34.5 (warning gone, Authorization header sent) and pnpm 9.15.9 (unchanged behavior) using a local registry; the sandbox's network restrictions prevented a full rush install.

…npm 10.34.2+ ignoring ${VAR} in project .npmrc credentials

Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
Comment thread libraries/rush-lib/src/logic/installManager/InstallHelpers.ts Outdated
Comment thread libraries/rush-lib/src/utilities/npmrcUtilities.ts
@iclanton

Copy link
Copy Markdown
Member

Copilot - I've left comments.

…ronment

Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com>
@iclanton
Ian Clanton-Thuon (iclanton) marked this pull request as ready for review August 28, 2026 07:57
@iclanton Ian Clanton-Thuon (iclanton) changed the title Add provideNpmrcCredentialsViaEnvironment experiment to restore ${VAR} credentials with PNPM >= 10.34.2 [rush] Add provideNpmrcCredentialsViaEnvironment experiment to restore ${VAR} credentials with PNPM >= 10.34.2 Aug 28, 2026
@mojaza
Mo Jazayeri (mojaza) requested a balanced review from Copilot August 28, 2026 16:49
@mojaza

Copy link
Copy Markdown
Contributor

I asked Copilot to review your PR. I will take a look when it's done. I do recommend you to do the same on your other PRs as its reviews were mostly legit to me :)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in PNPM workaround that moves environment-expanded .npmrc credentials into npm_config_* variables.

Changes:

  • Classifies and transforms sensitive .npmrc settings.
  • Injects reconstructed credentials into PNPM invocation environments.
  • Adds experiment configuration, documentation, tests, and change metadata.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts Tests .npmrc transformations.
libraries/rush-lib/src/utilities/npmrcUtilities.ts Implements classification, expansion, markers, and reconstruction.
libraries/rush-lib/src/schemas/experiments.schema.json Defines the experiment schema.
libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts Supplies the subspace .npmrc location.
libraries/rush-lib/src/logic/installManager/InstallHelpers.ts Gates the experiment and prepares PNPM environments.
libraries/rush-lib/src/logic/base/BaseInstallManager.ts Enables credential extraction during .npmrc synchronization.
libraries/rush-lib/src/logic/Autoinstaller.ts Applies credentials to autoinstaller commands.
libraries/rush-lib/src/cli/RushPnpmCommandLineParser.ts Applies credentials to rush-pnpm.
libraries/rush-lib/src/api/ExperimentsConfiguration.ts Exposes the experiment API.
libraries/rush-lib/assets/rush-init/common/config/rush/experiments.json Documents the experiment in generated repositories.
common/reviews/api/rush-lib.api.md Updates the API report.
common/changes/@microsoft/rush/copilot-npmrc-credentials-experiment_2026-08-28-05-19.json Records the Rush minor change.
Suppressed comments (1)

libraries/rush-lib/src/logic/installManager/InstallHelpers.ts:423

  • This object is still process.env by reference because _mergeEnvironmentVariables() returns its baseEnv argument directly. The subsequent Object.assign therefore permanently installs each subspace's credentials into the Rush process; later subspace installs inherit stale registry credentials that are absent from their own .npmrc. Clone process.env before merging so credentials remain scoped to the child invocation.
    const packageManagerEnvironment: NodeJS.ProcessEnv = _mergeEnvironmentVariables(
      process.env,
      configurationEnvironment,
      options
    );

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 495 to 498
const packageManagerEnv: NodeJS.ProcessEnv = InstallHelpers.getPackageManagerEnvironment(
this.rushConfiguration,
this.options
{ ...this.options, npmrcFolder: subspace.getSubspaceTempFolderPath() }
);
rushConfiguration: RushConfiguration,
options: {
debug?: boolean;
npmrcFolder?: string;
Comment on lines +680 to +682
export function getNpmrcEnvironmentVariables(
options: IGetNpmrcEnvironmentVariablesOptions
): Record<string, string> | undefined {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Needs triage

Development

Successfully merging this pull request may close these issues.

4 participants