Skip to content

Refactor/component helper struct#9

Open
agustingroh wants to merge 4 commits intomainfrom
refactor/component-helper-struct
Open

Refactor/component helper struct#9
agustingroh wants to merge 4 commits intomainfrom
refactor/component-helper-struct

Conversation

@agustingroh
Copy link
Copy Markdown
Contributor

@agustingroh agustingroh commented Apr 7, 2026

Summary by CodeRabbit

  • Breaking Changes

    • Version-management API refactored to an instance-based helper; configuration shape and call patterns changed.
    • Semver utilities moved to the top-level package path (import path updated).
  • New Features

    • Instance method to find the nearest matching component version.
    • Added 1.0.0 release entry.
  • Documentation

    • README examples updated for new initialization and expanded component output fields.
  • Tests

    • Semver test coverage expanded and reorganized.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Introduces an instance-based API: adds ComponentHelper with Cfg and NewHelper, converts package-level GetComponentsVersion into (*ComponentHelper) GetComponentsVersion(ctx, s, []ComponentDTO), adds Component.FindNearestVersion(), moves semver helpers into componenthelper, and updates tests, README, and CHANGELOG for v1.0.0.

Changes

Cohort / File(s) Summary
Core API Refactor
componenthelper/component.go
Replaces ComponentVersionCfg with Cfg, adds ComponentHelper (internal fields maxWorkers, db) and NewHelper(cfg Cfg) *ComponentHelper; converts package function GetComponentsVersion into method (*ComponentHelper) GetComponentsVersion(ctx context.Context, s *zap.SugaredLogger, componentDTOs []ComponentDTO); adds const MaxWorkers = 5 and moves internal use from config to helper state.
Entity Method Addition
componenthelper/component_entity.go
Adds func (c *Component) FindNearestVersion() string delegating to the semver nearest-version helper using the component's Requirement and Versions.
Semver Helper Relocation
componenthelper/semver.go
Changes package declaration from utilscomponenthelper, relocating semver utilities into the top-level package namespace.
Tests
componenthelper/component_test.go, componenthelper/semver_test.go, componenthelper/utils/semver_test.go
Updates tests to instantiate ComponentHelper via NewHelper(Cfg{...}) and call the new method; adds componenthelper/semver_test.go with expanded cases; deletes old componenthelper/utils/semver_test.go.
Docs & Changelog
README.md, CHANGELOG.md
README examples updated to use NewHelper/Cfg and per-call ctx/logger; README adds "Component Output" fields; CHANGELOG documents breaking changes for v1.0.0 and migration notes referencing API renames.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Helper as ComponentHelper
  participant WorkerPool
  participant ScanOSS_DB as "scanoss / DB"

  Caller->>Helper: NewHelper(cfg)
  Caller->>Helper: GetComponentsVersion(ctx, logger, []ComponentDTO)
  activate Helper
  Helper->>WorkerPool: start workers (maxWorkers)
  activate WorkerPool
  loop per component (concurrent)
    WorkerPool->>ScanOSS_DB: scan component (uses Helper.db)
    ScanOSS_DB-->>WorkerPool: scan results
  end
  WorkerPool-->>Helper: aggregate results
  deactivate WorkerPool
  Helper-->>Caller: []Component (resolved versions, status)
  deactivate Helper
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • isasmendiagus
  • eeisegn
  • scanoss-qg

Poem

🐇 In fields of code I hop and pry,
New Helper born beneath the sky.
Methods snug where functions ran,
Versions found by rabbit's plan.
Hooray for Cfg — a carrot pie! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main structural refactoring: converting GetComponentsVersion from a standalone function accepting ComponentVersionCfg to a ComponentHelper struct with a method, while simplifying the config object.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/component-helper-struct

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 and usage tips.

@agustingroh agustingroh force-pushed the refactor/component-helper-struct branch from d659c83 to 84f353e Compare April 7, 2026 11:21
@agustingroh agustingroh marked this pull request as ready for review April 7, 2026 11:24
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 (2)
componenthelper/component.go (2)

88-90: ⚠️ Potential issue | 🟡 Minor

Variable c shadows method receiver.

The loop variable c shadows the method receiver c, which could lead to bugs if the receiver is accessed inside the loop body.

✏️ Proposed fix
-	for _, c := range sanitisedComponents {
-		jobs <- c
+	for _, comp := range sanitisedComponents {
+		jobs <- comp
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 88 - 90, The for-loop in the
method on receiver named `c` uses a loop variable `c` which shadows the
receiver; rename the loop variable (e.g., to `comp` or `sanitisedComp`) in the
loop that iterates over `sanitisedComponents` and update any uses inside the
loop body (including the send to `jobs <- ...`) so the receiver `c` is not
shadowed and the code remains clear and correct.

48-49: 🛠️ Refactor suggestion | 🟠 Major

Remove unused Input field from Cfg.

The Input field is documented but never used—input is now passed directly to GetComponentsVersion(ctx, componentDTOs). This dead field adds confusion to the API.

🧹 Proposed fix
 type Cfg struct {
 	// MaxWorkers is the maximum number of concurrent goroutines used to resolve versions.
 	// If <= 0, defaults to MaxWorkers (5).
 	MaxWorkers int
 	// Ctx is the context used for cancellation and deadline propagation.
 	Ctx context.Context
 	// S is the sugared logger for structured logging.
 	S *zap.SugaredLogger
 	// DB is the database connection used to query component data.
 	DB *sqlx.DB
-	// Input is the list of components whose versions need to be resolved.
-	Input []ComponentDTO
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 48 - 49, Remove the dead Input
field from the Cfg struct: delete the `Input []ComponentDTO` field and its docs
in the `Cfg` type (used for componenthelper/component.go), and ensure no
remaining code references it (search for `Cfg.Input`); confirm callers use the
existing GetComponentsVersion(ctx, componentDTOs) path (e.g., where
`componentDTOs` is passed) and update any comments or tests that mention
`Cfg.Input`.
🧹 Nitpick comments (3)
componenthelper/component_test.go (1)

70-78: Fix typo in variable name: componenentHelpercomponentHelper.

The variable name has a typo ("componenent" instead of "component").

✏️ Proposed fix
-	componenentHelper := NewComponentHelper(Cfg{
+	componentHelper := NewComponentHelper(Cfg{
 		MaxWorkers: 2,
 		Ctx:        ctx,
 		S:          s,
 	})

 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			result := componenentHelper.GetComponentsVersion(ctx, tt.input)
+			result := componentHelper.GetComponentsVersion(ctx, tt.input)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component_test.go` around lines 70 - 78, Rename the
misspelled variable componenentHelper to componentHelper where it's declared and
referenced: change the instantiation using NewComponentHelper(Cfg{...}) and the
subsequent call to GetComponentsVersion(ctx, tt.input) to use componentHelper so
the variable name is consistent and typo-free.
CHANGELOG.md (1)

69-70: Add a trailing newline at end of file.

The file appears to be missing a trailing newline after line 70. Most linters and version control systems expect files to end with a newline character.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` around lines 69 - 70, The file ends without a trailing newline
after the final link line "[1.0.0]:
https://github.com/scanoss/go-component-helper/compare/v0.6.0...v1.0.0"; add a
single newline character at EOF so the last line terminates with '\n' (ensure
the file ends with a blank line or newline byte).
componenthelper/component.go (1)

78-80: Avoid mutating receiver state for default value logic.

c.maxWorkers = MaxWorkers permanently modifies the helper instance. If GetComponentsVersion is called multiple times, the first call's mutation persists. Use a local variable instead:

♻️ Proposed fix
-	if c.maxWorkers <= 0 {
-		c.maxWorkers = MaxWorkers
+	workers := c.maxWorkers
+	if workers <= 0 {
+		workers = MaxWorkers
 	}
-	numWorkers := min(c.maxWorkers, numJobs)
+	numWorkers := min(workers, numJobs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 78 - 80, In GetComponentsVersion
avoid mutating the receiver by not assigning to c.maxWorkers; instead compute a
local variable (e.g., maxWorkers := c.maxWorkers; if maxWorkers <= 0 {
maxWorkers = MaxWorkers }) and use that local value for subsequent logic; update
any references in GetComponentsVersion that currently read c.maxWorkers to use
the local maxWorkers so the helper instance state is not changed across calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@componenthelper/component.go`:
- Around line 88-90: The for-loop in the method on receiver named `c` uses a
loop variable `c` which shadows the receiver; rename the loop variable (e.g., to
`comp` or `sanitisedComp`) in the loop that iterates over `sanitisedComponents`
and update any uses inside the loop body (including the send to `jobs <- ...`)
so the receiver `c` is not shadowed and the code remains clear and correct.
- Around line 48-49: Remove the dead Input field from the Cfg struct: delete the
`Input []ComponentDTO` field and its docs in the `Cfg` type (used for
componenthelper/component.go), and ensure no remaining code references it
(search for `Cfg.Input`); confirm callers use the existing
GetComponentsVersion(ctx, componentDTOs) path (e.g., where `componentDTOs` is
passed) and update any comments or tests that mention `Cfg.Input`.

---

Nitpick comments:
In `@CHANGELOG.md`:
- Around line 69-70: The file ends without a trailing newline after the final
link line "[1.0.0]:
https://github.com/scanoss/go-component-helper/compare/v0.6.0...v1.0.0"; add a
single newline character at EOF so the last line terminates with '\n' (ensure
the file ends with a blank line or newline byte).

In `@componenthelper/component_test.go`:
- Around line 70-78: Rename the misspelled variable componenentHelper to
componentHelper where it's declared and referenced: change the instantiation
using NewComponentHelper(Cfg{...}) and the subsequent call to
GetComponentsVersion(ctx, tt.input) to use componentHelper so the variable name
is consistent and typo-free.

In `@componenthelper/component.go`:
- Around line 78-80: In GetComponentsVersion avoid mutating the receiver by not
assigning to c.maxWorkers; instead compute a local variable (e.g., maxWorkers :=
c.maxWorkers; if maxWorkers <= 0 { maxWorkers = MaxWorkers }) and use that local
value for subsequent logic; update any references in GetComponentsVersion that
currently read c.maxWorkers to use the local maxWorkers so the helper instance
state is not changed across calls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71bc67f8-e064-4909-a8da-c140d033f20d

📥 Commits

Reviewing files that changed from the base of the PR and between 67e5c1c and f877ba7.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • componenthelper/component.go
  • componenthelper/component_entity.go
  • componenthelper/component_test.go
  • componenthelper/semver.go
  • componenthelper/semver_test.go
  • componenthelper/utils/semver_test.go
💤 Files with no reviewable changes (1)
  • componenthelper/utils/semver_test.go

Copy link
Copy Markdown

@scanoss-qg scanoss-qg left a comment

Choose a reason for hiding this comment

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

LGTM

@agustingroh agustingroh force-pushed the refactor/component-helper-struct branch 2 times, most recently from afddd93 to dc4cc10 Compare April 7, 2026 18:00
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
componenthelper/component.go (2)

37-50: Remove unused fields from Cfg struct.

The Ctx (line 43) and Input (line 49) fields are defined in Cfg but are never used by NewHelper. The refactored API passes these as method parameters to GetComponentsVersion(ctx, componentDTOs) instead. Keeping unused fields in the config struct can confuse users about the intended API.

Proposed fix to remove unused fields
 // Cfg holds the configuration for the component helper.
 type Cfg struct {
 	// MaxWorkers is the maximum number of concurrent goroutines used to resolve versions.
 	// If <= 0, defaults to MaxWorkers (5).
 	MaxWorkers int
-	// Ctx is the context used for cancellation and deadline propagation.
-	Ctx context.Context
 	// S is the sugared logger for structured logging.
 	S *zap.SugaredLogger
 	// DB is the database connection used to query component data.
 	DB *sqlx.DB
-	// Input is the list of components whose versions need to be resolved.
-	Input []ComponentDTO
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 37 - 50, The Cfg struct contains
unused fields Ctx and Input that are no longer used by NewHelper; remove the Ctx
and Input fields from the Cfg definition and update any comments to reflect the
reduced configuration surface. Verify NewHelper and callers rely on passing
context and component list into GetComponentsVersion(ctx, componentDTOs) and
adjust any construction sites of Cfg to stop setting Ctx/Input so there are no
unused assignments or imports.

88-90: Variable shadowing: c shadows the receiver.

The loop variable c shadows the method receiver c *ComponentHelper. This doesn't cause a bug here since the receiver isn't used inside the loop, but it's confusing and error-prone for future maintenance.

Proposed fix to rename the loop variable
-	for _, c := range sanitisedComponents {
-		jobs <- c
+	for _, comp := range sanitisedComponents {
+		jobs <- comp
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 88 - 90, The for-loop variable
name `c` shadows the method receiver `c *ComponentHelper`; rename the loop
variable to a non-conflicting identifier (e.g., `comp` or `sc`) in the loop that
sends elements of `sanitisedComponents` into `jobs` so the receiver `c` is not
shadowed—update the loop `for _, c := range sanitisedComponents { jobs <- c }`
to use the new name (and send that variable into `jobs`) to avoid shadowing and
improve clarity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@componenthelper/component.go`:
- Around line 78-80: GetComponentsVersion currently mutates the ComponentHelper
field c.maxWorkers when it's <= 0; instead preserve instance state by
introducing a local variable (e.g., maxWorkers) initialized from c.maxWorkers,
set it to MaxWorkers if <= 0, and use that local variable throughout the
function (rather than assigning back to c.maxWorkers). Update any references in
GetComponentsVersion to use the local maxWorkers so ComponentHelper.maxWorkers
is not modified.

In `@README.md`:
- Around line 26-46: Update the Component Output JSON example to match Go's
default JSON marshaling for domain.ComponentStatus by changing the lowercase
status fields to their exported names: replace "status" with "Status" and inside
it replace "status_code" and "message" with "StatusCode" and "Message"
respectively so the example reflects the actual serialized output from the
ComponentStatus type.

---

Nitpick comments:
In `@componenthelper/component.go`:
- Around line 37-50: The Cfg struct contains unused fields Ctx and Input that
are no longer used by NewHelper; remove the Ctx and Input fields from the Cfg
definition and update any comments to reflect the reduced configuration surface.
Verify NewHelper and callers rely on passing context and component list into
GetComponentsVersion(ctx, componentDTOs) and adjust any construction sites of
Cfg to stop setting Ctx/Input so there are no unused assignments or imports.
- Around line 88-90: The for-loop variable name `c` shadows the method receiver
`c *ComponentHelper`; rename the loop variable to a non-conflicting identifier
(e.g., `comp` or `sc`) in the loop that sends elements of `sanitisedComponents`
into `jobs` so the receiver `c` is not shadowed—update the loop `for _, c :=
range sanitisedComponents { jobs <- c }` to use the new name (and send that
variable into `jobs`) to avoid shadowing and improve clarity.
🪄 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: 03df3836-945c-4462-9a56-bf39d4107bd7

📥 Commits

Reviewing files that changed from the base of the PR and between f877ba7 and afddd93.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • componenthelper/component.go
  • componenthelper/component_entity.go
  • componenthelper/component_test.go
  • componenthelper/semver.go
  • componenthelper/semver_test.go
  • componenthelper/utils/semver_test.go
💤 Files with no reviewable changes (1)
  • componenthelper/utils/semver_test.go
✅ Files skipped from review due to trivial changes (2)
  • componenthelper/component_test.go
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • componenthelper/semver.go

Comment thread componenthelper/component.go
Comment thread README.md
 BREAKING CHANGE: GetComponentsVersion is now a method on ComponentHelper
  instead of a standalone function. ComponentVersionCfg has been renamed to
  Cfg. Context and input are now passed as method parameters.
@agustingroh agustingroh force-pushed the refactor/component-helper-struct branch from dc4cc10 to e2b8fc6 Compare April 7, 2026 18:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
componenthelper/component.go (1)

70-73: ⚠️ Potential issue | 🟠 Major

Avoid mutating ComponentHelper.maxWorkers inside request execution path.

This concern is still present: mutating c.maxWorkers here creates persistent side effects across calls and can race when the same helper is used concurrently. Use a local variable instead.

Proposed fix
-	if c.maxWorkers <= 0 {
-		c.maxWorkers = MaxWorkers
-	}
-	numWorkers := min(c.maxWorkers, numJobs)
+	maxWorkers := c.maxWorkers
+	if maxWorkers <= 0 {
+		maxWorkers = MaxWorkers
+	}
+	numWorkers := min(maxWorkers, numJobs)
#!/bin/bash
# Verify whether GetComponentsVersion mutates helper state.
rg -n -C2 'c\.maxWorkers\s*=' componenthelper/component.go
rg -n -C2 'numWorkers\s*:=\s*min\(c\.maxWorkers,\s*numJobs\)' componenthelper/component.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/component.go` around lines 70 - 73, The code mutates
c.maxWorkers during request handling (c.maxWorkers <= 0 { c.maxWorkers =
MaxWorkers }) which creates persistent state and races; instead, inside the
request path (e.g., in GetComponentsVersion or the function using c), capture
into a local variable like maxWorkers := c.maxWorkers, then if maxWorkers <= 0
set the local to MaxWorkers, and compute numWorkers := min(maxWorkers, numJobs)
using that local; keep all changes local and do not assign back to c.maxWorkers
to avoid side effects and races.
🧹 Nitpick comments (1)
componenthelper/semver_test.go (1)

169-210: Consider adding a <= operator case for completeness.

HasSemverOperator is well covered; adding <= would close the remaining common-operator gap and protect against regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@componenthelper/semver_test.go` around lines 169 - 210, Test suite
TestHasSemverOperator is missing a case for the "<=" operator; add a test case
inside the testCases slice (in TestHasSemverOperator) that uses input "<=1.2.3"
and expected true so HasSemverOperator is exercised for the less-than-or-equal
operator and the common-operator gap is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@componenthelper/component.go`:
- Around line 70-73: The code mutates c.maxWorkers during request handling
(c.maxWorkers <= 0 { c.maxWorkers = MaxWorkers }) which creates persistent state
and races; instead, inside the request path (e.g., in GetComponentsVersion or
the function using c), capture into a local variable like maxWorkers :=
c.maxWorkers, then if maxWorkers <= 0 set the local to MaxWorkers, and compute
numWorkers := min(maxWorkers, numJobs) using that local; keep all changes local
and do not assign back to c.maxWorkers to avoid side effects and races.

---

Nitpick comments:
In `@componenthelper/semver_test.go`:
- Around line 169-210: Test suite TestHasSemverOperator is missing a case for
the "<=" operator; add a test case inside the testCases slice (in
TestHasSemverOperator) that uses input "<=1.2.3" and expected true so
HasSemverOperator is exercised for the less-than-or-equal operator and the
common-operator gap is covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f09144da-4709-4bce-9c4e-f636c2949edd

📥 Commits

Reviewing files that changed from the base of the PR and between afddd93 and e2b8fc6.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • README.md
  • componenthelper/component.go
  • componenthelper/component_entity.go
  • componenthelper/component_test.go
  • componenthelper/semver.go
  • componenthelper/semver_test.go
  • componenthelper/utils/semver_test.go
💤 Files with no reviewable changes (1)
  • componenthelper/utils/semver_test.go
✅ Files skipped from review due to trivial changes (2)
  • componenthelper/semver.go
  • componenthelper/component_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • CHANGELOG.md

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.

2 participants