Last active
July 30, 2025 21:35
-
-
Save debedb/f8a4b8384cd00b64aa5dfa5f0a961b58 to your computer and use it in GitHub Desktop.
Revisions
-
debedb revised this gist
Jul 30, 2025 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -20,13 +20,13 @@ When you set environment variables in `~/.bash_profile`, they're only available ```bash # Run the sync manually ~/bin/sync-env-to-launchctl # Check what would change without applying ~/bin/sync-env-to-launchctl --dry-run # See detailed output ~/bin/sync-env-to-launchctl --verbose ``` ### 2. Automatic sync (recommended) -
debedb created this gist
Jul 30, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,125 @@ # Environment Variable Sync Tool This tool automatically syncs environment variables from `~/.bash_profile` to macOS LaunchAgent, making them available to GUI applications and non-shell processes. ## Problem it solves When you set environment variables in `~/.bash_profile`, they're only available in Terminal sessions. GUI applications (like VS Code, IntelliJ, etc.) launched from Finder or Spotlight don't see these variables. ## How it works 1. Captures current environment variables 2. Sources `~/.bash_profile` to get new environment 3. Detects changes and new variables 4. Creates/updates a LaunchAgent plist that sets these variables 5. Applies changes immediately (full effect after logout/login) ## Installation ### 1. Manual sync (one-time setup) ```bash # Run the sync manually ~/g/svalka/bin/sync-env-to-launchctl # Check what would change without applying ~/g/svalka/bin/sync-env-to-launchctl --dry-run # See detailed output ~/g/svalka/bin/sync-env-to-launchctl --verbose ``` ### 2. Automatic sync (recommended) Install the LaunchAgent to automatically sync when `~/.bash_profile` changes: ```bash # Copy the LaunchAgent to the proper location cp ~/g/svalka/bin/com.user.sync-env.plist ~/Library/LaunchAgents/ # Load it launchctl load ~/Library/LaunchAgents/com.user.sync-env.plist # Verify it's running launchctl list | grep sync-env ``` Now whenever you edit `~/.bash_profile`, environment variables will sync automatically within 10 seconds. ## Usage ### After installation: 1. Edit `~/.bash_profile` as normal 2. Save the file 3. Within 10 seconds, new variables are available to newly launched apps 4. For complete effect (all running apps), log out and log back in ### Checking logs: ```bash # See sync activity tail -f /tmp/sync-env.log # Check for errors tail -f /tmp/sync-env.error.log ``` ### Troubleshooting: ```bash # Check if environment plist exists ls -la ~/Library/LaunchAgents/com.user.environment.plist # Check if sync agent is loaded launchctl list | grep com.user.sync-env # Manually reload the environment agent launchctl unload ~/Library/LaunchAgents/com.user.environment.plist launchctl load ~/Library/LaunchAgents/com.user.environment.plist # See what environment variables are currently set launchctl getenv PATH ``` ## Features - **Smart detection**: Only syncs variables that changed after sourcing `.bash_profile` - **Exclusions**: Automatically excludes shell-specific variables (PS1, HISTSIZE, etc.) - **Backup**: Creates timestamped backups before updates - **Validation**: Checks plist syntax before applying - **Dry-run mode**: Preview changes without applying - **Immediate effect**: Variables available to new apps right away ## Excluded variables The tool automatically excludes: - Shell internals (BASH_*, SHELL, PWD, etc.) - History variables (HIST*) - Prompt variables (PS1-PS4) - Terminal settings (COLUMNS, LINES, TERM) - Functions ## Notes - Changes apply to newly launched applications immediately - Running applications won't see changes until restarted - For system-wide effect, log out and log back in - The tool creates backups in `~/Library/LaunchAgents/com.user.environment.plist.backup.*` ## Uninstalling ```bash # Stop automatic sync launchctl unload ~/Library/LaunchAgents/com.user.sync-env.plist rm ~/Library/LaunchAgents/com.user.sync-env.plist # Remove environment variables launchctl unload ~/Library/LaunchAgents/com.user.environment.plist rm ~/Library/LaunchAgents/com.user.environment.plist # Remove the tools rm ~/g/svalka/bin/sync-env-to-launchctl rm ~/g/svalka/bin/com.user.sync-env.plist rm ~/g/svalka/bin/README.md ``` This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,24 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.user.sync-env</string> <key>ProgramArguments</key> <array> <string>/Users/alice/bin/sync-env-to-launchctl</string> </array> <key>WatchPaths</key> <array> <string>/Users/alice/.bash_profile</string> </array> <key>RunAtLoad</key> <true/> <key>ThrottleInterval</key> <integer>10</integer> <key>StandardOutPath</key> <string>/tmp/sync-env.log</string> <key>StandardErrorPath</key> <string>/tmp/sync-env.error.log</string> </dict> </plist> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,189 @@ #!/bin/bash # sync-env-to-launchctl # Syncs environment variables from ~/.bash_profile to LaunchAgent set -euo pipefail # Configuration PLIST_PATH="$HOME/Library/LaunchAgents/com.user.environment.plist" BASH_PROFILE="$HOME/.bash_profile" TEMP_DIR="/tmp/sync-env-$$" # Variables to exclude from syncing EXCLUDE_VARS=( "BASH_ARGC" "BASH_ARGV" "BASH_LINENO" "BASH_SOURCE" "DIRSTACK" "EUID" "GROUPS" "HOSTNAME" "PPID" "SECONDS" "SHELLOPTS" "SHLVL" "UID" "_" "COLUMNS" "LINES" "OLDPWD" "PWD" "SHELL" "TERM" "USER" "USERNAME" "HISTFILE" "HISTFILESIZE" "HISTSIZE" "HISTCONTROL" "PROMPT_COMMAND" "PIPESTATUS" "FUNCNAME" "IFS" "LINENO" "RANDOM" ) # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color # Helper functions log() { echo -e "${GREEN}[$(date +'%H:%M:%S')]${NC} $1"; } warn() { echo -e "${YELLOW}[$(date +'%H:%M:%S')] WARNING:${NC} $1"; } error() { echo -e "${RED}[$(date +'%H:%M:%S')] ERROR:${NC} $1" >&2; } cleanup() { rm -rf "$TEMP_DIR" } trap cleanup EXIT # Check if running in interactive mode (for debugging) DRY_RUN=false VERBOSE=false while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUN=true; shift ;; --verbose|-v) VERBOSE=true; shift ;; --help|-h) echo "Usage: $0 [--dry-run] [--verbose]" echo " --dry-run Show what would be done without making changes" echo " --verbose Show detailed output" exit 0 ;; *) error "Unknown option: $1"; exit 1 ;; esac done # Create temp directory mkdir -p "$TEMP_DIR" # Function to check if variable should be excluded should_exclude() { local var=$1 for exclude in "${EXCLUDE_VARS[@]}"; do [[ "$var" == "$exclude" ]] && return 0 done # Exclude functions and readonly variables [[ "$var" =~ ^[a-zA-Z_][a-zA-Z0-9_]*\(\)$ ]] && return 0 return 1 } # Get environment before sourcing log "Capturing current environment..." env | sort > "$TEMP_DIR/env_before.txt" # Source bash profile in a subshell and get new environment log "Sourcing $BASH_PROFILE..." ( set +e # Don't exit on errors during sourcing source "$BASH_PROFILE" 2>/dev/null env | sort ) > "$TEMP_DIR/env_after.txt" # Find new or changed environment variables log "Detecting changes..." comm -13 "$TEMP_DIR/env_before.txt" "$TEMP_DIR/env_after.txt" > "$TEMP_DIR/env_diff.txt" # Process the differences declare -A env_vars while IFS='=' read -r key value; do # Skip if no = sign [[ ! "$key" =~ = ]] || continue # Skip excluded variables should_exclude "$key" && continue # Skip empty keys [[ -z "$key" ]] && continue env_vars["$key"]="$value" [[ "$VERBOSE" == "true" ]] && echo " + $key=$value" done < "$TEMP_DIR/env_diff.txt" # Generate plist content log "Generating LaunchAgent plist..." cat > "$TEMP_DIR/environment.plist" <<'EOF' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.user.environment</string> <key>ProgramArguments</key> <array> <string>sh</string> <string>-c</string> <string> EOF # Build the launchctl commands commands="" for key in "${!env_vars[@]}"; do # Escape the value for shell value="${env_vars[$key]}" # Escape double quotes and backslashes value="${value//\\/\\\\}" value="${value//\"/\\\"}" commands+=" launchctl setenv \"$key\" \"$value\"\n" done # Complete the plist echo -n "$commands" >> "$TEMP_DIR/environment.plist" cat >> "$TEMP_DIR/environment.plist" <<'EOF' </string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> EOF # Validate plist if ! plutil -lint "$TEMP_DIR/environment.plist" >/dev/null 2>&1; then error "Generated plist is invalid!" exit 1 fi # Show what would be done in dry-run mode if [[ "$DRY_RUN" == "true" ]]; then log "DRY RUN: Would update $PLIST_PATH" echo "--- New environment variables ---" for key in "${!env_vars[@]}"; do echo "$key=${env_vars[$key]}" done echo "--- Plist content ---" cat "$TEMP_DIR/environment.plist" exit 0 fi # Backup existing plist if it exists if [[ -f "$PLIST_PATH" ]]; then backup_path="$PLIST_PATH.backup.$(date +%Y%m%d_%H%M%S)" cp "$PLIST_PATH" "$backup_path" log "Backed up existing plist to $backup_path" fi # Create LaunchAgents directory if needed mkdir -p "$(dirname "$PLIST_PATH")" # Install the new plist cp "$TEMP_DIR/environment.plist" "$PLIST_PATH" log "Updated $PLIST_PATH" # Unload and reload the LaunchAgent if launchctl list | grep -q "com.user.environment"; then log "Reloading LaunchAgent..." launchctl unload "$PLIST_PATH" 2>/dev/null || true fi launchctl load "$PLIST_PATH" # Apply changes immediately to current session log "Applying environment variables to current session..." for key in "${!env_vars[@]}"; do launchctl setenv "$key" "${env_vars[$key]}" done log "✅ Environment variables synced successfully!" log " ${#env_vars[@]} variables updated" log " Changes will fully take effect for new applications" log " For complete effect, log out and log back in"