← Back to Blog

A tag is a release trigger

·7 min read

The entire release pipeline (publish to PyPI, publish to npm, create the GitHub Release) is triggered by nothing more than a git tag matching a pattern. The tag is the release. Everything downstream is ceremony.

The trigger

on:
  push:
    tags:
      - "v*.*.*"
      - "v*.*.*-*"

Two glob patterns cover stable and prerelease tags: v0.12.0 and v0.12.0-beta.1. A push of either fires the whole workflow. No manual "run release" button, no secret ritual, a tag push is the whole ceremony.

The tag encodes release metadata

The workflow never needs an input parameter for "is this a prerelease?", it derives it from the tag name:

prerelease: ${{ contains(github.ref_name, '-') }}
make_latest: ${{ !contains(github.ref_name, '-') }}

A hyphen in the tag (v0.12.0-beta.1) marks the GitHub Release as a prerelease and excludes it from latest. A clean tag (v0.12.0) is a full release and becomes the latest. The versioning scheme and the workflow metadata are the same string.

npm's prerelease dist-tags mirror the same rule

The npm publish steps do the identical dance with npm's dist-tag system:

if [[ "$VERSION" == *"-"* ]]; then
  TAG=$(echo "$VERSION" | awk -F'-' '{print $2}' | awk -F'.' '{print $1}')
  npm publish --access public --tag "$TAG"
else
  npm publish --access public
fi

A prerelease version (0.12.0-beta.1) extracts the first hyphen segment (beta) and publishes under that dist-tag (so npm i @pyrpc/react@beta works without ever disturbing the latest tag. Stable versions publish plain, becoming latest. The prerelease signal travels from git tag, to GitHub Release, to npm dist-tag) one source of truth, three registries honoring it.

Why tag-based triggers win here

A tag is a git-native, immutable, auditable object. It names a specific commit, so a release is provably built from a known tree. It is also push-friendly: the release flow becomes git tag v0.12.0 && git push origin v0.12.0, two commands a human can reason about. The tag is the single point of entry for the entire pipeline, which keeps the "how do I release?" question answerable in one sentence.

The risk and the guard

The danger of tag-triggered publishing is a misfired tag. The workflow's countermeasure is the release PR flow upstream: the version bump lands on main first, CI runs, and the tag is created only after review. The tag is the last step of a reviewed process, not a footgun anyone can trip accidentally. That ordering (review, merge, then tag) is what makes a single-command trigger safe.