Files
questions/.gitea/workflows/deploy.yml
T
Bendik Aagaard LynghaugandClaude Fable 5 19eccfd212
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Pin portal v0.3.18: convergence animation, overlay results
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y42TyF8Zu7NGRR2893vNcZ
2026-09-01 16:01:03 +02:00

116 lines
5.0 KiB
YAML

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: v0.3.18
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
# Portal ships content-hashed pkg files (portal.<hash>.js) with
# a hash.txt in the site root; this makes the server reference
# them, so a stale cached bundle can never pair with new wasm.
LEPTOS_HASH_FILES=true
# 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.
# enable: the unit must come back after a host reboot (2026-08-30 a
# reboot left both portal instances down - deploys had only ever
# started them). Needs the manage-unit-files polkit grant; until
# that's on the host the enable is reported and skipped, never a
# failed deploy.
- name: Activate and restart
run: |
ln -sfn "/srv/app/$INSTANCE/releases/$PORTAL_RELEASE" /srv/app/$INSTANCE/current
systemctl enable app@$INSTANCE.service \
|| echo "::warning::could not enable app@$INSTANCE (polkit) - unit will not survive a reboot"
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