Builds a Node.js package, publishes it and creates a GitHub release, when a version tag is pushed.
name: Release
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: write
id-token: write
jobs:
publish:
uses: MyCoRe-Org/github-workflows/.github/workflows/node-release.yml@v1
with:
node-version: '24'
secrets:
MATRIX_ACCESS_TOKEN: ${{ secrets.MATRIX_ACCESS_TOKEN }}The workflow must run on a tag, it fails on branches to avoid a release named after the branch.
The published version comes from the package metadata, e.g. package.json, not from the tag,
so set both to the same version before tagging.
| Input | Required | Default | Description |
|---|---|---|---|
node-version |
yes | Node.js version, e.g. '24' |
|
build-script |
no | build |
Package script to run before packing, empty to skip |
publish-command |
no | npm publish "$PACKAGE" --access public |
Publish command, $PACKAGE is the packed package, e.g. npx jsr@0.14.3 publish |
registry-url |
no | https://registry.npmjs.org |
Registry for publishing |
create-release |
no | true |
Create a GitHub release for the tag |
| Secret | Description |
|---|---|
MATRIX_ACCESS_TOKEN |
Optional, see notifications |
The release runs in three jobs, so the code of the dependencies never runs with the permission to publish:
- build, with read access only. A tag check stops runs on branches,
node-setupinstalls the dependencies as innode-build.yml,build-scriptruns withyarn runandyarn packpacks the package for the next job. - publish, with
id-token: writeandcontents: write. It installs nothing, runspublish-commandwith the packed package in$PACKAGE, and withcreate-releasecreates the GitHub release for the tag with the notes GitHub generates since the previousv*tag. - notify reports the result to Matrix, if configured: published, published without GitHub release, or failed.
In a single job, a compromised dependency could use id-token: write to publish a version of its own,
npm accepts every publish of the configured workflow. It can still change the package it is built into.
Both jobs run without cache (cache-mode: none), a node_modules restored from a poisoned cache entry would end up
in the package.
Publishing uses OIDC (trusted publishing), no npm token is passed. This needs:
id-token: writein the caller, as in the example.- The package configured for trusted publishing on npmjs.com, with this repository and the file name
of the calling workflow. npm checks the caller, not this reusable workflow, so renaming the caller requires
changing the trusted publisher as well. The template names the file
node-release.yml, projects that already publish withrelease.ymlcan keep that name. - npm 11.5.1 or newer for the default
publish-command, as Node.js 24 brings it. The package packed by Yarn is the oneyarn npm publishwould upload.
Publish to JSR, without build script. The command runs in a checkout of the tag, with id-token: write,
so it names a fixed version of jsr:
with:
node-version: '24'
build-script: ''
publish-command: npx jsr@0.14.3 publish