Skip to content

Conversation

@dlevy-msft-sql
Copy link
Contributor

@dlevy-msft-sql dlevy-msft-sql commented Feb 1, 2026

Summary

This PR adds two new commands to replace the deprecated Azure Data Studio integration:

  • sqlcmd open vscode - Opens VS Code with a configured connection profile
  • sqlcmd open ssms - Opens SQL Server Management Studio (Windows only)

Both commands use clipboard-based password sharing since these tools use sandboxed credential storage that can't be accessed directly.

Changes

New Features

sqlcmd open vscode

  • Creates a connection profile in VS Code's user settings (mssql.connections)
  • Uses context name as profile name (e.g., mssql, mssql2)
  • Copies password to clipboard for easy paste when connecting
  • Sets trustServerCertificate: true and encrypt: Optional for local container connections
  • Optional --install-extension flag to install the MSSQL extension (not auto-installed)

sqlcmd open ssms (Windows only)

  • Launches SSMS with server and username pre-filled
  • Uses -C flag to trust server certificate
  • Copies password to clipboard (SSMS 18+ removed -P flag for security)
  • Exits with friendly error on macOS/Linux with guidance to use VS Code instead

New Files

File Description
cmd/modern/root/open/vscode.go VS Code launcher with profile creation
cmd/modern/root/open/vscode_*.go Platform-specific VS Code support
cmd/modern/root/open/ssms.go SSMS launcher (Windows only)
cmd/modern/root/open/ssms_linux.go Linux stub (exits with helpful error)
cmd/modern/root/open/ssms_darwin.go macOS stub (exits with helpful error)
cmd/modern/root/open/clipboard.go Helper to copy password to clipboard
internal/pal/clipboard*.go Cross-platform clipboard implementations
internal/tools/tool/vscode*.go VS Code tool detection
internal/tools/tool/ssms*.go SSMS tool detection

Bug Fixes

  • Fixed panic in tool.IsInstalled() when tool executable path is empty
  • Updated TestAds to skip when Azure Data Studio is not installed (deprecated)

Usage Examples

VS Code

# Create a SQL Server container and open in VS Code
sqlcmd create mssql --accept-eula
sqlcmd open vscode

# Output:
# Opening VS Code...
# Use the 'mssql' connection profile to connect
# Password copied to clipboard - paste it when prompted

VS Code with Extension Installation

# Open VS Code and install the MSSQL extension if needed
sqlcmd open vscode --install-extension

# Output:
# Installing MSSQL extension...
# MSSQL extension installed successfully
# Opening VS Code...
# Use the 'mssql' connection profile to connect
# Password copied to clipboard - paste it when prompted

SSMS (Windows)

sqlcmd open ssms

# Output:
# Password copied to clipboard - paste it when prompted
# Opening SSMS...

SSMS on Non-Windows

sqlcmd open ssms

# Output:
# SSMS is only available on Windows. Use 'sqlcmd open vscode' instead.

Technical Notes

Why Clipboard-Based Password Sharing?

  • VS Code MSSQL extension uses VS Code's SecretStorage API which is sandboxed per-extension
  • SSMS 18+ removed the -P command-line parameter for security reasons
  • Clipboard is the standard UX pattern for credential handoff between applications

Trust Server Certificate

Both commands configure trust for server certificates on local container connections:

  • VS Code: trustServerCertificate: true and encrypt: "Optional" in profile
  • SSMS: -C command-line flag

This is required because local SQL Server containers use self-signed certificates.

Extension Installation

The MSSQL extension is not auto-installed to respect user consent. Use --install-extension to explicitly opt-in:

sqlcmd open vscode --install-extension

Build Constraints

  • ssms.go has //go:build windows - only compiles on Windows
  • ssms_linux.go and ssms_darwin.go provide stubs that exit with helpful error messages

Testing

Tests skip gracefully when tools are not installed:

=== RUN   TestAds
    ads_test.go:25: Azure Data Studio is not installed (deprecated - use 'sqlcmd open vscode' instead)
--- SKIP: TestAds (0.00s)
=== RUN   TestVSCode
--- PASS: TestVSCode (0.07s)
=== RUN   TestSsms
--- PASS: TestSsms (0.06s)
...
PASS
ok      github.com/microsoft/go-sqlcmd/cmd/modern/root/open     2.721s

