Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Aviso importante

Los ejemplos de código y scripts proporcionados en estos tutoriales son solo para propósitos educativos. Macfleet no es responsable de ningún problema, daño o vulnerabilidad de seguridad que pueda surgir del uso, modificación o implementación de estos ejemplos. Siempre revisa y prueba el código en un entorno seguro antes de usarlo en sistemas de producción.

Menu Bar Customization and Icon Management on macOS

Customize menu bar appearance and manage system icons across your MacFleet devices. This tutorial covers icon visibility control, user interface optimization, and enterprise-grade menu bar management for improved user experience and organizational consistency.

Understanding Menu Bar Management

The macOS menu bar contains various system indicators and quick access icons:

  • Network indicators - VPN, Wi-Fi, Bluetooth status
  • System monitors - Battery, volume, date/time
  • Application icons - Third-party app status indicators
  • User controls - Spotlight, Control Center, Siri
  • Corporate tools - MDM agents, security software

Basic VPN Icon Management

Hide VPN Icon from Menu Bar

#!/bin/bash

# Hide VPN icon from menu bar
hide_vpn_icon() {
    echo "=== Hiding VPN Icon from Menu Bar ==="
    
    # Get current user
    local currentUser
    currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
    
    if [[ "$currentUser" == "loginwindow" || -z "$currentUser" ]]; then
        echo "❌ No active user session found"
        return 1
    fi
    
    echo "👤 Current user: $currentUser"
    
    # Function to run commands as current user
    runAsUser() {
        local currentUserID
        currentUserID=$(id -u "$currentUser")
        if [[ "$currentUser" != "loginwindow" ]]; then
            /bin/launchctl asuser "$currentUserID" sudo -u "$currentUser" "$@"
        fi
    }
    
    # Remove VPN menu extra
    if runAsUser /usr/libexec/PlistBuddy -c 'delete menuExtras: "/System/Library/CoreServices/Menu Extras/VPN.menu"' "/Users/$currentUser/Library/Preferences/com.apple.systemuiserver.plist" 2>/dev/null; then
        echo "✅ VPN menu entry removed"
    else
        echo "⚠️  VPN menu entry not found or already removed"
    fi
    
    # Restart system services to apply changes
    runAsUser /usr/bin/killall cfprefsd 2>/dev/null
    runAsUser /usr/bin/killall SystemUIServer 2>/dev/null
    
    echo "🔄 System UI refreshed"
    echo "✅ VPN icon hidden from menu bar"
}

# Execute function
hide_vpn_icon

Show VPN Icon in Menu Bar

#!/bin/bash

# Show VPN icon in menu bar
show_vpn_icon() {
    echo "=== Adding VPN Icon to Menu Bar ==="
    
    local currentUser
    currentUser=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
    
    if [[ "$currentUser" == "loginwindow" || -z "$currentUser" ]]; then
        echo "❌ No active user session found"
        return 1
    fi
    
    echo "👤 Current user: $currentUser"
    
    runAsUser() {
        local currentUserID
        currentUserID=$(id -u "$currentUser")
        if [[ "$currentUser" != "loginwindow" ]]; then
            /bin/launchctl asuser "$currentUserID" sudo -u "$currentUser" "$@"
        fi
    }
    
    # Add VPN menu extra
    local vpn_path="/System/Library/CoreServices/Menu Extras/VPN.menu"
    if runAsUser /usr/libexec/PlistBuddy -c "add menuExtras: string '$vpn_path'" "/Users/$currentUser/Library/Preferences/com.apple.systemuiserver.plist" 2>/dev/null; then
        echo "✅ VPN menu entry added"
    else
        echo "⚠️  VPN menu entry already exists"
    fi
    
    # Restart system services
    runAsUser /usr/bin/killall cfprefsd 2>/dev/null
    runAsUser /usr/bin/killall SystemUIServer 2>/dev/null
    
    echo "✅ VPN icon shown in menu bar"
}

