Files
Gaze/DEPLOYMENT.md
2026-01-11 17:45:32 -05:00

297 lines
7.5 KiB
Markdown

# 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-dmg` tool installed (`brew install create-dmg`)
- Sparkle EdDSA signing keys in macOS Keychain (see Key Management below)
- AWS credentials configured in `.env` file 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 `VERSION` and `BUILD_NUMBER` in `build_dmg` script
- [ ] 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
```bash
# 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
```bash
# 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 image
- `appcast.xml` - Update feed with signature
- `Gaze-X.Y.Z.delta` (optional) - Delta update from previous version
### 5. Upload to Hosting (if not using S3 auto-upload)
**DMG File:**
```bash
# Upload to: https://freno.me/downloads/
scp ./releases/Gaze-X.Y.Z.dmg your-server:/path/to/downloads/
```
**Appcast File:**
```bash
# 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:
```bash
# 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:**
1. Install previous version of Gaze
2. Launch app and check Settings → General → Software Updates
3. Click "Check for Updates Now"
4. Verify update notification appears
5. Complete update installation
6. Verify new version launches correctly
**Automatic Update Testing:**
1. Set `SUScheduledCheckInterval` to a low value (e.g., 60 seconds) for testing
2. Install previous version
3. Wait for automatic check
4. 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:
```bash
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.plist` as `SUPublicEDKey`
- **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:
1. Generate new key pair: `./generate_keys`
2. Update `SUPublicEDKey` in Info.plist
3. Release new version with new public key
4. Previous versions won't be able to update (users must manually install)
## Troubleshooting
### Appcast Generation Fails
**Problem:** `generate_appcast` tool not found
**Solution:**
```bash
# 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:**
```bash
# 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/`:
```bash
# 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:
1. Modify Info.plist temporarily:
```xml
<key>SUFeedURL</key>
<string>file:///Users/mike/Code/Gaze/releases/appcast.xml</string>
```
2. Build and run app
3. Check for updates
4. Revert Info.plist before committing
## Release Notes
Release notes are embedded in appcast XML as CDATA:
```xml
<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
- [Sparkle Documentation](https://sparkle-project.org/documentation/)
- [Publishing Updates](https://sparkle-project.org/documentation/publishing/)
- [Sandboxed Apps](https://sparkle-project.org/documentation/sandboxing/)
- [Gaze Repository](https://github.com/YOUR_USERNAME/Gaze)
## Support
For issues with deployment:
1. Check Console.app for Sparkle errors
2. Verify appcast validation with xmllint
3. Test with file:// URL first
4. Check AWS S3 permissions and CORS