Insert artifact domain paths after /v2/ in bottle URLs#23062
Conversation
HOMEBREW_ARTIFACT_DOMAIN rewrites ghcr.io bottle URLs by replacing the domain prefix, so a value with a path (e.g. a Harbor proxy cache such as https://example.com/ghcr-io) produced https://example.com/ghcr-io/v2/homebrew/core/... which no OCI registry can serve: the OCI Distribution API requires /v2/ at the root of the host, with the path acting as a repository prefix. Insert the artifact domain's path after /v2/ instead, producing https://example.com/v2/ghcr-io/homebrew/core/... Domains without a path keep the existing behaviour, as does the fallback to the original ghcr.io URLs. Apply the same logic to the portable-ruby URL in vendor-install.sh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Why wouldn't you just set the HOMEBREW_ARTIFACT_DOMAIN to https://mirror.example.com/v2/ghcr-io instead?
If the issue is that https://mirror.example.com/ghcr-io can literally never ever be valid with any host in any circumstance: that should error out. I don't think you can state that, though, so it seems better that that would be error message improvement rather than behaviour change.
|
in the current code brew/Library/Homebrew/github_packages.rb Lines 18 to 19 in 586f1c2 then if HOMEBREW_ARTIFACT_DOMAIN is set, it replaces the https://ghcr.io part with HOMEBREW_ARTIFACT_DOMAIN, the final v2 is keptbrew/Library/Homebrew/download_strategy/curl_download_strategy.rb Lines 46 to 49 in 586f1c2 so if I do Containerd use |
brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?I used Claude Code to help draft the patch, tests and this PR text. I manually reviewed the changes and verified them end-to-end against a real Harbor 2.x proxy cache for ghcr.io (
brew fetchandbrew reinstallof bottles, plusbrew vendor-install rubyfor the Bash code path), and ranbrew lgtmsuccessfully.What
HOMEBREW_ARTIFACT_DOMAINrewrites ghcr.io bottle URLs by replacing thehttps://ghcr.io/prefix with the configured value. When that value contains a path — e.g. a Harbor or Nexus pull-through proxy for ghcr.io, where the upstream is exposed as a project/repository prefix such ashttps://example.com/ghcr-io— the result is:No OCI registry can serve this: the OCI Distribution Spec requires
/v2/at the root of the host, with any prefix acting as part of the repository name.This PR inserts the artifact domain's path after
/v2/instead:Artifact domains without a path keep the exact same behaviour as today, as does the fallback to the original
ghcr.ioURLs (andHOMEBREW_ARTIFACT_DOMAIN_NO_FALLBACK). The same logic is applied to the portable-ruby URL invendor-install.sh, with cross-referencing comments to keep both implementations in sync.Why
This makes
HOMEBREW_ARTIFACT_DOMAINusable with standard OCI registry proxy caches (Harbor, Nexus, Artifactory) that expose proxied upstreams under a repository prefix rather than a dedicated (sub)domain — a common corporate setup where provisioning one virtual host per proxied registry isn't practical. Related demand: #22115.Reproduction
With any Harbor project
ghcr-ioconfigured as a proxy cache forhttps://ghcr.io:The registry answers 404 for this path (the repository would be
homebrew/core/hellounder an API root that doesn't exist) and every download falls back toghcr.io, defeating the mirror. With this change:Verified end-to-end against a real Harbor 2.x proxy-cache project (with
HOMEBREW_DOCKER_REGISTRY_TOKENset so the auth header is kept):brew fetch --bottle-tag=x86_64_linux hello,brew reinstall yqandbrew vendor-install rubyall download through the proxy. Domains without a path still producehttps://mirror.example.com/v2/homebrew/core/...(unchanged), covered by the updated tests.🤖 Generated with Claude Code