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

The project uses a dual release channel system:

ChannelTriggerBranchVersion FormatGitHub Release Type
BetaAutomatic on mergemainv{upcoming-version}-beta.{N}Prerelease
ProductionManual via workflow_dispatchreleasesvX.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 releases branch. 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 the ghcr.io/routatic/proxy:beta Docker 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., stable v0.6.3 → upcoming v0.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.3v0.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:

  1. Detects the latest production version from tags on the releases branch
  2. Increments the patch to get the upcoming version
  3. 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:

  1. Developer merges a pull request to main
  2. GitHub Actions triggers the beta-release.yml workflow
  3. Workflow runs tests and builds
  4. 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 binary
  • routatic-proxy_darwin-arm64 - macOS Apple Silicon binary
  • routatic-proxy_linux-amd64 - Linux Intel binary
  • routatic-proxy_linux-arm64 - Linux ARM64 binary
  • routatic-proxy_windows-amd64.exe - Windows Intel binary
  • routatic-proxy_windows-arm64.exe - Windows ARM64 binary
  • RoutaticProxy.dmg - macOS installer package
  • routatic-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:

  1. Ensure the releases branch contains the code you want to release
  2. Trigger the release.yml workflow via GitHub UI or CLI
  3. Specify the version number (e.g., v1.2.3)
  4. Workflow runs tests and builds
  5. Creates a GitHub stable release
  6. 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:

  1. Patch release (bug fixes): Increment Z in vX.Y.Z

    • v1.2.3 -> v1.2.4
  2. Minor release (new features): Increment Y in vX.Y.Z

    • v1.2.3 -> v1.3.0
  3. Major release (breaking changes): Increment X in vX.Y.Z

    • v1.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

  1. Navigate to the repository: https://github.com/routatic/proxy
  2. Click "Actions" tab
  3. Select "Release" workflow from the left sidebar
  4. Click "Run workflow" button
  5. Select the releases branch from dropdown
  6. Enter the version to release (e.g., v1.2.3)
  7. 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:write permission 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:

  1. Verify the merge was to main branch (not another branch)
  2. Check if the workflow is disabled in GitHub Actions settings
  3. Look for syntax errors in the workflow file
  4. 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:

  1. Ensure at least one production version tag exists
  2. The script falls back to v0.0.0 if no tags match the pattern
  3. 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:

  1. Docker push only works for the main repository (not forks)
  2. Verify packages: write permission in workflow
  3. 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:

  1. Use a higher version number
  2. Delete the existing tag (if it was a mistake): git push --delete origin v1.2.3
  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:

  1. Verify HOMEBREW_PAT secret exists (for homebrew-tap repo access)
  2. Verify SCOOP_PAT secret exists (for scoop-bucket repo access)
  3. Check PAT has repo scope for the respective repositories
  4. 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:

  1. Ensure the workflow file exists: .github/workflows/release.yml
  2. Check if workflow has workflow_dispatch trigger configured
  3. Workflow may need to be on the default branch (main) to appear
  4. 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:

  1. The script uses GNU date format: date -u +"%Y%m%d-%H%M%S"
  2. On macOS, install coreutils: brew install coreutils
  3. Or modify script to use gdate instead of date

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:

  1. Ensure you're in a git repository
  2. Check if git tag command works: git tag -l
  3. 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:

SecretUsed InPurpose
GITHUB_TOKENAll workflowsGitHub API access, releases
OPENROUTER_API_KEYBeta/ReleaseAI changelog generation
HOMEBREW_PATProductionHomebrew tap updates
SCOOP_PATProductionScoop 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