feat: auto-update feature
This commit is contained in:
296
DEPLOYMENT.md
Normal file
296
DEPLOYMENT.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user