Files
computer-playbook/scripts/tests/deploy.sh
T
kevinveenbirkenbach 6bc097f96f refactor(email,domain): unify primary domain and TLS handling
- replace localhost special-casing with valid FQDN (infinito.localhost)
- derive system email DOMAIN and HOST directly from DOMAIN_PRIMARY
- align Mailu TLS_FLAVOR with SYSTEM_EMAIL.TLS (mail vs notls)
- tie OIDC_VERIFY_SSL to TLS_ENABLED for consistent test/CI behavior
- update CI deploy script to use new primary domain

https://chatgpt.com/share/695311c0-fbe8-800f-8058-9d40a06b189a
2025-12-30 00:41:46 +01:00

335 lines
8.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Compose-based deploy test runner:
# - uses docker-compose.yml (profile "ci") instead of docker run
# - starts coredns + infinito service
# - computes excludes by executing inside the running infinito container
# - runs cli.create.inventory + cli.deploy.dedicated inside the container
# - shuts down compose stack at the end
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
TYPE=""
DISTRO=""
NO_CACHE=0
MISSING_ONLY=0
# -------------------------------------------------------------------
# Config (kept from the previous version)
# -------------------------------------------------------------------
AUTHORIZED_KEYS_DEFAULT="ssh-ed25519 AAAA_TEST_DUMMY_KEY github-ci-dummy@infinito"
AUTHORIZED_KEYS="${AUTHORIZED_KEYS:-$AUTHORIZED_KEYS_DEFAULT}"
# Optional: allow adding additional excludes globally (comma-separated or newline-separated)
ALWAYS_EXCLUDE="${ALWAYS_EXCLUDE:-}"
# Central excludes (same everywhere) - can be comma-separated or newline-separated.
BASE_EXCLUDE="${BASE_EXCLUDE:-$(
cat <<'EOF'
drv-lid-switch
svc-db-memcached
svc-db-redis
svc-net-wireguard-core
svc-net-wireguard-firewalled
svc-net-wireguard-plain
svc-bkp-loc-2-usb
svc-bkp-rmt-2-loc
svc-opt-keyboard-color
svc-opt-ssd-hdd
web-app-bridgy-fed
web-app-oauth2-proxy
web-app-postmarks
web-app-elk
web-app-syncope
web-app-socialhome
web-svc-xmpp
EOF
)}"
usage() {
cat <<'EOF'
Usage: scripts/tests/deploy.sh --type <server|workstation> --distro <arch|debian|ubuntu|fedora|centos> [options]
Options:
--no-cache Rebuild compose image with --no-cache
--missing Build only if missing (skip build if image exists)
-h, --help Show this help
What it runs (compose-based):
1) INFINITO_DISTRO=<distro> docker compose --profile ci build (optional)
2) docker compose --profile ci up -d coredns infinito
3) Compute excluded roles (invokable-based) + BASE_EXCLUDE + ALWAYS_EXCLUDE
4) docker compose exec infinito python -m cli.create.inventory ...
5) docker compose exec infinito python -m cli.deploy.dedicated ...
6) docker compose --profile ci down
EOF
}
die() {
echo "[ERROR] $*" >&2
exit 1
}
trim_lines() { sed -e 's/[[:space:]]\+$//' -e '/^$/d'; }
join_by_comma() {
local IFS=","
echo "$*"
}
# -------------------------------------------------------------------
# Compose helpers
# -------------------------------------------------------------------
compose() {
# Ensure we always operate in repo root where docker-compose.yml exists
(
cd "${REPO_ROOT}"
# profile ci is important for infinito + coredns
INFINITO_DISTRO="${DISTRO}" docker compose --profile ci "$@"
)
}
infinito_exec() {
docker exec "${INFINITO_CONTAINER}" "$@"
}
ensure_compose_image() {
# Compose image name in docker-compose.yml:
# image: "infinito-${INFINITO_DISTRO:-arch}"
local image="infinito-${DISTRO}"
if [[ "${MISSING_ONLY}" == "1" ]]; then
if docker image inspect "${image}" >/dev/null 2>&1; then
echo ">>> Image already exists: ${image} (skipping build due to --missing)"
return
fi
fi
if [[ "${NO_CACHE}" == "1" ]]; then
echo ">>> docker compose build --no-cache infinito (INFINITO_DISTRO=${DISTRO})"
compose build --no-cache infinito
else
echo ">>> docker compose build infinito (INFINITO_DISTRO=${DISTRO})"
compose build infinito
fi
}
start_stack() {
echo ">>> Starting compose stack (profile=ci): coredns only"
compose up -d coredns --force-recreate
# derive compose network from coredns container
local coredns_id net
coredns_id="$(compose ps -q coredns)"
[[ -n "$coredns_id" ]] || die "Could not get coredns container id"
net="$(docker inspect -f '{{ range $k,$v := .NetworkSettings.Networks }}{{$k}}{{end}}' "$coredns_id")"
[[ -n "$net" ]] || die "Could not determine compose network"
echo ">>> Starting infinito via docker run (network=${net})"
docker rm -f "${INFINITO_CONTAINER}" >/dev/null 2>&1 || true
docker run -d --name "${INFINITO_CONTAINER}" \
--privileged \
--cgroupns=host \
--tmpfs /run \
--tmpfs /run/lock \
-v /sys/fs/cgroup:/sys/fs/cgroup:rw \
-v /tmp/gh-action:/tmp/gh-action \
-v "${REPO_ROOT}:/opt/src/infinito" \
--network "${net}" \
-e INSTALL_LOCAL_BUILD=1 \
"infinito-${DISTRO}" \
/sbin/init
}
stop_stack() {
echo ">>> Stopping infinito container + compose stack"
docker rm -f "${INFINITO_CONTAINER}" >/dev/null 2>&1 || true
compose down --remove-orphans
}
# -------------------------------------------------------------------
# Inventory/exclude logic (now executed via compose exec)
# -------------------------------------------------------------------
get_invokable() {
infinito_exec sh -lc 'python3 -m cli.meta.applications.invokable' | trim_lines
}
filter_allowed() {
local type="$1"
case "$type" in
workstation)
grep -E '^(desk-|util-desk-)' || true
;;
server)
grep -E '^web-|svc-db-' || true
;;
*)
die "Unknown deploy type: $type (expected: workstation|server)"
;;
esac
}
compute_exclude_csv() {
local type="$1"
local all allowed computed combined final
local drv_exclude
local -a arr=()
all="$(get_invokable)" || die "get_invokable failed (cli.meta.applications.invokable not available?)"
allowed="$(printf "%s\n" "$all" | filter_allowed "$type")"
computed="$(
comm -23 \
<(printf "%s\n" "$all" | LC_ALL=C sort) \
<(printf "%s\n" "$allowed" | LC_ALL=C sort) |
trim_lines
)"
drv_exclude="$(printf "%s\n" "$all" | grep -E '^drv-' || true)"
combined="$(
printf "%s\n%s\n%s\n%s\n" \
"$computed" \
"$drv_exclude" \
"$(printf "%s\n" "$BASE_EXCLUDE" | tr ',' '\n')" \
"$(printf "%s\n" "$ALWAYS_EXCLUDE" | tr ',' '\n')" |
trim_lines | LC_ALL=C sort -u
)"
final="$(
comm -12 \
<(printf "%s\n" "$combined" | LC_ALL=C sort) \
<(printf "%s\n" "$all" | LC_ALL=C sort) |
trim_lines
)"
if [[ -n "${final}" ]]; then
mapfile -t arr <<<"${final}"
fi
if [[ ${#arr[@]} -eq 0 ]]; then
echo ""
else
join_by_comma "${arr[@]}"
fi
}
# -------------------------------------------------------------------
# Args
# -------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--type)
TYPE="${2:-}"
shift 2
;;
--distro)
DISTRO="${2:-}"
shift 2
;;
--no-cache)
NO_CACHE=1
shift
;;
--missing)
MISSING_ONLY=1
shift
;;
-h | --help)
usage
exit 0
;;
*) die "Unknown argument: $1" ;;
esac
done
[[ -n "${TYPE}" ]] || die "--type is required"
[[ -n "${DISTRO}" ]] || die "--distro is required"
INFINITO_CONTAINER="infinito_nexus_${DISTRO}"
case "${TYPE}" in
server | workstation ) ;;
*) die "Invalid --type '${TYPE}' (expected: server|workstation)" ;;
esac
case "${DISTRO}" in
arch | debian | ubuntu | fedora | centos) ;;
*) die "Invalid --distro '${DISTRO}' (expected: arch|debian|ubuntu|fedora|centos)" ;;
esac
echo ">>> Deploy type: ${TYPE}"
echo ">>> Distro: ${DISTRO}"
echo ">>> Repo root: ${REPO_ROOT}"
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
pushd "${REPO_ROOT}" >/dev/null
on_exit() {
rc=$?
if [[ $rc -eq 0 ]]; then
stop_stack
else
echo ">>> ERROR (rc=$rc) - keeping compose stack for debugging."
echo ">>> Hint:"
echo " docker ps"
echo " INFINITO_DISTRO=${DISTRO} docker compose --profile ci ps"
echo " INFINITO_DISTRO=${DISTRO} docker compose --profile ci logs --tail=200"
fi
exit $rc
}
trap on_exit EXIT
ensure_compose_image
start_stack
# ---------------------------------------------------------------------------
# Compute excludes
# ---------------------------------------------------------------------------
EXCLUDE_CSV="$(compute_exclude_csv "${TYPE}")"
echo ">>> Excluded roles: ${EXCLUDE_CSV:-<none>}"
# ---------------------------------------------------------------------------
# Run inventory generation + deploy INSIDE the compose container
# ---------------------------------------------------------------------------
echo ">>> Creating CI inventory inside compose container..."
infinito_exec sh -lc '
cd /opt/src/infinito &&
python3 -m cli.create.inventory \
/etc/inventories/github-ci \
--host localhost \
--ssl-disabled \
--primary-domain infinito.localhost \
--exclude "'"${EXCLUDE_CSV}"'" \
--vars-file inventory.sample.yml \
--authorized-keys "'"${AUTHORIZED_KEYS}"'"
'
echo ">>> Ensuring vault password file exists..."
infinito_exec sh -lc \
"mkdir -p /etc/inventories/github-ci && \
[ -f /etc/inventories/github-ci/.password ] || \
printf '%s\n' 'ci-vault-password' > /etc/inventories/github-ci/.password"
echo ">>> Running deploy via cli.deploy.dedicated inside compose container..."
infinito_exec python3 -m cli.deploy.dedicated \
/etc/inventories/github-ci/servers.yml \
-p /etc/inventories/github-ci/.password \
-vv \
--assert true \
--debug \
--diff \
-T "${TYPE}"
echo ">>> Deploy test suite finished successfully."
popd >/dev/null