Repository navigation
ci(chart-gate): pull docker_auth and loki from the digest-pinned GHCR mirror, never Docker Hub anonymously - #6404
Conversation
… mirror, never Docker Hub anonymously On a GitHub runner both executed-binary checks resolve their Docker Hub image through .github/scripts/dockerhub-mirror.sh (red when undeclared, red when the mirror's digest is not the chart's pin) and log in to ghcr.io with the job's own GITHUB_TOKEN (packages: read) — no stored secret, so forks and Dependabot work. On a laptop the image is used as written. Doc/Architecture/DockerHubInCi. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Digestless chart references can pass, and Loki’s tested mirror digest is not tied to the production deployment.
2 open findings
What changed in this PR
This PR follows #6400 by routing Chart Gate runtime images through the authenticated, digest-pinned GHCR mirror.
Changes:
- Adds GHCR authentication to both image-executing Chart Gate jobs.
- Resolves Loki and
docker_auththrough the mirror. - Updates mirror documentation and workflow comments.
| File | Description |
|---|---|
.github/workflows/chart-gate.yml |
Adds GHCR package-read authentication. |
.github/scripts/test-bundle-registry.py |
Resolves Docker Hub test images through GHCR. |
deploy/aks/scripts/check-observability-values.py |
Uses mirrored Loki for binary validation. |
.github/workflows/dockerhub-mirror.yml |
Documents the new consumers. |
src/MeshWeaver.Documentation/Data/Architecture/DockerHubInCi.md |
Removes the temporary Chart Gate exception. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if digest and not mirrored.endswith("@" + digest): | ||
| raise SystemExit(f"::error::the chart pins {image} but .github/dockerhub-mirror.list mirrors {mirrored} — " | ||
| f"move the list's digest to the chart's, or the check would execute bytes the chart does not deploy") |
There was a problem hiding this comment.
Fixed: on a runner, through_dockerhub_mirror now refuses a Docker Hub chart image that has no @sha256: pin, naming the image. So a change to a tag-only authImage turns red instead of quietly passing against the mirror's digest. Verified locally: the pinned image resolves to the mirror, and cesanta/docker_auth:1.14.0 without a digest is refused.
| name = f"grafana/loki:{app_version}" | ||
| if os.environ.get("GITHUB_ACTIONS") != "true": | ||
| return name | ||
| mirror = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", | ||
| ".github", "scripts", "dockerhub-mirror.sh") | ||
| result = run(["bash", mirror, "ref", name]) |
There was a problem hiding this comment.
Agreed, this drift is real, but it predates this change and is not created by it. install-observability.sh and values.observability.yaml deploy grafana/loki:<appVersion> by tag, with no digest. Before this PR, CI pulled that same moving tag at check time. Now it validates the declared digest (sha256:eb92f1a4…, which is the tag's current digest). Pinning the production Loki image, and having installer and checker read one shared pin, changes what the cluster runs. That deserves its own change through the observability install path, so it is not folded into a CI rate-limit fix.
… the rejection test accepts the mirror's name Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Test Results 24 files ±0 24 suites ±0 49m 54s ⏱️ -29s Results for commit e8c1d01. ± Comparison against base commit 0a2dc31. This pull request removes 40 and adds 16 tests. Note that renamed tests count towards both. |

Why
This is the follow-up to #6400 (Refs #6381). The Chart Gate's two executed-binary checks still pulled
cesanta/docker_authandgrafana/lokifrom Docker Hub anonymously, and both went red ontoomanyrequestson #6400's own run. #6400 created the digest-pinned GHCR mirror. Its first main sync (run 38009458740) copied both images and read their digests back, identical to the declared ones:ghcr.io/systemorph/dockerhub/cesanta/docker_auth:1.14.0=sha256:98e0307e…ghcr.io/systemorph/dockerhub/grafana/loki:2.9.3=sha256:eb92f1a4…What
chart-gate.yml→Observability valuesandBundle registry scripts (executed): each job getspackages: readand a ghcr.io login with the job's ownGITHUB_TOKEN. No stored secret is involved, so forks and Dependabot work the same way.test-bundle-registry.py: on a GitHub runner, Docker Hub images resolve throughdockerhub-mirror.sh ref. The check is red if an image is undeclared, and red if the mirror's digest is not the chart's pin, so the bytes under test are the bytes deployed. On a laptop the image is used as written.check-observability-values.py:grafana/loki:<appVersion>resolves the same way. A chart bump to an appVersion the mirror does not declare is red and names the list.There are no retries and no docker.io fallback.
Verification (local)
GITHUB_ACTIONS=true: docker_auth resolves to the mirror. A wrong digest is refused, an undeclaredalpine:3is refused, and a ghcr image passes through unchanged. On a laptop the Docker Hub name is kept.loki_image('2.9.3')resolves to the mirror.3.0.0exits 1, naming the list.test-observability-values.py: 21 tests OK.actionlintandcheck-workflow-shell.pyare clean.check-pr-secret-preflight.py: 0 violations.Pairs-with: none — CI scripts and docs only, no public C# surface.
🤖 Generated with Claude Code