8.6 KiB
VibeTunnel Release Process
This document describes the complete release process for VibeTunnel, including all prerequisites, steps, and troubleshooting information.
Table of Contents
- Prerequisites
- Pre-Release Checklist
- Release Process
- Post-Release Steps
- Troubleshooting
- Lessons Learned
Prerequisites
Required Tools
- Xcode (latest stable version)
- GitHub CLI (
brew install gh) - Apple Developer Account with valid certificates
- Sparkle EdDSA Keys (see Sparkle Key Management)
Environment Variables
# Required for notarization
export APP_STORE_CONNECT_API_KEY_P8="<your-api-key>"
export APP_STORE_CONNECT_KEY_ID="<your-key-id>"
export APP_STORE_CONNECT_ISSUER_ID="<your-issuer-id>"
# Optional - will be auto-detected if not set
export SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
Sparkle Key Management
VibeTunnel uses EdDSA signatures for secure updates via Sparkle framework.
Key Storage
- Private Key:
private/sparkle_private_key(NEVER commit this!) - Public Key:
VibeTunnel/sparkle-public-ed-key.txt(committed to repo)
Generating New Keys
If you need to generate new keys:
# Generate keys using Sparkle's tool
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_keys
# This creates a key pair - save the private key securely!
Restoring Keys
To restore keys from backup:
# Create private directory
mkdir -p private
# Copy your private key (base64 encoded, no comments)
echo "YOUR_PRIVATE_KEY_BASE64" > private/sparkle_private_key
# Ensure it's in .gitignore
echo "private/" >> .gitignore
Pre-Release Checklist
Before starting a release, ensure:
-
Version Configuration
- Update
MARKETING_VERSIONinVibeTunnel/version.xcconfig - Increment
CURRENT_PROJECT_VERSION(build number) - Ensure version follows semantic versioning
- Update
-
Code Quality
- All tests pass:
npm run test(in web/) and Swift tests - Linting passes:
npm run lint - Typechecking passes:
npm run typecheck - No uncommitted changes:
git status
- All tests pass:
-
Documentation
- Update
CHANGELOG.mdwith release notes - Version header format:
## X.Y.Z (YYYY-MM-DD) - Include sections: Features, Improvements, Bug Fixes
- Update
-
Authentication
- GitHub CLI authenticated:
gh auth status - Signing certificates valid:
security find-identity -v -p codesigning - Notarization credentials set (environment variables)
- GitHub CLI authenticated:
Release Process
Automated Release
The easiest way to create a release is using the automated script:
# For stable release
./scripts/release.sh stable
# For pre-release (beta, alpha, rc)
./scripts/release.sh beta 1 # Creates 1.0.0-beta.1
./scripts/release.sh rc 2 # Creates 1.0.0-rc.2
The script will:
- Run pre-flight checks
- Build the application
- Sign and notarize
- Create DMG
- Upload to GitHub
- Update appcast files
Manual Release Steps
If you need to run steps manually:
-
Run Pre-flight Checks
./scripts/preflight-check.sh -
Build Application
# For stable release ./scripts/build.sh --configuration Release # For pre-release IS_PRERELEASE_BUILD=YES ./scripts/build.sh --configuration Release -
Sign and Notarize
./scripts/sign-and-notarize.sh --sign-and-notarize -
Create DMG
./scripts/create-dmg.sh build/Build/Products/Release/VibeTunnel.app -
Notarize DMG
./scripts/notarize-dmg.sh build/VibeTunnel-X.Y.Z.dmg -
Create GitHub Release
# Create tag git tag -a "vX.Y.Z" -m "Release X.Y.Z" git push origin "vX.Y.Z" # Create release gh release create "vX.Y.Z" \ --title "VibeTunnel X.Y.Z" \ --notes "Release notes here" \ build/VibeTunnel-X.Y.Z.dmg -
Update Appcast
./scripts/generate-appcast.sh git add appcast*.xml git commit -m "Update appcast for vX.Y.Z" git push
Post-Release Steps
-
Verify Release
- Check GitHub release page
- Download and test the DMG
- Verify auto-update works from previous version
-
Clean Up
# Clean build artifacts (keeps DMG) ./scripts/clean.sh --keep-dmg # Restore development version if needed git checkout -- VibeTunnel/version.xcconfig -
Announce Release
- Update website/documentation
- Send release announcement
- Update issue tracker milestones
Troubleshooting
Common Issues
"Update isn't properly signed" Error
This indicates an EdDSA signature mismatch. Causes:
- Wrong private key used for signing
- Appcast not updated after DMG creation
- Cached signatures from different key
Solution:
- Ensure correct private key in
private/sparkle_private_key - Regenerate appcast:
./scripts/generate-appcast.sh - Commit and push appcast changes
Build Number Already Exists
Error: "Build number X already exists in appcast"
Solution:
- Increment
CURRENT_PROJECT_VERSIONinversion.xcconfig - Each release must have a unique build number
Notarization Fails
Common causes:
- Invalid or expired certificates
- Missing API credentials
- Network issues
Solution:
- Check credentials:
xcrun notarytool history - Verify certificates:
security find-identity -v - Check console logs for specific errors
Xcode Project Version Mismatch
If build shows wrong version:
- Ensure Xcode project uses
$(CURRENT_PROJECT_VERSION) - Not hardcoded values
- Clean and rebuild
Verification Commands
# Check signing
codesign -dv --verbose=4 build/VibeTunnel.app
# Check notarization
spctl -a -t exec -vv build/VibeTunnel.app
# Verify DMG
hdiutil verify build/VibeTunnel-X.Y.Z.dmg
# Test EdDSA signature
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
build/VibeTunnel-X.Y.Z.dmg \
-f private/sparkle_private_key
Lessons Learned
Critical Points
-
Always Use Correct Sparkle Keys
- The private key must match the public key in the app
- Store private key securely, never in version control
- Test signature generation before release
-
Timestamp All Code Signatures
- Required for Sparkle components
- Use
--timestampflag on all codesign operations - Prevents "update isn't properly signed" errors
-
Version Management
- Use xcconfig for centralized version control
- Never hardcode versions in Xcode project
- Increment build number for every release
-
Pre-flight Validation
- Always run pre-flight checks
- Ensure clean git state
- Verify all credentials before starting
-
Appcast Synchronization
- Push appcast updates immediately after release
- GitHub serves as the appcast host
- Users fetch from raw.githubusercontent.com
Best Practices
- Automate Everything: Use the release script for consistency
- Test Updates: Always test auto-update from previous version
- Keep Logs: Save notarization logs for debugging
- Document Issues: Update this guide when new issues arise
- Clean Regularly: Use
clean.shto manage disk space
Script Reference
| Script | Purpose | Key Options |
|---|---|---|
release.sh |
Complete automated release | stable, beta N, alpha N, rc N |
preflight-check.sh |
Validate release readiness | None |
build.sh |
Build application | --configuration Release/Debug |
sign-and-notarize.sh |
Sign and notarize app | --sign-and-notarize |
create-dmg.sh |
Create DMG installer | <app_path> [output_path] |
notarize-dmg.sh |
Notarize DMG | <dmg_path> |
generate-appcast.sh |
Update appcast files | None |
verify-appcast.sh |
Verify appcast validity | None |
clean.sh |
Clean build artifacts | --all, --keep-dmg, --dry-run |
lint.sh |
Run code linters | None |
Environment Setup
For team members setting up for releases:
# 1. Install dependencies
brew install gh
npm install -g swiftformat swiftlint
# 2. Authenticate GitHub CLI
gh auth login
# 3. Set up notarization credentials
# Add to ~/.zshrc or ~/.bash_profile:
export APP_STORE_CONNECT_API_KEY_P8="..."
export APP_STORE_CONNECT_KEY_ID="..."
export APP_STORE_CONNECT_ISSUER_ID="..."
# 4. Get Sparkle private key from secure storage
# Contact team lead for access
For questions or issues, consult the script headers or create an issue in the repository.