Skip to content

fix(lint): properly handle all errcheck violations from golangci-lint v2 - #1265

Closed
cristim wants to merge 11 commits into
mainfrom
fix/main-errcheck-lint
Closed

cristim wants to merge 11 commits into
mainfrom
fix/main-errcheck-lint

Conversation

@cristim

@cristim cristim commented Jun 19, 2026 •

Copy link
Copy Markdown
Member

Problem

golangci-lint v2 activated errcheck.check-blank: true which exposed 107 pre-existing blank-identifier error discards (_ = someCall()). This caused main CI to fail on every lint run.

Approach

The owner decided to properly handle each error, not flip check-blank to false.

Config changes (.golangci.yml)

Three path exclusions for generated/test-helper code where the pattern is unavoidable:

  • internal/mocks/ - testify mock args.Get(0).(*Type) type assertions
  • internal/auth/test_helpers.go
  • internal/database/postgres/testhelpers/

Production code changes

Each of the 16 remaining sites is handled appropriately:

  • Money path (internal/purchase/execution.go): SavePurchaseExecution errors are now logged as Errorf (AUDIT LOSS level); early-exit save extracted to saveExecWithLog helper
  • Scheduler (internal/scheduler/scheduler.go): g.Wait() and GetRecommendations errors are now checked and logged
  • HTTP response writer (internal/server/http.go): w.Write error logged
  • Deferred rollbacks (cmd/cleanup-lambda/main.go, cmd/rekey/main.go, internal/commitmentopts/store_postgres.go): proper rollback error handling with errors.Is(pgx.ErrTxClosed) guard
  • API handler (internal/api/handler.go, various): buildResponse errors propagated or logged; resolveAWSCallerIdentity annotated with //nolint:errcheck (best-effort, STS failure = empty AccountID = security gate)
  • Auth/MFA (internal/auth/service_mfa.go, internal/auth/service_apikeys.go): update errors logged; singleflight .Do annotated with justification
  • Credentials (internal/credentials/cipher.go): DevKey panics on invalid compile-time constant (programming error)
  • Config interactivity (cmd/configure_azure.go, cmd/configure_gcp.go): bufio.Reader.ReadString errors handled via readLine helper; subcommands extracted to keep gocyclo within gate

Complexity helpers extracted (to keep gocyclo gate green)

  • readLine(reader) - bufio.ReadString + io.EOF handling (configure_azure.go)
  • getGCPProjectID(reader) - read + validate GCP project ID (configure_gcp.go)
  • createAzureServicePrincipal(reader, subscriptionID) - SP creation block (configure_azure.go)
  • payloadTypeMatches(payload, want) - type-safe payload type check (internal/api/validation.go)
  • logMigrateVersion(m, steps) - pre-rollback version logging (migrate.go)
  • saveExecWithLog(ctx, exec, accountID) - best-effort save in early-exit path (execution.go)

Test plan

  • go build ./... passes
  • golangci-lint run --enable errcheck reports 0 errcheck violations
  • gocyclo -over 10 $(git ls-files "*.go" | grep -v _test.go | grep -v vendor/) reports 0 violations
  • All pre-commit hooks pass
  • go test ./... passes

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved API request/validation handling so response-construction errors are no longer silently ignored.
    • Enhanced rollback error handling with warnings/logging instead of discarding failures.
    • Azure recommendations now return an error when all attempted services fail (instead of appearing successful).
  • Improvements

    • More reliable interactive CLI prompts for Azure/GCP, including correct EOF behavior and stricter handling of “run/skip/continue anyway” inputs.
    • GCP workload identity resolution now surfaces storage/load errors instead of falling back.
  • Tests

    • Added regression coverage for EOF handling and Azure recommendation merge/all-fail behavior.

golangci-lint v2 activated errcheck.check-blank:true which exposed 107
pre-existing blank-identifier error discards. This commit handles each
violation properly:

- Exclude generated mock/test-helper paths from errcheck in .golangci.yml
  (internal/mocks/, internal/auth/test_helpers.go,
   internal/database/postgres/testhelpers/)
- Handle errors in production code with log/return as appropriate
- Use //nolint:errcheck with specific justification only for documented
  fire-and-forget cases (singleflight, best-effort STS, w.Write)
- Extract readLine helper (configure_azure.go) and getGCPProjectID,
  logMigrateVersion, createAzureServicePrincipal, payloadTypeMatches
  helpers to keep cyclomatic complexity within the gocyclo <=10 gate
- Save purchase execution errors on money path are now logged as Errorf
- errgroup.Wait + ctx.Err() checked in scheduler fan-out loops
@cristim cristim added triaged Item has been triaged priority/p1 Next up; this sprint type/bug Defect labels Jun 19, 2026
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 051f3f1c-151d-4d96-8f78-3f7011bb5ba4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR updates CLI prompting, API handler and server contracts, transaction cleanup, credential resolution, purchase and scheduler pointer flow, Azure recommendation merging, and mock return handling.

Changes

Primary functional and wiring changes

