Created
January 17, 2026 00:16
-
-
Save gbozee/dde0df52635cdd27eb09a3c1bf512d56 to your computer and use it in GitHub Desktop.
Tuteria Plus Sprite Bootstrap Script v3
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 characters
| #!/bin/bash | |
| # | |
| # bootstrap-sprite.sh - Bootstrap a Fly.io Sprites sandbox with Tuteria Plus environment | |
| # | |
| # Usage: | |
| # # Set required environment variables first: | |
| # export STARTUP_SH_BASE64=$(cat startup.sh | base64 -w0) | |
| # export SSH_PRIVATE_KEY_BASE64=$(cat ~/.ssh/id_ed25519 | base64 -w0) | |
| # export GITHUB_PAT="ghp_your_token_here" | |
| # | |
| # # Then run: | |
| # curl -fsSL https://raw.githubusercontent.com/gbozee/tuteria-plus/next/scripts/devops/bootstrap-sprite.sh | bash | |
| # | |
| # # Or locally: | |
| # ./scripts/devops/bootstrap-sprite.sh | |
| # | |
| # Required Environment Variables: | |
| # STARTUP_SH_BASE64 - Base64-encoded contents of startup.sh | |
| # SSH_PRIVATE_KEY_BASE64 - Base64-encoded SSH private key for git operations | |
| # GITHUB_PAT - GitHub Personal Access Token (needs repo, read:org scopes) | |
| # | |
| # Optional Environment Variables: | |
| # REPO_URL - Git repository URL (default: https://github.com/Tuteria/tuteria-plus.git) | |
| # BASE_BRANCH - Branch to checkout (default: next) | |
| # PROJECT_DIR - Directory to clone into (default: ~/tuteria-plus) | |
| # BUN_VERSION - Bun version to install (default: 1.3.0) | |
| # | |
| set -euo pipefail | |
| # ============================================ | |
| # Configuration | |
| # ============================================ | |
| readonly DEFAULT_REPO_URL="https://github.com/Tuteria/tuteria-plus.git" | |
| readonly DEFAULT_BASE_BRANCH="next" | |
| readonly DEFAULT_PROJECT_DIR="${HOME}/tuteria-plus" | |
| readonly DEFAULT_BUN_VERSION="1.3.0" | |
| REPO_URL="${REPO_URL:-$DEFAULT_REPO_URL}" | |
| BASE_BRANCH="${BASE_BRANCH:-$DEFAULT_BASE_BRANCH}" | |
| PROJECT_DIR="${PROJECT_DIR:-$DEFAULT_PROJECT_DIR}" | |
| BUN_VERSION="${BUN_VERSION:-$DEFAULT_BUN_VERSION}" | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' # No Color | |
| # ============================================ | |
| # Helper Functions | |
| # ============================================ | |
| log_info() { | |
| echo -e "${BLUE}[INFO]${NC} $1" | |
| } | |
| log_success() { | |
| echo -e "${GREEN}[SUCCESS]${NC} $1" | |
| } | |
| log_warning() { | |
| echo -e "${YELLOW}[WARNING]${NC} $1" | |
| } | |
| log_error() { | |
| echo -e "${RED}[ERROR]${NC} $1" | |
| } | |
| log_step() { | |
| echo "" | |
| echo -e "${CYAN}========================================${NC}" | |
| echo -e "${CYAN}$1${NC}" | |
| echo -e "${CYAN}========================================${NC}" | |
| } | |
| check_command() { | |
| if command -v "$1" &> /dev/null; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| # ============================================ | |
| # Phase 1: Validate Required Environment Variables | |
| # ============================================ | |
| validate_environment() { | |
| log_step "Phase 1: Validating Environment Variables" | |
| local missing_vars=() | |
| if [ -z "${STARTUP_SH_BASE64:-}" ]; then | |
| missing_vars+=("STARTUP_SH_BASE64") | |
| fi | |
| if [ -z "${SSH_PRIVATE_KEY_BASE64:-}" ]; then | |
| missing_vars+=("SSH_PRIVATE_KEY_BASE64") | |
| fi | |
| if [ -z "${GITHUB_PAT:-}" ]; then | |
| missing_vars+=("GITHUB_PAT") | |
| fi | |
| if [ ${#missing_vars[@]} -gt 0 ]; then | |
| log_error "Missing required environment variables:" | |
| for var in "${missing_vars[@]}"; do | |
| echo " - $var" | |
| done | |
| echo "" | |
| echo "To prepare these variables on your local machine:" | |
| echo "" | |
| echo " # Encode startup.sh" | |
| echo " export STARTUP_SH_BASE64=\$(cat startup.sh | base64 -w0)" | |
| echo "" | |
| echo " # Encode SSH private key (must be added to GitHub)" | |
| echo " export SSH_PRIVATE_KEY_BASE64=\$(cat ~/.ssh/id_ed25519 | base64 -w0)" | |
| echo "" | |
| echo " # Set GitHub PAT" | |
| echo " export GITHUB_PAT=\"ghp_your_token_here\"" | |
| exit 1 | |
| fi | |
| log_success "All required environment variables are set" | |
| log_info "Repository URL: $REPO_URL" | |
| log_info "Base branch: $BASE_BRANCH" | |
| log_info "Project directory: $PROJECT_DIR" | |
| log_info "Bun version: $BUN_VERSION" | |
| } | |
| # ============================================ | |
| # Phase 2: Install System Dependencies | |
| # ============================================ | |
| install_dependencies() { | |
| log_step "Phase 2: Installing System Dependencies" | |
| # Detect package manager | |
| local pkg_manager="" | |
| if check_command apt-get; then | |
| pkg_manager="apt" | |
| elif check_command apk; then | |
| pkg_manager="apk" | |
| elif check_command yum; then | |
| pkg_manager="yum" | |
| elif check_command dnf; then | |
| pkg_manager="dnf" | |
| fi | |
| # Install basic tools if package manager is available | |
| if [ -n "$pkg_manager" ]; then | |
| log_info "Detected package manager: $pkg_manager" | |
| case "$pkg_manager" in | |
| apt) | |
| log_info "Updating apt cache..." | |
| sudo apt-get update -qq 2>/dev/null || apt-get update -qq 2>/dev/null || true | |
| log_info "Installing git, curl, jq, unzip..." | |
| sudo apt-get install -y -qq git curl jq unzip 2>/dev/null || apt-get install -y -qq git curl jq unzip 2>/dev/null || true | |
| ;; | |
| apk) | |
| log_info "Installing git, curl, jq, unzip via apk..." | |
| sudo apk add --no-cache git curl jq unzip bash 2>/dev/null || apk add --no-cache git curl jq unzip bash 2>/dev/null || true | |
| ;; | |
| yum|dnf) | |
| log_info "Installing git, curl, jq, unzip via $pkg_manager..." | |
| sudo $pkg_manager install -y git curl jq unzip 2>/dev/null || $pkg_manager install -y git curl jq unzip 2>/dev/null || true | |
| ;; | |
| esac | |
| else | |
| log_warning "No package manager detected, assuming dependencies are installed" | |
| fi | |
| # Install Bun | |
| if check_command bun; then | |
| local current_bun_version=$(bun --version 2>/dev/null || echo "unknown") | |
| log_info "Bun is already installed (version: $current_bun_version)" | |
| else | |
| log_info "Installing Bun v${BUN_VERSION}..." | |
| curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" | |
| # Add bun to PATH for current session | |
| export BUN_INSTALL="${HOME}/.bun" | |
| export PATH="${BUN_INSTALL}/bin:${PATH}" | |
| fi | |
| # Ensure bun is in PATH | |
| if [ -d "${HOME}/.bun/bin" ]; then | |
| export PATH="${HOME}/.bun/bin:${PATH}" | |
| fi | |
| # Install GitHub CLI | |
| if check_command gh; then | |
| log_info "GitHub CLI is already installed" | |
| else | |
| log_info "Installing GitHub CLI..." | |
| case "$pkg_manager" in | |
| apt) | |
| # Install gh via apt repository | |
| curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null || \ | |
| curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null || true | |
| sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null || chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null || true | |
| 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 2>/dev/null || \ | |
| echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null 2>/dev/null || true | |
| sudo apt-get update -qq 2>/dev/null || apt-get update -qq 2>/dev/null || true | |
| sudo apt-get install -y -qq gh 2>/dev/null || apt-get install -y -qq gh 2>/dev/null || true | |
| ;; | |
| apk) | |
| sudo apk add --no-cache github-cli 2>/dev/null || apk add --no-cache github-cli 2>/dev/null || true | |
| ;; | |
| yum|dnf) | |
| sudo $pkg_manager install -y gh 2>/dev/null || $pkg_manager install -y gh 2>/dev/null || true | |
| ;; | |
| *) | |
| # Fallback: install from binary | |
| log_info "Installing gh CLI from binary release..." | |
| local gh_version="2.62.0" | |
| local arch=$(uname -m) | |
| case "$arch" in | |
| x86_64) arch="amd64" ;; | |
| aarch64|arm64) arch="arm64" ;; | |
| esac | |
| curl -fsSL "https://github.com/cli/cli/releases/download/v${gh_version}/gh_${gh_version}_linux_${arch}.tar.gz" | tar -xz | |
| sudo mv "gh_${gh_version}_linux_${arch}/bin/gh" /usr/local/bin/ 2>/dev/null || mv "gh_${gh_version}_linux_${arch}/bin/gh" /usr/local/bin/ 2>/dev/null || mv "gh_${gh_version}_linux_${arch}/bin/gh" "${HOME}/.local/bin/" | |
| rm -rf "gh_${gh_version}_linux_${arch}" | |
| ;; | |
| esac | |
| fi | |
| # Verify installations | |
| log_info "Verifying installations..." | |
| local errors=() | |
| if check_command git; then | |
| log_success "git: $(git --version)" | |
| else | |
| errors+=("git not found") | |
| fi | |
| if check_command curl; then | |
| log_success "curl: $(curl --version | head -1)" | |
| else | |
| errors+=("curl not found") | |
| fi | |
| if check_command jq; then | |
| log_success "jq: $(jq --version)" | |
| else | |
| errors+=("jq not found") | |
| fi | |
| if check_command bun; then | |
| log_success "bun: $(bun --version)" | |
| else | |
| errors+=("bun not found") | |
| fi | |
| if check_command gh; then | |
| log_success "gh: $(gh --version | head -1)" | |
| else | |
| errors+=("gh not found") | |
| fi | |
| if [ ${#errors[@]} -gt 0 ]; then | |
| log_error "Some dependencies are missing:" | |
| for err in "${errors[@]}"; do | |
| echo " - $err" | |
| done | |
| exit 1 | |
| fi | |
| log_success "All dependencies installed successfully" | |
| } | |
| # ============================================ | |
| # Phase 3: SSH Key Setup | |
| # ============================================ | |
| setup_ssh() { | |
| log_step "Phase 3: Setting Up SSH Keys" | |
| local ssh_dir="${HOME}/.ssh" | |
| # Create .ssh directory | |
| log_info "Creating SSH directory..." | |
| mkdir -p "$ssh_dir" | |
| chmod 700 "$ssh_dir" | |
| # Decode SSH key to temp file first to detect type | |
| local temp_key=$(mktemp) | |
| echo "${SSH_PRIVATE_KEY_BASE64}" | base64 -d > "$temp_key" | |
| # Detect key type from content | |
| local ssh_key | |
| if grep -q "BEGIN RSA PRIVATE KEY" "$temp_key" 2>/dev/null; then | |
| ssh_key="${ssh_dir}/id_rsa" | |
| log_info "Detected RSA key" | |
| elif grep -q "BEGIN OPENSSH PRIVATE KEY" "$temp_key" 2>/dev/null; then | |
| # Could be ed25519 or newer RSA format - check key size | |
| if [ $(wc -c < "$temp_key") -lt 1000 ]; then | |
| ssh_key="${ssh_dir}/id_ed25519" | |
| log_info "Detected Ed25519 key" | |
| else | |
| ssh_key="${ssh_dir}/id_rsa" | |
| log_info "Detected RSA key (OpenSSH format)" | |
| fi | |
| else | |
| ssh_key="${ssh_dir}/id_rsa" | |
| log_info "Unknown key type, defaulting to RSA" | |
| fi | |
| # Move temp key to final location | |
| log_info "Writing SSH private key..." | |
| mv "$temp_key" "$ssh_key" | |
| chmod 600 "$ssh_key" | |
| # Generate public key from private key | |
| log_info "Generating public key..." | |
| ssh-keygen -y -f "$ssh_key" > "${ssh_key}.pub" 2>/dev/null || true | |
| chmod 644 "${ssh_key}.pub" 2>/dev/null || true | |
| # Add GitHub to known_hosts | |
| log_info "Adding github.com to known_hosts..." | |
| ssh-keyscan -t ed25519,rsa github.com >> "${ssh_dir}/known_hosts" 2>/dev/null || true | |
| chmod 644 "${ssh_dir}/known_hosts" | |
| # Configure SSH to use the key for GitHub | |
| log_info "Configuring SSH for GitHub..." | |
| cat > "${ssh_dir}/config" << EOF | |
| Host github.com | |
| HostName github.com | |
| User git | |
| IdentityFile ${ssh_key} | |
| IdentitiesOnly yes | |
| StrictHostKeyChecking accept-new | |
| EOF | |
| chmod 600 "${ssh_dir}/config" | |
| # Test SSH connection | |
| log_info "Testing SSH connection to GitHub..." | |
| if ssh -T git@github.com 2>&1 | grep -q "successfully authenticated"; then | |
| log_success "SSH connection to GitHub successful" | |
| else | |
| # SSH -T returns exit code 1 even on success for GitHub | |
| local ssh_output=$(ssh -T git@github.com 2>&1 || true) | |
| if echo "$ssh_output" | grep -qi "successfully authenticated\|Hi \|You've successfully authenticated"; then | |
| log_success "SSH connection to GitHub successful" | |
| else | |
| log_warning "SSH test returned: $ssh_output" | |
| log_warning "Continuing anyway - connection may still work" | |
| fi | |
| fi | |
| } | |
| # ============================================ | |
| # Phase 4: GitHub CLI Authentication | |
| # ============================================ | |
| setup_github_cli() { | |
| log_step "Phase 4: Authenticating GitHub CLI" | |
| log_info "Authenticating gh CLI with PAT..." | |
| echo "${GITHUB_PAT}" | gh auth login --with-token | |
| # Configure git to use gh for authentication | |
| gh auth setup-git | |
| # Verify authentication | |
| log_info "Verifying GitHub authentication..." | |
| if gh auth status; then | |
| log_success "GitHub CLI authenticated successfully" | |
| else | |
| log_error "GitHub CLI authentication failed" | |
| exit 1 | |
| fi | |
| } | |
| # ============================================ | |
| # Phase 5: Clone Repository | |
| # ============================================ | |
| clone_repository() { | |
| log_step "Phase 5: Cloning Repository" | |
| # Convert HTTPS URL to SSH if needed | |
| local clone_url="$REPO_URL" | |
| if [[ "$REPO_URL" =~ ^https://github\.com/(.+)$ ]]; then | |
| clone_url="git@github.com:${BASH_REMATCH[1]}" | |
| log_info "Converted to SSH URL: $clone_url" | |
| fi | |
| if [ -d "$PROJECT_DIR" ]; then | |
| log_info "Project directory already exists: $PROJECT_DIR" | |
| log_info "Checking if it's a valid git repository..." | |
| if [ -d "${PROJECT_DIR}/.git" ]; then | |
| log_info "Valid git repository found, updating..." | |
| cd "$PROJECT_DIR" | |
| git fetch origin | |
| git checkout "$BASE_BRANCH" | |
| git pull origin "$BASE_BRANCH" | |
| log_success "Repository updated successfully" | |
| else | |
| log_warning "Directory exists but is not a git repository" | |
| log_info "Removing and re-cloning..." | |
| rm -rf "$PROJECT_DIR" | |
| log_info "Cloning via SSH..." | |
| git clone "$clone_url" "$PROJECT_DIR" | |
| cd "$PROJECT_DIR" | |
| git checkout "$BASE_BRANCH" | |
| log_success "Repository cloned successfully" | |
| fi | |
| else | |
| log_info "Cloning via SSH..." | |
| git clone "$clone_url" "$PROJECT_DIR" | |
| cd "$PROJECT_DIR" | |
| git checkout "$BASE_BRANCH" | |
| log_success "Repository cloned successfully" | |
| fi | |
| # Show current state | |
| log_info "Current branch: $(git branch --show-current)" | |
| log_info "Latest commit: $(git log -1 --oneline)" | |
| } | |
| # ============================================ | |
| # Phase 6: Setup Secrets | |
| # ============================================ | |
| setup_secrets() { | |
| log_step "Phase 6: Setting Up Secrets" | |
| cd "$PROJECT_DIR" | |
| local startup_file="${PROJECT_DIR}/startup.sh" | |
| log_info "Decoding startup.sh..." | |
| echo "${STARTUP_SH_BASE64}" | base64 -d > "$startup_file" | |
| chmod +x "$startup_file" | |
| # Verify the file was created and has content | |
| if [ -f "$startup_file" ] && [ -s "$startup_file" ]; then | |
| local line_count=$(wc -l < "$startup_file") | |
| log_success "startup.sh created successfully ($line_count lines)" | |
| else | |
| log_error "Failed to create startup.sh or file is empty" | |
| exit 1 | |
| fi | |
| # Source the startup file to load environment variables | |
| log_info "Sourcing startup.sh to load environment variables..." | |
| set +u # Temporarily allow unset variables during sourcing | |
| source "$startup_file" | |
| set -u | |
| # Verify key environment variables are set | |
| if [ -n "${DATABASE_URL:-}" ]; then | |
| log_success "DATABASE_URL is set" | |
| else | |
| log_warning "DATABASE_URL is not set - check startup.sh contents" | |
| fi | |
| if [ -n "${APPWRITE_API_KEY:-}" ]; then | |
| log_success "APPWRITE_API_KEY is set" | |
| else | |
| log_warning "APPWRITE_API_KEY is not set - check startup.sh contents" | |
| fi | |
| } | |
| # ============================================ | |
| # Phase 7: Install Project Dependencies | |
| # ============================================ | |
| install_project_dependencies() { | |
| log_step "Phase 7: Installing Project Dependencies" | |
| cd "$PROJECT_DIR" | |
| log_info "Running bun install..." | |
| bun install | |
| log_success "Dependencies installed successfully" | |
| } | |
| # ============================================ | |
| # Phase 8: Verification & Summary | |
| # ============================================ | |
| verify_and_summarize() { | |
| log_step "Phase 8: Verification & Summary" | |
| cd "$PROJECT_DIR" | |
| echo "" | |
| echo -e "${GREEN}============================================${NC}" | |
| echo -e "${GREEN} Bootstrap Complete!${NC}" | |
| echo -e "${GREEN}============================================${NC}" | |
| echo "" | |
| echo -e "${CYAN}Environment:${NC}" | |
| echo " Project Directory: $PROJECT_DIR" | |
| echo " Current Branch: $(git branch --show-current)" | |
| echo " Latest Commit: $(git log -1 --oneline)" | |
| echo "" | |
| echo -e "${CYAN}Tools:${NC}" | |
| echo " Bun: $(bun --version)" | |
| echo " Git: $(git --version | cut -d' ' -f3)" | |
| echo " gh CLI: $(gh --version | head -1 | cut -d' ' -f3)" | |
| echo "" | |
| echo -e "${CYAN}GitHub Authentication:${NC}" | |
| gh auth status 2>&1 | head -3 | sed 's/^/ /' | |
| echo "" | |
| echo -e "${CYAN}Environment Variables:${NC}" | |
| [ -n "${DATABASE_URL:-}" ] && echo " ✅ DATABASE_URL is set" || echo " ⚠️ DATABASE_URL is NOT set" | |
| [ -n "${APPWRITE_API_KEY:-}" ] && echo " ✅ APPWRITE_API_KEY is set" || echo " ⚠️ APPWRITE_API_KEY is NOT set" | |
| [ -n "${APPWRITE_PROJECT_ID:-}" ] && echo " ✅ APPWRITE_PROJECT_ID is set" || echo " ⚠️ APPWRITE_PROJECT_ID is NOT set" | |
| [ -n "${MINIO_ENDPOINT:-}" ] && echo " ✅ MINIO_ENDPOINT is set" || echo " ⚠️ MINIO_ENDPOINT is NOT set" | |
| echo "" | |
| echo -e "${CYAN}Next Steps:${NC}" | |
| echo " 1. cd $PROJECT_DIR" | |
| echo " 2. source startup.sh # Load environment (already done)" | |
| echo " 3. bun run dev # Start development server" | |
| echo "" | |
| echo " Or run specific commands:" | |
| echo " - bun run build # Build the project" | |
| echo " - bun run lint # Run linter" | |
| echo " - bun test # Run tests" | |
| echo "" | |
| log_success "Sprite bootstrap completed successfully!" | |
| } | |
| # ============================================ | |
| # Main Execution | |
| # ============================================ | |
| main() { | |
| echo "" | |
| echo -e "${BLUE}============================================${NC}" | |
| echo -e "${BLUE} Tuteria Plus - Sprite Bootstrap Script${NC}" | |
| echo -e "${BLUE}============================================${NC}" | |
| echo "" | |
| validate_environment | |
| install_dependencies | |
| setup_ssh | |
| setup_github_cli | |
| clone_repository | |
| setup_secrets | |
| install_project_dependencies | |
| verify_and_summarize | |
| } | |
| # Run main function | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment