Skip to content

Instantly share code, notes, and snippets.

@andrewpryor
Last active June 11, 2025 14:49
Show Gist options
  • Save andrewpryor/f2d52f6016ddf64d8f4d5f4976a2b8de to your computer and use it in GitHub Desktop.
Save andrewpryor/f2d52f6016ddf64d8f4d5f4976a2b8de to your computer and use it in GitHub Desktop.
env-sync bootstrap script
#!/usr/bin/env bash
# env-sync bootstrap script
# Usage: curl -sL https://gist.githubusercontent.com/andrewpryor/f2d52f6016ddf64d8f4d5f4976a2b8de/raw | bash
# Dry-run: curl -sL https://gist.githubusercontent.com/andrewpryor/f2d52f6016ddf64d8f4d5f4976a2b8de/raw | bash -s -- --dry-run
# Last updated: 2025-06-11 09:49:23 CDT
set -euo pipefail
# Parse command line arguments
DRY_RUN=false
FORCE=false
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
--force)
FORCE=true
shift
;;
-h|--help)
echo "env-sync bootstrap script"
echo "Usage: $0 [OPTIONS]"
echo ""
echo "OPTIONS:"
echo " --dry-run Show what would be done without making changes"
echo " --force Force reinstallation even if already present"
echo " --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Source color codes if available, otherwise define minimal set
if [ -f "$HOME/env-sync/helpers/10_colors.sh" ]; then
source "$HOME/env-sync/helpers/10_colors.sh"
else
# Minimal color definitions for bootstrap
cRED="\e[31m"
cGREEN="\e[32m"
cYELLOW="\e[33m"
cBLUE="\e[34m"
cRESET="\e[0m"
fi
# Helper functions
print_status() {
echo -e "${cBLUE}[*]${cRESET} $1"
}
print_success() {
echo -e "${cGREEN}[✓]${cRESET} $1"
}
print_error() {
echo -e "${cRED}[!]${cRESET} $1"
}
print_warning() {
echo -e "${cYELLOW}[!]${cRESET} $1"
}
print_dry_run() {
echo -e "${cYELLOW}[DRY-RUN]${cRESET} $1"
}
# Execute command or show what would be done
execute_cmd() {
local cmd="$1"
local description="$2"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run: $description"
print_dry_run "Command: $cmd"
else
print_status "$description"
eval "$cmd"
fi
}
# Detect OS
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
VER=$VERSION_ID
else
print_error "Cannot detect OS. /etc/os-release not found."
exit 1
fi
print_status "Detected OS: $OS $VER"
}
# Detect architecture
detect_arch() {
ARCH=$(uname -m)
case $ARCH in
x86_64)
ARCH_TYPE="amd64"
;;
aarch64|arm64)
ARCH_TYPE="arm64"
;;
*)
print_error "Unsupported architecture: $ARCH"
exit 1
;;
esac
print_status "Detected architecture: $ARCH_TYPE"
}
# Install GitHub CLI
install_gh() {
if command -v gh &> /dev/null; then
print_success "GitHub CLI already installed ($(gh --version | head -1))"
return 0
fi
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would install GitHub CLI for $OS $ARCH_TYPE"
return 0
fi
print_status "Installing GitHub CLI..."
case $OS in
ubuntu|pop|debian)
# Check if repository already exists
if [ ! -f /etc/apt/sources.list.d/github-cli.list ]; then
execute_cmd "curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg" "Adding GitHub CLI GPG key"
execute_cmd "sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg" "Setting GPG key permissions"
execute_cmd "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main' | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null" "Adding GitHub CLI repository"
else
print_success "GitHub CLI repository already configured"
fi
execute_cmd "sudo apt update" "Updating package lists"
execute_cmd "sudo apt install gh -y" "Installing GitHub CLI"
;;
*)
print_warning "Automatic installation not supported for $OS"
print_status "Attempting manual installation..."
# Direct binary download
GH_VERSION=$(curl -s https://api.github.com/repos/cli/cli/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
execute_cmd "curl -Lo gh.tar.gz 'https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${ARCH_TYPE}.tar.gz'" "Downloading GitHub CLI binary"
execute_cmd "tar -xzf gh.tar.gz" "Extracting GitHub CLI"
execute_cmd "sudo mv gh_${GH_VERSION}_linux_${ARCH_TYPE}/bin/gh /usr/local/bin/" "Installing GitHub CLI binary"
execute_cmd "rm -rf gh.tar.gz gh_${GH_VERSION}_linux_${ARCH_TYPE}" "Cleaning up temporary files"
;;
esac
if command -v gh &> /dev/null; then
print_success "GitHub CLI installed successfully ($(gh --version | head -1))"
else
print_error "Failed to install GitHub CLI"
exit 1
fi
}
# Authenticate with GitHub
authenticate_gh() {
print_status "Checking GitHub authentication..."
if gh auth status &> /dev/null; then
local auth_user=$(gh auth status 2>&1 | grep "Logged in to" | sed 's/.*as \([^[:space:]]*\).*/\1/')
print_success "Already authenticated with GitHub as $auth_user"
return 0
fi
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would prompt for GitHub authentication"
print_dry_run "Authentication method: web browser flow"
return 0
fi
print_warning "GitHub authentication required"
print_status "You will now be prompted to authenticate with GitHub"
# Detect if we're in SSH/headless environment
if [ -n "${SSH_CONNECTION:-}" ] || [ -z "${DISPLAY:-}" ]; then
print_status "Detected headless/SSH session. Using device code flow..."
gh auth login --web --git-protocol https
else
print_status "Using web browser authentication..."
gh auth login --web --git-protocol https
fi
if gh auth status &> /dev/null; then
local auth_user=$(gh auth status 2>&1 | grep "Logged in to" | sed 's/.*as \([^[:space:]]*\).*/\1/')
print_success "GitHub authentication successful as $auth_user"
else
print_error "GitHub authentication failed"
exit 1
fi
}
# Clone env-sync repository
clone_env_sync() {
ENV_SYNC_DIR="$HOME/env-sync"
if [ -d "$ENV_SYNC_DIR/.git" ]; then
print_success "env-sync already exists at $ENV_SYNC_DIR"
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would update existing env-sync installation"
print_dry_run "Would preserve: env-sync.conf, env/ directory"
return 0
fi
if [ "$FORCE" = true ]; then
print_warning "Force flag set - updating env-sync installation"
else
print_status "Updating existing env-sync installation..."
fi
# Create backup
local backup_dir="/tmp/env-sync-backup-$(date +%Y%m%d_%H%M%S)"
print_status "Creating backup at $backup_dir"
mkdir -p "$backup_dir"
# Backup critical files
[ -f "$ENV_SYNC_DIR/env-sync.conf" ] && cp "$ENV_SYNC_DIR/env-sync.conf" "$backup_dir/"
[ -d "$ENV_SYNC_DIR/env" ] && cp -r "$ENV_SYNC_DIR/env" "$backup_dir/"
# Update via git pull instead of destructive re-clone
cd "$ENV_SYNC_DIR"
if git remote get-url origin | grep -q "github.com/andrewpryor/env-sync"; then
print_status "Updating via git pull..."
git pull --rebase
else
print_warning "Repository origin doesn't match expected URL - skipping update"
fi
# Restore backed up files
[ -f "$backup_dir/env-sync.conf" ] && cp "$backup_dir/env-sync.conf" "$ENV_SYNC_DIR/"
[ -d "$backup_dir/env" ] && cp -r "$backup_dir/env/" "$ENV_SYNC_DIR/"
print_success "env-sync updated successfully (backup at $backup_dir)"
else
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would clone andrewpryor/env-sync to $ENV_SYNC_DIR"
return 0
fi
print_status "Cloning env-sync repository..."
gh repo clone andrewpryor/env-sync "$ENV_SYNC_DIR"
print_success "env-sync cloned to $ENV_SYNC_DIR"
fi
}
# Run env-sync
run_env_sync() {
if [ "$DRY_RUN" = true ]; then
print_dry_run "Would run env-sync setup (./env-sync.sh)"
return 0
fi
print_status "Running env-sync setup..."
cd "$HOME/env-sync"
if [ -f "./env-sync.sh" ]; then
bash ./env-sync.sh
else
print_error "env-sync.sh not found!"
exit 1
fi
}
# Main execution
main() {
echo -e "${cGREEN}╔══════════════════════════════════════╗${cRESET}"
echo -e "${cGREEN}║ env-sync Bootstrap Script ║${cRESET}"
echo -e "${cGREEN}╚══════════════════════════════════════╝${cRESET}"
echo
if [ "$DRY_RUN" = true ]; then
echo -e "${cYELLOW} DRY-RUN MODE${cRESET}"
echo -e "${cYELLOW} No changes will be made${cRESET}"
echo
fi
# Check if running as root
if [ "$EUID" -eq 0 ]; then
print_error "Do not run this script as root"
exit 1
fi
# Step 1: Detect system
detect_os
detect_arch
# Step 2: Install GitHub CLI
install_gh
# Step 3: Authenticate
authenticate_gh
# Step 4: Clone repository
clone_env_sync
# Step 5: Run env-sync
run_env_sync
if [ "$DRY_RUN" = true ]; then
print_success "Dry-run completed successfully!"
echo
echo "To actually run the bootstrap:"
echo " curl -sL https://gist.githubusercontent.com/andrewpryor/f2d52f6016ddf64d8f4d5f4976a2b8de/raw | bash"
else
print_success "Bootstrap completed successfully!"
fi
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment