Claude
Skills
Sign in
Back

ado-windows-git-bash-compatibility

Included with Lifetime
$97 forever

Windows and Git Bash compatibility for Azure Pipelines. PROACTIVELY activate for: (1) pipelines that pass on Linux but fail on Windows agents, (2) path conversion issues in pipeline scripts (MSYS, MINGW), (3) shell detection (bash@3, pwsh@2, powershell@2 task selection), (4) MSYS_NO_PATHCONV and MSYS2_ARG_CONV_EXCL workarounds, (5) Windows self-hosted agent configuration, (6) cross-platform script patterns, (7) line-ending issues (CRLF vs LF) in checked-in scripts, (8) Docker on Windows agents, (9) UNC and long-path limitations. Provides: shell-task selection matrix, path-conversion env-var recipes, line-ending fixes, and a Windows-specific pipeline troubleshooting playbook.

Ads & Marketing

What this skill does


# Azure Pipelines: Windows & Git Bash Compatibility

## Overview

Azure Pipelines frequently run on Windows agents, and teams often use Git Bash for scripting. This creates path conversion and shell compatibility challenges that can cause pipeline failures. This guide provides comprehensive solutions for Windows/Git Bash integration in Azure DevOps pipelines.

## Critical Windows Agent Facts

### Git Bash Integration

**Microsoft's Official Position:**
- Microsoft advises **avoiding mintty-based shells** (like git-bash) for agent configuration
- mintty is not fully compatible with native Windows Input/Output API
- However, Git Bash tasks in pipelines are widely used and supported

**Git Version Management:**
- Windows agents use Git bundled with agent software by default
- Microsoft recommends using bundled Git version
- Override available via `System.PreferGitFromPath=true`

**Git Bash Location on Windows Agents:**
```text
C:\Program Files (x86)\Git\usr\bin\bash.exe
C:\Program Files\Git\usr\bin\bash.exe
```

## Path Conversion Issues in Pipelines

### The Core Problem

When using Bash tasks on Windows agents, Azure DevOps variables return Windows-style paths, but Git Bash (MINGW) performs automatic path conversion that can cause issues.

### Common Failure Patterns

#### Issue 1: Backslash Escape in Bash
```yaml
# ❌ FAILS - Backslashes treated as escape characters
- bash: |
    cd $(System.DefaultWorkingDirectory)  # d:\a\s\1 becomes d:as1
```

**Solution:**
```yaml
# ✅ CORRECT - Use forward slashes or variable properly
- bash: |
    cd "$BUILD_SOURCESDIRECTORY"
    # Or use PWD variable which is already set correctly
    echo "Working in: $PWD"
```

#### Issue 2: Path Variables in Arguments
```yaml
# ❌ FAILS - MINGW converts /d /s style arguments
- bash: |
    my-tool /d $(Build.SourcesDirectory)
```

**Solution:**
```yaml
# ✅ CORRECT - Use double slashes or environment variable
- bash: |
    export MSYS_NO_PATHCONV=1
    my-tool /d $(Build.SourcesDirectory)
    unset MSYS_NO_PATHCONV
```

#### Issue 3: Colon-Separated Path Lists
```yaml
# ❌ FAILS - MINGW converts colon-separated Windows paths
- bash: |
    export PATH="/usr/bin:$(Agent.ToolsDirectory)"
```

**Solution:**
```yaml
# ✅ CORRECT - Use semicolon for Windows or convert properly
- bash: |
    # For Windows-style paths
    export PATH="/usr/bin;$(Agent.ToolsDirectory)"
```

## Shell Detection in Pipeline Scripts

### Method 1: Using $OSTYPE (Bash-Specific)

```yaml
- bash: |
    case "$OSTYPE" in
      linux-gnu*)
        echo "Running on Linux agent"
        BUILD_PATH="$(Build.SourcesDirectory)"
        ;;
      darwin*)
        echo "Running on macOS agent"
        BUILD_PATH="$(Build.SourcesDirectory)"
        ;;
      msys*|mingw*|cygwin*)
        echo "Running on Windows agent with Git Bash"
        # Windows paths already work in MINGW, but may need conversion
        BUILD_PATH="$(Build.SourcesDirectory)"
        export MSYS_NO_PATHCONV=1
        ;;
      *)
        echo "Unknown OS: $OSTYPE"
        BUILD_PATH="$(Build.SourcesDirectory)"
        ;;
    esac

    echo "Build path: $BUILD_PATH"
    cd "$BUILD_PATH"
  displayName: 'Cross-platform path handling'
```

### Method 2: Using uname (Most Portable)

```yaml
- bash: |
    OS_TYPE=$(uname -s)

    case "$OS_TYPE" in
      Darwin*)
        echo "macOS agent detected"
        ;;
      Linux*)
        echo "Linux agent detected"
        # Check if WSL
        if grep -qi microsoft /proc/version 2>/dev/null; then
          echo "Running in WSL"
        fi
        ;;
      MINGW64*|MINGW32*)
        echo "Git Bash on Windows detected"
        export MSYS_NO_PATHCONV=1
        ;;
      CYGWIN*)
        echo "Cygwin on Windows detected"
        ;;
      MSYS_NT*)
        echo "MSYS on Windows detected"
        export MSYS_NO_PATHCONV=1
        ;;
      *)
        echo "Unknown OS: $OS_TYPE"
        ;;
    esac
  displayName: 'Detect shell environment'
```

