Skip to content

Instantly share code, notes, and snippets.

@gbozee
Created January 17, 2026 00:16
Show Gist options
  • Select an option

  • Save gbozee/dde0df52635cdd27eb09a3c1bf512d56 to your computer and use it in GitHub Desktop.

Select an option

Save gbozee/dde0df52635cdd27eb09a3c1bf512d56 to your computer and use it in GitHub Desktop.
Tuteria Plus Sprite Bootstrap Script v3
#!/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