vibetunnel/docs/RELEASE.md
2025-06-17 02:08:59 +02:00

7.9 KiB

VibeTunnel Release Process

This guide explains how to create and publish releases for VibeTunnel, a macOS menu bar application using Sparkle 2.x for automatic updates.

🎯 Release Process Overview

VibeTunnel uses an automated release process that handles all the complexity of:

  • Building and code signing
  • Notarization with Apple
  • Creating DMG disk images
  • Publishing to GitHub
  • Updating Sparkle appcast files

🚀 Creating a Release

Step 1: Pre-flight Check

./scripts/preflight-check.sh

This validates your environment is ready for release.

Step 2: Create/Update CHANGELOG.md

Before creating any release, ensure the CHANGELOG.md file exists and contains a proper section for the version being released. If this is your first release, create a CHANGELOG.md file in the project root:

# Changelog

All notable changes to VibeTunnel will be documented in this file.

## [1.1.0] - 2025-06-10

### 🎨 UI Improvements
- **Enhanced feature** - Description of the improvement
...

CRITICAL: The appcast generation relies on the local CHANGELOG.md file, NOT the GitHub release description. The changelog must be added to CHANGELOG.md BEFORE running the release script.

Step 3: Create the Release

# For stable releases:
./scripts/release.sh stable

# For pre-releases (beta, alpha, rc):
./scripts/release.sh beta 1    # Creates version-beta.1
./scripts/release.sh alpha 2   # Creates version-alpha.2
./scripts/release.sh rc 1      # Creates version-rc.1

IMPORTANT: The release script does NOT automatically increment build numbers. You must manually update the build number in VibeTunnel.xcodeproj before running the script, or it will fail the pre-flight check.

The script will:

  1. Validate build number is unique and incrementing
  2. Build, sign, and notarize the app
  3. Create a DMG
  4. Publish to GitHub
  5. Update the appcast files with EdDSA signatures
  6. Commit and push all changes

Step 4: Verify Success

  • Check the GitHub releases page
  • Verify the appcast was updated correctly with proper changelog content
  • Test updating from a previous version
  • Important: Verify that the Sparkle update dialog shows the formatted changelog, not HTML tags

⚠️ Critical Requirements

1. Build Numbers MUST Increment

Sparkle uses build numbers (CFBundleVersion) to determine updates, NOT version strings!

Version Build Result
1.0.0-beta.1 100
1.0.0-beta.2 101
1.0.0-beta.3 99 Build went backwards
1.0.0 101 Duplicate build number

2. Required Environment Variables

export APP_STORE_CONNECT_KEY_ID="YOUR_KEY_ID"
export APP_STORE_CONNECT_ISSUER_ID="YOUR_ISSUER_ID"
export APP_STORE_CONNECT_API_KEY_P8="-----BEGIN PRIVATE KEY-----
YOUR_PRIVATE_KEY_CONTENT
-----END PRIVATE KEY-----"

3. Prerequisites

  • Xcode 16.4+ installed
  • GitHub CLI authenticated: gh auth status
  • Apple Developer ID certificate in Keychain
  • Sparkle tools in ~/.local/bin/ (sign_update, generate_appcast)

🔐 Sparkle Configuration

Sparkle Requirements for Non-Sandboxed Apps

VibeTunnel is not sandboxed, which simplifies Sparkle configuration:

1. Entitlements (VibeTunnel.entitlements)

<!-- App is NOT sandboxed -->
<key>com.apple.security.app-sandbox</key>
<false/>

<!-- Required for code injection/library validation -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>

2. Info.plist Configuration

"SUEnableInstallerLauncherService": false,  // Not needed for non-sandboxed apps
"SUEnableDownloaderService": false,         // Not needed for non-sandboxed apps

3. Code Signing Requirements

The notarization script handles all signing correctly:

  1. Do NOT use --deep flag when signing the app
  2. Sign the app with hardened runtime and entitlements

The notarize-app.sh script should sign the app:

# Sign the app WITHOUT --deep flag
codesign --force --sign "Developer ID Application" --entitlements VibeTunnel.entitlements --options runtime VibeTunnel.app

Common Sparkle Errors and Solutions

Error Cause Solution
"You're up to date!" when update exists Build number not incrementing Check build numbers in appcast are correct
"Update installation failed" Signing or permission issues Verify app signature and entitlements
"Cannot verify update signature" EdDSA key mismatch Ensure sparkle-public-ed-key.txt matches private key

📋 Update Channels

VibeTunnel supports two update channels:

  1. Stable Channel (appcast.xml)

    • Production releases only
    • Default for all users
  2. Pre-release Channel (appcast-prerelease.xml)

    • Includes beta, alpha, and RC versions
    • Users opt-in via Settings

🐛 Common Issues and Solutions

Appcast Shows HTML Tags Instead of Formatted Text

Problem: Sparkle update dialog shows escaped HTML like &lt;h2&gt; instead of formatted text.

Root Cause: The generate-appcast.sh script is escaping HTML content from GitHub release descriptions.

Solution:

  1. Ensure CHANGELOG.md has the proper section for the release version BEFORE running release script
  2. The appcast should use local CHANGELOG.md, not GitHub release body
  3. If the appcast is wrong, manually fix the generate-appcast.sh script to use local changelog content

Build Numbers Not Incrementing

Problem: Sparkle doesn't detect new version as an update.

Solution: Always increment the build number in the Xcode project before releasing.

🛠️ Manual Process (If Needed)

If the automated script fails, here's the manual process:

1. Update Build Number

Edit the project build settings in Xcode:

  • Open VibeTunnel.xcodeproj
  • Select the project
  • Update CURRENT_PROJECT_VERSION (build number)

2. Clean and Build

rm -rf build DerivedData .build
./scripts/build.sh --configuration Release

3. Sign and Notarize

./scripts/notarize-app.sh build/Build/Products/Release/VibeTunnel.app

4. Create DMG

./scripts/create-dmg.sh

5. Sign DMG for Sparkle

export PATH="$HOME/.local/bin:$PATH"
sign_update build/VibeTunnel-X.X.X.dmg
# Copy the sparkle:edSignature value

6. Create GitHub Release

gh release create "v1.0.0-beta.1" \
  --title "VibeTunnel 1.0.0-beta.1" \
  --notes "Beta release 1" \
  --prerelease \
  build/VibeTunnel-1.0.0-beta.1.dmg

7. Update Appcast

./scripts/update-appcast.sh
git add appcast*.xml
git commit -m "Update appcast for v1.0.0-beta.1"
git push

🔍 Troubleshooting

Debug Sparkle Updates

# Monitor VibeTunnel logs
log stream --predicate 'process == "VibeTunnel"' --level debug

# Check XPC errors
log stream --predicate 'process == "VibeTunnel"' | grep -i -E "(sparkle|xpc|installer)"

# Verify XPC services
codesign -dvv "VibeTunnel.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"

Verify Signing and Notarization

# Check app signature
./scripts/verify-app.sh build/VibeTunnel-1.0.0.dmg

# Verify XPC bundle IDs (should be org.sparkle-project.*)
/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" \
  "VibeTunnel.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc/Contents/Info.plist"

Appcast Issues

# Verify appcast has correct build numbers
./scripts/verify-appcast.sh

# Check if build number is "1" (common bug)
grep '<sparkle:version>' appcast-prerelease.xml

Remember: Always use the automated release script, ensure build numbers increment, and test updates before announcing!