7.5 KiB
Gaze Release Deployment Process
Overview
This document describes the complete process for building and deploying new releases of Gaze with Sparkle auto-update support.
Prerequisites
- Xcode with Gaze project configured
create-dmgtool installed (brew install create-dmg)- Sparkle EdDSA signing keys in macOS Keychain (see Key Management below)
- AWS credentials configured in
.envfile for S3 upload - Access to freno.me hosting infrastructure
Version Management
Version numbers are managed in Xcode project settings:
- Marketing Version (
MARKETING_VERSION): User-facing version (e.g., "0.1.1") - Build Number (
CURRENT_PROJECT_VERSION): Internal build number (e.g., "1")
These must be incremented before each release and kept in sync with build_dmg script.
Release Checklist
1. Prepare Release
- Update version numbers in Xcode:
- Project Settings → General → Identity
- Set Marketing Version (e.g., "0.1.2")
- Increment Build Number (e.g., "2")
- Update
VERSIONandBUILD_NUMBERinbuild_dmgscript - Update CHANGELOG or release notes
- Commit version changes:
git commit -am "Bump version to X.Y.Z" - Create git tag:
git tag v0.1.2
2. Build Application
# Build the app in Xcode (Product → Archive → Export)
# Or use the run script
./run build
# Verify the app runs correctly
open Gaze.app
3. Create DMG and Appcast
# Run the build_dmg script
./build_dmg
# This will:
# - Create versioned DMG file
# - Generate appcast.xml with EdDSA signature
# - Upload to S3 if AWS credentials are configured
# - Display next steps
4. Verify Artifacts
Check that the following files were created in ./releases/:
Gaze-X.Y.Z.dmg- Installable disk imageappcast.xml- Update feed with signatureGaze-X.Y.Z.delta(optional) - Delta update from previous version
5. Upload to Hosting (if not using S3 auto-upload)
DMG File:
# Upload to: https://freno.me/downloads/
scp ./releases/Gaze-X.Y.Z.dmg your-server:/path/to/downloads/
Appcast File:
# Upload to: https://freno.me/api/Gaze/
scp ./releases/appcast.xml your-server:/path/to/api/Gaze/
6. Verify Deployment
Test that files are accessible via HTTPS:
# Test appcast accessibility
curl -I https://freno.me/api/Gaze/appcast.xml
# Should return: HTTP/2 200, content-type: application/xml
# Test DMG accessibility
curl -I https://freno.me/downloads/Gaze-X.Y.Z.dmg
# Should return: HTTP/2 200, content-type: application/octet-stream
# Validate appcast XML structure
curl https://freno.me/api/Gaze/appcast.xml | xmllint --format -
7. Test Update Flow
Manual Testing:
- Install previous version of Gaze
- Launch app and check Settings → General → Software Updates
- Click "Check for Updates Now"
- Verify update notification appears
- Complete update installation
- Verify new version launches correctly
Automatic Update Testing:
- Set
SUScheduledCheckIntervalto a low value (e.g., 60 seconds) for testing - Install previous version
- Wait for automatic check
- Verify update notification appears
8. Finalize Release
- Push git tag:
git push origin v0.1.2 - Create GitHub release (optional)
- Announce release to users
- Monitor for update errors in the first 24 hours
Hosting Configuration
Current Setup
- Appcast URL:
https://freno.me/api/Gaze/appcast.xml - Download URL:
https://freno.me/downloads/Gaze-{VERSION}.dmg - Hosting: AWS S3 with freno.me domain
- SSL: HTTPS enabled (required by App Transport Security)
AWS S3 Configuration
Create a .env file in the project root with:
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_BUCKET_NAME=your_bucket_name
AWS_REGION=us-east-1
Note: .env is gitignored to protect credentials.
S3 Bucket Structure
your-bucket/
├── downloads/
│ ├── Gaze-0.1.1.dmg
│ ├── Gaze-0.1.2.dmg
│ └── ...
└── api/
└── Gaze/
└── appcast.xml
Key Management
EdDSA Signing Keys
Location:
- Public Key: In
Gaze/Info.plistasSUPublicEDKey - Private Key: In macOS Keychain as "Sparkle EdDSA Private Key"
- Backup:
~/sparkle_private_key_backup.pem(keep secure!)
Current Public Key:
Z2RmohI1y2bgeGQQUDqO9F0HNF2AzFotOt8CwGB6VJM=
Security:
- Never commit private key to version control
- Keep backup in secure location (password manager, encrypted drive)
- Private key is required to sign all future updates
Regenerating Keys (Emergency Only)
If private key is lost, you must:
- Generate new key pair:
./generate_keys - Update
SUPublicEDKeyin Info.plist - Release new version with new public key
- Previous versions won't be able to update (users must manually install)
Troubleshooting
Appcast Generation Fails
Problem: generate_appcast tool not found
Solution:
# Build the app first to generate Sparkle tools
xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Release
# Find Sparkle tools
find ~/Library/Developer/Xcode/DerivedData/Gaze-* -name "generate_appcast"
Update Check Fails in App
Problem: No updates found or connection error
Diagnostics:
# Check Console.app for Sparkle logs
# Filter by process: Gaze
# Look for:
# - "Downloading appcast..."
# - "Appcast downloaded successfully"
# - Connection errors
# - Signature verification errors
Common Issues:
- Appcast URL not accessible (check HTTPS)
- Signature mismatch (wrong private key used)
- XML malformed (validate with xmllint)
- Version number not higher than current version
DMG Not Downloading
Problem: Update found but download fails
Check:
- DMG URL is correct in appcast
- DMG file is accessible via HTTPS
- File size in appcast matches actual DMG size
- No CORS issues (check browser console)
Delta Updates
Sparkle automatically generates delta updates when multiple versions exist in ./releases/:
# Keep previous versions for delta generation
releases/
├── Gaze-0.1.1.dmg
├── Gaze-0.1.2.dmg
├── Gaze-0.1.1-to-0.1.2.delta # Generated automatically
└── appcast.xml
Benefits:
- Much smaller downloads (MB vs GB)
- Faster updates for users
- Generated automatically by
generate_appcast
Note: First-time users still download full DMG.
Testing with Local Appcast
For testing without deploying:
- Modify Info.plist temporarily:
<key>SUFeedURL</key>
<string>file:///Users/mike/Code/Gaze/releases/appcast.xml</string>
- Build and run app
- Check for updates
- Revert Info.plist before committing
Release Notes
Release notes are embedded in appcast XML as CDATA:
<description><![CDATA[
<h2>What's New in Version X.Y.Z</h2>
<ul>
<li>Feature 1</li>
<li>Bug fix 2</li>
</ul>
]]></description>
Tips:
- Use simple HTML (h2, ul, li, p, strong, em)
- No external resources (images, CSS, JS)
- Keep concise and user-focused
- Highlight breaking changes
References
Support
For issues with deployment:
- Check Console.app for Sparkle errors
- Verify appcast validation with xmllint
- Test with file:// URL first
- Check AWS S3 permissions and CORS