fix(ci): scrub the registry credential before running repo code
Both Claude reviewers caught this independently, and they are right. The test
job I added wrote a PUSH-CAPABLE REGISTRY_PASSWORD into a plaintext
~/.gitconfig and then ran `go build`/`go vet`/`go test` — repository code — on
pull_request events. This repo is public, so a fork PR could ship a test whose
only job is to print that file. The image build had already answered this
question correctly: its credentials are BuildKit secrets scoped to the
module-download RUN and are never present while code executes. I bolted on a
job that skipped the boundary its neighbour maintains.
Dependencies are now fetched in their own step which deletes ~/.gitconfig
before anything else runs, and asserts the scrub — against the whole home
directory, not against the file it just removed, because the credential can
also land in ~/.netrc or ~/.config/go/env. Verified the assertion is not
vacuous: planting the secret in ~/.netrc trips it. Later steps run with
GOPROXY=off, so any attempt to reach the network fails loudly rather than
quietly hunting for the credential that is now gone.
Also from round 4: TestEndpointProviderNamesAreAllAccepted pinned only
endpointProvider, while the constant is the error text for BOTH resolution
paths — it now asserts each advertised name resolves either way (break-checked
by dropping the gemini alias from resolveModel alone). preflight.sh documents
that ollama-cloud is checked on OLLAMA_API_KEY but hinted as
OLLAMA_CLOUD_API_KEY because run.sh copies one to the other first, an ordering
dependency that was invisible from the file.
And the comments that narrated this PR's own edit history ("the first version
of this change...") are rewritten as invariants. That history stops being true
the moment this merges, and the repo's doc policy says as much.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
+23
-11
@@ -75,13 +75,13 @@ func TestEndpointProvider(t *testing.T) {
|
||||
// together. kimi and qwen are majordomo built-ins that ARE the openai client at
|
||||
// a different base URL, and two independent places have to know it:
|
||||
// resolveModel's GADFLY_BASE_URL override, and endpointProvider's
|
||||
// GADFLY_ENDPOINT_* parser. Adding a name to one and not the other yields a
|
||||
// provider that works when configured one way and errors the other, for no
|
||||
// reason a user could guess — which is exactly what happened here on the first
|
||||
// pass. Asserting both in one table is what makes the pair fail together.
|
||||
// GADFLY_ENDPOINT_* parser. A name accepted by one and rejected by the other is
|
||||
// a provider that works when configured one way and errors the other, for no
|
||||
// reason a user could guess. Asserting both from one table makes the pair fail
|
||||
// together.
|
||||
func TestOpenAICompatProvidersResolveOnBothPaths(t *testing.T) {
|
||||
// Ranges the SHARED slice rather than a fourth copy of the names: a test
|
||||
// that pins a list against drift must not be able to drift from it.
|
||||
// Ranges the SHARED slice: a test that pins a list against drift must not
|
||||
// be able to drift from it.
|
||||
for _, provider := range openAICompatProviders {
|
||||
t.Run(provider+" via GADFLY_ENDPOINT_*", func(t *testing.T) {
|
||||
p, err := endpointProvider("ep", provider+"|https://host.example/v1|sk-x")
|
||||
@@ -105,21 +105,33 @@ func TestOpenAICompatProvidersResolveOnBothPaths(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestEndpointProviderNamesAreAllAccepted keeps the operator-facing list
|
||||
// honest. endpointProviderNames exists to stop two error messages drifting
|
||||
// apart, but nothing tied it to the switches it describes — and its first
|
||||
// version had already dropped the "gemini" alias, so the anti-drift list was
|
||||
// itself drifted. Every name it advertises must actually resolve.
|
||||
// honest: every name endpointProviderNames advertises must actually resolve.
|
||||
// The constant is read by somebody whose config just failed, so a name listed
|
||||
// there and rejected by the code sends them to debug a spelling that was never
|
||||
// going to work.
|
||||
func TestEndpointProviderNamesAreAllAccepted(t *testing.T) {
|
||||
for _, name := range strings.Split(endpointProviderNames, "/") {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Both switches, not one: this constant is the error text for BOTH
|
||||
// GADFLY_ENDPOINT_* and GADFLY_BASE_URL, so a name accepted by only
|
||||
// half of them still misleads whichever operator hits the other path.
|
||||
t.Run(name+" via GADFLY_ENDPOINT_*", func(t *testing.T) {
|
||||
if _, err := endpointProvider("ep", name+"|https://host.example/v1|sk-x"); err != nil {
|
||||
t.Errorf("endpointProviderNames advertises %q but endpointProvider rejects it: %v", name, err)
|
||||
}
|
||||
})
|
||||
t.Run(name+" via GADFLY_BASE_URL", func(t *testing.T) {
|
||||
t.Setenv("GADFLY_PROVIDER", name)
|
||||
t.Setenv("GADFLY_BASE_URL", "https://host.example/v1")
|
||||
t.Setenv("GADFLY_API_KEY", "sk-x")
|
||||
t.Setenv("GADFLY_MODEL", "some-model")
|
||||
if _, err := resolveModel(); err != nil {
|
||||
t.Errorf("endpointProviderNames advertises %q but resolveModel rejects it: %v", name, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user