diff --git a/.changeset/yummy-hairs-enjoy.md b/.changeset/yummy-hairs-enjoy.md new file mode 100644 index 00000000..d75e2911 --- /dev/null +++ b/.changeset/yummy-hairs-enjoy.md @@ -0,0 +1,5 @@ +--- +"changesets-gitlab": minor +--- + +Add support for Trusted Publishers diff --git a/README.md b/README.md index 378a2dab..99faae71 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,12 @@ release: #### With Publishing -Before you can setup this action with publishing, you'll need to have an [npm token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) that can publish the packages in the repo you're setting up the action for and doesn't have 2FA on publish enabled ([2FA on auth can be enabled](https://docs.npmjs.com/about-two-factor-authentication)). You'll also need to [add it as a custom environment variable on your GitLab repo](https://docs.gitlab.com/ee/ci/variables/#custom-cicd-variables) with the name `NPM_TOKEN`. Once you've done that, you can create a file at `.gitlab-ci.yml` with the following content. +There are two ways to authenticate with npm when publishing: + +1. Trusted Publishers (recommended): Configure npm Trusted Publishers for your GitLab pipeline (see the npm docs: ). When the pipeline runs, npm will inject an `NPM_ID_TOKEN`, which this tool detects. In this mode you do NOT need to set `NPM_TOKEN`, and no `.npmrc` file is required—the npm CLI exchanges the identity token automatically. +2. Classic Automation Token: Create an [npm automation token](https://docs.npmjs.com/creating-and-viewing-authentication-tokens) (without 2FA on publish) and add it as a [custom environment variable in GitLab](https://docs.gitlab.com/ee/ci/variables/#custom-cicd-variables) named `NPM_TOKEN`. + +For any of the methods, create a file at `.gitlab-ci.yml` with the following content: ```yml stages: @@ -114,13 +119,14 @@ release: INPUT_PUBLISH: yarn release ``` -By default the GitLab CI cli creates a `.npmrc` file with the following content: +By default the GitLab CI cli creates a `.npmrc` file with the following content when `NPM_TOKEN` is present and no `.npmrc` exists (classic token mode): ```sh //registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN} ``` However, if a `.npmrc` file is found, the GitLab CI cli does not recreate the file. This is useful if you need to configure the `.npmrc` file on your own. +If `NPM_ID_TOKEN` is detected (Trusted Publishers), no `.npmrc` file is created or required. For example, you can add a step before running the Changesets GitLab CI cli: ```yml diff --git a/src/main.ts b/src/main.ts index 5eabf022..f0029248 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,7 +22,7 @@ export const main = async ({ published, onlyChangesets, }: MainCommandOptions = {}) => { - const { GITLAB_TOKEN, NPM_TOKEN } = env + const { GITLAB_TOKEN, NPM_TOKEN, NPM_ID_TOKEN } = env setOutput('published', false) setOutput('publishedPackages', []) @@ -69,15 +69,20 @@ export const main = async ({ const npmrcPath = `${env.HOME}/.npmrc` if (fs.existsSync(npmrcPath)) { console.log('Found existing .npmrc file') + } else if (NPM_ID_TOKEN) { + // Using npm Trusted Publishers: npm will exchange the OIDC token for a publish token internally. + console.log( + 'Detected `NPM_ID_TOKEN`; skipping `.npmrc` creation (Trusted Publishers mode).', + ) } else if (NPM_TOKEN) { - console.log('No .npmrc file found, creating one') + console.log('No .npmrc file found, creating one with `NPM_TOKEN`') await fs.promises.writeFile( npmrcPath, `//registry.npmjs.org/:_authToken=${NPM_TOKEN}`, ) } else { setFailed( - 'No `.npmrc` found nor `NPM_TOKEN` provided, unable to publish packages', + 'No `.npmrc` found and neither `NPM_TOKEN` nor `NPM_ID_TOKEN` provided, unable to publish packages', ) return } diff --git a/src/types.ts b/src/types.ts index 80fd1088..359b577a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -27,6 +27,9 @@ export type Env = GitLabCIPredefinedVariables & HOME: string NPM_TOKEN?: string + // Presence of NPM_ID_TOKEN indicates usage of npm Trusted Publishers; + // when set we don't require nor create an .npmrc with NPM_TOKEN. + NPM_ID_TOKEN?: string } type MergeRequestVariables =