A GitHub Action for releasing multiple independently-versioned workflows from a single repository. Perfect to share reusable workflows monorepos that are interconnected but that require separate versioning.
Important
While WIRE is born to manage multiple reusable workflows in a single repository, it can also be used to manage versioning and releases for a single workflow or action. Keep reading for details!
When you distribute multiple reusable GitHub Workflows, managing their versions can get tricky.
Common approaches and their drawbacks:
-
Store all workflows in the same repository and version them together
- A breaking change in one workflow forces a major version bump for all workflows (often unnecessary)
-
Keep every workflow in its own repository
- Harder to maintain, share code, and discover
- When a change affects multiple workflows, you have to coordinate releases across multiple repositories
WIRE offers a middle ground:
- Keep all workflows in a single repository for easy maintenance and sharing
- Independent versioning per workflow (e.g.,
workflow-aat v2.1.0,workflow-bat v1.0.3) - Prefixed tags like
workflow-a/v2.1.0andworkflow-b/v1.0.3 - Floating major version tags like
workflow-a/v2for easy consumption - Batch releases to release multiple workflows at once
- Single source of truth for all versions in one JSON file
- Release single, multiple, or all workflows at once
- Configurable tag patterns (
{name}/v{version},v{version}-{name}, etc.) - Floating major version tags (auto-updated on each release) - this way consumers can always point to the latest major version
- Configurable version file path
- Customizable commit messages
- Fail-fast validation before any changes
- Detailed outputs for downstream steps
- Job summary with release details
Create workflow-versions.json (name and path customisable, see inputs ) in your repository root:
{
"workflow-a": "1.0.0",
"workflow-b": "1.0.0"
}Important
The versions file is mandatory, WIRE uses it to track the current versions of your workflows. Explanation here.
name: Release Workflows
on:
workflow_dispatch:
inputs:
workflows:
description: "Workflows to release (comma-separated or 'all')"
required: true
type: string
bump-type:
description: "Version bump type"
required: true
type: choice
options:
- patch
- minor
- major
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- name: Release
uses: albertodeago/wire@v1
with:
workflows: ${{ inputs.workflows }}
bump-type: ${{ inputs.bump-type }}
github-token: ${{ secrets.GITHUB_TOKEN }}| Input | Description | Required | Default |
|---|---|---|---|
workflows |
Workflows to release (comma-separated names, or 'all') |
true | - |
bump-type |
Version bump type: patch, minor, or major |
true | - |
versions-file |
Path to JSON file tracking workflow versions | false | workflow-versions.json |
tag-pattern |
Tag pattern. Must include {version}. {name} is required for multiple workflows, optional for single workflow |
false | {name}/v{version} |
github-token |
GitHub token for pushing commits and tags | true | - |
git-user-name |
Git user name for commits | false | github-actions[bot] |
git-user-email |
Git user email for commits | false | github-actions[bot]@users.noreply.github.com |
commit-message-pattern |
Commit message pattern. Use {workflows} placeholder |
false | chore({workflows}): release new versions |
| Output | Description | Example |
|---|---|---|
released |
JSON object mapping workflow names to new versions | {"workflow-a":"1.2.0","workflow-b":"2.0.0"} |
tags |
JSON array of all created tags (version + major tags) | ["workflow-a/v1.2.0","workflow-a/v1","workflow-b/v2.0.0","workflow-b/v2"] |
- uses: albertodeago/wire@v1
with:
workflows: "my-workflow"
bump-type: "patch"
tag-pattern: "v{version}"
github-token: ${{ secrets.GITHUB_TOKEN }}- uses: albertodeago/wire@v1
with:
workflows: "workflow-a, workflow-b, workflow-c"
bump-type: "minor"
github-token: ${{ secrets.GITHUB_TOKEN }}- uses: albertodeago/wire@v1
with:
workflows: "all"
bump-type: "patch"
github-token: ${{ secrets.GITHUB_TOKEN }}- uses: albertodeago/wire@v1
with:
workflows: "my-lib"
bump-type: "major"
tag-pattern: "v{version}-{name}"
github-token: ${{ secrets.GITHUB_TOKEN }}For repositories with a single action/workflow, you can omit {name} from the tag pattern to get clean tags like v1.0.0 and v1:
- uses: albertodeago/wire@v1
with:
workflows: "my-action"
bump-type: "patch"
tag-pattern: "v{version}"
github-token: ${{ secrets.GITHUB_TOKEN }}Tip
This is actually how WIRE itself is published
- uses: albertodeago/wire@v1
with:
workflows: "all"
bump-type: "patch"
versions-file: "config/versions.json"
github-token: ${{ secrets.GITHUB_TOKEN }}- name: Release
id: release
uses: albertodeago/wire@v1
with:
workflows: "all"
bump-type: "patch"
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Use outputs
run: |
echo "Released: ${{ steps.release.outputs.released }}"
echo "Tags: ${{ steps.release.outputs.tags }}"WIRE focuses on versioning and tagging. To also create GitHub Releases, combine it with a release action (this is just an example):
- name: Release
id: wire
uses: albertodeago/wire@v1
with:
workflows: "my-workflow"
bump-type: "patch"
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ fromJSON(steps.wire.outputs.tags)[0] }}
name: ${{ fromJSON(steps.wire.outputs.tags)[0] }}
body: |
## Released
${{ steps.wire.outputs.released }}
generate_release_notes: trueThe action expects a simple JSON object mapping workflow names to their current semver versions:
{
"workflow-a": "1.2.3",
"workflow-b": "0.5.0",
"workflow-c": "2.0.0"
}This action uses a JSON file to track component versions rather than deriving versions from existing Git tags via GitHub API.
| Approach | Description |
|---|---|
| JSON File (chosen) | Store versions in a committed file (e.g., versions.json) |
| Git Tags via API | Query GitHub API to find latest tag per component |
| Git Tags via CLI | Use git tag -l locally (requires fetch-depth: 0) |
| Aspect | JSON File | Git Tags (API) | Git Tags (CLI) |
|---|---|---|---|
| Speed | ✅ Fast (local read) | ❌ Slow (API calls, pagination) | |
| Clone requirements | ✅ Shallow clone OK | ✅ Shallow clone OK | ❌ Needs fetch-depth: 0 |
| Visibility | ✅ Easy to see all versions | ❌ Must query/parse tags | ❌ Must run git commands |
| Merge conflicts | ✅ None | ✅ None | |
| Extra commit | ✅ No | ✅ No | |
| First release | ✅ Auto (starts at 0.0.0) | ✅ Auto | |
| Rate limits | ✅ None | ✅ None | |
| "all" keyword | ✅ Read keys from file |
I chose the JSON file approach because:
- Simplicity - No complex tag parsing logic, no API pagination handling
- Speed - Local file read is instant vs. multiple API calls for repos with many tags
- Transparency - All versions visible at a glance in one file
- Reliability - No network dependencies, no rate limits
- Shallow clones - Works with default
actions/checkout(nofetch-depth: 0needed, possible problem with huge repos histories) - Explicit component list - The JSON keys define what can be released; no risk of accidentally matching unrelated tags
The tradeoff (extra commit + potential merge conflicts) I think is an acceptable trade-off considering the benefits above.
When triggered, WIRE will:
- Validate inputs - Check bump type, tag pattern, and requested workflows
- Read versions file - Load current versions from the JSON file
- Calculate new versions - Bump versions according to semver rules
- Update versions file - Write new versions back to the JSON file
- Commit changes - Create a commit with the updated versions file
- Push commit - Push the commit to the repository
- Create and push tags - Create version tags (e.g.,
workflow-a/v1.2.0) and floating major tags (e.g.,workflow-a/v1)
| Requirement | Why |
|---|---|
actions/checkout@v6 |
Needed to access the versions file and enable pushing |
permissions: contents: write |
Required to push commits and tags |
| A versions JSON file | Tracks current versions of all workflows |
npm installnpm testThe action must be compiled before committing changes:
npm run buildThis uses @vercel/ncc to bundle the TypeScript code into a single dist/index.js file that GitHub Actions can execute.
Important: Always commit the dist/ folder after making code changes.
Wire is itself released using WIRE!
To release a new version, trigger the Release Workflows workflow from the Actions tab.
Release it manually by:
Create and push tags: First make changes, build, and commit them (dist included, and remember to update the changelog). Then create a version tag and push it:
# Create version tag
git tag v1.0.0
git push origin v1.0.0
# Update floating major tag
git tag -f v1
git push origin -f v1Alternatively, you can use the Github Releases interface to create a new release, which will automatically create and push the corresponding tags.
MIT
