shared GitHub Actions workflows for codenautas libraries
This repository holds the GitHub Actions workflows shared by almost every codenautas library. Instead of repeating the same steps in each repository, each one has a minimal file that delegates to the shared workflow.
This way a change of criteria (a new Node version, an added control step) is made once here and every repository picks it up.
.github/workflows/— the shared workflows, the ones called withuses:..in-each-repo/— the templates to copy into each consuming repository. They do not run here: they live outside.github/workflows/precisely so GitHub does not pick them up as its own.
Repositories point at a moving tag. The current one is @v2.
v1is frozen. Repositories not yet migrated still callnpm-publish-from-tag.yaml@v1, which was renamed inv2.v2is the current one.advance-tag.batmoves it to the current commit.- A breaking change means freezing
v2and moving on tov3.
$ advance-tag.batBuilds and runs the tests on a matrix of Node versions (22, 24 and 26). On the version
marked as coverage_version (24) it runs npm run test-ci instead of npm test and
sends the result to Coveralls.
It needs the repository to have working npm ci and npm test. The build step uses
--if-present, so it is optional.
Runs qa-control over the project and then npm audit --omit=dev. If the repository
has bin/qa-control-run.js it uses that local copy; otherwise it goes through npx.
That branch exists so the qa-control repository itself can check itself with its
working version.
Bumps the package.json version, commits it on a new branch (version/vX.Y.Z) and
opens the pull request against the branch it was triggered from.
It does not create the tag. The tag is created by the publishing workflow, once the version change is already merged. That way the tag always points at the commit that actually gets published, and not at one left behind on a branch.
Before committing it checks that the tag for that version does not exist yet: if it already exists it fails, because that means the version was already published.
Inputs:
bump(required) —major,minor,patch,premajor,preminor,prepatch,prerelease, or a specific version like1.2.3.preid— the identifier for thepre*ones. Defaults tobeta.node_version— defaults to24.
Publishes to npm. It has two modes, depending on how the repository calls it.
By tag (create-tag: false, the default). This is the usual behavior: it is
triggered by pushing a tag, and before publishing it checks that the tag matches the
package.json version. If they do not match, it fails.
Manual (create-tag: true). There is no previous tag: it takes the version from
package.json, checks that such tag does not exist yet and creates it. If the tag
already exists it fails — that version was already published.
In both cases the publishing step is the same: npm ci, build, tests and
npm publish --provenance. The npm dist-tag comes from the version suffix: those
containing beta, alpha or rc are published under that tag instead of latest.
Inputs:
create-tag— defaults tofalse.node_version— defaults to24.skip-tests-until-date— skips the tests until a given date (YYYY-MM-DD). It is an escape hatch for when something has to be published with broken tests for a known reason and with an expiration date.
They are still here because some repositories have not migrated yet. Both emit a deprecation warning when they run.
node-test-coverage.yaml— used to run the tests with coverage on a single Node version. Replaced bynode-build-and-test.yaml, which does the same inside the matrix.npm-publish.yaml— the old publish. Replaced bynpm-publish-new-version.yaml, which also adds--provenance, the automatic dist-tag and the manual mode.
There are two paths. Both end up in the same place.
- Run
Create new versionfrom the Actions tab, choosing the version type. It leaves a pull request open with the updatedpackage.json. - Merge that pull request with the GitHub button.
- Run
Publish (manual)from Actions. It creates the tag from thepackage.jsonversion and publishes.
$ npm version patch
$ git push
$ git push --tagsPushing the tag triggers publish.yml, which checks that the tag matches
package.json and publishes. This is the usual path and keeps working the same.
These files go in each library's .github/workflows/, and they are ready to copy from
.in-each-repo/. The idea is to keep them as small as possible: everything that may
change over time lives here, not there.
name: Build and test
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
ci:
uses: codenautas/.github/.github/workflows/node-build-and-test.yaml@v2name: QA control
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
qa:
uses: codenautas/.github/.github/workflows/qa-control.yaml@v2The usual one: triggered by pushing a tag.
name: Publish from tag
on:
push:
tags:
- 'v[0-9]+.[0-9]*'
permissions:
id-token: write
contents: read
jobs:
publish:
uses: codenautas/.github/.github/workflows/npm-publish-new-version.yaml@v2
with:
node_version: '24'The same shared workflow, but triggered by hand and creating the tag. It needs
contents: write precisely to be able to create it.
name: Publish (manual)
on:
workflow_dispatch:
permissions:
id-token: write
contents: write
jobs:
publish:
uses: codenautas/.github/.github/workflows/npm-publish-new-version.yaml@v2
with:
node_version: '24'
create-tag: trueThe choice on bump is what makes GitHub show a dropdown instead of a free text
field when running it from the web.
name: Create new version
on:
workflow_dispatch:
inputs:
bump:
description: 'Tipo de versión'
type: choice
required: true
default: patch
options: [patch, minor, major, prerelease, premajor, preminor, prepatch]
preid:
description: 'Identificador para los pre* (beta, alpha, rc)'
type: string
required: false
default: beta
permissions:
contents: write
pull-requests: write
jobs:
create-new-version:
uses: codenautas/.github/.github/workflows/create-new-version.yaml@v2
with:
bump: ${{ inputs.bump }}
preid: ${{ inputs.preid }}
node_version: '24'npm version normally creates the commit and the tag. Here --no-git-tag-version
is used so that it only makes the commit.
The reason: the tag created by npm version would point at the commit on the version
branch, which is not merged yet. After the merge that commit may not be the tip of the
main branch, and the tag would end up pointing at code different from what is
published. By creating the tag only at publishing time, it always points at what
actually gets published.
A push made from Actions with the GITHUB_TOKEN does not trigger other workflows. It
is a GitHub protection against infinite loops. That is why the tag created by the
publishing workflow could not, in turn, trigger the publication: it has to be run by
hand.
This could be avoided with a personal token, but that would mean keeping one more secret in every repository.
The commits and tags created by these workflows are attributed to whoever triggered
them, using github.actor and the GitHub noreply address. The real email is not used
because GitHub does not expose it in the Actions context.

