Mirrors the nightly.yml smoke step against archiv.raddatz.cloud. Catches the same three failure modes (Caddy not reloaded, DNS missing, HSTS dropped, /actuator block bypassed) on the prod path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
92 lines
3.0 KiB
YAML
92 lines
3.0 KiB
YAML
name: release
|
|
|
|
# Builds and deploys the production environment on `v*` tag push.
|
|
# Runs on the self-hosted runner via Docker-out-of-Docker; images are
|
|
# tagged with the actual git tag (e.g. v1.0.0) so rollback is
|
|
# `TAG=<previous> docker compose -f docker-compose.prod.yml -p archiv-production up -d --wait`
|
|
#
|
|
# Production environment:
|
|
# - project name: archiv-production
|
|
# - host ports: backend 8080, frontend 3000
|
|
# - profile: (none) — mailpit is excluded; real SMTP relay is used
|
|
#
|
|
# Required Gitea secrets:
|
|
# PROD_POSTGRES_PASSWORD
|
|
# PROD_MINIO_PASSWORD
|
|
# PROD_MINIO_APP_PASSWORD
|
|
# PROD_OCR_TRAINING_TOKEN
|
|
# PROD_APP_ADMIN_USERNAME (CRITICAL: see docs/DEPLOYMENT.md)
|
|
# PROD_APP_ADMIN_PASSWORD (CRITICAL: locked in on first deploy)
|
|
# MAIL_HOST
|
|
# MAIL_PORT
|
|
# MAIL_USERNAME
|
|
# MAIL_PASSWORD
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
|
|
env:
|
|
DOCKER_BUILDKIT: "1"
|
|
|
|
jobs:
|
|
deploy-production:
|
|
runs-on: self-hosted
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Write production env file
|
|
run: |
|
|
cat > .env.production <<EOF
|
|
TAG=${{ gitea.ref_name }}
|
|
PORT_BACKEND=8080
|
|
PORT_FRONTEND=3000
|
|
APP_DOMAIN=archiv.raddatz.cloud
|
|
POSTGRES_PASSWORD=${{ secrets.PROD_POSTGRES_PASSWORD }}
|
|
MINIO_PASSWORD=${{ secrets.PROD_MINIO_PASSWORD }}
|
|
MINIO_APP_PASSWORD=${{ secrets.PROD_MINIO_APP_PASSWORD }}
|
|
OCR_TRAINING_TOKEN=${{ secrets.PROD_OCR_TRAINING_TOKEN }}
|
|
APP_ADMIN_USERNAME=${{ secrets.PROD_APP_ADMIN_USERNAME }}
|
|
APP_ADMIN_PASSWORD=${{ secrets.PROD_APP_ADMIN_PASSWORD }}
|
|
MAIL_HOST=${{ secrets.MAIL_HOST }}
|
|
MAIL_PORT=${{ secrets.MAIL_PORT }}
|
|
MAIL_USERNAME=${{ secrets.MAIL_USERNAME }}
|
|
MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }}
|
|
MAIL_SMTP_AUTH=true
|
|
MAIL_STARTTLS_ENABLE=true
|
|
APP_MAIL_FROM=noreply@raddatz.cloud
|
|
EOF
|
|
|
|
- name: Build images
|
|
run: |
|
|
docker compose \
|
|
-f docker-compose.prod.yml \
|
|
-p archiv-production \
|
|
--env-file .env.production \
|
|
build
|
|
|
|
- name: Deploy production
|
|
run: |
|
|
docker compose \
|
|
-f docker-compose.prod.yml \
|
|
-p archiv-production \
|
|
--env-file .env.production \
|
|
up -d --wait --remove-orphans
|
|
|
|
- name: Smoke test deployed environment
|
|
# See nightly.yml — same three checks, against the prod vhost.
|
|
run: |
|
|
set -e
|
|
URL="https://archiv.raddatz.cloud"
|
|
echo "Smoke test: $URL"
|
|
curl -fsS --max-time 10 "$URL/login" -o /dev/null
|
|
curl -fsS --max-time 10 -I "$URL/" | grep -qi 'strict-transport-security'
|
|
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$URL/actuator/health")
|
|
[ "$status" = "404" ] || { echo "expected 404 from /actuator/health, got $status"; exit 1; }
|
|
echo "All smoke checks passed"
|
|
|
|
- name: Cleanup env file
|
|
if: always()
|
|
run: rm -f .env.production
|