Checklist

  • All tests pass
  • Code follows project conventions
  • golangci-lint passes
  • Cross-platform support (Windows, macOS, Linux)
  • Error handling for missing tools
  • No auto-install without user consent

Supersedes #685
Fixes #584

Copilot AI and others added 10 commits January 31, 2026 19:49
- Created VSCode tool implementation with platform-specific search locations
- Created vscode command with connection profile management
- Updated open command to include vscode subcommand
- Added tests for VSCode functionality
- Updated README.md to use VSCode instead of ADS

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
- Improved test skip message with detailed explanation
- Added security comment about encrypt setting
- Removed unnecessary else block in VSCode tool
- Renamed settingsDir to dir for consistency

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
- Created SSMS tool with platform-specific search paths (Windows only)
- Created SSMS command to launch with connection parameters
- Added SSMS to open command alongside VSCode and ADS
- Uses SSMS command-line parameters: -S (server), -U (username), -E (integrated auth)
- Note: -P (password) removed in SSMS 18+ for security
- Tests passing for all platforms

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
Use strings.ReplaceAll instead of strings.Replace with -1 for better readability

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
- Use promoted fields from embedded structs (endpoint.Address, endpoint.Port, endpoint.ContainerDetails)
- Wrap all user-facing strings with localizer.Sprintf for proper localization
- Fix VSCode Insiders settings path detection - prefer Insiders if directory exists
- Remove redundant fmt imports from platform-specific files
- Addresses staticcheck QF1008 warnings about redundant embedded-field selectors

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
- Use promoted field endpoint.Id instead of endpoint.ContainerDetails.Id
- Applied to both ssms.go and vscode.go ensureContainerIsRunning functions
- All staticcheck QF1008 warnings now resolved

Co-authored-by: dlevy-msft-sql <194277063+dlevy-msft-sql@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Add 'sqlcmd open vscode' command with connection profile creation
- Add 'sqlcmd open ssms' command for Windows with SQL authentication
- Auto-install MSSQL extension when launching VS Code
- Copy password to clipboard for easy paste in login dialogs
- Add trustServerCertificate for local container connections
- Add comprehensive tests (15 new tests)
- Update README with documentation for both commands
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for opening Visual Studio Code and SQL Server Management Studio (SSMS) to work with SQL Server connections managed by sqlcmd, addressing the deprecation of Azure Data Studio. The implementation uses clipboard-based password sharing since both tools use sandboxed credential storage.

Changes:

  • Adds sqlcmd open vscode command that creates connection profiles in VS Code settings and auto-installs the MSSQL extension
  • Adds sqlcmd open ssms command (Windows-only) that launches SSMS with pre-configured connection parameters
  • Implements cross-platform clipboard support for secure password sharing
  • Includes comprehensive tests and documentation updates

Reviewed changes

