mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Typically, when attempting to stop a container via `docker stop` or
`podman stop`, the container engine will send a stop signal (SIGTERM by
default) to the container's main process. There are two common ways this
can go wrong:
1. The main process is run as PID 1 and doesn't register a signal
handler for the stop signal and is consequently ignored
2. The main process is a shell script running a foreground process with
no `trap`s and is consequently ignored by the *shell*
In either case, because the graceful stop signal is ignored, the
container engine will then send a `SIGKILL` to the container process
after a default timeout of 10 seconds. This is why some containers can
be observed to "hang" when being stopped when they have no other reason
to do so. Unlike `SIGTERM` or `SIGINT`, `SIGKILL` is an immediate,
ungraceful stop that doesn't give the process time to clean up.
There is a fair bit of nuance in how `sh` and `bash` handle signals in
different circumstances. The behavior relevant to the second case above
and Dawarich's entrypoints in particular is that the shell ignores
signals like `SIGTERM` and `SIGINT` when waiting on a foreground job; in
this case, that would be: `bundle exec ${@}`. The reason that `SIGINT`
is not ignored after pressing `Ctrl+C` while running the docker compose
stack is because in that case the shell is **interactive** and the shell
*does* respond to `SIGINT` then (c.f. the aforementioned nuance).
Thankfully, the fix is simple: `exec` the main process, which causes the
server process to *replace* the shell process and directly receive any
signals sent. Additionally, the stop signal for the app process should
be set to `SIGINT`, as that is the expected signal for graceful
shutdown. Sidekiq is fine with either `SIGTERM` or `SIGINT`, which is
convenient.
86 lines
2.8 KiB
Bash
86 lines
2.8 KiB
Bash
#!/bin/sh
|
|
|
|
unset BUNDLE_PATH
|
|
unset BUNDLE_BIN
|
|
|
|
set -e
|
|
|
|
echo "⚠️ Starting Rails environment: $RAILS_ENV ⚠️"
|
|
|
|
# Parse DATABASE_URL if present, otherwise use individual variables
|
|
if [ -n "$DATABASE_URL" ]; then
|
|
# Extract components from DATABASE_URL
|
|
DATABASE_HOST=$(echo $DATABASE_URL | awk -F[@/] '{print $4}')
|
|
DATABASE_PORT=$(echo $DATABASE_URL | awk -F[@/:] '{print $5}')
|
|
DATABASE_USERNAME=$(echo $DATABASE_URL | awk -F[:/@] '{print $4}')
|
|
DATABASE_PASSWORD=$(echo $DATABASE_URL | awk -F[:/@] '{print $5}')
|
|
DATABASE_NAME=$(echo $DATABASE_URL | awk -F[@/] '{print $5}')
|
|
else
|
|
# Use existing environment variables
|
|
DATABASE_HOST=${DATABASE_HOST}
|
|
DATABASE_PORT=${DATABASE_PORT}
|
|
DATABASE_USERNAME=${DATABASE_USERNAME}
|
|
DATABASE_PASSWORD=${DATABASE_PASSWORD}
|
|
DATABASE_NAME=${DATABASE_NAME}
|
|
fi
|
|
|
|
# Export main database variables to ensure they're available
|
|
export DATABASE_HOST
|
|
export DATABASE_PORT
|
|
export DATABASE_USERNAME
|
|
export DATABASE_PASSWORD
|
|
export DATABASE_NAME
|
|
|
|
# Remove pre-existing puma/passenger server.pid
|
|
rm -f $APP_PATH/tmp/pids/server.pid
|
|
|
|
# Sync static assets from image to volume
|
|
# This ensures new files (like maps_maplibre styles) are copied to the persistent volume
|
|
if [ -d "/tmp/public_assets" ]; then
|
|
echo "📦 Syncing new static assets to public volume..."
|
|
cp -rn /tmp/public_assets/* $APP_PATH/public/ 2>/dev/null || true
|
|
echo "✅ Static assets synced!"
|
|
fi
|
|
|
|
# Function to check and create a PostgreSQL database
|
|
create_database() {
|
|
local db_name=$1
|
|
local db_password=$2
|
|
local db_host=$3
|
|
local db_port=$4
|
|
local db_username=$5
|
|
|
|
echo "Attempting to create database $db_name if it doesn't exist..."
|
|
PGPASSWORD=$db_password createdb -h "$db_host" -p "$db_port" -U "$db_username" "$db_name" 2>/dev/null || echo "Note: Database $db_name may already exist or couldn't be created now"
|
|
|
|
# Wait for the database to become available
|
|
echo "⏳ Waiting for database $db_name to be ready..."
|
|
until PGPASSWORD=$db_password psql -h "$db_host" -p "$db_port" -U "$db_username" -d "$db_name" -c '\q' 2>/dev/null; do
|
|
>&2 echo "Postgres database $db_name is unavailable - retrying..."
|
|
sleep 2
|
|
done
|
|
echo "✅ PostgreSQL database $db_name is ready!"
|
|
}
|
|
|
|
# Step 1: Database Setup
|
|
echo "Setting up all required databases..."
|
|
|
|
# Create primary PostgreSQL database
|
|
create_database "$DATABASE_NAME" "$DATABASE_PASSWORD" "$DATABASE_HOST" "$DATABASE_PORT" "$DATABASE_USERNAME"
|
|
|
|
# Step 2: Run migrations for all databases
|
|
echo "Running migrations for all databases..."
|
|
|
|
# Run primary database migrations first (needed before other migrations)
|
|
echo "Running primary database migrations..."
|
|
bundle exec rails db:migrate
|
|
|
|
# Run data migrations
|
|
echo "Running DATA migrations..."
|
|
bundle exec rake data:migrate
|
|
|
|
echo "Running seeds..."
|
|
bundle exec rails db:seed
|
|
|
|
# run passed commands
|
|
exec bundle exec ${@}
|