Skip to content

Commit f18aee3

Browse files
saurabh500Saurabh SinghCopilotbewithgauravdlevy-msft-sql
authored
CHORE: Create devcontainer for repo (#147)
## Devcontainer: Production-Ready Setup for mssql-python Development ### Summary Refactored the devcontainer with battle-tested improvements from local development use. This provides a complete, zero-config development environment for GitHub Codespaces and local devcontainers. ### What's New **🏗️ Build & Setup** - Auto-builds C++ extension during container setup (fails fast if broken) - Added `unixodbc-dev` package - fixes `sql.h not found` compilation errors - Disabled CMake auto-configure to prevent duplicate/conflicting builds - Moved Python package installation from Dockerfile to post-create for faster image rebuilds **🖥️ ARM64 Support** - Detects architecture automatically - Uses Azure SQL Edge on ARM64 (Apple Silicon, ARM Codespaces) - Uses SQL Server 2025 on x86_64 **🔌 SQL Server Auto-Setup** - Starts SQL Server container via Docker-in-Docker - Generates random secure SA password - Sets `DB_CONNECTION_STRING` environment variable (persisted to `/etc/environment`, `.bashrc`, `.zshrc`) - Non-fatal if Docker fails - devcontainer still works for builds/tests against remote servers **⚡ Developer Experience** - Shell aliases work in both bash and zsh: - `build` - Rebuild C++ extension - `test` - Run pytest - Clean completion message with quick-start commands ### Quick Start 1. Open in Codespaces / "Reopen in Container" 2. Wait for setup (~4-5 mins) 3. `python main.py` - test connection 4. `test` - run all tests ### Testing - [x] Tested on macOS ARM64 (Apple Silicon) - [x] Tested in GitHub Codespaces Fixes #146 --------- Co-authored-by: Saurabh Singh <singhsaura@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Gaurav Sharma <sharmag@microsoft.com> Co-authored-by: David Levy <dlevy@microsoft.com>
1 parent c853631 commit f18aee3

4 files changed

Lines changed: 363 additions & 0 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Use Python 3.14 with Debian Bookworm as the base image
2+
FROM mcr.microsoft.com/devcontainers/python:3.14-bookworm
3+
4+
# Set environment variables
5+
ENV DEBIAN_FRONTEND=noninteractive
6+
ENV PYTHONPATH=/workspaces/mssql-python
7+
ENV CMAKE_BUILD_TYPE=Debug
8+
9+
# Set timezone (configurable via build arg)
10+
ARG TZ=UTC
11+
ENV TZ=$TZ
12+
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
13+
&& echo $TZ > /etc/timezone
14+
15+
# Install all system dependencies in one layer
16+
RUN apt-get update && apt-get install -y \
17+
# Build tools
18+
build-essential \
19+
cmake \
20+
pkg-config \
21+
ninja-build \
22+
python3-dev \
23+
pybind11-dev \
24+
# ODBC prerequisites (unixodbc-dev provides sql.h headers for compilation)
25+
unixodbc-dev \
26+
gnupg \
27+
software-properties-common \
28+
# Utilities
29+
curl \
30+
wget \
31+
git \
32+
vim \
33+
nano \
34+
htop \
35+
tree \
36+
jq \
37+
# Clean up
38+
&& apt-get clean \
39+
&& rm -rf /var/lib/apt/lists/*
40+
41+
# Install Microsoft ODBC Driver for SQL Server
42+
RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg \
43+
&& echo "deb [arch=arm64,amd64 signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list \
44+
&& apt-get update \
45+
&& ACCEPT_EULA=Y apt-get install -y msodbcsql18 \
46+
&& ACCEPT_EULA=Y apt-get install -y mssql-tools18 \
47+
&& apt-get clean \
48+
&& rm -rf /var/lib/apt/lists/*
49+
50+
# Add mssql-tools to PATH
51+
ENV PATH="$PATH:/opt/mssql-tools18/bin"
52+
53+
# Set the default user to vscode (created by the base image)
54+
USER vscode
55+
56+
# Set the working directory
57+
WORKDIR /workspaces/mssql-python
58+
59+
# Default command
60+
CMD ["/bin/bash"]

.devcontainer/devcontainer.json

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"name": "MSSQL Python Driver",
3+
"build": {
4+
"dockerfile": "Dockerfile",
5+
"context": "."
6+
},
7+
"customizations": {
8+
"vscode": {
9+
"settings": {
10+
"python.defaultInterpreterPath": "/usr/local/bin/python",
11+
"python.terminal.activateEnvironment": false,
12+
"editor.formatOnSave": true,
13+
"[python]": {
14+
"editor.defaultFormatter": "ms-python.black-formatter"
15+
},
16+
"cmake.configureOnOpen": false,
17+
"cmake.configureOnEdit": false,
18+
"cmake.automaticReconfigure": false,
19+
"files.exclude": {
20+
"**/__pycache__": true,
21+
"**/*.pyc": true,
22+
"**/.pytest_cache": true,
23+
"**/build": true
24+
}
25+
},
26+
"extensions": [
27+
"ms-python.python",
28+
"ms-python.vscode-pylance",
29+
"ms-python.pylint",
30+
"ms-python.black-formatter",
31+
"ms-toolsai.jupyter",
32+
"ms-vscode.cmake-tools",
33+
"ms-vscode.cpptools",
34+
"ms-vscode.cpptools-extension-pack",
35+
"github.copilot",
36+
"github.copilot-chat",
37+
"ms-vscode.test-adapter-converter",
38+
"littlefoxteam.vscode-python-test-adapter",
39+
"ms-azuretools.vscode-docker",
40+
"ms-mssql.mssql"
41+
]
42+
}
43+
},
44+
"features": {
45+
"ghcr.io/devcontainers/features/git:1": {},
46+
"ghcr.io/devcontainers/features/github-cli:1": {},
47+
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
48+
"ghcr.io/devcontainers/features/common-utils:2": {
49+
"installZsh": true,
50+
"configureZshAsDefaultShell": true,
51+
"installOhMyZsh": true,
52+
"upgradePackages": true,
53+
"username": "vscode",
54+
"userUid": "automatic",
55+
"userGid": "automatic"
56+
}
57+
},
58+
"forwardPorts": [1433],
59+
"portsAttributes": {
60+
"1433": {
61+
"label": "SQL Server",
62+
"onAutoForward": "notify"
63+
}
64+
},
65+
"postCreateCommand": "bash .devcontainer/post-create.sh",
66+
"remoteUser": "vscode",
67+
"containerEnv": {
68+
"PYTHONPATH": "/workspaces/mssql-python",
69+
"CMAKE_BUILD_TYPE": "Debug"
70+
},
71+
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/mssql-python,type=bind,consistency=cached",
72+
"workspaceFolder": "/workspaces/mssql-python"
73+
}

