Opinionated, customizable GitHub Action to publish releases out of pushed v* tags.
This is a generalization that collects patterns from the workflows used in Quarkdown and other personal projects from my past decade in OSS.
Each feature is opt-in.
-
Tagged release: publishes the release with a composed body and artifacts.
-
Version-file bump: writes the tag into a file like
version.txtreadable by downstream tools, such as build scripts or--versionflags. -
Version replacement: swaps the previous release version for the new one in any file, such as installation snippets in a
README.md. -
Changelog update and extraction: extracts the
[Unreleased]section in a keep-a-changelog file, turns it into a dated release section, and uses it to compose the release body. -
Release notes extraction: extracts the notes for the current tag from the changelog.
-
Devbuild prerelease: moves a rolling tag to the current commit and publishes a prerelease with attached artifacts as a nightly build.
-
Release from a button: bumps the version, commits it, and tags that commit in one atomic push, starting a release without pushing a tag by hand.
-
Major-version tag alias: extracts and pushes the major from the tag (
v2.3.4→v2), ideal for pinning versions in GitHub Actions (dogfooded in this very action).
name: Release
on:
push:
tags: ['v*']
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: iamgio/simple-release@v1
with:
version-file: version.txt
changelog-file: CHANGELOG.md
commit: 'true'
release-files: |
build/dist/*.zip
build/dist/*.tar.gz|
Note
|
The pushed tag points at the commit before the version bump, so checking out the tag shows the previous version in version.txt and CHANGELOG.md. To have the tag carry the release version, start releases with push-tag instead (see below).
|
Run the workflow from the Actions tab, or with gh workflow run release.yml -f version=1.2.3.
It bumps the version and changelog on main, commits them, and tags that commit as v1.2.3.
The commit and the tag are pushed together: if the tag already exists, or the version is already released, nothing is pushed.
name: Release
on:
workflow_dispatch:
inputs:
version:
description: Version to release (e.g. 1.2.3)
required: true
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- uses: iamgio/simple-release@v1
with:
tag: ${{ inputs.version }}
committer-token: ${{ secrets.COMMITTER_TOKEN }}
version-file: version.txt
changelog-file: CHANGELOG.md
commit: 'true'
push-tag: 'true'
release: 'false'The tag push then triggers your tag-based workflow (such as the one above, without commit), which builds and publishes the release.
This requires committer-token to be a PAT: GitHub doesn’t trigger workflows from pushes made with github.token.
name: Devbuild
on:
push:
branches: [main]
permissions:
contents: write
jobs:
devbuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: iamgio/simple-release@v1
with:
devbuild-tag: latest
devbuild-files: build/dist/*.zipThe following lets a push of a v2.3.4 tag publish a release and force-move the v2 tag to the same commit:
name: Release
on:
push:
tags: ['v*.*.*']
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: iamgio/simple-release@v1
with:
release: 'true'
major-tag: 'true'| Input | Default | Description |
|---|---|---|
|
|
Tag for the release. |
|
|
Token used to create the releases. |
|
Checkout credentials |
Token for pushing commits and tags, taking precedence over the credentials persisted by |
| Input | Default | Description |
|---|---|---|
|
|
Path to a file to overwrite with the version. Leave empty to skip. Not for JSON manifests: the file is replaced wholesale, so use |
|
|
When |
|
|
When |
|
|
Newline- or comma-separated list of files (e.g. |
| Input | Default | Description |
|---|---|---|
|
|
Path to a keep-a-changelog file. Leave empty to skip. |
|
|
Turn |
|
|
Extract release notes for the tag from the changelog into the |
| Input | Default | Description |
|---|---|---|
|
|
If set and the file exists, its content is appended after the release notes in the release body. |
| Input | Default | Description |
|---|---|---|
|
|
When |
| Input | Default | Description |
|---|---|---|
|
|
Commit the modified |
|
|
|
|
|
Name used for the auto-commit author. |
|
|
Email used for the auto-commit author. |
|
|
Branch to push the commit to. |
|
|
When |
| Input | Default | Description |
|---|---|---|
|
|
Name of the rolling tag to force-move to this commit (e.g. |
|
|
Display name for the prerelease. |
|
|
Newline- or comma-separated glob patterns of files to attach. |
| Input | Default | Description |
|---|---|---|
|
|
|
|
Tag name |
Release display name. |
|
Composed |
Literal Markdown body. If empty, composed from release notes and the append file. |
|
|
Newline- or comma-separated glob patterns of files to attach. |
|
|
Whether the release should be created as a draft. |
|
|
Whether the release should be marked as a prerelease. Ignored when |
|
|
When |
|
|
Comma-separated, case-insensitive keywords matched as substrings against the tag’s pre-release segment. Only used when |
| Output | Description |
|---|---|
|
The computed version (tag with leading |
|
Whether the workflow ref is a tag. |
|
Extracted release notes for the tag, if the changelog step ran. |
|
URL of the created release, if any. |
|
URL of the created devbuild release, if any. |
The calling workflow needs contents: write for any step that pushes tags,
commits, or creates releases. Pushes authenticate with committer-token when
set, and with the credentials persisted by actions/checkout otherwise. Pass a
PAT as committer-token (or to actions/checkout) when pushing to a protected
branch, or when the pushed commit or tag should trigger other workflows.
This action wraps several great community actions: