Skip to content

Vscode persistence - #282

Merged
agrasth merged 5 commits into
jfrog:mainfrom
agrasth:vscodePersistence
Oct 29, 2025
Merged

agrasth merged 5 commits into
jfrog:mainfrom
agrasth:vscodePersistence

Conversation

@agrasth

@agrasth agrasth commented Oct 15, 2025 •

Copy link
Copy Markdown
Collaborator
  • All tests passed. If this feature is not already covered by the tests, I added new tests.
  • All static analysis checks passed.
  • Appropriate label is added to auto generate release notes.
  • I used gofmt for formatting the code before submitting the pull request.
  • PR description is clear and concise, and it includes the proposed solution/fix.

Introduced a new IDE integration system for configuring VSCode-based editors (VSCode, Cursor, Windsurf) and JetBrains IDEs to use JFrog Artifactory as their extension/plugin marketplace.

New Commands

jf ide setup vscode --repo-key <key>      # Configure VSCode
jf ide setup cursor --repo-key <key>      # Configure Cursor
jf ide setup windsurf --repo-key <key>    # Configure Windsurf
jf ide setup jetbrains --repo-key <key>   # Configure JetBrains IDEs

Architecture & Design Patterns

1. Configuration-Based Design (VSCode Forks)

Uses a registry pattern to handle multiple VSCode-based IDEs with a single implementation:

aieditorextensions/
├── vscode_fork_registry.go    # Configuration registry
├── vscode_fork.go              # Single generic implementation
└── vscode_setup.go             # Entry points (SetupVSCode, SetupCursor, SetupWindsurf)

2. Common Package Pattern

Extracted shared logic into ide/common/ to eliminate duplication:

ide/
├── common/base.go              # Shared: Server config, validation, URL building
├── commands/
│   ├── aieditorextensions/     # AI Editor Extensions specific
│   └── jetbrains/              # JetBrains specific

3. Unified Command Structure

Single setup command with IDE name as argument:

jf ide setup <IDE_NAME> [OPTIONS]

Routing:

cli.go
    ├─> vscode/cursor/windsurf  → aieditorextensions.Setup*()
    └─> jetbrains               → jetbrains.SetupJetBrains()

📁 Final Structure

ide/
├── cli/
│   ├── cli.go              # Command registration + setup command & routing
│   ├── cli_test.go         # CLI tests
│   └── common.go           # Shared server flags
│
├── common/
│   └── base.go             # Shared utilities (server, validation, URLs)
│
└── commands/
    ├── aieditorextensions/
    │   ├── base.go                 # Package-specific config
    │   ├── vscode_fork_registry.go # VSCode fork configurations
    │   ├── vscode_fork.go          # Generic implementation
    │   ├── vscode_setup.go         # Entry points
    │   ├── settings_helper.go      # settings.json handling
    │   ├── instructions.go         # Manual setup instructions
    │   ├── vscode_fork_test.go     # Tests
    │   └── settings_helper_test.go # Tests
    │
    └── jetbrains/
        ├── base.go             # Package-specific config
        ├── jetbrains.go        # JetBrains implementation
        ├── jetbrains_setup.go  # Entry point
        ├── instructions.go     # Manual setup instructions
        └── jetbrains_test.go   # Tests

Technical Details

API Types

  • aieditorextensions - VSCode, Cursor, Windsurf (all VSCode forks)
  • jetbrainsplugins - IntelliJ, PyCharm, WebStorm, etc.

VSCode Fork Registry

var VSCodeForks = map[string]*VSCodeForkConfig{
    "vscode":   { Name: "vscode", DisplayName: "Visual Studio Code", ... },
    "cursor":   { Name: "cursor", DisplayName: "Cursor", ... },
    "windsurf": { Name: "windsurf", DisplayName: "Windsurf", ... },
}

Common Package Functions

common.GetServerDetails()           // Server configuration
common.ValidateRepository()         // Repository validation (parameterized)
common.BuildURL()                   // Generic URL builder
common.ExtractRepoKeyFromURL()      // Parse repo key from URL

CLI Common Flags

cli.GetCommonServerFlags()          // Shared server flags (url, user, password, etc.)

