diff --git a/README.md b/README.md index 715026de..aad5a82a 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,6 @@ If the runner is not able to access github.com, any Nodejs versions requested du - [Publishing to npmjs and GPR with npm](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-npm) - [Publishing to npmjs and GPR with yarn](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-yarn) - [Using private packages](docs/advanced-usage.md#use-private-packages) - - [Publishing to npm with Trusted Publisher (OIDC)](docs/advanced-usage.md#publishing-to-npm-with-trusted-publisher-oidc) - [Using private mirror](docs/advanced-usage.md#use-private-mirror) ## Recommended permissions diff --git a/__tests__/authutil.test.ts b/__tests__/authutil.test.ts index d884e23c..d5f6c195 100644 --- a/__tests__/authutil.test.ts +++ b/__tests__/authutil.test.ts @@ -118,27 +118,6 @@ describe('authutil tests', () => { expect(process.env.NODE_AUTH_TOKEN).toEqual('foobar'); }); - it('should not export NODE_AUTH_TOKEN if not set in environment', async () => { - const exportSpy = jest.spyOn(core, 'exportVariable'); - delete process.env.NODE_AUTH_TOKEN; - await auth.configAuthentication('https://registry.npmjs.org/'); - expect(fs.statSync(rcFile)).toBeDefined(); - const rc = readRcFile(rcFile); - expect(rc['registry']).toBe('https://registry.npmjs.org/'); - expect(exportSpy).not.toHaveBeenCalledWith( - 'NODE_AUTH_TOKEN', - expect.anything() - ); - }); - - it('should export NODE_AUTH_TOKEN if set to empty string', async () => { - const exportSpy = jest.spyOn(core, 'exportVariable'); - process.env.NODE_AUTH_TOKEN = ''; - await auth.configAuthentication('https://registry.npmjs.org/'); - expect(fs.statSync(rcFile)).toBeDefined(); - expect(exportSpy).toHaveBeenCalledWith('NODE_AUTH_TOKEN', ''); - }); - it('configAuthentication should overwrite non-scoped with non-scoped', async () => { fs.writeFileSync(rcFile, 'registry=NNN'); await auth.configAuthentication('https://registry.npmjs.org/'); diff --git a/dist/setup/index.js b/dist/setup/index.js index 8a86b779..90d70cfc 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78875,10 +78875,8 @@ function writeRegistryToFile(registryUrl, fileLocation) { newContents += `${authString}${os.EOL}${registryString}`; fs.writeFileSync(fileLocation, newContents); core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation); - // Only export NODE_AUTH_TOKEN if explicitly provided by user - if (Object.prototype.hasOwnProperty.call(process.env, 'NODE_AUTH_TOKEN')) { - core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN); - } + // Export empty node_auth_token if didn't exist so npm doesn't complain about not being able to find it + core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN || 'XXXXX-XXXXX-XXXXX-XXXXX'); } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 5f0edfb0..19f869f0 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -329,51 +329,36 @@ steps: - run: npm test ``` -**Restore-only cache** - -You can restore caches without saving new entries, which helps reduce cache writes and storage usage in read-only cache workflows. +**Restore-Only Cache** ```yaml -steps: -- uses: actions/checkout@v6 -# - uses: pnpm/action-setup@v6 -# with: -# version: 10 - -- name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: '24' - -- name: Normalize runner architecture - shell: bash - run: echo "ARCH=$(echo '${{ runner.arch }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - -- name: Output of cache path - id: cachepath - shell: bash - run: echo "path=$(npm config get cache)" >> $GITHUB_OUTPUT - # run: echo "path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT - # For yarn workflow, output of yarn cache dir (v1) or yarn config get cacheFolder (v2+) - # run: echo "path=$(yarn cache dir)" >> $GITHUB_OUTPUT - -- name: Restore Node cache - uses: actions/cache/restore@v5 - with: - path: ${{ steps.cachepath.outputs.path }} - key: node-cache-${{ runner.os }}-${{ env.ARCH }}-npm-${{ hashFiles('**/package-lock.json') }} - # key: node-cache-${{ runner.os }}-${{ env.ARCH }}-yarn-${{ hashFiles('**/yarn.lock') }} - # key: node-cache-${{ runner.os }}-${{ env.ARCH }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} - -- run: npm ci -# - run: yarn install --frozen-lockfile # optional, --immutable -# - run: pnpm install +## In some workflows, you may want to restore a cache without saving it. This can help reduce cache writes and storage usage in workflows that only need to read from cache +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + # Restore Node.js modules cache (restore-only) + - name: Restore Node modules cache + uses: actions/cache@v5 + id: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + # Setup Node.js + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: '24' + # Install dependencies + - run: npm install ``` -> **Note**: Uncomment the commands relevant to your project's package manager. -> For more details related to cache scenarios, please refer [actions/cache/restore](https://github.com/actions/cache/tree/main/restore#only-restore-cache). +> For more details related to cache scenarios, please refer [Node – npm](https://github.com/actions/cache/blob/main/examples.md#node---npm). -## Multiple operating systems and architectures +## Multiple Operating Systems and Architectures ```yaml jobs: @@ -490,45 +475,6 @@ To access private GitHub Packages within the same organization, go to "Manage Ac Please refer to the [Ensuring workflow access to your package - Configuring a package's access control and visibility](https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility#ensuring-workflow-access-to-your-package) for more details. -## Publishing to npm with Trusted Publisher (OIDC) - -npm supports Trusted Publishers, enabling packages to be published from GitHub Actions using OpenID Connect (OIDC) instead of long-lived npm tokens. This improves security by replacing static credentials with short-lived tokens, reducing the risk of credential leakage and simplifying authentication in CI/CD workflows. - -### Requirements - -Trusted publishing requires a compatible npm version: - -* **npm ≥ 11.5.1 (required)** -* **Node.js 24 or newer (recommended)** — includes a compatible npm version by default - -> If npm is below 11.5.1, publishing will fail even if OIDC permissions are correctly configured. - -You must also configure a **Trusted Publisher** in npm for your package/scope that matches your GitHub repository and workflow (and optional environment, if used). - -### Example workflow - -```yaml - permissions: - contents: read - id-token: write # Required for OIDC - - steps: - - uses: actions/checkout@v6 - - - uses: actions/setup-node@v6 - with: - node-version: '24' - registry-url: 'https://registry.npmjs.org' - - - run: npm ci - - run: npm run build --if-present - - run: npm publish -``` - -> **Note**: If the Trusted Publisher configuration (GitHub owner/repo/workflow file, and optional environment) does not match the workflow run identity exactly, publishing may fail with **E404 Not Found** even if the package exists on npm. - -For more details, see the [npm Trusted Publishers documentation](https://docs.npmjs.com/trusted-publishers) and the [GitHub Actions OpenID Connect (OIDC) overview](https://docs.github.com/en/actions/concepts/security/openid-connect). - ## Use private mirror It is possible to use a private mirror hosting Node.js binaries. This mirror must be a full mirror of the official Node.js distribution. diff --git a/src/authutil.ts b/src/authutil.ts index 37d8cfe1..e4b823bd 100644 --- a/src/authutil.ts +++ b/src/authutil.ts @@ -46,8 +46,9 @@ function writeRegistryToFile(registryUrl: string, fileLocation: string) { newContents += `${authString}${os.EOL}${registryString}`; fs.writeFileSync(fileLocation, newContents); core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation); - // Only export NODE_AUTH_TOKEN if explicitly provided by user - if (Object.prototype.hasOwnProperty.call(process.env, 'NODE_AUTH_TOKEN')) { - core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN); - } + // Export empty node_auth_token if didn't exist so npm doesn't complain about not being able to find it + core.exportVariable( + 'NODE_AUTH_TOKEN', + process.env.NODE_AUTH_TOKEN || 'XXXXX-XXXXX-XXXXX-XXXXX' + ); }