github-release is a reusable release branch, tag, and GitHub Release orchestrator for projects that need predictable release automation without writing large workflow files.
This repository is a public distribution repository. The source code is maintained in the private verzly/toolchain monorepo and this repository contains only the public surface that users need: README.md, action.yml, LICENSE, and GitHub Release assets.
The public repository intentionally does not contain src/, Cargo.toml, build workflows, or release configuration. That separation keeps the user-facing repository small while allowing all tools to share the same release infrastructure in verzly/toolchain.
GitHub releases usually involve the same fragile sequence in every project: create a release branch, update version files, build assets, merge the branch, tag the final commit, generate release notes, upload assets, and clean up temporary branches. When that logic lives directly in YAML, each repository slowly develops its own edge cases.
github-release moves that lifecycle into a typed executable. The workflow stays short, while the dangerous parts such as branch deletion, tag naming, version file updates, and release publishing are handled by one maintained tool.
It was created for the Verzly toolchain model where source code can live in verzly/toolchain, while public distribution repositories receive only release assets and public documentation.
The tool has two release modes.
For a normal source repository, prepare creates a temporary release branch and updates configured version files. After the project-specific build succeeds, finalize merges the release branch into the target branch, creates the tag, optionally creates the GitHub Release, and removes the temporary branch. If the build fails, abort deletes the temporary release branch.
For a distribution repository, publish can create a GitHub Release directly from an already-prepared source tag. This is useful when the source repository and public release repository are different repositories. In that model, release notes can still point to the source repository, so pull request references stay connected to the real code review history.
Use github-release when you want to:
- keep release workflow YAML short and readable;
- standardize release branch names, tag names, release names, and cleanup behavior across repositories;
- update version files before the build runs;
- publish release assets after a successful build;
- generate public release notes from a source repository tag;
- release several public distribution repositories from one private or internal source monorepo.
Run the executable directly from a workflow:
- uses: verzly/github-release@v1
with:
args: plan --version 1.2.3 --config crates/my-tool/source-github-release.tomlInstall it once and call it from later steps:
- uses: verzly/github-release@v1
with:
install-only: "true"
- run: github-release prepare --version 1.2.3 --config crates/my-tool/source-github-release.tomlThe composite action detects the runner operating system and CPU architecture, maps that host to a Rust-style target name, downloads the matching executable from this repository's GitHub Releases with gh release download, verifies a .sha256 file when one is present, copies the executable into a temporary bin directory, and adds that directory to PATH.
The action does not build from source. It does not clone verzly/toolchain. It only consumes the release assets published here.
| Input | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
github-token |
No | "" |
Any GitHub token readable by gh; empty uses ${{ github.token }} |
Used only to download release assets. Public repositories normally work with the default token. Pass a custom token when downloading from a private fork or restricted environment. |
version |
No | "" |
Empty, 1.2.3, v1.2.3, or any published release tag |
Selects the release asset to download. Empty means latest release. If the value does not start with v, the action prefixes it with v. |
install-only |
No | "false" |
String "true" or "false" |
When "true", the action only installs the executable and adds it to PATH. When "false", it installs and immediately runs the executable with args. |
args |
No | --help |
Any valid CLI argument string for the executable | Passed to the installed executable when install-only is not "true". Quote values carefully because this string is evaluated by the shell. |
working-directory |
No | . |
Relative or absolute path | Directory where the executable runs when install-only is not "true". |
| Output | Value | Purpose |
|---|---|---|
bin-path |
Absolute path to the installed executable | Use this when a later step should invoke the exact binary path instead of relying on PATH. |
host-target |
Rust-style host target such as x86_64-unknown-linux-gnu |
Shows which release asset was selected for the current runner. |
github-release --help
github-release init --config github-release.toml
github-release plan --version 1.2.3 --config github-release.toml
github-release prepare --version 1.2.3 --config github-release.toml
github-release finalize --version 1.2.3 --config github-release.toml --assets dist
github-release publish --version 1.2.3 --config github-release.toml --assets dist
github-release abort --version 1.2.3 --config github-release.tomlTop-level automatic options include --help and --version.
Creates a starter github-release.toml.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-c, --config |
No | github-release.toml |
File path | Where the starter config should be written. |
-f, --force |
No | false |
Boolean flag | Overwrite an existing config file. |
Prints the calculated release plan without changing files, branches, tags, or GitHub Releases.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-v, --version |
Yes | none | SemVer such as 1.2.3, 1.2.3-rc.1, 2.0.0-beta.1 |
Version used to render branch names, tag names, release names, and configured file updates. |
-c, --config |
No | github-release.toml |
File path | Config file to read. |
--target-branch |
No | Config value | Branch name | Temporary override for the branch that receives the release merge. |
--release-branch |
No | Generated from config and version | Branch name | Temporary override for the release branch name. |
Creates the release branch and applies configured version file changes before the project build starts.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-v, --version |
Yes | none | SemVer / prerelease version | Release version. Use the same value later in finalize or abort. |
-c, --config |
No | github-release.toml |
File path | Config file to read. |
--target-branch |
No | Config value | Branch name | Override the target branch. |
--release-branch |
No | Generated | Branch name | Override the release branch. |
--dry-run |
No | false |
Boolean flag | Print planned Git and file operations without executing them. |
--force-branch |
No | false |
Boolean flag | Allow recreating an existing local release branch. Remote branch checks still protect against accidental collisions. |
--commit-message |
No | Config template | String | Override the version update commit message. Template values such as {tag} are normally supplied by config. |
Merges the release branch into the target branch, creates the source tag, optionally publishes a GitHub Release, and cleans up the release branch.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-v, --version |
Yes | none | Same version passed to prepare |
Resolves the release branch, tag, and release name. |
-c, --config |
No | github-release.toml |
File path | Config file to read. |
--target-branch |
No | Config value | Branch name | Override the target branch. |
--release-branch |
No | Generated | Branch name | Override the release branch. |
--assets |
No | none | Directory path | Directory whose files should be uploaded as release assets when GitHub Release publishing is enabled. Nested files are collected recursively. |
--prerelease |
No | auto |
auto, true, false |
Controls the GitHub Release prerelease flag. auto marks SemVer prerelease versions as prereleases. |
--dry-run |
No | false |
Boolean flag | Print Git/GitHub commands without executing them. |
--keep-branch |
No | false |
Boolean flag | Keep the release branch after success instead of deleting it. |
--skip-github-release |
No | false |
Boolean flag | Merge and tag only. Use this for source monorepo tags followed by a separate public distribution release. |
Creates a GitHub Release without preparing or merging a branch. This is the distribution-repository command.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-v, --version |
Yes | none | SemVer / prerelease version | Version to publish. Public distribution repositories normally publish v{version}. |
-c, --config |
No | github-release.toml |
File path | Distribution config to read. |
--assets |
No | none | Directory path | Directory whose files should be uploaded. |
--prerelease |
No | auto |
auto, true, false |
Controls the prerelease flag. |
--dry-run |
No | false |
Boolean flag | Print the GitHub command without creating the release. |
Deletes a temporary release branch after a failed build.
| Argument | Required | Default | Accepted values | Purpose |
|---|---|---|---|---|
-v, --version |
No | none | Version string | Used to resolve the default release branch. Required unless --release-branch is provided. |
-c, --config |
No | github-release.toml |
File path | Config file to read. |
--release-branch |
No | Generated from version | Branch name | Explicit branch to delete. |
--allow-any-branch |
No | false |
Boolean flag | Disable the configured release branch prefix safety check. Use only when you know exactly what will be deleted. |
--dry-run |
No | false |
Boolean flag | Print deletion commands without executing them. |
A github-release.toml file has three main areas.
[release]
target_branch = "master"
branch_prefix = "release/"
tag_prefix = "v"
tag_suffix = ""
name_prefix = ""
name_suffix = ""
commit_message = "chore(release): prepare {tag}"
merge_message = "chore(release): merge {tag}"
cleanup = true
latest = true
[github]
target_repository = "verzly/my-tool"
source_repository = "verzly/toolchain"
source_tag_prefix = "my-tool-v"
source_tag_suffix = ""
generate_notes = true
[[files]]
path = "Cargo.toml"
kind = "toml"
key = "package.version"
value = "{version}"
optional = false| Field | Accepted values | Purpose |
|---|---|---|
release.target_branch |
Branch name | Branch that receives the final merge. Defaults are usually master or main. |
release.branch_prefix |
Branch prefix string | Prefix for generated release branches, for example release/. Used as a safety boundary by abort. |
release.tag_prefix / release.tag_suffix |
String | Added around the version to create the tag. Source monorepo tags may use cargo-release-v; public repositories usually use v. |
release.name_prefix / release.name_suffix |
String | Added around the version to create the GitHub Release title. |
release.commit_message |
String template | Commit message for version file updates. |
release.merge_message |
String template | Merge commit message used by finalize. |
release.cleanup |
Boolean | Deletes the release branch after success unless --keep-branch is used. |
release.latest |
Boolean | Controls whether the GitHub Release should be marked as latest. |
github.target_repository |
owner/repo or empty |
Repository where the GitHub Release is created. Empty means the current repository context. |
github.source_repository |
owner/repo or empty |
Repository used for generated release notes. Useful when distribution repositories are source-free. |
github.source_tag_prefix / github.source_tag_suffix |
String | Source tag naming when release notes should be generated from a different repository. |
github.generate_notes |
Boolean | Use GitHub-generated notes when possible. |
files |
Array | Version files to update during prepare. Use an empty array for source-free distribution repositories. |
files[].kind |
toml, json, text |
File update strategy. |
files[].key |
Key path or search text | TOML/JSON key path or text target depending on kind. |
files[].value |
String template | New value to write. |
files[].optional |
Boolean | When true, missing files are skipped instead of failing the release. |
Use this flow when the repository contains the actual source code and version files.
github-release prepare --version 1.4.0 --config crates/my-tool/source-github-release.toml
cargo test --workspace
github-release finalize --version 1.4.0 --config crates/my-tool/source-github-release.toml --skip-github-releaseprepare creates the release branch and updates configured version files before the build starts. finalize merges the release branch and creates the source tag only after the build and tests succeed.
Use this flow when a public repository only receives generated files and binary assets.
github-release publish --version 1.4.0 --config crates/my-tool/github-release.toml --assets dist/my-toolpublish does not create source branches or edit source files. It creates a GitHub Release from an existing release context and uploads assets.
github-release abort --version 1.4.0 --config crates/my-tool/source-github-release.tomlabort deletes only the configured release branch by default. Use --allow-any-branch only for manual recovery when you have verified the branch name.
If prepare fails because the release branch already exists, inspect the branch before using --force-branch. If release notes cannot be generated from a private source repository, the tool writes fallback notes instead of silently publishing misleading content. If asset upload fails, verify that gh is authenticated and that the --assets directory contains files rather than empty directories.
Release assets are named by tool, version, and host target. Typical examples:
github-release-v1.2.3-x86_64-unknown-linux-gnu
github-release-v1.2.3-aarch64-unknown-linux-gnu
github-release-v1.2.3-x86_64-apple-darwin
github-release-v1.2.3-aarch64-apple-darwin
github-release-v1.2.3-x86_64-pc-windows-msvc.exe
Checksum files use the same name with .sha256 appended. The action verifies them when the runner has sha256sum or shasum.
github-release shells out to git and gh. CI jobs must check out the repository with enough history and must provide a token that can push branches, push tags, and create releases. For distribution repositories, the token also needs access to the public target repository.
Open issues in this repository when the problem is user-facing: installation, documentation, release assets, action inputs, or behavior of the published executable. Source changes are made in verzly/toolchain under the matching crate directory.
The architectural rule is intentionally strict: each tool owns one responsibility. Do not make a distribution repository grow source code, CI logic, or release orchestration. Those belong in verzly/toolchain.
This project is licensed under the AGPL-3.0-only license.