Notes

  • VSCode-based IDEs: Modifies product.json and optionally settings.json
  • JetBrains IDEs: Modifies idea.properties files
  • Automatic backup/restore on configuration errors
  • Graceful fallback to manual instructions on permission issues

Related PRs

Comment thread ide/cli/cli.go Outdated
}
return errors.New(errorMsg)
// GetSetupCommand returns the setup command with IDE_NAME as argument
func GetSetupCommand() components.Command {

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.

let's not make a separate methods to get command components , can be done like this
func GetCommands() []components.Command { return []components.Command{ { Name: "setup", Description: setup.GetDescription(), Arguments: setup.GetArguments(), Flags: commands.GetSetupFlags(), Action: setupCmd, Aliases: []string{"s"}, Category: ideCategory, }, } }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

ok will update this

Comment thread ide/cli/cli.go Outdated
return errors.New(errorMsg)
}

func getSetupArguments() []components.Argument {

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.

move this to help doc section for ide and then use it like this in command components
Description: setup.GetDescription(), Arguments: setup.GetArguments(),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

great point, will update this.

Comment thread ide/cli/cli.go Outdated
return errors.New("IDE_NAME is required. Usage: jf ide setup <IDE_NAME>\nSupported IDEs: vscode, cursor, windsurf, jetbrains")
}

ideName := strings.ToLower(c.GetArgumentAt(0))

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.

we should not convert the ide name to lower case and validate , it should fail in case the command is jf ide setup VSCODE

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sorry I didn't get the second line, can you pls elaborate this point.

Comment thread ide/cli/cli.go Outdated
return []string{commands.IdeVSCode, commands.IdeJetBrains}
func setupCmd(c *components.Context) error {
if c.GetNumberOfArgs() == 0 {
return errors.New("IDE_NAME is required. Usage: jf ide setup <IDE_NAME>\nSupported IDEs: vscode, cursor, windsurf, jetbrains")

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.

i think for now only vscode and jetbrains are supported , please confirm this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This PR, I have also added support for cursor and windsurf.

Comment thread ide/cli/cli.go
return append(flags, ideSpecificFlags...)
}

func supportedIDEs() []string {

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.

why did we removed this method can we not use it and instead of adding every the suported ide's we can just use this unified method which will have the list of all the supported ide's

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

great point, added this method.

sudo jf ide setup %s --repo-key <your-repo-key>

Or with direct URL:
sudo jf ide setup %s '%s'

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.

why have we added %s two times?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sudo jf ide setup %s '%s'
                  ↑   ↑
                  │   └─ serviceURL
                  └───── ideName


// createBackup creates a backup of product.json
func (vc *VSCodeForkCommand) createBackup() error {
timestamp := time.Now().Format("20060102_150405")

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.

can you please check if this format is correct and the required timstamp format

}

// validateRepository validates that the repository exists and is the correct type
func (vc *VSCodeForkCommand) validateRepository() error {

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.

i think we have the same method for validating repository in jetbrains as well can you please check and make sure we don't have redundant code, let's make it exported and use it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is handled here.

DisplayName string // User-facing name (e.g., "Visual Studio Code", "Cursor")
InstallPaths map[string][]string // OS -> possible installation paths
ProductJson string // Relative path to product.json (usually just "product.json")
SettingsDir string // Settings directory name (e.g., "Code", "Cursor")

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.

let's remove these comments from struct fields

Comment thread ide/common/base.go
}

// ValidateRepository validates that the repository exists and is of the specified type
func ValidateRepository(repoKey string, rtDetails *config.ServerDetails, apiType string) error {

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.

we already have ValidateRepository method in other files and we just use this one everywhere

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Will update this thanks

Comment thread ide/common/base.go
return fmt.Errorf("repository '%s' is not of type '%s': %w", repoKey, apiType, err)
}

log.Info("Repository validation successful")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
log.Info("Repository validation successful")
log.Debug("Repository validation successful")

@agrasth
agrasth merged commit 348f357 into jfrog:main Oct 29, 2025
10 of 11 checks passed

This branch had an error being deployed

1 failed deployment
frogbot — 4277646a Deployed Oct 29, 2025 by agrasth via scan-pull-request #431
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants