Add a way to read back registry login state
Description of feature
There is no way to ask whether an app is already logged in to a registry with a given credential. registry:login shells out to docker login and keeps nothing of its own, registry:logout removes it, and registry:report exposes only image-repo, image-repo-template, push-on-release, server, tag-version, and push-extra-tags. The single observable is the exit code of registry:login itself, which means any tool converging a declared state onto a server has to re-run the login on every pass - a network round trip to the registry, and a credential written again, purely to discover nothing needed to change.
Most of the reader already exists. HasAppRegistryAuth in plugins/registry/functions.go opens the per-app config.json, unmarshals it, and stops one level short of the answer:
auths, ok := config["auths"].(map[string]interface{})
return ok && len(auths) > 0It only asks whether some credential exists, never for which server or what it holds. GetAppRegistryConfigPath already names the file, and the global case is root's docker config, since CommandLogin sets DOCKER_CONFIG only when an app name is supplied.
The shape that fits best is a comparator rather than a dump, mirroring git:auth-status from v0.38.0: something like registry:auth-status [--app <app>|--global] <server> [<username> <password>], exiting 0 when the stored credential matches, 1 when it does not, printing nothing. With no username it would report whether any entry exists for that server, which is the check a caller needs before deciding to log out. The caller has to already hold the password to ask, so nothing is disclosed - the same posture git:auth-status settled on, and the reason it reads exit codes instead of values. Implementation on top of the existing reader is a lookup of auths[<key>].auth, a base64 decode, and a split on the first colon.
Four things make this less mechanical than it first appears, and they are the reason this is worth doing in the plugin rather than leaving each caller to poke at config.json themselves.
Server normalization happens twice. registry:login already rewrites hub.docker.com and docker.com to docker.io, and docker itself then stores Docker Hub under https://index.docker.io/v1/ rather than docker.io. So the key in auths is two transformations away from the string the user passed, and a naive lookup reports a mismatch for the most commonly used registry. That second transformation is docker's behaviour rather than dokku's and is worth confirming against a real config.json before relying on it.
Credential helpers mean the secret may not be in the file at all. With credsStore or credHelpers configured, docker login puts the credential in the OS keychain and leaves the auths entry effectively empty. Per-app configs live in dokku's own directory and would not normally have a helper, but the global case is root's ~/.docker/config.json, which an operator may well have set up that way. The command therefore needs a third outcome meaning "cannot determine", distinct from "does not match" - otherwise a converging caller re-runs the login forever while believing it has a real answer.
Identity tokens behave the same way. Some registries cause docker login to write an identitytoken and leave auth empty or placeholder, so a username:password comparison fails against a login that is perfectly valid.
And "the stored credential matches" is not "the credential still works" - a revoked password compares equal and fails at pull time. That is the right semantic for drift detection and matches what git:auth-status does, but it belongs in the help text so nobody reads exit 0 as a guarantee of authentication.
Separately and additively, a --registry-auth-username field on registry:report (plus perhaps a boolean for whether auth is configured at all) would make the non-secret half of the credential readable. The username is not secret in the way the password is - docker login prompts for it in the clear - and having it is the difference between a caller being able to detect drift and being able to reconstruct the configuration. Worth considering, but not required for the comparator above.
A hashed fingerprint field is the one option I would argue against: an unsalted digest of username:password is offline-bruteforceable, and salting it per install makes it useless for comparing state across servers.
Source: dokku/dokku