Layer / File(s) Summary
CLI input handling
cmd/configure_azure.go, cmd/configure_gcp.go, cmd/configure_test.go, cmd/lambda/main_test.go, cmd/rekey/main.go
Azure and GCP setup commands use EOF-aware line reading, propagate stdin read failures, and adjust interactive command execution, with matching tests and command callers updated.
Storage, migrations, and credential guards
cmd/cleanup-lambda/main.go, internal/auth/..., internal/commitmentopts/..., internal/config/..., internal/credentials/..., internal/database/..., internal/mocks/secretsmanager.go, internal/database/postgres/testhelpers/postgres.go
Cleanup, auth, credential, and Postgres helpers now inspect rollback and lookup errors explicitly, handle wrapped sentinels, and guard mock return types.
API package rename and core handler contracts
internal/api/analytics_postgres.go, internal/api/handler.go, internal/api/handler_accounts.go, internal/api/exchange_lookup.go, internal/api/router.go, internal/api/types.go, internal/server/...
The HTTP API moves to apihttp, core handler construction switches to pointer configs, and shared router, interface, and server wiring update to the new contracts.
API feature handlers and routing
internal/api/handler_dashboard.go, internal/api/handler_inventory.go, internal/api/handler_recommendations.go, internal/api/handler_recommendations_refresh.go, internal/api/handler_registrations.go, internal/api/ri_utilization_cache.go, internal/api/validation.go, internal/api/..._test.go
Dashboard, inventory, recommendations, registrations, RI cache, validation, exchange lookup, and remaining handlers switch package names and pointer-based request/filter contracts, while related tests follow the new call shapes.
Purchase execution and command callers
internal/purchase/..., cmd/lambda/main_test.go, internal/server/app.go, internal/server/http.go
Purchase execution, provider resolution, and confirmation building switch to pointer-based accounts and recommendation records, with callers and tests updated to the new return shape and logging behavior.
Scheduler collection and server wiring
internal/scheduler/..., internal/server/interfaces.go, internal/testutil/mocks.go
The scheduler moves to pointer-based config and filter contracts, aggregates provider collection through structured results, updates fan-out callbacks, and aligns server construction and tests with the new interfaces.
Azure recommendation merge guard
providers/azure/recommendations.go, providers/azure/recommendations_test.go
Azure recommendation collection precomputes service inclusion, tracks attempted services, propagates context cancellation, and returns an error when every attempted service fails.
Shared mocks and test doubles
internal/auth/test_helpers.go, internal/email/..., internal/mocks/..., internal/config/store_postgres_recommendations_test.go, internal/scheduler/scheduler_*_test.go
Auth helpers, email clients, mock stores, and recommendation-store test doubles switch to guarded type assertions, pointer filters, and updated mock types.
Remaining package namespace updates
internal/api/..., internal/server/..., internal/credentials/resolver_extra_test.go
The remaining files switch from api to apihttp, with matching tests and wrappers updated to compile under the new package name.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • LeanerCloud/CUDly#79: Both PRs change internal/api/exchange_lookup.go’s recommendation-filter contract to use a pointer.
  • LeanerCloud/CUDly#200: Both PRs touch internal/api/handler_dashboard.go’s coverage aggregation and summary flow.
  • LeanerCloud/CUDly#269: Both PRs modify internal/scheduler/scheduler.go collection fan-out and deterministic merge behavior.

Poem

🐇 I hopped through logs from dawn till night,
and caught the errors left out of sight.
Rollbacks now squeak when things go wrong,
while pointer trails keep flows right along.
Azure and mocks both learned to sing —
a tidy hop, a safer spring.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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 summarizes the main change: fixing errcheck violations surfaced by golangci-lint v2.
Docstring Coverage ✅ Passed Docstring coverage is 80.52% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/main-errcheck-lint

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

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
cmd/configure_gcp.go (1)

388-389: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid re-creating stdin readers inside command execution.

Line [415] creates a fresh bufio.Reader(os.Stdin) rather than reusing the caller’s reader. In piped/scripted flows this can lose buffered bytes and stall follow-up prompts.

