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:
| Channel | Trigger | Branch | Version Format | GitHub Release Type |
|---|---|---|---|---|
| Beta | Automatic on merge | main |
v{prod-version}-beta-{timestamp} |
Prerelease |
| Production | Manual via workflow_dispatch |
releases |
vX.Y.Z (user specified) |
Stable |
- 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.
Format: v{prod-version}-beta-{timestamp}
prod-version: The current production version (e.g.,v1.2.3)timestamp: UTC timestamp in formatYYYYMMDD-HHMMSS
Example: v1.2.3-beta-20260712-143000
The beta version is automatically generated by the .github/scripts/get-versions.sh script, which:
- Detects the latest production version from git tags
- Generates a UTC timestamp
- Combines them into the beta version format
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 packagechecksums.txt- SHA256 checksums for all binaries
Beta releases are tagged as:
ghcr.io/routatic/proxy:{beta_tag}(e.g.,v1.2.3-beta-20260712-143000)ghcr.io/routatic/proxy:beta-{prod_version}(e.g.,beta-1.2.3)
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.0-beta-... 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:
| 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 |
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