fix(ci): authenticate to Gitea's registry with a package-scoped token
CI / Typecheck, test, feed (push) Successful in 13s
CI / Build site (push) Successful in 9s
CI / Container image (push) Successful in 34s

`docker login` was refused with a bare `unauthorized`. The root cause is
not a typo in the credentials but a difference in what a Gitea package
is: it belongs to a user or org **owner**, not to a repository, so the
ephemeral repo-scoped Actions token carries no authority over it and the
registry rejects it outright. GHCR accepted GITHUB_TOKEN and GitLab
issues working per-job credentials in $CI_REGISTRY_PASSWORD; Gitea
deliberately does neither, so this needs a real access token with the
package scope, supplied as REGISTRY_TOKEN.

The username moves from github.actor to github.repository_owner for the
same reason. The image is pushed into the owner's namespace and the
token belongs to that owner, while actor is merely whoever triggered the
run — a different person on a dispatch, the wrong account on a fork.

Added a preflight check on the secret, because the failure it replaces
said nothing about what to do. It also settles a question the evidence
left open: the reports of this problem describe login succeeding and the
*push* failing, while ours failed at login, which is consistent with the
token being rejected but equally with its never having been set. The
check distinguishes the two the next time it happens.

No test pins this. The three workflow assertions in test/refresh.test.ts
exist because those defects are silent; this one turns CI red on the
spot, which is the condition that made them worth writing.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-09-16 04:38:53 +02:00
co-authored by Claude Opus 5
parent f32eb93289
commit 76bd7d0b70
2 changed files with 50 additions and 2 deletions
+16
View File
@@ -358,6 +358,22 @@ The image lands at `gitea.lucaswinther.info/lucasw89/gacha-event-tracker`, tagge
the commit SHA. The registry host is derived from `github.server_url` rather than hardcoded, so a
fork on another instance pushes to its own registry.
**Pushing the image needs one one-time step CI cannot do for itself.** A Gitea package belongs to a
*user or org owner*, not to a repository, so the ephemeral Actions token has no authority over it and
the registry refuses it — `secrets.GITHUB_TOKEN` does not work here, unlike on GHCR and unlike
GitLab's `$CI_REGISTRY_PASSWORD`. So:
1. Create an access token at **Settings → Applications** with the **package** read + write scope.
2. Add it to the repository under **Settings → Actions → Secrets** as **`REGISTRY_TOKEN`**.
The `image` job checks for that secret before it tries to log in, and fails with those instructions
rather than a bare `unauthorized` from the Docker daemon. The username it logs in with is
`github.repository_owner`, not `github.actor` — the image goes to the owner's namespace, and the
actor is merely whoever triggered the run.
One consequence of packages being owner-scoped: the pushed image is **not** automatically associated
with this repository. Linking it is a manual step on the package's own settings page.
The feed job fails if the event count collapses, or if any single source parses to nothing — nine
healthy sources hide a tenth that has gone quiet, and the total stays comfortably over the floor
while one game shows an empty calendar. That is the failure mode a parser-only pipeline is most