diff --git a/mac/scripts/check-node-simple.sh b/mac/scripts/check-node-simple.sh new file mode 100755 index 00000000..4b6a7cba --- /dev/null +++ b/mac/scripts/check-node-simple.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# +# Simplified Node.js check for build process +# +# This is a simpler version that's more robust and easier to debug +# + +set -e + +echo "Checking for Node.js..." + +# Common Node.js locations to check +NODE_PATHS=( + "/opt/homebrew/bin/node" # Homebrew ARM + "/usr/local/bin/node" # Homebrew Intel + "$HOME/.nvm/versions/node/*/bin/node" # NVM (glob) + "$HOME/.volta/bin/node" # Volta + "$HOME/.fnm/node-versions/*/bin/node" # fnm (glob) + "/usr/bin/node" # System +) + +# Find Node.js +NODE_BIN="" +for path in "${NODE_PATHS[@]}"; do + # Handle glob patterns + for expanded in $path; do + if [[ -x "$expanded" ]]; then + NODE_BIN="$expanded" + break 2 + fi + done +done + +# Also check PATH +if [[ -z "$NODE_BIN" ]] && command -v node &>/dev/null; then + NODE_BIN=$(command -v node) +fi + +# Verify Node.js +if [[ -z "$NODE_BIN" ]] || [[ ! -x "$NODE_BIN" ]]; then + echo "❌ Node.js not found!" + echo "" + echo "Please install Node.js v20 or later:" + echo " • Homebrew: brew install node" + echo " • Download: https://nodejs.org/" + echo " • NVM: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash" + echo "" + echo "After installation, restart your terminal and try again." + exit 1 +fi + +# Check version +NODE_VERSION=$("$NODE_BIN" --version 2>/dev/null | cut -d'v' -f2) +NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d'.' -f1) + +echo "✅ Node.js found: $NODE_BIN" +echo " Version: v$NODE_VERSION" + +if [[ "$NODE_MAJOR" -lt 20 ]]; then + echo "⚠️ Warning: Node.js v20+ is recommended (found v$NODE_VERSION)" +fi + +# Check pnpm +echo "" +echo "Checking for pnpm..." + +PNPM_PATHS=( + "$HOME/Library/pnpm/pnpm" # User install + "$HOME/.local/share/pnpm/pnpm" # Linux user install + "/opt/homebrew/bin/pnpm" # Homebrew ARM + "/usr/local/bin/pnpm" # Homebrew Intel +) + +PNPM_BIN="" +for path in "${PNPM_PATHS[@]}"; do + if [[ -x "$path" ]]; then + PNPM_BIN="$path" + break + fi +done + +# Also check PATH +if [[ -z "$PNPM_BIN" ]] && command -v pnpm &>/dev/null; then + PNPM_BIN=$(command -v pnpm) +fi + +if [[ -z "$PNPM_BIN" ]] || [[ ! -x "$PNPM_BIN" ]]; then + echo "❌ pnpm not found!" + echo "" + echo "Please install pnpm:" + echo " • NPM: npm install -g pnpm" + echo " • Homebrew: brew install pnpm" + echo " • Standalone: curl -fsSL https://get.pnpm.io/install.sh | sh -" + exit 1 +fi + +PNPM_VERSION=$("$PNPM_BIN" --version 2>/dev/null) +echo "✅ pnpm found: $PNPM_BIN" +echo " Version: $PNPM_VERSION" + +# Export paths for build scripts +export NODE_PATH="$NODE_BIN" +export PNPM_PATH="$PNPM_BIN" + +# Success +echo "" +echo "✅ All build dependencies found!" +exit 0 \ No newline at end of file diff --git a/mac/scripts/install-node.sh b/mac/scripts/install-node.sh index 6cf96f66..1b58d8ad 100755 --- a/mac/scripts/install-node.sh +++ b/mac/scripts/install-node.sh @@ -17,7 +17,7 @@ fi echo "Checking for Node.js..." # Skip Node.js check in CI when web artifacts are pre-built -if [ "${CI}" = "true" ] && [ "${SKIP_NODE_CHECK}" = "true" ]; then +if [ "${CI:-false}" = "true" ] && [ "${SKIP_NODE_CHECK:-false}" = "true" ]; then echo "✓ Skipping Node.js check in CI (web artifacts are pre-built)" exit 0 fi diff --git a/mac/scripts/node-path-setup.sh b/mac/scripts/node-path-setup.sh index b5bc12d8..ccdc7698 100644 --- a/mac/scripts/node-path-setup.sh +++ b/mac/scripts/node-path-setup.sh @@ -23,7 +23,7 @@ else fi # Verify Node.js is available (skip in CI when using pre-built artifacts) -if [ "${SKIP_NODE_CHECK}" = "true" ] && [ "${CI}" = "true" ]; then +if [ "${SKIP_NODE_CHECK:-false}" = "true" ] && [ "${CI:-false}" = "true" ]; then # In CI with pre-built artifacts, Node.js is not required return 0 2>/dev/null || exit 0 fi diff --git a/mac/scripts/release-health-check.sh b/mac/scripts/release-health-check.sh new file mode 100755 index 00000000..c6bd2872 --- /dev/null +++ b/mac/scripts/release-health-check.sh @@ -0,0 +1,215 @@ +#!/bin/bash +# +# Release Health Check +# +# Comprehensive pre-release validation to catch issues early +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Counters +ERRORS=0 +WARNINGS=0 + +echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ VibeTunnel Release Health Check ║${NC}" +echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}" +echo "" + +# Helper functions +check_pass() { + echo -e "${GREEN}✅ $1${NC}" +} + +check_fail() { + echo -e "${RED}❌ $1${NC}" + ((ERRORS++)) +} + +check_warn() { + echo -e "${YELLOW}⚠️ $1${NC}" + ((WARNINGS++)) +} + +# 1. Git Status +echo -e "${BLUE}1. Git Repository${NC}" +if git diff --quiet && git diff --cached --quiet; then + check_pass "Working directory clean" +else + check_fail "Uncommitted changes detected" +fi + +BRANCH=$(git branch --show-current) +if [[ "$BRANCH" == "main" ]]; then + check_pass "On main branch" +else + check_warn "Not on main branch (current: $BRANCH)" +fi + +if git rev-parse '@{u}' &>/dev/null; then + if git diff '@{u}' --quiet; then + check_pass "Up to date with remote" + else + check_warn "Not synchronized with remote" + fi +else + check_warn "No upstream branch set" +fi + +# 2. Environment Variables +echo "" +echo -e "${BLUE}2. Environment Variables${NC}" + +if [[ -n "${SPARKLE_ACCOUNT:-}" ]]; then + check_pass "SPARKLE_ACCOUNT is set" +else + check_warn "SPARKLE_ACCOUNT not set (run: export SPARKLE_ACCOUNT=\"VibeTunnel\")" +fi + +if [[ -n "${APP_STORE_CONNECT_KEY_ID:-}" ]] && \ + [[ -n "${APP_STORE_CONNECT_ISSUER_ID:-}" ]] && \ + [[ -n "${APP_STORE_CONNECT_API_KEY_P8:-}" ]]; then + check_pass "Notarization credentials configured" +else + check_fail "Notarization credentials missing" +fi + +# 3. Tools & Dependencies +echo "" +echo -e "${BLUE}3. Build Tools${NC}" + +# Node.js +if "$SCRIPT_DIR/check-node-simple.sh" &>/dev/null; then + NODE_VERSION=$(node --version 2>/dev/null || echo "unknown") + check_pass "Node.js found ($NODE_VERSION)" +else + check_fail "Node.js not properly configured" +fi + +# Xcode +if xcodebuild -version &>/dev/null; then + XCODE_VERSION=$(xcodebuild -version | head -1) + check_pass "Xcode found ($XCODE_VERSION)" +else + check_fail "Xcode not found" +fi + +# GitHub CLI +if command -v gh &>/dev/null; then + if gh auth status &>/dev/null; then + check_pass "GitHub CLI authenticated" + else + check_fail "GitHub CLI not authenticated" + fi +else + check_fail "GitHub CLI not installed" +fi + +# Sparkle tools +if command -v sign_update &>/dev/null; then + check_pass "Sparkle tools installed" +else + check_fail "Sparkle sign_update not found" +fi + +# 4. Signing & Certificates +echo "" +echo -e "${BLUE}4. Code Signing${NC}" + +if security find-identity -v -p codesigning | grep -q "Developer ID Application"; then + check_pass "Developer ID certificate found" +else + check_fail "Developer ID certificate not found" +fi + +if [[ -f "$PROJECT_ROOT/private/sparkle_ed_private_key" ]]; then + check_pass "Sparkle private key found" +else + check_fail "Sparkle private key missing" +fi + +# 5. Version Configuration +echo "" +echo -e "${BLUE}5. Version Configuration${NC}" + +if [[ -f "$PROJECT_ROOT/VibeTunnel/version.xcconfig" ]]; then + MARKETING_VERSION=$(grep "MARKETING_VERSION" "$PROJECT_ROOT/VibeTunnel/version.xcconfig" | cut -d= -f2 | tr -d ' ') + BUILD_NUMBER=$(grep "CURRENT_PROJECT_VERSION" "$PROJECT_ROOT/VibeTunnel/version.xcconfig" | cut -d= -f2 | tr -d ' ') + check_pass "Version config found: $MARKETING_VERSION (build $BUILD_NUMBER)" + + # Check web version sync + WEB_VERSION=$(grep '"version"' "$PROJECT_ROOT/../web/package.json" | cut -d'"' -f4) + if [[ "$WEB_VERSION" == "$MARKETING_VERSION" ]]; then + check_pass "Web version synchronized" + else + check_fail "Web version mismatch (web: $WEB_VERSION, mac: $MARKETING_VERSION)" + fi +else + check_fail "version.xcconfig not found" +fi + +# 6. Disk Space +echo "" +echo -e "${BLUE}6. System Resources${NC}" + +AVAILABLE_SPACE=$(df -h . | awk 'NR==2 {print $4}' | sed 's/G.*//') +if [[ $(echo "$AVAILABLE_SPACE > 10" | bc -l) -eq 1 ]]; then + check_pass "Sufficient disk space (${AVAILABLE_SPACE}GB available)" +else + check_warn "Low disk space (${AVAILABLE_SPACE}GB available, recommend >10GB)" +fi + +# 7. Previous Release State +echo "" +echo -e "${BLUE}7. Release State${NC}" + +if [[ -f "$PROJECT_ROOT/.release-state.json" ]]; then + check_warn "Previous release state found - consider cleaning up" +else + check_pass "No stale release state" +fi + +# 8. Appcast Files +echo "" +echo -e "${BLUE}8. Appcast Configuration${NC}" + +if [[ -f "$PROJECT_ROOT/../appcast-prerelease.xml" ]]; then + if xmllint --noout "$PROJECT_ROOT/../appcast-prerelease.xml" 2>/dev/null; then + check_pass "Pre-release appcast valid" + else + check_fail "Pre-release appcast has XML errors" + fi +else + check_fail "Pre-release appcast not found" +fi + +# Summary +echo "" +echo -e "${BLUE}════════════════════════════════════════════${NC}" +echo -e "${BLUE}Summary:${NC}" +echo -e " Errors: ${ERRORS}" +echo -e " Warnings: ${WARNINGS}" + +if [[ $ERRORS -eq 0 ]]; then + echo "" + echo -e "${GREEN}✅ System is ready for release!${NC}" + echo "" + echo "Next steps:" + echo " 1. Set any missing environment variables" + echo " 2. Run: ./scripts/release.sh beta N" + exit 0 +else + echo "" + echo -e "${RED}❌ Please fix the errors above before releasing${NC}" + exit 1 +fi \ No newline at end of file diff --git a/mac/scripts/release-improved.sh b/mac/scripts/release-improved.sh new file mode 100755 index 00000000..6cfc38fb --- /dev/null +++ b/mac/scripts/release-improved.sh @@ -0,0 +1,335 @@ +#!/bin/bash +# +# Improved release script with better state tracking and progress indicators +# +# This script automates the entire release process for VibeTunnel: +# 1. Pre-flight checks +# 2. Clean build directory +# 3. Set version +# 4. Build application +# 5. Sign and notarize +# 6. Create DMG and ZIP +# 7. Create GitHub release +# 8. Update appcast +# 9. Commit and push +# + +set -eo pipefail + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." + +# Source required scripts +source "$SCRIPT_DIR/release-state.sh" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Progress tracking +STEP_START_TIME="" + +start_step() { + local step_num=$1 + local step_name=$2 + STEP_START_TIME=$(date +%s) + echo "" + echo -e "${BLUE}📋 Step $step_num/9: $step_name...${NC}" + update_step "$step_num" "in_progress" +} + +complete_step() { + local step_num=$1 + local step_name=$2 + local duration=$(($(date +%s) - STEP_START_TIME)) + echo -e "${GREEN}✅ $step_name completed (${duration}s)${NC}" + update_step "$step_num" "completed" +} + +fail_step() { + local step_num=$1 + local step_name=$2 + local error_msg=$3 + echo -e "${RED}❌ $step_name failed: $error_msg${NC}" + update_step "$step_num" "failed" + exit 1 +} + +# Show progress with timeout warnings +show_progress() { + local operation=$1 + local timeout_warning=${2:-300} # Default 5 minutes + local start_time=$(date +%s) + local warned=false + + while true; do + local elapsed=$(($(date +%s) - start_time)) + + # Show timeout warning + if [[ $elapsed -gt $timeout_warning ]] && [[ "$warned" == "false" ]]; then + echo -e "${YELLOW}⏰ $operation is taking longer than expected (>${timeout_warning}s)...${NC}" + warned=true + fi + + # Check if process is still running + if ! jobs %1 &>/dev/null; then + break + fi + + # Update progress indicator + printf "." + sleep 5 + done + echo "" # New line after dots +} + +# Parse command line arguments +RELEASE_TYPE="" +RELEASE_NUMBER="" +DRY_RUN=false +RESUME=false +SHOW_STATUS=false + +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + --resume) + RESUME=true + shift + ;; + --status) + SHOW_STATUS=true + shift + ;; + stable|beta|alpha|rc) + RELEASE_TYPE=$1 + shift + if [[ $# -gt 0 && ! "$1" =~ ^-- ]]; then + RELEASE_NUMBER=$1 + shift + fi + ;; + *) + echo -e "${RED}❌ Unknown argument: $1${NC}" + exit 1 + ;; + esac +done + +# Show status if requested +if [[ "$SHOW_STATUS" == "true" ]]; then + show_release_status + exit 0 +fi + +# Main release process +if [[ "$RESUME" == "true" ]]; then + echo -e "${BLUE}📋 Resuming release${NC}" + # Load state and continue from last step + if [[ ! -f "$PROJECT_ROOT/.release-state.json" ]]; then + echo -e "${RED}❌ No release state found to resume${NC}" + exit 1 + fi + + # Check if release is stale (>2 hours old) + LAST_UPDATED=$(jq -r '.last_updated // empty' "$PROJECT_ROOT/.release-state.json") + if [[ -n "$LAST_UPDATED" ]]; then + LAST_TIMESTAMP=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$LAST_UPDATED" +%s 2>/dev/null || date -d "$LAST_UPDATED" +%s) + CURRENT_TIMESTAMP=$(date +%s) + AGE=$((CURRENT_TIMESTAMP - LAST_TIMESTAMP)) + + if [[ $AGE -gt 7200 ]]; then # 2 hours + echo -e "${YELLOW}⚠️ Release state is over 2 hours old${NC}" + echo "Continue anyway? (y/n): " + read -r response + if [[ "$response" != "y" ]]; then + exit 1 + fi + fi + fi + + CURRENT_STEP=$(get_current_step) + echo "Resuming from step $CURRENT_STEP..." +else + # Validate release type and number + if [[ -z "$RELEASE_TYPE" ]]; then + show_usage + exit 1 + fi + + if [[ "$RELEASE_TYPE" != "stable" ]] && [[ -z "$RELEASE_NUMBER" ]]; then + echo -e "${RED}❌ Error: Pre-release number is required for $RELEASE_TYPE releases${NC}" + exit 1 + fi + + # Initialize state + RELEASE_VERSION=$(get_release_version "$RELEASE_TYPE" "$RELEASE_NUMBER") + BUILD_NUMBER=$(get_build_number) + TAG_NAME="v$RELEASE_VERSION" + + init_state "$RELEASE_TYPE" "$RELEASE_VERSION" "$BUILD_NUMBER" "$TAG_NAME" + CURRENT_STEP=1 +fi + +# Execute steps based on current progress +echo -e "${BLUE}🚀 VibeTunnel Release Process${NC}" +echo "==================================" + +# Step 1: Pre-flight check +if [[ $(get_current_step) -le 1 ]]; then + start_step 1 "Pre-flight check" + + # Run the preflight check + if ! "$SCRIPT_DIR/preflight-check.sh"; then + fail_step 1 "Pre-flight check" "Pre-flight checks failed" + fi + + complete_step 1 "Pre-flight check" +fi + +# Step 2: Clean build +if [[ $(get_current_step) -le 2 ]]; then + start_step 2 "Clean build directory" + + "$SCRIPT_DIR/clean.sh" || fail_step 2 "Clean build" "Failed to clean build directory" + + complete_step 2 "Clean build directory" +fi + +# Step 3: Set version +if [[ $(get_current_step) -le 3 ]]; then + start_step 3 "Set version" + + # Version should already be set in version.xcconfig + echo "Version: $RELEASE_VERSION" + echo "Build: $BUILD_NUMBER" + + complete_step 3 "Set version" +fi + +# Step 4: Build application +if [[ $(get_current_step) -le 4 ]]; then + start_step 4 "Build application" + + # Build with progress tracking + ( + export CI=${CI:-false} + export SKIP_NODE_CHECK=${SKIP_NODE_CHECK:-false} + export IS_PRERELEASE_BUILD=$([[ "$RELEASE_TYPE" != "stable" ]] && echo "true" || echo "false") + "$SCRIPT_DIR/build.sh" --configuration Release + ) & + + show_progress "Building application" 600 # 10 minute warning + wait $! || fail_step 4 "Build application" "Build failed" + + complete_step 4 "Build application" +fi + +# Step 5: Sign and notarize +if [[ $(get_current_step) -le 5 ]]; then + start_step 5 "Sign and notarize" + + APP_PATH="$PROJECT_ROOT/build/Build/Products/Release/VibeTunnel.app" + + # Sign and notarize with progress tracking + "$SCRIPT_DIR/sign-and-notarize.sh" "$APP_PATH" & + show_progress "Notarization" 600 # 10 minute warning + wait $! || fail_step 5 "Sign and notarize" "Notarization failed" + + # Update artifact path + update_artifact "app_path" "$APP_PATH" + + complete_step 5 "Sign and notarize" +fi + +# Step 6: Create DMG and ZIP +if [[ $(get_current_step) -le 6 ]]; then + start_step 6 "Create DMG and ZIP" + + DMG_PATH="$PROJECT_ROOT/build/VibeTunnel-$RELEASE_VERSION.dmg" + ZIP_PATH="$PROJECT_ROOT/build/VibeTunnel-$RELEASE_VERSION.zip" + + # Create DMG + "$SCRIPT_DIR/create-dmg.sh" "$APP_PATH" || fail_step 6 "Create DMG" "DMG creation failed" + + # Create ZIP + "$SCRIPT_DIR/create-zip.sh" "$APP_PATH" || fail_step 6 "Create ZIP" "ZIP creation failed" + + # Update artifact paths + update_artifact "dmg_path" "$DMG_PATH" + update_artifact "zip_path" "$ZIP_PATH" + + complete_step 6 "Create DMG and ZIP" +fi + +# Step 7: GitHub release +if [[ $(get_current_step) -le 7 ]]; then + start_step 7 "GitHub release" + + # Generate release notes + RELEASE_NOTES=$("$SCRIPT_DIR/generate-release-notes.sh" "$RELEASE_VERSION") + + # Create GitHub release + gh release create "$TAG_NAME" \ + --title "VibeTunnel $RELEASE_VERSION" \ + --notes "$RELEASE_NOTES" \ + $([[ "$RELEASE_TYPE" != "stable" ]] && echo "--prerelease") \ + "$DMG_PATH" \ + "$ZIP_PATH" || fail_step 7 "GitHub release" "Failed to create GitHub release" + + complete_step 7 "GitHub release" +fi + +# Step 8: Update appcast +if [[ $(get_current_step) -le 8 ]]; then + start_step 8 "Update appcast" + + # Sign DMG for Sparkle + export SPARKLE_ACCOUNT="VibeTunnel" + SPARKLE_SIG=$(sign_update -f "$PROJECT_ROOT/private/sparkle_ed_private_key" "$DMG_PATH" --account VibeTunnel | grep -o 'sparkle:edSignature="[^"]*"' | cut -d'"' -f2) + + if [[ -z "$SPARKLE_SIG" ]]; then + fail_step 8 "Update appcast" "Failed to sign DMG for Sparkle" + fi + + # Update artifact + update_artifact "sparkle_signature" "$SPARKLE_SIG" + + # Update appcast file + "$SCRIPT_DIR/update-appcast.sh" "$RELEASE_VERSION" "$BUILD_NUMBER" "$SPARKLE_SIG" || fail_step 8 "Update appcast" "Failed to update appcast" + + complete_step 8 "Update appcast" +fi + +# Step 9: Commit and push +if [[ $(get_current_step) -le 9 ]]; then + start_step 9 "Commit and push" + + # Add and commit appcast changes + git add ../appcast*.xml + git commit -m "Update appcast for v$RELEASE_VERSION" || true # Ignore if no changes + git push || fail_step 9 "Commit and push" "Failed to push changes" + + complete_step 9 "Commit and push" +fi + +# Clean up state file on success +rm -f "$PROJECT_ROOT/.release-state.json" + +echo "" +echo -e "${GREEN}🎉 Release $RELEASE_VERSION completed successfully!${NC}" +echo "" +echo "Release artifacts:" +echo " - GitHub: https://github.com/amantus-ai/vibetunnel/releases/tag/$TAG_NAME" +echo " - DMG: $DMG_PATH" +echo " - ZIP: $ZIP_PATH" +echo "" +echo "Users on the $([[ "$RELEASE_TYPE" == "stable" ]] && echo "stable" || echo "pre-release") channel will receive the update via Sparkle." \ No newline at end of file diff --git a/mac/scripts/release-improvements.patch b/mac/scripts/release-improvements.patch new file mode 100644 index 00000000..6baa97a0 --- /dev/null +++ b/mac/scripts/release-improvements.patch @@ -0,0 +1,113 @@ +#!/bin/bash +# Release Script Improvements Patch +# Apply with: patch -p0 < release-improvements.patch + +--- release.sh.orig 2025-01-01 00:00:00 ++++ release.sh 2025-01-01 00:00:01 +@@ -77,6 +77,7 @@ + set -euo pipefail + + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + + # Source state management functions + source "$SCRIPT_DIR/release-state.sh" ++source "$SCRIPT_DIR/release-progress.sh" + +@@ -496,10 +497,15 @@ + echo -e "${GREEN}✅ Build would be performed${NC}" + else ++ start_step "Building application" "2-5 minutes" ++ + # Check for custom Node.js build + echo "" + echo "🔍 Checking for custom Node.js build..." + WEB_DIR="$PROJECT_ROOT/../web" + ++ # Update state to in_progress ++ update_step 4 "in_progress" ++ + # Check if .node-builds directory exists + if [[ -d "$WEB_DIR/.node-builds" ]]; then + CUSTOM_NODE_PATH=$(find "$WEB_DIR/.node-builds" -name "node-v*-minimal" -type d 2>/dev/null | sort -V | tail -n1)/out/Release/node +@@ -570,6 +576,9 @@ + fi + fi + ++ update_step 4 "completed" ++ complete_step "Building application" ++ + echo -e "${GREEN}✅ Build complete${NC}" + fi + +@@ -603,7 +612,15 @@ + exit 0 + fi + ++"$SCRIPT_DIR/sign-and-notarize.sh" --sign-and-notarize ++start_step "Signing and notarizing" "10-15 minutes" ++update_step 5 "in_progress" ++ ++# Start timeout warning in background ++timeout_warning "Notarization" 600 $$ & ++TIMEOUT_PID=$! ++ ++"$SCRIPT_DIR/sign-and-notarize.sh" --sign-and-notarize ++ ++# Kill timeout warning ++kill $TIMEOUT_PID 2>/dev/null || true ++ ++update_step 5 "completed" ++complete_step "Signing and notarizing" + + # Verify Sparkle component signing + echo "" +@@ -655,10 +672,17 @@ + # Step 6.5: Notarize DMG + echo "" + echo -e "${BLUE}📋 Notarizing DMG...${NC}" ++start_step "Notarizing DMG" "5-10 minutes" ++update_step_progress 6 "Notarizing DMG..." ++ ++# Start timeout warning in background ++timeout_warning "DMG notarization" 300 $$ & ++TIMEOUT_PID=$! ++ + "$SCRIPT_DIR/notarize-dmg.sh" "$DMG_PATH" ++ ++# Kill timeout warning ++kill $TIMEOUT_PID 2>/dev/null || true ++ ++complete_step "Notarizing DMG" + echo -e "${GREEN}✅ DMG notarized${NC}" + + # Verify DMG notarization +@@ -826,6 +850,9 @@ + git tag -a "$TAG_NAME" -m "Release $RELEASE_VERSION (build $BUILD_NUMBER)" + git push origin "$TAG_NAME" + ++update_step 7 "in_progress" ++update_step_progress 7 "Creating GitHub release..." ++ + # Create release + echo "📤 Creating GitHub release..." + +@@ -891,6 +918,7 @@ + "$ZIP_PATH" + fi + ++update_step 7 "completed" + echo -e "${GREEN}✅ GitHub release created${NC}" + + # Step 7: Update appcast +@@ -962,6 +990,9 @@ + echo -e "${YELLOW}⚠️ Some appcast issues detected. Please review the output above.${NC}" + fi + ++# Clean up state file on success ++cleanup_state ++ ++show_total_time + echo "" + echo -e "${GREEN}🎉 Release Complete!${NC}" + echo "==================" \ No newline at end of file diff --git a/mac/scripts/release-progress.sh b/mac/scripts/release-progress.sh new file mode 100755 index 00000000..2100da0d --- /dev/null +++ b/mac/scripts/release-progress.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# +# Visual release progress display +# +# Shows the current state of a release with visual indicators +# + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$SCRIPT_DIR/.." +STATE_FILE="$PROJECT_ROOT/.release-state.json" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +GRAY='\033[0;90m' +NC='\033[0m' + +# Status icons +ICON_PENDING="⏳" +ICON_PROGRESS="🔄" +ICON_COMPLETE="✅" +ICON_FAILED="❌" + +# Check if state file exists +if [[ ! -f "$STATE_FILE" ]]; then + echo -e "${RED}No active release found${NC}" + exit 1 +fi + +# Parse state +RELEASE_VERSION=$(jq -r '.release_version' "$STATE_FILE") +RELEASE_TYPE=$(jq -r '.release_type' "$STATE_FILE") +STARTED_AT=$(jq -r '.started_at' "$STATE_FILE") +LAST_UPDATED=$(jq -r '.last_updated // empty' "$STATE_FILE") +CURRENT_STEP=$(jq -r '.current_step' "$STATE_FILE") + +# Calculate duration +START_TIMESTAMP=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STARTED_AT" +%s 2>/dev/null || date -d "$STARTED_AT" +%s) +CURRENT_TIMESTAMP=$(date +%s) +DURATION=$((CURRENT_TIMESTAMP - START_TIMESTAMP)) +DURATION_MIN=$((DURATION / 60)) +DURATION_SEC=$((DURATION % 60)) + +# Calculate time since last update +if [[ -n "$LAST_UPDATED" ]]; then + LAST_TIMESTAMP=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$LAST_UPDATED" +%s 2>/dev/null || date -d "$LAST_UPDATED" +%s) + IDLE_TIME=$((CURRENT_TIMESTAMP - LAST_TIMESTAMP)) + IDLE_MIN=$((IDLE_TIME / 60)) +fi + +# Clear screen for clean display +clear + +echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}" +echo -e "${BLUE}║ VibeTunnel Release Progress Monitor ║${NC}" +echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}" +echo "" +echo -e "${CYAN}Version:${NC} $RELEASE_VERSION ($RELEASE_TYPE)" +echo -e "${CYAN}Started:${NC} $STARTED_AT" +echo -e "${CYAN}Duration:${NC} ${DURATION_MIN}m ${DURATION_SEC}s" + +if [[ -n "$LAST_UPDATED" ]] && [[ $IDLE_MIN -gt 5 ]]; then + echo -e "${YELLOW}⚠️ No activity for ${IDLE_MIN} minutes${NC}" +fi + +echo "" +echo -e "${BLUE}Progress:${NC}" +echo "" + +# Display each step +for i in {1..9}; do + STEP_NAME=$(jq -r ".steps.\"$i\".name" "$STATE_FILE") + STEP_STATUS=$(jq -r ".steps.\"$i\".status" "$STATE_FILE") + STEP_STARTED=$(jq -r ".steps.\"$i\".in_progress_at // empty" "$STATE_FILE") + STEP_COMPLETED=$(jq -r ".steps.\"$i\".completed_at // empty" "$STATE_FILE") + + # Choose icon and color + case "$STEP_STATUS" in + pending) + ICON=$ICON_PENDING + COLOR=$GRAY + ;; + in_progress) + ICON=$ICON_PROGRESS + COLOR=$YELLOW + ;; + completed) + ICON=$ICON_COMPLETE + COLOR=$GREEN + ;; + failed) + ICON=$ICON_FAILED + COLOR=$RED + ;; + esac + + # Format step name + STEP_DISPLAY=$(echo "$STEP_NAME" | tr '_' ' ' | sed 's/\b\(.\)/\u\1/g') + + # Calculate step duration if completed + STEP_DURATION="" + if [[ -n "$STEP_STARTED" ]] && [[ -n "$STEP_COMPLETED" ]]; then + START_TS=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STEP_STARTED" +%s 2>/dev/null || date -d "$STEP_STARTED" +%s) + END_TS=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STEP_COMPLETED" +%s 2>/dev/null || date -d "$STEP_COMPLETED" +%s) + DURATION=$((END_TS - START_TS)) + STEP_DURATION=" (${DURATION}s)" + elif [[ "$STEP_STATUS" == "in_progress" ]] && [[ -n "$STEP_STARTED" ]]; then + START_TS=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$STEP_STARTED" +%s 2>/dev/null || date -d "$STEP_STARTED" +%s) + DURATION=$((CURRENT_TIMESTAMP - START_TS)) + STEP_DURATION=" (${DURATION}s...)" + fi + + # Current step indicator + if [[ $i -eq $CURRENT_STEP ]]; then + echo -e "${COLOR}→ $ICON Step $i: $STEP_DISPLAY$STEP_DURATION${NC}" + else + echo -e "${COLOR} $ICON Step $i: $STEP_DISPLAY$STEP_DURATION${NC}" + fi +done + +echo "" + +# Show artifacts if any +DMG_PATH=$(jq -r '.artifacts.dmg_path // empty' "$STATE_FILE") +ZIP_PATH=$(jq -r '.artifacts.zip_path // empty' "$STATE_FILE") +SPARKLE_SIG=$(jq -r '.artifacts.sparkle_signature // empty' "$STATE_FILE") + +if [[ -n "$DMG_PATH" ]] || [[ -n "$ZIP_PATH" ]]; then + echo -e "${BLUE}Artifacts:${NC}" + [[ -n "$DMG_PATH" ]] && [[ -f "$DMG_PATH" ]] && echo -e " ${GREEN}✓${NC} DMG: $(basename "$DMG_PATH")" + [[ -n "$ZIP_PATH" ]] && [[ -f "$ZIP_PATH" ]] && echo -e " ${GREEN}✓${NC} ZIP: $(basename "$ZIP_PATH")" + [[ -n "$SPARKLE_SIG" ]] && echo -e " ${GREEN}✓${NC} Sparkle signature generated" + echo "" +fi + +# Show estimated time for current step +if [[ "$STEP_STATUS" == "in_progress" ]]; then + case "$STEP_NAME" in + build_app) + echo -e "${CYAN}ℹ️ Build typically takes 2-5 minutes${NC}" + ;; + sign_notarize) + echo -e "${CYAN}ℹ️ Notarization typically takes 5-15 minutes${NC}" + ;; + create_dmg_zip) + echo -e "${CYAN}ℹ️ DMG creation typically takes 1-2 minutes${NC}" + ;; + esac +fi + +# Show next steps or completion +if [[ $CURRENT_STEP -gt 9 ]]; then + echo -e "${GREEN}🎉 Release completed successfully!${NC}" +else + echo -e "${BLUE}Next:${NC} Step $CURRENT_STEP will continue automatically" + echo -e "${GRAY}To check status again: ./scripts/release-progress.sh${NC}" + echo -e "${GRAY}To resume if interrupted: ./scripts/release.sh --resume${NC}" +fi \ No newline at end of file diff --git a/mac/scripts/release-state.sh b/mac/scripts/release-state.sh index 042866eb..982e5dc6 100644 --- a/mac/scripts/release-state.sh +++ b/mac/scripts/release-state.sh @@ -93,6 +93,23 @@ update_step() { # Add timestamp jq ".steps.\"$step\".${status}_at = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE" + + # Also update last_updated timestamp + jq ".last_updated = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE" +} + +# Update step progress (for long-running operations) +update_step_progress() { + local step=$1 + local progress_message=$2 + + if [[ ! -f "$STATE_FILE" ]]; then + return 1 + fi + + local tmp=$(mktemp) + jq ".steps.\"$step\".progress = \"$progress_message\"" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE" + jq ".last_updated = \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"" "$STATE_FILE" > "$tmp" && mv "$tmp" "$STATE_FILE" } # Save artifact path diff --git a/web/docs/npm.md b/web/docs/npm.md index 06176644..2cae1f6c 100644 --- a/web/docs/npm.md +++ b/web/docs/npm.md @@ -242,8 +242,6 @@ Development artifacts are excluded from the final package: - Recording files (`*.cast` prevented by .gitignore) - Build artifacts (`dist/` selectively included via package.json `files` field) -**Note**: `screencap.js` is kept as it provides screen capture functionality for the web interface. - ### Size Optimization - **Final size**: ~8.5 MB - **File count**: ~275 files @@ -453,6 +451,30 @@ npm install -g vibetunnel --build-from-source ## Release Notes +### Version 1.0.0-beta.13 (2025-07-19) + +**Published to npm**: Successfully published as both `vibetunnel@beta` and `vibetunnel@latest` + +**Key Features**: +- All features from previous releases maintained +- Updated to match macOS app version 1.0.0-beta.13 +- Full cross-platform support with prebuilt binaries +- Zero-dependency installation experience + +**Package Details**: +- Package size: 15.5 MB (37.1 MB unpacked) +- Contains 235 files including all prebuilds and web assets +- Includes prebuilds for Node.js 20, 22, 23, and 24 + +**Installation**: +```bash +# Install latest (now 1.0.0-beta.13) +npm install -g vibetunnel + +# Or install beta specifically +npm install -g vibetunnel@beta +``` + ### Version 1.0.0-beta.11 (2025-07-16) **Published to npm**: Successfully published as `vibetunnel@beta` @@ -514,7 +536,10 @@ docker run --rm --platform linux/amd64 vibetunnel-test ### Version History -- **1.0.0-beta.11.1** (2025-07-16): Fixed npm installation issues, latest stable release +- **1.0.0-beta.13** (2025-07-19): Latest release, synchronized with macOS app version +- **1.0.0-beta.12.1** (2025-07-17): Minor updates and fixes +- **1.0.0-beta.12** (2025-07-17): Package structure improvements +- **1.0.0-beta.11.1** (2025-07-16): Fixed npm installation issues - **1.0.0-beta.11** (2025-07-16): Initial release with full prebuild system - **1.0.0-beta.10** (2025-07-14): Previous version (unpublished) @@ -523,8 +548,8 @@ docker run --rm --platform linux/amd64 vibetunnel-test VibeTunnel uses npm dist-tags to manage different release channels: ### Current Tags -- **latest**: Points to the most stable release (currently 1.0.0-beta.11.1) -- **beta**: Points to the latest beta release (currently 1.0.0-beta.11.1) +- **latest**: Points to the most stable release (currently 1.0.0-beta.13) +- **beta**: Points to the latest beta release (currently 1.0.0-beta.13) ### Managing Tags @@ -533,10 +558,10 @@ VibeTunnel uses npm dist-tags to manage different release channels: npm dist-tag ls vibetunnel # Set a version as latest -npm dist-tag add vibetunnel@1.0.0-beta.11.1 latest +npm dist-tag add vibetunnel@1.0.0-beta.13 latest # Add a new tag -npm dist-tag add vibetunnel@1.0.0-beta.12 next +npm dist-tag add vibetunnel@1.0.0-beta.14 next # Remove a tag npm dist-tag rm vibetunnel next diff --git a/web/src/server/websocket/control-unix-handler.ts b/web/src/server/websocket/control-unix-handler.ts index 5cc6cea1..83d7d23e 100644 --- a/web/src/server/websocket/control-unix-handler.ts +++ b/web/src/server/websocket/control-unix-handler.ts @@ -148,7 +148,6 @@ export class ControlUnixHandler { // Ignore if already exists } - // Changed from screencap.sock to control.sock this.socketPath = path.join(socketDir, 'control.sock'); // Initialize handlers @@ -381,7 +380,6 @@ export class ControlUnixHandler { logger.log( `🔌 Mac socket status on browser connect: ${this.macSocket ? 'CONNECTED' : 'NOT CONNECTED'}` ); - // The browser connection is established but screencap functionality has been removed ws.on('message', async (data) => { try {