This document provides guidance for GitHub Copilot when working with the go-sqlcmd repository.
go-sqlcmd is a Go-based command line tool (sqlcmd) for working with Microsoft SQL Server, Azure SQL Database, and Azure Synapse. The project aims to be a complete port of the original ODBC sqlcmd to Go, utilizing the go-mssqldb driver.
/
├── cmd/ # Entry points for the application
│ ├── modern/ # Modern CLI entry point (Cobra-based)
│ │ ├── root/ # Root command and subcommands
│ │ └── sqlconfig/ # SQL configuration management
│ └── sqlcmd/ # Legacy CLI entry point (Kong-based)
├── pkg/ # Public packages consumable by other hosts
│ └── sqlcmd/ # Core sqlcmd functionality
├── internal/ # Internal packages (not for external use)
│ ├── buffer/ # Buffer management
│ ├── cmdparser/ # Command parsing utilities
│ ├── color/ # Console coloring
│ ├── config/ # Configuration management
│ ├── container/ # Docker/Podman container management
│ ├── credman/ # Credential management
│ ├── http/ # HTTP utilities
│ ├── io/ # I/O utilities
│ ├── localizer/ # Localization support
│ ├── net/ # Network utilities
│ ├── output/ # Output formatting
│ ├── pal/ # Platform abstraction layer
│ ├── secret/ # Secret/credential management
│ ├── sql/ # SQL-related utilities
│ ├── test/ # Test utilities
│ ├── tools/ # Development tools
│ └── translations/ # Localized string translations
├── build/ # Build scripts and templates
├── testdata/ # Test data files
├── release/ # Release-related files
└── .github/ # GitHub workflows and configurations
# Build the sqlcmd executable
./build/build.sh # Linux/macOS
.\build\build.cmd # Windows
# Or build directly with Go
go build -o sqlcmd ./cmd/modernThe project uses Go modules. Run go mod download to fetch dependencies.
# Run all tests
go test ./...
# Run tests with verbose output
go test -v ./...
# Run tests for a specific package
go test -v ./pkg/sqlcmd/...
go test -v ./internal/config/...Tests may require the following environment variables for database connectivity:
SQLCMDSERVER- SQL Server hostnameSQLCMDPORT- SQL Server port (default: 1433)SQLCMDUSER- Username (e.g.,sa)SQLCMDPASSWORD- PasswordSQLCMDDATABASE- Database name
- Follow standard Go conventions and idioms
- Use
gofmtfor formatting - Use tabs for indentation (as specified in
.editorconfig) - Follow effective Go guidelines: https://go.dev/doc/effective_go
All Go files should include the following copyright header:
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.Each package should have a doc.go file with package-level documentation.
- Use the standard Go error handling patterns
- For user-facing errors, prefer localized error messages via the
internal/localizerpackage - Wrap errors with context when propagating
- Use camelCase for unexported identifiers
- Use PascalCase for exported identifiers
- Acronyms should be all uppercase (e.g.,
SQL,URL,HTTP) - Package names should be lowercase, single-word if possible
The modern CLI is located in cmd/modern/ and uses the Cobra library. Key points:
- Root command is in
cmd/modern/root.go - Subcommands are in
cmd/modern/root/directory - Uses dependency injection for testability
The legacy CLI is in cmd/sqlcmd/ and maintains backward compatibility with the original ODBC sqlcmd.
When adding new commands:
- Create the command in
cmd/modern/root/ - Follow the existing pattern for subcommands (see
query.go,start.go,stop.go) - Add corresponding tests with
_test.gosuffix
- Configuration files are stored in
~/.sqlcmd/sqlconfig - Use the
internal/configpackage for configuration management - Viper is used for configuration parsing (
internal/config/viper.go)
The project supports creating SQL Server instances using Docker or Podman:
- Container management is in
internal/container/ - Supports SQL Server images
- Localized strings are in
internal/translations/ - Use the
internal/localizerpackage for localized messages - Supported languages: Chinese (Simplified/Traditional), English, French, German, Italian, Japanese, Korean, Portuguese (Brazil), Russian, Spanish
When adding user-facing strings to the code, use the localizer package:
import "github.com/microsoft/go-sqlcmd/internal/localizer"
// Use localizer.Sprintf for formatted strings
message := localizer.Sprintf("This is a localizable message with %s", value)
// Use localizer.Errorf for localized errors
err := localizer.Errorf("Error: %s failed", operation)Constants that are not user-facing (like environment variable names, command names) should be placed in internal/localizer/constants.go and do not need localization.
After adding new localizable strings, you must regenerate the translation catalog files before committing. The build scripts handle this automatically.
build\build.cmdThis script:
- Installs
gotextif not already installed - Runs
go generatewhich executes the gotext command defined ininternal/translations/translations.go - Generates/updates the translation catalog in
internal/translations/catalog.go - Reports any conflicting localizable strings that need to be fixed
Run the following commands manually:
# Install gotext if not already installed
go install golang.org/x/text/cmd/gotext@latest
# Generate translation files
go generate ./...- Always run the build script after adding new user-facing strings
- Check the build output for "conflicting localizable strings" warnings and resolve them
- The
SQLCMD_LANGenvironment variable controls the runtime language (e.g.,de-de,fr-fr) - Test your changes with different language settings to ensure proper localization
- Azure AD authentication is supported via the
azidentitypackage - Authentication code is in
pkg/sqlcmd/azure_auth.go - Supports multiple authentication methods: DefaultAzureCredential, Password, Interactive, ManagedIdentity, ServicePrincipal
- Never commit secrets or credentials
- Use environment variables or secure credential stores for sensitive data
- Follow the Microsoft Security Development Lifecycle (SDL)
- Report security vulnerabilities via SECURITY.md
- Ensure all tests pass locally
- Follow the existing code style
- Update documentation if adding new features
- Add tests for new functionality
- Keep commits focused and well-described
This project uses Conventional Commits for automated version management and changelog generation via Release Please.
ALWAYS use conventional commit format for PR titles and commit messages:
<type>: <description>
[optional body]
[optional footer]
| Type | Version Bump | When to Use | Example |
|---|---|---|---|
feat: |
Minor (X.Y.0) | New features or functionality | feat: add support for SQL Server 2025 |
fix: |
Patch (X.Y.Z) | Bug fixes | fix: resolve timeout issue in query parsing |
feat!: or BREAKING CHANGE: |
Major (X.0.0) | Breaking changes | feat!: change CLI flag behavior |
docs: |
No bump | Documentation only changes | docs: update README with examples |
chore: |
No bump | Maintenance tasks | chore: update dependencies |
deps: |
No bump | Dependency updates | deps: bump go-mssqldb to v1.10.0 |
ci: |
No bump | CI/CD changes | ci: update GitHub Actions workflow |
test: |
No bump | Test additions or fixes | test: add coverage for edge cases |
refactor: |
No bump | Code refactoring without behavior change | refactor: simplify command parsing |
perf: |
Patch (X.Y.Z) | Performance improvements | perf: optimize batch processing |
Good commit messages:
feat: add --server-name flag for tunneled connections
fix: help flags preprocessing for -h and -help
deps: bump go-mssqldb to v1.9.8
ci: add release-please workflow
Bad commit messages:
Update README
Bug fix
Added new feature
Bump go directive to go 1.25.9
- Simplicity first: Make every change as simple as possible. Minimal code impact. Maximum modularity.
- No laziness: Find root causes. No temporary fixes. Senior developer standards.
- Minimal impact: Changes should only touch what's necessary. Avoid introducing bugs.
- Write code for the maintenance programmer: Write clear, concise code that is easy to read and understand.
Applies to all output: code, tests, docstrings, comments, PR descriptions, commit messages.
Prose and comments:
- No filler phrases: "This ensures that...", "In order to...", "It's worth noting..."
- No over-commenting obvious code (comments that restate what the code does)
- No bloated docstrings with "Validates:" bullet lists or "Note:" paragraphs
- No redundant inner docstrings on helpers inside tests
Code:
- No redundant logic (e.g.,
strings.ToLower()insidestrings.EqualFold()) - No duplicate validation (checking the same condition twice)
- No excessive blank lines or formatting
- No stale examples in docstrings that don't match the actual API
Before git push, always run the repo-specific checks. Default:
git fetch upstream && git rebase upstream/main(avoid "out-of-date with base branch")- Review your own diff (
git diff upstream/main) -- check for AI slop, stale docstrings, weak assertions, anti-patterns. Read every changed line as a reviewer, not the author. - Build
- Run full test suite
- Run linter
If repo has .github/copilot-instructions.md, follow its build/test/lint instructions instead.
Rule: Review means reading every changed line as a hostile reviewer, not the author. For each addition, ask: (1) Is this used? grep for it. (2) Is this consistent with parallel code? Check sibling maps/lists. (3) Does the docstring match the code? Compare them line by line. (4) Would a senior engineer flag this? If you can't articulate why each line is correct, you haven't reviewed it.
Tests:
- DAMP over DRY: tests should be Descriptive And Meaningful Phrases, each readable top-to-bottom as a self-contained story
- Test behavior, not implementation
- Assertions must match claims: if the test says "all types", check all of them
- Cover what motivated the fix: the case that caused the bug is the most important assertion
When in doubt: would a senior engineer roll their eyes at this?
For new commands related to context management, container operations, or configuration:
- Create a new file in
cmd/modern/root/(e.g.,mycommand.go) - Define a command struct that embeds
cmdparser.Cmd - Implement the
DefineCommandmethod to set up command options, flags, and examples - Implement the
runmethod with the command logic - Add corresponding tests in
mycommand_test.go
Example structure:
type MyCommand struct {
cmdparser.Cmd
// flags
}
func (c *MyCommand) DefineCommand(...cmdparser.CommandOptions) {
options := cmdparser.CommandOptions{
Use: "mycommand",
Short: localizer.Sprintf("Description"),
Run: c.run,
}
c.Cmd.DefineCommand(options)
// Add flags
}
func (c *MyCommand) run() {
// Command logic
}- Update the config struct in
internal/config/config.go - Add validation if needed
- Update the Viper bindings in
internal/config/viper.go - Add tests
For new features related to querying SQL Server and displaying query results, add them to the legacy CLI:
- Add new fields to the
SQLCmdArgumentsstruct incmd/sqlcmd/sqlcmd.go - Register new flags in the
setFlagsfunction - Add validation logic in the
Validatemethod if needed - Determine from existing patterns whether to add a SQLCMD variable to support it
- Update
setVarsorsetConnectfunctions to use the new arguments - Implement the feature logic in the
runfunction or related functions - Add corresponding tests in
cmd/sqlcmd/sqlcmd_test.go - Update README.md to show example usage
- Use
pkg/sqlcmdfor SQL connection management - Connection options are defined in
pkg/sqlcmd/connect.go - Support for various transports: TCP, named pipes, shared memory
- GitHub Actions workflows are in
.github/workflows/ golangci-lint.yml- Linting with golangci-lintpr-validation.yml- Build and test validation for PRs- Azure Pipelines configurations are in
.pipelines/
The project uses golangci-lint. To run locally:
golangci-lint run