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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
293 lines
11 KiB
Bash
Executable File
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
|