Skip to content

Commit e2c6c61

Browse files
authored
Handle npm tokens with environment variables (#1211)
1 parent 1253276 commit e2c6c61

43 files changed

Lines changed: 433 additions & 134 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pr.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ permissions: {}
2121
jobs:
2222
build:
2323
strategy:
24+
# Sometimes it can be helpful to see if a failure is specific to a particular environment
25+
fail-fast: false
2426
matrix:
2527
include:
2628
- os: ubuntu-latest
@@ -72,8 +74,31 @@ jobs:
7274
if: matrix.os == 'ubuntu-latest' && matrix.node == 14 && matrix.npm == 8
7375

7476
- run: yarn lint:ci
77+
if: matrix.os == 'ubuntu-latest' && matrix.node == 14 && matrix.npm == 8
7578

7679
- run: yarn test --verbose
80+
id: test
81+
82+
# See packageManager.ts comments...
83+
- name: test publish (Windows bash)
84+
if: matrix.os == 'windows-latest'
85+
shell: bash
86+
run: yarn workspace beachball test packagePublish
87+
88+
# For a few specific test failures, it can be helpful to see npm debug logs
89+
# (usually not relevant, but there's not a good way to distinguish by specific failure)
90+
- name: (on test failure) Get npm cache path
91+
if: failure() && steps.test.outcome == 'failure'
92+
shell: bash
93+
run: echo "NPM_CACHE_DIR=$(npm config get cache)" >> "$GITHUB_ENV"
94+
95+
- name: (on test failure) Upload npm logs as artifact
96+
if: failure() && steps.test.outcome == 'failure'
97+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
98+
with:
99+
name: npm-logs-${{ matrix.os }}-node${{ matrix.node }}-npm${{ matrix.npm }}
100+
path: ${{ env.NPM_CACHE_DIR }}/_logs
101+
retention-days: 3
77102

78103
# The docs have a separate installation using Node 22 due to needing newer dependencies
79104
docs:

.vscode/launch.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
"type": "node",
99
"request": "launch",
1010
"name": "Debug current open test",
11-
"runtimeExecutable": "npm",
12-
"cwd": "${workspaceFolder}",
13-
"runtimeArgs": ["run-script", "test"],
11+
"runtimeExecutable": null,
12+
"program": "${workspaceRoot}/scripts/debugTests.js",
13+
"cwd": "${fileDirname}",
1414
"args": ["--", "--runInBand", "--watch", "--testTimeout=1000000", "${file}"],
1515
"sourceMaps": true,
1616
"outputCapture": "std",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"type": "minor",
5+
"comment": "Add support for providing npm token via NPM_TOKEN environment variable, and internally pass token to npm using an environment variable",
6+
"packageName": "beachball",
7+
"email": "elcraig@microsoft.com",
8+
"dependentChangeType": "patch"
9+
}
10+
]
11+
}

docs/cli/publish.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Publishing automates all the bumping and synchronizing of package versions in th
1515
<!-- prettier-ignore -->
1616
| Option | Alias | Default | Description |
1717
| ------ | ----- | ------- | ----------- |
18-
| `--auth-type` | `-a` | `'authtoken'` | npm auth type: `'authtoken'` or `'password'` |
18+
| `--auth-type` | `-a` | `'authtoken'` | npm auth type for `NPM_TOKEN` or `--token`: `'authtoken'` or `'password'` |
1919
| `--git-tags`, `--no-git-tags` | | `true` (`--git-tags`) | whether to create git tags for published package versions |
2020
| `--keep-change-files` | | | don't delete the change files from disk after bumping |
2121
| `--message` | `-m` | `'applying package updates'` | custom commit message |
@@ -25,10 +25,23 @@ Publishing automates all the bumping and synchronizing of package versions in th
2525
| `--registry` | `-r` | `'https://registry.npmjs.org'` | npm registry for publishing |
2626
| `--retries` | | `3` | number of retries for a package publish before failing |
2727
| `--tag` | `-t` | `'latest'` | dist-tag for npm publishes |
28-
| `--token` | `-n` | | credential to use with npm commands (type is specified by `--auth-type`) - locally, use `npm login` instead, or see [alternatives for CI](../concepts/ci-integration) |
28+
| `--token` | `-n` | | Not recommended; see alternatives below |
2929
| `--verbose` | | `false` | prints additional information to the console |
3030
| `--yes` | `-y` | if CI detected, `true` | skips the prompts for publish |
3131

32+
#### Providing a token
33+
34+
There are a few different ways to handle npm authentication for `beachball publish`.
35+
36+
In CI, you should use [trusted publishing](https://docs.npmjs.com/trusted-publishers) if supported to remove the need for tokens. Unfortunately this isn't available in Azure DevOps.
37+
38+
If trusted publishing is unavailable or you're running `beachball` locally, you can do any of the following:
39+
40+
- Set the `NPM_TOKEN` environment variable (`beachball` passes this through to `npm`)
41+
- Run `npm login` first (or a task which does the same)
42+
- Manually set the token in [`.npmrc`](https://docs.npmjs.com/cli/v11/configuring-npm/npmrc#auth-related-configuration), possibly referencing an environment variable
43+
- Old way: use `--token <token>` on the command line (not recommended)
44+
3245
### Algorithm
3346

3447
The `publish` command is designed to run steps in an order that minimizes the chances of mid-publish failure by doing validation upfront.

docs/concepts/ci-integration.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ There are a couple of options here:
5757

5858
## Setting options for publishing
5959

60+
### Providing an npm token
61+
62+
As mentioned above, if possible you should use [trusted publishing](https://docs.npmjs.com/trusted-publishers) to remove the need for tokens.
63+
64+
Other options:
65+
66+
- Set the `NPM_TOKEN` environment variable while running `beachball`
67+
- Run `npm login` first (or a task which does the same)
68+
- Manually set the token in [`.npmrc`](https://docs.npmjs.com/cli/v11/configuring-npm/npmrc#auth-related-configuration), possibly referencing an environment variable
69+
- Old way: use `--token <token>` on the command line (not recommended)
70+
71+
### Other options
72+
6073
If you're passing any custom options besides the npm token to `beachball publish`, it's recommended to set them in either the `beachball` config (if they don't interfere with other commands), or a `package.json` script (if specific to `publish`).
6174

6275
For example, the following script could be used for publishing public scoped packages (`access` is also safe to set in the beachball config):
@@ -152,10 +165,6 @@ This sample assumes the following:
152165
- `REPO_PAT`: A GitHub fine-grained personal access token with write access ([as described above](#github-token))
153166
- `NPM_TOKEN`: An npm token with write access to the package(s) and/or scope(s), such as a [fine-grained token for public npm](#npm-token)
154167
- A repo root `package.json` script `release` which runs `beachball publish`
155-
- A `.npmrc` file with the following content (change the registry if needed):
156-
```txt
157-
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
158-
```
159168

160169
```yml
161170
# Add trigger configuration of your choice (this one is manual only)
@@ -184,7 +193,7 @@ steps:
184193
185194
- script: npm run release
186195
name: Publish
187-
# This works because .npmrc references NPM_TOKEN
196+
# Beachball will use this environment variable
188197
env:
189198
NPM_TOKEN: $(NPM_TOKEN)
190199
```

packages/beachball/src/__e2e__/bump.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe('bump command', () => {
3838
const parsedOptions = getParsedOptions({
3939
cwd: cwd || repo?.rootPath || '',
4040
argv: [],
41+
env: {},
4142
testRepoOptions: { branch: defaultRemoteBranchName, fetch: false, ...repoOptions },
4243
});
4344
return { options: parsedOptions.options, parsedOptions };

packages/beachball/src/__e2e__/change.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ describe('change command', () => {
8484
const parsedOptions = getParsedOptions({
8585
cwd: repo!.rootPath,
8686
argv: ['node', 'beachball', 'change', ...(extraArgv ?? [])],
87+
env: {},
8788
testRepoOptions: {
8889
branch: defaultRemoteBranchName,
8990
...repoOptions,

packages/beachball/src/__e2e__/getChangedPackages.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe('getChangedPackages (basic)', () => {
3131
const parsedOptions = getParsedOptions({
3232
cwd: reusedRepo.rootPath,
3333
argv: [],
34+
env: {},
3435
testRepoOptions: {
3536
fetch: false,
3637
branch: defaultRemoteBranchName,
@@ -127,6 +128,7 @@ describe('getChangedPackages', () => {
127128
const parsedOptions = getParsedOptions({
128129
cwd,
129130
argv: ['node', 'beachball', 'change', ...extraArgv],
131+
env: {},
130132
testRepoOptions: {
131133
branch: defaultRemoteBranchName,
132134
fetch: false,

packages/beachball/src/__e2e__/publishE2E.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ describe('publish command (e2e)', () => {
4343
const parsedOptions = getParsedOptions({
4444
cwd: repo!.rootPath,
4545
argv: ['node', 'beachball', 'publish', '--yes', ...(extraArgv || [])],
46+
env: {},
4647
testRepoOptions: {
4748
branch: defaultRemoteBranchName,
4849
registry: 'fake',

packages/beachball/src/__e2e__/publishGit.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('publish command (git)', () => {
2828
const parsedOptions = getParsedOptions({
2929
cwd,
3030
argv: ['node', 'beachball', 'publish', '--yes'],
31+
env: {},
3132
testRepoOptions: {
3233
branch: defaultRemoteBranchName,
3334
registry: 'http://localhost:99999/',

0 commit comments

Comments
 (0)