Six prior PRs (#273, #319, #322, #341, #484, #486) all kept calling
`docker login` and tried to coerce credsStore via increasingly elaborate
config tricks. None worked. The latest publish-canvas-image and
publish-platform-image runs on main are still failing with:
error storing credentials - err: exit status 1,
out: `User interaction is not allowed. (-25308)`
Verified locally on the runner host (2026-04-16): `docker login` on
macOS unconditionally writes credentials to osxkeychain after a
successful login, regardless of the config presented to it.
# I wrote this:
{ "auths": {}, "credsStore": "", "credHelpers": {} }
# After `docker login --config <dir> ghcr.io ...` succeeded:
{
"auths": { "ghcr.io": {} }, # empty — auth is in Keychain
"credsStore": "osxkeychain" # Docker rewrote it back
}
So `--config` flag, DOCKER_CONFIG env var, credsStore="" etc. all share
the same fate: Docker re-enables osxkeychain after every successful
login. The Mac mini runner is a launchd user agent with a locked
Keychain, so storage fails with -25308.
This PR replaces the `docker login` invocation entirely. We write
`base64(user:pat)` directly into the disposable DOCKER_CONFIG's `auths`
map. `docker/build-push-action@v5` and the daemon honor the auths map
for push without ever calling `docker login`, so the Keychain is never
involved.
Same shape in both workflows:
- publish-canvas-image.yml — single registry (ghcr.io)
- publish-platform-image.yml — two registries (ghcr.io + registry.fly.io)
Fly username remains literal "x".
Security:
- Token env vars never echoed. Heredoc writes the auth blob via
`umask 077` (file mode 600). The temp config dir lives under
RUNNER_TEMP and is reaped at job end.
- Diagnostics preserved (docker version + binary ls + registry keys
only, no values) so future runner permission regressions remain
visible without leaking secrets.
Equivalent to closed PR #464 — re-opening because main is still
broken (verified by inspecting the most recent failure). The closing
comment on #464 stated the issue was already addressed by #341, but
it isn't.