mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
356 lines
No EOL
12 KiB
Bash
Executable file
356 lines
No EOL
12 KiB
Bash
Executable file
#!/bin/zsh
|
|
set -e # Exit on any error
|
|
set -o pipefail # Exit if any command in a pipeline fails
|
|
|
|
# Get the project directory
|
|
if [ -z "${SRCROOT}" ]; then
|
|
# If SRCROOT is not set (running outside Xcode), determine it from script location
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
else
|
|
PROJECT_DIR="${SRCROOT}"
|
|
fi
|
|
|
|
WEB_DIR="${PROJECT_DIR}/../web"
|
|
HASH_FILE="${BUILT_PRODUCTS_DIR}/.web-content-hash"
|
|
PREVIOUS_HASH_FILE="${BUILT_PRODUCTS_DIR}/.web-content-hash.previous"
|
|
PUBLIC_DIR="${WEB_DIR}/public"
|
|
|
|
# Set destination directory
|
|
if [ -z "${BUILT_PRODUCTS_DIR}" ]; then
|
|
# Default for testing outside Xcode
|
|
DEST_DIR="/tmp/vibetunnel-web-build"
|
|
else
|
|
DEST_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/web/public"
|
|
fi
|
|
|
|
APP_RESOURCES="${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
|
|
|
# Read the current hash
|
|
if [ -f "${HASH_FILE}" ]; then
|
|
CURRENT_HASH=$(cat "${HASH_FILE}")
|
|
else
|
|
echo "warning: Hash file not found. Forcing full rebuild..."
|
|
CURRENT_HASH="force-rebuild-$(date +%s)"
|
|
fi
|
|
|
|
# Check if we need to rebuild
|
|
NEED_REBUILD=1
|
|
|
|
# Check if previous hash exists and matches current
|
|
if [ -f "${PREVIOUS_HASH_FILE}" ]; then
|
|
PREVIOUS_HASH=$(cat "${PREVIOUS_HASH_FILE}")
|
|
if [ "${CURRENT_HASH}" = "${PREVIOUS_HASH}" ]; then
|
|
# Also check if the built files actually exist
|
|
if [ -d "${DEST_DIR}" ] && [ -f "${APP_RESOURCES}/vibetunnel" ] && [ -f "${APP_RESOURCES}/pty.node" ] && [ -f "${APP_RESOURCES}/spawn-helper" ]; then
|
|
echo "Web content unchanged and build outputs exist. Skipping rebuild."
|
|
NEED_REBUILD=0
|
|
else
|
|
echo "Web content unchanged but build outputs missing. Rebuilding..."
|
|
fi
|
|
else
|
|
echo "Web content changed. Hash: ${PREVIOUS_HASH} -> ${CURRENT_HASH}"
|
|
fi
|
|
else
|
|
echo "No previous build hash found. Building web frontend..."
|
|
fi
|
|
|
|
if [ ${NEED_REBUILD} -eq 0 ]; then
|
|
echo "Skipping web frontend build (no changes detected)"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Building web frontend..."
|
|
|
|
# Setup Node.js PATH (Homebrew, nvm, Volta, fnm)
|
|
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
|
|
# Set environment variable to use clean build environment
|
|
export VIBETUNNEL_BUILD_CLEAN_ENV=true
|
|
source "${SCRIPT_DIR}/node-path-setup.sh"
|
|
|
|
# Export CI to prevent interactive prompts
|
|
export CI=true
|
|
|
|
# Check if pnpm is available
|
|
if ! command -v pnpm &> /dev/null; then
|
|
echo "error: pnpm not found. Please install pnpm"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Using pnpm version: $(pnpm --version)"
|
|
echo "Using Node.js version: $(node --version)"
|
|
|
|
# Check if web directory exists
|
|
if [ ! -d "${WEB_DIR}" ]; then
|
|
echo "error: Web directory not found at ${WEB_DIR}"
|
|
exit 1
|
|
fi
|
|
|
|
# Change to web directory
|
|
cd "${WEB_DIR}"
|
|
|
|
# Clean build artifacts
|
|
echo "Cleaning build artifacts..."
|
|
rm -rf dist public/bundle public/output.css native
|
|
|
|
# Install dependencies
|
|
echo "Installing dependencies..."
|
|
# For Xcode builds, ensure C++20 standard for native modules
|
|
export MACOSX_DEPLOYMENT_TARGET="14.0"
|
|
export CXXFLAGS="-std=c++20 -stdlib=libc++ -mmacosx-version-min=14.0"
|
|
export CXX="${CXX:-clang++}"
|
|
export CC="${CC:-clang}"
|
|
|
|
# Filter common non-actionable warnings from build output
|
|
filter_build_output() {
|
|
# Allow bypassing filter with VERBOSE_BUILD environment variable
|
|
if [ "${VERBOSE_BUILD:-false}" = "true" ]; then
|
|
cat # Pass through unfiltered
|
|
return
|
|
fi
|
|
|
|
local patterns=(
|
|
# C++ compiler warnings from node-gyp builds
|
|
'missing field .* initializer'
|
|
'expanded from macro'
|
|
'converts to incompatible function type'
|
|
'instantiation of function template'
|
|
|
|
# Deprecation warnings
|
|
'is deprecated.*Use.*instead'
|
|
'has been explicitly marked deprecated'
|
|
|
|
# npm/pnpm configuration warnings
|
|
'npm warn Unknown.*config'
|
|
|
|
# Tailwind CSS content configuration warnings
|
|
'warn - Your.*content.*configuration'
|
|
'warn - Pattern:.*\*\.js'
|
|
'warn - See our documentation'
|
|
|
|
# Build tool information messages
|
|
'gyp info spawn args'
|
|
'\.\.\.\/node_modules\/.*install:'
|
|
|
|
# ESBuild bundle size warnings
|
|
'exceeds recommended size limit'
|
|
'This can impact web performance'
|
|
)
|
|
|
|
# Combine patterns with OR operator
|
|
local pattern=$(IFS='|'; echo "${patterns[*]}")
|
|
grep -v -E "$pattern" || true
|
|
}
|
|
|
|
# Run pnpm install with filtered output
|
|
pnpm install --frozen-lockfile 2>&1 | filter_build_output
|
|
|
|
# Determine build configuration
|
|
BUILD_CONFIG="${CONFIGURATION:-Debug}"
|
|
echo "Build configuration: $BUILD_CONFIG"
|
|
|
|
# Check for custom Node.js build
|
|
echo "Searching for custom Node.js builds..."
|
|
|
|
# Find all Node build directories
|
|
if [ -d "${WEB_DIR}/.node-builds" ]; then
|
|
ALL_NODE_DIRS=$(find "${WEB_DIR}/.node-builds" -name "node-v*-minimal" -type d 2>/dev/null | sort -V)
|
|
else
|
|
ALL_NODE_DIRS=""
|
|
fi
|
|
if [ -n "$ALL_NODE_DIRS" ]; then
|
|
echo "Found Node.js build directories:"
|
|
echo "$ALL_NODE_DIRS" | while read -r dir; do
|
|
if [ -f "$dir/out/Release/node" ]; then
|
|
VERSION=$(basename "$dir" | sed 's/node-v\(.*\)-minimal/\1/')
|
|
echo " ✓ $VERSION (complete build)"
|
|
else
|
|
VERSION=$(basename "$dir" | sed 's/node-v\(.*\)-minimal/\1/')
|
|
echo " ✗ $VERSION (incomplete build - missing binary)"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Find directories with complete builds (containing the actual node binary)
|
|
if [ -d "${WEB_DIR}/.node-builds" ]; then
|
|
CUSTOM_NODE_DIR=$(find "${WEB_DIR}/.node-builds" -name "node-v*-minimal" -type d -exec test -f {}/out/Release/node \; -print 2>/dev/null | sort -V | tail -n1)
|
|
else
|
|
CUSTOM_NODE_DIR=""
|
|
fi
|
|
CUSTOM_NODE_PATH="${CUSTOM_NODE_DIR}/out/Release/node"
|
|
|
|
if [ -n "$CUSTOM_NODE_DIR" ]; then
|
|
SELECTED_VERSION=$(basename "$CUSTOM_NODE_DIR" | sed 's/node-v\(.*\)-minimal/\1/')
|
|
echo "Selected custom Node.js v$SELECTED_VERSION"
|
|
else
|
|
echo "No complete custom Node.js builds found"
|
|
fi
|
|
|
|
# Build the web frontend
|
|
if [ "$BUILD_CONFIG" = "Release" ]; then
|
|
echo "Release build - checking for custom Node.js..."
|
|
|
|
# Skip custom Node.js build in CI to avoid timeout
|
|
if [ "${CI:-false}" = "true" ]; then
|
|
echo "CI environment detected - skipping custom Node.js build to avoid timeout"
|
|
echo "The app will be larger than optimal but will build within CI time limits."
|
|
pnpm run build 2>&1 | filter_build_output
|
|
elif [ ! -f "$CUSTOM_NODE_PATH" ]; then
|
|
echo "Custom Node.js not found, building it for optimal size..."
|
|
echo "This will take 10-20 minutes on first run but will be cached."
|
|
node build-custom-node.js --latest 2>&1 | filter_build_output
|
|
if [ -d "${WEB_DIR}/.node-builds" ]; then
|
|
CUSTOM_NODE_DIR=$(find "${WEB_DIR}/.node-builds" -name "node-v*-minimal" -type d -exec test -f {}/out/Release/node \; -print 2>/dev/null | sort -V | tail -n1)
|
|
else
|
|
CUSTOM_NODE_DIR=""
|
|
fi
|
|
CUSTOM_NODE_PATH="${CUSTOM_NODE_DIR}/out/Release/node"
|
|
fi
|
|
|
|
if [ "${CI:-false}" != "true" ] && [ -f "$CUSTOM_NODE_PATH" ]; then
|
|
CUSTOM_NODE_VERSION=$("$CUSTOM_NODE_PATH" --version 2>/dev/null || echo "unknown")
|
|
CUSTOM_NODE_SIZE=$(ls -lh "$CUSTOM_NODE_PATH" 2>/dev/null | awk '{print $5}' || echo "unknown")
|
|
echo "Using custom Node.js for release build:"
|
|
echo " Version: $CUSTOM_NODE_VERSION"
|
|
echo " Size: $CUSTOM_NODE_SIZE (vs ~110MB for standard Node.js)"
|
|
echo " Path: $CUSTOM_NODE_PATH"
|
|
pnpm run build -- --custom-node 2>&1 | filter_build_output
|
|
else
|
|
echo "WARNING: Custom Node.js build failed, using system Node.js"
|
|
echo "The app will be larger than optimal."
|
|
pnpm run build 2>&1 | filter_build_output
|
|
fi
|
|
else
|
|
# Debug build
|
|
if [ -f "$CUSTOM_NODE_PATH" ]; then
|
|
CUSTOM_NODE_VERSION=$("$CUSTOM_NODE_PATH" --version 2>/dev/null || echo "unknown")
|
|
echo "Debug build - found existing custom Node.js $CUSTOM_NODE_VERSION, using it for consistency"
|
|
pnpm run build -- --custom-node 2>&1 | filter_build_output
|
|
else
|
|
echo "Debug build - using system Node.js for faster builds"
|
|
echo "System Node.js: $(node --version)"
|
|
echo "To use custom Node.js in debug builds, run: cd web && node build-custom-node.js --latest"
|
|
pnpm run build 2>&1 | filter_build_output
|
|
fi
|
|
fi
|
|
|
|
# Clean and create destination directory
|
|
echo "Cleaning destination directory..."
|
|
rm -rf "${DEST_DIR}"
|
|
mkdir -p "${DEST_DIR}"
|
|
|
|
# Copy built files to Resources
|
|
echo "Copying web files to app bundle..."
|
|
cp -R "${PUBLIC_DIR}/"* "${DEST_DIR}/"
|
|
|
|
# Copy native executable and modules to app bundle
|
|
NATIVE_DIR="${WEB_DIR}/native"
|
|
|
|
if [ -f "${NATIVE_DIR}/vibetunnel" ]; then
|
|
echo "Copying native executable to app bundle..."
|
|
EXEC_SIZE=$(ls -lh "${NATIVE_DIR}/vibetunnel" | awk '{print $5}')
|
|
echo " Executable size: $EXEC_SIZE"
|
|
cp "${NATIVE_DIR}/vibetunnel" "${APP_RESOURCES}/"
|
|
chmod +x "${APP_RESOURCES}/vibetunnel"
|
|
else
|
|
echo "error: Native executable not found at ${NATIVE_DIR}/vibetunnel"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f "${NATIVE_DIR}/pty.node" ]; then
|
|
echo "Copying pty.node..."
|
|
cp "${NATIVE_DIR}/pty.node" "${APP_RESOURCES}/"
|
|
else
|
|
echo "error: pty.node not found"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f "${NATIVE_DIR}/spawn-helper" ]; then
|
|
echo "Copying spawn-helper..."
|
|
cp "${NATIVE_DIR}/spawn-helper" "${APP_RESOURCES}/"
|
|
chmod +x "${APP_RESOURCES}/spawn-helper"
|
|
else
|
|
echo "error: spawn-helper not found"
|
|
exit 1
|
|
fi
|
|
|
|
# Copy authenticate_pam.node if it exists
|
|
if [ -f "${NATIVE_DIR}/authenticate_pam.node" ]; then
|
|
echo "Copying authenticate_pam.node..."
|
|
cp "${NATIVE_DIR}/authenticate_pam.node" "${APP_RESOURCES}/"
|
|
else
|
|
echo "Warning: authenticate_pam.node not found. PAM authentication may not work."
|
|
fi
|
|
|
|
echo "✓ Native executable and modules copied successfully"
|
|
|
|
# Sanity check: Verify all required binaries are present in the app bundle
|
|
echo "Performing final sanity check..."
|
|
|
|
MISSING_FILES=()
|
|
|
|
# Check for vibetunnel executable
|
|
if [ ! -f "${APP_RESOURCES}/vibetunnel" ]; then
|
|
MISSING_FILES+=("vibetunnel executable")
|
|
fi
|
|
|
|
# Check for pty.node
|
|
if [ ! -f "${APP_RESOURCES}/pty.node" ]; then
|
|
MISSING_FILES+=("pty.node native module")
|
|
fi
|
|
|
|
# Check for spawn-helper (Unix only)
|
|
if [ ! -f "${APP_RESOURCES}/spawn-helper" ]; then
|
|
MISSING_FILES+=("spawn-helper")
|
|
fi
|
|
|
|
# Check if vibetunnel is executable
|
|
if [ -f "${APP_RESOURCES}/vibetunnel" ] && [ ! -x "${APP_RESOURCES}/vibetunnel" ]; then
|
|
MISSING_FILES+=("vibetunnel is not executable")
|
|
fi
|
|
|
|
# Check if spawn-helper is executable
|
|
if [ -f "${APP_RESOURCES}/spawn-helper" ] && [ ! -x "${APP_RESOURCES}/spawn-helper" ]; then
|
|
MISSING_FILES+=("spawn-helper is not executable")
|
|
fi
|
|
|
|
# If any files are missing, fail the build
|
|
if [ ${#MISSING_FILES[@]} -gt 0 ]; then
|
|
echo "error: Build sanity check failed! Missing required files:"
|
|
for file in "${MISSING_FILES[@]}"; do
|
|
echo " - $file"
|
|
done
|
|
echo "Build artifacts in ${NATIVE_DIR}:"
|
|
ls -la "${NATIVE_DIR}" || echo " Directory does not exist"
|
|
echo "App resources in ${APP_RESOURCES}:"
|
|
ls -la "${APP_RESOURCES}/vibetunnel" "${APP_RESOURCES}/pty.node" "${APP_RESOURCES}/spawn-helper" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
|
|
# Verify the executable works
|
|
echo "Verifying vibetunnel executable..."
|
|
echo "Full path: ${APP_RESOURCES}/vibetunnel"
|
|
if "${APP_RESOURCES}/vibetunnel" version &>/dev/null; then
|
|
VERSION_OUTPUT=$("${APP_RESOURCES}/vibetunnel" version 2>&1 | head -1)
|
|
echo "✓ VibeTunnel executable verified: $VERSION_OUTPUT"
|
|
else
|
|
echo "error: VibeTunnel executable failed verification (version command failed)"
|
|
echo "Full executable path: ${APP_RESOURCES}/vibetunnel"
|
|
echo "Checking if file exists and is executable:"
|
|
ls -la "${APP_RESOURCES}/vibetunnel" || echo "File not found!"
|
|
echo "Attempting to run with error output:"
|
|
"${APP_RESOURCES}/vibetunnel" version 2>&1 || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ All sanity checks passed"
|
|
|
|
# Save the current hash as the previous hash for next build
|
|
if [ -f "${HASH_FILE}" ]; then
|
|
cp "${HASH_FILE}" "${PREVIOUS_HASH_FILE}"
|
|
else
|
|
# If hash file doesn't exist, create it with the current hash value
|
|
echo "${CURRENT_HASH}" > "${PREVIOUS_HASH_FILE}"
|
|
fi
|
|
|
|
echo "Web frontend build completed successfully" |