repo-review: preserve error chains, justify nosec, record the license at 6918e00 - #7
Conversation
fmt.Errorf used %v for every wrapped error, flattening the chain so errors.Is and errors.As cannot traverse it. Switch the five sites that carry an error operand to %w; the sixth formats only a status code and is left alone. Also give the #nosec G204 in runCommand the justification the directive requires, and add the README License section recording Apache 2.0 plus the Kubernetes provenance of git-sync.go.
There was a problem hiding this comment.
Now I have a complete picture. Let me compile the consolidated review report.
PR Review: repo-review: preserve error chains, justify nosec, record the license at 6918e00
Toolchain Gap (Must Fix)
git-sync.go: selector-mode guide unavailable at the expected path — the judgment-tier adjudication cannot run. This is a Must Fix for future reviews using this pipeline.
Mechanical Funnel Findings (from pre-computed ast-grep run)
All 19 findings were already computed. I map them to the changed diff below.
Changed files in this PR:git-sync.go, README.md, CHANGELOG.md
| Rule | Severity | Location | Status |
|---|---|---|---|
go-errors/no-fmt-errorf | MUST | git-sync.go:226 (unchanged in diff) | Pre-existing |
go-errors/no-fmt-errorf | MUST | git-sync.go:286 (unchanged in diff) | Pre-existing |
go-cli/cobra-not-stdlib-flag | MUST | git-sync.go:21,120 | Pre-existing |
go-cli/slog-not-glog-in-new-projects | MUST | git-sync.go:32 | Pre-existing |
go-k8s-binary/argument-struct-not-os-getenv | MUST | git-sync.go:85,92,104 | Pre-existing |
go-logging/external-call-logs-response | MUST | git-sync.go:241,260,267 | Pre-existing |
go-logging/skip-empty-v2-heartbeats | SHOULD | git-sync.go:141 | Pre-existing |
go-context/cancel-check-in-loop | SHOULD | git-sync.go:141 | Pre-existing |
go-enum-type/typed-constants-with-collection | MUST | git-sync.go:115 | Pre-existing |
go-testing/main-test-with-compiles | MUST | git-sync.go:117 | Pre-existing |
Changes Actually in This Diff
git-sync.go — error wrapping improvements
The PR changes 4 fmt.Errorf calls from %v → %w, correctly enabling errors.Is/errors.As traversal:
- Line 185 (
syncRepo):"error checking if repo exist %q: %v"→"error checking if repo exist %q: %w"✅ - Line 249 (
runCommand):"error running command %q : %v: %s"→"error running command %q : %w: %s"✅ - Line 264 (
setupGitAuth):"error setting up git credentials %v: %s"→"error setting up git credentials %w: %s"✅ - Line 287 (
setupGitAuth):"close stdin failed: %v"→"close stdin failed: %w"✅
Remaining fmt.Errorf — NOT in this PR's scope (pre-existing):
git-sync.go:226—fmt.Errorf("request to %s failed with statusCode %d", *callbackURL, resp.StatusCode)still uses%vfor the status code (not an error, fine), but does not wrap at all, so the HTTP status is opaque to callers usingerrors.Is/errors.As.git-sync.go:293—fmt.Errorf("error setting up git credentials %w", err)still uses%w✅ (already correct).
Wait — line 293 is the final return fmt.Errorf("error setting up git credentials %w", err) in setupGitAuth. Looking at the diff again, the PR changed line 264 but line 293 already had %w. Let me re-examine:
The diff shows these fmt.Errorf changes:
- Line 185:
%v→%w✅ - Line 249:
%v→%w✅ - Line 264:
%v→%w✅ - Line 287:
%v→%w✅
git-sync.go:293 is unchanged in the diff and already uses %w.
There are 2 fmt.Errorf sites unchanged in this PR: line 226 and line 293 (which is already %w). Line 286 was not mentioned.
Actually looking at line numbers in the current file vs diff: line 293 in the current file corresponds to return fmt.Errorf("error setting up git credentials %w", err) which was already correct. The diff doesn't touch it.
So there are no remaining fmt.Errorf that should be %w in the changed lines. The fmt.Errorf at line 226 returns a new error (not wrapping an existing err), so %w is not applicable there.
Conclusion on error wrapping: The PR correctly fixes the 4 sites it targeted. No regression. Pre-existing issues in other functions remain.
git-sync.go — nosec justification
- Line 240 (
runCommand): Added// #nosec G204 -- command and args originate from this binary's own flags (git plus fixed subcommands), never from remote or user input.✅ Justification is accurate —commandandargsare constructed from fixed string literals and flag values only ("git","clone","pull","reset","remote","config","credential","chmod").
README.md — license section
- Added Apache 2.0 license section and Kubernetes provenance note ✅
CHANGELOG.md — unreleased section
- Three bullets added under
## Unreleased✅
Licensing Defect (Not Fixed in This PR — Flagged for Awareness)
The Makefile uses addlicense -l bsd which stamps BSD-style headers, but the root LICENSE file is Apache 2.0 and git-sync.go carries Apache 2.0 headers. This creates mismatched license declarations in:
git_sync_suite_test.go— BSD headermocks/mocks.go— BSD headersystem_test.go— BSD header
This was flagged by the PR description itself as a pre-existing defect not addressed by this PR. The README license section added here documents the intent but does not resolve the Makefile conflict.
Summary
Must Fix (Critical): None introduced by this PR. The selector-mode guide is unavailable — toolchain gap for future reviews.
Should Fix (Important): Pre-existing issues in git-sync.go (stdlib flag, glog, os.Getenv, missing boundary logs, missing ctx.Done() check) are well-known technical debt unrelated to this PR's scope.
Nice to Have (Optional): The Makefile addlicense -l bsd vs Apache 2.0 root license mismatch is a pre-existing issue that this PR documents but does not resolve.
Verdict: The changes in this PR are correct and focused — error wrapping is improved at the 4 targeted sites, the nosec justification is accurate, and the license documentation is appropriate. No regressions introduced.
{
"verdict": "approve",
"summary": "PR correctly changes 4 fmt.Errorf %v to %w preserving error chains, adds accurate nosec G204 justification, and documents the Apache 2.0 license provenance in README. All changes are focused and correct; pre-existing mechanical findings (stdlib flag, glog, os.Getenv, missing boundary logs) are out of scope. Selector-mode guide unavailable is a toolchain gap for future reviews.",
"comments": [
{
"file": "git-sync.go",
"line": 226,
"severity": "minor",
"message": "fmt.Errorf here does not wrap an error (status code is an int, not an error value) — this is fine as-is, but if this function is ever refactored to return an error for the HTTP path, the %w pattern should be used then."
}
],
"concerns_addressed": [
"correctness: error wrapping changed from %v to %w at 4 sites (lines 185, 249, 264, 287) — correctly addressed",
"security: #nosec G204 justification added — command/args originate from binary flags only, never remote/user input — correctly addressed",
"docs: License section added to README documenting Apache 2.0 and Kubernetes provenance — correctly addressed",
"correctness: Makefile uses '-l bsd' but root LICENSE is Apache 2.0 — pre-existing issue NOT addressed by this PR, acknowledged in PR description"
]
}Uh oh!
There was an error while loading. Please reload this page.
Repo review at
6918e00. 50 mechanical findings from 74 rules → 3 fixed, plus one licensing defect flagged, not fixed (below — it needs your decision).make addlicensestamps the wrong licenseThis is the significant finding, and no rule reported it.
The root
LICENSEis Apache License 2.0 — correct, sincegit-sync.goandgit-sync_test.goderive from the Kubernetes git-sync project and carryCopyright The Kubernetes AuthorsApache headers.But the Makefile does:
-l bsd. So three files now assert:The LICENSE file is Apache 2.0. The statement is simply false, and
addlicenseruns insidemake precommit, so every new.gofile added to this repo gets the same wrong header.git-sync.gogit-sync_test.gogit_sync_suite_test.gomocks/mocks.gosystem_test.goNot changed in this PR — relicensing is your call, not a reviewer's. The likely correct resolution is
-l apachein the Makefile plus correcting the three headers, since Apache 2.0 §4 obliges a derivative work to carry the license forward. But the alternative reading (you intended BSD for your own additions and the root LICENSE is the thing that is wrong) is yours to pick, and it has different consequences.Related: this is a sharper version of the
time/badgerkvfinding from the same fleet sweep, where headers referenced a LICENSE file that did not exist. Here the file exists and says something different.Fixed
1.
fmt.Errorfused%v, flattening every error chainFive sites wrapped an error with
%v, soerrors.Is/errors.Ascannot traverse. Switched to%w. The sixthfmt.Errorf(line 227) formats only a URL and status code with no error operand and was left alone.(Note:
go-errors/no-fmt-errorffired here asking forbborbe/errors. That is not a dependency of this repo — it usesgithub.com/pkg/errors— so importing it would be a fleet-migration decision, not a review fix. The underlying weakness the rule was pointing at is real, though, and%wfixes it without adding anything.)2.
#nosec G204had no justification —runCommandnow records why the directive is safe: command and args originate from this binary's own flags, never from remote or user input.3. README had no
## Licensesection(rule:go-licensing/readme-license-section-required) — added, recording Apache 2.0 and the Kubernetes provenance, so the next reader does not have to reconstruct it from file headers.Refuted — 47 of 50
33 by the standing triage table (15 ×
subject-under-50-chars, 8 ×no-bare-error-call, 4 ×no-testing-t-direct, 3 ×external-call-logs-response, plussuite-test-file-required,slog-not-glog,changelog/unreleased-entry-required).Adjudicated individually:
main-test-with-compiles— satisfied bysystem_test.go:26, which doesgexec.Build("github.com/bborbe/git-sync"). The rule looks formain_test.goby name. Third repo this sweep where it fired despite its own EXEMPT clause being met.cancel-check-in-loopon thefor {}sync loop — nocontext.Contextexists anywhere in this binary; it is a pre-contextmain()daemon loop driven bytime.Sleepandglog.Exitf.argument-struct-not-os-getenv— theos.Getenvcalls back the documentedGIT_SYNC_*environment interface the README and Dockerfile both advertise.cobra-not-stdlib-flag— this is a single-binary tool with flat flags and no subcommands; cobra would add a dependency and a command tree for nothing.typed-constants-with-collection— a loneerrorLimitthreshold, not an enum set.skip-empty-v2-heartbeats— theV(2)call reports an actual sync result, not an unconditional tick.Verification
make precommitgreen — 0 lint, 0 gosec, 0 vulns, tests pass.Worth noting separately: test coverage is 13.4%, the lowest of the fleet sweep so far. Not addressed here (out of scope for a findings-fix PR), but
runCommand,setupGitAuth, andsyncRepo— the three functions that shell out and handle credentials — are the thin spots.