# Execute function
show_vpn_icon

Enterprise Menu Bar Management System

#!/bin/bash

# MacFleet Enterprise Menu Bar Management System
# Comprehensive interface customization, user experience optimization, and fleet deployment

# Configuration
LOG_FILE="/var/log/macfleet_menubar.log"
CONFIG_DIR="/etc/macfleet/interface"
PROFILES_DIR="$CONFIG_DIR/profiles"
BACKUP_DIR="/var/backups/menubar_configs"
REPORTS_DIR="$CONFIG_DIR/reports"

# Available menu extras
declare -A MENU_EXTRAS=(
    ["vpn"]="/System/Library/CoreServices/Menu Extras/VPN.menu"
    ["wifi"]="/System/Library/CoreServices/Menu Extras/AirPort.menu"
    ["bluetooth"]="/System/Library/CoreServices/Menu Extras/Bluetooth.menu"
    ["volume"]="/System/Library/CoreServices/Menu Extras/Volume.menu"
    ["battery"]="/System/Library/CoreServices/Menu Extras/Battery.menu"
    ["clock"]="/System/Library/CoreServices/Menu Extras/Clock.menu"
    ["displays"]="/System/Library/CoreServices/Menu Extras/Displays.menu"
    ["timemachine"]="/System/Library/CoreServices/Menu Extras/TimeMachine.menu"
    ["textinput"]="/System/Library/CoreServices/Menu Extras/TextInput.menu"
    ["script"]="/System/Library/CoreServices/Menu Extras/Script Menu.menu"
)

# Profile configurations
declare -A MENU_PROFILES=(
    ["minimal"]="clock,battery,wifi"
    ["standard"]="clock,battery,wifi,volume,bluetooth"
    ["full"]="clock,battery,wifi,volume,bluetooth,displays,vpn"
    ["corporate"]="clock,battery,wifi,vpn,timemachine"
    ["education"]="clock,battery,wifi,volume,textinput"
    ["kiosk"]="clock,battery"
    ["developer"]="clock,battery,wifi,volume,bluetooth,displays,script"
    ["secure"]="clock,battery,wifi"
)

# Logging function
log_action() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Setup directories
setup_directories() {
    for dir in "$CONFIG_DIR" "$PROFILES_DIR" "$BACKUP_DIR" "$REPORTS_DIR"; do
        if [[ ! -d "$dir" ]]; then
            mkdir -p "$dir"
            log_action "Created directory: $dir"
        fi
    done
}

# Get current user
get_current_user() {
    local user
    user=$(echo "show State:/Users/ConsoleUser" | scutil | awk '/Name :/ { print $3 }')
    
    if [[ "$user" == "loginwindow" || -z "$user" ]]; then
        return 1
    fi
    
    echo "$user"
    return 0
}

# Execute command as current user
run_as_user() {
    local user="$1"
    shift
    
    local userID
    userID=$(id -u "$user" 2>/dev/null) || return 1
    
    /bin/launchctl asuser "$userID" sudo -u "$user" "$@"
}

# Get current menu bar configuration
get_current_menubar_config() {
    local user="$1"
    local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
    
    if [[ ! -f "$plist_path" ]]; then
        echo "[]"
        return 1
    fi
    
    # Extract current menu extras
    local menu_extras
    menu_extras=$(run_as_user "$user" /usr/libexec/PlistBuddy -c "print menuExtras:" "$plist_path" 2>/dev/null | grep -E "\.menu$" | sed 's/^[ ]*//' || echo "")
    
    if [[ -z "$menu_extras" ]]; then
        echo "[]"
    else
        echo "$menu_extras" | jq -R -s 'split("\n") | map(select(length > 0))'
    fi
}

