Publish docs — automated release
Dispatch publish.yml with the epoch Devin tagged
after pushing changes. The old manual flow (stepwise sync → wiki
builds → deploys → backups → bookkeeping) is fully automated — review
is the only manual gate. Architecture: publish-pipeline;
infrastructure: gitea, wiki, infra-wiki, dev-vm.
Steps
- Devin works in
~/repos/infra-docs(branch → squash-merge →main), tags the publish epoch, then pushesmain+ tags to the devvm mirror. - Operator reviews the diff and dispatches
publish.ymlfrom gitea → Actions on the tooling repo,expect_epoch=<hash>(the bare hash — nopublish-epoch-prefix).force_wiki=trueonly to force a redeploy when content moved butinfra-wikididn’t. - Watch the run — each phase logs a
===header. Phases: setup → sync → wiki → backups → finalize-epoch → state-save. - Verify:
ci/epoch.sh status ~/mirrors infra-docs infra-docs-mirror infra-wiki— the new epoch tag on all repos = the run landed end-to-end (mirrors ← runner ← store). Content on the wiki: green deploy message in the log, or spot-check a new page. - Per operator machine:
./operator-pull.sh— ff-pulls the clones. On the LiveSync-source machine, open Obsidian once to push to couchdb; other clients receive it from LiveSync.
What the run does
setup → poll for expect_epoch on all epoch repos (5s, 2min
budget; waits out the ferry) → set the publish-lock ref → render the
docmirror config from ci.env + secrets → sync-in → sync-out →
wiki: w.sh sync into infra-wiki → commit content → build + deploy
local (wiki over rsync/ssh) and portfolio (wrangler → Pages) →
--mirror backup push of each backup repo to gitea → stamp the new
epoch tags → commit/push the state repo (declared files only) → the
lock ref clears on exit and the ferry shuttles the run’s output refs
back to the devvm mirrors on its next pass.
Operator prerequisites
host-ferryservice running on «HOSTNAME» —ssh «HOSTNAME» 'systemctl status host-ferry'. Output relay uses ssh aliasesdevin/cifrom the host’s~/.ssh/config.- Workflow credentials in gitea — Actions secrets + variables on the
tooling repo (
GITEA_*is a reserved prefix — names can’t start with it). - Runner LXC 116
gitea-runnerup (10.0.1.36), service unitgitea-runner, registered labelgitea-runner:host— the workflow selects it viaruns-on: gitea-runner. Needsfeatures: nesting=1in/etc/pve/lxc/116.conffor the containerized wiki build.
Notes
- The runner mirrors are read-only endpoints for everyone but the
runner —
ssh ci /srv/mirrors/*.gitonly for inspection. Push to them only if you’re fixing runner state by hand (log says what it refused to deliver). - Devin doesn’t touch devvm mirrors while a run is in flight — if a repo’s tracked head diverges from canonical the ferry abandons the whole repo’s output, so the epoch can never claim a completed run that lost writes. It’s already happened for a routine push during a window; treat the epoch as the verdict.
- The output epoch lives on
mainofinfra-docs,infra-docs-mirror,infra-wiki. Watching it appear on the devvm mirrors is the green-light — a run that never completes never stamps it.
Failure → action
- Epoch wait times out (
still waiting forin setup): the ferry hasn’t delivered the input epoch to the runner yet — check the ferry service on «HOSTNAME», then re-dispatch the same epoch. abandoning this run's outputin the ferry log: runner-side heads diverged from canonical — check whether the run actually finished (stalepublish-epoch-*tag on the runner’s repo is the tell). If so the diverged data is recoverable from the runner’s/srv/mirrorsor the tracking refs in the store; if not, rerun.- Wiki deploy failed but the rest went green: phase order
preserves the redaction bookkeeping; force the wiki stage only via
force_wiki=trueon re-dispatch. - State push failure at
state-save: the epoch still landed — fix the state clone remote/permissions on the runner, then re-run or push manually. - Ferry service down / machine change: output stays on the runner’s mirrors until a pass relays it — no data loss, epoch just lags on devvm.