Files
ai-recruit-site-template/deploy-to-apps.example.sh
Mikael Westöö 0fce839b7c Fix POSIX shell compatibility in deployment script
- Replace bash-specific &> with >/dev/null 2>&1
- Replace bash-specific == with POSIX-compliant = in [ ] tests
- Script now works with both sh and bash
- Fixes "unexpected operator" errors when run with sh

Issue: User ran script with 'sh' instead of 'bash', causing errors
Root cause: Bash-specific syntax (== and &>) not compatible with POSIX sh
Solution: Use POSIX-compliant alternatives that work in both shells
2026-01-24 19:58:32 +01:00

658 lines
20 KiB
Bash

#!/bin/bash
# ========================================
# AI Recruitment Site - SAAC Deployment Script
# ========================================
# This script deploys your recruitment site to StartAnAiCompany infrastructure
# via apps.startanaicompany.com
#
# IMPORTANT: Copy this file to deploy-to-apps.sh and customize it
# DO NOT commit deploy-to-apps.sh to git (it's in .gitignore)
#
# Prerequisites:
# 1. Install jq (JSON processor): apt install jq or brew install jq
# 2. Set GITEA_API_TOKEN environment variable (required for setup mode)
# 3. Customize your .env file with company information
#
# First-time users:
# - Script will prompt for email and handle registration/verification automatically
# - Check MailHog at https://mailhog.goryan.io for verification code
# - Configuration saved to .saac.json (do not commit to git!)
#
# Modes:
# ./deploy-to-apps.sh # Auto-detect (setup or update)
# ./deploy-to-apps.sh --setup # Force new deployment
# ./deploy-to-apps.sh --update # Force update existing
# ./deploy-to-apps.sh --status # Check deployment status
set -e # Exit on error
# Configuration
SAAC_CONFIG_FILE=".saac.json"
SAAC_API="https://apps.startanaicompany.com/api/v1"
GITEA_API="https://git.startanaicompany.com/api/v1"
# ========================================
# Helper Functions
# ========================================
# Load configuration from .saac.json
load_config() {
if [ -f "$SAAC_CONFIG_FILE" ]; then
USER_EMAIL=$(jq -r '.user.email // ""' "$SAAC_CONFIG_FILE")
USER_ID=$(jq -r '.user.user_id // ""' "$SAAC_CONFIG_FILE")
SAAC_API_KEY=$(jq -r '.user.api_key // ""' "$SAAC_CONFIG_FILE")
GITEA_USERNAME=$(jq -r '.user.gitea_username // ""' "$SAAC_CONFIG_FILE")
VERIFIED=$(jq -r '.user.verified // false' "$SAAC_CONFIG_FILE")
APP_UUID=$(jq -r '.deployment.application_uuid // ""' "$SAAC_CONFIG_FILE")
APP_NAME=$(jq -r '.deployment.application_name // ""' "$SAAC_CONFIG_FILE")
DEPLOYED_AT=$(jq -r '.deployment.deployed_at // ""' "$SAAC_CONFIG_FILE")
return 0
fi
return 1
}
# Save configuration to .saac.json
save_config() {
cat > "$SAAC_CONFIG_FILE" <<CONFIG
{
"version": "1.0",
"user": {
"email": "$USER_EMAIL",
"user_id": "$USER_ID",
"api_key": "$SAAC_API_KEY",
"gitea_username": "$GITEA_USERNAME",
"verified": $VERIFIED,
"registered_at": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
},
"deployment": {
"application_uuid": "$APP_UUID",
"application_name": "$APP_NAME",
"domain": "$DOMAIN",
"subdomain": "$SUBDOMAIN",
"repository": "$REPO_URL",
"deployed_at": "$DEPLOYED_AT",
"last_updated": "$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
},
"config": {
"saac_api": "$SAAC_API",
"gitea_api": "$GITEA_API"
}
}
CONFIG
chmod 600 "$SAAC_CONFIG_FILE"
echo "💾 Configuration saved to $SAAC_CONFIG_FILE"
}
# ========================================
# Check Dependencies
# ========================================
# Check if jq is installed
if ! command -v jq >/dev/null 2>&1; then
echo "❌ Error: jq is not installed"
echo ""
echo "jq is required for JSON processing. Install it with:"
echo " - Ubuntu/Debian: sudo apt install jq"
echo " - macOS: brew install jq"
echo " - Alpine: apk add jq"
echo ""
exit 1
fi
# ========================================
# Parse Command Line Arguments
# ========================================
MODE="auto"
if [ "$1" = "--setup" ]; then
MODE="setup"
elif [ "$1" = "--update" ]; then
MODE="update"
elif [ "$1" = "--status" ]; then
MODE="status"
elif [ -n "$1" ]; then
echo "❌ Unknown option: $1"
echo ""
echo "Usage:"
echo " $0 # Auto-detect mode"
echo " $0 --setup # Force new deployment"
echo " $0 --update # Force update existing"
echo " $0 --status # Check deployment status"
exit 1
fi
# ========================================
# Load Environment Variables
# ========================================
if [ ! -f .env ]; then
echo "❌ Error: .env file not found. Copy .env.example to .env and customize it."
exit 1
fi
source .env
# Check required .env variables
if [ -z "$COMPANY_NAME" ]; then
echo "❌ Error: COMPANY_NAME not set in .env"
exit 1
fi
if [ -z "$SUBDOMAIN" ]; then
echo "❌ Error: SUBDOMAIN not set in .env"
exit 1
fi
# ========================================
# Load or Create Configuration
# ========================================
# Try to load existing configuration
if load_config; then
echo "✅ Loaded configuration from $SAAC_CONFIG_FILE"
echo " User: $USER_EMAIL"
echo " Verified: $VERIFIED"
if [ -n "$APP_UUID" ]; then
echo " Application: $APP_UUID"
fi
echo ""
else
# No configuration exists - need to register
echo "========================================="
echo " First-Time Setup"
echo "========================================="
echo ""
echo "No configuration found. Let's register your account."
echo ""
# Get Gitea username from .env
if [ -z "$GITEA_USERNAME" ]; then
echo "❌ Error: GITEA_USERNAME not set in .env"
exit 1
fi
# Prompt for email
read -p "Enter your email address: " USER_EMAIL
if [ -z "$USER_EMAIL" ]; then
echo "❌ Error: Email address is required"
exit 1
fi
echo ""
echo "📧 Registering user: $USER_EMAIL"
echo " Gitea username: $GITEA_USERNAME"
echo ""
# Register user
REGISTER_RESPONSE=$(curl -s -X POST "${SAAC_API}/users/register" \
-H "Content-Type: application/json" \
-d "{\"email\":\"$USER_EMAIL\",\"gitea_username\":\"$GITEA_USERNAME\"}")
# Check for errors
if echo "$REGISTER_RESPONSE" | grep -q "error"; then
echo "❌ Registration failed:"
echo "$REGISTER_RESPONSE" | jq '.' 2>/dev/null || echo "$REGISTER_RESPONSE"
exit 1
fi
# Extract user data
USER_ID=$(echo "$REGISTER_RESPONSE" | jq -r '.user_id')
SAAC_API_KEY=$(echo "$REGISTER_RESPONSE" | jq -r '.api_key')
if [ "$USER_ID" = "null" ] || [ -z "$USER_ID" ] || [ "$SAAC_API_KEY" = "null" ] || [ -z "$SAAC_API_KEY" ]; then
echo "❌ Failed to extract user data from registration response"
echo "Response: $REGISTER_RESPONSE"
exit 1
fi
echo "✅ User registered!"
echo " User ID: $USER_ID"
echo ""
# Save initial configuration (unverified)
VERIFIED=false
APP_UUID=""
APP_NAME=""
DOMAIN=""
REPO_URL=""
DEPLOYED_AT=""
save_config
echo ""
# Prompt for verification code
echo "========================================="
echo " Email Verification"
echo "========================================="
echo ""
echo "📧 Verification email sent to: $USER_EMAIL"
echo ""
echo "🔍 Check your email at MailHog:"
echo " https://mailhog.goryan.io"
echo ""
read -p "Enter the verification code from the email: " VERIFY_CODE
if [ -z "$VERIFY_CODE" ]; then
echo "❌ Error: Verification code is required"
echo ""
echo "You can verify later by running this script again."
echo "Your configuration has been saved to $SAAC_CONFIG_FILE"
exit 1
fi
echo ""
echo "🔐 Verifying email..."
# Verify email
VERIFY_RESPONSE=$(curl -s -X POST "${SAAC_API}/users/verify" \
-H "X-API-Key: ${SAAC_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"verification_code\":\"$VERIFY_CODE\"}")
# Check if verified
if echo "$VERIFY_RESPONSE" | grep -q '"verified":true'; then
echo "✅ Email verified successfully!"
echo ""
VERIFIED=true
save_config
echo ""
else
echo "❌ Verification failed"
echo "$VERIFY_RESPONSE" | jq '.' 2>/dev/null || echo "$VERIFY_RESPONSE"
echo ""
echo "You can verify later by running this script again."
echo "Your configuration has been saved to $SAAC_CONFIG_FILE"
exit 1
fi
fi
# ========================================
# Check Verification Status
# ========================================
if [ "$VERIFIED" != "true" ]; then
echo "========================================="
echo " Email Verification Required"
echo "========================================="
echo ""
echo "Your email ($USER_EMAIL) is not verified yet."
echo ""
echo "🔍 Check your email at MailHog:"
echo " https://mailhog.goryan.io"
echo ""
read -p "Enter the verification code from the email: " VERIFY_CODE
if [ -z "$VERIFY_CODE" ]; then
echo "❌ Error: Verification code is required"
exit 1
fi
echo ""
echo "🔐 Verifying email..."
# Verify email
VERIFY_RESPONSE=$(curl -s -X POST "${SAAC_API}/users/verify" \
-H "X-API-Key: ${SAAC_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"verification_code\":\"$VERIFY_CODE\"}")
# Check if verified
if echo "$VERIFY_RESPONSE" | grep -q '"verified":true'; then
echo "✅ Email verified successfully!"
echo ""
VERIFIED=true
save_config
echo ""
else
echo "❌ Verification failed"
echo "$VERIFY_RESPONSE" | jq '.' 2>/dev/null || echo "$VERIFY_RESPONSE"
exit 1
fi
fi
# ========================================
# Auto-detect Mode
# ========================================
if [ "$MODE" = "auto" ]; then
if [ -n "$APP_UUID" ]; then
MODE="update"
else
MODE="setup"
fi
fi
# ========================================
# Setup Mode: Additional Checks
# ========================================
if [ "$MODE" = "setup" ]; then
# GITEA_API_TOKEN only required for setup mode (webhook creation)
if [ -z "$GITEA_API_TOKEN" ]; then
echo "❌ Error: GITEA_API_TOKEN environment variable not set"
echo ""
echo "GITEA_API_TOKEN is required for setup mode to:"
echo " - Set up automatic deployment webhooks"
echo ""
echo "To get your Gitea API token:"
echo "1. Go to https://git.startanaicompany.com"
echo "2. Click your profile → Settings → Applications"
echo "3. Generate New Token (grant 'repo' permissions)"
echo "4. Export it: export GITEA_API_TOKEN='your_token_here'"
echo ""
exit 1
fi
if [ -z "$GITEA_REPO_NAME" ]; then
echo "❌ Error: GITEA_REPO_NAME not set in .env"
exit 1
fi
fi
# ========================================
# Build URLs
# ========================================
# Repository URL (SSH format required by Coolify for private repos)
REPO_URL="git@git.startanaicompany.com:${GITEA_USERNAME}/${GITEA_REPO_NAME}.git"
# Domain (e.g., annarecruit.startanaicompany.com or johnrecruit.startanaicompany.com)
FULL_DOMAIN="${SUBDOMAIN}recruit.startanaicompany.com"
# ========================================
# MODE: STATUS
# ========================================
if [ "$MODE" = "status" ]; then
echo "========================================="
echo " Deployment Status"
echo "========================================="
echo ""
# Check if deployment exists
if [ -z "$APP_UUID" ]; then
echo "❌ No deployment found"
echo " Run './deploy-to-apps.sh --setup' to create a new deployment"
exit 1
fi
echo "📦 Fetching status for application: $APP_UUID"
echo ""
# Get application details
STATUS_RESPONSE=$(curl -s -X GET "${SAAC_API}/applications/${APP_UUID}" \
-H "X-API-Key: ${SAAC_API_KEY}")
# Check for errors
if echo "$STATUS_RESPONSE" | grep -q "error"; then
echo "❌ Failed to fetch status:"
echo "$STATUS_RESPONSE" | jq '.' 2>/dev/null || echo "$STATUS_RESPONSE"
exit 1
fi
# Display formatted status
echo "Application UUID: $APP_UUID"
echo "Name: $(echo "$STATUS_RESPONSE" | jq -r '.app_name')"
echo "Domain: $(echo "$STATUS_RESPONSE" | jq -r '.domain')"
echo "Status: $(echo "$STATUS_RESPONSE" | jq -r '.status')"
echo "Repository: $(echo "$STATUS_RESPONSE" | jq -r '.git_repo')"
echo "Branch: $(echo "$STATUS_RESPONSE" | jq -r '.git_branch')"
echo "Created: $(echo "$STATUS_RESPONSE" | jq -r '.created_at')"
echo ""
echo "🔍 View logs:"
echo " curl -H \"X-API-Key: ${SAAC_API_KEY}\" \\"
echo " ${SAAC_API}/applications/${APP_UUID}/logs"
echo ""
exit 0
fi
# ========================================
# MODE: UPDATE
# ========================================
if [ "$MODE" = "update" ]; then
echo "========================================="
echo " AI Recruitment Site Deployment"
echo "========================================="
echo "📝 Mode: UPDATE (existing deployment)"
echo ""
# Check if deployment exists
if [ -z "$APP_UUID" ]; then
echo "❌ No deployment found in configuration"
echo " Run './deploy-to-apps.sh --setup' to create a new deployment"
exit 1
fi
echo "📦 Configuration:"
echo " Application UUID: $APP_UUID"
echo " Company: $COMPANY_NAME"
echo " Domain: https://$FULL_DOMAIN"
echo ""
# Verify application still exists
echo "🔍 Verifying application exists..."
VERIFY_RESPONSE=$(curl -s -X GET "${SAAC_API}/applications/${APP_UUID}" \
-H "X-API-Key: ${SAAC_API_KEY}")
if echo "$VERIFY_RESPONSE" | grep -q "error"; then
echo "❌ Application not found or access denied"
echo " The application may have been deleted or UUID is invalid"
echo " Run './deploy-to-apps.sh --setup' to create a new deployment"
exit 1
fi
echo "✅ Application verified"
echo ""
# Update environment variables
echo "🔄 Updating environment variables..."
UPDATE_RESPONSE=$(curl -s -X PATCH "${SAAC_API}/applications/${APP_UUID}/env" \
-H "X-API-Key: ${SAAC_API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"variables\": {
\"COMPANY_NAME\": \"${COMPANY_NAME}\",
\"COMPANY_TAGLINE\": \"${COMPANY_TAGLINE}\",
\"COMPANY_DESCRIPTION\": \"${COMPANY_DESCRIPTION}\",
\"PRIMARY_COLOR\": \"${PRIMARY_COLOR}\",
\"ACCENT_COLOR\": \"${ACCENT_COLOR}\",
\"DARK_COLOR\": \"${DARK_COLOR}\",
\"CONTACT_EMAIL\": \"${CONTACT_EMAIL}\",
\"CONTACT_PHONE\": \"${CONTACT_PHONE}\",
\"CONTACT_ADDRESS\": \"${CONTACT_ADDRESS}\"
}
}")
# Check for errors
if echo "$UPDATE_RESPONSE" | grep -q "error"; then
echo "❌ Failed to update environment variables:"
echo "$UPDATE_RESPONSE" | jq '.' 2>/dev/null || echo "$UPDATE_RESPONSE"
exit 1
fi
echo "✅ Environment variables updated"
echo ""
# Trigger redeployment
echo "🚀 Triggering redeployment..."
DEPLOY_RESPONSE=$(curl -s -X POST "${SAAC_API}/applications/${APP_UUID}/deploy" \
-H "X-API-Key: ${SAAC_API_KEY}")
# Check for errors
if echo "$DEPLOY_RESPONSE" | grep -q "error"; then
echo "❌ Failed to trigger deployment:"
echo "$DEPLOY_RESPONSE" | jq '.' 2>/dev/null || echo "$DEPLOY_RESPONSE"
exit 1
fi
echo "✅ Deployment triggered"
echo ""
# Update configuration with latest timestamp
DOMAIN="$FULL_DOMAIN"
save_config
echo ""
echo "========================================="
echo " Update Complete!"
echo "========================================="
echo ""
echo "⏳ Your changes will be live in 2-3 minutes at:"
echo " https://$FULL_DOMAIN"
echo ""
echo "🔍 Monitor deployment:"
echo " ./deploy-to-apps.sh --status"
echo ""
exit 0
fi
# ========================================
# MODE: SETUP
# ========================================
echo "========================================="
echo " AI Recruitment Site Deployment"
echo "========================================="
echo "📝 Mode: SETUP (new deployment)"
echo ""
echo "📦 Configuration:"
echo " Company: $COMPANY_NAME"
echo " Repository: $REPO_URL"
echo " Domain: https://$FULL_DOMAIN"
echo ""
# Warn if deployment already exists
if [ -n "$APP_UUID" ]; then
echo "⚠️ Warning: Existing deployment found in configuration"
echo " Current Application UUID: $APP_UUID"
echo " This will create a NEW deployment and overwrite the configuration."
echo ""
read -p "Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
exit 0
fi
echo ""
fi
# Create application via SAAC API
echo "📝 Creating application on StartAnAiCompany server..."
APP_RESPONSE=$(curl -s -X POST "${SAAC_API}/applications" \
-H "X-API-Key: ${SAAC_API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"${SUBDOMAIN}-recruit\",
\"subdomain\": \"${SUBDOMAIN}\",
\"domain_suffix\": \"recruit.startanaicompany.com\",
\"git_repository\": \"${REPO_URL}\",
\"git_branch\": \"master\",
\"gitea_api_token\": \"${GITEA_API_TOKEN}\",
\"template_type\": \"recruitment\",
\"environment_variables\": {
\"COMPANY_NAME\": \"${COMPANY_NAME}\",
\"COMPANY_TAGLINE\": \"${COMPANY_TAGLINE}\",
\"COMPANY_DESCRIPTION\": \"${COMPANY_DESCRIPTION}\",
\"PRIMARY_COLOR\": \"${PRIMARY_COLOR}\",
\"ACCENT_COLOR\": \"${ACCENT_COLOR}\",
\"DARK_COLOR\": \"${DARK_COLOR}\",
\"CONTACT_EMAIL\": \"${CONTACT_EMAIL}\",
\"CONTACT_PHONE\": \"${CONTACT_PHONE}\",
\"CONTACT_ADDRESS\": \"${CONTACT_ADDRESS}\"
}
}")
# Check for errors
if echo "$APP_RESPONSE" | grep -q "error"; then
echo "❌ Deployment failed:"
echo "$APP_RESPONSE" | jq '.' 2>/dev/null || echo "$APP_RESPONSE"
exit 1
fi
# Extract application details
APP_UUID=$(echo "$APP_RESPONSE" | jq -r '.application_uuid' 2>/dev/null)
DOMAIN=$(echo "$APP_RESPONSE" | jq -r '.domain' 2>/dev/null)
WEBHOOK_URL=$(echo "$APP_RESPONSE" | jq -r '.webhook_url' 2>/dev/null)
if [ "$APP_UUID" = "null" ] || [ -z "$APP_UUID" ]; then
echo "❌ Failed to create application"
echo "Response: $APP_RESPONSE"
exit 1
fi
echo "✅ Application created!"
echo " Application UUID: $APP_UUID"
echo " Domain: $DOMAIN"
echo ""
# Save configuration
APP_NAME="${SUBDOMAIN}-recruit"
DEPLOYED_AT=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
save_config
echo ""
# Configure webhook for automatic deployments
echo "🪝 Setting up deployment webhook..."
WEBHOOK_RESPONSE=$(curl -s -X POST "${GITEA_API}/repos/${GITEA_USERNAME}/${GITEA_REPO_NAME}/hooks" \
-H "Authorization: token ${GITEA_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"gitea\",
\"config\": {
\"url\": \"${WEBHOOK_URL}\",
\"content_type\": \"json\",
\"http_method\": \"GET\"
},
\"events\": [\"push\"],
\"authorization_header\": \"Bearer ${SAAC_API_KEY}\",
\"active\": true
}")
WEBHOOK_ID=$(echo "$WEBHOOK_RESPONSE" | jq -r '.id' 2>/dev/null)
if [ "$WEBHOOK_ID" = "null" ] || [ -z "$WEBHOOK_ID" ]; then
echo "⚠️ Warning: Failed to create Gitea webhook (may already exist)"
else
echo "✅ Webhook configured for automatic deployments"
fi
echo ""
echo "========================================="
echo " Deployment Complete!"
echo "========================================="
echo ""
echo "📋 Deployment Details:"
echo " Application UUID: $APP_UUID"
echo " Domain: $DOMAIN"
echo ""
echo "⏳ Your site will be available in 2-3 minutes at:"
echo " $DOMAIN"
echo ""
echo "📝 Next Steps:"
echo " 1. Configure DNS (if not already done):"
echo " - For Cloudflare: Add CNAME record"
echo " - Name: ${SUBDOMAIN}recruit"
echo " - Target: apps.startanaicompany.com"
echo " - Proxy: Enabled"
echo ""
echo " 2. Wait 2-3 minutes for deployment to complete"
echo ""
echo " 3. Visit your site:"
echo " $DOMAIN"
echo ""
echo "🔄 To update your deployment:"
echo " 1. Edit .env file with new values"
echo " 2. Run: ./deploy-to-apps.sh"
echo " (Auto-detects update mode since UUID file exists)"
echo ""
echo "🔍 Monitor deployment:"
echo " ./deploy-to-apps.sh --status"
echo ""
echo "💾 Configuration saved to:"
echo " - $SAAC_CONFIG_FILE (user credentials and deployment info)"
echo " ⚠️ Keep this file secure and do NOT commit it to git!"
echo ""