# Apply menu bar profile
apply_menubar_profile() {
    local profile_name="$1"
    local target_user="$2"
    
    log_action "Applying menu bar profile: $profile_name"
    
    # Get profile configuration
    local profile_config="${MENU_PROFILES[$profile_name]}"
    if [[ -z "$profile_config" ]]; then
        log_action "❌ Unknown profile: $profile_name"
        return 1
    fi
    
    # Get target users
    local users=()
    if [[ -n "$target_user" ]]; then
        users=("$target_user")
    else
        # Get all users with UID >= 500
        while IFS= read -r user; do
            users+=("$user")
        done < <(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}')
    fi
    
    # Apply profile to each user
    for user in "${users[@]}"; do
        log_action "Configuring menu bar for user: $user"
        
        local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
        
        # Backup current configuration
        backup_user_menubar_config "$user"
        
        # Clear existing menu extras
        run_as_user "$user" /usr/libexec/PlistBuddy -c "delete menuExtras:" "$plist_path" 2>/dev/null || true
        run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras: array" "$plist_path" 2>/dev/null
        
        # Add profile menu items
        IFS=',' read -ra ITEMS <<< "$profile_config"
        local index=0
        
        for item in "${ITEMS[@]}"; do
            local menu_path="${MENU_EXTRAS[$item]}"
            if [[ -n "$menu_path" && -f "$menu_path" ]]; then
                run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras:$index string '$menu_path'" "$plist_path" 2>/dev/null
                log_action "Added menu item: $item for user: $user"
                ((index++))
            else
                log_action "⚠️  Menu item not found: $item"
            fi
        done
        
        # Restart UI services
        run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
        run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
        
        log_action "✅ Profile '$profile_name' applied to user: $user"
    done
    
    return 0
}

# Backup user menu bar configuration
backup_user_menubar_config() {
    local user="$1"
    local timestamp
    timestamp=$(date '+%Y%m%d_%H%M%S')
    
    local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
    local backup_file="$BACKUP_DIR/menubar_${user}_${timestamp}.plist"
    
    if [[ -f "$plist_path" ]]; then
        cp "$plist_path" "$backup_file" 2>/dev/null
        log_action "Backed up menu bar config for $user: $backup_file"
        echo "$backup_file"
    fi
}

# Custom menu bar configuration
configure_custom_menubar() {
    local items_string="$1"
    local target_user="$2"
    
    log_action "Applying custom menu bar configuration: $items_string"
    
    local users=()
    if [[ -n "$target_user" ]]; then
        users=("$target_user")
    else
        while IFS= read -r user; do
            users+=("$user")
        done < <(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}')
    fi
    
    # Parse items
    IFS=',' read -ra ITEMS <<< "$items_string"
    
    for user in "${users[@]}"; do
        local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
        
        # Backup and clear
        backup_user_menubar_config "$user"
        run_as_user "$user" /usr/libexec/PlistBuddy -c "delete menuExtras:" "$plist_path" 2>/dev/null || true
        run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras: array" "$plist_path" 2>/dev/null
        
        # Add custom items
        local index=0
        for item in "${ITEMS[@]}"; do
            # Remove whitespace
            item=$(echo "$item" | sed 's/^[ \t]*//;s/[ \t]*$//')
            
            local menu_path="${MENU_EXTRAS[$item]}"
            if [[ -n "$menu_path" && -f "$menu_path" ]]; then
                run_as_user "$user" /usr/libexec/PlistBuddy -c "add menuExtras:$index string '$menu_path'" "$plist_path" 2>/dev/null
                ((index++))
            fi
        done
        
        # Apply changes
        run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
        run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
        
        log_action "✅ Custom configuration applied to user: $user"
    done
}

