diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 88e72057..97bf0a9f 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -128,10 +128,10 @@ jobs: find web-artifacts-temp -type f | head -20 || echo "No files found" # Ensure web directory structure exists - mkdir -p web/dist web/public/bundle + mkdir -p web/dist web/public/bundle web/native web/bin # The artifacts are uploaded without the web/ prefix - # So they're at web-artifacts-temp/dist and web-artifacts-temp/public/bundle + # So they're at web-artifacts-temp/dist, web-artifacts-temp/public/bundle, etc. if [ -d "web-artifacts-temp/dist" ]; then # Copy from the root of artifacts cp -r web-artifacts-temp/dist/* web/dist/ 2>/dev/null || true @@ -141,6 +141,14 @@ jobs: cp -r web-artifacts-temp/public/bundle/* web/public/bundle/ 2>/dev/null || true echo "Copied bundle files" fi + if [ -d "web-artifacts-temp/native" ]; then + cp -r web-artifacts-temp/native/* web/native/ 2>/dev/null || true + echo "Copied native binaries" + fi + if [ -d "web-artifacts-temp/bin" ]; then + cp -r web-artifacts-temp/bin/* web/bin/ 2>/dev/null || true + echo "Copied bin scripts" + fi # Debug: Show what we have echo "=== Web directory structure ===" @@ -149,6 +157,8 @@ jobs: ls -la web/dist/ | head -10 || true echo "=== Bundle contents ===" ls -la web/public/bundle/ | head -10 || true + echo "=== Native contents ===" + ls -la web/native/ | head -10 || true # Clean up temp directory rm -rf web-artifacts-temp @@ -162,6 +172,9 @@ jobs: echo "Web artifacts successfully downloaded and positioned" - name: Resolve Dependencies (once) + env: + CI: "true" # Ensure CI environment variable is set + SKIP_NODE_CHECK: "true" # Skip Node.js check in CI since we download pre-built artifacts run: | echo "Resolving Swift package dependencies..." # Workspace is at root level @@ -175,6 +188,9 @@ jobs: - name: Build Debug timeout-minutes: 10 id: build + env: + CI: "true" # Ensure CI environment variable is set for build scripts + SKIP_NODE_CHECK: "true" # Skip Node.js check in CI since we download pre-built artifacts run: | # Always use Debug for now to match test expectations BUILD_CONFIG="Debug" @@ -218,6 +234,8 @@ jobs: - name: Run tests with coverage id: test-coverage timeout-minutes: 15 + env: + SKIP_NODE_CHECK: "true" # Skip Node.js check in CI since we download pre-built artifacts run: | # Debug: Check if web build artifacts were downloaded echo "=== Checking web build artifacts ===" diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index b7945b92..cc780e7c 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -281,6 +281,8 @@ jobs: path: | web/dist/ web/public/bundle/ + web/native/ + web/bin/vt retention-days: 1 if-no-files-found: error diff --git a/mac/VibeTunnel/Presentation/Views/Settings/CLIInstallationSection.swift b/mac/VibeTunnel/Presentation/Views/Settings/CLIInstallationSection.swift index fcc662bd..f67f3467 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/CLIInstallationSection.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/CLIInstallationSection.swift @@ -55,7 +55,7 @@ struct CLIInstallationSection: View { } .buttonStyle(.bordered) .disabled(cliInstaller.isInstalling) - + Button(action: { Task { await cliInstaller.uninstall() @@ -88,7 +88,7 @@ struct CLIInstallationSection: View { .foregroundColor(.accentColor) .help("Reinstall CLI tool") } - + Button(action: { Task { await cliInstaller.uninstall() diff --git a/mac/VibeTunnel/Utilities/CLIInstaller.swift b/mac/VibeTunnel/Utilities/CLIInstaller.swift index b8e8942c..d9f39d66 100644 --- a/mac/VibeTunnel/Utilities/CLIInstaller.swift +++ b/mac/VibeTunnel/Utilities/CLIInstaller.swift @@ -369,7 +369,8 @@ final class CLIInstaller { logger.debug("Could not read error output: \(error.localizedDescription)") errorString = "Unknown error (could not read stderr)" } - logger.error("CLIInstaller: Uninstallation failed with status \(task.terminationStatus): \(errorString)") + logger + .error("CLIInstaller: Uninstallation failed with status \(task.terminationStatus): \(errorString)") lastError = "Uninstallation failed: \(errorString)" isInstalling = false isUninstalling = false diff --git a/mac/VibeTunnel/Utilities/TerminalLauncher.swift b/mac/VibeTunnel/Utilities/TerminalLauncher.swift index 1ec3ca0a..c5ec84cd 100644 --- a/mac/VibeTunnel/Utilities/TerminalLauncher.swift +++ b/mac/VibeTunnel/Utilities/TerminalLauncher.swift @@ -145,6 +145,12 @@ enum Terminal: String, CaseIterable { allCases.filter(\.isInstalled) } + /// Check if a specific terminal application is currently running + static func isTerminalRunning(_ terminal: Self) -> Bool { + let runningApps = NSWorkspace.shared.runningApplications + return runningApps.contains { $0.bundleIdentifier == terminal.bundleIdentifier } + } + /// Generate unified AppleScript for all terminals func unifiedAppleScript(for config: TerminalLaunchConfig) -> String { // Terminal.app supports 'do script' which handles complex commands better @@ -201,6 +207,40 @@ enum Terminal: String, CaseIterable { """ } + // Special handling for Ghostty with dynamic delays based on running state + if self == .ghostty { + let isRunning = Self.isTerminalRunning(.ghostty) + let startupDelay = isRunning ? "0.5" : "2.0" // Longer delay for cold start + + return """ + tell application "\(processName)" + activate + -- Wait longer if Ghostty wasn't already running + delay 0.2 + set windowCount to 0 + try + set windowCount to count of windows + end try + if windowCount = 0 then + -- No windows open, need extra time for UI initialization + delay \(startupDelay) + end if + tell application "System Events" + tell process "\(processName)" + -- Create new window + keystroke "n" using {command down} + delay 0.5 + -- Paste command from clipboard + keystroke "v" using {command down} + delay 0.1 + -- Execute the command + key code 36 + end tell + end tell + end tell + """ + } + // For other terminals, Cmd+N typically creates a new window return """ tell application "\(processName)" diff --git a/mac/scripts/build-web-frontend.sh b/mac/scripts/build-web-frontend.sh index 748fc391..ab5aa330 100755 --- a/mac/scripts/build-web-frontend.sh +++ b/mac/scripts/build-web-frontend.sh @@ -26,6 +26,54 @@ fi APP_RESOURCES="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +# In CI with pre-built artifacts, skip the entire build process +if [ "${CI}" = "true" ] && [ -f "${WEB_DIR}/dist/server/server.js" ]; then + echo "✓ CI environment detected with pre-built web artifacts" + echo "✓ Skipping web frontend build entirely" + + # Still need to copy the pre-built files to the app bundle + # Clean and create destination directory + echo "Cleaning destination directory..." + rm -rf "${DEST_DIR}" + mkdir -p "${DEST_DIR}" + + # Copy built files to Resources + echo "Copying pre-built web files to app bundle..." + if [ -d "${PUBLIC_DIR}" ]; then + cp -R "${PUBLIC_DIR}/"* "${DEST_DIR}/" + fi + + # Copy native executable and modules to app bundle if they exist + NATIVE_DIR="${WEB_DIR}/native" + + if [ -f "${NATIVE_DIR}/vibetunnel" ]; then + echo "Copying native executable to app bundle..." + cp "${NATIVE_DIR}/vibetunnel" "${APP_RESOURCES}/" + chmod +x "${APP_RESOURCES}/vibetunnel" + fi + + if [ -f "${NATIVE_DIR}/pty.node" ]; then + cp "${NATIVE_DIR}/pty.node" "${APP_RESOURCES}/" + fi + + if [ -f "${NATIVE_DIR}/spawn-helper" ]; then + cp "${NATIVE_DIR}/spawn-helper" "${APP_RESOURCES}/" + chmod +x "${APP_RESOURCES}/spawn-helper" + fi + + if [ -f "${NATIVE_DIR}/authenticate_pam.node" ]; then + cp "${NATIVE_DIR}/authenticate_pam.node" "${APP_RESOURCES}/" + fi + + if [ -f "${WEB_DIR}/bin/vt" ]; then + cp "${WEB_DIR}/bin/vt" "${APP_RESOURCES}/" + chmod +x "${APP_RESOURCES}/vt" + fi + + echo "✓ Pre-built web artifacts copied successfully" + exit 0 +fi + # Read the current hash if [ -f "${HASH_FILE}" ]; then CURRENT_HASH=$(cat "${HASH_FILE}") @@ -71,7 +119,13 @@ source "${SCRIPT_DIR}/node-path-setup.sh" # Export CI to prevent interactive prompts export CI=true -# Check if pnpm is available +# Check if pnpm is available (skip in CI when web artifacts are pre-built) +if [ "${SKIP_NODE_CHECK}" = "true" ] && [ "${CI}" = "true" ]; then + echo "✓ Skipping pnpm check in CI (web artifacts are pre-built)" + echo "✓ This script should not be running in CI - web build should already be complete" + exit 0 +fi + if ! command -v pnpm &> /dev/null; then echo "error: pnpm not found. Please install pnpm" exit 1 diff --git a/mac/scripts/install-node.sh b/mac/scripts/install-node.sh index 5683ced4..6cf96f66 100755 --- a/mac/scripts/install-node.sh +++ b/mac/scripts/install-node.sh @@ -16,6 +16,12 @@ 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 + echo "✓ Skipping Node.js check in CI (web artifacts are pre-built)" + exit 0 +fi + # Load Node.js environment managers (Homebrew, nvm, Volta, fnm) source "${SCRIPT_DIR}/node-path-setup.sh" diff --git a/mac/scripts/node-path-setup.sh b/mac/scripts/node-path-setup.sh index e742a795..b5bc12d8 100644 --- a/mac/scripts/node-path-setup.sh +++ b/mac/scripts/node-path-setup.sh @@ -22,7 +22,12 @@ else export PATH="/opt/homebrew/bin:/usr/local/bin:$HOME/.volta/bin:$HOME/Library/pnpm:$HOME/.bun/bin:$PATH" fi -# Verify Node.js is available +# Verify Node.js is available (skip in CI when using pre-built artifacts) +if [ "${SKIP_NODE_CHECK}" = "true" ] && [ "${CI}" = "true" ]; then + # In CI with pre-built artifacts, Node.js is not required + return 0 2>/dev/null || exit 0 +fi + if ! command -v node >/dev/null 2>&1; then echo "error: Node.js not found. Install via: brew install node" >&2 return 1 2>/dev/null || exit 1