feat(cli): add version update notification#2491
Conversation
Add non-blocking version check that compares the running binary version against the latest published GitHub release. Warns to stderr when a newer stable version is available, with results cached locally for 24h. Features: - Async check via goroutine + cobra.OnFinalize (no CLI slowdown) - 24h cache in XDG_CACHE_HOME/ocm/version-check.json - Rate-limited warnings (max once per 24h) - Silent failure on network errors / offline environments - Opt-out via OCM_DISABLE_VERSION_CHECK=1, --version-check=disable flag, or versioncheck.config.ocm.software/v1alpha1 config type - Skips dev builds (version "n/a") and the version command itself - Only considers stable cli/* tagged releases (excludes drafts/prereleases) Resolves: open-component-model/ocm-project#1044 On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Simplify opt-out mechanism to env var (OCM_DISABLE_VERSION_CHECK) and --version-check=disable flag only. Config type was over-engineered for the current use case. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Reverts the removal of the versioncheck.config.ocm.software/v1alpha1 config type. All three opt-out mechanisms are retained: - OCM_DISABLE_VERSION_CHECK env var - --version-check=disable flag - Config file with disabled: true On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
…b API Tests verify: - Fetching latest version from real GitHub releases - Update detection with old version (0.0.1) - No false positive with future version (999.999.999) - Opt-out via OCM_DISABLE_VERSION_CHECK env var - Opt-out via --version-check=disable flag - Cache persistence across calls On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Add comprehensive documentation: - doc.go with package-level overview of behavior, release filtering, opt-out mechanisms, and cache location - GoDoc comments on all exported types, functions, and constants - Inline comments explaining non-obvious implementation details Also uncomments the config scheme registration which is required for the config type to be decodable at runtime. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Replace raw fmt.Fprintf to stderr with structured slog.Warn call. This respects the user's --loglevel, --logformat, and --logoutput preferences. Users who set --loglevel=error will no longer see version notifications, which is consistent with opting out of warnings. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
✅ Deploy Preview for ocm-website ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a non-blocking CLI version check that queries GitHub Releases for the latest stable CLI tag, caches results on-disk (24h), rate-limits stderr warnings (24h), supports env/config opt-out, runs asynchronously on command start, and defers printing a structured warning until command finalize. ChangesCLI Version Check Feature
Sequence DiagramsequenceDiagram
participant CLI as CLI Command
participant VC as VersionCheck
participant C as Cache
participant GH as GitHub API
participant F as cobra.OnFinalize
participant S as slog.Warn
CLI->>VC: VersionCheck(cmd, BuildVersion) (start background goroutine)
VC->>C: ReadCache(cacheDir)
alt cache fresh
C-->>VC: cached entry (no network)
else cache stale/missing
VC->>GH: GET /repos/:owner/:repo/releases
GH-->>VC: releases JSON
VC->>C: WriteCache(updated entry)
end
CLI->>F: command completes, finalize triggers
F->>VC: wait for result (short timeout)
alt UpdateAvailable
VC->>S: slog.Warn(current, available, releases URL)
VC->>C: MarkWarned(cacheDir)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…ck config Change the config from `disabled: true` to `policy: disable|auto`. This aligns with the CLI flag pattern and enables a layered override model: 1. Environment variable (OCM_DISABLE_VERSION_CHECK) — hard override 2. CLI flag (--version-check=auto|disable) — overrides config when explicitly set 3. Config file policy — sets the default baseline This allows administrators to disable version checks org-wide via config while individual users can re-enable with --version-check=auto. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Replace `_ = WriteCache(...)` with proper error logging via slog.Debug. If cache writes fail consistently, users would get network calls on every invocation — logging makes this diagnosable. ReadCache errors are still discarded intentionally (cache miss on first run is expected flow, not an error condition). On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
Remove the --version-check persistent flag from the root command. Opt-out is now handled exclusively via: 1. OCM_DISABLE_VERSION_CHECK environment variable 2. OCM config file with policy: disable The flag polluted every subcommand's help output unnecessarily. Config-based control is sufficient for persistent preferences. Regenerated command reference documentation. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
cli/integration/version_check_integration_test.go (1)
41-60: ⚡ Quick winTest does not verify its stated purpose.
The test name
Test_Integration_VersionCheck_PrintsWarningToStderrimplies it verifies that a warning is printed, but the comment on lines 57-59 acknowledges that "the warning may or may not appear depending on timing" and the test only verifies thatExecute()doesn't error.Either:
- Fix the test to reliably verify the warning appears (e.g., add a small sleep or synchronization mechanism after
Execute()completes and before checking stderr)- Rename the test to reflect what it actually verifies (e.g.,
Test_Integration_VersionCheck_DoesNotErrorWithOldVersion)🤖 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 `@cli/integration/version_check_integration_test.go` around lines 41 - 60, The test Test_Integration_VersionCheck_PrintsWarningToStderr currently sets version.BuildVersion and captures stderr but never asserts that a warning was printed (it only checks rootCmd.Execute() succeeds), so either make the test deterministic by waiting for the async version check to complete and then assert stderr contains the expected warning, or rename the test to reflect current behavior; to fix, either add synchronization (e.g., have the version check signal completion or poll/wait briefly after rootCmd.Execute() before reading stderr) and assert stderr.String() includes the warning text, or change the test name to something like Test_Integration_VersionCheck_DoesNotErrorWithOldVersion so it matches the existing assertion that rootCmd.Execute() returns no error (use the existing symbols Test_Integration_VersionCheck_PrintsWarningToStderr, rootCmd.Execute, stderr, and version.BuildVersion to locate and update the test).cli/internal/versioncheck/cache_test.go (2)
10-45: ⚡ Quick winConsider using fixed timestamps for deterministic tests.
The test uses
time.Now()both when constructing test cases and when callingIsFresh(). If the system clock advances between these calls (even by microseconds), the test assertions might occasionally fail or pass unexpectedly.♻️ Proposed fix using fixed timestamps
func TestCacheEntry_IsFresh(t *testing.T) { + now := time.Date(2026, 5, 12, 12, 0, 0, 0, time.UTC) tests := []struct { name string checkedAt time.Time - now time.Time want bool }{ { name: "fresh cache", - checkedAt: time.Now().Add(-1 * time.Hour), - now: time.Now(), + checkedAt: now.Add(-1 * time.Hour), want: true, }, { name: "stale cache", - checkedAt: time.Now().Add(-25 * time.Hour), - now: time.Now(), + checkedAt: now.Add(-25 * time.Hour), want: false, }, { name: "exactly at boundary", - checkedAt: time.Now().Add(-24 * time.Hour), - now: time.Now(), + checkedAt: now.Add(-24 * time.Hour), want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { entry := &CacheEntry{CheckedAt: tt.checkedAt} - if got := entry.IsFresh(tt.now); got != tt.want { + if got := entry.IsFresh(now); got != tt.want { t.Errorf("IsFresh() = %v, want %v", got, tt.want) } }) } }🤖 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 `@cli/internal/versioncheck/cache_test.go` around lines 10 - 45, The test TestCacheEntry_IsFresh is non-deterministic because it calls time.Now() multiple times; replace those dynamic calls with a single fixed base time (e.g. base := time.Date(...)) and build each test case's checkedAt and now relative to that base so CacheEntry{CheckedAt: ...} and entry.IsFresh(now) use deterministic timestamps; update the three cases (fresh, stale, exactly at boundary) to use base.Add(...) offsets to preserve the intended expectations for CacheEntry.IsFresh.
47-82: ⚡ Quick winConsider using fixed timestamps for deterministic tests.
Similar to
TestCacheEntry_IsFresh, this test usestime.Now()in test case construction, which could lead to flaky behavior.♻️ Proposed fix using fixed timestamps
func TestCacheEntry_ShouldWarn(t *testing.T) { + now := time.Date(2026, 5, 12, 12, 0, 0, 0, time.UTC) tests := []struct { name string warnedAt time.Time - now time.Time want bool }{ { name: "never warned", warnedAt: time.Time{}, - now: time.Now(), want: true, }, { name: "warned recently", - warnedAt: time.Now().Add(-1 * time.Hour), - now: time.Now(), + warnedAt: now.Add(-1 * time.Hour), want: false, }, { name: "warned long ago", - warnedAt: time.Now().Add(-25 * time.Hour), - now: time.Now(), + warnedAt: now.Add(-25 * time.Hour), want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { entry := &CacheEntry{WarnedAt: tt.warnedAt} - if got := entry.ShouldWarn(tt.now); got != tt.want { + if got := entry.ShouldWarn(now); got != tt.want { t.Errorf("ShouldWarn() = %v, want %v", got, tt.want) } }) } }🤖 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 `@cli/internal/versioncheck/cache_test.go` around lines 47 - 82, The test TestCacheEntry_ShouldWarn is using time.Now() which makes it flaky; update the test to use fixed deterministic timestamps (e.g., base := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)) and create warnedAt and now values relative to that base (e.g., base, base.Add(-1*time.Hour), base.Add(-25*time.Hour)) when constructing the test cases, then use those values to initialize CacheEntry{WarnedAt: ...} and pass now into entry.ShouldWarn so the expectations are stable.cli/internal/versioncheck/check.go (2)
160-220: ⚖️ Poor tradeoffConsider documenting the single-page limitation.
The implementation fetches only the first page of releases (20 items). If more than 20 releases have been published since the last stable CLI release, the version check will fail to find an update.
While this is unlikely given the PR description notes that the latest stable release is "typically within the first page," adding a code comment near line 163 could help future maintainers understand this design choice:
func fetchLatestVersion(ctx context.Context, opts Options) (string, error) { + // Note: We fetch only the first page of releases. If the latest stable CLI + // release is beyond the first 20 releases, it will not be detected. url := fmt.Sprintf("%s/repos/%s/%s/releases?per_page=%d", opts.BaseURL, opts.GitHubOwner, opts.GitHubRepo, releasesPerPage)🤖 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 `@cli/internal/versioncheck/check.go` around lines 160 - 220, In fetchLatestVersion, add a concise comment near the URL/release-per-page construction (around the releasesPerPage usage) documenting that the function intentionally only requests the first page of GitHub releases (per_page=releasesPerPage) and therefore may miss updates if more than that many releases exist; mention the chosen page-size (releasesPerPage) and the rationale/assumption (latest stable release is typically on page one) and note that paging could be added later if needed so future maintainers understand this limitation.
99-103: 💤 Low valueConsider logging cache read errors for debugging.
The code silently ignores cache read errors. While this aligns with the "silent failure" requirement, distinguishing between "cache not found" (expected on first run) and "permission denied" or "corrupted cache" (unexpected issues) could help users debug cache problems.
♻️ Optional: Add debug logging for cache read errors
// Use cached result if the last check was recent enough. - cache, _ := ReadCache(cacheDir) + cache, err := ReadCache(cacheDir) + if err != nil && !os.IsNotExist(err) { + slog.Debug("version check: cache read failed", slog.String("error", err.Error())) + } if cache != nil && cache.IsFresh(now) { return compareVersions(current, cache.LatestVersion) }🤖 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 `@cli/internal/versioncheck/check.go` around lines 99 - 103, The ReadCache call currently ignores its error, so permission or corruption issues are silently lost; update the logic around ReadCache(cacheDir) in check.go to capture the returned error, and when an error is non-nil, log or debug-log it (distinguish expected "not found" vs unexpected errors) before proceeding with the existing fallback; keep the existing behavior of using cache only when cache != nil && cache.IsFresh(now) and then calling compareVersions(current, cache.LatestVersion).
🤖 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 `@cli/integration/version_check_integration_test.go`:
- Around line 14-26: The integration test
Test_Integration_VersionCheck_FetchesLatestFromGitHub directly calls the live
GitHub API and runs in parallel, so add a short-mode skip at the start of the
test to avoid flaky CI runs: check testing.Short() and call t.Skip("skipping
GitHub API integration test in short mode") when true; apply the same pattern to
the other integration tests referenced in this file (the other functions around
the indicated ranges) to ensure live-GitHub tests are skipped during
short/test-fast runs while keeping the rest of the test logic
(versioncheck.Check, options, assertions) unchanged.
In `@cli/internal/versioncheck/config.go`:
- Around line 68-76: The loop decodes each entry into Config but only checks for
non-empty Policy and can return invalid values; update the block after
configScheme.Convert to validate that config.Policy is exactly one of the
allowed constants (PolicyAuto or PolicyDisable) and if not return an error (e.g.
fmt.Errorf("invalid versioncheck policy: %q", config.Policy)); keep the existing
non-empty check but replace the unconditional return of &config with this
validation so only permitted policy values are accepted.
---
Nitpick comments:
In `@cli/integration/version_check_integration_test.go`:
- Around line 41-60: The test
Test_Integration_VersionCheck_PrintsWarningToStderr currently sets
version.BuildVersion and captures stderr but never asserts that a warning was
printed (it only checks rootCmd.Execute() succeeds), so either make the test
deterministic by waiting for the async version check to complete and then assert
stderr contains the expected warning, or rename the test to reflect current
behavior; to fix, either add synchronization (e.g., have the version check
signal completion or poll/wait briefly after rootCmd.Execute() before reading
stderr) and assert stderr.String() includes the warning text, or change the test
name to something like Test_Integration_VersionCheck_DoesNotErrorWithOldVersion
so it matches the existing assertion that rootCmd.Execute() returns no error
(use the existing symbols Test_Integration_VersionCheck_PrintsWarningToStderr,
rootCmd.Execute, stderr, and version.BuildVersion to locate and update the
test).
In `@cli/internal/versioncheck/cache_test.go`:
- Around line 10-45: The test TestCacheEntry_IsFresh is non-deterministic
because it calls time.Now() multiple times; replace those dynamic calls with a
single fixed base time (e.g. base := time.Date(...)) and build each test case's
checkedAt and now relative to that base so CacheEntry{CheckedAt: ...} and
entry.IsFresh(now) use deterministic timestamps; update the three cases (fresh,
stale, exactly at boundary) to use base.Add(...) offsets to preserve the
intended expectations for CacheEntry.IsFresh.
- Around line 47-82: The test TestCacheEntry_ShouldWarn is using time.Now()
which makes it flaky; update the test to use fixed deterministic timestamps
(e.g., base := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)) and create warnedAt
and now values relative to that base (e.g., base, base.Add(-1*time.Hour),
base.Add(-25*time.Hour)) when constructing the test cases, then use those values
to initialize CacheEntry{WarnedAt: ...} and pass now into entry.ShouldWarn so
the expectations are stable.
In `@cli/internal/versioncheck/check.go`:
- Around line 160-220: In fetchLatestVersion, add a concise comment near the
URL/release-per-page construction (around the releasesPerPage usage) documenting
that the function intentionally only requests the first page of GitHub releases
(per_page=releasesPerPage) and therefore may miss updates if more than that many
releases exist; mention the chosen page-size (releasesPerPage) and the
rationale/assumption (latest stable release is typically on page one) and note
that paging could be added later if needed so future maintainers understand this
limitation.
- Around line 99-103: The ReadCache call currently ignores its error, so
permission or corruption issues are silently lost; update the logic around
ReadCache(cacheDir) in check.go to capture the returned error, and when an error
is non-nil, log or debug-log it (distinguish expected "not found" vs unexpected
errors) before proceeding with the existing fallback; keep the existing behavior
of using cache only when cache != nil && cache.IsFresh(now) and then calling
compareVersions(current, cache.LatestVersion).
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 84cd3cad-2d6b-4c96-86a8-798909d11a0f
📒 Files selected for processing (14)
cli/cmd/setup/hooks/pre_run.gocli/cmd/setup/versioncheck.gocli/integration/version_check_integration_test.gocli/internal/versioncheck/cache.gocli/internal/versioncheck/cache_test.gocli/internal/versioncheck/check.gocli/internal/versioncheck/check_test.gocli/internal/versioncheck/config.gocli/internal/versioncheck/config_test.gocli/internal/versioncheck/doc.gocli/internal/versioncheck/schemas/Config.schema.jsoncli/internal/versioncheck/zz_generated.deepcopy.gocli/internal/versioncheck/zz_generated.ocm_jsonschema.gocli/internal/versioncheck/zz_generated.ocm_type.go
- Add runtime validation for policy values in config (reject invalid) - Add testing.Short() skip for integration tests hitting GitHub API - Use fixed timestamps in cache tests for determinism - Rename misleading test to reflect actual behavior - Log non-NotExist cache read errors via slog.Debug On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
- Use strconv.ParseBool for env var parsing (supports all Go bool formats) - Rename printWarning → printUpgradeWarning for clarity - Add comment explaining why draft/prerelease filtering is client-side (GitHub list API has no server-side filter for these fields) On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
cli/integration/version_check_integration_test.go (1)
47-61: 💤 Low valueMutating
version.BuildVersion(a package global) inside at.Parallel()test is racy.
version.BuildVersionis a process-wide variable. If any other parallel test in this package (now or later) reads or writes it, the writes here race with those reads under-race. The current siblings happen to be safe becauseTest_Integration_VersionCheck_DisabledByEnvVarcallst.Setenv(which forces serial execution), but this is fragile — adding any future parallel test that touchesBuildVersion(or any cmd path that reads it) reintroduces the race.Two safer options:
- Drop
t.Parallel()from tests that mutate the global, or- Plumb the version via an option/parameter instead of mutating the package var.
🤖 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 `@cli/integration/version_check_integration_test.go` around lines 47 - 61, Test_Integration_VersionCheck_DoesNotErrorWithOldVersion mutates the process-global version.BuildVersion while calling t.Parallel(), which creates a data race; remove the t.Parallel() call from this test so the global mutation and cleanup are executed serially, or alternatively refactor the code path (e.g., cmd.New or the command execution) to accept an injected version value instead of reading version.BuildVersion so the test can pass a custom version without touching the package global; update Test_Integration_VersionCheck_DoesNotErrorWithOldVersion to use the chosen approach and keep the existing cleanup that restores version.BuildVersion if you keep the global mutation.
🤖 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 `@cli/cmd/setup/versioncheck.go`:
- Around line 99-112: The G706 false positive comes from logging the env var
value in envDisabled; update envDisabled to avoid logging the raw
user-controlled value by removing the slog.String("value", val) attribute from
the slog.Debug call (keep slog.String("var", VersionCheckEnvVar) and the
message) or alternatively add an inline suppression comment on the slog.Debug
line like //nolint:gosec // G706 false positive: slog.String is structured and
not a format directive; reference symbols: envDisabled, VersionCheckEnvVar, val,
slog.String.
In `@cli/integration/version_check_integration_test.go`:
- Around line 47-80: The tests use "--help" which short-circuits before
PersistentPreRunE so setup.VersionCheck inside PreRunEWithConfig() never runs;
update both Test_Integration_VersionCheck_DoesNotErrorWithOldVersion and
Test_Integration_VersionCheck_DisabledByEnvVar to call a real subcommand (for
example set rootCmd.SetArgs([]string{"version"}) or another subcommand that
triggers PersistentPreRunE) instead of "--help" so PreRunEWithConfig() and
setup.VersionCheck are executed, or alternatively attach a capturing slog
handler to the logger and directly invoke PreRunEWithConfig()/setup.VersionCheck
to assert the version-check behavior and stderr output; ensure you still mutate
version.BuildVersion and the env var OCM_DISABLE_VERSION_CHECK as the tests
expect.
---
Nitpick comments:
In `@cli/integration/version_check_integration_test.go`:
- Around line 47-61: Test_Integration_VersionCheck_DoesNotErrorWithOldVersion
mutates the process-global version.BuildVersion while calling t.Parallel(),
which creates a data race; remove the t.Parallel() call from this test so the
global mutation and cleanup are executed serially, or alternatively refactor the
code path (e.g., cmd.New or the command execution) to accept an injected version
value instead of reading version.BuildVersion so the test can pass a custom
version without touching the package global; update
Test_Integration_VersionCheck_DoesNotErrorWithOldVersion to use the chosen
approach and keep the existing cleanup that restores version.BuildVersion if you
keep the global mutation.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bd39a6af-841d-42d8-9f55-36af5f4c7a87
📒 Files selected for processing (6)
cli/cmd/setup/versioncheck.gocli/integration/version_check_integration_test.gocli/internal/versioncheck/cache_test.gocli/internal/versioncheck/check.gocli/internal/versioncheck/config.gocli/internal/versioncheck/config_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
- cli/internal/versioncheck/config_test.go
- cli/internal/versioncheck/cache_test.go
- cli/internal/versioncheck/config.go
- cli/internal/versioncheck/check.go
- Replace slog.String("value", val) with slog.String("error", err.Error())
to avoid gosec G706 log injection false positive
- Fix integration tests to use "version" subcommand with empty config
instead of --help (which skips PersistentPreRunE entirely)
- Pass --config with empty YAML to avoid loading user's local OCM config
which may reference unavailable plugins
On-behalf-of: @SAP <jakob.moeller@sap.com>
Signed-off-by: Jakob Möller <contact@jakob-moeller.com>
On-behalf-of: @SAP <jakob.moeller@sap.com>
Remove the error message from slog.Debug call since gosec considers strconv.ParseBool error output as tainted (derived from user input). Invalid env var values now silently treated as "disabled" for safety. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@cli/integration/version_check_integration_test.go`:
- Around line 49-66: The tests (e.g.,
Test_Integration_VersionCheck_DoesNotErrorWithOldVersion and the similar test
covering lines 68-84) are leaking the real XDG cache; before calling
rootCmd.ExecuteContext(t.Context()) set the XDG_CACHE_HOME environment variable
to a temporary dir (use t.TempDir()) and restore it via t.Cleanup or defer, so
the version-check cache is isolated to the test workspace; ensure this is done
in both tests and, after execution, the env-based case can assert no real cache
file was written.
- Around line 96-113: The test currently only compares LatestVersion between two
versioncheck.Check calls which doesn't prove the cache was used; modify the test
around versioncheck.Check and versioncheck.ReadCache to capture the
cache.CheckedAt value after the first call (e.g., firstCheckedAt :=
cache.CheckedAt) and then call versioncheck.Check a second time and re-read the
cache and assert that cache.CheckedAt equals firstCheckedAt to ensure the second
call did not update the cache timestamp; reference versioncheck.Check,
versioncheck.ReadCache, cache.LatestVersion and cache.CheckedAt when adding the
assertion.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a0a14398-00e7-41cb-a958-e8897f4ac3fa
📒 Files selected for processing (2)
cli/cmd/setup/versioncheck.gocli/integration/version_check_integration_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- cli/cmd/setup/versioncheck.go
- Cache fetch failures with CheckedAt set so offline/firewalled users don't face network delays on every CLI invocation (respects 24h TTL) - Log fetch errors via slog.Debug for debuggability - Remove t.Parallel() from tests that mutate version.BuildVersion global - Isolate XDG_CACHE_HOME in integration tests to prevent cache leakage - Assert cache.CheckedAt unchanged on second call to prove cache hit On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
bf0cca7 to
135320a
Compare
The CLI flag was dropped but the Config doc comment still referenced it. Updated to accurately describe the config-only opt-out mechanism. On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com> Signed-off-by: Jakob Möller <contact@jakob-moeller.com> On-behalf-of: @SAP <jakob.moeller@sap.com>
135320a to
03180ce
Compare
Summary
Adds a non-blocking version update notification to the OCM CLI that warns users when a newer stable release is available.
cobra.OnFinalize— no CLI slowdowncli/v*GitHub release$XDG_CACHE_HOME/ocm/version-check.jsonslog.Warn(max once per 24h)Opt-out mechanisms (priority order)
OCM_DISABLE_VERSION_CHECK=1environment variablethis was cut during review--version-check=disablepersistent flagversioncheck.cli.config.ocm.software/v1alpha1withdisabled: trueSkip conditions
BuildVersion == "n/a")ocm versioncommand itselfResolves: open-component-model/ocm-project#1044
Test plan
go build ./...passesgo vet ./...passesBuildVersion=0.0.1shows slog warningOCM_DISABLE_VERSION_CHECK=1suppresses warning