Verify the deploy workflow end to end, before there is anything to lose #9
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
.gitea/workflows/deploy.yamlhas never run. It is the stage 0 done-when andthe gate every later stage passes through, and it currently carries at least two
untested assumptions:
actions/upload-artifact@v3anddownload-artifact@v3are EOL upstream, andthe deploy job assumes the artifact preserved the
asset/anddashboard/dist/directory structure. If v3 flattens or the action isunavailable on this runner, every rsync path in the deploy job is wrong.
WEB_HOSTneeds agitea_ciaccount and the narrower sudoers grant thatscript/infra-setup.shnow provisions. That path has never been exercised.
Run it now, while a failed deploy costs nothing, rather than during the first
stage that has state worth keeping.
Files
.gitea/workflows/deploy.yaml— whatever the dry run shows is wrongscript/infra-setup.sh— sameSteps
script/infra-setup.shagainst bob and the proxy, and confirm bothgitea_cigrants validate withvisudo -cf.workflow_dispatch.version if v3 does not work on this runner.
bob:23296/v1/readyfrom the proxy, so closing the firewalld serviceshould make it fail. Verify that, rather than assuming it.
/v1/readyreportsconfig: "ok"anddatabase: "not_implemented",which is the honest state until stage 1 lands.
https://tireless.internalafter the nginxvhost is installed by hand (infra-setup step 3).
Acceptance
workflow_dispatch.curl -fsS https://tireless.internal/v1/readyreturns 200 from inside themesh.
systemctl is-activereports all three units active on bob.sudo -u tireless tireless preflighton bob reports the expected billingmode and a non-Anthropic OpenCode lane.
Out of scope
one-time steps documented in
script/infra-setup.sh; this issue only needsthe runner to start, and it is expected to refuse until the login exists.
not.
Green: run 8.
What was wrong
Eight faults. The two the issue predicted were both real, but neither was the
one that would have cost the most to find later.
runs-on: fedora-43-rustis not a registered labelstarted_at: 1970-01-01and are recorded as "cancelled", which tells you nothing:443, owned by the stream SNI routernginx -tpasses; the symptom is the wrong certificate on a working handshaketireless.internal--rsync-pathword-split by an unquoted variablerestoreconon/var/lib/tireless, which cannot exist yet0640 root:root; services run astireless--chmodbeing a no-op without-p, so the fix would have worked once and silently stopped[api] bind$uniton the remote sidesystemctl is-active .service: checking nothing, and would have reported healthy regardlessThe artifact-action risk this issue led with turned out to be moot — build and
deploy collapsed into one job, since the
rustimage already carries node, sshand rsync, so there is nothing to hand between jobs.
Six of the eight were silent. That is the part worth carrying forward: where a
probe exists, it must be able to fail. The health probe now runs from the
proxy over the mesh rather than bob's loopback, so closing firewalld breaks it.
Faults 4–8 were found by exercising the deploy directly against the hosts as
gitea_ci— every rsync destination, everysudocommand, and the health probestep extracted from the YAML and run as a file — rather than at six minutes per
attempt.
Provisioned
infra-setup.shnow does the ingress rather than describing it: mints thetireless.internalcert through the JWK provisioner (removing the credentialeven on failure), installs the vhost via
sites-available+ symlink, enablesstep@tireless.timer, and registers the split-horizon record on bothrouters — a record on one router
NXDOMAINs at the other site.opn-clihas noreconfigure verb, so the apply is a direct API POST; without it the name
resolves only whenever Unbound next happens to reload.
The served certificate was checked against disk by serial, per
internal-tls.md§3. They match.Remaining, and out of scope per this issue
tireless-runneris infailedbecause the interactive agent login has not beendone as the service account (
script/infra-setup.shstep 1). That is theinvariant working — a runner with no credentials refuses to start rather than
pretending — and it is why the deploy tolerates that one unit failing.
Note the acceptance criterion "all three units active" contradicted this issue's
own Out of scope section, which said the runner is expected to refuse. The Out
of scope text was right; the criterion was over-specified when I wrote it.
Also worth fixing, outside this repo
architecture/internal-tls.md§5 says hanzalova vhosts bind:443directly.reverse-proxies.md§4 says both proxies use the127.0.0.1:14443tier andnotes hanzalova gained its router on 2026-07-30; the live
bench.internal.confconfirms it. Anyone following §5 gets fault #2 — which that same doc warns about
in §3. Worth a correction in the architecture repo.