Skip to content

refactor: run the Go 1.27 modernizer (go fix) across the repo - #448

Merged
bomly-guy merged 2 commits into
mainfrom
claude/modernize-go-fix
Sep 12, 2026
Merged

bomly-guy merged 2 commits into
mainfrom
claude/modernize-go-fix

Conversation

@bomly-guy

@bomly-guy bomly-guy commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

Phase 3 of the SDK maturity plan (dev-docs/SDK_MATURITY_PLAN.md) asks for a modernizer pass per repo, reviewed as its own PR. This is that pass for bomly-cli.

Tool

go fix from the Go 1.27.0 toolchain — i.e. cmd/fix, which since Go 1.26 carries the modernizer analyzers that previously lived in golang.org/x/tools/gopls/internal/analysis/modernize. There is no separate modernize binary to fetch on this toolchain; go tool fix help lists the 27 registered analyzers, and go fix -diff prints the patch without applying it.

Every category was reviewed in -diff mode first, per analyzer, before anything was applied. The final command was:

go fix -embedlit=false -omitzero=false -stringsbuilder=false ./...

What changed

88 files, no behavior changes. Sites rewritten per analyzer:

analyzer sites what it does
slicescontains 63 membership loop → slices.Contains / slices.ContainsFunc
minmax 29 if/else clamp → min() / max()
rangeint 28 3-clause counting loop → for range n
stringsseq 28 range strings.Split(...) → strings.SplitSeq
mapsloop 22 copy loop → maps.Copy
stringscutprefix 8 HasPrefix+TrimPrefix → CutPrefix / CutSuffix
stditerators 7 reflect NumField/Field(i) → Type.Fields()
stringscut 7 Index + slicing → strings.Cut
reflecttypefor 6 reflect.TypeOf(T{}) → reflect.TypeFor[T]()
errorsastype 4 errors.As → errors.AsType[T]
slicesbackward 4 reverse index loop → slices.Backward
waitgroupgo 3 Add(1)/go/Done() → wg.Go
forvar 2 drop redundant loop-variable re-declaration
any 1 interface{} → any

stringsseq is the one with a real runtime effect: it drops the intermediate slice allocation in the lockfile and build-tool output parsers, which run per line over untrusted input.

What was rejected

The three disabled analyzers, plus two individual hunks. Each declined hunk that go fix will offer again now carries a short comment at the call site saying so, so the next pass does not re-litigate it from scrollback.

  • embedlit (119 sites) — flattens Coordinates: sdk.Coordinates{...} into the surrounding literal at all 119 sites. Coordinates is a named identity concept (dev-docs/MODELS.md, ADR-0041); writing it explicitly is documentation, not redundancy. The rewrite also reflows multi-line literals badly (dangling }) and lost key alignment), and it would churn 49 files — 4 of them under internal/detectors/node/ — for no behavior or performance gain.
  • omitzero (2 sites) — drops omitempty from struct-valued JSON fields in internal/output/view.go and internal/benchmark/summary.go. The tool's own preferred fix (omitempty → omitzero) is a behavior change it declines to make; the fallback it does offer has no upside and moves a published output contract that docs/schemas/* and the goldens key on.
  • stringsbuilder (2 sites) — rewrites small bounded TUI concatenations into strings.Builder while still concatenating inside WriteString(" " + x). Readability loss, no allocation win.
  • maps.Copy in mergeBunDependencyMaps (internal/detectors/node/bun/bun_lockfile_parser.go) — the variadic parameter is named maps, so the fix imports the stdlib package as maps0. The explicit loop reads better than the alias.
  • max() in cycloneDXDocumentAssertions (internal/sbom/cyclonedx_assertions.go) — the rewrite hoists the ADR-0037 comment that explains the default into the middle of the max(...) call arguments.

Golden-backed areas

internal/sbom and internal/output were reviewed hunk by hunk. What landed there is slices.Contains, maps.Copy, and strings.SplitSeq in lookup/clone helpers — nothing that touches field ordering, tags, or emitted values. Confirmed by the suite and by make generate producing no drift.

Verification

make verify passes end to end: fmt-check, golangci-lint (0 issues), go vet on the default and lite build tags, go vet -tags smoke, both builds, go test ./..., and make generate with a clean git diff -- docs/.

Note for reviewers: overlap with the in-flight internal/detectors work