# Generate menu bar audit report
generate_menubar_audit() {
    log_action "Generating menu bar audit report"
    
    local report_file="$REPORTS_DIR/menubar_audit_$(date '+%Y%m%d_%H%M%S').json"
    
    cat > "$report_file" << EOF
{
    "audit_metadata": {
        "timestamp": "$(date -Iseconds)",
        "hostname": "$(hostname)",
        "os_version": "$(sw_vers -productVersion)",
        "generator": "MacFleet Menu Bar Manager"
    },
    "user_configurations": [
EOF

    local first=true
    local total_users=0
    
    # Audit each user
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        total_users=$((total_users + 1))
        
        if [[ "$first" == true ]]; then
            first=false
        else
            echo "," >> "$report_file"
        fi
        
        local current_config
        current_config=$(get_current_menubar_config "$user")
        
        local last_login
        last_login=$(last -1 "$user" | head -1 | awk '{print $4, $5, $6, $7}' || echo 'Never')
        
        cat >> "$report_file" << EOF
        {
            "username": "$user",
            "current_menu_items": $current_config,
            "item_count": $(echo "$current_config" | jq 'length'),
            "last_login": "$last_login",
            "config_file_exists": $([ -f "/Users/$user/Library/Preferences/com.apple.systemuiserver.plist" ] && echo "true" || echo "false")
        }
EOF
        
        log_action "Audited user: $user"
    done
    
    cat >> "$report_file" << EOF
    ],
    "summary": {
        "total_users": $total_users,
        "available_menu_items": $(echo "${!MENU_EXTRAS[@]}" | tr ' ' '\n' | jq -R . | jq -s .),
        "available_profiles": $(echo "${!MENU_PROFILES[@]}" | tr ' ' '\n' | jq -R . | jq -s .)
    }
}
EOF

    log_action "✅ Menu bar audit completed: $report_file"
    echo "$report_file"
}

# Restore menu bar configuration from backup
restore_menubar_config() {
    local backup_file="$1"
    local target_user="$2"
    
    if [[ ! -f "$backup_file" ]]; then
        log_action "❌ Backup file not found: $backup_file"
        return 1
    fi
    
    log_action "Restoring menu bar configuration from: $backup_file"
    
    local users=()
    if [[ -n "$target_user" ]]; then
        users=("$target_user")
    else
        # Extract username from backup filename
        local extracted_user
        extracted_user=$(basename "$backup_file" | sed 's/menubar_\(.*\)_[0-9]*_[0-9]*.plist/\1/')
        if [[ -n "$extracted_user" ]]; then
            users=("$extracted_user")
        fi
    fi
    
    for user in "${users[@]}"; do
        local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
        
        # Create current backup before restore
        backup_user_menubar_config "$user"
        
        # Restore configuration
        if cp "$backup_file" "$plist_path" 2>/dev/null; then
            chown "$user:staff" "$plist_path" 2>/dev/null
            
            # Apply changes
            run_as_user "$user" /usr/bin/killall cfprefsd 2>/dev/null
            run_as_user "$user" /usr/bin/killall SystemUIServer 2>/dev/null
            
            log_action "✅ Configuration restored for user: $user"
        else
            log_action "❌ Failed to restore configuration for user: $user"
            return 1
        fi
    done
    
    return 0
}

# Monitor menu bar changes
monitor_menubar_changes() {
    local duration="${1:-3600}"  # 1 hour default
    local check_interval="${2:-60}"  # 1 minute default
    
    log_action "Starting menu bar monitoring for ${duration} seconds"
    
    local monitoring_report="$REPORTS_DIR/menubar_monitoring_$(date '+%Y%m%d_%H%M%S').json"
    local baseline_file="/tmp/menubar_baseline.json"
    
    # Create baseline
    generate_menubar_audit > "$baseline_file"
    
    cat > "$monitoring_report" << EOF
{
    "monitoring_metadata": {
        "start_time": "$(date -Iseconds)",
        "duration_seconds": $duration,
        "check_interval_seconds": $check_interval,
        "hostname": "$(hostname)"
    },
    "changes_detected": [
EOF

    local start_time end_time
    start_time=$(date +%s)
    end_time=$((start_time + duration))
    
    local first=true
    while [[ $(date +%s) -lt $end_time ]]; do
        sleep "$check_interval"
        
        # Check for changes
        local current_state="/tmp/menubar_current.json"
        generate_menubar_audit > "$current_state"
        
        if ! diff "$baseline_file" "$current_state" >/dev/null 2>&1; then
            if [[ "$first" == true ]]; then
                first=false
            else
                echo "," >> "$monitoring_report"
            fi
            
            cat >> "$monitoring_report" << EOF
        {
            "timestamp": "$(date -Iseconds)",
            "change_type": "menu_bar_configuration_change",
            "details": "Changes detected in menu bar configuration"
        }
EOF
            
            log_action "⚠️  Menu bar configuration changes detected"
            cp "$current_state" "$baseline_file"
        fi
        
        rm -f "$current_state"
    done
    
    cat >> "$monitoring_report" << EOF
    ],
    "end_time": "$(date -Iseconds)"
}
EOF

    log_action "✅ Menu bar monitoring completed: $monitoring_report"
    rm -f "$baseline_file"
    echo "$monitoring_report"
}