.devcontainer/post-create.sh

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/bin/bash
2+
3+
# Post-create script for MSSQL Python Driver devcontainer
4+
set -e
5+
6+
echo "🚀 Setting up MSSQL Python Driver development environment..."
7+
8+
# Install Python packages from requirements.txt
9+
echo "📦 Installing Python packages..."
10+
pip install --upgrade pip setuptools wheel
11+
pip install -r requirements.txt
12+
13+
# Create symlink for 'python' command (build.sh expects it)
14+
echo "🔗 Creating python symlink..."
15+
sudo ln -sf $(which python3) /usr/local/bin/python
16+
17+
# Set up useful shell aliases (for both bash and zsh)
18+
echo "⚡ Setting up aliases..."
19+
cat > ~/.shell_aliases << 'EOF'
20+
# MSSQL Python Driver development aliases
21+
alias build='cd /workspaces/mssql-python/mssql_python/pybind && ./build.sh && cd /workspaces/mssql-python'
22+
alias test='python -m pytest -v'
23+
EOF
24+
25+
# Ensure aliases are sourced in both shells
26+
grep -qxF 'source ~/.shell_aliases' ~/.bashrc 2>/dev/null || echo 'source ~/.shell_aliases' >> ~/.bashrc
27+
grep -qxF 'source ~/.shell_aliases' ~/.zshrc 2>/dev/null || echo 'source ~/.shell_aliases' >> ~/.zshrc
28+
29+
# Verify environment
30+
echo ""
31+
echo "🔍 Verifying environment..."
32+
python --version
33+
pip --version
34+
cmake --version
35+
if command -v sqlcmd &> /dev/null; then
36+
echo "✅ sqlcmd available"
37+
else
38+
echo "❌ sqlcmd not found"
39+
fi
40+
41+
# Build the C++ extension
42+
echo ""
43+
echo "🔨 Building C++ extension..."
44+
if cd mssql_python/pybind && ./build.sh && cd ../..; then
45+
echo "✅ C++ extension built successfully"
46+
else
47+
echo "❌ C++ extension build failed!"
48+
exit 1
49+
fi
50+
51+
# Generate random password for SQL Server
52+
echo ""
53+
echo "Generating SQL Server password..."
54+
SA_PASSWORD="$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | head -c 16)Aa1!"
55+
echo "$SA_PASSWORD" > /tmp/.sqlserver_sa_password
56+
chmod 600 /tmp/.sqlserver_sa_password
57+
58+
# Start SQL Server container (use Azure SQL Edge for ARM64 compatibility)
59+
# This is optional - if Docker-in-Docker fails, the devcontainer still works
60+
echo ""
61+
echo "Starting SQL Server container (optional)..."
62+
63+
ARCH=$(uname -m)
64+
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
65+
echo "Detected ARM64 - using Azure SQL Edge..."
66+
docker run -e 'ACCEPT_EULA=Y' -e "MSSQL_SA_PASSWORD=$SA_PASSWORD" \
67+
-p 1433:1433 --name sqlserver \
68+
-d mcr.microsoft.com/azure-sql-edge:latest && SQL_STARTED=true || SQL_STARTED=false
69+
else
70+
echo "Detected x86_64 - using SQL Server 2025..."
71+
docker run -e 'ACCEPT_EULA=Y' -e "MSSQL_SA_PASSWORD=$SA_PASSWORD" \
72+
-p 1433:1433 --name sqlserver \
73+
-d mcr.microsoft.com/mssql/server:2025-latest && SQL_STARTED=true || SQL_STARTED=false
74+
fi
75+
76+
if [ "$SQL_STARTED" = "true" ]; then
77+
echo "Waiting for SQL Server to start..."
78+
sleep 15
79+
else
80+
echo "WARNING: SQL Server container failed to start (Docker issue)"
81+
echo " You can start it manually later with:"
82+
echo " docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=YourPassword123!' -p 1433:1433 --name sqlserver -d mcr.microsoft.com/azure-sql-edge:latest"
83+
fi
84+
85+
# Set DB_CONNECTION_STRING environment variable (persist across all terminals)
86+
DB_CONNECTION_STRING="Server=localhost,1433;Database=master;UID=sa;PWD=$SA_PASSWORD;TrustServerCertificate=Yes;Encrypt=Yes"
87+
88+
# Write to /etc/environment for system-wide persistence
89+
echo "DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" | sudo tee -a /etc/environment > /dev/null
90+
91+
# Also add to shell rc files for immediate availability in new terminals
92+
echo "export DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" >> ~/.bashrc
93+
echo "export DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" >> ~/.zshrc
94+
95+
# Export for current session
96+
export DB_CONNECTION_STRING
97+
98+
# Display completion message and next steps
99+
echo ""
100+
echo "=============================================="
101+
echo "🎉 Dev environment setup complete!"
102+
echo "=============================================="
103+
echo ""
104+
echo "📦 What's ready:"
105+
echo " ✅ C++ extension built"
106+
echo " ✅ SQL Server running (localhost:1433)"
107+
echo " ✅ DB_CONNECTION_STRING set in environment"
108+
echo ""
109+
echo "🚀 Quick start - just type these commands:"
110+
echo " python main.py → Test the connection"
111+
echo " test → Run all pytest tests"
112+
echo " build → Rebuild C++ extension"
113+
echo ""
114+
echo "=============================================="
115+
echo ""

