Files
esh-pfi-infrastructure/stacks/hrafn/ci/gitea-workflows-deploy.yaml
T
vh 9642952a54 docs(hrafn-ci): mirror the now-canonical vh/hrafn pipeline; record two failed runs
claude-bot holds write on vh/hrafn as of 2026-08-23, so the canonical copy of
the pipeline moved there and infra-ops maintains it directly instead of
routing patches through the repo holder. The files here are a verified mirror
(byte-identical to live at 22e0eb9d75a6).

Live state: run 9922 green, and both new content assertions executed rather
than merely existing --

  verify 4/5  host tree matches shipped context (a99ce748a0c9...)
  verify 5/5  image source matches host source (06f209fd0641...)

The CI-computed context hash matching on the host is the end-to-end proof
that the converge lands what CI ships. Its absence is what let the
frozen-source bug survive every green deploy.

Also records why the HEAD == GITHUB_SHA assertion was added and then removed:
it needed the git binary (run 9920, exit 127), and installing git flipped
actions/checkout@v4 from its node implementation to the git binary, which
died on a missing CA bundle (run 9921). A nice-to-have assertion changed the
checkout code path and broke a working pipeline; it guarded a hypothesis that
proved wrong, so it went rather than getting ca-certificates bolted on.
2026-08-23 02:55:02 -07:00

128 lines
5.7 KiB
YAML

# Gitea Actions workflow for hrafn.
#
# Runs on every push to main (and on manual workflow_dispatch). Drives
# the in-repo elway playbook (playbooks/deploy.yaml) — that file is the
# single source of truth for "how hrafn is deployed", manual or automated.
#
# Replaces the hand-rsync deploy. Two things change as a result:
# - the image is tagged with the commit SHA rather than a fixed `v1`,
# so the host can answer "what is running" and a rollback is a retag
# - only the build context ships (Dockerfile, compose.yaml,
# pyproject.toml, README.md, src/) instead of the whole working tree,
# so tests/, docs/, persistent-memory.md and friends stop living in
# /opt/docker/compose/hrafn on the server
#
# Required Actions secrets — both already exist at user scope on vh from
# the nevermore/task-board wiring, so there is nothing new to provision:
#
# DEPLOY_SSH_KEY Private SSH key authorized for lkraven@ana-docker.
# MGMT_REPO_TOKEN Gitea PAT (read:repository) on vh/esh-pfi-infrastructure.
# Needed to clone elway from the management repo.
#
# NOT handled here: .env. It is host-owned, 0600, holds the bearer token,
# and is provisioned once from vault item ana-docker/hrafn/bearer-token.
# The playbook refuses to deploy if it is missing or not 0600.
name: Deploy hrafn
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: pfi-fleet
steps:
- name: Install playbook prerequisites
# DO NOT add `git` here. actions/checkout@v4 uses its node
# implementation when no git binary is present, which is what every
# working run of this pipeline has used. Installing git flips it to
# the git binary, and this image ships no CA bundle — the checkout
# then dies with "server certificate verification failed. CAfile:
# none" (run 9921). Adding ca-certificates would paper over it; not
# installing git avoids the code-path change entirely.
run: |
apt-get update -qq
apt-get install -y --no-install-recommends \
python3 python3-yaml openssh-client tar
rm -rf /var/lib/apt/lists/*
- name: Checkout hrafn (triggering repo)
uses: actions/checkout@v4
with:
# Self-hosted runners reuse workspaces. Without a clean checkout a
# stale tree can be tarred while GITHUB_SHA claims the new commit —
# exactly the kind of drift the SHA tagging is supposed to prevent.
clean: true
# NO HEAD-vs-GITHUB_SHA ASSERTION. One was added here and removed: it
# needed the git binary, whose installation broke the checkout (above),
# and it was guarding a hypothesis that turned out to be wrong — the
# frozen-source bug was a self-referential rsync in the playbook, not a
# stale runner checkout. `clean: true` covers workspace reuse, and the
# host-side content hash proves the shipped bytes landed. Adding a
# package and a code-path change to assert a third time was not worth
# destabilising a working checkout.
- name: Checkout management repo (for elway)
uses: actions/checkout@v4
with:
repository: vh/esh-pfi-infrastructure
token: ${{ secrets.MGMT_REPO_TOKEN }}
path: _mgmt
- name: Configure SSH to ana-docker
run: |
mkdir -p ~/.ssh
printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
cat > ~/.ssh/config <<'EOF'
Host ana-docker
HostName 10.250.50.70
User lkraven
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking accept-new
EOF
chmod 600 ~/.ssh/config
- name: Build the deploy context
# Only what the Dockerfile actually consumes, plus compose.yaml
# and .env.example. Deliberately excludes tests/, docs/,
# persistent-memory.md, ROADMAP.md, CLAUDE.md, LICENSE and .env —
# none of them belong in a production compose directory.
#
# This list is AUTHORITATIVE: the playbook converges the host
# directory onto this tarball with rsync --delete, so anything
# omitted here is removed from the host on the next deploy
# (except the host-owned .env and .deployed).
run: |
mkdir -p dist
tar czf dist/hrafn-context.tgz \
Dockerfile compose.yaml pyproject.toml README.md .env.example src
echo "context contents:"
tar tzf dist/hrafn-context.tgz
# Content hash over the same file list, in the same order the
# playbook recomputes it on the host. This is what turns "the
# deploy said OK" into "the bytes on the host are the bytes CI
# built" — the assertion whose absence let the host source stay
# frozen at 0.1.0 through every green deploy.
CTX=$(find Dockerfile compose.yaml pyproject.toml README.md .env.example src \
-type f | LC_ALL=C sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
echo "context_sha256=$CTX" | tee -a "$GITHUB_ENV"
echo " version in this context:"
grep -h '__version__' src/hrafn/__init__.py || true
- name: Deploy hrafn (in-repo elway playbook)
# hrafn_sha becomes the image tag and is written to
# /opt/docker/compose/hrafn/.deployed on the host. context_sha256
# is asserted against the host tree AFTER the converge, so a deploy
# that fails to land new source fails the job instead of passing.
run: |
_mgmt/scripts/elway ana-docker \
--playbook playbooks/deploy.yaml \
--var hrafn_sha=${GITHUB_SHA::12} \
--var context_sha256=${context_sha256}