Skip to content

[CFX-5210] Implement plugin update check functionality and state management#402

Open
cdevent wants to merge 8 commits intomainfrom
cdevent/plugin-upgrade-flow-CFX-5210
Open

[CFX-5210] Implement plugin update check functionality and state management#402
cdevent wants to merge 8 commits intomainfrom
cdevent/plugin-upgrade-flow-CFX-5210

Conversation

@cdevent
Copy link
Contributor

@cdevent cdevent commented Mar 18, 2026

RATIONALE

This pull request introduces a new automatic plugin update check feature for managed plugins, along with user prompts and cooldowns to prevent repeated notifications. It also adds configuration options for controlling the update check interval and enables robust error handling and rollback for plugin updates. The changes are grouped into new functionality, configuration improvements, and test coverage.

CHANGES

New plugin update check functionality:

  • Added a new internal/plugin/updatecheck.go module implementing plugin update checks, cooldown logic, version comparison, and registry fetch with timeout. This checks for updates before running managed plugins and prompts the user to upgrade if a newer version is available.
  • Integrated update check and prompt logic into plugin command execution in cmd/root.go, including user interaction, backup, install, validation, and rollback on failure. Cooldown is reset regardless of user action or update outcome. [1] [2] [3]

Configuration and usability improvements:

  • Added new CLI flags and Viper bindings for plugin-update-check-interval (controls cooldown between update checks) and skip-plugin-update-check (disables pre-run checks), with sensible defaults and documentation. [1] [2]
  • Improved managed plugin detection to ensure update checks are only performed for plugins installed under the managed directory.

Test coverage and reliability:

  • Added a comprehensive test suite in internal/plugin/updatecheck_test.go covering all cases: disabled checks, cooldowns, network errors, version comparisons (including non-semver), registry errors, and successful update detection.
  • Refactored registry fetch logic to support context-based cancellation and timeout, improving reliability and responsiveness.

These changes collectively make plugin management more user-friendly and robust, reducing unnecessary prompts and handling failures gracefully.

PR Automation

Comment-Commands: Trigger CI by commenting on the PR:

  • /trigger-smoke-test or /trigger-test-smoke - Run smoke tests
  • /trigger-install-test or /trigger-test-install - Run installation tests

Labels: Apply labels to trigger workflows:

  • run-smoke-tests or go - Run smoke tests on demand (only works for non-forked PRs)

Important

For Forked PRs: If you're an external contributor, the run-smoke-tests label won't work. Only maintainers can trigger smoke tests on forked PRs by applying the approved-for-smoke-tests label after security review. Please comment requesting maintainer review if you need smoke tests to run.


Note

Medium Risk
Adds new pre-execution network-based update checks and an interactive upgrade prompt for managed plugins, plus new persisted global state; failures could affect plugin startup UX and update behavior despite rollback safeguards.

Overview
Adds an automatic managed-plugin update check that runs before executing discovered plugins, compares installed vs latest registry version, and prompts the user to upgrade (with --skip-plugin-update-check and configurable --plugin-update-check-interval).

Introduces a new user-global state file (~/.config/datarobot/state.yaml) to persist per-plugin cooldown timestamps, and refactors registry fetching to support context/timeouts for fast, non-blocking checks.

Refactors the manual dr plugin update flow to reuse a shared RunPluginUpdate (backup → install → validate → rollback) implementation, adds a reusable stdin AskYesNo helper, and includes new unit tests for update checking and global state persistence.

Written by Cursor Bugbot for commit db0569a. This will update automatically on new commits. Configure here.

feat: add state management for the version checks and cooldown for the updates
@github-actions github-actions bot added the go Pull requests that update go code label Mar 18, 2026
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

DisableSuggestions: true,
Run: func(_ *cobra.Command, args []string) {
checkAndPromptPluginUpdate(pluginName, manifest.Version, pluginPath)

Copy link

Choose a reason for hiding this comment

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

Stale manifest used after successful in-place plugin update

Medium Severity

After checkAndPromptPluginUpdate successfully updates the plugin on disk, ExecutePlugin is called with the manifest struct captured at command-registration time. This stale manifest may have an outdated Authentication field, causing the auth check to be skipped (or unnecessarily enforced) for the newly installed version. The Version in the user-agent header will also be wrong. The issue only affects the first execution immediately after the in-place update.

Additional Locations (1)
Fix in Cursor Fix in Web


const (
// DefaultUpdateCheckInterval is the default cooldown between update checks for a given plugin.
DefaultUpdateCheckInterval = 24 * time.Hour
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should stick to a lower value? We sometimes release twice a day... So maybe 1 hour... What's the downside of a lower value?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think there's a downside. Given this is configurable.

Copy link
Contributor

@victorborshak victorborshak left a comment

Choose a reason for hiding this comment

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

LGTM. Left a comment about using 1 hour instead of 24 hour cooldown

@ajalon1 ajalon1 self-assigned this Mar 20, 2026
Copy link
Contributor

@ajalon1 ajalon1 left a comment

Choose a reason for hiding this comment

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

@cdevent Requesting changes so that you can make sure to update docs/. You can do it in this PR or the next, but please do so since this is a large bit of functionality.

Still reviewing.

Also, agree w. Victor let's set it to one hour. That still feels slow enough.

Copy link
Contributor

@ajalon1 ajalon1 left a comment

Choose a reason for hiding this comment

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

Leaving Request Changes in place for these reasons:

  1. update the docs for the new global state.yaml
  2. update the docs to document new configs
  3. consider defer state.SetLastPluginCheck() in root
  4. change DefaultUpdateCheckInterval to ~1 hour like @victorborshak suggested

Any other changes or suggestions I'm fine with handling later

"gopkg.in/yaml.v3"
)

const globalStateFileName = "state.yaml"
Copy link
Contributor

Choose a reason for hiding this comment

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

😁 If we haven't already, we should standardize on .yaml ;)

Comment on lines +331 to +337
if !askYesNo() {
// User declined — reset cooldown so they are not nagged again
state.SetLastPluginCheck(pluginName)
fmt.Println()

return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest a log.Debug() here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this file is getting large enough that we might pull plugin code into a separate module. Not a blocker.

Comment on lines +311 to +313
if viper.GetBool("skip-plugin-update-check") {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for checking from viper ;)

Comment on lines +315 to +323
// Only check managed plugins (those under ~/.config/datarobot/plugins/)
if !isManagedPlugin(pluginPath) {
return
}

result := internalPlugin.CheckForUpdate(pluginName, installedVersion, internalPlugin.PluginRegistryURL)
if result == nil {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This in particularly feels like it could go under plugin/shared/helpers. But maybe it's fine here for now.

Comment on lines +316 to +318
if !isManagedPlugin(pluginPath) {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest a log.Debug() here.


if !askYesNo() {
// User declined — reset cooldown so they are not nagged again
state.SetLastPluginCheck(pluginName)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can defer state.SetLastPluginCheck(pluginName) earlier, say before we call internalPlugin.CheckForUpdate.

That way we don't have to invoke it twice here on line 333 and on line 342.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

00 - Ready for Review go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants