This document explains the dual release channel system for routatic-proxy, which supports both automated beta releases and manual production releases.
- Overview
- Version Naming Conventions
- Beta Releases
- Production Releases
- Promoting Beta to Production
- GitHub Release Channel Separation
- Triggering Releases
- Troubleshooting
The project uses a dual release channel system:
- Beta releases: Automatically built and published when code is merged to
main. These are marked as prereleases on GitHub and are intended for testing. - Production releases: Triggered manually on the
releasesbranch. These are stable releases intended for end users.
This document covers producing releases. If you just want to run a beta, see Beta Releases — in short,
routatic-proxy update-channel beta && routatic-proxy update, or theghcr.io/routatic/proxy:betaDocker tag.
Format: v{upcoming-version}-beta.{N}
upcoming-version: The latest production version with the patch incremented — the version this beta is working toward (e.g., stablev0.6.3→ upcomingv0.6.4)N: Sequential counter,max(existing counters for this upcoming version) + 1, minimum 1. It resets to 1 once the upcoming version ships as stable.
Example: stable v0.6.3 → v0.6.4-beta.1, then v0.6.4-beta.2, … until v0.6.4 ships → v0.6.5-beta.1
The beta version is automatically generated by the .github/scripts/get-versions.sh script, which:
- Detects the latest production version from tags on the
releasesbranch - Increments the patch to get the upcoming version
- Scans existing
v{UPCOMING}-beta.*tags and picks the next counter
Format: vX.Y.Z (Semantic Versioning)
X: Major version (breaking changes)Y: Minor version (new features, backward compatible)Z: Patch version (bug fixes)
Example: v1.2.3
Production versions are user-specified when triggering the release workflow. The workflow does not auto-increment versions.
Beta releases are fully automated:
- Developer merges a pull request to
main - GitHub Actions triggers the
beta-release.ymlworkflow - Workflow runs tests and builds
- Creates a GitHub prerelease with:
- Cross-platform binaries (Linux, macOS, Windows)
- Docker image published to GHCR
- AI-generated changelog
- Marked as
prerelease: true
Each beta release includes:
routatic-proxy_darwin-amd64- macOS Intel binaryroutatic-proxy_darwin-arm64- macOS Apple Silicon binaryroutatic-proxy_linux-amd64- Linux Intel binaryroutatic-proxy_linux-arm64- Linux ARM64 binaryroutatic-proxy_windows-amd64.exe- Windows Intel binaryroutatic-proxy_windows-arm64.exe- Windows ARM64 binaryRoutaticProxy.dmg- macOS installer packageroutatic-proxy-{version}-1.x86_64.rpm- Fedora/RHEL package (Intel)routatic-proxy-{version}-1.aarch64.rpm- Fedora/RHEL package (ARM64)checksums.txt- SHA256 checksums for all binaries and RPMs
The RPMs are built with nfpm and verified in a dedicated rpm job on
ubuntu-latest (.github/scripts/build-rpms.sh, then verify-rpm.sh asserts
metadata, payload paths, the noreplace config flag, and the packaged binary's
ELF architecture). They are handed to the release job as the rpm-packages
artifact and included in the same atomic gh release create call as everything
else, because immutable releases reject assets added after the release exists.
Verification runs on Linux because rpm/rpm2cpio do not exist on the macOS
runner. See packaging/nfpm.yaml and make rpm for local builds. Beta RPMs use
an RPM-native tilde version (0.6.4~beta.1-1) so they sort below the eventual
stable package.
Beta releases are tagged as:
ghcr.io/routatic/proxy:{beta_tag}(e.g.,v0.6.4-beta.1)ghcr.io/routatic/proxy:beta-{prod_version}— the latest stable version, tag included (e.g.,beta-v0.6.3)ghcr.io/routatic/proxy:beta(rolling pointer to the newest beta)
Production releases are triggered manually:
- Ensure the
releasesbranch contains the code you want to release - Trigger the
release.ymlworkflow via GitHub UI or CLI - Specify the version number (e.g.,
v1.2.3) - Workflow runs tests and builds
- Creates a GitHub stable release
- Updates Homebrew tap and Scoop bucket
Same as beta releases, plus:
- Published to package managers (Homebrew, Scoop)
- Docker image tagged as
latest
Production releases are tagged as:
ghcr.io/routatic/proxy:{version}(e.g.,v1.2.3)ghcr.io/routatic/proxy:{major}.{minor}(e.g.,1.2)ghcr.io/routatic/proxy:{major}(e.g.,1)ghcr.io/routatic/proxy:latest
To promote a beta release to production:
# Ensure you're on main and have the latest changes
git checkout main
git pull origin main
# Checkout releases branch
git checkout releases
git pull origin releases
# Merge main into releases
git merge main
# Push to origin
git push origin releasesSee Triggering Releases below for detailed instructions.
When triggering a production release, you must specify the version. Common approaches:
-
Patch release (bug fixes): Increment Z in
vX.Y.Zv1.2.3->v1.2.4
-
Minor release (new features): Increment Y in
vX.Y.Zv1.2.3->v1.3.0
-
Major release (breaking changes): Increment X in
vX.Y.Zv1.2.3->v2.0.0
GitHub releases are separated by the prerelease flag:
prerelease: true- Appears under "Releases" with a "Pre-release" badge
- Not shown as "Latest" on the repository homepage
- Intended for testing and early adopters
prerelease: false- Appears as the "Latest" release on the repository homepage
- Shown to all users as the recommended version
- Triggers package manager updates
Navigate to: https://github.com/routatic/proxy/releases
- Latest stable: The most recent non-prerelease
- All releases: Includes both stable and prereleases
- Tags: All git tags (including betas without releases)
No manual action required. Beta releases trigger automatically when code is merged to main.
To verify a beta release was created:
# List recent beta tags
git tag -l "v*-beta.*" --sort=-version:refname | head -10
# Or check GitHub CLI
gh release list --repo routatic/proxy --limit 20- Navigate to the repository:
https://github.com/routatic/proxy - Click "Actions" tab
- Select "Release" workflow from the left sidebar
- Click "Run workflow" button
- Select the
releasesbranch from dropdown - Enter the version to release (e.g.,
v1.2.3) - Click "Run workflow"
# Trigger a production release
gh workflow run release.yml \
--repo routatic/proxy \
--ref releases \
-f version=v1.2.3
# Monitor the workflow run
gh run watch --repo routatic/proxy# Trigger via GitHub API
curl -X POST \
-H "Authorization: token YOUR_GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/routatic/proxy/actions/workflows/release.yml/dispatches \
-d '{
"ref": "releases",
"inputs": {
"version": "v1.2.3"
}
}'To trigger workflows, you need:
- Write access to the repository, OR
actions:writepermission scope for API/CLI access
Symptoms: Code merged to main but no beta release created.
Diagnosis:
# Check if the workflow file exists
cat .github/workflows/beta-release.yml
# Check recent workflow runs
gh run list --workflow=beta-release.yml --limit 10Solutions:
- Verify the merge was to
mainbranch (not another branch) - Check if the workflow is disabled in GitHub Actions settings
- Look for syntax errors in the workflow file
- Check repository Actions permissions (Settings > Actions > General)
Symptoms: Beta tag shows v0.0.1-beta.1 (derived from the v0.0.0 fallback) instead of actual version.
Diagnosis:
# Check if production tags exist
git tag -l "v[0-9]*.[0-9]*.[0-9]*" --sort=-version:refname | head -5
# Run version script locally
./.github/scripts/get-versions.shSolutions:
- Ensure at least one production version tag exists
- The script falls back to
v0.0.0if no tags match the pattern - Push a production tag manually if needed:
git tag v0.1.0 && git push origin v0.1.0
Symptoms: Beta release created but no Docker image in GHCR.
Diagnosis:
# Check if docker job ran
gh run view --repo routatic/proxy --job=dockerSolutions:
- Docker push only works for the main repository (not forks)
- Verify
packages: writepermission in workflow - Check GHCR authentication in workflow logs
Symptoms: Workflow fails with "tag already exists".
Diagnosis:
# Check if tag exists
git tag -l "v1.2.3"
# Check GitHub releases
gh release view v1.2.3 --repo routatic/proxySolutions:
- Use a higher version number
- Delete the existing tag (if it was a mistake):
git push --delete origin v1.2.3 - Check if a beta release already uses this version pattern
Symptoms: Release created but package managers not updated.
Diagnosis:
# Check if HOMEBREW_PAT or SCOOP_PAT secrets are set
gh secret list --repo routatic/proxySolutions:
- Verify
HOMEBREW_PATsecret exists (for homebrew-tap repo access) - Verify
SCOOP_PATsecret exists (for scoop-bucket repo access) - Check PAT has
reposcope for the respective repositories - Verify the tap/bucket repositories exist and are accessible
Symptoms: Can't find the Release workflow to trigger manually.
Solutions:
- Ensure the workflow file exists:
.github/workflows/release.yml - Check if workflow has
workflow_dispatchtrigger configured - Workflow may need to be on the default branch (
main) to appear - Check if the workflow was disabled due to inactivity
Symptoms: Script fails on macOS with date command errors.
Solutions:
- The script uses GNU date format:
date -u +"%Y%m%d-%H%M%S" - On macOS, install coreutils:
brew install coreutils - Or modify script to use
gdateinstead ofdate
Symptoms: Script outputs empty or malformed JSON.
Diagnosis:
# Run with debug mode
bash -x ./.github/scripts/get-versions.shSolutions:
- Ensure you're in a git repository
- Check if
git tagcommand works:git tag -l - Verify the script is executable:
chmod +x .github/scripts/get-versions.sh
# List recent runs
gh run list --limit 20
# View specific run logs
gh run view <run-id> --log
# View failed job logs
gh run view <run-id> --job=<job-name> --log# Install actionlint
brew install actionlint
# Validate workflow files
actionlint .github/workflows/*.yml# Make script executable
chmod +x .github/scripts/get-versions.sh
# Run and check output
./.github/scripts/get-versions.shThe workflows expect these secrets:
PR merged to main
|
v
GitHub Actions triggers
|
v
Run tests (ubuntu-latest)
|
v
Build binaries (macos-latest)
|
v
Create prerelease on GitHub
|
v
Publish Docker image to GHCR
Manual trigger on releases branch
|
v
Specify version (e.g., v1.2.3)
|
v
Run tests (ubuntu-latest)
|
v
Build binaries (macos-latest)
|
v
Create stable release on GitHub
|
v
Publish Docker image to GHCR
|
v
Update Homebrew tap
|
v
Update Scoop bucket
# List all tags
git tag -l --sort=-version:refname
# List beta tags only
git tag -l "v*-beta.*" --sort=-version:refname
# List production tags only
git tag -l "v[0-9]*.[0-9]*.[0-9]*" --sort=-version:refname
# Delete a local tag
git tag -d v1.2.3
# Delete a remote tag
git push --delete origin v1.2.3
# Fetch all tags from remote
git fetch --tags
# View release assets
gh release view v1.2.3 --repo routatic/proxy
# Download release asset
gh release download v1.2.3 --repo routatic/proxy --pattern "routatic-proxy_linux-amd64"Last updated: 2026-07-12
