mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-27 09:45:53 +00:00
- Add state tracking and resume capability to release process - Create release-state.sh for tracking 9 major release steps - Add --resume and --status flags to release.sh - Fix private key format handling for sign_update tool - Create clean key file (sparkle_ed_private_key) automatically - Handle missing custom Node.js builds gracefully - Add DerivedData app location fallback - Create comprehensive release-checklist.sh script - Update release documentation with critical learnings - Enhance Stats.store documentation with setup instructions - Add troubleshooting for 'Application not found' error - Document fallback options for direct GitHub URLs - Update all scripts to handle clean key file format These improvements make the release process more reliable and resilient to interruptions, with better error handling throughout.
438 lines
No EOL
13 KiB
Bash
Executable file
438 lines
No EOL
13 KiB
Bash
Executable file
#!/bin/bash
|
||
#
|
||
# Release Checklist Script for VibeTunnel
|
||
#
|
||
# This script provides an interactive checklist to ensure all release
|
||
# requirements are met before and during the release process.
|
||
#
|
||
# Usage: ./scripts/release-checklist.sh [version]
|
||
#
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||
|
||
# Color codes
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
# Icons
|
||
CHECK="✓"
|
||
CROSS="✗"
|
||
WARN="⚠"
|
||
INFO="ℹ"
|
||
|
||
# Version argument (optional)
|
||
VERSION="${1:-}"
|
||
|
||
# Function to print colored output
|
||
print_header() {
|
||
echo -e "\n${BLUE}=== $1 ===${NC}\n"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}${CHECK} $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}${CROSS} $1${NC}"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}${WARN} $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${BLUE}${INFO} $1${NC}"
|
||
}
|
||
|
||
# Function to prompt for confirmation
|
||
confirm() {
|
||
local prompt="$1"
|
||
local response
|
||
|
||
echo -en "${YELLOW}$prompt (y/n): ${NC}"
|
||
read -r response
|
||
[[ "$response" =~ ^[Yy]$ ]]
|
||
}
|
||
|
||
# Function to check if command exists
|
||
command_exists() {
|
||
command -v "$1" >/dev/null 2>&1
|
||
}
|
||
|
||
# Function to check file exists
|
||
file_exists() {
|
||
[ -f "$1" ]
|
||
}
|
||
|
||
# Function to check directory exists
|
||
dir_exists() {
|
||
[ -d "$1" ]
|
||
}
|
||
|
||
# Function to get current version from version.xcconfig
|
||
get_current_version() {
|
||
grep "MARKETING_VERSION" "$PROJECT_ROOT/VibeTunnel/version.xcconfig" | cut -d'=' -f2 | tr -d ' '
|
||
}
|
||
|
||
# Function to get current build number
|
||
get_current_build() {
|
||
grep "CURRENT_PROJECT_VERSION" "$PROJECT_ROOT/VibeTunnel/version.xcconfig" | cut -d'=' -f2 | tr -d ' '
|
||
}
|
||
|
||
# Function to check if on main branch
|
||
check_main_branch() {
|
||
local current_branch=$(git branch --show-current)
|
||
[ "$current_branch" = "main" ]
|
||
}
|
||
|
||
# Function to check for uncommitted changes
|
||
check_git_clean() {
|
||
[ -z "$(git status --porcelain)" ]
|
||
}
|
||
|
||
# Function to check if release already exists
|
||
check_release_exists() {
|
||
local version="$1"
|
||
gh release view "v$version" >/dev/null 2>&1
|
||
}
|
||
|
||
# Main checklist
|
||
main() {
|
||
echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
|
||
echo -e "${BLUE}║ VibeTunnel Release Checklist v1.0 ║${NC}"
|
||
echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
|
||
|
||
# Get version info
|
||
local current_version=$(get_current_version)
|
||
local current_build=$(get_current_build)
|
||
|
||
if [ -z "$VERSION" ]; then
|
||
VERSION="$current_version"
|
||
fi
|
||
|
||
print_info "Checking release readiness for version: $VERSION"
|
||
print_info "Current version.xcconfig: $current_version (build $current_build)"
|
||
|
||
local checks_passed=0
|
||
local total_checks=0
|
||
|
||
# ========================================
|
||
print_header "1. Environment & Tools"
|
||
# ========================================
|
||
|
||
# Check Xcode
|
||
((total_checks++))
|
||
if command_exists xcodebuild; then
|
||
print_success "Xcode is installed"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Xcode is not installed"
|
||
fi
|
||
|
||
# Check GitHub CLI
|
||
((total_checks++))
|
||
if command_exists gh; then
|
||
if gh auth status >/dev/null 2>&1; then
|
||
print_success "GitHub CLI is authenticated"
|
||
((checks_passed++))
|
||
else
|
||
print_error "GitHub CLI is not authenticated (run: gh auth login)"
|
||
fi
|
||
else
|
||
print_error "GitHub CLI is not installed"
|
||
fi
|
||
|
||
# Check Sparkle tools
|
||
((total_checks++))
|
||
if command_exists sign_update || [ -f "$HOME/.local/bin/sign_update" ]; then
|
||
print_success "Sparkle tools are available"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Sparkle tools not found (sign_update)"
|
||
fi
|
||
|
||
# Check Node.js
|
||
((total_checks++))
|
||
if command_exists node; then
|
||
print_success "Node.js is installed"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Node.js is not installed"
|
||
fi
|
||
|
||
# Check Bun
|
||
((total_checks++))
|
||
if command_exists bun; then
|
||
print_success "Bun is installed"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Bun is not installed"
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "2. Version Configuration"
|
||
# ========================================
|
||
|
||
# Check version consistency
|
||
((total_checks++))
|
||
if [ "$VERSION" = "$current_version" ]; then
|
||
print_success "Target version matches version.xcconfig"
|
||
((checks_passed++))
|
||
else
|
||
print_warning "Target version ($VERSION) differs from version.xcconfig ($current_version)"
|
||
if confirm "Is this intentional?"; then
|
||
((checks_passed++))
|
||
fi
|
||
fi
|
||
|
||
# Check web package.json version
|
||
((total_checks++))
|
||
local web_version=$(grep '"version"' "$PROJECT_ROOT/../web/package.json" | cut -d'"' -f4)
|
||
local base_web_version="${web_version%-*}" # Remove pre-release suffix
|
||
local base_current_version="${current_version%-*}" # Remove pre-release suffix
|
||
|
||
if [ "$base_web_version" = "$base_current_version" ]; then
|
||
print_success "Web package.json version matches ($web_version)"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Web package.json version ($web_version) doesn't match app version ($current_version)"
|
||
fi
|
||
|
||
# Check if this is a pre-release
|
||
if [[ "$VERSION" =~ -(beta|alpha|rc)\. ]]; then
|
||
print_info "This is a pre-release version"
|
||
else
|
||
print_info "This is a stable release version"
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "3. Git Repository Status"
|
||
# ========================================
|
||
|
||
# Check branch
|
||
((total_checks++))
|
||
if check_main_branch; then
|
||
print_success "On main branch"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Not on main branch (current: $(git branch --show-current))"
|
||
fi
|
||
|
||
# Check for uncommitted changes
|
||
((total_checks++))
|
||
if check_git_clean; then
|
||
print_success "No uncommitted changes"
|
||
((checks_passed++))
|
||
else
|
||
print_warning "Uncommitted changes detected:"
|
||
git status --short
|
||
if confirm "Continue anyway?"; then
|
||
((checks_passed++))
|
||
fi
|
||
fi
|
||
|
||
# Check if release already exists
|
||
((total_checks++))
|
||
if check_release_exists "$VERSION"; then
|
||
print_error "Release v$VERSION already exists on GitHub"
|
||
else
|
||
print_success "Release v$VERSION does not exist yet"
|
||
((checks_passed++))
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "4. Critical Files"
|
||
# ========================================
|
||
|
||
# Check CHANGELOG.md
|
||
((total_checks++))
|
||
local changelog_found=false
|
||
local changelog_location=""
|
||
|
||
if file_exists "$PROJECT_ROOT/CHANGELOG.md"; then
|
||
changelog_found=true
|
||
changelog_location="$PROJECT_ROOT/CHANGELOG.md"
|
||
elif file_exists "$PROJECT_ROOT/../CHANGELOG.md"; then
|
||
changelog_found=true
|
||
changelog_location="$PROJECT_ROOT/../CHANGELOG.md"
|
||
fi
|
||
|
||
if [ "$changelog_found" = true ]; then
|
||
if grep -q "## \[$VERSION\]" "$changelog_location" 2>/dev/null; then
|
||
print_success "CHANGELOG.md has entry for version $VERSION"
|
||
((checks_passed++))
|
||
else
|
||
print_error "CHANGELOG.md missing entry for version $VERSION"
|
||
fi
|
||
else
|
||
print_error "CHANGELOG.md not found"
|
||
fi
|
||
|
||
# Check Sparkle private key (clean version)
|
||
((total_checks++))
|
||
if file_exists "$PROJECT_ROOT/private/sparkle_ed_private_key"; then
|
||
print_success "Clean Sparkle private key exists"
|
||
((checks_passed++))
|
||
elif file_exists "$PROJECT_ROOT/private/sparkle_private_key"; then
|
||
print_warning "Only commented private key exists - clean version will be created"
|
||
((checks_passed++))
|
||
else
|
||
print_error "No Sparkle private key found"
|
||
fi
|
||
|
||
# Check version.xcconfig
|
||
((total_checks++))
|
||
if file_exists "$PROJECT_ROOT/VibeTunnel/version.xcconfig"; then
|
||
print_success "version.xcconfig exists"
|
||
((checks_passed++))
|
||
else
|
||
print_error "version.xcconfig not found"
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "5. Build Configuration"
|
||
# ========================================
|
||
|
||
# Check for custom Node.js build
|
||
((total_checks++))
|
||
if dir_exists "$PROJECT_ROOT/../web/.node-builds"; then
|
||
print_success "Custom Node.js build directory exists"
|
||
print_info "Note: If custom Node.js not found, will fall back to system Node.js"
|
||
((checks_passed++))
|
||
else
|
||
print_warning "No custom Node.js build directory"
|
||
print_info "Release will use system Node.js (larger app size)"
|
||
((checks_passed++))
|
||
fi
|
||
|
||
# Check for stuck DMG volumes
|
||
((total_checks++))
|
||
if ls /Volumes/VibeTunnel* >/dev/null 2>&1; then
|
||
print_warning "Stuck DMG volumes detected"
|
||
if confirm "Unmount them?"; then
|
||
for volume in /Volumes/VibeTunnel*; do
|
||
hdiutil detach "$volume" -force 2>/dev/null || true
|
||
done
|
||
print_success "Volumes unmounted"
|
||
fi
|
||
((checks_passed++))
|
||
else
|
||
print_success "No stuck DMG volumes"
|
||
((checks_passed++))
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "6. Environment Variables"
|
||
# ========================================
|
||
|
||
# Check SPARKLE_ACCOUNT
|
||
((total_checks++))
|
||
if [ -n "${SPARKLE_ACCOUNT:-}" ]; then
|
||
print_success "SPARKLE_ACCOUNT is set: $SPARKLE_ACCOUNT"
|
||
((checks_passed++))
|
||
else
|
||
print_warning "SPARKLE_ACCOUNT not set"
|
||
print_info "Run: export SPARKLE_ACCOUNT=\"VibeTunnel\""
|
||
((checks_passed++))
|
||
fi
|
||
|
||
# Check notarization credentials
|
||
((total_checks++))
|
||
local notary_ok=true
|
||
if [ -z "${APP_STORE_CONNECT_KEY_ID:-}" ]; then
|
||
print_warning "APP_STORE_CONNECT_KEY_ID not set"
|
||
notary_ok=false
|
||
fi
|
||
if [ -z "${APP_STORE_CONNECT_ISSUER_ID:-}" ]; then
|
||
print_warning "APP_STORE_CONNECT_ISSUER_ID not set"
|
||
notary_ok=false
|
||
fi
|
||
if [ -z "${APP_STORE_CONNECT_API_KEY_P8:-}" ]; then
|
||
print_warning "APP_STORE_CONNECT_API_KEY_P8 not set"
|
||
notary_ok=false
|
||
fi
|
||
|
||
if [ "$notary_ok" = true ]; then
|
||
print_success "Notarization credentials are set"
|
||
((checks_passed++))
|
||
else
|
||
print_error "Missing notarization credentials"
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "7. Release State"
|
||
# ========================================
|
||
|
||
# Check for existing release state
|
||
((total_checks++))
|
||
if file_exists "$PROJECT_ROOT/.release-state"; then
|
||
print_warning "Previous release state found"
|
||
print_info "Use './scripts/release.sh --status' to check"
|
||
print_info "Use './scripts/release.sh --resume' to continue"
|
||
if confirm "Clear previous state?"; then
|
||
rm -f "$PROJECT_ROOT/.release-state"
|
||
print_success "State cleared"
|
||
fi
|
||
((checks_passed++))
|
||
else
|
||
print_success "No previous release state"
|
||
((checks_passed++))
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "Summary"
|
||
# ========================================
|
||
|
||
local percentage=$((checks_passed * 100 / total_checks))
|
||
|
||
echo
|
||
echo -e "Checks passed: ${GREEN}$checks_passed${NC} / $total_checks ($percentage%)"
|
||
echo
|
||
|
||
if [ "$checks_passed" -eq "$total_checks" ]; then
|
||
print_success "All checks passed! Ready to release."
|
||
echo
|
||
echo "Next steps:"
|
||
echo "1. Run: export SPARKLE_ACCOUNT=\"VibeTunnel\""
|
||
echo "2. Run: ./scripts/release.sh [release-type] [number]"
|
||
echo "3. Monitor the release process"
|
||
echo "4. If interrupted, use: ./scripts/release.sh --resume"
|
||
else
|
||
print_warning "Some checks need attention."
|
||
echo
|
||
echo "Please address the issues above before proceeding with the release."
|
||
fi
|
||
|
||
# ========================================
|
||
print_header "Quick Commands Reference"
|
||
# ========================================
|
||
|
||
echo "# Set up environment:"
|
||
echo "export SPARKLE_ACCOUNT=\"VibeTunnel\""
|
||
echo
|
||
echo "# Check versions:"
|
||
echo "grep MARKETING_VERSION VibeTunnel/version.xcconfig"
|
||
echo "grep CURRENT_PROJECT_VERSION VibeTunnel/version.xcconfig"
|
||
echo
|
||
echo "# Run release:"
|
||
echo "./scripts/release.sh stable # For stable release"
|
||
echo "./scripts/release.sh beta 10 # For beta.10"
|
||
echo "./scripts/release.sh --resume # Resume interrupted release"
|
||
echo "./scripts/release.sh --status # Check release status"
|
||
echo
|
||
echo "# If release fails, manual recovery:"
|
||
echo "./scripts/build.sh --configuration Release"
|
||
echo "./scripts/sign-and-notarize.sh build/Build/Products/Release/VibeTunnel.app"
|
||
echo "./scripts/create-dmg.sh build/Build/Products/Release/VibeTunnel.app"
|
||
echo "gh release create \"v$VERSION\" --title \"VibeTunnel $VERSION\" --prerelease build/VibeTunnel-*.dmg"
|
||
echo
|
||
}
|
||
|
||
# Run main function
|
||
main "$@" |