diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..6b684a13 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,60 @@ +# Use Python 3.14 with Debian Bookworm as the base image +FROM mcr.microsoft.com/devcontainers/python:3.14-bookworm + +# Set environment variables +ENV DEBIAN_FRONTEND=noninteractive +ENV PYTHONPATH=/workspaces/mssql-python +ENV CMAKE_BUILD_TYPE=Debug + +# Set timezone (configurable via build arg) +ARG TZ=UTC +ENV TZ=$TZ +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ + && echo $TZ > /etc/timezone + +# Install all system dependencies in one layer +RUN apt-get update && apt-get install -y \ + # Build tools + build-essential \ + cmake \ + pkg-config \ + ninja-build \ + python3-dev \ + pybind11-dev \ + # ODBC prerequisites (unixodbc-dev provides sql.h headers for compilation) + unixodbc-dev \ + gnupg \ + software-properties-common \ + # Utilities + curl \ + wget \ + git \ + vim \ + nano \ + htop \ + tree \ + jq \ + # Clean up + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Install Microsoft ODBC Driver for SQL Server +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg \ + && 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 \ + && apt-get update \ + && ACCEPT_EULA=Y apt-get install -y msodbcsql18 \ + && ACCEPT_EULA=Y apt-get install -y mssql-tools18 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Add mssql-tools to PATH +ENV PATH="$PATH:/opt/mssql-tools18/bin" + +# Set the default user to vscode (created by the base image) +USER vscode + +# Set the working directory +WORKDIR /workspaces/mssql-python + +# Default command +CMD ["/bin/bash"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..39ae36e4 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,73 @@ +{ + "name": "MSSQL Python Driver", + "build": { + "dockerfile": "Dockerfile", + "context": "." + }, + "customizations": { + "vscode": { + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.terminal.activateEnvironment": false, + "editor.formatOnSave": true, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "cmake.configureOnOpen": false, + "cmake.configureOnEdit": false, + "cmake.automaticReconfigure": false, + "files.exclude": { + "**/__pycache__": true, + "**/*.pyc": true, + "**/.pytest_cache": true, + "**/build": true + } + }, + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.pylint", + "ms-python.black-formatter", + "ms-toolsai.jupyter", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "github.copilot", + "github.copilot-chat", + "ms-vscode.test-adapter-converter", + "littlefoxteam.vscode-python-test-adapter", + "ms-azuretools.vscode-docker", + "ms-mssql.mssql" + ] + } + }, + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": true, + "configureZshAsDefaultShell": true, + "installOhMyZsh": true, + "upgradePackages": true, + "username": "vscode", + "userUid": "automatic", + "userGid": "automatic" + } + }, + "forwardPorts": [1433], + "portsAttributes": { + "1433": { + "label": "SQL Server", + "onAutoForward": "notify" + } + }, + "postCreateCommand": "bash .devcontainer/post-create.sh", + "remoteUser": "vscode", + "containerEnv": { + "PYTHONPATH": "/workspaces/mssql-python", + "CMAKE_BUILD_TYPE": "Debug" + }, + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/mssql-python,type=bind,consistency=cached", + "workspaceFolder": "/workspaces/mssql-python" +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 00000000..63062828 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +# Post-create script for MSSQL Python Driver devcontainer +set -e + +echo "🚀 Setting up MSSQL Python Driver development environment..." + +# Install Python packages from requirements.txt +echo "📦 Installing Python packages..." +pip install --upgrade pip setuptools wheel +pip install -r requirements.txt + +# Create symlink for 'python' command (build.sh expects it) +echo "🔗 Creating python symlink..." +sudo ln -sf $(which python3) /usr/local/bin/python + +# Set up useful shell aliases (for both bash and zsh) +echo "⚡ Setting up aliases..." +cat > ~/.shell_aliases << 'EOF' +# MSSQL Python Driver development aliases +alias build='cd /workspaces/mssql-python/mssql_python/pybind && ./build.sh && cd /workspaces/mssql-python' +alias test='python -m pytest -v' +EOF + +# Ensure aliases are sourced in both shells +grep -qxF 'source ~/.shell_aliases' ~/.bashrc 2>/dev/null || echo 'source ~/.shell_aliases' >> ~/.bashrc +grep -qxF 'source ~/.shell_aliases' ~/.zshrc 2>/dev/null || echo 'source ~/.shell_aliases' >> ~/.zshrc + +# Verify environment +echo "" +echo "🔍 Verifying environment..." +python --version +pip --version +cmake --version +if command -v sqlcmd &> /dev/null; then + echo "✅ sqlcmd available" +else + echo "❌ sqlcmd not found" +fi + +# Build the C++ extension +echo "" +echo "🔨 Building C++ extension..." +if cd mssql_python/pybind && ./build.sh && cd ../..; then + echo "✅ C++ extension built successfully" +else + echo "❌ C++ extension build failed!" + exit 1 +fi + +# Generate random password for SQL Server +echo "" +echo "Generating SQL Server password..." +SA_PASSWORD="$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | head -c 16)Aa1!" +echo "$SA_PASSWORD" > /tmp/.sqlserver_sa_password +chmod 600 /tmp/.sqlserver_sa_password + +# Start SQL Server container (use Azure SQL Edge for ARM64 compatibility) +# This is optional - if Docker-in-Docker fails, the devcontainer still works +echo "" +echo "Starting SQL Server container (optional)..." + +ARCH=$(uname -m) +if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + echo "Detected ARM64 - using Azure SQL Edge..." + docker run -e 'ACCEPT_EULA=Y' -e "MSSQL_SA_PASSWORD=$SA_PASSWORD" \ + -p 1433:1433 --name sqlserver \ + -d mcr.microsoft.com/azure-sql-edge:latest && SQL_STARTED=true || SQL_STARTED=false +else + echo "Detected x86_64 - using SQL Server 2025..." + docker run -e 'ACCEPT_EULA=Y' -e "MSSQL_SA_PASSWORD=$SA_PASSWORD" \ + -p 1433:1433 --name sqlserver \ + -d mcr.microsoft.com/mssql/server:2025-latest && SQL_STARTED=true || SQL_STARTED=false +fi + +if [ "$SQL_STARTED" = "true" ]; then + echo "Waiting for SQL Server to start..." + sleep 15 +else + echo "WARNING: SQL Server container failed to start (Docker issue)" + echo " You can start it manually later with:" + 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" +fi + +# Set DB_CONNECTION_STRING environment variable (persist across all terminals) +DB_CONNECTION_STRING="Server=localhost,1433;Database=master;UID=sa;PWD=$SA_PASSWORD;TrustServerCertificate=Yes;Encrypt=Yes" + +# Write to /etc/environment for system-wide persistence +echo "DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" | sudo tee -a /etc/environment > /dev/null + +# Also add to shell rc files for immediate availability in new terminals +echo "export DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" >> ~/.bashrc +echo "export DB_CONNECTION_STRING=\"$DB_CONNECTION_STRING\"" >> ~/.zshrc + +# Export for current session +export DB_CONNECTION_STRING + +# Display completion message and next steps +echo "" +echo "==============================================" +echo "🎉 Dev environment setup complete!" +echo "==============================================" +echo "" +echo "📦 What's ready:" +echo " ✅ C++ extension built" +echo " ✅ SQL Server running (localhost:1433)" +echo " ✅ DB_CONNECTION_STRING set in environment" +echo "" +echo "🚀 Quick start - just type these commands:" +echo " python main.py → Test the connection" +echo " test → Run all pytest tests" +echo " build → Rebuild C++ extension" +echo "" +echo "==============================================" +echo "" diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..8c2fe614 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,115 @@ +# Git +.git +.gitignore +.gitattributes + +# Python +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +env +pip-log.txt +pip-delete-this-directory.txt +.tox +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.log +.mypy_cache +.pytest_cache +.hypothesis + +# Distribution / packaging +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +htmlcov/ +.coverage +.coverage.* +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Documentation +docs/_build/ + +# PyBuilder +target/ + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Temporary files +*.tmp +*.temp +*.log + +# Node.js (if any) +node_modules/ +npm-debug.log* + +# CMake +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +*.cmake + +# Build artifacts +*.so +*.dll +*.dylib +*.pdb +*.obj +*.exe + +# Test results +test-results/ +.pytest_cache/ + +# Azure DevOps +.azure/