mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-14 12:46:05 +00:00
Improve release scripts and documentation after successful beta 2 release
Major improvements: - Add common.sh library for consistent error handling and logging - Fix hardcoded values in scripts (signing identity, volume names, GitHub repo) - Add comprehensive release documentation with lessons learned - Create Sparkle key management guide - Add clean.sh script for managing build artifacts - Improve error handling and validation across all scripts - Update lint.sh with proper documentation and better error handling - Make generate-appcast.sh fail fast if private key is missing Script improvements: - release.sh: Add GitHub CLI auth check, remote tag validation - notarize-app.sh: Auto-detect signing identity from keychain - create-dmg.sh: Make volume name configurable - generate-appcast.sh: Extract GitHub info from git remote - All scripts: Add proper documentation headers This ensures more reliable and maintainable release process.
This commit is contained in:
parent
b2b340fd1e
commit
ba7d66aa88
9 changed files with 1196 additions and 27 deletions
329
docs/release-guide.md
Normal file
329
docs/release-guide.md
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
# 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 test` (in web/) and Swift tests
|
||||
- [ ] Linting passes: `./scripts/lint.sh`
|
||||
- [ ] 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.
|
||||
230
docs/sparkle-keys.md
Normal file
230
docs/sparkle-keys.md
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
# Sparkle Key Management Guide
|
||||
|
||||
This guide covers the management of EdDSA keys used for signing VibeTunnel updates with the Sparkle framework.
|
||||
|
||||
## Overview
|
||||
|
||||
VibeTunnel uses Sparkle's EdDSA (Ed25519) signatures for secure software updates. This system requires:
|
||||
- A **private key** (kept secret) for signing updates
|
||||
- A **public key** (distributed with the app) for verifying signatures
|
||||
|
||||
## Key Locations
|
||||
|
||||
### Public Key
|
||||
- **Location**: `VibeTunnel/sparkle-public-ed-key.txt`
|
||||
- **Status**: Committed to repository
|
||||
- **Usage**: Embedded in app via `SUPublicEDKey` in Info.plist
|
||||
- **Current Value**: `AGCY8w5vHirVfGGDGc8Szc5iuOqupZSh9pMj/Qs67XI=`
|
||||
|
||||
### Private Key
|
||||
- **Location**: `private/sparkle_private_key`
|
||||
- **Status**: NOT in version control (in .gitignore)
|
||||
- **Usage**: Required for signing updates during release
|
||||
- **Format**: Base64-encoded key data (no comments or headers)
|
||||
|
||||
## Initial Setup
|
||||
|
||||
### For New Team Members
|
||||
|
||||
1. **Request Access**
|
||||
```bash
|
||||
# Contact team lead for secure key transfer
|
||||
# Keys are stored in: Dropbox/Backup/Sparkle-VibeTunnel/
|
||||
```
|
||||
|
||||
2. **Install Private Key**
|
||||
```bash
|
||||
# Create private directory
|
||||
mkdir -p private
|
||||
|
||||
# Add key file (get content from secure backup)
|
||||
echo "BASE64_PRIVATE_KEY_HERE" > private/sparkle_private_key
|
||||
|
||||
# Verify it's ignored by git
|
||||
git status # Should not show private/
|
||||
```
|
||||
|
||||
3. **Verify Setup**
|
||||
```bash
|
||||
# Test signing with your key
|
||||
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
|
||||
any_file.dmg \
|
||||
-f private/sparkle_private_key
|
||||
```
|
||||
|
||||
### For New Projects
|
||||
|
||||
1. **Generate New Keys**
|
||||
```bash
|
||||
# Build Sparkle tools first
|
||||
./scripts/build.sh
|
||||
|
||||
# Generate new key pair
|
||||
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_keys
|
||||
```
|
||||
|
||||
2. **Save Keys**
|
||||
```bash
|
||||
# Copy displayed keys:
|
||||
# Private key: [base64 string]
|
||||
# Public key: [base64 string]
|
||||
|
||||
# Save private key
|
||||
mkdir -p private
|
||||
echo "PRIVATE_KEY_BASE64" > private/sparkle_private_key
|
||||
|
||||
# Save public key
|
||||
echo "PUBLIC_KEY_BASE64" > VibeTunnel/sparkle-public-ed-key.txt
|
||||
```
|
||||
|
||||
3. **Update App Configuration**
|
||||
- Add public key to Info.plist under `SUPublicEDKey`
|
||||
- Commit public key file to repository
|
||||
|
||||
## Key Security
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Never Commit Private Keys**
|
||||
- Private directory is in .gitignore
|
||||
- Double-check before committing
|
||||
|
||||
2. **Secure Backup**
|
||||
- Store in encrypted location
|
||||
- Use password manager or secure cloud storage
|
||||
- Keep multiple secure backups
|
||||
|
||||
3. **Limited Access**
|
||||
- Only release managers need private key
|
||||
- Use secure channels for key transfer
|
||||
- Rotate keys if compromised
|
||||
|
||||
4. **Key Format**
|
||||
- Private key file must contain ONLY the base64 key
|
||||
- No comments, headers, or extra whitespace
|
||||
- Single line of base64 data
|
||||
|
||||
### Example Private Key Format
|
||||
```
|
||||
SMYPxE98bJ5iLdHTLHTqGKZNFcZLgrT5Hyjh79h3TaU=
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "EdDSA signature does not match" Error
|
||||
|
||||
**Cause**: Wrong private key or key format issues
|
||||
|
||||
**Solution**:
|
||||
1. Verify private key matches public key
|
||||
2. Check key file has no extra characters
|
||||
3. Regenerate appcast with correct key
|
||||
|
||||
### "Failed to decode base64 encoded key data"
|
||||
|
||||
**Cause**: Private key file contains comments or headers
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Extract just the key
|
||||
grep -v '^#' your_key_backup.txt | grep -v '^$' > private/sparkle_private_key
|
||||
```
|
||||
|
||||
### Testing Key Pair Match
|
||||
|
||||
```bash
|
||||
# Sign a test file
|
||||
echo "test" > test.txt
|
||||
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
|
||||
test.txt \
|
||||
-f private/sparkle_private_key
|
||||
|
||||
# The signature should generate successfully
|
||||
# Compare with production signatures to verify
|
||||
```
|
||||
|
||||
## Key Rotation
|
||||
|
||||
If keys need to be rotated:
|
||||
|
||||
1. **Generate New Keys**
|
||||
```bash
|
||||
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_keys
|
||||
```
|
||||
|
||||
2. **Update App**
|
||||
- Change `SUPublicEDKey` in Info.plist
|
||||
- Update `sparkle-public-ed-key.txt`
|
||||
- Release new version with new public key
|
||||
|
||||
3. **Transition Period**
|
||||
- Keep old private key for emergency updates
|
||||
- Sign new updates with new key
|
||||
- After all users update, retire old key
|
||||
|
||||
## Integration with Release Process
|
||||
|
||||
The release scripts automatically use the private key:
|
||||
|
||||
1. **generate-appcast.sh**
|
||||
- Expects key at `private/sparkle_private_key`
|
||||
- Fails if key missing or invalid
|
||||
- Signs all DMG files in releases
|
||||
|
||||
2. **release.sh**
|
||||
- Calls generate-appcast.sh after creating DMG
|
||||
- Ensures signatures are created before pushing
|
||||
|
||||
## Recovery Procedures
|
||||
|
||||
### Lost Private Key
|
||||
|
||||
If private key is lost:
|
||||
1. Generate new key pair
|
||||
2. Update app with new public key
|
||||
3. Release update signed with old key (if possible)
|
||||
4. All future updates use new key
|
||||
|
||||
### Compromised Private Key
|
||||
|
||||
If private key is compromised:
|
||||
1. Generate new key pair immediately
|
||||
2. Release security update with new public key
|
||||
3. Notify users of security update
|
||||
4. Revoke compromised key (document publicly)
|
||||
|
||||
## Verification Commands
|
||||
|
||||
### Verify Current Setup
|
||||
```bash
|
||||
# Check public key in app
|
||||
/usr/libexec/PlistBuddy -c "Print :SUPublicEDKey" \
|
||||
build/Build/Products/Release/VibeTunnel.app/Contents/Info.plist
|
||||
|
||||
# Check private key exists
|
||||
ls -la private/sparkle_private_key
|
||||
|
||||
# Test signing
|
||||
./scripts/generate-appcast.sh --dry-run
|
||||
```
|
||||
|
||||
### Verify Release Signatures
|
||||
```bash
|
||||
# Check signature in appcast
|
||||
grep "sparkle:edSignature" appcast-prerelease.xml
|
||||
|
||||
# Manually verify a DMG
|
||||
./build/SourcePackages/artifacts/sparkle/Sparkle/bin/sign_update \
|
||||
build/VibeTunnel-1.0.0.dmg \
|
||||
-f private/sparkle_private_key
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Sparkle Documentation](https://sparkle-project.org/documentation/)
|
||||
- [EdDSA on Wikipedia](https://en.wikipedia.org/wiki/EdDSA)
|
||||
- [Ed25519 Key Security](https://ed25519.cr.yp.to/)
|
||||
|
||||
---
|
||||
|
||||
For questions about key management, contact the release team lead.
|
||||
173
scripts/clean.sh
Executable file
173
scripts/clean.sh
Executable file
|
|
@ -0,0 +1,173 @@
|
|||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# VibeTunnel Build Cleanup Script
|
||||
# =============================================================================
|
||||
#
|
||||
# This script cleans up build artifacts and temporary files to free up disk space.
|
||||
#
|
||||
# USAGE:
|
||||
# ./scripts/clean.sh [options]
|
||||
#
|
||||
# OPTIONS:
|
||||
# --all Clean everything including release DMGs
|
||||
# --keep-dmg Keep release DMG files (default)
|
||||
# --dry-run Show what would be deleted without actually deleting
|
||||
#
|
||||
# FEATURES:
|
||||
# - Removes build directories and DerivedData
|
||||
# - Cleans temporary files and caches
|
||||
# - Preserves release DMGs by default
|
||||
# - Shows disk space freed
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/common.sh" ]] && source "$SCRIPT_DIR/common.sh" || true
|
||||
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Parse arguments
|
||||
CLEAN_ALL=false
|
||||
KEEP_DMG=true
|
||||
DRY_RUN=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--all)
|
||||
CLEAN_ALL=true
|
||||
KEEP_DMG=false
|
||||
shift
|
||||
;;
|
||||
--keep-dmg)
|
||||
KEEP_DMG=true
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Usage: $0 [--all] [--keep-dmg] [--dry-run]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Function to get directory size
|
||||
get_size() {
|
||||
local path="$1"
|
||||
if [[ -d "$path" ]]; then
|
||||
du -sh "$path" 2>/dev/null | awk '{print $1}'
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to remove with dry-run support
|
||||
remove_item() {
|
||||
local item="$1"
|
||||
local description="${2:-$item}"
|
||||
|
||||
if [[ -e "$item" ]]; then
|
||||
local size=$(get_size "$item")
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
print_info "[DRY RUN] Would remove $description ($size)"
|
||||
else
|
||||
print_info "Removing $description ($size)..."
|
||||
rm -rf "$item"
|
||||
print_success "Removed $description"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
print_info "Starting cleanup..."
|
||||
[[ "$DRY_RUN" == "true" ]] && print_warning "DRY RUN MODE - Nothing will be deleted"
|
||||
|
||||
# Get initial disk usage
|
||||
INITIAL_SIZE=$(du -sh . 2>/dev/null | awk '{print $1}')
|
||||
|
||||
# Clean build directories
|
||||
remove_item "build/Build" "Xcode build artifacts"
|
||||
remove_item "build/ModuleCache" "Module cache"
|
||||
remove_item "build/SourcePackages" "Source packages"
|
||||
remove_item "build/dmg-temp" "DMG temporary files"
|
||||
remove_item "DerivedData" "DerivedData"
|
||||
|
||||
# Clean tty-fwd Rust target (but keep the built binaries)
|
||||
if [[ "$CLEAN_ALL" == "true" ]]; then
|
||||
remove_item "tty-fwd/target" "Rust build artifacts"
|
||||
else
|
||||
# Keep the release binaries
|
||||
find tty-fwd/target -type f -name "*.d" -delete 2>/dev/null || true
|
||||
find tty-fwd/target -type f -name "*.rmeta" -delete 2>/dev/null || true
|
||||
find tty-fwd/target -type d -name "incremental" -exec rm -rf {} + 2>/dev/null || true
|
||||
[[ "$DRY_RUN" == "false" ]] && print_success "Cleaned Rust intermediate files"
|
||||
fi
|
||||
|
||||
# Clean SPM build artifacts
|
||||
remove_item ".build" "Swift Package Manager build"
|
||||
|
||||
# Clean user-specific Xcode DerivedData
|
||||
XCODE_DERIVED_DATA="$HOME/Library/Developer/Xcode/DerivedData"
|
||||
if [[ -d "$XCODE_DERIVED_DATA" ]]; then
|
||||
for dir in "$XCODE_DERIVED_DATA"/VibeTunnel-*; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
remove_item "$dir" "Xcode DerivedData for VibeTunnel"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Clean temporary files
|
||||
find . -name ".DS_Store" -delete 2>/dev/null || true
|
||||
find . -name "*.swp" -delete 2>/dev/null || true
|
||||
find . -name "*~" -delete 2>/dev/null || true
|
||||
find . -name "*.tmp" -delete 2>/dev/null || true
|
||||
[[ "$DRY_RUN" == "false" ]] && print_success "Cleaned temporary files"
|
||||
|
||||
# Clean old DMGs (keep latest)
|
||||
if [[ "$KEEP_DMG" == "false" ]]; then
|
||||
remove_item "build/*.dmg" "All DMG files"
|
||||
else
|
||||
# Keep only the latest DMG
|
||||
DMG_COUNT=$(ls -1 build/*.dmg 2>/dev/null | wc -l | tr -d ' ')
|
||||
if [[ $DMG_COUNT -gt 1 ]]; then
|
||||
print_info "Keeping latest DMG, removing older ones..."
|
||||
ls -t build/*.dmg | tail -n +2 | while read dmg; do
|
||||
remove_item "$dmg" "Old DMG: $(basename "$dmg")"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean node_modules if requested
|
||||
if [[ "$CLEAN_ALL" == "true" ]]; then
|
||||
remove_item "web/node_modules" "Node.js dependencies"
|
||||
remove_item "web/.next" "Next.js build cache"
|
||||
fi
|
||||
|
||||
# Clean Python caches
|
||||
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
|
||||
find . -type f -name "*.pyc" -delete 2>/dev/null || true
|
||||
[[ "$DRY_RUN" == "false" ]] && print_success "Cleaned Python caches"
|
||||
|
||||
# Get final disk usage
|
||||
FINAL_SIZE=$(du -sh . 2>/dev/null | awk '{print $1}')
|
||||
|
||||
print_success "Cleanup complete!"
|
||||
print_info "Disk usage: $INITIAL_SIZE → $FINAL_SIZE"
|
||||
|
||||
# Suggest additional cleanups if not using --all
|
||||
if [[ "$CLEAN_ALL" == "false" ]]; then
|
||||
echo ""
|
||||
print_info "For more aggressive cleanup, use: $0 --all"
|
||||
print_info "This will also remove:"
|
||||
print_info " - Release DMG files"
|
||||
print_info " - Node.js dependencies"
|
||||
print_info " - Rust target directory"
|
||||
fi
|
||||
287
scripts/common.sh
Normal file
287
scripts/common.sh
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# VibeTunnel Common Script Library
|
||||
# =============================================================================
|
||||
#
|
||||
# This file provides common functions and utilities for all VibeTunnel scripts
|
||||
# to ensure consistency in error handling, logging, and output formatting.
|
||||
#
|
||||
# USAGE:
|
||||
# Source this file at the beginning of your script:
|
||||
# source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
#
|
||||
# FEATURES:
|
||||
# - Consistent color codes for output
|
||||
# - Error handling and logging functions
|
||||
# - Common validation functions
|
||||
# - Progress indicators
|
||||
# - Platform detection utilities
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
# Color codes for consistent output
|
||||
export RED='\033[0;31m'
|
||||
export GREEN='\033[0;32m'
|
||||
export YELLOW='\033[1;33m'
|
||||
export BLUE='\033[0;34m'
|
||||
export PURPLE='\033[0;35m'
|
||||
export CYAN='\033[0;36m'
|
||||
export NC='\033[0m' # No Color
|
||||
|
||||
# Logging levels
|
||||
export LOG_LEVEL="${LOG_LEVEL:-INFO}"
|
||||
export LOG_DEBUG=0
|
||||
export LOG_INFO=1
|
||||
export LOG_WARN=2
|
||||
export LOG_ERROR=3
|
||||
|
||||
# Get current log level
|
||||
get_log_level() {
|
||||
case "$LOG_LEVEL" in
|
||||
DEBUG) echo $LOG_DEBUG ;;
|
||||
INFO) echo $LOG_INFO ;;
|
||||
WARN) echo $LOG_WARN ;;
|
||||
ERROR) echo $LOG_ERROR ;;
|
||||
*) echo $LOG_INFO ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Logging functions
|
||||
log_debug() {
|
||||
[[ $(get_log_level) -le $LOG_DEBUG ]] && echo -e "${CYAN}[DEBUG]${NC} $*" >&2
|
||||
}
|
||||
|
||||
log_info() {
|
||||
[[ $(get_log_level) -le $LOG_INFO ]] && echo -e "${BLUE}[INFO]${NC} $*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
[[ $(get_log_level) -le $LOG_WARN ]] && echo -e "${YELLOW}[WARN]${NC} $*" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
[[ $(get_log_level) -le $LOG_ERROR ]] && echo -e "${RED}[ERROR]${NC} $*" >&2
|
||||
}
|
||||
|
||||
# Success/failure indicators
|
||||
print_success() {
|
||||
echo -e "${GREEN}✅ $*${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}❌ $*${NC}" >&2
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠️ $*${NC}" >&2
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ️ $*${NC}"
|
||||
}
|
||||
|
||||
# Error handling with cleanup
|
||||
error_exit() {
|
||||
local message="${1:-Unknown error}"
|
||||
local exit_code="${2:-1}"
|
||||
print_error "Error: $message"
|
||||
# Call cleanup function if it exists
|
||||
if declare -f cleanup >/dev/null; then
|
||||
log_debug "Running cleanup function"
|
||||
cleanup
|
||||
fi
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# Trap handler for errors
|
||||
setup_error_trap() {
|
||||
trap 'error_exit "Script failed at line $LINENO"' ERR
|
||||
}
|
||||
|
||||
# Validate required commands
|
||||
require_command() {
|
||||
local cmd="$1"
|
||||
local install_hint="${2:-}"
|
||||
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
print_error "Required command not found: $cmd"
|
||||
[[ -n "$install_hint" ]] && echo " Install with: $install_hint"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate required environment variables
|
||||
require_env_var() {
|
||||
local var_name="$1"
|
||||
local description="${2:-$var_name}"
|
||||
|
||||
if [[ -z "${!var_name:-}" ]]; then
|
||||
print_error "Required environment variable not set: $description"
|
||||
echo " Export $var_name=<value>"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate file exists
|
||||
require_file() {
|
||||
local file="$1"
|
||||
local description="${2:-$file}"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
print_error "Required file not found: $description"
|
||||
echo " Expected at: $file"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate directory exists
|
||||
require_dir() {
|
||||
local dir="$1"
|
||||
local description="${2:-$dir}"
|
||||
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
print_error "Required directory not found: $description"
|
||||
echo " Expected at: $dir"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Platform detection
|
||||
is_macos() {
|
||||
[[ "$OSTYPE" == "darwin"* ]]
|
||||
}
|
||||
|
||||
is_linux() {
|
||||
[[ "$OSTYPE" == "linux"* ]]
|
||||
}
|
||||
|
||||
# Get platform name
|
||||
get_platform() {
|
||||
if is_macos; then
|
||||
echo "macos"
|
||||
elif is_linux; then
|
||||
echo "linux"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# Progress indicator
|
||||
show_progress() {
|
||||
local message="$1"
|
||||
echo -ne "${BLUE}⏳ $message...${NC}\r"
|
||||
}
|
||||
|
||||
end_progress() {
|
||||
local message="$1"
|
||||
local status="${2:-success}"
|
||||
|
||||
# Clear the line
|
||||
echo -ne "\033[2K\r"
|
||||
|
||||
case "$status" in
|
||||
success) print_success "$message" ;;
|
||||
error) print_error "$message" ;;
|
||||
warning) print_warning "$message" ;;
|
||||
*) print_info "$message" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Confirmation prompt
|
||||
confirm() {
|
||||
local prompt="${1:-Are you sure?}"
|
||||
local default="${2:-n}"
|
||||
|
||||
local yn_prompt="[y/N]"
|
||||
[[ "$default" == "y" ]] && yn_prompt="[Y/n]"
|
||||
|
||||
read -p "$prompt $yn_prompt " -n 1 -r
|
||||
echo
|
||||
|
||||
if [[ "$default" == "y" ]]; then
|
||||
[[ ! $REPLY =~ ^[Nn]$ ]]
|
||||
else
|
||||
[[ $REPLY =~ ^[Yy]$ ]]
|
||||
fi
|
||||
}
|
||||
|
||||
# Version comparison
|
||||
version_compare() {
|
||||
# Returns 0 if $1 = $2, 1 if $1 > $2, 2 if $1 < $2
|
||||
if [[ "$1" == "$2" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local IFS=.
|
||||
local i ver1=($1) ver2=($2)
|
||||
|
||||
# Fill empty fields in ver1 with zeros
|
||||
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do
|
||||
ver1[i]=0
|
||||
done
|
||||
|
||||
for ((i=0; i<${#ver1[@]}; i++)); do
|
||||
if [[ -z ${ver2[i]} ]]; then
|
||||
# Fill empty fields in ver2 with zeros
|
||||
ver2[i]=0
|
||||
fi
|
||||
if ((10#${ver1[i]} > 10#${ver2[i]})); then
|
||||
return 1
|
||||
fi
|
||||
if ((10#${ver1[i]} < 10#${ver2[i]})); then
|
||||
return 2
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Safe temporary file/directory creation
|
||||
create_temp_file() {
|
||||
local prefix="${1:-vibetunnel}"
|
||||
mktemp -t "${prefix}.XXXXXX"
|
||||
}
|
||||
|
||||
create_temp_dir() {
|
||||
local prefix="${1:-vibetunnel}"
|
||||
mktemp -d -t "${prefix}.XXXXXX"
|
||||
}
|
||||
|
||||
# Cleanup registration
|
||||
CLEANUP_ITEMS=()
|
||||
|
||||
register_cleanup() {
|
||||
CLEANUP_ITEMS+=("$1")
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
log_debug "Running cleanup for ${#CLEANUP_ITEMS[@]} items"
|
||||
for item in "${CLEANUP_ITEMS[@]}"; do
|
||||
if [[ -f "$item" ]]; then
|
||||
log_debug "Removing file: $item"
|
||||
rm -f "$item"
|
||||
elif [[ -d "$item" ]]; then
|
||||
log_debug "Removing directory: $item"
|
||||
rm -rf "$item"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Set up cleanup trap
|
||||
trap cleanup EXIT
|
||||
|
||||
# Export functions for use in subshells
|
||||
export -f log_debug log_info log_warn log_error
|
||||
export -f print_success print_error print_warning print_info
|
||||
export -f error_exit require_command require_env_var require_file require_dir
|
||||
export -f is_macos is_linux get_platform
|
||||
export -f show_progress end_progress confirm
|
||||
export -f version_compare create_temp_file create_temp_dir
|
||||
export -f register_cleanup cleanup
|
||||
|
||||
# Verify bash version
|
||||
BASH_MIN_VERSION="4.0"
|
||||
if ! version_compare "$BASH_VERSION" "$BASH_MIN_VERSION" || [[ $? -eq 2 ]]; then
|
||||
print_warning "Bash version $BASH_VERSION is older than recommended $BASH_MIN_VERSION"
|
||||
print_warning "Some features may not work as expected"
|
||||
fi
|
||||
|
|
@ -1,9 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
# =============================================================================
|
||||
# VibeTunnel DMG Creation Script
|
||||
# =============================================================================
|
||||
#
|
||||
# This script creates a DMG disk image for VibeTunnel distribution.
|
||||
#
|
||||
# USAGE:
|
||||
# ./scripts/create-dmg.sh <app_path> [output_path]
|
||||
#
|
||||
# ARGUMENTS:
|
||||
# app_path Path to the .app bundle
|
||||
# output_path Path for output DMG (optional, defaults to build/VibeTunnel-<version>.dmg)
|
||||
#
|
||||
# ENVIRONMENT VARIABLES:
|
||||
# DMG_VOLUME_NAME Name for the DMG volume (optional, defaults to app name)
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Script to create a DMG for VibeTunnel
|
||||
# Usage: ./scripts/create-dmg.sh <app_path> [output_path]
|
||||
# Source common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/common.sh" ]] && source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
if [[ $# -lt 1 ]] || [[ $# -gt 2 ]]; then
|
||||
echo "Usage: $0 <app_path> [output_path]"
|
||||
|
|
@ -20,9 +39,11 @@ if [[ ! -d "$APP_PATH" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Get version info
|
||||
# Get app name and version info
|
||||
APP_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleName" "$APP_PATH/Contents/Info.plist" 2>/dev/null || echo "VibeTunnel")
|
||||
VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$APP_PATH/Contents/Info.plist")
|
||||
DMG_NAME="VibeTunnel-${VERSION}.dmg"
|
||||
DMG_NAME="${APP_NAME}-${VERSION}.dmg"
|
||||
DMG_VOLUME_NAME="${DMG_VOLUME_NAME:-$APP_NAME}"
|
||||
|
||||
# Use provided output path or default
|
||||
if [[ $# -eq 2 ]]; then
|
||||
|
|
@ -46,7 +67,7 @@ ln -s /Applications "$DMG_TEMP/Applications"
|
|||
|
||||
# Create DMG
|
||||
hdiutil create \
|
||||
-volname "VibeTunnel" \
|
||||
-volname "$DMG_VOLUME_NAME" \
|
||||
-srcfolder "$DMG_TEMP" \
|
||||
-ov \
|
||||
-format UDZO \
|
||||
|
|
|
|||
|
|
@ -19,9 +19,20 @@ if [ -f "$CONFIG_FILE" ]; then
|
|||
fi
|
||||
|
||||
# Configuration
|
||||
GITHUB_USERNAME="${GITHUB_USERNAME:-amantus-ai}"
|
||||
GITHUB_REPO="${GITHUB_USERNAME}/${GITHUB_REPO:-vibetunnel}"
|
||||
SPARKLE_PRIVATE_KEY_PATH="private/sparkle_private_key"
|
||||
# Try to extract from git remote if not set
|
||||
if [[ -z "${GITHUB_USERNAME:-}" ]] || [[ -z "${GITHUB_REPO:-}" ]]; then
|
||||
GIT_REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [[ "$GIT_REMOTE_URL" =~ github\.com[:/]([^/]+)/([^/]+)(\.git)?$ ]]; then
|
||||
GITHUB_USERNAME="${GITHUB_USERNAME:-${BASH_REMATCH[1]}}"
|
||||
GITHUB_REPO="${GITHUB_REPO:-${BASH_REMATCH[2]}}"
|
||||
else
|
||||
GITHUB_USERNAME="${GITHUB_USERNAME:-amantus-ai}"
|
||||
GITHUB_REPO="${GITHUB_REPO:-vibetunnel}"
|
||||
fi
|
||||
fi
|
||||
|
||||
GITHUB_REPO_FULL="${GITHUB_USERNAME}/${GITHUB_REPO}"
|
||||
SPARKLE_PRIVATE_KEY_PATH="${SPARKLE_PRIVATE_KEY_PATH:-private/sparkle_private_key}"
|
||||
|
||||
# Verify private key exists
|
||||
if [ ! -f "$SPARKLE_PRIVATE_KEY_PATH" ]; then
|
||||
|
|
@ -302,7 +313,7 @@ EOF
|
|||
|
||||
# Main function
|
||||
main() {
|
||||
print_info "Generating appcast files for $GITHUB_REPO"
|
||||
print_info "Generating appcast files for $GITHUB_REPO_FULL"
|
||||
|
||||
# Create temporary directory
|
||||
local temp_dir=$(mktemp -d)
|
||||
|
|
@ -311,13 +322,13 @@ main() {
|
|||
# Fetch all releases from GitHub with error handling
|
||||
print_info "Fetching releases from GitHub..."
|
||||
local releases
|
||||
if ! releases=$(gh api "repos/$GITHUB_REPO/releases" --paginate 2>/dev/null); then
|
||||
if ! releases=$(gh api "repos/$GITHUB_REPO_FULL/releases" --paginate 2>/dev/null); then
|
||||
print_error "Failed to fetch releases from GitHub. Please check your GitHub CLI authentication and network connection."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$releases" ] || [ "$releases" = "[]" ]; then
|
||||
print_warning "No releases found for repository $GITHUB_REPO"
|
||||
print_warning "No releases found for repository $GITHUB_REPO_FULL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -1,28 +1,81 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Swift linting and formatting script
|
||||
# =============================================================================
|
||||
# VibeTunnel Swift Linting and Formatting Script
|
||||
# =============================================================================
|
||||
#
|
||||
# This script runs SwiftFormat and SwiftLint on the VibeTunnel codebase
|
||||
# to ensure consistent code style and catch potential issues.
|
||||
#
|
||||
# USAGE:
|
||||
# ./scripts/lint.sh
|
||||
#
|
||||
# DEPENDENCIES:
|
||||
# - swiftformat (brew install swiftformat)
|
||||
# - swiftlint (brew install swiftlint)
|
||||
#
|
||||
# FEATURES:
|
||||
# - Automatically formats Swift code with SwiftFormat
|
||||
# - Fixes auto-correctable SwiftLint issues
|
||||
# - Reports remaining SwiftLint warnings and errors
|
||||
#
|
||||
# EXIT CODES:
|
||||
# 0 - Success (all checks passed)
|
||||
# 1 - Missing dependencies or linting errors
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )"
|
||||
# Source common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/common.sh" ]] && source "$SCRIPT_DIR/common.sh" || true
|
||||
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Change to project root
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo "Running SwiftFormat..."
|
||||
# Check if project has Swift files
|
||||
if ! find . -name "*.swift" -not -path "./.build/*" -not -path "./build/*" | head -1 | grep -q .; then
|
||||
print_warning "No Swift files found in project"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Run SwiftFormat
|
||||
print_info "Running SwiftFormat..."
|
||||
if command -v swiftformat &> /dev/null; then
|
||||
swiftformat . --verbose
|
||||
if swiftformat . --verbose; then
|
||||
print_success "SwiftFormat completed successfully"
|
||||
else
|
||||
print_error "SwiftFormat encountered errors"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "SwiftFormat not installed. Install with: brew install swiftformat"
|
||||
print_error "SwiftFormat not installed"
|
||||
echo " Install with: brew install swiftformat"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Running SwiftLint..."
|
||||
# Run SwiftLint
|
||||
print_info "Running SwiftLint..."
|
||||
if command -v swiftlint &> /dev/null; then
|
||||
# First run auto-corrections
|
||||
print_info "Applying auto-corrections..."
|
||||
swiftlint --fix
|
||||
swiftlint
|
||||
|
||||
# Then run full lint check
|
||||
print_info "Checking for remaining issues..."
|
||||
if swiftlint; then
|
||||
print_success "SwiftLint completed successfully"
|
||||
else
|
||||
print_warning "SwiftLint found issues that require manual attention"
|
||||
# Don't exit with error as these may be warnings
|
||||
fi
|
||||
else
|
||||
echo "SwiftLint not installed. Install with: brew install swiftlint"
|
||||
print_error "SwiftLint not installed"
|
||||
echo " Install with: brew install swiftlint"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Linting complete!"
|
||||
print_success "Linting complete!"
|
||||
|
|
@ -1,9 +1,34 @@
|
|||
#!/bin/bash
|
||||
# notarize-app.sh - Complete notarization script for VibeTunnel with Sparkle
|
||||
# Handles hardened runtime, proper signing of all components, and notarization
|
||||
|
||||
# =============================================================================
|
||||
# VibeTunnel App Notarization Script
|
||||
# =============================================================================
|
||||
#
|
||||
# This script handles complete notarization for VibeTunnel including:
|
||||
# - Hardened runtime signing
|
||||
# - Proper signing of all components (including Sparkle)
|
||||
# - Apple notarization submission and stapling
|
||||
#
|
||||
# USAGE:
|
||||
# ./scripts/notarize-app.sh <app_path>
|
||||
#
|
||||
# ARGUMENTS:
|
||||
# app_path Path to the .app bundle to notarize
|
||||
#
|
||||
# ENVIRONMENT VARIABLES:
|
||||
# SIGN_IDENTITY Developer ID identity (optional)
|
||||
# APP_STORE_CONNECT_API_KEY_P8 App Store Connect API key
|
||||
# APP_STORE_CONNECT_KEY_ID API Key ID
|
||||
# APP_STORE_CONNECT_ISSUER_ID API Issuer ID
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# Source common functions
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/common.sh" ]] && source "$SCRIPT_DIR/common.sh"
|
||||
|
||||
# ============================================================================
|
||||
# Configuration
|
||||
# ============================================================================
|
||||
|
|
@ -26,9 +51,19 @@ success() {
|
|||
}
|
||||
|
||||
APP_BUNDLE="${1:-build/Build/Products/Release/VibeTunnel.app}"
|
||||
SIGN_IDENTITY="Developer ID Application: Peter Steinberger (Y5PE65HELJ)"
|
||||
# Use environment variable or detect from keychain
|
||||
SIGN_IDENTITY="${SIGN_IDENTITY:-$(security find-identity -v -p codesigning | grep "Developer ID Application" | head -1 | awk -F'"' '{print $2}')}"
|
||||
TIMEOUT_MINUTES=30
|
||||
|
||||
# Validate signing identity
|
||||
if [[ -z "$SIGN_IDENTITY" ]]; then
|
||||
error "No signing identity found. Please set SIGN_IDENTITY environment variable."
|
||||
echo "Example: export SIGN_IDENTITY=\"Developer ID Application: Your Name (TEAMID)\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Using signing identity: $SIGN_IDENTITY"
|
||||
|
||||
# Check if app bundle exists
|
||||
if [ ! -d "$APP_BUNDLE" ]; then
|
||||
error "App bundle not found at $APP_BUNDLE"
|
||||
|
|
|
|||
|
|
@ -155,6 +155,25 @@ if [[ ! -x "$SCRIPT_DIR/notarize-dmg.sh" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Check if GitHub CLI is installed and authenticated
|
||||
if ! command -v gh >/dev/null 2>&1; then
|
||||
echo -e "${RED}❌ Error: GitHub CLI (gh) is not installed${NC}"
|
||||
echo " Install with: brew install gh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! gh auth status >/dev/null 2>&1; then
|
||||
echo -e "${RED}❌ Error: GitHub CLI is not authenticated${NC}"
|
||||
echo " Run: gh auth login"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if changelog file exists
|
||||
if [[ ! -f "$PROJECT_ROOT/CHANGELOG.md" ]]; then
|
||||
echo -e "${YELLOW}⚠️ Warning: CHANGELOG.md not found${NC}"
|
||||
echo " Release notes will be basic"
|
||||
fi
|
||||
|
||||
# Check if we're up to date with origin/main
|
||||
git fetch origin main --quiet
|
||||
LOCAL=$(git rev-parse HEAD)
|
||||
|
|
@ -482,10 +501,21 @@ fi
|
|||
echo ""
|
||||
echo -e "${BLUE}📋 Step 7/9: Creating GitHub release...${NC}"
|
||||
|
||||
# Check if tag already exists
|
||||
# Check if tag already exists locally
|
||||
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}⚠️ Tag $TAG_NAME already exists!${NC}"
|
||||
|
||||
echo -e "${YELLOW}⚠️ Tag $TAG_NAME already exists locally!${NC}"
|
||||
DELETE_EXISTING=true
|
||||
else
|
||||
# Check if tag exists on remote
|
||||
if git ls-remote --tags origin | grep -q "refs/tags/$TAG_NAME"; then
|
||||
echo -e "${YELLOW}⚠️ Tag $TAG_NAME already exists on remote!${NC}"
|
||||
DELETE_EXISTING=true
|
||||
else
|
||||
DELETE_EXISTING=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$DELETE_EXISTING" == "true" ]]; then
|
||||
# Check if a release exists for this tag
|
||||
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
|
||||
echo ""
|
||||
|
|
|
|||
Loading…
Reference in a new issue