Files
KNEL-TSYSDevStack-SupportSt…/Package-Workspace/Collaboration/consuldemocracy/start.sh
T
mrcharles 7c65dc18d3 feat(consuldemocracy): add initial Cloudron package
Build from source on ruby:3.4.10-trixie (trimmed from the upstream dev
Dockerfile: no Chromium, no sudo, fixed non-root user). Uses the
postgresql and localstorage addons; start.sh regenerates
database.yml/secrets.yml under /app/data every boot, seeds the DB once
(marker file) with a local admin, runs memcached in-container, and
backgrounds a delayed_job worker gated on migrations. Platform OIDC is
wired into secrets.yml; local login stays for the seeded admin.

Also drop --rm from the app container in grind-stack.sh so a crashed
container keeps its logs for `logs`.
2026-09-06 18:03:58 -05:00

124 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# ConsulDemocracy runtime setup for Cloudron:
# 1. generate config/database.yml + config/secrets.yml under /app/data
# (config/* are symlinks; addon creds rotate on restore/migration, so
# both files are rewritten on EVERY start)
# 2. start memcached (production cache_store is mem_cache_store/dalli)
# 3. wait for the postgresql addon, then create/migrate/seed the DB once
# (marker file) or just migrate on later boots
# 4. background the delayed_job worker (gated on the web port, so the
# schema exists first), then exec the Rails server on :3000
#
# Authentication: OIDC via the Cloudron platform provider, mapped into
# secrets.yml (oidc_client_id/secret/issuer). The seeds create a local
# admin (admin@consul.dev / 12345678) - CHANGE ITS PASSWORD on first
# login (see README).
DATA_DIR="/app/data"
SECRET_FILE="${DATA_DIR}/.secret_key_base"
MARKER="${DATA_DIR}/.db_seeded"
cd /var/www/consul
# --- 1. runtime configs (regenerated every boot) -------------------------------
wait_tcp() {
local host="$1" port="$2" name="$3"
echo "Waiting for ${name} at ${host}:${port} ..."
until (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; do
echo "${name} is unavailable - sleeping"
sleep 2
done
echo "${name} is up"
}
yml_escape() { printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e "s/'/\\\\'/g"; }
if [[ ! -s "${SECRET_FILE}" ]]; then
( umask 077; openssl rand -hex 32 > "${SECRET_FILE}" )
echo "Generated new secret_key_base"
fi
SECRET_KEY_BASE="$(cat "${SECRET_FILE}")"
DB_HOST="${CLOUDRON_POSTGRESQL_HOST:-127.0.0.1}"
DB_PORT="${CLOUDRON_POSTGRESQL_PORT:-5432}"
DB_NAME="${CLOUDRON_POSTGRESQL_DATABASE:-consul}"
DB_USER="${CLOUDRON_POSTGRESQL_USERNAME:-consul}"
DB_PASS="${CLOUDRON_POSTGRESQL_PASSWORD:-}"
cat > "${DATA_DIR}/database.yml" <<EOF
default: &default
adapter: postgresql
encoding: unicode
host: ${DB_HOST}
port: ${DB_PORT}
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
schema_search_path: "public,shared_extensions"
username: ${DB_USER}
password: <%= ENV["PGPASSWORD"] %>
production:
<<: *default
database: ${DB_NAME}
EOF
export PGPASSWORD="${DB_PASS}"
OIDC_CLIENT_ID="$(yml_escape "${CLOUDRON_OIDC_CLIENT_ID:-}")"
OIDC_CLIENT_SECRET="$(yml_escape "${CLOUDRON_OIDC_CLIENT_SECRET:-}")"
OIDC_ISSUER="$(yml_escape "${CLOUDRON_OIDC_ISSUER:-}")"
SERVER_NAME="$(yml_escape "${CLOUDRON_APP_DOMAIN:-localhost}")"
cat > "${DATA_DIR}/secrets.yml" <<EOF
# Generated by start.sh on every boot - manual edits will be lost.
shared:
map_tiles_provider: "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
map_tiles_provider_attribution: "OpenStreetMap contributors"
production:
secret_key_base: ${SECRET_KEY_BASE}
server_name: ${SERVER_NAME}
force_ssl: false
delay_jobs: true
multitenancy: false
http_basic_username: ""
http_basic_password: ""
authentication_logs: false
devise_lockable: false
oidc_client_id: "${OIDC_CLIENT_ID}"
oidc_client_secret: "${OIDC_CLIENT_SECRET}"
oidc_issuer: "${OIDC_ISSUER}"
EOF
chmod 600 "${DATA_DIR}/secrets.yml"
# --- 2. memcached (localhost, as upstream's image intends) ----------------------
memcached -d -p 11211 -u consul -m 64
# --- 3. database lifecycle -------------------------------------------------------
wait_tcp "${DB_HOST}" "${DB_PORT}" "PostgreSQL"
if [[ ! -f "${MARKER}" ]]; then
echo "First boot: creating + migrating + seeding the database ..."
bundle exec rails db:create db:migrate db:seed
touch "${MARKER}"
echo "Database seeded (local admin: admin@consul.dev - change the password!)"
else
bundle exec rails db:migrate
fi
# assets:precompile needs the booted environment (DB-dependent
# initializers), so it runs here - after the PG wait, every boot
# (idempotent: fast no-op when the manifests are current)
bundle exec rails assets:precompile
# --- 4. delayed_job worker + web server ------------------------------------------
# gate on the web port (migrations done) so the worker never races the schema
(
until (exec 3<>/dev/tcp/127.0.0.1/3000) 2>/dev/null; do sleep 2; done
echo "web port is up - starting delayed_job worker"
exec bundle exec rake jobs:work
) &
echo "Starting ConsulDemocracy on :3000 ..."
exec bundle exec rails server -b 0.0.0.0 -p 3000