### Method 3: Using Agent.OS (Azure Pipelines Variable)

```yaml
- bash: |
    if [ "$(Agent.OS)" = "Windows_NT" ]; then
      echo "Windows agent - applying MINGW path handling"
      export MSYS_NO_PATHCONV=1
    elif [ "$(Agent.OS)" = "Linux" ]; then
      echo "Linux agent"
    elif [ "$(Agent.OS)" = "Darwin" ]; then
      echo "macOS agent"
    fi
  displayName: 'Agent-specific configuration'
```

## Path Conversion Control

### MSYS_NO_PATHCONV (Primary Method)

Disables ALL automatic path conversion in MINGW/Git Bash:

```yaml
- bash: |
    # Disable path conversion for this script
    export MSYS_NO_PATHCONV=1

    # Now Windows paths work as-is
    dotnet build /p:Configuration=Release
    docker run -v "$(Build.SourcesDirectory):/workspace" myimage

    # Optionally re-enable
    unset MSYS_NO_PATHCONV
  displayName: 'Build with path conversion disabled'
```

### MSYS2_ARG_CONV_EXCL (Selective Exclusion)

Exclude specific argument patterns from conversion:

```yaml
- bash: |
    # Exclude specific prefixes from conversion
    export MSYS2_ARG_CONV_EXCL="--config=;/p:"

    dotnet build /p:Configuration=Release --config=$(Build.SourcesDirectory)/app.config
  displayName: 'Selective path conversion'
```

### Manual Conversion with cygpath

Convert between Windows and Unix paths explicitly:

```yaml
- bash: |
    # Convert Windows path to Unix
    UNIX_PATH=$(cygpath -u "$(Build.SourcesDirectory)")
    echo "Unix path: $UNIX_PATH"

    # Convert Unix path to Windows
    WINDOWS_PATH=$(cygpath -w "$PWD")
    echo "Windows path: $WINDOWS_PATH"

    # Mixed format (forward slashes with drive letter)
    MIXED_PATH=$(cygpath -m "$(Build.SourcesDirectory)")
    echo "Mixed path: $MIXED_PATH"
  displayName: 'Path conversion examples'
```

## Cross-Platform Pipeline Patterns

### Pattern 1: Platform-Specific Steps with Conditions

```yaml
jobs:
  - job: CrossPlatformBuild
    strategy:
      matrix:
        Linux:
          imageName: 'ubuntu-24.04'
          osType: 'Linux'
        Windows:
          imageName: 'windows-2025'
          osType: 'Windows_NT'
        macOS:
          imageName: 'macOS-15'
          osType: 'Darwin'
    pool:
      vmImage: $(imageName)

    steps:
      # Windows-specific setup
      - bash: |
          export MSYS_NO_PATHCONV=1
          echo "Windows Git Bash configuration applied"
        condition: eq(variables['Agent.OS'], 'Windows_NT')
        displayName: 'Windows Git Bash setup'

      # Cross-platform build
      - bash: |
          echo "Building on: $(Agent.OS)"
          cd "$(Build.SourcesDirectory)"
          npm install
          npm run build
        displayName: 'Cross-platform build'
```

### Pattern 2: Reusable Template with Platform Detection

```yaml
# File: templates/cross-platform-script.yml
parameters:
  - name: script
    type: string

steps:
  - bash: |
      # Auto-detect Windows and apply MSYS configuration
      if [ "$(Agent.OS)" = "Windows_NT" ]; then
        export MSYS_NO_PATHCONV=1
      fi

      # Run provided script
      ${{ parameters.script }}
    displayName: 'Cross-platform script execution'

# Usage in main pipeline:
steps:
  - template: templates/cross-platform-script.yml
    parameters:
      script: |
        dotnet build /p:Configuration=Release
        dotnet test --no-build
```

### Pattern 3: PowerShell for Windows, Bash for Unix

```yaml
- pwsh: |
    Write-Host "Building on Windows with PowerShell"
    dotnet build /p:Configuration=Release
  condition: eq(variables['Agent.OS'], 'Windows_NT')
  displayName: 'Windows build (PowerShell)'

- bash: |
    echo "Building on Unix with Bash"
    dotnet build -p:Configuration=Release
  condition: ne(variables['Agent.OS'], 'Windows_NT')
  displayName: 'Unix build (Bash)'
```

## Azure DevOps CLI on Windows Agents

### Common CLI Path Issues

```yaml
# ❌ FAILS - Windows paths in bash arguments
- bash: |
    az pipelines run --id 123 --variables sourceDir=$(Build.SourcesDirectory)
```

**Solution:**
```yaml
# ✅ CORRECT - Use MSYS_NO_PATHCONV or proper quoting

Related in Ads & Marketing