@yankeeinlondon/gha
v0.10.6
Published
Reusable Github workflow which publishes pnpm library code to npm
Readme
gha
Provides a test and publish configuration for Github Actions.
Usage
The most common usage is to add the following to your .github/workflows/main.yml file:
name: On Push
on:
push:
branches:
- main
jobs:
test:
name: testing
uses: yankeeinlondon/gha/.github/workflows/test.yml@main
publish:
name: publish
if: contains(github.event.head_commit.message, 'release v')
needs: test # This ensures tests pass before publishing
# A caller permissions block is the CEILING for every job in the reusable
# workflow, so grant the union all of them need:
# id-token: write -> Trusted Publishing (OIDC) for npm + JSR
# contents: write -> changelog commit / tag removal
# packages: write -> GitHub Packages publishing
permissions:
id-token: write
contents: write
packages: write
uses: yankeeinlondon/gha/.github/workflows/publish.yml@main
# with:
# jsr_scope: '@org' # Optional: scope for JSR if package.json has root-level name
# github_packages_scope: '@org' # Optional: scope for GitHub Packages if package.json has root-level name
# NPM_TOKEN is optional — omit it to publish to npm via Trusted Publishing (OIDC).
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}[!IMPORTANT] The
permissions: id-token: writeblock on the calling job is mandatory for Trusted Publishing. Because this is a reusable workflow, grantingid-token: writeonly insidepublish.ymlis not enough — GitHub caps a called workflow's token to what the caller provides, so the grant must live in yourmain.yml.
Customizing Test Configuration
The test workflow supports both single and matrix configurations for OS and Node versions.
Single OS and Node Version (Fast CI)
For faster CI runs, you can test on a single OS with a single Node version:
jobs:
test:
name: testing
uses: yankeeinlondon/gha/.github/workflows/test.yml@main
with:
os: 'ubuntu-latest'
nodeVersion: '20.x'Custom Matrix Configuration
You can customize which operating systems and Node versions to test:
jobs:
test:
name: testing
uses: yankeeinlondon/gha/.github/workflows/test.yml@main
with:
os: '["ubuntu-latest", "windows-latest"]' # Skip macOS
nodeVersion: '["18.x", "20.x", "22.x"]' # Test more Node versionsLinux-Only Testing
A common configuration is to test only on Linux with multiple Node versions:
jobs:
test:
name: testing
uses: yankeeinlondon/gha/.github/workflows/test.yml@main
with:
os: '["ubuntu-latest"]'
nodeVersion: '["20.x", "22.x"]'Note: The workflow automatically detects whether to use a single job or matrix strategy based on your inputs. When both os and nodeVersion are single values (not arrays), it runs a single test job for better performance.
Platforms
The possible targets for publishing are:
Workspaced versus Root Packages
- it's worth noting that both JSR and Github Packages require all packages to be published under a namespace
- in contrast, NPM has both "root level" packages and namespaced ones
Publishing Root-Level NPM Packages with Scoped JSR/GitHub Packages
If your package.json has a root-level name (e.g., my-package) but you want to publish to JSR and GitHub Packages with a scope, you can use the jsr_scope and github_packages_scope inputs:
jobs:
publish:
name: publish
if: contains(github.event.head_commit.message, 'release v')
permissions:
id-token: write
contents: write
packages: write
uses: yankeeinlondon/gha/.github/workflows/publish.yml@main
with:
jsr_scope: '@org' # Will publish as @org/my-package to JSR
github_packages_scope: '@org' # Will publish as @org/my-package to GitHub Packages
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}This allows you to:
- Publish to NPM as a root-level package:
my-package - Publish to JSR with a scope:
@org/my-package - Publish to GitHub Packages with a scope:
@org/my-package
How it works:
- For NPM: Always uses the name from package.json as-is
- For JSR: If package.json has a root-level name and
jsr_scopeis provided, updates jsr.json with the scoped name - For GitHub Packages: If package.json has a root-level name and
github_packages_scopeis provided, temporarily modifies package.json during publishing (then restores it)
Requirements by Platform
First Time Publication
Both NPM and JSR expect you to manually publish once before you're ready to publish via CI/CD:
- on NPM you'll run
npm publish --access=public|private- a browser window will pop up and ask you to login,
- assuming the user you've logged in as has the right permissions for the repo in question then you're first version will be published and you can shutdown the web page.
- on JSR you'll run
npx jsr publish- a browser will will open and you'll need to login and press the authorize button
On Github Packages you'll need to add a .npmrc.github file which looks like;
PACKAGE_NAME:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}CI/CD Publication
to publish to NPM you can use either of two auth methods:
- Trusted Publishing (OIDC) — recommended, tokenless:
- configure a Trusted Publisher on npmjs.com (Package Settings → Trusted Publishers)
- ⚠️ Register your caller workflow filename, NOT
publish.yml. Because this is a reusable workflow, npm validates the filename of the workflow that is triggered (the one withon: pushin your repo, e.g.main.yml) — not the reusable workflow that actually runsnpm publish. Registeringpublish.ymlproduces404 "OIDC token exchange error - package not found". Set: Organization/User = your GitHub owner, Repository = your repo, Workflow =main.yml(or whatever your entry workflow is named), Environment = blank. - grant
permissions: id-token: writeon the job that calls this workflow (see the usage example above) - do not set an
NPM_TOKENsecret — when it is absent the workflow publishes via OIDC (leave it unset entirely) - requires npm ≥ 11.5.1 (the workflow upgrades npm automatically)
- Token — legacy fallback:
- have an NPM token which has workflow permissions to publish, exposed as the
NPM_TOKENsecret
- have an NPM token which has workflow permissions to publish, exposed as the
- Trusted Publishing (OIDC) — recommended, tokenless:
to publish to JSR you must:
- have already have an account
- you must have added the workspaced package name
- you must have linked to package to a Github repo
- you must have a
deno.jsoncfile orjsr.jsonfile in the root of the repo
Workflows
Test Workflow
A rather simple flow that tests:
Unit Tests
If there is a
package.jsonfile and a "test" script then that will be used to kick off a matrix test across Windows, Linux, and macOS.Linting
If there is a
package.jsonfile and a "lint" script then that will be used to kick off linting on Linux.Coverage Tests
If there is a
package.jsonfile and a "coverage" script then that will be run to kick off a coverage test.
If none of the above tests are available then this workflow will CANCEL the workflow and provide context on why it was cancelled.
Publish Workflow
- publishing is only run when a release's commit message starts with
release v - job:
detect_platforms- once it has been determined that the current commit should be published we enter an evaluation stage:
- the goal of this phase is to be sure that we have the ability to publish to platforms before we try
- there is a concept of "skip files":
if the file
.npm-skipis found at the root of the repo then we know that there is no intention of publishing tonpmsimilarly we look for a
.jsr-skipfile for the intention to publish tojsr.with Github Packages we first check that there is a
.npmrc.githubfile; this is necessary for a publishing to Github Packages and will looks something like:PACKAGE_NAME:registry=https://npm.pkg.github.com/ //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}- With that package found in the root of the repo this flow will try to publish to Github Packages unless it also files the
.skip-github-packagesfile
- With that package found in the root of the repo this flow will try to publish to Github Packages unless it also files the
each enabled platform is calculated and the user is presented with a notice about which platforms we expect to publish to.
Optional Configuration
Bun- if you want the bun to install and build the project set thebuninput variable totruein thewithgrouping.- Note: regardless of whether you set this flag, Bun will be made available in the test environment so if you need to run some Typescript scripts directly this should be available.