Copilot reviewed 30 out of 31 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cmd/modern/root/open.go Updates open command to include VSCode and SSMS subcommands
cmd/modern/root/open/vscode.go Main VSCode command implementation with settings file manipulation
cmd/modern/root/open/vscode_*.go Platform-specific VSCode implementations
cmd/modern/root/open/ssms.go Main SSMS command implementation
cmd/modern/root/open/ssms_*.go Platform-specific SSMS implementations (Fatal on non-Windows)
cmd/modern/root/open/clipboard.go Helper function for clipboard-based password sharing
cmd/modern/root/open/*_test.go Comprehensive test coverage for new commands
internal/tools/tool/vscode*.go VSCode tool detection and configuration
internal/tools/tool/ssms*.go SSMS tool detection and configuration
internal/pal/clipboard*.go Cross-platform clipboard implementation using native APIs
internal/tools/tools.go Registers VSCode and SSMS tools
README.md Adds clear documentation with usage examples
.gitignore Adds /modern build artifact

- Check error returns from os.WriteFile and json.Unmarshal in tests
- Simplify embedded field access (endpoint.ContainerDetails instead of endpoint.AssetDetails.ContainerDetails)
- Add safe type assertion for profileName in updateOrAddProfile to prevent panic
- Add nil check for user.BasicAuth before accessing Username in vscode.go
- Add nil check for user.BasicAuth before accessing Username in ssms.go
- Change endpoint.ContainerDetails.Id to endpoint.Id in ssms.go
- Change endpoint.ContainerDetails.Id to endpoint.Id in vscode.go
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 9 comments.

- Use explicit endpoint.AssetDetails.ContainerDetails pattern for consistency
- Use endpoint.AssetDetails.ContainerDetails.Id instead of embedded field
- Replace custom contains() with strings.Contains() in tests
- Replace custom itoa() with strconv.Itoa() in tests
- Move SSMS platform check to run() for early exit on non-Windows
- Set authenticationType and savePassword conditionally in VS Code profiles
Fixes govulncheck CI failure caused by duplicate run() method declaration.
The ssms.go file (Windows SSMS launcher) was being compiled on all platforms,
conflicting with ssms_linux.go and ssms_darwin.go which define platform-specific
run() methods that exit with a friendly error message.
- Simplify embedded field access per staticcheck QF1008:
  endpoint.AssetDetails.ContainerDetails -> endpoint.ContainerDetails
- Add DefineCommand to Linux/macOS SSMS stubs so run() is referenced
  and the command is properly registered on all platforms
- Simplify endpoint.ContainerDetails.Id to endpoint.Id (QF1008)
  The Id field is promoted through Go embedding chain
- Remove unused displayPreLaunchInfo stubs from Linux/macOS files
  These were dead code since run() exits via Fatal()
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 1 comment.

Copilot correctly identified that accessing promoted field endpoint.ContainerDetails
will panic if endpoint.AssetDetails is nil. Changed back to explicit path
endpoint.AssetDetails.ContainerDetails to ensure nil-safety.

The staticcheck QF1008 suggestion doesn't apply here because the embedded
pointer must be checked before accessing promoted fields.
Staticcheck QF1008 suggests simplifying endpoint.AssetDetails.ContainerDetails
to endpoint.ContainerDetails, but this is unsafe when AssetDetails is an
embedded *pointer*. Accessing promoted fields through a nil pointer panics.

The explicit path is intentional for nil-safety, so we suppress the warning.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated 1 comment.

…nflicts

The previous approach caused a cycle between:
- Copilot review: use explicit path for nil-safety
- staticcheck QF1008: simplify embedded field access

Solution: Pass the container ID string directly to ensureContainerIsRunning()
instead of the whole endpoint. This way:
1. The nil-safe check happens once at the call site with explicit path
2. The helper function receives just the ID, avoiding embedded field issues
3. No nolint directives needed
- Fix panic when exeName is empty (tool not found during Init)
- Update TestAds to skip when ADS is not installed
- ADS is deprecated and being replaced by VS Code/SSMS support
…sion

- Add --install-extension flag to 'sqlcmd open vscode' command
- Only install MSSQL extension when explicitly requested (no auto-install)
- Update tests to skip when VS Code or SSMS are not installed
- Follows principle of asking permission before installing anything
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 32 out of 33 changed files in this pull request and generated 3 comments.

- Add RunWithOutput() method to Tool interface to capture command output
- Check for ms-mssql.mssql extension using 'code --list-extensions'
- Show helpful error with --install-extension hint if extension is missing
Use local variable for AssetDetails to avoid embedded field selector warning
while maintaining nil-safety checks.
ContainerDetails is embedded in AssetDetails, so Id is promoted directly.
- Windows: winget and choco commands for VS Code and SSMS
- macOS: brew command for VS Code
- Linux: apt, dnf, and snap commands for VS Code
- Also mention --install-extension flag for MSSQL extension
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 33 out of 34 changed files in this pull request and generated 1 comment.


// If the context has a local container, ensure it is running, otherwise bail out
if endpoint.AssetDetails != nil && endpoint.AssetDetails.ContainerDetails != nil {
c.ensureContainerIsRunning(endpoint.AssetDetails.ContainerDetails.Id)
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

Inconsistent access pattern for container ID. The VSCode command uses asset.Id (line 60 of vscode.go) which leverages struct embedding from AssetDetails, while this code uses the longer form endpoint.AssetDetails.ContainerDetails.Id. Both work due to Go's struct embedding, but for consistency with the VSCode command in the same PR, consider using endpoint.AssetDetails.Id instead.

Suggested change
c.ensureContainerIsRunning(endpoint.AssetDetails.ContainerDetails.Id)
c.ensureContainerIsRunning(endpoint.AssetDetails.Id)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-work Size: S Small issue (less than one week effort) SSMS vscode-mssql-ext

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove ADS and add support for VSCode + MSSQL

1 participant