.dockerignore

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Git
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# Python
7+
__pycache__
8+
*.pyc
9+
*.pyo
10+
*.pyd
11+
.Python
12+
env
13+
pip-log.txt
14+
pip-delete-this-directory.txt
15+
.tox
16+
.coverage
17+
.coverage.*
18+
.cache
19+
nosetests.xml
20+
coverage.xml
21+
*.cover
22+
*.log
23+
.mypy_cache
24+
.pytest_cache
25+
.hypothesis
26+
27+
# Distribution / packaging
28+
build/
29+
develop-eggs/
30+
dist/
31+
downloads/
32+
eggs/
33+
.eggs/
34+
lib/
35+
lib64/
36+
parts/
37+
sdist/
38+
var/
39+
wheels/
40+
*.egg-info/
41+
.installed.cfg
42+
*.egg
43+
44+
# PyInstaller
45+
*.manifest
46+
*.spec
47+
48+
# Installer logs
49+
pip-log.txt
50+
51+
# Unit test / coverage reports
52+
htmlcov/
53+
.coverage
54+
.coverage.*
55+
nosetests.xml
56+
coverage.xml
57+
*.cover
58+
.hypothesis/
59+
60+
# Translations
61+
*.mo
62+
*.pot
63+
64+
# Documentation
65+
docs/_build/
66+
67+
# PyBuilder
68+
target/
69+
70+
# IDEs
71+
.vscode/
72+
.idea/
73+
*.swp
74+
*.swo
75+
*~
76+
77+
# OS
78+
.DS_Store
79+
.DS_Store?
80+
._*
81+
.Spotlight-V100
82+
.Trashes
83+
ehthumbs.db
84+
Thumbs.db
85+
86+
# Temporary files
87+
*.tmp
88+
*.temp
89+
*.log
90+
91+
# Node.js (if any)
92+
node_modules/
93+
npm-debug.log*
94+
95+
# CMake
96+
CMakeCache.txt
97+
CMakeFiles/
98+
cmake_install.cmake
99+
Makefile
100+
*.cmake
101+
102+
# Build artifacts
103+
*.so
104+
*.dll
105+
*.dylib
106+
*.pdb
107+
*.obj
108+
*.exe
109+
110+
# Test results
111+
test-results/
112+
.pytest_cache/
113+
114+
# Azure DevOps
115+
.azure/

0 commit comments

Comments
 (0)