19 files under internal/detectors/ are touched here, in case a concurrent branch is routing purl-type literals through the SDK:

cargo/detector.go                 cargo/workspace.go
cocoapods/detector.go             conan/detector.go
gomod/detector.go                 gradle/detector.go
guards_test.go                    maven/detector.go
node/bun/bun_lockfile_parser.go   node/bun/bun_native_parser.go
node/lockfile_common.go           node/package_manager_warnings.go
node/pnpm/pnpm_lockfile_parser.go node/yarn/yarn_lockfile_parser.go
nuget/detector.go                 python/common.go
python/piplock_test.go            ruby/detector.go
sbt/sbt_native.go

All of it is line-local (slices.Contains, strings.Cut/CutPrefix, SplitSeq, maps.Copy, max()) inside parser and helper functions. Rejecting embedlit kept the sdk.Coordinates literals — the construct that branch is most likely editing — untouched.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Modernized internal processing, iteration, string handling, collection checks, map copying, reflection, and layout calculations using current Go standard-library features.
    • Parsing, validation, dependency analysis, reporting, and terminal interface behavior remain unchanged.
    • Some line-processing paths avoid unnecessary intermediate allocations.
  • Tests

    • Updated test utilities and loops to use modern Go syntax and standard-library helpers.
    • Existing assertions and coverage remain unchanged.

Phase 3 of the SDK maturity plan asks for a modernizer pass per repo,
reviewed as its own PR. This is that pass for bomly-cli.

Tool: `go fix` from the Go 1.27.0 toolchain (cmd/fix, which since Go 1.26
carries the modernizer analyzers that used to live in gopls). Run as
`go fix -embedlit=false -omitzero=false -stringsbuilder=false ./...` after
reviewing every category in `-diff` mode first.

Categories applied (sites rewritten per analyzer):

  63x slicescontains   loop -> slices.Contains / slices.ContainsFunc
  29x minmax           if/else clamp -> min() / max()
  28x rangeint         3-clause counting loop -> for range n
  28x stringsseq       range strings.Split -> strings.SplitSeq
  22x mapsloop         copy loop -> maps.Copy
   8x stringscutprefix HasPrefix+TrimPrefix -> CutPrefix / CutSuffix
   7x stditerators     reflect NumField/Field(i) -> Type.Fields()
   7x stringscut       Index + slicing -> strings.Cut
   6x reflecttypefor   reflect.TypeOf(T{}) -> reflect.TypeFor[T]()
   4x errorsastype     errors.As -> errors.AsType[T]
   4x slicesbackward   reverse index loop -> slices.Backward
   3x waitgroupgo      Add/go/Done -> wg.Go
   2x forvar           drop redundant loop-variable re-declaration
   1x any              interface{} -> any

Declined, with the reason recorded next to the code where it will be
offered again:

  - embedlit (119 sites): flattens `Coordinates: sdk.Coordinates{...}` into
    the surrounding literal. Coordinates is a named identity concept
    (dev-docs/MODELS.md, ADR-0041); the explicit wrapper is documentation,
    not redundancy, and the rewrite also reflows multi-line literals badly.
  - omitzero (2 sites): drops `omitempty` from struct-valued JSON fields in
    golden- and schema-backed output types. No behavior gain, and it moves
    a published output contract.
  - stringsbuilder (2 sites): rewrites small bounded concatenations in the
    TUI into strings.Builder while still concatenating inside WriteString.
  - maps.Copy in mergeBunDependencyMaps: the parameter is named `maps`, so
    the fix imports the package as `maps0`.
  - max() in cycloneDXDocumentAssertions: swallows the ADR-0037 comment
    that explains the default into the middle of the call.

No behavior changes. `make verify` passes (fmt, lint, vet on both build
variants, vet on the smoke tag, both builds, `go test ./...`, and
`make generate` with no docs drift).

Co-Authored-By: Claude Opus 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: f4babd8b-c056-415c-8947-b22ed5cb3488

📥 Commits

Reviewing files that changed from the base of the PR and between ad99084 and 73a015d.

