Files
gh-josiahsiegel-claude-code…/skills/windows-git-bash-paths.md
2025-11-30 08:29:18 +08:00

530 lines
14 KiB
Markdown

---
name: windows-git-bash-paths
description: Windows and Git Bash path handling for SSDT, SqlPackage, and DACPAC files with shell detection
---
## 🚨 CRITICAL GUIDELINES
### Windows File Path Requirements
**MANDATORY: Always Use Backslashes on Windows for File Paths**
When using Edit or Write tools on Windows, you MUST use backslashes (`\`) in file paths, NOT forward slashes (`/`).
**Examples:**
- ❌ WRONG: `D:/repos/project/file.tsx`
- ✅ CORRECT: `D:\repos\project\file.tsx`
This applies to:
- Edit tool file_path parameter
- Write tool file_path parameter
- All file operations on Windows systems
### Documentation Guidelines
**NEVER create new documentation files unless explicitly requested by the user.**
- **Priority**: Update existing README.md files rather than creating new documentation
- **Repository cleanliness**: Keep repository root clean - only README.md unless user requests otherwise
- **Style**: Documentation should be concise, direct, and professional - avoid AI-generated tone
- **User preference**: Only create additional .md files when user specifically asks for documentation
---
# Windows and Git Bash Path Handling for SSDT
## Overview
SQL Server development is Windows-heavy, and many developers use Git Bash (MINGW/MSYS2) as their preferred shell on Windows. This creates unique path conversion challenges when working with Windows-native tools like SqlPackage, MSBuild, and Visual Studio that expect Windows-style paths.
This skill provides comprehensive guidance on handling path conversion issues, shell detection, and best practices for SSDT workflows on Windows with Git Bash.
## The Path Conversion Problem
### What Happens in Git Bash/MINGW
Git Bash automatically converts POSIX-style paths to Windows paths, but this can cause issues with command-line arguments:
**Automatic Conversions:**
- `/foo``C:/Program Files/Git/usr/foo`
- `/foo:/bar``C:\msys64\foo;C:\msys64\bar`
- `--dir=/foo``--dir=C:/msys64/foo`
**Problematic for SqlPackage:**
```bash
# Git Bash converts /Action to a path!
sqlpackage /Action:Publish /SourceFile:MyDB.dacpac
# Becomes: sqlpackage C:/Program Files/Git/usr/Action:Publish ...
```
### What Triggers Conversion
✓ Leading forward slash (/) in arguments
✓ Colon-separated path lists
✓ Arguments after `-` or `,` with path components
### What's Exempt
✓ Arguments containing `=` (variable assignments)
✓ Drive specifiers (`C:`)
✓ Arguments with `;` (already Windows format)
✓ Arguments starting with `//` (Windows switches)
## Solutions for SqlPackage in Git Bash
### Method 1: MSYS_NO_PATHCONV (Recommended)
Disable path conversion for specific commands:
```bash
# Temporarily disable path conversion
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"MyDatabase.dacpac" \
/TargetServerName:"localhost" \
/TargetDatabaseName:"MyDB"
# Works for all SqlPackage actions
MSYS_NO_PATHCONV=1 sqlpackage /Action:Extract \
/SourceConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;" \
/TargetFile:"MyDB_backup.dacpac"
```
**Important Notes:**
- The VALUE doesn't matter - setting to `0`, `false`, or empty still disables conversion
- Only matters that variable is DEFINED
- To re-enable: `env -u MSYS_NO_PATHCONV`
### Method 2: Double Slash // (Alternative)
Use double slashes for SqlPackage parameters:
```bash
# Works in Git Bash and CMD
sqlpackage //Action:Publish \
//SourceFile:MyDatabase.dacpac \
//TargetServerName:localhost \
//TargetDatabaseName:MyDB
# Extract with double slashes
sqlpackage //Action:Extract \
//SourceConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;" \
//TargetFile:output.dacpac
```
**Advantages:**
- No environment variable needed
- Works across shells
- Shell-agnostic scripts
### Method 3: Use Windows-Style Paths with Quotes
Always quote paths with backslashes:
```bash
# Quoted Windows paths work in Git Bash
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"D:\Projects\MyDB\bin\Release\MyDB.dacpac" \
/TargetConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;"
# Or with forward slashes (Windows accepts both)
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"D:/Projects/MyDB/bin/Release/MyDB.dacpac" \
/TargetConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;"
```
### Method 4: Switch to PowerShell or CMD
For Windows-native tools, consider using native shells:
```powershell
# PowerShell (recommended for Windows SSDT workflows)
sqlpackage /Action:Publish `
/SourceFile:"MyDatabase.dacpac" `
/TargetServerName:"localhost" `
/TargetDatabaseName:"MyDB"
```
```cmd
:: CMD
sqlpackage /Action:Publish ^
/SourceFile:"MyDatabase.dacpac" ^
/TargetServerName:"localhost" ^
/TargetDatabaseName:"MyDB"
```
## Shell Detection for Scripts
### Bash Script Detection
```bash
#!/bin/bash
# Method 1: Check $OSTYPE
case "$OSTYPE" in
msys*) # MSYS/Git Bash/MinGW
export MSYS_NO_PATHCONV=1
SQLPACKAGE_ARGS="/Action:Publish /SourceFile:MyDB.dacpac"
;;
cygwin*) # Cygwin
export MSYS_NO_PATHCONV=1
SQLPACKAGE_ARGS="/Action:Publish /SourceFile:MyDB.dacpac"
;;
linux-gnu*) # Linux
SQLPACKAGE_ARGS="/Action:Publish /SourceFile:MyDB.dacpac"
;;
darwin*) # macOS
SQLPACKAGE_ARGS="/Action:Publish /SourceFile:MyDB.dacpac"
;;
esac
sqlpackage $SQLPACKAGE_ARGS
```
```bash
# Method 2: Check uname -s (most portable)
case "$(uname -s)" in
MINGW64*|MINGW32*)
# Git Bash
export MSYS_NO_PATHCONV=1
echo "Git Bash detected - path conversion disabled"
;;
MSYS_NT*)
# MSYS
export MSYS_NO_PATHCONV=1
echo "MSYS detected - path conversion disabled"
;;
CYGWIN*)
# Cygwin
export MSYS_NO_PATHCONV=1
echo "Cygwin detected - path conversion disabled"
;;
Linux*)
# Linux or WSL
echo "Linux detected"
;;
Darwin*)
# macOS
echo "macOS detected"
;;
esac
```
```bash
# Method 3: Check $MSYSTEM (Git Bash specific)
if [ -n "$MSYSTEM" ]; then
# Running in Git Bash/MSYS2
export MSYS_NO_PATHCONV=1
echo "MSYS environment detected: $MSYSTEM"
case "$MSYSTEM" in
MINGW64) echo "64-bit native Windows environment" ;;
MINGW32) echo "32-bit native Windows environment" ;;
MSYS) echo "POSIX-compliant environment" ;;
esac
fi
```
### Complete Build Script Example
```bash
#!/bin/bash
# build-and-deploy.sh - Cross-platform SSDT build script
set -e # Exit on error
# Detect shell and set path conversion
if [ -n "$MSYSTEM" ]; then
echo "Git Bash/MSYS2 detected - disabling path conversion"
export MSYS_NO_PATHCONV=1
fi
# Variables
PROJECT_NAME="MyDatabase"
BUILD_CONFIG="Release"
DACPAC_PATH="bin/${BUILD_CONFIG}/${PROJECT_NAME}.dacpac"
TARGET_SERVER="${SQL_SERVER:-localhost}"
TARGET_DB="${SQL_DATABASE:-MyDB}"
# Build
echo "Building ${PROJECT_NAME}..."
dotnet build "${PROJECT_NAME}.sqlproj" -c "$BUILD_CONFIG"
# Verify DACPAC exists
if [ ! -f "$DACPAC_PATH" ]; then
echo "ERROR: DACPAC not found at $DACPAC_PATH"
exit 1
fi
echo "DACPAC built successfully: $DACPAC_PATH"
# Deploy
echo "Deploying to ${TARGET_SERVER}/${TARGET_DB}..."
# Use double-slash method for maximum compatibility
sqlpackage //Action:Publish \
//SourceFile:"$DACPAC_PATH" \
//TargetServerName:"$TARGET_SERVER" \
//TargetDatabaseName:"$TARGET_DB" \
//p:BlockOnPossibleDataLoss=False
echo "Deployment complete!"
```
## Common SSDT Path Issues in Git Bash
### Issue 1: DACPAC File Paths
**Problem:**
```bash
# Git Bash mangles the path
sqlpackage /Action:Publish /SourceFile:./bin/Release/MyDB.dacpac
# Error: Cannot find file
```
**Solution:**
```bash
# Use MSYS_NO_PATHCONV
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"./bin/Release/MyDB.dacpac"
# OR use absolute Windows path
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"D:/Projects/MyDB/bin/Release/MyDB.dacpac"
# OR use double slashes
sqlpackage //Action:Publish //SourceFile:./bin/Release/MyDB.dacpac
```
### Issue 2: SQL Project File Paths
**Problem:**
```bash
# Path with spaces causes issues
dotnet build "D:/Program Files/MyProject/Database.sqlproj"
# Works in Git Bash (dotnet handles paths correctly)
# But MSBuild paths may fail
msbuild "D:/Program Files/MyProject/Database.sqlproj"
# May fail if not quoted properly
```
**Solution:**
```bash
# Always quote paths with spaces
dotnet build "D:/Program Files/MyProject/Database.sqlproj"
# Use backslashes for MSBuild on Windows
msbuild "D:\Program Files\MyProject\Database.sqlproj"
# Or use 8.3 short names (no spaces)
msbuild "D:/PROGRA~1/MyProject/Database.sqlproj"
```
### Issue 3: Publish Profile Paths
**Problem:**
```bash
# Publish profile not found
sqlpackage /Action:Publish \
/SourceFile:MyDB.dacpac \
/Profile:./Profiles/Production.publish.xml
```
**Solution:**
```bash
# Use MSYS_NO_PATHCONV with quoted paths
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"MyDB.dacpac" \
/Profile:"./Profiles/Production.publish.xml"
# Or absolute Windows path
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish \
/SourceFile:"D:/Projects/MyDB.dacpac" \
/Profile:"D:/Projects/Profiles/Production.publish.xml"
```
### Issue 4: Connection Strings
**Problem:**
```bash
# File paths in connection strings
/SourceConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;AttachDbFilename=D:/Data/MyDB.mdf"
# Path gets mangled
```
**Solution:**
```bash
# Quote entire connection string
MSYS_NO_PATHCONV=1 sqlpackage /Action:Extract \
/SourceConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;AttachDbFilename=D:\Data\MyDB.mdf" \
/TargetFile:"output.dacpac"
# Or use double backslashes in connection string
/SourceConnectionString:"Server=localhost;Database=MyDB;Integrated Security=True;AttachDbFilename=D:\\Data\\MyDB.mdf"
```
## CI/CD Considerations
### GitHub Actions with Git Bash
```yaml
name: SSDT Build and Deploy
on: [push]
jobs:
build:
runs-on: windows-latest
defaults:
run:
shell: bash # Use Git Bash
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Install SqlPackage
run: dotnet tool install -g Microsoft.SqlPackage
- name: Build Database Project
run: dotnet build Database.sqlproj -c Release
- name: Deploy with Path Conversion Disabled
env:
MSYS_NO_PATHCONV: 1
run: |
sqlpackage /Action:Publish \
/SourceFile:"bin/Release/MyDatabase.dacpac" \
/TargetServerName:"localhost" \
/TargetDatabaseName:"MyDB"
```
### PowerShell Alternative (Recommended for Windows)
```yaml
jobs:
build:
runs-on: windows-latest
defaults:
run:
shell: pwsh # Use PowerShell - no path issues
steps:
- name: Deploy Database
run: |
sqlpackage /Action:Publish `
/SourceFile:"bin/Release/MyDatabase.dacpac" `
/TargetServerName:"localhost" `
/TargetDatabaseName:"MyDB"
```
## Best Practices Summary
### For Interactive Development
1. **Use PowerShell or CMD for SSDT on Windows** - avoids path conversion issues entirely
2. **If using Git Bash**, set `MSYS_NO_PATHCONV=1` in your shell profile for SSDT work
3. **Always quote paths** containing spaces or special characters
4. **Use absolute paths** when possible to avoid ambiguity
### For Scripts
1. **Detect shell environment** and set `MSYS_NO_PATHCONV=1` conditionally
2. **Use double-slash // syntax** for SqlPackage arguments (most portable)
3. **Prefer PowerShell for Windows-specific workflows** (build scripts, CI/CD)
4. **Test scripts on all target platforms** (Windows PowerShell, Git Bash, Linux)
### For CI/CD
1. **Use PowerShell shell in GitHub Actions** for Windows runners (`shell: pwsh`)
2. **Self-hosted Windows agents** - use native Windows paths and shells
3. **Set MSYS_NO_PATHCONV=1 as environment variable** if Git Bash required
4. **Prefer dotnet CLI over MSBuild** for cross-platform compatibility
### For Teams
1. **Document shell requirements** in repository README
2. **Provide scripts for all shells** (bash, PowerShell, CMD)
3. **Standardize on PowerShell** for Windows SSDT workflows when possible
4. **Use containerized builds** to avoid shell-specific issues
## Quick Reference
### Environment Variables
```bash
# Disable path conversion (Git Bash/MSYS2/Cygwin)
export MSYS_NO_PATHCONV=1
# Re-enable path conversion
env -u MSYS_NO_PATHCONV
```
### SqlPackage Command Templates
```bash
# Git Bash - Method 1 (MSYS_NO_PATHCONV)
MSYS_NO_PATHCONV=1 sqlpackage /Action:Publish /SourceFile:"MyDB.dacpac" /TargetServerName:"localhost" /TargetDatabaseName:"MyDB"
# Git Bash - Method 2 (Double Slash)
sqlpackage //Action:Publish //SourceFile:MyDB.dacpac //TargetServerName:localhost //TargetDatabaseName:MyDB
# PowerShell (Recommended for Windows)
sqlpackage /Action:Publish /SourceFile:"MyDB.dacpac" /TargetServerName:"localhost" /TargetDatabaseName:"MyDB"
# CMD
sqlpackage /Action:Publish /SourceFile:"MyDB.dacpac" /TargetServerName:"localhost" /TargetDatabaseName:"MyDB"
```
### Shell Detection One-Liners
```bash
# Check if Git Bash/MSYS
[ -n "$MSYSTEM" ] && echo "Git Bash/MSYS2 detected"
# Check uname
[[ "$(uname -s)" =~ ^MINGW ]] && echo "Git Bash detected"
# Set path conversion conditionally
[ -n "$MSYSTEM" ] && export MSYS_NO_PATHCONV=1
```
## Resources
- [Git Bash Path Conversion Guide](https://www.pascallandau.com/blog/setting-up-git-bash-mingw-msys2-on-windows/)
- [MSYS2 Path Conversion Documentation](https://www.msys2.org/docs/filesystem-paths/)
- [SqlPackage Documentation](https://learn.microsoft.com/sql/tools/sqlpackage/)
- [Microsoft.Build.Sql SDK](https://www.nuget.org/packages/Microsoft.Build.Sql)
- [Git for Windows](https://gitforwindows.org/)
## Troubleshooting
### "Invalid parameter" errors
**Symptom:** SqlPackage reports "Invalid parameter" or "Unknown action"
**Cause:** Git Bash converting `/Action` to a file path
**Fix:** Use `MSYS_NO_PATHCONV=1` or double-slash `//Action`
### "File not found" with valid paths
**Symptom:** DACPAC or project file not found despite correct path
**Cause:** Path conversion mangling the file path
**Fix:** Quote paths and use `MSYS_NO_PATHCONV=1`
### Build succeeds but publish fails
**Symptom:** `dotnet build` works but `sqlpackage` fails
**Cause:** dotnet CLI handles paths correctly, but SqlPackage uses `/` parameters
**Fix:** Use `MSYS_NO_PATHCONV=1` for SqlPackage commands only
### Spaces in paths cause errors
**Symptom:** Paths with "Program Files" or other spaces fail
**Cause:** Unquoted paths split into multiple arguments
**Fix:** Always quote paths: `/SourceFile:"D:/Program Files/MyDB.dacpac"`