diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1 @@ + diff --git a/ide/cli/cli.go b/ide/cli/cli.go index 29777fb4..7ea152ce 100644 --- a/ide/cli/cli.go +++ b/ide/cli/cli.go @@ -6,7 +6,7 @@ import ( "github.com/jfrog/jfrog-cli-artifactory/ide/commands/aieditorextensions" "github.com/jfrog/jfrog-cli-artifactory/ide/commands/jetbrains" - "github.com/jfrog/jfrog-cli-artifactory/ide/docs" + "github.com/jfrog/jfrog-cli-artifactory/ide/docs/setup" "github.com/jfrog/jfrog-cli-core/v2/plugins/components" "github.com/jfrog/jfrog-client-go/utils/log" ) @@ -28,8 +28,8 @@ func GetCommands() []components.Command { return []components.Command{ { Name: "setup", - Description: docs.GetDescription(), - Arguments: docs.GetArguments(), + Description: setup.GetDescription(), + Arguments: setup.GetArguments(), Flags: getSetupFlags(), Action: setupCmd, Aliases: []string{"s"}, diff --git a/ide/commands/aieditorextensions/instructions.go b/ide/commands/aieditorextensions/instructions.go index 4257fcf6..b6a2e4a0 100644 --- a/ide/commands/aieditorextensions/instructions.go +++ b/ide/commands/aieditorextensions/instructions.go @@ -9,23 +9,15 @@ Manual %s Setup Instructions: ================================= 1. Close %s completely - -2. Locate your %s installation directory and find product.json - -3. Open the product.json file in a text editor with appropriate permissions: - • macOS: sudo nano "/Contents/Resources/app/product.json" - • Windows: Run editor as Administrator - • Linux: sudo nano /path/to/resources/app/product.json - -4. Find the "extensionsGallery" section and modify the "serviceUrl": +2. Find product.json in your %s installation directory +3. Edit the "extensionsGallery" section: { "extensionsGallery": { "serviceUrl": "%s", ... } } - -5. Save the file and restart %s +4. Save and restart %s Service URL: %s `, ideName, ideName, ideName, serviceURL, ideName, serviceURL) @@ -59,12 +51,10 @@ Alternative: Install %s in a user-writable location like ~/Applications/`, func GetGenericPermissionError(ideName, serviceURL string) string { return fmt.Sprintf(`insufficient permissions to modify %s configuration. -To fix this, try running the command with elevated privileges: - sudo jf ide setup %s --repo-key - -Or with direct URL: - sudo jf ide setup %s '%s' +Try running with elevated privileges: + • Linux/macOS: sudo jf ide setup %s '%s' + • Windows: Run PowerShell as Administrator -Or use the manual setup instructions provided in the error output.`, - ideName, ideName, ideName, serviceURL) +Or use the manual setup instructions.`, + ideName, ideName, serviceURL) } diff --git a/ide/commands/aieditorextensions/vscode_fork.go b/ide/commands/aieditorextensions/vscode_fork.go index 75c8a6ce..0818589c 100644 --- a/ide/commands/aieditorextensions/vscode_fork.go +++ b/ide/commands/aieditorextensions/vscode_fork.go @@ -116,16 +116,19 @@ func (vc *VSCodeForkCommand) detectInstallation() (string, error) { paths := vc.forkConfig.GetAllInstallPaths() for _, path := range paths { - // Expand environment variables and home directory - expandedPath := os.ExpandEnv(path) - if strings.HasPrefix(expandedPath, "~") { - home, err := os.UserHomeDir() - if err == nil { - expandedPath = filepath.Join(home, expandedPath[2:]) + expandedPath := expandPath(path) + productJsonPath := filepath.Join(expandedPath, vc.forkConfig.ProductJson) + + // Handle glob patterns (e.g., for .vscode-server/bin/*) + if strings.Contains(productJsonPath, "*") { + matches, err := filepath.Glob(productJsonPath) + if err == nil && len(matches) > 0 { + // Use the first match + return matches[0], nil } + continue } - productJsonPath := filepath.Join(expandedPath, vc.forkConfig.ProductJson) if fileutils.IsPathExists(productJsonPath, false) { return productJsonPath, nil } @@ -134,6 +137,32 @@ func (vc *VSCodeForkCommand) detectInstallation() (string, error) { return "", fmt.Errorf("%s installation not found in standard locations", vc.forkConfig.DisplayName) } +// expandPath expands environment variables and home directory in a path +// Handles both Unix-style ($VAR, ~) and Windows-style (%VAR%) variables +func expandPath(path string) string { + // Handle home directory expansion + if strings.HasPrefix(path, "~") { + if home, err := os.UserHomeDir(); err == nil { + path = filepath.Join(home, path[2:]) + } + } + + // On Windows, manually expand common environment variables for reliability + // os.ExpandEnv() can be unreliable with %VAR% syntax on some Windows systems + if runtime.GOOS == "windows" { + path = strings.ReplaceAll(path, "%LOCALAPPDATA%", os.Getenv("LOCALAPPDATA")) + path = strings.ReplaceAll(path, "%APPDATA%", os.Getenv("APPDATA")) + path = strings.ReplaceAll(path, "%PROGRAMFILES%", os.Getenv("PROGRAMFILES")) + path = strings.ReplaceAll(path, "%PROGRAMFILES(X86)%", os.Getenv("PROGRAMFILES(X86)")) + path = strings.ReplaceAll(path, "%USERPROFILE%", os.Getenv("USERPROFILE")) + } + + // Try standard expansion for any remaining variables (Unix-style $VAR) + path = os.ExpandEnv(path) + + return filepath.Clean(path) +} + // checkWritePermissions verifies write access to product.json func (vc *VSCodeForkCommand) checkWritePermissions() error { file, err := os.OpenFile(vc.productPath, os.O_RDWR, 0644) diff --git a/ide/commands/aieditorextensions/vscode_fork_registry.go b/ide/commands/aieditorextensions/vscode_fork_registry.go index 8039263a..f2d29ac7 100644 --- a/ide/commands/aieditorextensions/vscode_fork_registry.go +++ b/ide/commands/aieditorextensions/vscode_fork_registry.go @@ -32,13 +32,18 @@ var VSCodeForks = map[string]*VSCodeForkConfig{ "~/Applications/Visual Studio Code.app/Contents/Resources/app", }, "windows": { - `C:\Program Files\Microsoft VS Code\resources\app`, `%LOCALAPPDATA%\Programs\Microsoft VS Code\resources\app`, + `C:\Program Files\Microsoft VS Code\resources\app`, + `C:\Program Files (x86)\Microsoft VS Code\resources\app`, + `%PROGRAMFILES%\Microsoft VS Code\resources\app`, + `%LOCALAPPDATA%\Code\resources\app`, }, "linux": { "/usr/share/code/resources/app", "/opt/visual-studio-code/resources/app", + "/snap/code/current/usr/share/code/resources/app", "~/.local/share/code/resources/app", + "~/.vscode-server/bin/*/resources/app", }, }, ProductJson: "product.json", @@ -54,7 +59,9 @@ var VSCodeForks = map[string]*VSCodeForkConfig{ }, "windows": { `%LOCALAPPDATA%\Programs\Cursor\resources\app`, + `%LOCALAPPDATA%\Programs\cursor\resources\app`, `C:\Program Files\Cursor\resources\app`, + `%LOCALAPPDATA%\Cursor\resources\app`, }, "linux": { "/usr/share/cursor/resources/app", @@ -74,7 +81,9 @@ var VSCodeForks = map[string]*VSCodeForkConfig{ }, "windows": { `%LOCALAPPDATA%\Programs\Windsurf\resources\app`, + `%LOCALAPPDATA%\Programs\windsurf\resources\app`, `C:\Program Files\Windsurf\resources\app`, + `%LOCALAPPDATA%\Windsurf\resources\app`, }, "linux": { "/usr/share/windsurf/resources/app", diff --git a/ide/docs/setup.go b/ide/docs/setup.go deleted file mode 100644 index 9b73cd62..00000000 --- a/ide/docs/setup.go +++ /dev/null @@ -1,21 +0,0 @@ -package docs - -import "github.com/jfrog/jfrog-cli-core/v2/plugins/components" - -func GetDescription() string { - return "Setup IDE integration with JFrog Artifactory." -} - -func GetArguments() []components.Argument { - return []components.Argument{ - { - Name: "ide-name", - Description: "IDE to setup. Supported: vscode, cursor, windsurf, jetbrains", - }, - { - Name: "url", - Description: "[Optional] Direct repository/service URL. When provided, --repo-key and server config are not required.", - Optional: true, - }, - } -} diff --git a/ide/docs/setup/help.go b/ide/docs/setup/help.go index bb0f4676..5d45d3c1 100644 --- a/ide/docs/setup/help.go +++ b/ide/docs/setup/help.go @@ -1,20 +1,15 @@ package setup -import ( - "github.com/jfrog/jfrog-cli-core/v2/plugins/components" -) +import "github.com/jfrog/jfrog-cli-core/v2/plugins/components" var Usage = []string{ - "ide setup [SERVICE_URL]", - "ide s [SERVICE_URL]", + "ide setup [url]", + "ide s [url]", } func GetDescription() string { return `Setup IDE integration with JFrog Artifactory. -Supported Action: - setup Configure your IDE to use JFrog Artifactory - Supported IDEs: vscode Visual Studio Code cursor Cursor IDE @@ -28,22 +23,19 @@ Examples: # Setup Cursor jf ide setup cursor --repo-key=cursor-remote - # Setup Windsurf - jf ide setup windsurf --repo-key=windsurf-remote - - # Setup JetBrains - jf ide setup jetbrains --repo-key=jetbrains-remote` + # Setup with direct URL + jf ide setup vscode "https://artifactory.example.com/artifactory/api/aieditorextensions/vscode-repo/_apis/public/gallery"` } func GetArguments() []components.Argument { return []components.Argument{ { - Name: "IDE_NAME", - Description: "The name of the IDE to setup. Supported IDEs are 'vscode', 'cursor', 'windsurf', and 'jetbrains'.", + Name: "ide-name", + Description: "IDE to setup. Supported: vscode, cursor, windsurf, jetbrains", }, { - Name: "SERVICE_URL", - Description: "(Optional) Direct repository service URL. When provided, --repo-key and server config are not required. Example: https://host/api/aieditorextensions/repo/_apis/public/gallery", + Name: "url", + Description: "[Optional] Direct repository/service URL. When provided, --repo-key and server config are not required.", Optional: true, }, }