Dual Release Channel System
August 22, 2026 · View on GitHub
This document explains the dual release channel system for routatic-proxy, which supports both automated beta releases and manual production releases.
Table of Contents
- Overview
- Version Naming Conventions
- Beta Releases
- Production Releases
- Promoting Beta to Production
- GitHub Release Channel Separation
- Triggering Releases
- Troubleshooting
Overview
The project uses a dual release channel system:
| Channel | Trigger | Branch | Version Format | GitHub Release Type |
|---|---|---|---|---|
| Beta | Automatic on merge | main | v{upcoming-version}-beta.{N} | Prerelease |
| Production | Manual via workflow_dispatch | releases | vX.Y.Z (user specified) | Stable |
Key Differences
- 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.
Version Naming Conventions
Beta Versions
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
Production Versions
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
How They Work
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
Beta Release Artifacts
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.
Docker Tags for Beta
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
How They Work
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
Production Release Artifacts
Same as beta releases, plus:
- Published to package managers (Homebrew, Scoop)
- Docker image tagged as
latest
Docker Tags for Production
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
Promoting Beta to Production
To promote a beta release to production:
Step 1: Merge to Releases Branch
# 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 releases
Step 2: Trigger Production Release
See Triggering Releases below for detailed instructions.
Version Selection
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 Release Channel Separation
GitHub releases are separated by the prerelease flag:
Beta Releases (Prerelease)
prerelease: true- Appears under "Releases" with a "Pre-release" badge
- Not shown as "Latest" on the repository homepage
- Intended for testing and early adopters
Production Releases (Stable)
prerelease: false- Appears as the "Latest" release on the repository homepage
- Shown to all users as the recommended version
- Triggers package manager updates
Viewing Releases
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)
Triggering Releases
Beta Releases (Automatic)
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
Production Releases (Manual)
Option 1: GitHub Web UI
- 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"
Option 2: GitHub CLI
# 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
Option 3: REST API
# 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"
}
}'
Required Permissions
To trigger workflows, you need:
- Write access to the repository, OR
actions:writepermission scope for API/CLI access
Troubleshooting
Beta Release Issues
Issue: Beta release not triggering after merge
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 10
Solutions:
- 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)
Issue: Beta version shows wrong production version
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.sh
Solutions:
- 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
Issue: Docker image not published
Symptoms: Beta release created but no Docker image in GHCR.
Diagnosis:
# Check if docker job ran
gh run view --repo routatic/proxy --job=docker
Solutions:
- Docker push only works for the main repository (not forks)
- Verify
packages: writepermission in workflow - Check GHCR authentication in workflow logs
Production Release Issues
Issue: "Version already exists" error
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/proxy
Solutions:
- 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
Issue: Homebrew/Scoop update fails
Symptoms: Release created but package managers not updated.
Diagnosis:
# Check if HOMEBREW_PAT or SCOOP_PAT secrets are set
gh secret list --repo routatic/proxy
Solutions:
- 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
Issue: Workflow not appearing in Actions tab
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
Version Script Issues
Issue: get-versions.sh fails with "date: illegal option"
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
Issue: get-versions.sh returns empty version
Symptoms: Script outputs empty or malformed JSON.
Diagnosis:
# Run with debug mode
bash -x ./.github/scripts/get-versions.sh
Solutions:
- 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
General Troubleshooting
Check Workflow Logs
# 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
Validate Workflow Syntax
# Install actionlint
brew install actionlint
# Validate workflow files
actionlint .github/workflows/*.yml
Test Version Script Locally
# Make script executable
chmod +x .github/scripts/get-versions.sh
# Run and check output
./.github/scripts/get-versions.sh
Common Environment Variables
The workflows expect these secrets:
| Secret | Used In | Purpose |
|---|---|---|
GITHUB_TOKEN | All workflows | GitHub API access, releases |
OPENROUTER_API_KEY | Beta/Release | AI changelog generation |
HOMEBREW_PAT | Production | Homebrew tap updates |
SCOOP_PAT | Production | Scoop bucket updates |
Quick Reference
Beta Release Flow
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
Production Release Flow
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
Useful Commands
# 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