From 1722239d8352b33a9e3f80510208c2360916d497 Mon Sep 17 00:00:00 2001 From: David Levy Date: Sun, 1 Feb 2026 23:23:07 -0600 Subject: [PATCH 1/8] feat: add devcontainer for VS Code and GitHub Codespaces Add comprehensive development container setup for easier onboarding: - Go 1.24 with gopls, delve, staticcheck, golangci-lint - SQL Server 2025 (Developer Edition) as sidecar container - Docker-in-Docker for container operations - GitHub CLI for PR management - Pre-configured VS Code extensions (Go, MSSQL, Docker, Copilot, GitLens) - Environment variables pre-set for tests (SQLCMDSERVER, etc.) - Helpful aliases (gtest, gbuild, sql, etc.) - Locally built sqlcmd added to PATH automatically - Detailed documentation in .devcontainer/README.md Files added: - .devcontainer/Dockerfile - .devcontainer/docker-compose.yml - .devcontainer/devcontainer.json - .devcontainer/post-create.sh - .devcontainer/mssql/setup.sql - .devcontainer/README.md - .dockerignore Also updates main README.md with: - 'Open in Dev Containers' badge - Development section with quick start instructions --- .devcontainer/Dockerfile | 20 +++ .devcontainer/README.md | 236 +++++++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 89 ++++++++++++ .devcontainer/docker-compose.yml | 35 +++++ .devcontainer/mssql/setup.sql | 58 ++++++++ .devcontainer/post-create.sh | 99 +++++++++++++ .dockerignore | 69 +++++++++ README.md | 48 +++++++ 8 files changed, 654 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .devcontainer/mssql/setup.sql create mode 100644 .devcontainer/post-create.sh create mode 100644 .dockerignore diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..63035165 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,20 @@ +# go-sqlcmd Development Container +FROM mcr.microsoft.com/devcontainers/go:1-1.24-bookworm + +# Install additional OS packages +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get install -y curl libkrb5-dev \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* + +# Install golangci-lint for code quality +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin v1.64.8 + +# Install additional Go tools +RUN go install golang.org/x/tools/gopls@latest \ + && go install github.com/go-delve/delve/cmd/dlv@latest \ + && go install honnef.co/go/tools/cmd/staticcheck@latest \ + && go install golang.org/x/text/cmd/gotext@latest + +# Create bin directory for local sqlcmd builds +RUN mkdir -p /home/vscode/bin && chown vscode:vscode /home/vscode/bin +ENV PATH="/home/vscode/bin:${PATH}" diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000..4a9f37df --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,236 @@ +# go-sqlcmd Development Container + +This folder contains the configuration for a VS Code Dev Container / GitHub Codespaces development environment for go-sqlcmd. + +## What's Included + +- **Go 1.24** development environment with all necessary tools +- **SQL Server 2025** (Developer Edition) running in a sidecar container +- **Pre-configured VS Code extensions**: + - Go (official extension) + - MS SQL (for database management) + - Docker + - GitHub Copilot + - GitLens +- **Go quality tools**: golangci-lint, gopls, delve debugger, staticcheck +- **Locally built sqlcmd** added to PATH automatically + +## Quick Start + +### Using VS Code (Recommended) + +1. Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) +2. Open this repository in VS Code +3. When prompted, click **"Reopen in Container"**, or: + - Press `F1` and select **"Dev Containers: Reopen in Container"** +4. Wait for the container to build (first time takes ~5 minutes) +5. Start developing! + +### Using GitHub Codespaces + +1. Click the green **"Code"** button on the repository +2. Select **"Codespaces"** tab +3. Click **"Create codespace on main"** (or your preferred branch) +4. Wait for the environment to start + +## Running Tests + +Environment variables are pre-configured for running tests: + +```bash +# Run all tests +go test ./... + +# Run short tests +go test -short ./... + +# Run tests with verbose output +go test -v ./... +``` + +### Helpful Aliases + +After the container starts, these aliases are available: + +| Alias | Command | +|-------|---------| +| `gtest` | Run all tests | +| `gtest-short` | Run short tests | +| `gtest-v` | Run tests with verbose output | +| `gbuild` | Build sqlcmd locally | +| `ginstall` | Build and install sqlcmd to ~/bin | +| `gfmt` | Format code | +| `gvet` | Run go vet | +| `glint` | Run golangci-lint | +| `ggen` | Run go generate (for translations) | +| `test-db` | Test database connection | +| `sql` | Connect to SQL Server interactively | +| `rebuild` | Rebuild sqlcmd | + +## SQL Server Connection + +The SQL Server instance is accessible at: + +- **Server**: `localhost,1433` +- **Username**: `sa` +- **Password**: `SqlCmd@2025!` +- **Database**: `master` (default) or `SqlCmdTest` (created for testing) + +### Using the Built-in sqlcmd + +The container builds sqlcmd from source and adds it to your PATH: + +```bash +# Using the alias +sql + +# Or explicitly +~/bin/sqlcmd -S localhost -U sa -P "SqlCmd@2025!" -C + +# Run a query +sql -Q "SELECT @@VERSION" +``` + +### VS Code SQL Extension + +The MSSQL extension is pre-configured with a connection profile named **"sqlcmd-container"**. Click the SQL Server icon in the Activity Bar to connect. + +## Environment Variables + +The following environment variables are set automatically: + +| Variable | Value | +|----------|-------| +| `SQLCMDSERVER` | `localhost` | +| `SQLCMDUSER` | `sa` | +| `SQLCMDPASSWORD` | `SqlCmd@2025!` | +| `SQLCMDDATABASE` | `master` | +| `SQLCMDDBNAME` | `master` | + +These are the same variables used by the CI pipeline, so tests run identically. + +## Working with sqlcmd + +### Build and Test Workflow + +```bash +# Build sqlcmd from source +ginstall + +# Test the build +~/bin/sqlcmd --version + +# Run a query against the local SQL Server +sql -Q "SELECT name FROM sys.databases" + +# Run the test suite +gtest +``` + +### Rebuilding After Changes + +```bash +# Quick rebuild +rebuild + +# Or full rebuild with install +ginstall +``` + +## Customization + +### Adding SQL Setup Scripts + +Place `.sql` files in `.devcontainer/mssql/` to have them executed when the container starts. + +### Modifying the SA Password + +To change the SQL Server password: + +1. Update `MSSQL_SA_PASSWORD` in `docker-compose.yml` +2. Update `SQLCMDPASSWORD` in `devcontainer.json` (remoteEnv section) +3. Update the password in the `mssql.connections` settings in `devcontainer.json` + +### Using a Different SQL Server Version + +Edit `docker-compose.yml` and change the image tag: + +```yaml +db: + image: mcr.microsoft.com/mssql/server:2022-latest # or 2019-latest +``` + +> **Note:** SQL Server 2025 is the default as it includes the latest features. + +## Troubleshooting + +### ARM64 (Apple Silicon) Users + +SQL Server container images may have issues on ARM64 architecture. If you encounter problems: + +1. Edit `docker-compose.yml` to use SQL Server 2022: + ```yaml + db: + image: mcr.microsoft.com/mssql/server:2022-latest + ``` +2. Ensure Rosetta is enabled in Docker Desktop: **Settings > General > "Use Rosetta for x86_64/amd64 emulation on Apple Silicon"** + +### SQL Server not starting + +Check the Docker logs: +```bash +docker logs $(docker ps -qf "name=db") +``` + +Common issues: +- Insufficient memory (SQL Server requires at least 2GB RAM) +- Port 1433 already in use +- ARM64 architecture issues (see above) + +### Connection refused + +Wait a few seconds after the container starts. SQL Server takes ~30 seconds to become ready. The health check should handle this automatically. + +### Tests failing with connection errors + +Ensure the environment variables are set: +```bash +echo $SQLCMDSERVER +echo $SQLCMDPASSWORD +``` + +If empty, try restarting the terminal or running: +```bash +source ~/.bashrc +``` + +### sqlcmd not found + +Run the install command: +```bash +ginstall +``` + +Or manually: +```bash +go build -o ~/bin/sqlcmd ./cmd/modern +``` + +## Files Reference + +| File | Purpose | +|------|---------| +| `devcontainer.json` | Main configuration file | +| `docker-compose.yml` | Container orchestration (Go + SQL Server) | +| `Dockerfile` | Go development container image | +| `post-create.sh` | Setup script (runs after container creation) | +| `mssql/setup.sql` | Initial database setup script | + +## Contributing + +When modifying the devcontainer: + +1. Test locally with `Dev Containers: Rebuild Container` +2. Ensure all tests pass: `go test ./...` +3. Verify SQL connection works: `test-db` +4. Verify sqlcmd builds: `ginstall && ~/bin/sqlcmd --version` diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..08759660 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,89 @@ +{ + "name": "go-sqlcmd Development", + "dockerComposeFile": "docker-compose.yml", + "service": "devcontainer", + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "shutdownAction": "stopCompose", + + // Configure tool-specific properties + "customizations": { + "vscode": { + "extensions": [ + "golang.go", + "ms-mssql.mssql", + "ms-azuretools.vscode-docker", + "GitHub.copilot", + "GitHub.copilot-chat", + "eamodio.gitlens", + "EditorConfig.EditorConfig", + "streetsidesoftware.code-spell-checker" + ], + "settings": { + "go.toolsManagement.autoUpdate": true, + "go.useLanguageServer": true, + "go.lintTool": "golangci-lint", + "go.lintFlags": ["--fast"], + "go.testEnvVars": { + "SQLCMDSERVER": "localhost", + "SQLCMDUSER": "sa", + "SQLCMDPASSWORD": "SqlCmd@2025!", + "SQLCMDDATABASE": "master" + }, + "mssql.connections": [ + { + "server": "localhost,1433", + "database": "master", + "authenticationType": "SqlLogin", + "user": "sa", + "password": "SqlCmd@2025!", + "savePassword": true, + "profileName": "sqlcmd-container", + "encrypt": "Optional", + "trustServerCertificate": true + } + ], + "editor.formatOnSave": true, + "editor.defaultFormatter": "golang.go", + "[go]": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + }, + "terminal.integrated.defaultProfile.linux": "bash" + } + } + }, + + // Forward the SQL Server port + "forwardPorts": [1433], + "portsAttributes": { + "1433": { + "label": "SQL Server", + "onAutoForward": "silent" + } + }, + + // Use 'postCreateCommand' to run commands after the container is created + "postCreateCommand": "bash .devcontainer/post-create.sh", + + // Environment variables for tests + "remoteEnv": { + "SQLCMDSERVER": "localhost", + "SQLCMDUSER": "sa", + "SQLCMDPASSWORD": "SqlCmd@2025!", + "SQLCMDDATABASE": "master", + "SQLCMDDBNAME": "master" + }, + + // Features to add to the dev container + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "latest", + "moby": true + }, + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "latest" + } + } +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..6b05a29d --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3.8' + +services: + devcontainer: + build: + context: . + dockerfile: Dockerfile + volumes: + - ../..:/workspaces:cached + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. + network_mode: service:db + depends_on: + db: + condition: service_healthy + + db: + image: mcr.microsoft.com/mssql/server:2025-latest + restart: unless-stopped + environment: + ACCEPT_EULA: "Y" + MSSQL_SA_PASSWORD: "SqlCmd@2025!" + MSSQL_PID: "Developer" + volumes: + - mssql-data:/var/opt/mssql + healthcheck: + test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "SqlCmd@2025!" -C -Q "SELECT 1" || exit 1 + interval: 10s + timeout: 5s + retries: 15 + start_period: 45s + +volumes: + mssql-data: diff --git a/.devcontainer/mssql/setup.sql b/.devcontainer/mssql/setup.sql new file mode 100644 index 00000000..25825e28 --- /dev/null +++ b/.devcontainer/mssql/setup.sql @@ -0,0 +1,58 @@ +-- go-sqlcmd Development Database Setup +-- This script runs automatically when the devcontainer starts + +USE master; +GO + +-- Create a test database for development +IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'SqlCmdTest') +BEGIN + CREATE DATABASE SqlCmdTest; + PRINT 'Created database: SqlCmdTest'; +END +GO + +-- Enable contained database authentication for testing +EXEC sp_configure 'contained database authentication', 1; +RECONFIGURE; +GO + +-- Make SqlCmdTest a contained database for testing +ALTER DATABASE SqlCmdTest SET CONTAINMENT = PARTIAL; +GO + +USE SqlCmdTest; +GO + +-- Create a sample table for quick testing +IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'TestTable') +BEGIN + CREATE TABLE TestTable ( + ID INT IDENTITY(1,1) PRIMARY KEY, + Name NVARCHAR(100) NOT NULL, + Value NVARCHAR(MAX), + CreatedAt DATETIME2 DEFAULT GETUTCDATE() + ); + + INSERT INTO TestTable (Name, Value) VALUES + ('Test1', 'Sample value 1'), + ('Test2', 'Sample value 2'), + ('Test3', 'Sample value 3'); + + PRINT 'Created table: TestTable with sample data'; +END +GO + +-- Create a view for testing +IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'TestView') +BEGIN + EXEC('CREATE VIEW TestView AS SELECT ID, Name, CreatedAt FROM TestTable'); + PRINT 'Created view: TestView'; +END +GO + +PRINT 'go-sqlcmd development database setup complete!'; +PRINT 'Test database: SqlCmdTest'; +PRINT 'Sample table: TestTable (3 rows)'; +PRINT 'Sample view: TestView'; +GO diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100644 index 00000000..dda2adcb --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,99 @@ +#!/bin/bash +set -e + +echo "=== go-sqlcmd Development Container Setup ===" + +# Get workspace folder name +WORKSPACE_FOLDER=$(basename "$PWD") + +# Download Go dependencies +echo "πŸ“¦ Downloading Go dependencies..." +go mod download + +# Build sqlcmd and add to PATH +echo "πŸ”¨ Building sqlcmd..." +go build -o ~/bin/sqlcmd ./cmd/modern +echo "βœ… sqlcmd built and added to PATH at ~/bin/sqlcmd" + +# Verify build works +echo "πŸ”¨ Verifying full build..." +go build ./... + +# Wait for SQL Server to be ready (health check should have done this, but let's verify) +echo "πŸ”„ Verifying SQL Server connection..." +max_attempts=30 +attempt=1 +while [ $attempt -le $max_attempts ]; do + if ~/bin/sqlcmd -S localhost -U sa -P "${SQLCMDPASSWORD}" -C -Q "SELECT 1" > /dev/null 2>&1; then + echo "βœ… SQL Server is ready!" + break + fi + echo " Waiting for SQL Server... (attempt $attempt/$max_attempts)" + sleep 2 + attempt=$((attempt + 1)) +done + +if [ $attempt -gt $max_attempts ]; then + echo "⚠️ Warning: Could not verify SQL Server connection. Tests may fail." +fi + +# Run initial setup SQL if it exists +if [ -f ".devcontainer/mssql/setup.sql" ]; then + echo "πŸ“‹ Running setup.sql..." + ~/bin/sqlcmd -S localhost -U sa -P "${SQLCMDPASSWORD}" -C -i .devcontainer/mssql/setup.sql +fi + +# Create useful aliases +echo "πŸ”§ Setting up helpful aliases..." +cat >> ~/.bashrc << 'EOF' + +# go-sqlcmd development aliases +alias gtest='go test ./...' +alias gtest-short='go test -short ./...' +alias gtest-v='go test -v ./...' +alias gbuild='go build ./cmd/modern && echo "Built: ./modern"' +alias ginstall='go build -o ~/bin/sqlcmd ./cmd/modern && echo "Installed to ~/bin/sqlcmd"' +alias gfmt='go fmt ./...' +alias gvet='go vet ./...' +alias glint='golangci-lint run' +alias ggen='go generate ./...' + +# sqlcmd shortcut - uses the locally built version +alias sql='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C' + +# Quick test connection +alias test-db='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -Q "SELECT @@VERSION"' + +# Rebuild and test +alias rebuild='go build -o ~/bin/sqlcmd ./cmd/modern && echo "Rebuilt sqlcmd"' + +EOF + +echo "" +echo "=== Setup Complete! ===" +echo "" +echo "πŸ“– Quick Reference:" +echo " gtest - Run all tests" +echo " gtest-short - Run short tests" +echo " gtest-v - Run tests with verbose output" +echo " gbuild - Build sqlcmd locally" +echo " ginstall - Build and install sqlcmd to ~/bin" +echo " gfmt - Format code" +echo " gvet - Run go vet" +echo " glint - Run golangci-lint" +echo " ggen - Run go generate (for translations)" +echo " test-db - Test database connection" +echo " sql - Connect to SQL Server interactively" +echo " rebuild - Rebuild sqlcmd" +echo "" +echo "πŸ”§ Your locally built sqlcmd is at ~/bin/sqlcmd and in PATH" +echo "" +echo "πŸ”— SQL Server Connection:" +echo " Server: localhost,1433" +echo " User: sa" +echo " Password: SqlCmd@2025!" +echo " Database: master (or SqlCmdTest)" +echo "" +echo "πŸ§ͺ Environment variables are pre-configured for tests." +echo " Run 'go test ./...' to execute the full test suite." +echo "" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..dae9ec9e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,69 @@ +# go-sqlcmd Docker Ignore File +# Exclude files not needed in the container build context + +# Git +.git/ +.gitignore + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Go build artifacts +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out + +# Pre-built binaries (we build from source in the container) +darwin-amd64/ +darwin-arm64/ +linux-amd64/ +linux-arm64/ +linux-s390x/ +windows-amd64/ +windows-arm/ +windows-arm64/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Test coverage +*.cover +*.coverprofile +coverage.out +coverage.html + +# Dependency directories +vendor/ + +# Documentation and non-essential files (keep devcontainer docs) +LICENSE* +SECURITY* +CONTRIBUTING* +CHANGELOG* + +# CI/CD files (not needed in container) +.github/ +.pipelines/ +appveyor.yml + +# Test output and temporary files +*.log +*.tmp +tmp/ + +# Release files +release/ diff --git a/README.md b/README.md index e4a1e35d..8170c546 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # SQLCMD CLI +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/go-sqlcmd) + This repo contains the `sqlcmd` command line tool and Go packages for working with Microsoft SQL Server, Azure SQL Database, and Azure Synapse. Learn more about how `sqlcmd` is used from a articles/posts written by the community: [Community Buzz][]. @@ -307,6 +309,52 @@ e.g. sqlcmd.exe: error: sqlcmd.exe: '-w 4': Der Wert muss grâßer als 8 und kleiner als 65536 sein. ``` +## Development + +### Quick Start with Dev Containers + +The easiest way to develop and test sqlcmd is using the included [Dev Container](.devcontainer/README.md), which works with: + +- **VS Code**: Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), open this repo, and click "Reopen in Container" +- **GitHub Codespaces**: Click the "Code" button on GitHub and select "Create codespace" + +The dev container includes: +- Go 1.24 with all development tools (golangci-lint, gopls, delve) +- SQL Server 2025 ready for integration tests +- Your locally built `sqlcmd` added to PATH automatically +- Pre-configured environment variables for tests + +Once inside the container: +```bash +# Build sqlcmd from source +ginstall + +# Run the test suite +gtest + +# Connect to SQL Server +sql -Q "SELECT @@VERSION" +``` + +### Manual Setup + +If you prefer to set up your environment manually: + +1. Install Go 1.24 or higher +2. Clone this repository +3. Set up a SQL Server instance (2017 or later) +4. Configure environment variables: + - `SQLCMDSERVER` - Server hostname (e.g., `localhost`) + - `SQLCMDUSER` - Username (e.g., `sa`) + - `SQLCMDPASSWORD` - Password + - `SQLCMDDATABASE` - Database name (optional) + +5. Build and run: + ```bash + go build -o sqlcmd ./cmd/modern + ./sqlcmd --version + ``` + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a From e6f43c5d0563467a898977e2c79be29842306fb0 Mon Sep 17 00:00:00 2001 From: David Levy Date: Sun, 1 Feb 2026 23:38:37 -0600 Subject: [PATCH 2/8] fix: address Copilot review feedback - Fix grammar: 'is using' -> 'is to use' in README.md - Remove unused WORKSPACE_FOLDER variable from post-create.sh - Make bashrc alias updates idempotent (remove existing block first) - Only run setup.sql if SQL Server connection succeeded - Don't print SA password to terminal (reference env var instead) - Use SA_PASSWORD in addition to MSSQL_SA_PASSWORD for CI consistency - Use environment variable reference in healthcheck - Pin Go tools to specific versions for reproducibility: - gopls@v0.18.1, dlv@v1.24.1, staticcheck@v0.6.1, gotext@v0.22.0 - Improve golangci-lint install security by using versioned tag URL - Fix docs to match actual behavior (only setup.sql is executed) - Install legacy ODBC sqlcmd (mssql-tools18) for compatibility testing - Add sql-legacy alias to easily compare behavior between versions - Document connecting from host tools (Azure Data Studio, SSMS) --- .devcontainer/Dockerfile | 24 ++++++++++----- .devcontainer/README.md | 52 +++++++++++++++++++++++++++----- .devcontainer/docker-compose.yml | 3 +- .devcontainer/post-create.sh | 32 ++++++++++++++------ README.md | 2 +- 5 files changed, 86 insertions(+), 27 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 63035165..5fa88b07 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,17 +3,27 @@ FROM mcr.microsoft.com/devcontainers/go:1-1.24-bookworm # Install additional OS packages RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get install -y curl libkrb5-dev \ + && apt-get install -y curl libkrb5-dev gnupg2 \ && apt-get clean -y && rm -rf /var/lib/apt/lists/* +# Install Microsoft ODBC driver and mssql-tools18 (legacy ODBC sqlcmd/bcp for compatibility testing) +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg \ + && curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && ACCEPT_EULA=Y apt-get install -y msodbcsql18 mssql-tools18 unixodbc-dev \ + && apt-get clean -y && rm -rf /var/lib/apt/lists/* +ENV PATH="/opt/mssql-tools18/bin:${PATH}" + # Install golangci-lint for code quality -RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin v1.64.8 +# Download from specific release tag instead of master branch for supply chain security +ARG GOLANGCI_LINT_VERSION=v1.64.8 +RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/${GOLANGCI_LINT_VERSION}/install.sh | sh -s -- -b /usr/local/bin ${GOLANGCI_LINT_VERSION} -# Install additional Go tools -RUN go install golang.org/x/tools/gopls@latest \ - && go install github.com/go-delve/delve/cmd/dlv@latest \ - && go install honnef.co/go/tools/cmd/staticcheck@latest \ - && go install golang.org/x/text/cmd/gotext@latest +# Install additional Go tools (pinned versions for reproducibility) +RUN go install golang.org/x/tools/gopls@v0.18.1 \ + && go install github.com/go-delve/delve/cmd/dlv@v1.24.1 \ + && go install honnef.co/go/tools/cmd/staticcheck@v0.6.1 \ + && go install golang.org/x/text/cmd/gotext@v0.22.0 # Create bin directory for local sqlcmd builds RUN mkdir -p /home/vscode/bin && chown vscode:vscode /home/vscode/bin diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 4a9f37df..f3d9e77d 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -64,7 +64,8 @@ After the container starts, these aliases are available: | `glint` | Run golangci-lint | | `ggen` | Run go generate (for translations) | | `test-db` | Test database connection | -| `sql` | Connect to SQL Server interactively | +| `sql` | Connect to SQL Server (go-sqlcmd) | +| `sql-legacy` | Connect using legacy ODBC sqlcmd | | `rebuild` | Rebuild sqlcmd | ## SQL Server Connection @@ -78,23 +79,58 @@ The SQL Server instance is accessible at: ### Using the Built-in sqlcmd -The container builds sqlcmd from source and adds it to your PATH: +The container has **two versions** of sqlcmd available: + +1. **go-sqlcmd** (this project) - the modern Go-based sqlcmd, built from source at `~/bin/sqlcmd` +2. **Legacy ODBC sqlcmd** - the classic version from mssql-tools18 at `/opt/mssql-tools18/bin/sqlcmd` + +Since go-sqlcmd is first in PATH, the `sqlcmd` command runs the modern version. Use the aliases or full paths to choose which version: ```bash -# Using the alias -sql +# go-sqlcmd (this project) - default in PATH +sql # alias for go-sqlcmd with connection args +sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -# Or explicitly -~/bin/sqlcmd -S localhost -U sa -P "SqlCmd@2025!" -C +# Legacy ODBC sqlcmd (for compatibility testing) +sql-legacy # alias for legacy sqlcmd with connection args +/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -# Run a query +# Run a query with go-sqlcmd sql -Q "SELECT @@VERSION" + +# Compare behavior between versions +sql -Q "SELECT 1 AS test" +sql-legacy -Q "SELECT 1 AS test" ``` ### VS Code SQL Extension The MSSQL extension is pre-configured with a connection profile named **"sqlcmd-container"**. Click the SQL Server icon in the Activity Bar to connect. +### Connecting from Host Machine Tools + +The SQL Server port (1433) is forwarded to your host machine, so you can connect using tools installed locally: + +#### Azure Data Studio + +1. Open Azure Data Studio on your host machine +2. Create a new connection: + - Server: `localhost,1433` + - Authentication: SQL Login + - User: `sa` + - Password: `SqlCmd@2025!` + - Trust server certificate: Yes + +#### SQL Server Management Studio (Windows) + +1. Open SSMS on your Windows host +2. Connect to Server: + - Server name: `localhost,1433` + - Authentication: SQL Server Authentication + - Login: `sa` + - Password: `SqlCmd@2025!` + - Check "Trust server certificate" + ## Environment Variables The following environment variables are set automatically: @@ -141,7 +177,7 @@ ginstall ### Adding SQL Setup Scripts -Place `.sql` files in `.devcontainer/mssql/` to have them executed when the container starts. +The `setup.sql` script in `.devcontainer/mssql/` is executed automatically when the container starts. To run additional SQL scripts, either add them to `setup.sql` or update `post-create.sh` to execute them explicitly. ### Modifying the SA Password diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 6b05a29d..1a0f0cdf 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -20,12 +20,13 @@ services: restart: unless-stopped environment: ACCEPT_EULA: "Y" + SA_PASSWORD: "SqlCmd@2025!" MSSQL_SA_PASSWORD: "SqlCmd@2025!" MSSQL_PID: "Developer" volumes: - mssql-data:/var/opt/mssql healthcheck: - test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "SqlCmd@2025!" -C -Q "SELECT 1" || exit 1 + test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$SA_PASSWORD\" -C -Q \"SELECT 1\" || exit 1"] interval: 10s timeout: 5s retries: 15 diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index dda2adcb..f63b35bb 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -3,9 +3,6 @@ set -e echo "=== go-sqlcmd Development Container Setup ===" -# Get workspace folder name -WORKSPACE_FOLDER=$(basename "$PWD") - # Download Go dependencies echo "πŸ“¦ Downloading Go dependencies..." go mod download @@ -23,9 +20,11 @@ go build ./... echo "πŸ”„ Verifying SQL Server connection..." max_attempts=30 attempt=1 +sql_ready=false while [ $attempt -le $max_attempts ]; do if ~/bin/sqlcmd -S localhost -U sa -P "${SQLCMDPASSWORD}" -C -Q "SELECT 1" > /dev/null 2>&1; then echo "βœ… SQL Server is ready!" + sql_ready=true break fi echo " Waiting for SQL Server... (attempt $attempt/$max_attempts)" @@ -33,18 +32,27 @@ while [ $attempt -le $max_attempts ]; do attempt=$((attempt + 1)) done -if [ $attempt -gt $max_attempts ]; then +if [ "$sql_ready" = false ]; then echo "⚠️ Warning: Could not verify SQL Server connection. Tests may fail." fi -# Run initial setup SQL if it exists +# Run initial setup SQL if it exists and SQL Server is ready if [ -f ".devcontainer/mssql/setup.sql" ]; then - echo "πŸ“‹ Running setup.sql..." - ~/bin/sqlcmd -S localhost -U sa -P "${SQLCMDPASSWORD}" -C -i .devcontainer/mssql/setup.sql + if [ "$sql_ready" = true ]; then + echo "πŸ“‹ Running setup.sql..." + ~/bin/sqlcmd -S localhost -U sa -P "${SQLCMDPASSWORD}" -C -i .devcontainer/mssql/setup.sql + else + echo "⚠️ Skipping setup.sql because SQL Server connection could not be verified." + fi fi -# Create useful aliases +# Create useful aliases (idempotent - remove existing block first) echo "πŸ”§ Setting up helpful aliases..." +ALIAS_MARKER="# go-sqlcmd development aliases" +if grep -q "$ALIAS_MARKER" ~/.bashrc 2>/dev/null; then + # Remove existing alias block (from marker to next blank line or EOF) + sed -i "/$ALIAS_MARKER/,/^$/d" ~/.bashrc +fi cat >> ~/.bashrc << 'EOF' # go-sqlcmd development aliases @@ -61,6 +69,9 @@ alias ggen='go generate ./...' # sqlcmd shortcut - uses the locally built version alias sql='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C' +# Legacy ODBC sqlcmd for compatibility testing +alias sql-legacy='/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C' + # Quick test connection alias test-db='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -Q "SELECT @@VERSION"' @@ -83,7 +94,8 @@ echo " gvet - Run go vet" echo " glint - Run golangci-lint" echo " ggen - Run go generate (for translations)" echo " test-db - Test database connection" -echo " sql - Connect to SQL Server interactively" +echo " sql - Connect to SQL Server (go-sqlcmd)" +echo " sql-legacy - Connect using legacy ODBC sqlcmd" echo " rebuild - Rebuild sqlcmd" echo "" echo "πŸ”§ Your locally built sqlcmd is at ~/bin/sqlcmd and in PATH" @@ -91,7 +103,7 @@ echo "" echo "πŸ”— SQL Server Connection:" echo " Server: localhost,1433" echo " User: sa" -echo " Password: SqlCmd@2025!" +echo " Password: (from SQLCMDPASSWORD environment variable)" echo " Database: master (or SqlCmdTest)" echo "" echo "πŸ§ͺ Environment variables are pre-configured for tests." diff --git a/README.md b/README.md index 8170c546..0ec6c31f 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,7 @@ sqlcmd.exe: error: sqlcmd.exe: '-w 4': Der Wert muss grâßer als 8 und kleiner ### Quick Start with Dev Containers -The easiest way to develop and test sqlcmd is using the included [Dev Container](.devcontainer/README.md), which works with: +The easiest way to develop and test sqlcmd is to use the included [Dev Container](.devcontainer/README.md), which works with: - **VS Code**: Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers), open this repo, and click "Reopen in Container" - **GitHub Codespaces**: Click the "Code" button on GitHub and select "Create codespace" From 08a702980a639b9249e834124bc6289b6f64c33c Mon Sep 17 00:00:00 2001 From: David Levy Date: Sun, 1 Feb 2026 23:48:46 -0600 Subject: [PATCH 3/8] fix: improve golangci-lint install security Download pre-built binary directly from GitHub releases instead of piping install script to sh. This addresses supply chain security concerns about executing remote scripts. The image tag 1-1.24-bookworm is valid - the first '1' is the devcontainer definition version, second '1.24' is the Go version. The markdown tables render correctly (Copilot false positive). --- .devcontainer/Dockerfile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5fa88b07..2f46ee51 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -15,9 +15,13 @@ RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor ENV PATH="/opt/mssql-tools18/bin:${PATH}" # Install golangci-lint for code quality -# Download from specific release tag instead of master branch for supply chain security -ARG GOLANGCI_LINT_VERSION=v1.64.8 -RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/${GOLANGCI_LINT_VERSION}/install.sh | sh -s -- -b /usr/local/bin ${GOLANGCI_LINT_VERSION} +# Download pre-built binary directly instead of running install script (supply chain security) +ARG GOLANGCI_LINT_VERSION=1.64.8 +RUN curl -fsSLO "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ + && tar -xzf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ + && mv "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64/golangci-lint" /usr/local/bin/ \ + && rm -rf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64" "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ + && golangci-lint --version # Install additional Go tools (pinned versions for reproducibility) RUN go install golang.org/x/tools/gopls@v0.18.1 \ From af11705dcbc1bddea2b0d8af1822f99ae3058e99 Mon Sep 17 00:00:00 2001 From: David Levy Date: Mon, 2 Feb 2026 00:05:02 -0600 Subject: [PATCH 4/8] fix: address additional Copilot review feedback Security improvements: - Add SHA256 checksum verification for golangci-lint v1.64.8 (b6270687afb143d019f387c791cd2a6f1cb383be9b3124d241ca11bd3ce2e54e) Password handling: - Consolidate to single env var (MSSQL_SA_PASSWORD only) - Use MSSQL_SA_PASSWORD in healthcheck for consistency - Set savePassword: false in mssql.connections (prompts on first use) Tool management: - Disable go.toolsManagement.autoUpdate to preserve pinned versions Documentation: - Clarify CI comparison (same env var names, not identical config) - Update password change instructions to be clearer --- .devcontainer/Dockerfile | 4 +++- .devcontainer/README.md | 4 ++-- .devcontainer/devcontainer.json | 4 ++-- .devcontainer/docker-compose.yml | 3 +-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 2f46ee51..3890adf4 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -15,9 +15,11 @@ RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor ENV PATH="/opt/mssql-tools18/bin:${PATH}" # Install golangci-lint for code quality -# Download pre-built binary directly instead of running install script (supply chain security) +# Download pre-built binary with SHA256 checksum verification (supply chain security) ARG GOLANGCI_LINT_VERSION=1.64.8 +ARG GOLANGCI_LINT_SHA256=b6270687afb143d019f387c791cd2a6f1cb383be9b3124d241ca11bd3ce2e54e RUN curl -fsSLO "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ + && echo "${GOLANGCI_LINT_SHA256} golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" | sha256sum -c - \ && tar -xzf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ && mv "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64/golangci-lint" /usr/local/bin/ \ && rm -rf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64" "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ diff --git a/.devcontainer/README.md b/.devcontainer/README.md index f3d9e77d..b3eb7058 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -143,7 +143,7 @@ The following environment variables are set automatically: | `SQLCMDDATABASE` | `master` | | `SQLCMDDBNAME` | `master` | -These are the same variables used by the CI pipeline, so tests run identically. +These use the same environment variable names as the CI pipeline to ensure local tests behave similarly, although the actual password and SQL Server version in CI may differ. ## Working with sqlcmd @@ -184,7 +184,7 @@ The `setup.sql` script in `.devcontainer/mssql/` is executed automatically when To change the SQL Server password: 1. Update `MSSQL_SA_PASSWORD` in `docker-compose.yml` -2. Update `SQLCMDPASSWORD` in `devcontainer.json` (remoteEnv section) +2. Update `SQLCMDPASSWORD` in `devcontainer.json` (both `remoteEnv` and `go.testEnvVars` sections) 3. Update the password in the `mssql.connections` settings in `devcontainer.json` ### Using a Different SQL Server Version diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 08759660..2354a0d2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -19,7 +19,7 @@ "streetsidesoftware.code-spell-checker" ], "settings": { - "go.toolsManagement.autoUpdate": true, + "go.toolsManagement.autoUpdate": false, "go.useLanguageServer": true, "go.lintTool": "golangci-lint", "go.lintFlags": ["--fast"], @@ -36,7 +36,7 @@ "authenticationType": "SqlLogin", "user": "sa", "password": "SqlCmd@2025!", - "savePassword": true, + "savePassword": false, "profileName": "sqlcmd-container", "encrypt": "Optional", "trustServerCertificate": true diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 1a0f0cdf..353b314b 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -20,13 +20,12 @@ services: restart: unless-stopped environment: ACCEPT_EULA: "Y" - SA_PASSWORD: "SqlCmd@2025!" MSSQL_SA_PASSWORD: "SqlCmd@2025!" MSSQL_PID: "Developer" volumes: - mssql-data:/var/opt/mssql healthcheck: - test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$SA_PASSWORD\" -C -Q \"SELECT 1\" || exit 1"] + test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$MSSQL_SA_PASSWORD\" -C -Q \"SELECT 1\" || exit 1"] interval: 10s timeout: 5s retries: 15 From eefc9d6c52978132e236c3e555b3eb22ad726139 Mon Sep 17 00:00:00 2001 From: David Levy Date: Mon, 2 Feb 2026 00:17:06 -0600 Subject: [PATCH 5/8] fix: apply lessons learned from go-mssqldb PR #317 Dockerfile: - Fix image tag format: 1.24-bookworm (not 1-1.24-bookworm) docker-compose.yml: - Add both SA_PASSWORD and MSSQL_SA_PASSWORD for CI compatibility (different SQL Server images use different env vars) post-create.sh: - Add dynamic workspace detection with robust fallback - Handles different clone names (go-sqlcmd, fork names, etc.) setup.sql: - Add TRY/CATCH error handling for contained database operations - Prevents script failure on unsupported SQL Server configurations .dockerignore: - Add .devcontainer/ to reduce build context size Reference: https://github.com/microsoft/go-mssqldb/pull/317 --- .devcontainer/Dockerfile | 2 +- .devcontainer/docker-compose.yml | 1 + .devcontainer/mssql/setup.sql | 17 ++++++++++++++--- .devcontainer/post-create.sh | 17 +++++++++++++++++ .dockerignore | 3 +++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3890adf4..9bfe75e1 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,5 +1,5 @@ # go-sqlcmd Development Container -FROM mcr.microsoft.com/devcontainers/go:1-1.24-bookworm +FROM mcr.microsoft.com/devcontainers/go:1.24-bookworm # Install additional OS packages RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 353b314b..9ae1139d 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -20,6 +20,7 @@ services: restart: unless-stopped environment: ACCEPT_EULA: "Y" + SA_PASSWORD: "SqlCmd@2025!" MSSQL_SA_PASSWORD: "SqlCmd@2025!" MSSQL_PID: "Developer" volumes: diff --git a/.devcontainer/mssql/setup.sql b/.devcontainer/mssql/setup.sql index 25825e28..79aac446 100644 --- a/.devcontainer/mssql/setup.sql +++ b/.devcontainer/mssql/setup.sql @@ -13,12 +13,23 @@ END GO -- Enable contained database authentication for testing -EXEC sp_configure 'contained database authentication', 1; -RECONFIGURE; +-- Use TRY/CATCH in case this fails on certain SQL Server configurations +BEGIN TRY + EXEC sp_configure 'contained database authentication', 1; + RECONFIGURE; +END TRY +BEGIN CATCH + PRINT 'Note: Could not enable contained database authentication (may already be enabled or not supported)'; +END CATCH; GO -- Make SqlCmdTest a contained database for testing -ALTER DATABASE SqlCmdTest SET CONTAINMENT = PARTIAL; +BEGIN TRY + ALTER DATABASE SqlCmdTest SET CONTAINMENT = PARTIAL; +END TRY +BEGIN CATCH + PRINT 'Note: Could not set database containment (may not be supported)'; +END CATCH; GO USE SqlCmdTest; diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index f63b35bb..6e5fee68 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -3,6 +3,23 @@ set -e echo "=== go-sqlcmd Development Container Setup ===" +# Dynamic workspace detection with fallback +if [ -n "${WORKSPACE_FOLDER}" ] && [ -d "${WORKSPACE_FOLDER}" ]; then + cd "${WORKSPACE_FOLDER}" +elif [ -d "/workspaces/go-sqlcmd" ]; then + cd /workspaces/go-sqlcmd +else + # Find workspace by looking for go.mod + workspace_go_mod="$(find /workspaces -maxdepth 2 -name 'go.mod' -type f -print -quit 2>/dev/null)" + if [ -n "$workspace_go_mod" ]; then + cd "$(dirname "$workspace_go_mod")" + else + echo "Error: Could not determine workspace directory" >&2 + exit 1 + fi +fi +echo "πŸ“ Working in: $(pwd)" + # Download Go dependencies echo "πŸ“¦ Downloading Go dependencies..." go mod download diff --git a/.dockerignore b/.dockerignore index dae9ec9e..329bd7a0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -60,6 +60,9 @@ CHANGELOG* .pipelines/ appveyor.yml +# Dev container configuration (not needed in image build) +.devcontainer/ + # Test output and temporary files *.log *.tmp From 3407d43f210ef01bed9baa99b3672a616ceb8c93 Mon Sep 17 00:00:00 2001 From: David Levy Date: Mon, 2 Feb 2026 00:27:14 -0600 Subject: [PATCH 6/8] fix: address Copilot review feedback round 5 Addresses review #3737525469: 1. README badge: Changed from hardcoded repo URL to link to devcontainer docs, so forks don't redirect to upstream repo 2. Aliases: Use dedicated ~/.bash_aliases file instead of modifying ~/.bashrc with sed, avoiding potential accidental content deletion 3. Volume mount: Fixed from ../.. to ../workspaces/go-sqlcmd to only mount the repo root, not parent directories that could expose secrets 4. MSSQL connection: Removed hardcoded password from mssql.connections config. Users will be prompted to enter password on first connect. 5. Password handling: Added environment variable substitution support: - docker-compose.yml: Uses SQLCMDPASSWORD env var with default - devcontainer.json: Uses localEnv with fallback to default - Added comments explaining this is dev-only, not production secrets - Updated README to clarify password is available via env var --- .devcontainer/README.md | 6 +++++- .devcontainer/devcontainer.json | 10 ++++++---- .devcontainer/docker-compose.yml | 7 ++++--- .devcontainer/post-create.sh | 18 +++++++++--------- README.md | 2 +- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index b3eb7058..13c5ec0c 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -74,9 +74,13 @@ The SQL Server instance is accessible at: - **Server**: `localhost,1433` - **Username**: `sa` -- **Password**: `SqlCmd@2025!` +- **Password**: Available via `$SQLCMDPASSWORD` environment variable - **Database**: `master` (default) or `SqlCmdTest` (created for testing) +> **Note**: The password is set via environment variables and is not stored in any committed files. +> For the default devcontainer setup, the password is `SqlCmd@2025!`. +> When using VS Code's MSSQL extension, copy this value from `$SQLCMDPASSWORD` when prompted. + ### Using the Built-in sqlcmd The container has **two versions** of sqlcmd available: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2354a0d2..589cb819 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -35,9 +35,9 @@ "database": "master", "authenticationType": "SqlLogin", "user": "sa", - "password": "SqlCmd@2025!", + "password": "", "savePassword": false, - "profileName": "sqlcmd-container", + "profileName": "sqlcmd-container (use SQLCMDPASSWORD env var)", "encrypt": "Optional", "trustServerCertificate": true } @@ -67,11 +67,13 @@ // Use 'postCreateCommand' to run commands after the container is created "postCreateCommand": "bash .devcontainer/post-create.sh", - // Environment variables for tests + // Environment variables for tests - password must match docker-compose.yml + // This is a development-only container credential, not a production secret. + // For GitHub Codespaces, you can override SQLCMDPASSWORD via Codespaces Secrets. "remoteEnv": { "SQLCMDSERVER": "localhost", "SQLCMDUSER": "sa", - "SQLCMDPASSWORD": "SqlCmd@2025!", + "SQLCMDPASSWORD": "${localEnv:SQLCMDPASSWORD:SqlCmd@2025!}", "SQLCMDDATABASE": "master", "SQLCMDDBNAME": "master" }, diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 9ae1139d..010fd74f 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -6,7 +6,7 @@ services: context: . dockerfile: Dockerfile volumes: - - ../..:/workspaces:cached + - ..:/workspaces/go-sqlcmd:cached # Overrides default command so things don't shut down after the process ends. command: sleep infinity # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. @@ -20,8 +20,9 @@ services: restart: unless-stopped environment: ACCEPT_EULA: "Y" - SA_PASSWORD: "SqlCmd@2025!" - MSSQL_SA_PASSWORD: "SqlCmd@2025!" + # Password can be overridden via SQLCMDPASSWORD environment variable + SA_PASSWORD: "${SQLCMDPASSWORD:-SqlCmd@2025!}" + MSSQL_SA_PASSWORD: "${SQLCMDPASSWORD:-SqlCmd@2025!}" MSSQL_PID: "Developer" volumes: - mssql-data:/var/opt/mssql diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 6e5fee68..08482118 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -63,15 +63,9 @@ if [ -f ".devcontainer/mssql/setup.sql" ]; then fi fi -# Create useful aliases (idempotent - remove existing block first) +# Create useful aliases in a dedicated file (safe and idempotent) echo "πŸ”§ Setting up helpful aliases..." -ALIAS_MARKER="# go-sqlcmd development aliases" -if grep -q "$ALIAS_MARKER" ~/.bashrc 2>/dev/null; then - # Remove existing alias block (from marker to next blank line or EOF) - sed -i "/$ALIAS_MARKER/,/^$/d" ~/.bashrc -fi -cat >> ~/.bashrc << 'EOF' - +cat > ~/.bash_aliases << 'EOF' # go-sqlcmd development aliases alias gtest='go test ./...' alias gtest-short='go test -short ./...' @@ -94,9 +88,15 @@ alias test-db='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -Q "SELEC # Rebuild and test alias rebuild='go build -o ~/bin/sqlcmd ./cmd/modern && echo "Rebuilt sqlcmd"' - EOF +# Ensure .bash_aliases is sourced from .bashrc +if ! grep -q 'source ~/.bash_aliases' ~/.bashrc 2>/dev/null; then + echo '' >> ~/.bashrc + echo '# Source aliases file' >> ~/.bashrc + echo 'if [ -f ~/.bash_aliases ]; then source ~/.bash_aliases; fi' >> ~/.bashrc +fi + echo "" echo "=== Setup Complete! ===" echo "" diff --git a/README.md b/README.md index 0ec6c31f..576439da 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SQLCMD CLI -[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/go-sqlcmd) +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](.devcontainer/README.md) This repo contains the `sqlcmd` command line tool and Go packages for working with Microsoft SQL Server, Azure SQL Database, and Azure Synapse. From 75cec78b67ba0ee7aa31340e1d296cf0bf498377 Mon Sep 17 00:00:00 2001 From: David Levy Date: Mon, 2 Feb 2026 00:33:30 -0600 Subject: [PATCH 7/8] docs: clarify both SA_PASSWORD vars must be updated Update README to mention that both SA_PASSWORD and MSSQL_SA_PASSWORD must be updated in docker-compose.yml when changing the password, matching the fix applied to go-mssqldb PR #317. --- .devcontainer/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 13c5ec0c..9ea0f00b 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -187,7 +187,7 @@ The `setup.sql` script in `.devcontainer/mssql/` is executed automatically when To change the SQL Server password: -1. Update `MSSQL_SA_PASSWORD` in `docker-compose.yml` +1. Update both `SA_PASSWORD` and `MSSQL_SA_PASSWORD` in `docker-compose.yml` (these must match) 2. Update `SQLCMDPASSWORD` in `devcontainer.json` (both `remoteEnv` and `go.testEnvVars` sections) 3. Update the password in the `mssql.connections` settings in `devcontainer.json` From d6557ff3e501f45e1b9f6933918bbbc07f04fb6d Mon Sep 17 00:00:00 2001 From: David Levy Date: Mon, 2 Feb 2026 00:38:22 -0600 Subject: [PATCH 8/8] fix: address Copilot review feedback round 6 Addresses review #3737557661: 1. Password note wording: Fixed misleading claim that password is not stored in committed files. Now clarifies it's a dev-only default checked into repo for convenience. 2. MSSQL profile name: Updated docs to match devcontainer.json profile name 'sqlcmd-container (use SQLCMDPASSWORD env var)' 3. workspaceFolder mismatch: Changed from variable-based path to fixed '/workspaces/go-sqlcmd' to match docker-compose.yml mount 4. go.testEnvVars password: Now uses env:SQLCMDPASSWORD instead of hardcoded value, so VS Code tests use same password as container 5. ARM64 architecture support: Added architecture detection for golangci-lint with separate SHA256 checksums for amd64/arm64 6. Alias management: Now uses ~/.bash_aliases.d/ directory instead of overwriting ~/.bash_aliases, preserving user's custom aliases --- .devcontainer/Dockerfile | 22 ++++++++++++++++------ .devcontainer/README.md | 8 ++++---- .devcontainer/devcontainer.json | 4 ++-- .devcontainer/post-create.sh | 27 ++++++++++++++++++++------- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9bfe75e1..a944f693 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -16,13 +16,23 @@ ENV PATH="/opt/mssql-tools18/bin:${PATH}" # Install golangci-lint for code quality # Download pre-built binary with SHA256 checksum verification (supply chain security) +# Supports both amd64 and arm64 architectures ARG GOLANGCI_LINT_VERSION=1.64.8 -ARG GOLANGCI_LINT_SHA256=b6270687afb143d019f387c791cd2a6f1cb383be9b3124d241ca11bd3ce2e54e -RUN curl -fsSLO "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ - && echo "${GOLANGCI_LINT_SHA256} golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" | sha256sum -c - \ - && tar -xzf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ - && mv "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64/golangci-lint" /usr/local/bin/ \ - && rm -rf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64" "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz" \ +ARG GOLANGCI_LINT_SHA256_AMD64=b6270687afb143d019f387c791cd2a6f1cb383be9b3124d241ca11bd3ce2e54e +ARG GOLANGCI_LINT_SHA256_ARM64=a6ab58ebcb1c48572622146cdaec2956f56871038a54ed1149f1386e287789a5 +RUN ARCH=$(dpkg --print-architecture) \ + && if [ "$ARCH" = "amd64" ]; then \ + CHECKSUM="${GOLANGCI_LINT_SHA256_AMD64}"; \ + elif [ "$ARCH" = "arm64" ]; then \ + CHECKSUM="${GOLANGCI_LINT_SHA256_ARM64}"; \ + else \ + echo "Unsupported architecture: $ARCH" && exit 1; \ + fi \ + && curl -fsSLO "https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}.tar.gz" \ + && echo "${CHECKSUM} golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}.tar.gz" | sha256sum -c - \ + && tar -xzf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}.tar.gz" \ + && mv "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}/golangci-lint" /usr/local/bin/ \ + && rm -rf "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}" "golangci-lint-${GOLANGCI_LINT_VERSION}-linux-${ARCH}.tar.gz" \ && golangci-lint --version # Install additional Go tools (pinned versions for reproducibility) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 9ea0f00b..30d855df 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -77,9 +77,9 @@ The SQL Server instance is accessible at: - **Password**: Available via `$SQLCMDPASSWORD` environment variable - **Database**: `master` (default) or `SqlCmdTest` (created for testing) -> **Note**: The password is set via environment variables and is not stored in any committed files. -> For the default devcontainer setup, the password is `SqlCmd@2025!`. -> When using VS Code's MSSQL extension, copy this value from `$SQLCMDPASSWORD` when prompted. +> **Note**: The SQL Server password for this devcontainer is provided via the `$SQLCMDPASSWORD` environment variable and is a non-production, development-only default. +> For the default devcontainer setup, the password value (`SqlCmd@2025!`) is checked into this repository for convenience; override it via environment variables or Codespaces/CI secrets for non-local use. +> When using VS Code's MSSQL extension, copy the value from `$SQLCMDPASSWORD` when prompted. ### Using the Built-in sqlcmd @@ -109,7 +109,7 @@ sql-legacy -Q "SELECT 1 AS test" ### VS Code SQL Extension -The MSSQL extension is pre-configured with a connection profile named **"sqlcmd-container"**. Click the SQL Server icon in the Activity Bar to connect. +The MSSQL extension is pre-configured with a connection profile named **"sqlcmd-container (use SQLCMDPASSWORD env var)"**. Click the SQL Server icon in the Activity Bar to connect. ### Connecting from Host Machine Tools diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 589cb819..41021397 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ "name": "go-sqlcmd Development", "dockerComposeFile": "docker-compose.yml", "service": "devcontainer", - "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "workspaceFolder": "/workspaces/go-sqlcmd", "shutdownAction": "stopCompose", // Configure tool-specific properties @@ -26,7 +26,7 @@ "go.testEnvVars": { "SQLCMDSERVER": "localhost", "SQLCMDUSER": "sa", - "SQLCMDPASSWORD": "SqlCmd@2025!", + "SQLCMDPASSWORD": "${env:SQLCMDPASSWORD}", "SQLCMDDATABASE": "master" }, "mssql.connections": [ diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 08482118..364281ef 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -63,9 +63,10 @@ if [ -f ".devcontainer/mssql/setup.sql" ]; then fi fi -# Create useful aliases in a dedicated file (safe and idempotent) +# Create useful aliases in a dedicated directory (safe and idempotent) echo "πŸ”§ Setting up helpful aliases..." -cat > ~/.bash_aliases << 'EOF' +mkdir -p ~/.bash_aliases.d +cat > ~/.bash_aliases.d/go-sqlcmd << 'EOF' # go-sqlcmd development aliases alias gtest='go test ./...' alias gtest-short='go test -short ./...' @@ -90,11 +91,23 @@ alias test-db='~/bin/sqlcmd -S localhost -U sa -P "$SQLCMDPASSWORD" -C -Q "SELEC alias rebuild='go build -o ~/bin/sqlcmd ./cmd/modern && echo "Rebuilt sqlcmd"' EOF -# Ensure .bash_aliases is sourced from .bashrc -if ! grep -q 'source ~/.bash_aliases' ~/.bashrc 2>/dev/null; then - echo '' >> ~/.bashrc - echo '# Source aliases file' >> ~/.bashrc - echo 'if [ -f ~/.bash_aliases ]; then source ~/.bash_aliases; fi' >> ~/.bashrc +# Ensure aliases are sourced from .bashrc +if ! grep -q 'go-sqlcmd aliases' ~/.bashrc 2>/dev/null; then + { + echo '' + echo '# go-sqlcmd aliases' + echo 'if [ -f ~/.bash_aliases ]; then' + echo ' # Source traditional aliases file if present' + echo ' . ~/.bash_aliases' + echo 'fi' + echo '' + echo 'if [ -d ~/.bash_aliases.d ]; then' + echo ' # Source all alias snippets from ~/.bash_aliases.d' + echo ' for f in ~/.bash_aliases.d/*; do' + echo ' [ -r "$f" ] && . "$f"' + echo ' done' + echo 'fi' + } >> ~/.bashrc fi echo ""