📒 Files selected for processing (88)
  • cmd/bomly/release_licensing_test.go
  • internal/auditors/package/auditor.go
  • internal/baseline/baseline.go
  • internal/benchmark/run.go
  • internal/benchmark/summary.go
  • internal/cli/benchmark_run_test.go
  • internal/cli/opts/filters.go
  • internal/cli/opts/filters_test.go
  • internal/cli/opts/flag_options.go
  • internal/cli/opts/options_test.go
  • internal/cli/opts/planning.go
  • internal/cli/opts/selector.go
  • internal/cli/plugin_cmd.go
  • internal/cli/plugin_cmd_test.go
  • internal/cli/render/diff.go
  • internal/cli/render/logo.go
  • internal/cli/render/scan_warnings_test.go
  • internal/cli/render/scorecard_render_test.go
  • internal/cli/root_cmd_test.go
  • internal/config/load.go
  • internal/detectors/cargo/detector.go
  • internal/detectors/cargo/workspace.go
  • internal/detectors/cocoapods/detector.go
  • internal/detectors/conan/detector.go
  • internal/detectors/gomod/detector.go
  • internal/detectors/gradle/detector.go
  • internal/detectors/guards_test.go
  • internal/detectors/maven/detector.go
  • internal/detectors/node/bun/bun_lockfile_parser.go
  • internal/detectors/node/bun/bun_native_parser.go
  • internal/detectors/node/lockfile_common.go
  • internal/detectors/node/package_manager_warnings.go
  • internal/detectors/node/pnpm/pnpm_lockfile_parser.go
  • internal/detectors/node/yarn/yarn_lockfile_parser.go
  • internal/detectors/nuget/detector.go
  • internal/detectors/python/common.go
  • internal/detectors/python/piplock_test.go
  • internal/detectors/ruby/detector.go
  • internal/detectors/sbt/sbt_native.go
  • internal/engine/engine.go
  • internal/engine/pipeline.go
  • internal/engine/pipeline_explain.go
  • internal/engine/pipeline_helpers.go
  • internal/engine/pipeline_resolve.go
  • internal/git/git.go
  • internal/git/git_test.go
  • internal/logging/logger.go
  • internal/mcp/compact_diff_test.go
  • internal/mcp/compact_limits_test.go
  • internal/mcp/mcp_test.go
  • internal/mcp/remediation_test.go
  • internal/mcp/server.go
  • internal/output/hierarchy.go
  • internal/output/registry_lookup.go
  • internal/output/sarif.go
  • internal/output/types.go
  • internal/output/view.go
  • internal/progress/progress.go
  • internal/progress/progress_test.go
  • internal/registry/builder.go
  • internal/registry/discovery.go
  • internal/registry/discovery_test.go
  • internal/registry/support.go
  • internal/remediation/derive.go
  • internal/remediation/derive_test.go
  • internal/sbom/cyclonedx_assertions.go
  • internal/sbom/document_sources_test.go
  • internal/sbom/spdx23.go
  • internal/support/component_docs.go
  • internal/support/config_reference.go
  • internal/support/generate_test.go
  • internal/support/schema.go
  • internal/support/schema_docs.go
  • internal/support/schema_outputs.go
  • internal/support/support_matrix.go
  • internal/tools/benchmarkrun/main.go
  • internal/tools/publicevidence/main.go
  • internal/tools/sbomassurance/main.go
  • internal/tui/diff.go
  • internal/tui/diff_aggregations_test.go
  • internal/tui/posture.go
  • internal/tui/posture_diff.go
  • internal/tui/posture_test.go
  • internal/tui/scan.go
  • internal/tui/tui.go
  • internal/tui/tui_test.go
  • internal/tui/utils.go
  • test/assurance/execution_boundaries_test.go
💤 Files with no reviewable changes (2)
  • test/assurance/execution_boundaries_test.go
  • internal/cli/render/scorecard_render_test.go

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

This pull request modernizes Go code across detectors, pipelines, registries, output, TUI rendering, and tests. It adopts iterator APIs, collection helpers, reflection APIs, range-over-integer loops, and built-in bounds functions without intended behavior changes.

Changes

Go API modernization

Layer / File(s) Summary
String and collection API replacements
internal/detectors/..., internal/cli/..., internal/output/..., internal/registry/...
Replaces manual membership checks, map-copy loops, string splitting, prefix handling, and comment parsing with slices, maps, strings.SplitSeq, strings.Cut, and strings.CutPrefix.
Runtime, reflection, and error handling
internal/config/..., internal/support/..., internal/engine/..., internal/mcp/..., internal/progress/...
Uses reflect.TypeFor, reflect.VisibleFields, errors.AsType, WaitGroup.Go, slices.Backward, and built-in min/max expressions.
TUI and loop syntax modernization
internal/tui/..., internal/cli/render/..., internal/mcp/*_test.go, internal/progress/*_test.go
Rewrites rendering clamps and loop forms with equivalent range and bounds expressions. Tests remove obsolete loop-variable copies and use integer-range loops.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Refactor

Merge Risk: ⚪ Minimal · up to 73a01

The modernization changes preserve existing behavior across the reviewed parsing, rendering, collection, reflection, and concurrency paths, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 100 functions across 50 files. (36 skippe… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: applying the Go 1.27 modernizer across the repository.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 100 functions across 50 files. (36 skipped: 36 over the file limit.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/modernize-go-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Bomly Diff Summary

Compared ad99084f63cee5deab63a1edacfe8690e36e77a7 to 73a015d9b686a1a101a32972be28048cf43dff70.

Overview

Status Manifests Dependencies Findings Duration
✅ Pass +0 / ~0 / -0 0 added / 0 version changed / 0 detail changes / 0 removed 0 introduced / 0 persisted / 0 resolved 1m 22s

Dependency Changes

✅ No dependency changes.

Vulnerabilities

✅ No vulnerability changes.

License Changes

✅ No license changes.

Project Posture

✅ No project posture changes (--matchers +scorecard was not selected).

Policy Findings

✅ No policy differences were identified.

@bomly-guy
bomly-guy merged commit 7070b16 into main Sep 12, 2026
16 checks passed
@bomly-guy
bomly-guy deleted the claude/modernize-go-fix branch September 12, 2026 06:24
bomly-guy added a commit that referenced this pull request Sep 12, 2026
Codex checked the merged pass and found my note wrong. #448 ran
`go fix -embedlit=false -omitzero=false -stringsbuilder=false ./...` and
I recorded one of the three, so an agent following the note would have
reintroduced the other two -- a note that is worse than none, because it
looks authoritative.

omitzero is the one that matters. It drops omitempty from struct-valued
JSON fields in golden- and schema-backed output types. The bytes do not
move, so no test, linter or API gate objects -- and the tag is the
published schema, so a consumer generating one by reflection starts
reading the field as required. The SDK took that rewrite in its own pass
and spent four review rounds undoing the consequences. The same analyzer
was one command away from doing it here.

Also says what `go fix` does: it applies in place, and `-diff` prints
instead. A reader who assumed it previewed would find out afterwards.

Co-Authored-By: Claude Opus 5 <[email protected]>
bomly-guy added a commit that referenced this pull request Sep 12, 2026
…451)

* docs: record the declined modernizer analyzer in both guidance files

The Go 1.27 modernizer pass here rejected the embedlit analyzer; the
SDK's pass applied it. The same question went on record with two
different answers, and undoing it cost 43 hunks in the SDK.

Nothing mechanical objects to embedlit -- it is behaviour-identical and
gorelease is indifferent -- so the only thing that can keep the two
repositories agreeing is a written reason where the next run will look.
Running `go fix ./...` without the flag proposes all 119 hunks here
again, silently.

Both CLAUDE.md and AGENTS.md carry it, since they are maintained as
copies of each other, and bomly-sdk's AGENTS.md carries the matching
note.

[skip release]

Co-Authored-By: Claude Opus 5 <[email protected]>

* docs: record all three declined analyzers, not just the one

Codex checked the merged pass and found my note wrong. #448 ran
`go fix -embedlit=false -omitzero=false -stringsbuilder=false ./...` and
I recorded one of the three, so an agent following the note would have
reintroduced the other two -- a note that is worse than none, because it
looks authoritative.

omitzero is the one that matters. It drops omitempty from struct-valued
JSON fields in golden- and schema-backed output types. The bytes do not
move, so no test, linter or API gate objects -- and the tag is the
published schema, so a consumer generating one by reflection starts
reading the field as required. The SDK took that rewrite in its own pass
and spent four review rounds undoing the consequences. The same analyzer
was one command away from doing it here.

Also says what `go fix` does: it applies in place, and `-diff` prints
instead. A reader who assumed it previewed would find out afterwards.

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant