# 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}