diff --git a/.gitea/workflows/deploy-site.yml b/.gitea/workflows/deploy-site.yml index 11e3a70..ec0548b 100644 --- a/.gitea/workflows/deploy-site.yml +++ b/.gitea/workflows/deploy-site.yml @@ -1,15 +1,15 @@ name: Deploy test site # Builds site/dist (site/public/ plus demo pages copied out of docs/, see -# site/build.sh) and publishes it to https://cnxvinyl.lol/ on the -# buildfor.life web server (s1), the same Caddy that serves the blog. -# One-time setup (DNS, Caddy vhost, secrets, deploy key): site/README.md. +# site/build.sh) and publishes it to the B4L package registry as the generic +# package B4L/cnxvinyl.lol. bkk01 serves https://cnxvinyl.lol/ from those +# releases (cnx-network-clan, docs/src/s1-migration.md): the registry's +# package event makes it fetch a new version within seconds, and a timer +# there fetches every 15 minutes as a backstop. # -# Secrets (repo or user level): -# VPS_HOST, VPS_USER, VPS_SSH_KEY required; same server as the blog -# VPS_PORT optional, default 22 -# VPS_KNOWN_HOSTS optional pinned host key line(s); -# without it the key is scanned per run +# Secrets (organization level): REGISTRY_USER, REGISTRY_TOKEN, an account with +# a write:package token in a B4L team that may write packages (b4l-ci). The +# job's own token only reads the repository: the registry refuses it. on: push: @@ -21,20 +21,19 @@ on: - ".gitea/workflows/deploy-site.yml" workflow_dispatch: -# A newer push supersedes a running deploy. Safe to cancel mid-run: rsync -# writes the non-live directory and the rename swap happens last. +# A newer push supersedes a running deploy; bkk01 serves whichever version +# the registry lists as the latest. concurrency: group: deploy-cnxvinyl-lol cancel-in-progress: true +permissions: + contents: read + jobs: deploy: runs-on: ubuntu-latest env: - VPS_HOST: ${{ secrets.VPS_HOST }} - VPS_USER: ${{ secrets.VPS_USER }} - VPS_PORT: ${{ secrets.VPS_PORT }} - SITE_DIR: /var/www/cnxvinyl.lol SITE_URL: https://cnxvinyl.lol steps: - name: Checkout @@ -43,14 +42,9 @@ jobs: - name: Build site/dist run: bash site/build.sh - - name: Publish to the package registry (bkk01) - # bkk01 serves cnxvinyl.lol from releases in the B4L package registry - # (cnx-network-clan, docs/src/s1-migration.md): the package event - # makes it fetch this version within seconds. Until DNS moves to - # bkk01, s1 below stays the live server and gets the same build. - # First, so a failing s1 step cannot hold back bkk01's release. - # REGISTRY_USER / REGISTRY_TOKEN: organization secrets, an account - # with a write:package token in a B4L team that may write packages. + - name: Publish to the package registry + # The version joins the UTC time and the commit, so versions never + # collide. The archive holds the site's root itself. env: REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} @@ -65,86 +59,18 @@ jobs: "${{ github.server_url }}/api/packages/${{ github.repository_owner }}/generic/cnxvinyl.lol/$version/site.tar.gz" echo "published cnxvinyl.lol $version ($(du -h site.tar.gz | cut -f1))" - - name: Set up SSH - env: - VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }} - VPS_KNOWN_HOSTS: ${{ secrets.VPS_KNOWN_HOSTS }} - run: | - if [ -z "$VPS_HOST" ] || [ -z "$VPS_USER" ] || [ -z "$VPS_SSH_KEY" ]; then - echo "::error::VPS_HOST, VPS_USER and VPS_SSH_KEY must be set as Actions secrets (see site/README.md)" - exit 1 - fi - port=${VPS_PORT:-22} - # ssh refuses a config that is group/world-writable or owned by - # another user ("Bad owner or permissions on ~/.ssh/config"), which - # a runner with a loose umask or a reused HOME produces: create every - # file here private and fresh. - umask 077 - mkdir -p ~/.ssh && chmod 700 ~/.ssh - rm -f ~/.ssh/config - printf '%s\n' "$VPS_SSH_KEY" | tr -d '\r' > ~/.ssh/deploy_key - chmod 600 ~/.ssh/deploy_key - # Fail here, not at the server, when the secret is not a usable key, - # and log its fingerprint (public) so it can be compared with - # `ssh-keygen -lf ~deploy/.ssh/authorized_keys` on the server. - if ! ssh-keygen -y -P "" -f ~/.ssh/deploy_key > ~/.ssh/deploy_key.pub 2>/dev/null; then - echo "::error::VPS_SSH_KEY does not load as an unencrypted private key; paste the whole OpenSSH-format key including the BEGIN/END lines" - exit 1 - fi - echo "key in VPS_SSH_KEY: $(ssh-keygen -lf ~/.ssh/deploy_key.pub)" - if [ -n "$VPS_KNOWN_HOSTS" ]; then - printf '%s\n' "$VPS_KNOWN_HOSTS" >> ~/.ssh/known_hosts - else - for i in 1 2 3; do - ssh-keyscan -T 10 -p "$port" "$VPS_HOST" > ~/.ssh/keyscan.out && [ -s ~/.ssh/keyscan.out ] && break - echo "ssh-keyscan attempt $i failed; retrying in 15 s" >&2 - sleep 15 - done - if [ ! -s ~/.ssh/keyscan.out ]; then - echo "::error::ssh-keyscan could not reach $VPS_HOST:$port (server down or runner IP blocked?)" - exit 1 - fi - cat ~/.ssh/keyscan.out >> ~/.ssh/known_hosts - fi - { - echo "Host webhost" - echo " HostName $VPS_HOST" - echo " User $VPS_USER" - echo " Port $port" - echo " IdentityFile ~/.ssh/deploy_key" - echo " IdentitiesOnly yes" - echo " BatchMode yes" - } > ~/.ssh/config - chmod 600 ~/.ssh/config ~/.ssh/known_hosts - - - name: Ensure rsync is available - run: command -v rsync || (sudo apt-get update && sudo apt-get install -y rsync) || (apt-get update && apt-get install -y rsync) - - - name: Deploy (rsync to the idle copy, then rename swap) - # Two persistent directories alternate as live and previous, as for - # the blog: rsync only ever writes the one that is not live, and the - # swap is three same-filesystem renames, so Caddy never serves a - # half-written tree. The previous build stays on disk for rollback. - run: | - # SC2029: the paths are meant to expand on the runner side - # shellcheck disable=SC2029 - ssh webhost "mkdir -p '$SITE_DIR' '$SITE_DIR-prev' && rm -rf '$SITE_DIR-tmp'" - rsync -az --delete site/dist/ "webhost:$SITE_DIR-prev/" - # shellcheck disable=SC2029 - ssh webhost "mv '$SITE_DIR' '$SITE_DIR-tmp' && mv '$SITE_DIR-prev' '$SITE_DIR' && mv '$SITE_DIR-tmp' '$SITE_DIR-prev'" - # shellcheck disable=SC2029 - live=$(ssh webhost "cat '$SITE_DIR/version.txt'") - echo "live on server: $live" - [ "$live" = "$(cat site/dist/version.txt)" ] || { echo "::error::server holds $live, expected $(cat site/dist/version.txt)"; exit 1; } - - name: Check the public URL - # Warning only: DNS or a basic_auth login can make this - # fail while the files on the server are already correct. + # bkk01 fetches the release a few seconds after the upload; give it up + # to two minutes. Warning only: the release is in the registry either + # way, and the timer on bkk01 picks it up. run: | expected=$(cat site/dist/version.txt) - got=$(curl -sS --max-time 20 "$SITE_URL/version.txt?nocache=$expected" || true) - if [ "$got" = "$expected" ]; then - echo "$SITE_URL serves $expected" - else - echo "::warning::$SITE_URL/version.txt returned '${got:0:80}', expected $expected" - fi + for i in $(seq 1 24); do + got=$(curl -sS --max-time 20 "$SITE_URL/version.txt?nocache=$expected" || true) + if [ "$got" = "$expected" ]; then + echo "$SITE_URL serves $expected" + exit 0 + fi + sleep 5 + done + echo "::warning::$SITE_URL/version.txt returned '${got:0:80}', expected $expected"