vibetunnel/mac/docs/release-guide.md
2025-06-23 18:02:15 +02:00

330 lines
No EOL
8.6 KiB
Markdown

# VibeTunnel Release Process
This document describes the complete release process for VibeTunnel, including all prerequisites, steps, and troubleshooting information.
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Pre-Release Checklist](#pre-release-checklist)
3. [Release Process](#release-process)
4. [Post-Release Steps](#post-release-steps)
5. [Troubleshooting](#troubleshooting)
6. [Lessons Learned](#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](#sparkle-key-management))
### Environment Variables
```bash
# 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:
```bash
# 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:
```bash
# 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:
1. **Version Configuration**
- [ ] Update `MARKETING_VERSION` in `VibeTunnel/version.xcconfig`
- [ ] Increment `CURRENT_PROJECT_VERSION` (build number)
- [ ] Ensure version follows semantic versioning
2. **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`
3. **Documentation**
- [ ] Update `CHANGELOG.md` with release notes
- [ ] Version header format: `## X.Y.Z (YYYY-MM-DD)`
- [ ] Include sections: Features, Improvements, Bug Fixes
4. **Authentication**
- [ ] GitHub CLI authenticated: `gh auth status`
- [ ] Signing certificates valid: `security find-identity -v -p codesigning`
- [ ] Notarization credentials set (environment variables)
## Release Process
### Automated Release
The easiest way to create a release is using the automated script:
```bash
# 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:
1. Run pre-flight checks
2. Build the application
3. Sign and notarize
4. Create DMG
5. Upload to GitHub
6. Update appcast files
### Manual Release Steps
If you need to run steps manually:
1. **Run Pre-flight Checks**
```bash
./scripts/preflight-check.sh
```
2. **Build Application**
```bash
# For stable release
./scripts/build.sh --configuration Release
# For pre-release
IS_PRERELEASE_BUILD=YES ./scripts/build.sh --configuration Release
```
3. **Sign and Notarize**
```bash
./scripts/sign-and-notarize.sh --sign-and-notarize
```
4. **Create DMG**
```bash
./scripts/create-dmg.sh build/Build/Products/Release/VibeTunnel.app
```
5. **Notarize DMG**
```bash
./scripts/notarize-dmg.sh build/VibeTunnel-X.Y.Z.dmg
```
6. **Create GitHub Release**
```bash
# 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
```
7. **Update Appcast**
```bash
./scripts/generate-appcast.sh
git add appcast*.xml
git commit -m "Update appcast for vX.Y.Z"
git push
```
## Post-Release Steps
1. **Verify Release**
- [ ] Check GitHub release page
- [ ] Download and test the DMG
- [ ] Verify auto-update works from previous version
2. **Clean Up**
```bash
# Clean build artifacts (keeps DMG)
./scripts/clean.sh --keep-dmg
# Restore development version if needed
git checkout -- VibeTunnel/version.xcconfig
```
3. **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:
1. Ensure correct private key in `private/sparkle_private_key`
2. Regenerate appcast: `./scripts/generate-appcast.sh`
3. Commit and push appcast changes
#### Build Number Already Exists
Error: "Build number X already exists in appcast"
Solution:
1. Increment `CURRENT_PROJECT_VERSION` in `version.xcconfig`
2. Each release must have a unique build number
#### Notarization Fails
Common causes:
- Invalid or expired certificates
- Missing API credentials
- Network issues
Solution:
1. Check credentials: `xcrun notarytool history`
2. Verify certificates: `security find-identity -v`
3. Check console logs for specific errors
#### Xcode Project Version Mismatch
If build shows wrong version:
1. Ensure Xcode project uses `$(CURRENT_PROJECT_VERSION)`
2. Not hardcoded values
3. Clean and rebuild
### Verification Commands
```bash
# 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
1. **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
2. **Timestamp All Code Signatures**
- Required for Sparkle components
- Use `--timestamp` flag on all codesign operations
- Prevents "update isn't properly signed" errors
3. **Version Management**
- Use xcconfig for centralized version control
- Never hardcode versions in Xcode project
- Increment build number for every release
4. **Pre-flight Validation**
- Always run pre-flight checks
- Ensure clean git state
- Verify all credentials before starting
5. **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.sh` to 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:
```bash
# 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.