#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")/.." release_ts="${VR_RELEASE_TIMESTAMP:-$(date -u +%Y%m%dT%H%M%SZ)}" release_root="${VR_RELEASE_ROOT:-./release-artifacts/${release_ts}}" stack_base="vr-release-${release_ts}" image_tag="vacatureradar:release-${release_ts}" smoke_fixture="fixtures/pages/sample_jsonld_job.html" smoke_url="https://release-smoke.example.org/jobs/infrastructure-engineer" smoke_source="Release Smoke Source" admin_username="release-admin" admin_password="release-admin-${release_ts}" smoke_port="18080" restore_port="18081" smoke_stack="${stack_base}-smoke" restore_stack="${stack_base}-restore" smoke_root="${release_root}/smoke" restore_root="${release_root}/restore" primary_report="${release_root}/artifacts/smoke-primary.json" restore_report="${release_root}/artifacts/smoke-restored-validate.json" require_cmd() { if ! command -v "$1" >/dev/null 2>&1; then echo "release_verify: ontbrekende command '$1'" >&2 exit 1 fi } compose_exec() { local stack_name="$1" local stack_root="$2" local service="$3" shift 3 docker compose -f docker-compose.yml -f "${stack_root}/docker-compose.release.yml" -p "${stack_name}" exec -T "${service}" "$@" } compose_up() { local stack_name="$1" local stack_root="$2" docker compose -f docker-compose.yml -f "${stack_root}/docker-compose.release.yml" -p "${stack_name}" up -d } compose_down() { local stack_name="$1" local stack_root="$2" docker compose -f docker-compose.yml -f "${stack_root}/docker-compose.release.yml" -p "${stack_name}" down --remove-orphans } build_stack_override() { local stack_root="$1" local stack_port="$2" local env_file="${stack_root}/release.env" mkdir -p "${stack_root}"/{media,logs,local,postgres,redis,backups} cat > "${env_file}" < "${stack_root}/docker-compose.release.yml" </dev/null 2>&1; then git diff --unified=1 HEAD~1 -- docker-compose.yml docker-compose.unraid.yml .env.example docs/operations/UNRAID_DEPLOYMENT.md > "${release_root}/artifacts/config-diff.txt" else git diff --unified=1 -- docker-compose.yml docker-compose.unraid.yml .env.example docs/operations/UNRAID_DEPLOYMENT.md > "${release_root}/artifacts/config-diff.txt" fi echo "Bouw image ${image_tag} en verzamel metadata..." docker build --pull -t "${image_tag}" . docker image inspect "${image_tag}" > "${release_root}/artifacts/docker-image-inspect.json" python - < "${release_root}/artifacts/upgrade-rollback.md" <<'EOF' # Upgrade- en rollbackpad (VR-117) ## Upgrade 1. Maak eerst een PostgreSQL-backup en documenteer de restorebron. 2. Verifieer image-id met `docker image inspect`. 3. Schakel alleen de oude versie uit en houd precies één vorige image-tag of digest klaar. 4. Start de nieuwe versie met exact `docker-compose.yml` en matching `DATABASE_URL`. 5. Wacht op `/health/live/` en `/health/ready/` vóór het herstarten van worker en scheduler. 6. Laat één herstelbaar model-smokepad lopen (fixture-import, score, feedback, application, digest). 7. Zet de vorige image gereed als rollbackoptie totdat één volledige cycle stabiel draait. ## Rollback 1. Stop worker en scheduler, daarna web. 2. Start met vorige image-tag of -digest. 3. Start web, daarna worker en scheduler. 4. Controleer liveness/readiness, fixture-smoke en modelcounts. 5. Restore uit de vooraf gemaakte back-up wanneer datamodelmigraties incompatibel zijn. EOF cat > "${release_root}/artifacts/release-notes.txt" < "${release_root}/artifacts/checksums.txt" echo "Start smoke-stack..." run_smoke "${smoke_stack}" "${smoke_root}" "smoke-primary" "${smoke_port}" "${primary_report}" compose_exec "${smoke_stack}" "${smoke_root}" web ./scripts/backup.sh backup_file="$(ls -1t "${smoke_root}/backups"/vacatureradar-*.sql.gz "${smoke_root}/backups"/vacatureradar-*.sqlite3 2>/dev/null | head -n 1)" if [[ -z "${backup_file}" ]]; then echo "release_verify: backupbestand niet aangetroffen" >&2 exit 1 fi echo "Start restore-stack..." mkdir -p "${restore_root}/backups" cp "${backup_file}" "${restore_root}/backups/$(basename "${backup_file}")" build_stack_override "${restore_root}" "${restore_port}" compose_up "${restore_stack}" "${restore_root}" compose_exec "${restore_stack}" "${restore_root}" web python scripts/healthcheck.py http://127.0.0.1:8080/health/ready/ compose_exec "${restore_stack}" "${restore_root}" web python scripts/healthcheck.py http://127.0.0.1:8080/health/live/ compose_exec "${restore_stack}" "${restore_root}" web ./scripts/restore.sh "/app/backups/$(basename "${backup_file}")" run_smoke "${restore_stack}" "${restore_root}" "smoke-restored-validate" "${restore_port}" "${restore_report}" assert_model_counts "${primary_report}" "${restore_report}" alias_count="$(json_value "${primary_report}" "alias_count")" restore_alias_count="$(json_value "${restore_report}" "alias_count")" if [[ "${alias_count}" != "${restore_alias_count}" ]]; then echo "release_verify: alias_count mismatch (${alias_count} vs ${restore_alias_count})" >&2 exit 1 fi echo "VR-117 release verification voltooid; artifacts in: ${release_root}" echo "- package : ${package_file}" echo "- upgrade/rollback: ${release_root}/artifacts/upgrade-rollback.md" echo "- checksums: ${release_root}/artifacts/checksums.txt" echo "- configdiff: ${release_root}/artifacts/config-diff.txt" exit 0