Own the redoal-portal instance: deploy pinned portal releases from here
Same instance-ownership move as uhhm/questions: pin PORTAL_RELEASE, ship /srv/app/redoal-portal from the published artifact, write env from this repo's own Actions config, own the redoal.com Caddy route. Lint now uses this instance's question_lint, matching the pinned version. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
35bcfc33ae
commit
6c5b9a55b7
@@ -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 <<EOF
|
||||
NATS_URL=${{ secrets.NATS_URL }}
|
||||
KANIDM_URL=${{ vars.KANIDM_URL }}
|
||||
OAUTH2_CLIENT_ID=${{ vars.OAUTH2_CLIENT_ID }}
|
||||
OAUTH2_CLIENT_SECRET=${{ secrets.OAUTH2_CLIENT_SECRET }}
|
||||
PUBLIC_URL=${{ vars.PUBLIC_URL }}
|
||||
COOKIE_SECURE=true
|
||||
# This repo is its own instance's content source; branding
|
||||
# (title, wordmark, gesture hero + relay) rides in site.yaml.
|
||||
CONTENT_REPO=${{ github.server_url }}/${{ github.repository }}
|
||||
CONTENT_BRANCH=main
|
||||
LEPTOS_SITE_ADDR=0.0.0.0:3020
|
||||
# The release tarball carries the site bundle at site/ (no
|
||||
# target/ prefix), so override Cargo.toml's build-time path.
|
||||
LEPTOS_SITE_ROOT=site
|
||||
# Read-only Gitea token - here it also backs the
|
||||
# gitea_releases source for the private redoal/redoal repo,
|
||||
# so the owning user needs read access there.
|
||||
GITEA_API_TOKEN=${{ secrets.PORTAL_GITEA_API_TOKEN }}
|
||||
EOF
|
||||
|
||||
# Activate only after the release and env are fully written, so a
|
||||
# failed download or missing config never takes the site down.
|
||||
#
|
||||
# No sudo: the runner's unit sets NoNewPrivileges=yes - systemctl
|
||||
# talks to PID1 over D-Bus, authorized by the polkit rule scoped
|
||||
# to deploy-runner + the app@* unit pattern.
|
||||
- name: Activate and restart
|
||||
run: |
|
||||
ln -sfn "/srv/app/$INSTANCE/releases/$PORTAL_RELEASE" /srv/app/$INSTANCE/current
|
||||
systemctl restart app@$INSTANCE.service
|
||||
|
||||
# www redirects to the apex for the same single-cookie-scope
|
||||
# reason as uhhm's instance. The runner is in the docker group,
|
||||
# so no sudo here either.
|
||||
- name: Update Caddy routing
|
||||
run: |
|
||||
cat > /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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user