aspect-build / aspect-build/rules_js
[Bug]: npm_translate_lock doesn't use credential helper / tokenHelper for update_pnpm_lock
- Dominant language
- Starlark
- Stars
- 378
- Forks
- 183
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 32
Description
### What happened?
### Problem
When `update_pnpm_lock = True`, running `pnpm install --lockfile-only` fails to authenticate with private registries that use `tokenHelper` in .npmrc, even though package downloads work fine.
### Current Behavior
`npm_translate_lock` handles authentication differently for package downloads vs lock file updates:
1. Package downloads (`npm_import`): aspect_rules_js parses .npmrc, executes tokenHelper, and passes credentials to Bazel's downloader. This works correctly.
2. Lock file updates (`_update_pnpm_lock`): aspect_rules_js runs pnpm install --lockfile-only directly without any .npmrc processing or tokenHelper execution. pnpm then fails with 401 Unauthorized.
Additionally, pnpm itself disallows tokenHelper in project-level .npmrc for security reasons, so even if the file is available, pnpm won't use it:
```
[ERROR] tokenHelper must not be configured in project-level .npmrc
```
### Expected Behavior
`npm_translate_lock` should handle authentication for `pnpm install --lockfile-only` the same way it does for package downloads - by parsing .npmrc, executing tokenHelper if configured, and making credentials available to pnpm (e.g., by writing a temporary .npmrc with _authToken populated).
### Workaround
We're using a preupdate script that:
1. Invokes our Bazel credential helper to get the auth token
2. Replaces the symlinked .npmrc with a regular file containing the original content plus _authToken
```javascript
// Invoke credential-helper, parse response, inject into .npmrc
const output = execSync(`${credentialHelper} get`, { input: JSON.stringify({ uri: REGISTRY_URI }), ... });
const { headers } = JSON.parse(output);
const token = headers.Authorization[0].replace("Bearer ", "");
// Replace symlink with regular file to avoid modifying source tree
if (lstatSync(".npmrc").isSymbolicLink()) {
unlinkSync(".npmrc");
}
writeFileSync(".npmrc", `${originalNpmrc}\n${REGISTRY_URL}:_authToken=${token}\n`);
```
### Suggested Solution
In _update_pnpm_lock, before executing pnpm:
1. Parse the .npmrc (already done elsewhere in aspect_rules_js)
2. If tokenHelper is configured for any registry, execute it and capture the token
3. Write a modified .npmrc in the external repository with _authToken populated
4. Then run pnpm install --lockfile-only
This would make update_pnpm_lock work seamlessly with private registries that use tokenHelper, matching the behavior of package downloads.
### Version
Development (host) and target OS/architectures:
Output of `bazel --version`: 9.2.0
Version of the Aspect rules, or other relevant rules from your
`WORKSPACE` or `MODULE.bazel` file:
```
bazel_dep(name = "aspect_rules_js", version = "3.4.1")
```
Language(s) and/or frameworks involved:
JavaScript
### How to reproduce
```shell
```
### Any other information?
_No response_
Contributor guide
Research direction
Start at the _update_pnpm_lock entry point and compare its authentication flow with the existing .npmrc parsing used for package downloads. Reproduce the pnpm install --lockfile-only failure against a private registry using tokenHelper, then verify that the update succeeds with credentials available through a temporary .npmrc without modifying the source tree.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100