283 lines
7.1 KiB
Bash
Executable File
283 lines
7.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
MANUAL_ACTION="${MANUAL_ACTION:-noop}"
|
|
DOKPLOY_TARGET="${DOKPLOY_TARGET:-all}"
|
|
CLEANUP_CONFIRM="${CLEANUP_CONFIRM:-}"
|
|
SUMMARY_FILE="${GITHUB_STEP_SUMMARY:-/dev/stdout}"
|
|
|
|
require_dev_branch() {
|
|
if [ "${GITHUB_REF:-}" != "refs/heads/dev" ]; then
|
|
echo "Manual dev actions are allowed only from dev branch, got ${GITHUB_REF:-unknown}" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
write_usage() {
|
|
{
|
|
echo "No manual action selected."
|
|
echo "For dev DB cleanup run with:"
|
|
echo "- manual_action=cleanup_dev_database"
|
|
echo "- cleanup_confirm=CLEAN_DEV_DB"
|
|
echo "This drops and recreates the dev database, then triggers Dokploy web/worker/beat."
|
|
echo "For base image refresh run with manual_action=build_golden_images."
|
|
echo "For Dokploy image deploy run with manual_action=dokploy_start and dokploy_target=all|web|worker|beat."
|
|
} >>"${SUMMARY_FILE}"
|
|
}
|
|
|
|
registry_login() {
|
|
local registry_user registry_password
|
|
|
|
registry_user="${REGISTRY_USER:-${GITHUB_ACTOR}}"
|
|
registry_password="${REGISTRY_PASSWORD:-${GITEA_TOKEN:-}}"
|
|
|
|
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY all_proxy ALL_PROXY
|
|
export NO_PROXY="${NO_PROXY:-},${REGISTRY_HOST}"
|
|
export no_proxy="${no_proxy:-},${REGISTRY_HOST}"
|
|
|
|
if [ -z "${registry_password}" ]; then
|
|
echo "REGISTRY_TOKEN secret is not set and GITEA_TOKEN fallback is empty" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "${registry_password}" \
|
|
| docker login "${REGISTRY_HOST}" \
|
|
-u "${registry_user}" \
|
|
--password-stdin
|
|
}
|
|
|
|
ensure_buildx() {
|
|
if ! docker buildx inspect mostovik-builder >/dev/null 2>&1; then
|
|
docker buildx create --name mostovik-builder --use
|
|
else
|
|
docker buildx use mostovik-builder
|
|
fi
|
|
docker buildx inspect --bootstrap
|
|
}
|
|
|
|
build_golden_images() {
|
|
local registry_path ci_golden_ref web_golden_ref celery_golden_ref
|
|
|
|
registry_path="${REGISTRY_HOST}/${REGISTRY_NAMESPACE}"
|
|
ci_golden_ref="${registry_path}/${CI_GOLDEN_IMAGE}"
|
|
web_golden_ref="${registry_path}/${WEB_GOLDEN_IMAGE}"
|
|
celery_golden_ref="${registry_path}/${CELERY_GOLDEN_IMAGE}"
|
|
|
|
registry_login
|
|
ensure_buildx
|
|
|
|
docker buildx prune --all --force || true
|
|
docker builder prune --all --force || true
|
|
|
|
build_golden() {
|
|
local target="$1"
|
|
local ref="$2"
|
|
local build_args=()
|
|
|
|
if [ "${target}" = "celery-deps-base" ]; then
|
|
build_args+=(--build-arg "GOLDEN_WEB_IMAGE=${web_golden_ref}:${GOLDEN_TAG}")
|
|
fi
|
|
|
|
docker buildx build \
|
|
-f ./docker/Dockerfile \
|
|
--target "${target}" \
|
|
"${build_args[@]}" \
|
|
--push \
|
|
-t "${ref}:${GOLDEN_TAG}" \
|
|
-t "${ref}:latest" \
|
|
.
|
|
}
|
|
|
|
build_golden "ci-deps-base" "${ci_golden_ref}"
|
|
build_golden "web-deps-base" "${web_golden_ref}"
|
|
build_golden "celery-deps-base" "${celery_golden_ref}"
|
|
|
|
{
|
|
echo "Golden images pushed:"
|
|
echo "- ${ci_golden_ref}:${GOLDEN_TAG}"
|
|
echo "- ${web_golden_ref}:${GOLDEN_TAG}"
|
|
echo "- ${celery_golden_ref}:${GOLDEN_TAG}"
|
|
} >>"${SUMMARY_FILE}"
|
|
}
|
|
|
|
trigger_dokploy() {
|
|
local target="$1"
|
|
case "${target}" in
|
|
all | web | worker | celery | beat) ;;
|
|
*)
|
|
echo "dokploy_target must be one of: all, web, worker, beat" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
bash scripts/ci/dokploy_deploy_image.sh "${target}"
|
|
}
|
|
|
|
install_postgres_client() {
|
|
local apt_runner=()
|
|
|
|
if command -v psql >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
apt_runner=(sudo)
|
|
fi
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
"${apt_runner[@]}" apt-get update
|
|
"${apt_runner[@]}" apt-get install -y postgresql-client
|
|
}
|
|
|
|
drop_and_recreate_database() {
|
|
local db_exists db_encoding
|
|
|
|
export PGPASSWORD="${POSTGRES_PASSWORD}"
|
|
|
|
db_exists="$(psql \
|
|
--set ON_ERROR_STOP=1 \
|
|
--host="${POSTGRES_HOST}" \
|
|
--port="${POSTGRES_PORT}" \
|
|
--username="${POSTGRES_USER}" \
|
|
--dbname=postgres \
|
|
--tuples-only \
|
|
--no-align \
|
|
--set=dbname="${POSTGRES_DB}" \
|
|
<<'SQL'
|
|
SELECT 1 FROM pg_database WHERE datname = :'dbname';
|
|
SQL
|
|
)"
|
|
|
|
if [ "${db_exists:-}" = "1" ]; then
|
|
echo "Closing active connections to ${POSTGRES_DB}"
|
|
psql \
|
|
--set ON_ERROR_STOP=1 \
|
|
--host="${POSTGRES_HOST}" \
|
|
--port="${POSTGRES_PORT}" \
|
|
--username="${POSTGRES_USER}" \
|
|
--dbname=postgres \
|
|
--set=dbname="${POSTGRES_DB}" \
|
|
<<'SQL'
|
|
ALTER DATABASE :"dbname" WITH ALLOW_CONNECTIONS false;
|
|
SELECT pg_terminate_backend(pid)
|
|
FROM pg_stat_activity
|
|
WHERE datname = :'dbname'
|
|
AND pid <> pg_backend_pid();
|
|
SQL
|
|
fi
|
|
|
|
echo "Dropping and recreating ${POSTGRES_DB} with UTF8 encoding"
|
|
psql \
|
|
--set ON_ERROR_STOP=1 \
|
|
--host="${POSTGRES_HOST}" \
|
|
--port="${POSTGRES_PORT}" \
|
|
--username="${POSTGRES_USER}" \
|
|
--dbname=postgres \
|
|
--set=dbname="${POSTGRES_DB}" \
|
|
--set=dbuser="${POSTGRES_USER}" \
|
|
<<'SQL'
|
|
DROP DATABASE IF EXISTS :"dbname";
|
|
CREATE DATABASE :"dbname" WITH OWNER :"dbuser" TEMPLATE template0 ENCODING 'UTF8';
|
|
SQL
|
|
|
|
db_encoding="$(psql \
|
|
--set ON_ERROR_STOP=1 \
|
|
--host="${POSTGRES_HOST}" \
|
|
--port="${POSTGRES_PORT}" \
|
|
--username="${POSTGRES_USER}" \
|
|
--dbname=postgres \
|
|
--tuples-only \
|
|
--no-align \
|
|
--set=dbname="${POSTGRES_DB}" \
|
|
<<'SQL'
|
|
SELECT pg_encoding_to_char(encoding)
|
|
FROM pg_database
|
|
WHERE datname = :'dbname';
|
|
SQL
|
|
)"
|
|
|
|
printf '%s\n' "${db_encoding}" | tee /tmp/mostovik-db-encoding
|
|
|
|
if [ "${db_encoding}" != "UTF8" ]; then
|
|
echo "Database ${POSTGRES_DB} is not UTF8 after cleanup" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
wait_for_migrations() {
|
|
local schema_state
|
|
|
|
export PGPASSWORD="${POSTGRES_PASSWORD}"
|
|
|
|
for attempt in $(seq 1 60); do
|
|
schema_state="$(psql \
|
|
--set ON_ERROR_STOP=1 \
|
|
--host="${POSTGRES_HOST}" \
|
|
--port="${POSTGRES_PORT}" \
|
|
--username="${POSTGRES_USER}" \
|
|
--dbname="${POSTGRES_DB}" \
|
|
--tuples-only \
|
|
--no-align \
|
|
<<'SQL'
|
|
SELECT CASE
|
|
WHEN to_regclass('public.django_migrations') IS NOT NULL
|
|
AND to_regclass('public.core_backgroundjob') IS NOT NULL
|
|
THEN 'ready'
|
|
ELSE 'waiting'
|
|
END;
|
|
SQL
|
|
)"
|
|
if [ "${schema_state}" = "ready" ]; then
|
|
echo "Database schema is ready after web deploy"
|
|
return 0
|
|
fi
|
|
echo "Waiting for web migrations (${attempt}/60)"
|
|
sleep 5
|
|
done
|
|
|
|
echo "Database schema was not ready after web deploy" >&2
|
|
exit 1
|
|
}
|
|
|
|
cleanup_dev_database() {
|
|
if [ "${CLEANUP_CONFIRM}" != "CLEAN_DEV_DB" ]; then
|
|
echo "Manual confirmation must be exactly CLEAN_DEV_DB" >&2
|
|
exit 1
|
|
fi
|
|
|
|
install_postgres_client
|
|
drop_and_recreate_database
|
|
bash scripts/ci/dokploy_deploy_image.sh web
|
|
wait_for_migrations
|
|
bash scripts/ci/dokploy_deploy_image.sh worker
|
|
bash scripts/ci/dokploy_deploy_image.sh beat
|
|
|
|
{
|
|
echo "Dev database was dropped and recreated."
|
|
echo "Database: ${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
|
echo "Encoding: UTF8"
|
|
echo "Dokploy web/worker/beat image deploy was triggered."
|
|
} >>"${SUMMARY_FILE}"
|
|
}
|
|
|
|
require_dev_branch
|
|
|
|
case "${MANUAL_ACTION}" in
|
|
"" | noop)
|
|
write_usage
|
|
;;
|
|
build_golden_images)
|
|
build_golden_images
|
|
;;
|
|
dokploy_start)
|
|
trigger_dokploy "${DOKPLOY_TARGET}"
|
|
;;
|
|
cleanup_dev_database)
|
|
cleanup_dev_database
|
|
;;
|
|
*)
|
|
echo "Unknown manual_action: ${MANUAL_ACTION}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|