# Main execution function
main() {
    local action="${1:-audit}"
    local parameter="$2"
    local additional_param="$3"
    
    log_action "=== MacFleet Menu Bar Management Started ==="
    log_action "Action: $action"
    log_action "Parameter: ${parameter:-N/A}"
    
    setup_directories
    
    case "$action" in
        "profile")
            if [[ -z "$parameter" ]]; then
                echo "Available menu bar profiles:"
                for profile in "${!MENU_PROFILES[@]}"; do
                    echo "  - $profile: ${MENU_PROFILES[$profile]}"
                done
                echo ""
                echo "Usage: $0 profile <profile_name> [username]"
                exit 1
            fi
            apply_menubar_profile "$parameter" "$additional_param"
            ;;
        "custom")
            if [[ -z "$parameter" ]]; then
                echo "Available menu items:"
                for item in "${!MENU_EXTRAS[@]}"; do
                    echo "  - $item"
                done
                echo ""
                echo "Usage: $0 custom <item1,item2,item3> [username]"
                echo "Example: $0 custom clock,battery,wifi,vpn john.doe"
                exit 1
            fi
            configure_custom_menubar "$parameter" "$additional_param"
            ;;
        "audit")
            generate_menubar_audit
            ;;
        "monitor")
            monitor_menubar_changes "$parameter" "$additional_param"
            ;;
        "restore")
            if [[ -z "$parameter" ]]; then
                echo "Usage: $0 restore <backup_file> [username]"
                echo "Available backups:"
                ls -la "$BACKUP_DIR"/*.plist 2>/dev/null || echo "No backups found"
                exit 1
            fi
            restore_menubar_config "$parameter" "$additional_param"
            ;;
        "hide-vpn")
            local user
            if user=$(get_current_user); then
                hide_vpn_icon
            else
                echo "❌ No active user session"
                exit 1
            fi
            ;;
        "show-vpn")
            local user
            if user=$(get_current_user); then
                show_vpn_icon
            else
                echo "❌ No active user session"
                exit 1
            fi
            ;;
        *)
            echo "Usage: $0 {profile|custom|audit|monitor|restore|hide-vpn|show-vpn}"
            echo "  profile     - Apply predefined menu bar profile"
            echo "  custom      - Apply custom menu bar configuration"
            echo "  audit       - Generate menu bar audit report"
            echo "  monitor     - Monitor menu bar changes over time"
            echo "  restore     - Restore from backup"
            echo "  hide-vpn    - Hide VPN icon from menu bar"
            echo "  show-vpn    - Show VPN icon in menu bar"
            exit 1
            ;;
    esac
    
    log_action "=== Menu bar management completed ==="
}

# Execute main function
main "$@"

Menu Bar Profile Examples

Corporate Profile Setup

#!/bin/bash

# Apply corporate menu bar profile across all devices
deploy_corporate_profile() {
    echo "=== Corporate Menu Bar Deployment ==="
    
    # Corporate profile: clock, battery, wifi, vpn, timemachine
    local corporate_items="clock,battery,wifi,vpn,timemachine"
    
    # Get all users
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        echo "Configuring corporate profile for: $user"
        
        # Apply configuration
        configure_custom_menubar "$corporate_items" "$user"
        
        # Verify application
        sleep 2
        local current_config
        current_config=$(get_current_menubar_config "$user")
        echo "Applied configuration: $current_config"
    done
    
    echo "✅ Corporate profile deployed to all users"
}

Kiosk Mode Configuration

#!/bin/bash

# Minimal menu bar for kiosk environments
setup_kiosk_menubar() {
    echo "=== Kiosk Menu Bar Setup ==="
    
    # Minimal items: clock and battery only
    local kiosk_items="clock,battery"
    
    local target_user="$1"
    if [[ -z "$target_user" ]]; then
        echo "Usage: setup_kiosk_menubar <username>"
        return 1
    fi
    
    configure_custom_menubar "$kiosk_items" "$target_user"
    
    echo "✅ Kiosk menu bar configured for: $target_user"
}

Advanced Menu Bar Features

Dynamic Profile Switching

#!/bin/bash

# Switch between different profiles based on conditions
smart_profile_switching() {
    local user="$1"
    
    echo "=== Smart Profile Switching ==="
    
    # Detect environment
    if pgrep -f "VPN" >/dev/null; then
        echo "VPN detected - applying corporate profile"
        apply_menubar_profile "corporate" "$user"
    elif [[ $(pmset -g batt | grep -c "Battery") -gt 0 ]]; then
        echo "Battery power detected - applying minimal profile"
        apply_menubar_profile "minimal" "$user"
    else
        echo "Standard environment - applying full profile"
        apply_menubar_profile "full" "$user"
    fi
}

Menu Bar Health Check

#!/bin/bash

# Verify menu bar configuration integrity
menubar_health_check() {
    echo "=== Menu Bar Health Check ==="
    
    local issues_found=0
    
    for user in $(dscl . list /Users UniqueID | awk '$2 >= 500 {print $1}'); do
        local plist_path="/Users/$user/Library/Preferences/com.apple.systemuiserver.plist"
        
        if [[ ! -f "$plist_path" ]]; then
            echo "⚠️  Missing configuration file for user: $user"
            issues_found=$((issues_found + 1))
            continue
        fi
        
        # Check if SystemUIServer is running
        if ! pgrep -u "$(id -u "$user")" SystemUIServer >/dev/null; then
            echo "⚠️  SystemUIServer not running for user: $user"
            issues_found=$((issues_found + 1))
        fi
        
        # Validate plist format
        if ! plutil -lint "$plist_path" >/dev/null 2>&1; then
            echo "❌ Corrupted plist file for user: $user"
            issues_found=$((issues_found + 1))
        fi
    done
    
    if [[ $issues_found -eq 0 ]]; then
        echo "✅ All menu bar configurations are healthy"
    else
        echo "⚠️  Found $issues_found issues requiring attention"
    fi
    
    return $issues_found
}

Best Practices

🎯 User Experience Optimization

  • Consistent branding across all devices with standardized menu bar layouts
  • Profile-based configurations for different user roles and environments
  • Minimal kiosk setups for public or restricted-use devices
  • Dynamic switching based on power state or network conditions

🔧 Enterprise Management

  • Centralized deployment of menu bar configurations across device fleets
  • Backup and restore capabilities for configuration management
  • Audit reporting for compliance and standardization verification
  • Change monitoring with automated detection and alerting

🔐 Security Considerations

  • Remove sensitive indicators from public-facing devices
  • Hide VPN status in environments where network information should be private
  • Standardize corporate displays to prevent information leakage
  • Monitor unauthorized changes to menu bar configurations

📊 Monitoring and Maintenance

  • Regular health checks to ensure configuration integrity
  • Automated profile enforcement to maintain consistency
  • Change detection with rollback capabilities
  • User experience feedback collection and optimization

Important Notes

  • macOS 12+ required for optimal compatibility with modern menu extras
  • User session active required for menu bar modifications
  • System restart may be needed for some changes to take full effect
  • Test thoroughly before deploying to production environments
  • Backup configurations before making changes to allow easy rollback

Tutorial

Nuevas actualizaciones y mejoras para Macfleet.

Configurando un Runner de GitHub Actions en un Mac Mini (Apple Silicon)

Runner de GitHub Actions

GitHub Actions es una plataforma poderosa de CI/CD que te permite automatizar tus flujos de trabajo de desarrollo de software. Aunque GitHub ofrece runners hospedados, los runners auto-hospedados proporcionan mayor control y personalización para tu configuración de CI/CD. Este tutorial te guía a través de la configuración y conexión de un runner auto-hospedado en un Mac mini para ejecutar pipelines de macOS.

Prerrequisitos

Antes de comenzar, asegúrate de tener:

  • Un Mac mini (regístrate en Macfleet)
  • Un repositorio de GitHub con derechos de administrador
  • Un gestor de paquetes instalado (preferiblemente Homebrew)
  • Git instalado en tu sistema

Paso 1: Crear una Cuenta de Usuario Dedicada

Primero, crea una cuenta de usuario dedicada para el runner de GitHub Actions:

# Crear la cuenta de usuario 'gh-runner'
sudo dscl . -create /Users/gh-runner
sudo dscl . -create /Users/gh-runner UserShell /bin/bash
sudo dscl . -create /Users/gh-runner RealName "GitHub runner"
sudo dscl . -create /Users/gh-runner UniqueID "1001"
sudo dscl . -create /Users/gh-runner PrimaryGroupID 20
sudo dscl . -create /Users/gh-runner NFSHomeDirectory /Users/gh-runner

# Establecer la contraseña para el usuario
sudo dscl . -passwd /Users/gh-runner tu_contraseña

# Agregar 'gh-runner' al grupo 'admin'
sudo dscl . -append /Groups/admin GroupMembership gh-runner

Cambia a la nueva cuenta de usuario:

su gh-runner

Paso 2: Instalar Software Requerido

Instala Git y Rosetta 2 (si usas Apple Silicon):

# Instalar Git si no está ya instalado
brew install git

# Instalar Rosetta 2 para Macs Apple Silicon
softwareupdate --install-rosetta

Paso 3: Configurar el Runner de GitHub Actions

  1. Ve a tu repositorio de GitHub
  2. Navega a Configuración > Actions > Runners

Runner de GitHub Actions

  1. Haz clic en "New self-hosted runner" (https://github.com/<username>/<repository>/settings/actions/runners/new)
  2. Selecciona macOS como imagen del runner y ARM64 como arquitectura
  3. Sigue los comandos proporcionados para descargar y configurar el runner

Runner de GitHub Actions

Crea un archivo .env en el directorio _work del runner:

# archivo _work/.env
ImageOS=macos15
XCODE_15_DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
  1. Ejecuta el script run.sh en tu directorio del runner para completar la configuración.
  2. Verifica que el runner esté activo y escuchando trabajos en la terminal y revisa la configuración del repositorio de GitHub para la asociación del runner y el estado Idle.

Runner de GitHub Actions

Paso 4: Configurar Sudoers (Opcional)

Si tus acciones requieren privilegios de root, configura el archivo sudoers:

sudo visudo

Agrega la siguiente línea:

gh-runner ALL=(ALL) NOPASSWD: ALL

Paso 5: Usar el Runner en Flujos de Trabajo

Configura tu flujo de trabajo de GitHub Actions para usar el runner auto-hospedado:

name: Flujo de trabajo de muestra

on:
  workflow_dispatch:

jobs:
  build:
    runs-on: [self-hosted, macOS, ARM64]
    steps:
      - name: Instalar NodeJS
        run: brew install node

El runner está autenticado en tu repositorio y etiquetado con self-hosted, macOS, y ARM64. Úsalo en tus flujos de trabajo especificando estas etiquetas en el campo runs-on:

runs-on: [self-hosted, macOS, ARM64]

Mejores Prácticas

  • Mantén tu software del runner actualizado
  • Monitorea regularmente los logs del runner para problemas
  • Usa etiquetas específicas para diferentes tipos de runners
  • Implementa medidas de seguridad apropiadas
  • Considera usar múltiples runners para balanceo de carga

Solución de Problemas

Problemas comunes y soluciones:

  1. Runner no conectando:

    • Verifica conectividad de red
    • Verifica validez del token de GitHub
    • Asegúrate de permisos apropiados
  2. Fallas de construcción:

    • Verifica instalación de Xcode
    • Verifica dependencias requeridas
    • Revisa logs del flujo de trabajo
  3. Problemas de permisos:

    • Verifica permisos de usuario
    • Verifica configuración de sudoers
    • Revisa permisos del sistema de archivos

Conclusión

Ahora tienes un runner auto-hospedado de GitHub Actions configurado en tu Mac mini. Esta configuración te proporciona más control sobre tu entorno de CI/CD y te permite ejecutar flujos de trabajo específicos de macOS de manera eficiente.

Recuerda mantener regularmente tu runner y mantenerlo actualizado con los últimos parches de seguridad y versiones de software.

Aplicación Nativa

Aplicación nativa de Macfleet

Guía de Instalación de Macfleet

Macfleet es una solución poderosa de gestión de flota diseñada específicamente para entornos de Mac Mini alojados en la nube. Como proveedor de hosting en la nube de Mac Mini, puedes usar Macfleet para monitorear, gestionar y optimizar toda tu flota de instancias Mac virtualizadas.

Esta guía de instalación te llevará a través de la configuración del monitoreo de Macfleet en sistemas macOS, Windows y Linux para asegurar una supervisión integral de tu infraestructura en la nube.

🍎 macOS

  • Descarga el archivo .dmg para Mac aquí
  • Haz doble clic en el archivo .dmg descargado
  • Arrastra la aplicación Macfleet a la carpeta Aplicaciones
  • Expulsa el archivo .dmg
  • Abre Preferencias del Sistema > Seguridad y Privacidad
    • Pestaña Privacidad > Accesibilidad
    • Marca Macfleet para permitir el monitoreo
  • Inicia Macfleet desde Aplicaciones
  • El seguimiento comienza automáticamente

🪟 Windows

  • Descarga el archivo .exe para Windows aquí
  • Haz clic derecho en el archivo .exe > "Ejecutar como administrador"
  • Sigue el asistente de instalación
  • Acepta los términos y condiciones
  • Permite en Windows Defender si se solicita
  • Concede permisos de monitoreo de aplicaciones
  • Inicia Macfleet desde el Menú Inicio
  • La aplicación comienza el seguimiento automáticamente

🐧 Linux

  • Descarga el paquete .deb (Ubuntu/Debian) o .rpm (CentOS/RHEL) aquí
  • Instala usando tu gestor de paquetes
    • Ubuntu/Debian: sudo dpkg -i Macfleet-linux.deb
    • CentOS/RHEL: sudo rpm -ivh Macfleet-linux.rpm
  • Permite permisos de acceso X11 si se solicita
  • Agrega el usuario a los grupos apropiados si es necesario
  • Inicia Macfleet desde el menú de Aplicaciones
  • La aplicación comienza el seguimiento automáticamente

Nota: Después de la instalación en todos los sistemas, inicia sesión con tus credenciales de Macfleet para sincronizar datos con tu panel de control.