diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..dab90ad --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,104 @@ +name: Deploy instance + +# This content repo owns its portal instance: which portal version runs, +# the service env, the systemd unit, and the Caddy route. The portal repo +# only publishes versioned release artifacts (uhhm/portal's Publish +# workflow); PORTAL_RELEASE below pins the one this site runs. +# +# Rolling out a new portal version = bumping PORTAL_RELEASE (a commit, +# so every rollout is auditable and revertable). Content-only changes +# never come through here - lint-and-reload hot-swaps those into the +# running instance over NATS. + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - .gitea/workflows/deploy.yml + +env: + PORTAL_RELEASE: build-2b593ba + INSTANCE: redoal-portal + +jobs: + deploy: + runs-on: bare + steps: + # Instance config comes from THIS repo's Actions variables/secrets + # (Settings -> Actions) - not the portal repo's. Guard before + # touching anything on the host: a missing secret must fail the + # run, not silently write an empty value into the live env file. + - name: Check instance secrets are configured + run: | + set -eu + [ -n "${{ secrets.NATS_URL }}" ] || { echo "missing secret NATS_URL"; exit 1; } + [ -n "${{ secrets.OAUTH2_CLIENT_SECRET }}" ] || { echo "missing secret OAUTH2_CLIENT_SECRET"; exit 1; } + [ -n "${{ secrets.PORTAL_GITEA_API_TOKEN }}" ] || { echo "missing secret PORTAL_GITEA_API_TOKEN"; exit 1; } + + # uhhm/portal is public, so the asset download is anonymous. The + # app@ template's ExecStart is /srv/app/%i/current/%i - the shipped + # binary is named "portal", so link the instance name to it. + - name: Ship pinned portal release + run: | + set -euo pipefail + api="${{ github.server_url }}/api/v1/repos/uhhm/portal" + url=$(curl -sf "$api/releases/tags/$PORTAL_RELEASE" | jq -r '.assets[0].browser_download_url') + rel="/srv/app/$INSTANCE/releases/$PORTAL_RELEASE" + rm -rf "$rel" + mkdir -p "$rel" + curl -sfL "$url" | tar -xz -C "$rel" + ln -sfn portal "$rel/$INSTANCE" + + - name: Write service env + run: | + cat > /etc/app/$INSTANCE.env < /etc/caddy/services.d/$INSTANCE.caddy <<'EOF' + www.redoal.com { + redir https://redoal.com{uri} permanent + } + + redoal.com { + reverse_proxy host.docker.internal:3020 + log { + output file /var/log/caddy/redoal.log + } + } + EOF + docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile diff --git a/.gitea/workflows/lint-and-reload.yml b/.gitea/workflows/lint-and-reload.yml index 07f03fc..8619f0f 100644 --- a/.gitea/workflows/lint-and-reload.yml +++ b/.gitea/workflows/lint-and-reload.yml @@ -15,13 +15,13 @@ jobs: steps: - uses: actions/checkout@v4 - # Same bare-metal runner/host as portal's own deploy job, which - # publishes this binary to a stable path on every deploy - runs - # portal's real transition-table/shape validation directly, not a - # hand-maintained yq/jq subset of the same rules (lint.sh, now - # superseded and removed). + # Same bare-metal runner/host as this repo's own Deploy workflow, + # which ships question_lint alongside the portal binary in every + # pinned release - so content is validated by the exact portal + # version this instance runs, not a hand-maintained yq/jq subset + # of the same rules. - name: Lint questions - run: /srv/app/uhhm-portal/current/question_lint --path questions + run: /srv/app/redoal-portal/current/question_lint --path questions reload: runs-on: bare