PSA/setup/entrypoint.sh
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

293 lines
11 KiB
Bash
Executable File

#!/bin/bash
# Function to log with timestamp
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# Function to handle errors
handle_error() {
local exit_code=$?
local last_command=${BASH_COMMAND}
log "ERROR: Command '$last_command' failed with exit code $exit_code."
log "Exiting with error code: $exit_code"
# Exit with the captured exit code
exit $exit_code
}
# Set up error handling to call handle_error on ERR
# and also exit immediately if a command exits with a non-zero status.
set -e
trap 'handle_error' ERR
is_enabled() {
case "${1:-true}" in
[Ff][Aa][Ll][Ss][Ee]|0|[Nn][Oo]|[Oo][Ff][Ff])
return 1
;;
*)
return 0
;;
esac
}
get_admin_password() {
local password=""
if [ -f /run/secrets/postgres_password ]; then
password=$(cat /run/secrets/postgres_password 2>/dev/null)
fi
if [ -z "$password" ] && [ -n "${DB_PASSWORD_ADMIN:-}" ]; then
password="${DB_PASSWORD_ADMIN}"
fi
if [ -z "$password" ] && [ -n "${DB_PASSWORD_SUPERUSER:-}" ]; then
password="${DB_PASSWORD_SUPERUSER}"
fi
echo "$password" | tr -d '[:space:]'
}
# Function to check if postgres is ready
wait_for_postgres() {
log "Waiting for PostgreSQL to be ready..."
# Use DB_HOST_ADMIN for direct postgres connections (admin operations)
# Fall back to DB_HOST if not set, then to 'postgres'
local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
# If host points at PgBouncer, prefer talking to Postgres directly for setup
if [ "$PG_ADMIN_HOST" = "pgbouncer" ]; then
log "Detected DB_HOST pointing to PgBouncer; using direct Postgres for admin ops"
PG_ADMIN_HOST=postgres
PG_ADMIN_PORT=5432
export DB_HOST_ADMIN=$PG_ADMIN_HOST
export DB_PORT_ADMIN=$PG_ADMIN_PORT
fi
local PG_PASSWORD
PG_PASSWORD=$(get_admin_password)
if [ -z "$PG_PASSWORD" ]; then
log "ERROR: No admin database password available for wait_for_postgres"
exit 1
fi
set +e # Temporarily disable exit on error for the until loop
until PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -c '\q' 2>/dev/null; do
# Try direct Postgres as a smart fallback if PgBouncer is the target and not responding
if [ "$PG_ADMIN_HOST" != "postgres" ] && PGPASSWORD="${PG_PASSWORD}" pg_isready -h postgres -p 5432 -U postgres >/dev/null 2>&1; then
log "PgBouncer not ready but Postgres is reachable; switching admin host to Postgres"
PG_ADMIN_HOST=postgres
PG_ADMIN_PORT=5432
export DB_HOST_ADMIN=$PG_ADMIN_HOST
export DB_PORT_ADMIN=$PG_ADMIN_PORT
continue
fi
log "PostgreSQL is unavailable - sleeping"
sleep 1
done
set -e # Re-enable exit on error
log "PostgreSQL is up and running!"
}
# Function to check if seeds have been run
check_seeds_status() {
local has_seeds
# Use DB_HOST_ADMIN for direct postgres connections (admin operations)
local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
local PG_PASSWORD
PG_PASSWORD=$(get_admin_password)
has_seeds=$(PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -tAc "SELECT EXISTS (SELECT 1 FROM users LIMIT 1);" 2>/dev/null)
if [ "$has_seeds" = "t" ]; then
return 0 # Seeds have been run
else
return 1 # Seeds haven't been run
fi
}
# Main setup process
main() {
wait_for_postgres
log "Creating database..."
log "Running create_database.js with timeout protection..."
timeout 120 node /app/server/setup/create_database.js || {
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "ERROR: Database creation timed out after 120 seconds"
else
log "ERROR: Database creation failed with exit code $exit_code"
fi
exit 1
}
log "Database creation completed!"
# Use DB_HOST_ADMIN for direct postgres connections (admin operations)
# pgbouncer doesn't support certain admin commands
local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
# Read and trim the postgres password (be extra careful with whitespace)
local PG_PASSWORD
PG_PASSWORD=$(get_admin_password)
log "DEBUG: Connecting to ${PG_ADMIN_HOST}:${PG_ADMIN_PORT} with user postgres"
log "DEBUG: Password available: $([ -n "$PG_PASSWORD" ] && echo 'yes' || echo 'no')"
log "DEBUG: Password length: ${#PG_PASSWORD}"
if is_enabled "${SETUP_RUN_MIGRATIONS:-true}"; then
log "Creating pgboss schema..."
# Add retry logic for pgboss schema creation in case of temporary connection issues
local RETRY_COUNT=0
local MAX_RETRIES=3
local RETRY_DELAY=2
until PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'CREATE SCHEMA IF NOT EXISTS pgboss;' 2>&1; do
RETRY_COUNT=$((RETRY_COUNT + 1))
if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
log "ERROR: Failed to create pgboss schema after $MAX_RETRIES attempts"
log "Testing connection with psql..."
PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'SELECT version();' 2>&1 || log "Connection test failed"
log "Checking if Postgres is still accessible..."
pg_isready -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres || log "Postgres not ready"
exit 1
fi
log "Retry $RETRY_COUNT/$MAX_RETRIES: pgboss schema creation failed, retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
done
log "pgboss schema created successfully"
log "Granting necessary permissions..."
PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'GRANT ALL ON SCHEMA public TO postgres;'
log "Running migrations..."
# For Enterprise Edition, we need to run migrations from a combined directory
if [ "${EDITION}" = "enterprise" ] || [ "${EDITION}" = "ee" ]; then
log "Setting up EE migrations..."
# Create a combined migrations directory within /app/server
mkdir -p /app/server/combined-migrations
# Copy base migrations first (these should run first)
log "Copying base migrations..."
cp /app/server/migrations/*.cjs /app/server/combined-migrations/ 2>/dev/null || true
# Copy EE migrations (these run after base migrations)
log "Copying EE migrations..."
cp /app/ee/server/migrations/*.cjs /app/server/combined-migrations/ 2>/dev/null || true
# Create a temporary knexfile in /app/server where node_modules exist
log "Creating temporary knexfile for EE..."
cat > /app/server/knexfile-ee.cjs << 'EOF'
const fs = require('fs');
const path = require('path');
const DOCKER_SECRETS_PATH = '/run/secrets';
const SECRETS_PATH = DOCKER_SECRETS_PATH;
function getSecret(secretName, envVar, defaultValue = '') {
const secretPath = path.join(SECRETS_PATH, secretName);
try {
return fs.readFileSync(secretPath, 'utf8').trim();
} catch (error) {
if (process.env[envVar]) {
return process.env[envVar] || defaultValue;
}
return defaultValue;
}
}
module.exports = {
migration: {
client: 'pg',
connection: {
host: process.env.DB_HOST || 'postgres',
port: process.env.DB_PORT || '5432',
user: process.env.DB_USER_ADMIN || 'postgres',
password: getSecret('postgres_password', 'DB_PASSWORD_ADMIN'),
database: process.env.DB_NAME_SERVER || 'server',
},
pool: {
min: 2,
max: 20,
},
migrations: {
directory: './combined-migrations'
}
}
};
EOF
log "Running combined migrations for EE..."
log "Current directory: $(pwd)"
log "Migration directory contents:"
ls -la /app/server/combined-migrations/ || log "Could not list migration directory"
cd /app/server && NODE_ENV=migration timeout 300 npx knex migrate:latest --knexfile knexfile-ee.cjs --verbose || {
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "ERROR: EE migrations timed out after 300 seconds"
else
log "ERROR: EE migrations failed with exit code $exit_code"
fi
exit 1
}
log "EE migrations completed!"
# Clean up
rm -rf /app/server/combined-migrations
rm -f /app/server/knexfile-ee.cjs
else
# For CE, just run the base migrations
log "Running base migrations for CE..."
log "Current directory: $(pwd)"
log "Migration directory: /app/server/migrations"
ls -la /app/server/migrations/ || log "Could not list migration directory"
NODE_ENV=migration timeout 300 npx knex migrate:latest --knexfile /app/server/knexfile.cjs --verbose || {
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "ERROR: Migrations timed out after 300 seconds"
else
log "ERROR: Migrations failed with exit code $exit_code"
fi
exit 1
}
log "Migrations completed!"
fi
else
log "SETUP_RUN_MIGRATIONS is disabled; skipping pgboss schema creation and migrations."
fi
# Check if seeds need to be run
if is_enabled "${SETUP_RUN_SEEDS:-true}"; then
if ! check_seeds_status; then
log "Running seeds..."
log "Seed directory: /app/server/seeds"
ls -la /app/server/seeds/ || log "Could not list seed directory"
NODE_ENV=migration timeout 300 npx knex seed:run --knexfile /app/server/knexfile.cjs --verbose || {
local exit_code=$?
if [ $exit_code -eq 124 ]; then
log "ERROR: Seeds timed out after 300 seconds"
else
log "ERROR: Seeds failed with exit code $exit_code"
fi
exit $exit_code
}
log "Seeds completed!"
else
log "Seeds have already been run, skipping..."
fi
else
log "SETUP_RUN_SEEDS is disabled; skipping seeds."
fi
log "Setup completed!"
log "Exiting with success code: 0"
exit 0
}
# Execute main function
main