Suggested fix
 func promptAndRunGCPCommand(reader *bufio.Reader, name, displayCmd string, program string, args ...string) error {
@@
 	switch choice {
 	case "r", "run", "":
-		return executeGCPCommand(displayCmd, program, args...)
+		return executeGCPCommand(reader, displayCmd, program, args...)
@@
-func executeGCPCommand(displayCmd string, program string, args ...string) error {
+func executeGCPCommand(reader *bufio.Reader, displayCmd string, program string, args ...string) error {
@@
 	if err != nil {
 		fmt.Printf("Command failed: %v\n", err)
 		fmt.Print("Continue anyway? [y/N]: ")
-		reader := bufio.NewReader(os.Stdin)
 		response, rfErr := readLine(reader)

Also applies to: 415-418

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/configure_gcp.go` around lines 388 - 389, The code is creating a fresh
bufio.Reader from os.Stdin instead of reusing the reader parameter passed to the
function, which causes loss of buffered bytes in piped/scripted flows. Replace
the bufio.Reader(os.Stdin) instantiation (around line 415) with the existing
reader parameter that was passed to the current function, ensuring the same
reader instance is reused throughout the command execution flow to preserve any
buffered input.
cmd/configure_azure.go (1)

386-387: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reuse the same buffered reader for follow-up prompts.

Line [413] creates a new bufio.Reader(os.Stdin) inside executeExplicitCommand. If input is piped and the caller’s reader already buffered bytes, this can drop buffered input and block/hang on retries.

Suggested fix
 func promptAndRunExplicitCommand(reader *bufio.Reader, name, displayCmd string, program string, args ...string) error {
@@
 	switch choice {
 	case "r", "run", "":
-		return executeExplicitCommand(displayCmd, program, args...)
+		return executeExplicitCommand(reader, displayCmd, program, args...)
@@
-func executeExplicitCommand(displayCmd string, program string, args ...string) error {
+func executeExplicitCommand(reader *bufio.Reader, displayCmd string, program string, args ...string) error {
@@
 	if err != nil {
 		fmt.Printf("Command failed: %v\n", err)
 		fmt.Print("Continue anyway? [y/N]: ")
-		reader := bufio.NewReader(os.Stdin)
 		response, readErr := readLine(reader)

Also applies to: 413-417

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/configure_azure.go` around lines 386 - 387, The executeExplicitCommand
function creates a new bufio.Reader(os.Stdin) at lines 413-417, which drops any
buffered bytes from the caller's existing reader and causes input loss when
piped. Modify executeExplicitCommand to accept a *bufio.Reader parameter instead
of creating a new one internally. Then update the function call at lines 386-387
(and any other calls to executeExplicitCommand) to pass the caller's existing
buffered reader as an argument, ensuring the same reader instance is reused for
all prompts and input operations.
providers/azure/recommendations.go (1)

167-167: ⚠️ Potential issue | 🟠 Major

Handle g.Wait() error instead of blank-discarding it.

With check-blank: true enabled in .golangci.yml, blank-discarding the error from g.Wait() at line 167 violates lint rules and masks potential failures. Add proper error handling:

Proposed fix
-	_ = g.Wait()
+	if err := g.Wait(); err != nil {
+		return nil, err
+	}
 	if err := ctx.Err(); err != nil {
 		return nil, err
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@providers/azure/recommendations.go` at line 167, The g.Wait() error is being
blank-discarded with the underscore assignment which violates the golangci-yml
check-blank rule and hides potential failures. Replace the `_ = g.Wait()`
statement with proper error handling by capturing the returned error, checking
if it is not nil, and then either returning the error to the caller, logging it
appropriately, or handling it based on the context of the recommendations.go
function logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/configure_azure.go`:
- Around line 261-266: In the readLine function, the issue is that when io.EOF
is encountered (indicating closed or non-interactive stdin), the error is being
silently converted to nil even when no data was read. This allows empty input
from closed stdin to be treated as a valid empty choice. Fix this by checking
whether the trimmed line is actually empty when io.EOF is encountered. If the
line is empty and we received an EOF error, return the EOF error instead of nil
to properly indicate that no valid input was provided. Only convert EOF to nil
success when actual data was successfully read before the EOF.

In `@internal/credentials/resolver.go`:
- Around line 411-416: The current code treats all LoadRaw errors uniformly by
setting raw to nil, but according to the CredentialStore interface, only
not-found cases should fall back to the federated path while other errors
represent real store failures that need to be surfaced. Modify the error
handling for the store.LoadRaw call to use errors.Is(loadErr, pgx.ErrNoRows) to
specifically check if the error is a not-found error, and only set raw = nil in
that case. For other non-nil errors, propagate or return the error instead of
silently collapsing it to nil.

In `@internal/scheduler/scheduler.go`:
- Around line 352-355: In the collectAllProviders function, the error returned
from g.Wait() is being logged but not propagated, which allows the function to
continue and return success even when goroutines have failed. Instead of only
logging the waitErr, check if waitErr is not nil and return it immediately to
ensure that any goroutine errors are properly surfaced to the caller and prevent
partial/incorrect results from being returned.

---

Outside diff comments:
In `@cmd/configure_azure.go`:
- Around line 386-387: The executeExplicitCommand function creates a new
bufio.Reader(os.Stdin) at lines 413-417, which drops any buffered bytes from the
caller's existing reader and causes input loss when piped. Modify
executeExplicitCommand to accept a *bufio.Reader parameter instead of creating a
new one internally. Then update the function call at lines 386-387 (and any
other calls to executeExplicitCommand) to pass the caller's existing buffered
reader as an argument, ensuring the same reader instance is reused for all
prompts and input operations.

In `@cmd/configure_gcp.go`:
- Around line 388-389: The code is creating a fresh bufio.Reader from os.Stdin
instead of reusing the reader parameter passed to the function, which causes
loss of buffered bytes in piped/scripted flows. Replace the
bufio.Reader(os.Stdin) instantiation (around line 415) with the existing reader
parameter that was passed to the current function, ensuring the same reader
instance is reused throughout the command execution flow to preserve any
buffered input.

In `@providers/azure/recommendations.go`:
- Line 167: The g.Wait() error is being blank-discarded with the underscore
assignment which violates the golangci-yml check-blank rule and hides potential
failures. Replace the `_ = g.Wait()` statement with proper error handling by
capturing the returned error, checking if it is not nil, and then either
returning the error to the caller, logging it appropriately, or handling it
based on the context of the recommendations.go function logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3a5dd19b-1687-434c-a0a1-ceb2589cf9eb

📥 Commits

Reviewing files that changed from the base of the PR and between 451a70f and 2b21899.

📒 Files selected for processing (24)
  • .golangci.yml
  • cmd/cleanup-lambda/main.go
  • cmd/configure_azure.go
  • cmd/configure_gcp.go
  • cmd/rekey/main.go
  • internal/api/handler.go
  • internal/api/handler_accounts.go
  • internal/api/handler_dashboard.go
  • internal/api/handler_recommendations_refresh.go
  • internal/api/handler_registrations.go
  • internal/api/ri_utilization_cache.go
  • internal/api/validation.go
  • internal/auth/service_apikeys.go
  • internal/auth/service_mfa.go
  • internal/commitmentopts/store_postgres.go
  • internal/credentials/cipher.go
  • internal/credentials/resolver.go
  • internal/database/connection.go
  • internal/database/postgres/migrations/migrate.go
  • internal/purchase/execution.go
  • internal/scheduler/scheduler.go
  • internal/server/http.go
  • providers/azure/recommendations.go
  • providers/azure/recommendations_test.go

Comment thread cmd/configure_azure.go Outdated
Comment thread internal/credentials/resolver.go
Comment thread internal/scheduler/scheduler.go
Three Major CR findings on the errcheck changes, each fixed at root cause
rather than silencing the linter:

- cmd/configure_azure.go: readLine treated a zero-byte io.EOF as a
  successful empty line, so a closed/non-interactive stdin looked like an
  empty choice and fell through to the default Run in prompt handlers. Now
  EOF is success only when bytes were actually read (final line without a
  trailing newline); an EOF with no data surfaces the error. Uses the raw
  read buffer (not the trimmed text) so a bare newline still counts as a
  valid empty line rather than a closed stream.

- internal/credentials/resolver.go: resolveGCPWIFCredential swallowed every
  LoadRaw error by setting raw=nil and falling back to the federated path.
  LoadRaw already maps not-found to (nil, nil), so any non-nil error is a
  real store failure (DB connectivity, decrypt) that must be surfaced.
  Matches the established pattern in resolveAccessKeyProvider.

- internal/scheduler/scheduler.go: collectAllProviders logged g.Wait()'s
  error but still merged outcomes, risking a partial aggregate reported as
  success. It now returns the error. fanOutPerAccount has no error return,
  so an unexpected g.Wait() error is reflected into the accountOutcome
  (FailedCount/LastErr) instead of being silently swallowed.

Regression tests added: TestReadLine_EOFHandling (table-driven, covers the
closed-stream-surfaces-EOF and bare-newline cases) and
TestResolveGCPWIF_LoadRawError_Surfaces (wires the full federated path so
the pre-fix code would mask the store error). Both fail on the pre-fix code
and pass after.
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Per owner directive: no //nolint:errcheck and no errcheck path-exclusions
in .golangci.yml. Every errcheck violation is now handled in code.

Inline //nolint:errcheck removed (8 sites):

- internal/config/store_postgres.go (DeleteCloudAccount, SetPlanAccounts):
  deferred tx.Rollback now runs in a closure that logs a real rollback
  failure and ignores only the already-committed case (errors.Is
  pgx.ErrTxClosed).
- internal/config/store_postgres_recommendations.go (ReplaceRecommendations,
  UpsertRecommendations): same deferred-rollback closure pattern.
- internal/api/ri_utilization_cache.go: singleflight callback now returns
  nil after logging its own fetch error (no double-log); the outer Do result
  is captured and logged defensively instead of blank-discarded.
- internal/auth/service_apikeys.go: same singleflight treatment for the
  fire-and-forget UpdateLastUsed write.
- internal/api/handler.go (resolveSourceIdentity): the
  resolveAWSCallerIdentity error is captured and logged; AccountID is left
  empty so the consumer's empty-string check stays the security gate.
- internal/api/handler_dashboard.go (getDeploymentInfo): the
  resolveAWSAccountID error is captured and logged; empty-string-safe
  downstream behavior is unchanged.

.golangci.yml errcheck path-exclusions removed (internal/mocks/,
internal/auth/test_helpers.go, internal/database/postgres/testhelpers/),
and the violations in those files are hand-handled instead:

- 67 testify-mock type assertions converted from the single-value
  args.Get(0).(T) form to a checked two-value form: val, ok :=
  args.Get(0).(T); if !ok { return <zero>, args.Error(1) }. This satisfies
  errcheck check-type-assertions without suppression and handles a
  wrong-typed value a test might register.
- internal/database/postgres/testhelpers/postgres.go: the
  postgresContainer.Terminate cleanup error on the connection-failure path
  is now checked and logged via t.Logf.

Verified with golangci-lint v2.10.1 (the CI-pinned version): zero errcheck
issues, no //nolint:errcheck anywhere, no errcheck path-exclusions in
.golangci.yml (now identical to origin/main). go build ./... and
go test ./... pass (the one pre-existing Azure scheduler mock failure is
unrelated). The three earlier CR-finding fixes are preserved.
@cristim

cristim commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim cristim added severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/internal Team-internal only effort/l Weeks labels Jun 24, 2026
Raw ==/!= comparisons against sentinel errors fail on wrapped errors.
Convert every such comparison in the files this PR touches to errors.Is.

internal/config/store_postgres.go (7 sites):
- lines 89, 263, 2271, 2559, 2609, 2827, 2881:
  err == pgx.ErrNoRows -> errors.Is(err, pgx.ErrNoRows)

internal/database/postgres/migrations/migrate.go (7 sites):
- lines 45, 450, 485: err != migrate.ErrNoChange
  -> !errors.Is(err, migrate.ErrNoChange)
- lines 51, 455, 518: err != migrate.ErrNilVersion
  -> !errors.Is(err, migrate.ErrNilVersion)
- line 376: err == migrate.ErrNilVersion
  -> errors.Is(err, migrate.ErrNilVersion)

errors is already imported in both files. nil comparisons (err != nil)
and non-error-sentinel comparisons are left unchanged.

Verified with golangci-lint v2.10.1: zero errorlint issues in the changed
files, no new issues introduced, .golangci.yml unchanged, no errcheck
suppression. go build ./... and go test ./... pass (the one pre-existing
Azure scheduler mock failure is unrelated).
@cristim

cristim commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

The previous errorlint conversion landed on three lines whose inline
`if err := m.Op(); ...` declarations shadow an outer `err` (govet
enable-all flags this as shadow). Rename each inner variable so the diff
stays clean:

- RunMigrations (line 45): m.Up() -> upErr
- RollbackMigrations (line 450): m.Steps(-steps) -> stepErr
- MigrateToVersion (line 485): m.Migrate(version) -> migErr

Renaming (rather than reusing the outer err) avoids an ineffassign, since
the outer err is overwritten immediately afterward by m.Version().

Verified with golangci-lint v2.10.1: zero new govet shadow issues and zero
errorlint issues vs origin/main; .golangci.yml unchanged; no errcheck
suppression. go build ./... and go test ./internal/database/... pass.
@cristim

cristim commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…touched files

Make every .go file in this PR's diff fully golangci-lint v2.10.1 clean
(all linters), so the change introduces zero new lint findings. No
.golangci.yml changes; handled in code, with justified inline nolint only
for genuinely-unfixable cases.

Code fixes (no nolint):
- godot: added terminating periods to flagged comments (auto-fixed).
- misspell: British -> US spelling in prose comments and user-facing
  messages (behaviour->behavior, defence->defense, cancelled->canceled in
  ctx/error-message prose, etc.).
- gocritic: equalFold (strings.ToLower ==  -> strings.EqualFold),
  builtinShadow (max -> maxDepth), sloppyReassign, exitAfterDefer
  (rekey main runs cancel before log.Fatalf), importShadow (renamed local
  accounts/mock/provider vars), paramTypeCombine (merged same-type params),
  unnamedResult (named multi-returns).
- govet: shadow (renamed inner err in if-init statements),
  fieldalignment (reordered struct fields via the fieldalignment fixer).
- gosec G115: added explicit [0, MaxInt32] bounds guards before the int32
  / int narrowing conversions in connection.go and migrate.go.
- revive: reordered gcpTokenExchangeAttempt results so error is last.
- staticcheck: removed a dead freshness read (SA4006); switched the GCP
  loader off the deprecated CredentialsFromJSON.
- unused: removed dead planIntersectsAllowed and triggerColdStartCollect.
- unconvert: dropped a redundant int() on syscall.Stdin.
- noctx: exec.Command -> exec.CommandContext in the configure-* CLI flows.

Justified nolint (genuinely unfixable / cross-cutting):
- misspell: DB status value 'cancelled' (status CHECK constraint) and
  column cancelled_by (migration 000035) inside SQL strings / status
  literals; documented and tracked for rename in PR #1277.
- gosec: G505 HMAC-SHA1 is the RFC 6238 TOTP default; G117 fields must
  carry credential secrets (not logged); G204/G702/G703 are local CLI
  subprocess/file reads with hardcoded or validated arguments; G705 the
  Lambda->HTTP adapter relays an already-escaped handler response.
- gocritic: hugeParam and tooManyResults on interface-bound / range-fed /
  constructor signatures where a pointer/struct rewrite is broad
  aliasing-prone churn for a marginal copy saving; rangeValCopy on
  read-only loops over large elements.
- revive: established exported names (SchedulerConfig, MockSESClient,
  MockSNSClient) and the package name "api".
- unparam: router-handler contract error returns, the buildMigrateDSN
  sslmode override seam, and executePurchase's documented wasMultiAccount
  signal.

Verified with golangci-lint v2.10.1: 0 issues across all touched files,
--new-from-rev=origin/main reports 0 new issues, .golangci.yml is identical
to origin/main, no errcheck suppression. go build ./... passes;
go test ./... passes (the one pre-existing Azure scheduler mock failure is
unrelated).
@cristim

cristim commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Eliminate the gocritic/unparam/revive/staticcheck //nolint suppressions
that were genuinely fixable, leaving only the gosec security exceptions,
the DB-schema 'cancelled' misspell exceptions, and a small set of
interface/SDK-contract judgment cases.

Code-fixed (nolint removed):
- gocritic rangeValCopy (15): converted every read-only range-over-large-
  element loop to an index loop (for i := range xs { x := &xs[i] / xs[i] }),
  preserving value-copy semantics where the body mutates or appends.
- gocritic hugeParam (28): pointer-ized the value params and threaded the
  change through all callers and tests. The account/rec fan-out paths in
  purchase/execution.go and scheduler.go (incl. fanOutPerAccount's callback
  type) are race-clean (verified with go test -race). NewHandler and
  NewScheduler now take *Config; ListStoredRecommendations' standalone
  helper buildRecommendationFilter takes *RecommendationFilter.
- gocritic tooManyResults (1): collectAllProviders now returns a single
  collectAllResult struct instead of six values.
- unparam (5): removed the always-"gcloud" program param from the GCP CLI
  helpers; removed the always-"" sslModeOverride param from buildMigrateDSN;
  dropped executePurchase's never-used wasMultiAccount result; dropped the
  always-nil error returns from getPublicInfo/getDeploymentInfo (router
  wrappers + tests updated).

Kept as documented exceptions:
- 18 gosec (each with a specific security justification).
- 16 misspell on the DB status value 'cancelled' / column cancelled_by
  (status CHECK + migration 000035; rename tracked in #1277).

Verified with golangci-lint v2.10.1: 0 issues on the 33 PR-touched files,
--new-from-rev=origin/main reports 0 new issues, .golangci.yml is identical
to origin/main, no nolint:errcheck. go build ./... and go test ./... pass
(the one pre-existing Azure scheduler mock failure is unrelated).
@cristim

cristim commented Jun 24, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/database/postgres/migrations/migrate.go (1)

530-561: 🩺 Stability & Availability | 🟠 Major

Preserve the configured sslmode here. ConnConfig.TLSConfig only says TLS is enabled, so verify-ca and verify-full get flattened to require and certificate/hostname verification is lost for migrations. Use config.ConnConfig.SSLMode instead of inferring from TLSConfig.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/database/postgres/migrations/migrate.go` around lines 530 - 561, The
DSN builder is flattening all TLS-enabled connections to require, which drops
the configured Postgres sslmode behavior. Update buildMigrateDSN to use
config.ConnConfig.SSLMode instead of inferring from ConnConfig.TLSConfig, so
migrate preserves verify-ca and verify-full semantics. Keep the rest of the DSN
assembly in migrate.go unchanged, including the existing
host/user/password/database handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/configure_gcp.go`:
- Line 391: The failure-continuation prompt is creating a new bufio.Reader from
os.Stdin instead of reusing the caller-provided reader, which can drop buffered
scripted input and cause blocking/EOF. Update executeGCPCommand and its prompt
flow to accept and use the same injected reader that the earlier prompt already
consumed from, and thread that reader through the call site in
configureGCPCommand so both prompts share one input source.

In `@internal/api/handler_registrations.go`:
- Around line 240-244: The best-effort email notification in notifyRegistrant is
using context.Background(), which can block the approval/rejection flow
indefinitely if the notifier stalls. Update notifyRegistrant to create a
short-lived timeout context before calling
h.emailNotifier.SendRegistrationDecisionNotification, and pass that context into
the send so the notification cannot hang the request. Keep the existing
nil/empty checks and preserve the best-effort behavior by ignoring
timeout-related failures after the send attempt.

In `@internal/api/handler.go`:
- Around line 77-78: NewHandler currently dereferences cfg immediately, so it
will panic when called with nil and should preserve zero-value-friendly
behavior. Update NewHandler to normalize a nil HandlerConfig to an empty/default
config before reading CORSAllowedOrigin, and keep the rest of the handler setup
using that safe config path so NewHandler(nil) works without crashing.

---

Outside diff comments:
In `@internal/database/postgres/migrations/migrate.go`:
- Around line 530-561: The DSN builder is flattening all TLS-enabled connections
to require, which drops the configured Postgres sslmode behavior. Update
buildMigrateDSN to use config.ConnConfig.SSLMode instead of inferring from
ConnConfig.TLSConfig, so migrate preserves verify-ca and verify-full semantics.
Keep the rest of the DSN assembly in migrate.go unchanged, including the
existing host/user/password/database handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f93dd029-2435-4ae5-95ce-8a401c70b656

📥 Commits

Reviewing files that changed from the base of the PR and between 4d8be01 and 5588bf5.

📒 Files selected for processing (32)
  • cmd/configure_gcp.go
  • cmd/lambda/main_test.go
  • internal/api/coverage_extras_test.go
  • internal/api/handler.go
  • internal/api/handler_accounts.go
  • internal/api/handler_commitment_options_test.go
  • internal/api/handler_coverage_test.go
  • internal/api/handler_dashboard.go
  • internal/api/handler_dashboard_test.go
  • internal/api/handler_federation_test.go
  • internal/api/handler_inventory.go
  • internal/api/handler_inventory_test.go
  • internal/api/handler_registrations.go
  • internal/api/handler_test.go
  • internal/api/router.go
  • internal/config/store_postgres_recommendations.go
  • internal/config/store_postgres_savings_filter_test.go
  • internal/database/connection.go
  • internal/database/postgres/migrations/migrate.go
  • internal/database/postgres/migrations/migrate_security_test.go
  • internal/mocks/stores.go
  • internal/purchase/coverage_extra_test.go
  • internal/purchase/execution.go
  • internal/purchase/execution_test.go
  • internal/purchase/manager.go
  • internal/scheduler/scheduler.go
  • internal/scheduler/scheduler_test.go
  • internal/server/app.go
  • internal/server/app_test.go
  • internal/server/http_test.go
  • internal/server/lambda_coverage_test.go
  • internal/server/lambda_test.go
✅ Files skipped from review due to trivial changes (2)
  • internal/api/handler_commitment_options_test.go
  • internal/api/handler_federation_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/database/connection.go
  • internal/mocks/stores.go

Comment thread cmd/configure_gcp.go Outdated
Comment thread internal/api/handler_registrations.go Outdated
Comment thread internal/api/handler.go
…osec+misspell only

Drive the residual non-security nolints to zero so the only suppressions left
on #1265 are the 18 gosec security exceptions and the 16 DB-schema 'cancelled'
misspell exceptions. CUDly is an application with no external consumers, so
internal renames are safe (all in-repo callers updated).

RecommendationFilter cascade (matches #1276 so the branches reconcile):
- ListStoredRecommendations / ListRecommendations now take
  *config.RecommendationFilter across the StoreInterface, the server
  SchedulerInterface, the api recsLister/SchedulerInterface, the PostgresStore
  and Scheduler impls, the MockConfigStore/MockScheduler mocks, all test
  fakes, and ~40 call sites. parseRecommendationFilter / buildCoverageRecFilter
  return *RecommendationFilter; testify matchers use mock.Anything or
  &config.RecommendationFilter{...}, and the captured-filter assertions and
  MatchedBy closures take the pointer type.

revive renames:
- mocks.MockSESClient -> mocks.SESClient, mocks.MockSNSClient -> mocks.SNSClient.
- scheduler.SchedulerConfig -> scheduler.Config (NewScheduler takes *Config).
- package api -> package apihttp (the 8 external importers alias it back to
  api so their api.X references are unchanged).

SDK-mirror pointer-ize:
- Connection.BeginTx takes *pgx.TxOptions (nil = default opts), dereferencing
  at the pgx boundary; callers pass nil.
- Handler.runOrgDiscovery takes *aws.Config, dereferencing at the
  accounts.DiscoverOrgAccounts / discoverOrgFn boundary.

staticcheck SA1019:
- loadStoredGCPTokenSource now detects the credential JSON's "type" field
  (service_account vs external_account) and calls the non-deprecated
  CredentialsFromJSONWithTypeAndParams with the matching google.CredentialsType,
  so both service-account keys and WIF configs load without the deprecated
  CredentialsFromJSON.

Also dropped a stray gocritic appendAssign suppression in handler_history_test
by building the slice on a fresh backing array.

Verified with golangci-lint v2.10.1: --new-from-rev=origin/main reports 0 new
issues; the only nolints in any changed file are gosec and misspell;
.golangci.yml is identical to origin/main; no nolint:errcheck. go build ./...
and go test ./... pass (the one pre-existing Azure scheduler mock failure is
unrelated), and go test -race on the scheduler/purchase paths is clean.
@cristim

cristim commented Jun 25, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 25, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/mocks/sns.go (1)

21-24: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fail loudly on non-nil wrong return types.

The nil guard is useful for (nil, error), but a non-nil value of the wrong type currently returns nil, nil when no error is configured, masking broken mock setup.

Proposed fix
 	val, ok := args.Get(0).(*sns.PublishOutput)
 	if !ok {
-		return nil, args.Error(1)
+		panic("SNSClient.Publish mock return[0] must be *sns.PublishOutput")
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/mocks/sns.go` around lines 21 - 24, The Publish mock in sns.go
currently hides misconfigured returns by falling through to nil, nil when the
first return value is not a *sns.PublishOutput and no error is set. Update the
Publish method to fail loudly on non-nil wrong return types by checking the
first return from args.Get(0) before the nil guard and returning a clear error
(or panicking if that matches the mock pattern) instead of silently succeeding;
keep the existing nil,error behavior for valid nil returns.
♻️ Duplicate comments (1)
internal/api/handler_registrations.go (1)

240-244: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the best-effort notification call with a timeout.

notifyRegistrant still uses context.Background() for the synchronous email send, so a stalled notifier can hold the approval/rejection request indefinitely after the registration state has already changed. Wrap the send in a short context.WithTimeout.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/handler_registrations.go` around lines 240 - 244,
`notifyRegistrant` currently sends the email with `context.Background()`, so a
slow notifier can block the request indefinitely; update this method to create a
short-lived `context.WithTimeout` for the
`h.emailNotifier.SendRegistrationDecisionNotification` call, and make sure the
timeout context is canceled with `defer`. Keep the behavior best-effort by
preserving the existing nil/email guards and only changing the context used in
`notifyRegistrant`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/config/interfaces.go`:
- Line 248: The recommendation store still assumes a non-nil filter and can
panic when ListStoredRecommendations calls buildRecommendationFilter with a nil
pointer. Update the ListStoredRecommendations path in
internal/config/store_postgres_recommendations.go to normalize a nil
RecommendationFilter to an empty one before building the query, and keep the
handling consistent with the ListStoredRecommendations interface contract and
buildRecommendationFilter helper.

---

Outside diff comments:
In `@internal/mocks/sns.go`:
- Around line 21-24: The Publish mock in sns.go currently hides misconfigured
returns by falling through to nil, nil when the first return value is not a
*sns.PublishOutput and no error is set. Update the Publish method to fail loudly
on non-nil wrong return types by checking the first return from args.Get(0)
before the nil guard and returning a clear error (or panicking if that matches
the mock pattern) instead of silently succeeding; keep the existing nil,error
behavior for valid nil returns.

---

Duplicate comments:
In `@internal/api/handler_registrations.go`:
- Around line 240-244: `notifyRegistrant` currently sends the email with
`context.Background()`, so a slow notifier can block the request indefinitely;
update this method to create a short-lived `context.WithTimeout` for the
`h.emailNotifier.SendRegistrationDecisionNotification` call, and make sure the
timeout context is canceled with `defer`. Keep the behavior best-effort by
preserving the existing nil/email guards and only changing the context used in
`notifyRegistrant`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35ff029f-f133-4c88-b304-d64ff33f0793

📥 Commits

Reviewing files that changed from the base of the PR and between 5588bf5 and d5da6b5.

📒 Files selected for processing (121)
  • cmd/lambda/main_test.go
  • cmd/rekey/main.go
  • internal/analytics/collector_test.go
  • internal/api/analytics_postgres.go
  • internal/api/analytics_postgres_test.go
  • internal/api/base64_password_guard_test.go
  • internal/api/build_suppressions_test.go
  • internal/api/coverage_extras_test.go
  • internal/api/coverage_gaps_test.go
  • internal/api/db_rate_limiter.go
  • internal/api/db_rate_limiter_integration_test.go
  • internal/api/db_rate_limiter_test.go
  • internal/api/exchange_helpers_test.go
  • internal/api/exchange_lookup.go
  • internal/api/exchange_lookup_test.go
  • internal/api/handler.go
  • internal/api/handler_accounts.go
  • internal/api/handler_accounts_external_id_test.go
  • internal/api/handler_accounts_router_test.go
  • internal/api/handler_accounts_test.go
  • internal/api/handler_analytics.go
  • internal/api/handler_analytics_test.go
  • internal/api/handler_apikeys.go
  • internal/api/handler_apikeys_test.go
  • internal/api/handler_auth.go
  • internal/api/handler_auth_test.go
  • internal/api/handler_commitment_options.go
  • internal/api/handler_commitment_options_test.go
  • internal/api/handler_config.go
  • internal/api/handler_config_test.go
  • internal/api/handler_coverage_test.go
  • internal/api/handler_dashboard.go
  • internal/api/handler_dashboard_test.go
  • internal/api/handler_docs.go
  • internal/api/handler_docs_test.go
  • internal/api/handler_federation.go
  • internal/api/handler_federation_test.go
  • internal/api/handler_groups.go
  • internal/api/handler_groups_test.go
  • internal/api/handler_history.go
  • internal/api/handler_history_test.go
  • internal/api/handler_inventory.go
  • internal/api/handler_inventory_test.go
  • internal/api/handler_oidc.go
  • internal/api/handler_oidc_test.go
  • internal/api/handler_per_account_perms_test.go
  • internal/api/handler_plans.go
  • internal/api/handler_plans_test.go
  • internal/api/handler_purchases.go
  • internal/api/handler_purchases_guards_test.go
  • internal/api/handler_purchases_revoke.go
  • internal/api/handler_purchases_revoke_test.go
  • internal/api/handler_purchases_test.go
  • internal/api/handler_recommendations.go
  • internal/api/handler_recommendations_refresh.go
  • internal/api/handler_recommendations_test.go
  • internal/api/handler_registrations.go
  • internal/api/handler_registrations_autoenable_test.go
  • internal/api/handler_registrations_recipients_test.go
  • internal/api/handler_registrations_validate_test.go
  • internal/api/handler_ri_exchange.go
  • internal/api/handler_ri_exchange_integration_test.go
  • internal/api/handler_ri_exchange_test.go
  • internal/api/handler_router.go
  • internal/api/handler_router_test.go
  • internal/api/handler_security_test.go
  • internal/api/handler_test.go
  • internal/api/handler_users.go
  • internal/api/handler_users_test.go
  • internal/api/handler_version.go
  • internal/api/handler_version_test.go
  • internal/api/health.go
  • internal/api/health_test.go
  • internal/api/inmemory_rate_limiter.go
  • internal/api/inmemory_rate_limiter_test.go
  • internal/api/middleware.go
  • internal/api/middleware_test.go
  • internal/api/mocks_test.go
  • internal/api/rate_limiter.go
  • internal/api/rate_limiter_test.go
  • internal/api/ri_utilization_cache.go
  • internal/api/ri_utilization_cache_integration_test.go
  • internal/api/ri_utilization_cache_test.go
  • internal/api/router.go
  • internal/api/router_660_permission_flips_test.go
  • internal/api/router_auth_test.go
  • internal/api/router_authuser_test.go
  • internal/api/router_handlers_test.go
  • internal/api/scoping.go
  • internal/api/types.go
  • internal/api/types_apikeys.go
  • internal/api/types_test.go
  • internal/api/validation.go
  • internal/api/validation_test.go
  • internal/config/interfaces.go
  • internal/config/store_postgres_recommendations.go
  • internal/config/store_postgres_recommendations_test.go
  • internal/credentials/resolver.go
  • internal/credentials/resolver_extra_test.go
  • internal/database/connection.go
  • internal/database/coverage_extra_test.go
  • internal/email/coverage_test.go
  • internal/email/sender_test.go
  • internal/email/templates_test.go
  • internal/mocks/ses.go
  • internal/mocks/sns.go
  • internal/mocks/stores.go
  • internal/scheduler/scheduler.go
  • internal/scheduler/scheduler_overrides_test.go
  • internal/scheduler/scheduler_suppressions_test.go
  • internal/scheduler/scheduler_test.go
  • internal/server/adapter_test.go
  • internal/server/app.go
  • internal/server/app_test.go
  • internal/server/http.go
  • internal/server/http_test.go
  • internal/server/interfaces.go
  • internal/server/lambda_coverage_test.go
  • internal/server/lambda_test.go
  • internal/server/test_helpers_test.go
  • internal/testutil/mocks.go
✅ Files skipped from review due to trivial changes (18)
  • internal/api/types_test.go
  • internal/api/db_rate_limiter_integration_test.go
  • internal/server/adapter_test.go
  • internal/api/handler_purchases_guards_test.go
  • internal/api/scoping.go
  • internal/api/handler_accounts_external_id_test.go
  • internal/api/handler_oidc.go
  • internal/api/handler_ri_exchange.go
  • internal/api/handler_version.go
  • internal/analytics/collector_test.go
  • internal/api/handler_ri_exchange_integration_test.go
  • internal/api/ri_utilization_cache_test.go
  • internal/api/router_auth_test.go
  • internal/api/handler_auth_test.go
  • internal/api/router_authuser_test.go
  • internal/api/handler_oidc_test.go
  • internal/api/handler_purchases_test.go
  • internal/api/handler_router_test.go
🚧 Files skipped from review as they are similar to previous changes (18)
  • internal/api/handler_coverage_test.go
  • internal/server/http_test.go
  • internal/server/lambda_test.go
  • internal/api/handler_commitment_options_test.go
  • cmd/lambda/main_test.go
  • internal/api/handler_test.go
  • internal/server/app.go
  • internal/server/lambda_coverage_test.go
  • internal/server/http.go
  • cmd/rekey/main.go
  • internal/server/app_test.go
  • internal/api/handler_inventory.go
  • internal/credentials/resolver.go
  • internal/api/handler_recommendations_refresh.go
  • internal/api/handler_dashboard_test.go
  • internal/config/store_postgres_recommendations.go
  • internal/api/handler_dashboard.go
  • internal/mocks/stores.go

Comment thread internal/config/interfaces.go
- Thread configure_gcp.go: pass the caller-supplied bufio.Reader into
  executeGCPCommand instead of creating a fresh bufio.NewReader(os.Stdin);
  avoids double-buffering that drops buffered scripted/piped input on the
  failure-continuation prompt.
- Thread handler_registrations.go: wrap the best-effort email send in a
  5-second timeout context so a stalled notifier cannot hold the
  approval/rejection request open after state has already changed.
- Thread handler.go: guard NewHandler against a nil *HandlerConfig to
  prevent panic on NewHandler(nil).
- Thread store_postgres_recommendations.go: guard buildRecommendationFilter
  against a nil *RecommendationFilter; nil is a valid call under the
  ListStoredRecommendations interface contract (means no filter).
@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Replace silent cfg=&HandlerConfig{} / cfg=&Config{} fallbacks in
NewHandler and NewScheduler with explicit panics that surface the
programming error at construction time (no-silent-fallback rule).

Normalize nil *RecommendationFilter at the top of
ListStoredRecommendations (single normalization site) instead of
inside buildRecommendationFilter, and document the nil-means-match-all
contract on StoreInterface, PurchaseManagerInterface, and the
exchange-lookup and server interface.

Extend handler_test.go and scheduler_test.go with PanicsWithValue
assertions for the nil-config path; add
TestPostgresStore_ListStoredRecommendations_NilFilter to guard the
nil-filter contract with a real Postgres round-trip.

Reuse the caller-supplied bufio.Reader in executeExplicitCommand
(configure_azure.go) to avoid double-buffering piped stdin; bump
notifyRegistrant timeout from 5s to 20s with its own background
context and a documenting comment.
@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…Failed

The pre-existing TestScheduler_CollectAzureRecommendations_Success
expected `require.NoError(t, err)` and was passing back when
collectAzureRecommendations logged-and-swallowed per-account credential
failures. After PR #1265 fan-out wiring landed errAllAccountsFailed
(fail-loud when every enabled account fails so a partial / empty result
isn't merged into the aggregate as success), the test breaks: 1 enabled
account + 1 inevitable DefaultAzureCredential failure = FailedCount ==
len(accounts) -> error returned.

The test name was misleading (it never actually exercised a successful
Azure recommendation fetch — managed_identity always fails in test).
Rename to _AllAccountsFail and assert the new contract: error returned,
recs nil, message contains "all 1 accounts failed". Pins the regression
the new fan-out contract is meant to prevent.
@cristim

cristim commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/scheduler/scheduler_test.go (1)

1459-1498: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Make this failure-path test deterministic.

This test currently passes only when ambient Azure auth is unavailable, so it can flip on CI or a developer machine that has valid Azure credentials. Please force the per-account failure through an injected stub/provider mock instead of relying on DefaultAzureCredential failing in the host environment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/scheduler/scheduler_test.go` around lines 1459 - 1498, The
failure-path test for collectAzureRecommendations is nondeterministic because it
depends on DefaultAzureCredential being unavailable in the host environment.
Update TestScheduler_CollectAzureRecommendations_AllAccountsFail to inject a
stubbed Azure provider or client that always returns an error for the enabled
account, and wire it through Scheduler/collectAzureRecommendations so the
per-account failure is forced regardless of local credentials. Keep the
assertions that all accounts fail, but remove the ambient-auth dependency by
mocking the provider path used for azure recommendations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/scheduler/scheduler_test.go`:
- Around line 1459-1498: The failure-path test for collectAzureRecommendations
is nondeterministic because it depends on DefaultAzureCredential being
unavailable in the host environment. Update
TestScheduler_CollectAzureRecommendations_AllAccountsFail to inject a stubbed
Azure provider or client that always returns an error for the enabled account,
and wire it through Scheduler/collectAzureRecommendations so the per-account
failure is forced regardless of local credentials. Keep the assertions that all
accounts fail, but remove the ambient-auth dependency by mocking the provider
path used for azure recommendations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df2c0359-f901-4917-bf91-89a74de2e0da

📥 Commits

Reviewing files that changed from the base of the PR and between b952269 and f3b0a0f.

📒 Files selected for processing (12)
  • cmd/configure_azure.go
  • internal/api/exchange_lookup.go
  • internal/api/handler.go
  • internal/api/handler_registrations.go
  • internal/api/handler_test.go
  • internal/api/types.go
  • internal/config/interfaces.go
  • internal/config/store_postgres_recommendations.go
  • internal/config/store_postgres_recommendations_test.go
  • internal/scheduler/scheduler.go
  • internal/scheduler/scheduler_test.go
  • internal/server/interfaces.go
🚧 Files skipped from review as they are similar to previous changes (9)
  • internal/server/interfaces.go
  • internal/config/interfaces.go
  • internal/api/types.go
  • internal/api/handler_test.go
  • internal/config/store_postgres_recommendations.go
  • internal/api/exchange_lookup.go
  • internal/api/handler_registrations.go
  • cmd/configure_azure.go
  • internal/scheduler/scheduler.go

@cristim

cristim commented Jul 3, 2026

Copy link
Copy Markdown
Member Author

Closing as superseded. Disposition from the open-PR reconciliation (2026-07-03): (1) the errcheck work here (107 findings) is fully on main via #1343 with an equivalent implementation; (2) the undocumented package api -> apihttp rename across 91 files was never in this PR's stated scope and has no recorded motivation - if a linter in the #1342 burn-down set genuinely requires it, it will resurface there with a concrete driver and should land as its own documented PR; (3) the ListStoredRecommendations pointer-receiver change is a gocritic hugeParam-class fix covered by #1342's remaining 446 gocritic findings. Nothing unique is lost.

@cristim cristim closed this Jul 3, 2026
cristim added a commit that referenced this pull request Jul 3, 2026
Fix the ~45 golangci-lint v2 violations that #1276 deliberately skips
because these files are also touched by other open PRs (#1265, #1299).
internal/deploy/* is excluded here as #1246 deletes that package.

Files fixed and linters addressed:
- cmd/helpers_test.go: fieldalignment (govet), unparam
- cmd/main_test.go: fieldalignment (govet), also fix positional struct
  literals broken by field reordering
- cmd/multi_service_filters.go: hugeParam + rangeValCopy (gocritic),
  equalFold (gocritic), godot; all filter functions updated to *Config /
  *Recommendation params with callers updated across the cmd package
- cmd/multi_service_engine_versions_test.go: fieldalignment (govet), godot
- internal/auth/service_password_test.go: fieldalignment (govet), godot
- internal/auth/store_postgres_test.go: fieldalignment (govet), godot
- internal/purchase/approvals.go: err-shadow (govet), misspell
  (analogue->analog, cancelled->canceled, cancelling->canceling)
- internal/purchase/messages.go: hugeParam + rangeValCopy (gocritic),
  godot, misspell (authorised->authorized)

Incidental changes: caller sites in cmd/multi_service{,_helpers,_test,
_filters_test}.go; handle*Message signature callers in
internal/purchase/{coverage_extra,money_path_regression}_test.go;
test assertions updated to match renamed error strings.
@cristim

cristim commented Jul 11, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added a commit that referenced this pull request Sep 27, 2026
Fix the ~45 golangci-lint v2 violations that #1276 deliberately skips
because these files are also touched by other open PRs (#1265, #1299).
internal/deploy/* is excluded here as #1246 deletes that package.

Files fixed and linters addressed:
- cmd/helpers_test.go: fieldalignment (govet), unparam
- cmd/main_test.go: fieldalignment (govet), also fix positional struct
  literals broken by field reordering
- cmd/multi_service_filters.go: hugeParam + rangeValCopy (gocritic),
  equalFold (gocritic), godot; all filter functions updated to *Config /
  *Recommendation params with callers updated across the cmd package
- cmd/multi_service_engine_versions_test.go: fieldalignment (govet), godot
- internal/auth/service_password_test.go: fieldalignment (govet), godot
- internal/auth/store_postgres_test.go: fieldalignment (govet), godot
- internal/purchase/approvals.go: err-shadow (govet), misspell
  (analogue->analog, cancelled->canceled, cancelling->canceling)
- internal/purchase/messages.go: hugeParam + rangeValCopy (gocritic),
  godot, misspell (authorised->authorized)

Incidental changes: caller sites in cmd/multi_service{,_helpers,_test,
_filters_test}.go; handle*Message signature callers in
internal/purchase/{coverage_extra,money_path_regression}_test.go;
test assertions updated to match renamed error strings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/l Weeks impact/internal Team-internal only priority/p1 Next up; this sprint severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant