feat(ios): add iOS deployment support and web assets parity

Add comprehensive iOS build and deployment infrastructure with command-line
tooling, documentation, and web assets synchronization.

Changes:
- Update Capacitor dependencies to v6.0 in podspec
- Add iOS build support to build-native.sh with NVM integration
- Sync iOS web assets to match www/ source directory
- Create deployment scripts for both native iOS app and Vue 3 test app
- Add comprehensive iOS simulator deployment documentation
- Document web assets parity requirements between Android and iOS

This enables:
- Command-line iOS builds without Xcode UI
- Automated deployment to iOS simulators
- Consistent web assets across platforms
- Clear separation between native iOS app (ios/App) and Vue 3 test app

Files modified:
- ios/DailyNotificationPlugin.podspec (Capacitor 6.0)
- ios/App/App/public/index.html (synced from www/)
- scripts/build-native.sh (iOS build support)

Files added:
- docs/WEB_ASSETS_PARITY.md
- docs/standalone-ios-simulator-guide.md
- scripts/build-and-deploy-native-ios.sh
- test-apps/daily-notification-test/docs/IOS_BUILD_QUICK_REFERENCE.md
- test-apps/daily-notification-test/scripts/build-and-deploy-ios.sh
This commit is contained in:
Matthew Raymer
2025-11-04 01:40:38 -08:00
parent c4b7f6382f
commit 4be87acc14
12 changed files with 2325 additions and 108 deletions

View File

@@ -0,0 +1,161 @@
#!/bin/bash
# Native iOS Development App Build and Deploy Script
# Builds and deploys the ios/App development app to iOS Simulator
# Similar to android/app - a simple Capacitor app for plugin development
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Check if we're in the project root
if [ ! -d "$PROJECT_ROOT/ios/App" ]; then
log_error "ios/App directory not found"
log_info "This script must be run from the project root directory"
log_info "Usage: cd /path/to/daily-notification-plugin && ./scripts/build-and-deploy-native-ios.sh"
exit 1
fi
# Check prerequisites
log_step "Checking prerequisites..."
if ! command -v xcodebuild &> /dev/null; then
log_error "xcodebuild not found. Install Xcode command line tools:"
log_info " xcode-select --install"
exit 1
fi
if ! command -v pod &> /dev/null; then
log_error "CocoaPods not found. Install with:"
log_info " gem install cocoapods"
exit 1
fi
# Get simulator device (default to iPhone 15 Pro)
SIMULATOR_DEVICE="${1:-iPhone 15 Pro}"
log_info "Using simulator: $SIMULATOR_DEVICE"
# Boot simulator
log_step "Booting simulator..."
if xcrun simctl list devices | grep -q "$SIMULATOR_DEVICE.*Booted"; then
log_info "Simulator already booted"
else
# Try to boot the device
if xcrun simctl boot "$SIMULATOR_DEVICE" 2>/dev/null; then
log_info "✓ Simulator booted"
else
log_warn "Could not boot simulator automatically"
log_info "Opening Simulator app... (you may need to select device manually)"
open -a Simulator
sleep 5
fi
fi
# Build plugin
log_step "Building plugin..."
cd "$PROJECT_ROOT"
if ! ./scripts/build-native.sh --platform ios; then
log_error "Plugin build failed"
exit 1
fi
# Install CocoaPods dependencies
log_step "Installing CocoaPods dependencies..."
cd "$PROJECT_ROOT/ios"
if [ ! -f "Podfile.lock" ] || [ "Podfile" -nt "Podfile.lock" ]; then
pod install
else
log_info "CocoaPods dependencies up to date"
fi
# Build iOS app
log_step "Building native iOS development app..."
cd "$PROJECT_ROOT/ios/App"
WORKSPACE="App.xcworkspace"
SCHEME="App"
CONFIG="Debug"
SDK="iphonesimulator"
if ! xcodebuild -workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk "$SDK" \
-destination "platform=iOS Simulator,name=$SIMULATOR_DEVICE" \
-derivedDataPath build/derivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
clean build; then
log_error "iOS app build failed"
exit 1
fi
# Find built app
APP_PATH=$(find build/derivedData -name "*.app" -type d -path "*/Build/Products/*-iphonesimulator/*.app" | head -1)
if [ -z "$APP_PATH" ]; then
log_error "Could not find built app"
log_info "Searching in: build/derivedData"
find build/derivedData -name "*.app" -type d 2>/dev/null | head -5
exit 1
fi
log_info "Found app: $APP_PATH"
# Install app on simulator
log_step "Installing app on simulator..."
if xcrun simctl install booted "$APP_PATH"; then
log_info "✓ App installed"
else
log_error "Failed to install app"
exit 1
fi
# Get bundle identifier
BUNDLE_ID=$(plutil -extract CFBundleIdentifier raw App/Info.plist 2>/dev/null || echo "com.timesafari.dailynotification")
log_info "Bundle ID: $BUNDLE_ID"
# Launch app
log_step "Launching app..."
if xcrun simctl launch booted "$BUNDLE_ID"; then
log_info "✓ App launched"
else
log_warn "App may already be running"
fi
log_info ""
log_info "✅ Build and deploy complete!"
log_info ""
log_info "To view logs:"
log_info " xcrun simctl spawn booted log stream"
log_info ""
log_info "To uninstall app:"
log_info " xcrun simctl uninstall booted $BUNDLE_ID"
log_info ""
log_info "Note: This is the native iOS development app (ios/App)"
log_info "For the Vue 3 test app, use: test-apps/daily-notification-test/scripts/build-and-deploy-ios.sh"

View File

@@ -7,6 +7,7 @@ set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
@@ -22,6 +23,10 @@ log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Validation functions
check_command() {
if ! command -v $1 &> /dev/null; then
@@ -31,9 +36,68 @@ check_command() {
}
check_environment() {
# Check for required tools
local platform=$1
# Initialize NVM if available (for Node.js version management)
if [ -s "$HOME/.nvm/nvm.sh" ]; then
log_info "Loading NVM..."
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
# Use default Node.js version if available
if [ -f "$NVM_DIR/alias/default" ]; then
DEFAULT_NODE=$(cat "$NVM_DIR/alias/default")
if [ -n "$DEFAULT_NODE" ]; then
nvm use default >/dev/null 2>&1 || true
fi
fi
# Use latest LTS if no default
if ! command -v node &> /dev/null; then
log_info "No default Node.js version set, using latest LTS..."
nvm use --lts >/dev/null 2>&1 || nvm install --lts >/dev/null 2>&1 || true
fi
fi
# Common checks
check_command "node"
check_command "npm"
# Check Node.js version
NODE_VERSION=$(node -v | cut -d. -f1 | tr -d 'v')
if [ -z "$NODE_VERSION" ] || ! [[ "$NODE_VERSION" =~ ^[0-9]+$ ]]; then
log_error "Could not determine Node.js version"
log_error "Please install Node.js: nvm install --lts (if using NVM)"
exit 1
fi
if [ "$NODE_VERSION" -lt 14 ]; then
log_error "Node.js version 14 or higher is required (found: $NODE_VERSION)"
exit 1
fi
# Platform-specific checks
case $platform in
"android")
check_environment_android
;;
"ios")
check_environment_ios
;;
"all")
check_environment_android
check_environment_ios
;;
*)
log_error "Invalid platform: $platform"
exit 1
;;
esac
}
check_environment_android() {
log_step "Checking Android environment..."
check_command "java"
# Check for Gradle Wrapper instead of system gradle
@@ -41,31 +105,98 @@ check_environment() {
log_error "Gradle wrapper not found at android/gradlew"
exit 1
fi
# Check Node.js version
NODE_VERSION=$(node -v | cut -d. -f1 | tr -d 'v')
if [ "$NODE_VERSION" -lt 14 ]; then
log_error "Node.js version 14 or higher is required"
# Check Java version (more robust parsing)
JAVA_VERSION_OUTPUT=$(java -version 2>&1 | head -n 1)
if [ -z "$JAVA_VERSION_OUTPUT" ]; then
log_error "Could not determine Java version"
exit 1
fi
# Check Java version
JAVA_VERSION=$(java -version 2>&1 | head -n 1 | cut -d'"' -f2 | cut -d. -f1)
# Try multiple parsing methods for different Java output formats
JAVA_VERSION=$(echo "$JAVA_VERSION_OUTPUT" | grep -oE 'version "([0-9]+)' | grep -oE '[0-9]+' | head -1)
# Fallback: try to extract from "openjdk version" or "java version" format
if [ -z "$JAVA_VERSION" ]; then
JAVA_VERSION=$(echo "$JAVA_VERSION_OUTPUT" | sed -E 's/.*version "([0-9]+).*/\1/' | head -1)
fi
# Validate we got a number
if [ -z "$JAVA_VERSION" ] || ! [[ "$JAVA_VERSION" =~ ^[0-9]+$ ]]; then
log_error "Could not parse Java version from: $JAVA_VERSION_OUTPUT"
log_error "Please ensure Java is installed correctly"
exit 1
fi
if [ "$JAVA_VERSION" -lt 11 ]; then
log_error "Java version 11 or higher is required"
log_error "Java version 11 or higher is required (found: $JAVA_VERSION)"
exit 1
fi
# Check for Android SDK
if [ -z "$ANDROID_HOME" ]; then
log_error "ANDROID_HOME environment variable is not set"
log_error "Set it with: export ANDROID_HOME=/path/to/android/sdk"
exit 1
fi
log_info "✓ Android environment OK (Java $JAVA_VERSION)"
}
check_environment_ios() {
log_step "Checking iOS environment..."
# Check for Xcode command line tools
if ! command -v xcodebuild &> /dev/null; then
log_error "xcodebuild not found. Install Xcode Command Line Tools:"
log_error " xcode-select --install"
exit 1
fi
# Check for CocoaPods
if ! command -v pod &> /dev/null; then
log_error "CocoaPods not found. Install with:"
log_error " sudo gem install cocoapods"
exit 1
fi
# Check for Swift
if ! command -v swift &> /dev/null; then
log_error "Swift compiler not found"
exit 1
fi
# Verify workspace exists
if [ ! -d "ios/DailyNotificationPlugin.xcworkspace" ]; then
log_error "iOS workspace not found: ios/DailyNotificationPlugin.xcworkspace"
exit 1
fi
log_info "✓ iOS environment OK"
}
# Build functions
build_typescript() {
log_info "Building TypeScript..."
# Ensure npm dependencies are installed
if [ ! -d "node_modules" ]; then
log_step "Installing npm dependencies..."
if ! npm install; then
log_error "Failed to install npm dependencies"
exit 1
fi
else
# Check if package.json changed (compare with package-lock.json)
if [ -f "package-lock.json" ] && [ "package.json" -nt "package-lock.json" ]; then
log_step "package.json changed, updating dependencies..."
if ! npm install; then
log_error "Failed to update npm dependencies"
exit 1
fi
fi
fi
npm run clean
if ! npm run build; then
log_error "TypeScript build failed"
@@ -149,33 +280,6 @@ build_android() {
# =============================================================================
# AUTOMATIC FIX: capacitor.build.gradle for Plugin Development Projects
# =============================================================================
#
# PROBLEM: The capacitor.build.gradle file is auto-generated by Capacitor CLI
# and includes a line that tries to load a file that doesn't exist in plugin
# development projects:
# apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
#
# WHY THIS HAPPENS:
# - This file is generated by 'npx cap sync', 'npx cap update', etc.
# - It assumes a full Capacitor app with proper plugin integration
# - Plugin development projects don't have the full Capacitor setup
#
# THE FIX:
# - Comment out the problematic line to prevent build failures
# - Add explanatory comment about why it's commented out
# - This fix gets applied automatically every time the build script runs
#
# WHEN THIS FIX GETS OVERWRITTEN:
# - Running 'npx cap sync' will regenerate the file and remove our fix
# - Running 'npx cap update' will regenerate the file and remove our fix
# - Running 'npx cap add android' will regenerate the file and remove our fix
#
# HOW TO RESTORE THE FIX:
# - Run this build script again (it will reapply the fix automatically)
# - Or run: ./scripts/fix-capacitor-build.sh
# - Or manually comment out the problematic line
#
# =============================================================================
if [ -f "app/capacitor.build.gradle" ]; then
if grep -q "^apply from: \"../capacitor-cordova-android-plugins/cordova.variables.gradle\"" "app/capacitor.build.gradle"; then
@@ -237,6 +341,185 @@ build_android() {
cd ..
}
build_ios() {
log_info "Building iOS..."
cd ios || exit 1
# Build configuration (define early for validation)
SCHEME="DailyNotificationPlugin"
CONFIG="Release"
WORKSPACE="DailyNotificationPlugin.xcworkspace"
# Install CocoaPods dependencies
log_step "Installing CocoaPods dependencies..."
if [ ! -f "Podfile.lock" ] || [ "Podfile" -nt "Podfile.lock" ] || [ ! -d "Pods" ] || [ ! -d "Pods/Target Support Files" ]; then
log_info "Podfile changed, Podfile.lock missing, or Pods incomplete - running pod install..."
if ! pod install --repo-update; then
log_error "Failed to install CocoaPods dependencies"
exit 1
fi
else
log_info "Podfile.lock is up to date and Pods directory exists, skipping pod install"
fi
# Quick Swift syntax validation (full validation happens during build)
log_step "Validating Swift syntax..."
cd Plugin
SWIFT_FILES=$(find . -name "*.swift" -type f 2>/dev/null)
if [ -z "$SWIFT_FILES" ]; then
log_warn "No Swift files found in Plugin directory"
else
# Use swiftc with iOS SDK for basic syntax check
IOS_SDK=$(xcrun --show-sdk-path --sdk iphoneos 2>/dev/null)
if [ -n "$IOS_SDK" ]; then
for swift_file in $SWIFT_FILES; do
# Quick syntax check without full compilation
if ! swiftc -sdk "$IOS_SDK" -target arm64-apple-ios16.0 -parse "$swift_file" 2>&1 | grep -q "error:"; then
continue
else
log_warn "Syntax check found issues in $swift_file (will be caught during build)"
# Don't exit - let xcodebuild catch real errors
fi
done
else
log_warn "Could not find iOS SDK, skipping syntax validation"
fi
fi
cd ..
# Clean build
log_step "Cleaning iOS build..."
xcodebuild clean \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-quiet || {
log_warn "Clean failed or unnecessary, continuing..."
}
# Check if iOS device platform is available
log_step "Checking iOS device platform availability..."
BUILD_DEVICE=false
if xcodebuild -showsdks 2>&1 | grep -q "iOS.*iphoneos"; then
IOS_SDK_VERSION=$(xcrun --show-sdk-version --sdk iphoneos 2>&1)
log_info "Found iOS SDK: $IOS_SDK_VERSION"
# Check if platform components are installed by trying a dry-run
DRY_RUN_OUTPUT=$(xcodebuild -workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-destination 'generic/platform=iOS' \
-dry-run 2>&1)
if echo "$DRY_RUN_OUTPUT" | grep -q "iOS.*is not installed"; then
log_warn "iOS device platform components not installed"
log_info "To install iOS device platform components, run:"
log_info " xcodebuild -downloadPlatform iOS"
log_info "Or via Xcode: Settings > Components > iOS $IOS_SDK_VERSION"
log_info ""
log_info "Building for iOS Simulator instead (sufficient for plugin development)"
else
BUILD_DEVICE=true
fi
else
log_warn "iOS SDK not found"
fi
# Build for device (iOS) if available
if [ "$BUILD_DEVICE" = true ]; then
log_step "Building for iOS device (arm64)..."
BUILD_OUTPUT=$(xcodebuild build \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphoneos \
-destination 'generic/platform=iOS' \
-derivedDataPath build/derivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1)
if echo "$BUILD_OUTPUT" | grep -q "error.*iOS.*is not installed"; then
log_warn "iOS device build failed - platform components not installed"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
log_info "Check build log: /tmp/xcodebuild_device.log"
BUILD_DEVICE=false
elif echo "$BUILD_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
log_info "✓ iOS device build completed"
else
log_warn "iOS device build completed with warnings"
echo "$BUILD_OUTPUT" > /tmp/xcodebuild_device.log
fi
fi
# Build for simulator
log_step "Building for iOS simulator..."
SIMULATOR_BUILD_OUTPUT=$(xcodebuild build \
-workspace "$WORKSPACE" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath build/derivedData \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
2>&1)
if echo "$SIMULATOR_BUILD_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
log_info "✓ iOS simulator build completed successfully"
elif echo "$SIMULATOR_BUILD_OUTPUT" | grep -q "error:"; then
log_error "iOS simulator build failed"
echo "$SIMULATOR_BUILD_OUTPUT" | grep -E "(error:|warning:)" | head -20
exit 1
else
log_warn "iOS simulator build completed with warnings"
echo "$SIMULATOR_BUILD_OUTPUT" | grep -E "(warning:|error:)" | head -10
fi
# Find built frameworks
DEVICE_FRAMEWORK=$(find build/derivedData -path "*/Build/Products/*-iphoneos/DailyNotificationPlugin.framework" -type d | head -1)
SIMULATOR_FRAMEWORK=$(find build/derivedData -path "*/Build/Products/*-iphonesimulator/DailyNotificationPlugin.framework" -type d | head -1)
if [ -n "$DEVICE_FRAMEWORK" ]; then
log_info "✓ Device framework: $DEVICE_FRAMEWORK"
fi
if [ -n "$SIMULATOR_FRAMEWORK" ]; then
log_info "✓ Simulator framework: $SIMULATOR_FRAMEWORK"
fi
# Create universal framework (optional)
if [ -n "$DEVICE_FRAMEWORK" ] && [ -n "$SIMULATOR_FRAMEWORK" ]; then
log_step "Creating universal framework..."
UNIVERSAL_DIR="build/universal"
mkdir -p "$UNIVERSAL_DIR"
# Copy device framework
cp -R "$DEVICE_FRAMEWORK" "$UNIVERSAL_DIR/"
# Create universal binary
UNIVERSAL_FRAMEWORK="$UNIVERSAL_DIR/DailyNotificationPlugin.framework/DailyNotificationPlugin"
if lipo -create \
"$DEVICE_FRAMEWORK/DailyNotificationPlugin" \
"$SIMULATOR_FRAMEWORK/DailyNotificationPlugin" \
-output "$UNIVERSAL_FRAMEWORK" 2>/dev/null; then
log_info "✓ Universal framework: $UNIVERSAL_DIR/DailyNotificationPlugin.framework"
else
log_warn "Universal framework creation failed (may not be needed)"
fi
fi
cd ..
log_info "iOS build completed successfully!"
}
# Main build process
main() {
log_info "Starting build process..."
@@ -249,35 +532,53 @@ main() {
BUILD_PLATFORM="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [--platform PLATFORM]"
echo ""
echo "Options:"
echo " --platform PLATFORM Build platform: 'android', 'ios', or 'all' (default: all)"
echo ""
echo "Examples:"
echo " $0 --platform android # Build Android only"
echo " $0 --platform ios # Build iOS only"
echo " $0 --platform all # Build both platforms"
echo " $0 # Build both platforms (default)"
exit 0
;;
*)
log_error "Unknown option: $1"
log_error "Use --help for usage information"
exit 1
;;
esac
done
# Check environment
check_environment
# Check environment (platform-specific)
check_environment "$BUILD_PLATFORM"
# Build TypeScript
build_typescript
# Build based on platform
case $BUILD_PLATFORM in
"android")
build_android
;;
"ios")
build_ios
;;
"all")
build_android
build_ios
;;
*)
log_error "Invalid platform: $BUILD_PLATFORM. Use 'android' or 'all'"
log_error "Invalid platform: $BUILD_PLATFORM. Use 'android', 'ios', or 'all'"
exit 1
;;
esac
log_info "Build completed successfully!"
}
# Run main function with all arguments
main "$@"
main "$@"

250
scripts/setup-ruby.sh Executable file
View File

@@ -0,0 +1,250 @@
#!/bin/bash
# Ruby Version Manager (rbenv) Setup Script
# Installs rbenv and Ruby 3.1+ for CocoaPods compatibility
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Check if rbenv is already installed
if command -v rbenv &> /dev/null; then
log_info "rbenv is already installed"
RBENV_INSTALLED=true
else
log_step "Installing rbenv..."
RBENV_INSTALLED=false
fi
# Install rbenv via Homebrew (recommended on macOS)
if [ "$RBENV_INSTALLED" = false ]; then
if command -v brew &> /dev/null; then
log_info "Installing rbenv via Homebrew..."
brew install rbenv ruby-build
# Initialize rbenv in shell
if [ -n "$ZSH_VERSION" ]; then
SHELL_CONFIG="$HOME/.zshrc"
else
SHELL_CONFIG="$HOME/.bash_profile"
fi
# Add rbenv initialization to shell config
if ! grep -q "rbenv init" "$SHELL_CONFIG" 2>/dev/null; then
log_info "Adding rbenv initialization to $SHELL_CONFIG..."
echo '' >> "$SHELL_CONFIG"
echo '# rbenv initialization' >> "$SHELL_CONFIG"
echo 'eval "$(rbenv init - zsh)"' >> "$SHELL_CONFIG"
fi
# Load rbenv in current session
eval "$(rbenv init - zsh)"
log_info "✓ rbenv installed successfully"
else
log_warn "Homebrew not found. Installing rbenv manually..."
# Manual installation via git
if [ ! -d "$HOME/.rbenv" ]; then
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
fi
if [ ! -d "$HOME/.rbenv/plugins/ruby-build" ]; then
mkdir -p ~/.rbenv/plugins
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
fi
# Add to PATH
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init - zsh)"
# Add to shell config
if [ -n "$ZSH_VERSION" ]; then
SHELL_CONFIG="$HOME/.zshrc"
else
SHELL_CONFIG="$HOME/.bash_profile"
fi
if ! grep -q "rbenv init" "$SHELL_CONFIG" 2>/dev/null; then
echo '' >> "$SHELL_CONFIG"
echo '# rbenv initialization' >> "$SHELL_CONFIG"
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> "$SHELL_CONFIG"
echo 'eval "$(rbenv init - zsh)"' >> "$SHELL_CONFIG"
fi
log_info "✓ rbenv installed manually"
fi
fi
# Reload shell config
log_step "Reloading shell configuration..."
if [ -n "$ZSH_VERSION" ]; then
source ~/.zshrc 2>/dev/null || true
else
source ~/.bash_profile 2>/dev/null || true
fi
# Ensure rbenv is in PATH
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init - zsh)" 2>/dev/null || eval "$(rbenv init - bash)" 2>/dev/null || true
# Check current Ruby version
log_step "Checking current Ruby version..."
CURRENT_RUBY=$(ruby -v 2>/dev/null | cut -d' ' -f2 | cut -d. -f1,2) || CURRENT_RUBY="unknown"
if [ "$CURRENT_RUBY" != "unknown" ]; then
RUBY_MAJOR=$(echo "$CURRENT_RUBY" | cut -d. -f1)
RUBY_MINOR=$(echo "$CURRENT_RUBY" | cut -d. -f2)
if [ "$RUBY_MAJOR" -ge 3 ] && [ "$RUBY_MINOR" -ge 1 ]; then
log_info "✓ Ruby version $CURRENT_RUBY is already >= 3.1.0"
log_info "You can proceed with CocoaPods installation"
exit 0
else
log_warn "Current Ruby version: $CURRENT_RUBY (needs 3.1.0+)"
fi
fi
# Check if rbenv has a suitable Ruby version already installed
log_step "Checking installed Ruby versions..."
if rbenv versions | grep -qE "3\.(1|2|3|4)\."; then
INSTALLED_RUBY=$(rbenv versions | grep -E "3\.(1|2|3|4)\." | tail -1 | sed 's/^[[:space:]]*//' | cut -d' ' -f1)
log_info "Found installed Ruby version: $INSTALLED_RUBY"
# Set as global if not already set
CURRENT_GLOBAL=$(rbenv global)
if [ "$CURRENT_GLOBAL" != "$INSTALLED_RUBY" ]; then
log_info "Setting $INSTALLED_RUBY as default..."
rbenv global "$INSTALLED_RUBY"
rbenv rehash
fi
# Verify it works
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init - zsh)" 2>/dev/null || eval "$(rbenv init - bash)" 2>/dev/null || true
if ruby -rpsych -e "true" 2>/dev/null; then
VERIFIED_RUBY=$(ruby -v)
log_info "✓ Ruby $VERIFIED_RUBY is working correctly"
log_info ""
log_info "Ruby setup complete!"
log_info ""
log_info "Next steps:"
log_info " 1. Reload your shell: source ~/.zshrc"
log_info " 2. Verify Ruby: ruby -v"
log_info " 3. Install CocoaPods: gem install cocoapods"
exit 0
else
log_warn "Installed Ruby $INSTALLED_RUBY found but psych extension not working"
log_warn "May need to reinstall Ruby or install libyaml dependencies"
fi
fi
# Check for libyaml dependency (required for psych extension)
log_step "Checking for libyaml dependency..."
LIBYAML_FOUND=false
if command -v brew &> /dev/null; then
if brew list libyaml &> /dev/null; then
LIBYAML_FOUND=true
log_info "✓ libyaml found via Homebrew"
else
log_warn "libyaml not installed. Installing via Homebrew..."
if brew install libyaml; then
LIBYAML_FOUND=true
log_info "✓ libyaml installed successfully"
else
log_error "Failed to install libyaml via Homebrew"
fi
fi
else
# Check if libyaml headers exist in system locations
if find /usr/local /opt /Library -name "yaml.h" 2>/dev/null | grep -q yaml.h; then
LIBYAML_FOUND=true
log_info "✓ libyaml headers found in system"
else
log_warn "libyaml not found. Ruby installation may fail."
log_warn "Install libyaml via Homebrew: brew install libyaml"
log_warn "Or install via MacPorts: sudo port install libyaml"
fi
fi
# List available Ruby versions
log_step "Fetching available Ruby versions..."
rbenv install --list | grep -E "^[[:space:]]*3\.[1-9]" | tail -5 || log_warn "Could not fetch Ruby versions list"
# Install Ruby 3.1.0 (preferred for compatibility)
log_step "Installing Ruby 3.1.0..."
if rbenv install 3.1.0; then
log_info "✓ Ruby 3.1.0 installed successfully"
# Set as global default
log_step "Setting Ruby 3.1.0 as default..."
rbenv global 3.1.0
# Verify installation
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init - zsh)" 2>/dev/null || eval "$(rbenv init - bash)" 2>/dev/null || true
NEW_RUBY=$(ruby -v)
log_info "✓ Current Ruby version: $NEW_RUBY"
# Verify psych extension works
if ruby -rpsych -e "true" 2>/dev/null; then
log_info "✓ psych extension verified"
else
log_warn "psych extension may not be working correctly"
log_warn "This may affect CocoaPods installation"
fi
# Rehash to make Ruby available
rbenv rehash
log_info ""
log_info "Ruby setup complete!"
log_info ""
log_info "Next steps:"
log_info " 1. Reload your shell: source ~/.zshrc"
log_info " 2. Verify Ruby: ruby -v"
log_info " 3. Install CocoaPods: gem install cocoapods"
else
log_error "Failed to install Ruby 3.1.0"
if [ "$LIBYAML_FOUND" = false ]; then
log_error ""
log_error "Installation failed. This is likely due to missing libyaml dependency."
log_error ""
log_error "To fix:"
if command -v brew &> /dev/null; then
log_error " brew install libyaml"
else
log_error " Install Homebrew: /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
log_error " Then: brew install libyaml"
fi
log_error ""
log_error "After installing libyaml, run this script again."
else
log_error "Installation failed. Please check your internet connection and try again."
fi
exit 1
fi