fix(cli): warn when ZERO_PROVIDER overrides a providers-use selection (#721)#767
Conversation
Closes #721. `zero providers use <name>` wrote activeProvider to config.json and reported "Active provider set to <name>" even when ZERO_PROVIDER was set, which applyEnv makes win over config.json unconditionally. So the switch was reported as a success while `providers current` still showed the env-pinned provider, a silent no-op. Detect the override and say so: the text path prints a note to stderr naming the env var and the provider that stays active, and the --json path adds effectiveProvider / overriddenByEnv fields so a machine consumer is not misled either. The config write still lands unchanged; only the reporting is honest. The env read is injected via a new appDeps.getenv (os.Getenv in production, left nil by fillAppDeps like exportActiveProvider) so tests stay hermetic against an ambient ZERO_PROVIDER. The related env-derived-profile "not found" case the issue also mentions is already handled by the unpersisted-provider path (#707) and #716.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
Walkthrough
ChangesProvider override reporting
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant runProvidersUse
participant ZERO_PROVIDER
participant config.json
CLI->>runProvidersUse: select provider
runProvidersUse->>config.json: save selected provider
runProvidersUse->>ZERO_PROVIDER: read environment override
ZERO_PROVIDER-->>runProvidersUse: effective provider
runProvidersUse-->>CLI: success output with override details
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Zero automated PR reviewVerdict: No blockers found Blockers
Validation
ScopeHead: This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality. |
gnanam1990
left a comment
There was a problem hiding this comment.
APPROVE. The premise checks out, the dependency plumbing follows the file's existing convention, and both output modes are covered.
The load-bearing claim is correct
The warning is only worth printing if ZERO_PROVIDER really does beat the config write. It does, unconditionally — applyEnv at internal/config/resolver.go:560-563:
activeProvider := strings.TrimSpace(envValue(env, ActiveProviderEnv))
if activeProvider != "" {
cfg.ActiveProvider = activeProvider
}No precedence check, no merge — the env value simply replaces whatever providers use just persisted. So reporting the write as a bare success genuinely does read as a switch that silently has no effect, which is #721.
strings.EqualFold for the "is it actually different" test is the right comparison rather than a stricter one: provider-name resolution is case-insensitive elsewhere in the resolver (resolver.go:467), so ZERO_PROVIDER=OpenAI against a saved openai resolves to the same profile and correctly produces no warning. A case-sensitive check would have emitted a spurious warning there.
The dependency-injection shape is right
getenv follows the same pattern as the existing exportActiveProvider directly above it — set in defaultAppDeps(), deliberately not filled by fillAppDeps. I checked both ends of that:
- Production is fine:
Run()(app.go:116) passesdefaultAppDeps(), which setsgetenv: os.Getenv, so the warning actually fires for real users. Worth confirming explicitly, since a dep thatfillAppDepsskips could easily have ended up nil on the live path and made this a silent no-op. - Tests stay hermetic:
fillAppDepsleaves it nil, andactiveProviderEnvOverridereturns""for a nil getenv, so an ambientZERO_PROVIDERcannot make the new warning appear in unrelated tests.
Handling the JSON path separately with effectiveProvider / overriddenByEnv rather than only writing to stderr is the right call — a script consuming --json would otherwise have no way to see the override.
Unrelated observation, since it's adjacent to this PR's subject
internal/cli is not hermetic against an ambient ZERO_PROVIDER today, through a different path than the one this PR adds. With ZERO_PROVIDER=leaky exported, TestRunProvidersUseEnvDerivedJSONIncludesConfigPath fails on current main:
exit code = 3, want 0: [zero] no active provider configured: active provider "leaky" not found
That comes from resolveConfig reading the real process environment, not from anything here — I verified it reproduces identically on upstream/main without this branch. Flagging it only because you're the person currently in this code: since zero itself exports ZERO_PROVIDER via SetActiveProviderEnv, a developer can plausibly end up with it set and then see a confusing local test failure. A t.Setenv(config.ActiveProviderEnv, "") in the affected tests would close it. Separate PR, not this one.
Verification
gofmt and go vet ./internal/cli/ clean. All three new tests pass, including the two-case NoWarnWithoutEnvOverride (env unset / env matches). Full internal/cli suite compared against current main: identical five failures on both (TestExecScopeReRegistration…, TestRunAddDirDispatch…, both TestRunDoctor…, TestRunSandboxCheckJSON…) — all sandbox/network artifacts of my environment, and this PR introduces none.
Merge is kevin's call per the program gate.
|
@coderabbitai review |
✅ Action performedReview finished.
|
anandh8x
left a comment
There was a problem hiding this comment.
Approved on the latest head. providers use now reports a conflicting ZERO_PROVIDER override in both text and JSON output while preserving the saved selection. Focused tests and all checks pass.
Closes #721.
The bug
zero providers use <name>writesactiveProviderto config.json and reportsActive provider set to <name>, even whenZERO_PROVIDERis set.applyEnv(resolver.go) makesZERO_PROVIDERwin over config.json unconditionally, so the reported switch is a silent no-op:providers currentstill shows the env-pinned provider.The fix
Detect when
ZERO_PROVIDERis set and names a different provider than the selection, and say so:unset ZERO_PROVIDER).effectiveProviderandoverriddenByEnvso a machine consumer is not misled either.The config write is unchanged and still lands; only the reporting becomes honest. No warning when
ZERO_PROVIDERis unset or already names the selection.The env read is injected via a new
appDeps.getenv(os.Getenvin production, deliberately left nil byfillAppDepslikeexportActiveProvider) so the existing tests stay hermetic against a developer's ambientZERO_PROVIDER(the reporter's setup) rather than newly failing on it.Scope note
The issue also mentions
providers use openaifailing with "not found" for an env-derived profile. That path is already handled by the unpersisted-provider reporting (#707) and the env-derived-profile fix in #716, so this PR closes the remaining gap: the silent override on a successful switch.Tests: text warning, JSON override fields, and the two no-warn cases (unset / matches). Package builds and the provider-use suite is green. (Pre-existing, unrelated
TestBuildServeScopeKeepsLexicalPathsfails on main independently of this change.)Summary by CodeRabbit
New Features
ZERO_PROVIDERenvironment variable.Bug Fixes