Edit

Share via


Cross-platform scripting

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022 | Azure DevOps Server 2020

Azure Pipelines can run pipelines on Linux, macOS, and Windows machines. If you use cross-platform development technologies like .NET Core, Node.js, or Python, these cross-platform build capabilities bring both benefits and challenges. For example, most pipelines include one or more scripts to run during the build process, but script syntax often differs over platforms.

This article explains how you can use cross-platform scripting to support different build platforms. You can use an Azure Pipelines script step to ease writing cross-platform scripts. You can also use conditions to target scripts to specific platforms.

Script step

The script keyword is a shortcut for the command line task, which runs Bash on Linux and macOS or cmd.exe on Windows.

You can use script to easily pass arguments to a cross-platform tool. The script step runs in each platform's native script interpreter, Bash on macOS and Linux or cmd.exe on Windows. The following example uses a script step to call npm with a set of arguments.

steps:
- script: |
    npm install
    npm test

Environment variables

Command line, PowerShell, and Bash resolve environment variables differently. To access a system-provided value like PATH, you must use a different syntax per platform.

Azure Pipelines uses macro syntax as a cross-platform way to refer to variables at runtime. Variables with macro syntax get processed before a task executes during runtime. The variable expands before the platform shell encounters it.

To use macro syntax in a pipeline, surround the variable name as follows: $(<variable name>). The following cross-platform example script echoes the ID of the pipeline.

steps:
- script: echo This is pipeline $(System.DefinitionId)

This syntax also works for variables you define within the pipeline.

variables:
  Example: 'myValue'

steps:
- script: echo The value passed in is $(Example)

Bash task

If you need more complex scripts, consider writing them in Bash and using the Bash task in your pipeline. Most macOS and Linux agents use Bash as a shell, and Windows agents can use Git Bash or Windows Subsystem for Linux Bash. Microsoft-hosted agents have Bash preinstalled by default.

The following example runs a Bash task that helps make a decision about whether to trigger a build.

trigger:
    batch: true
    branches:
        include:
        - main
steps:
- bash: |
    echo "Hello world from $AGENT_NAME running on $AGENT_OS"
    case $BUILD_REASON in
            "Manual") echo "$BUILD_REQUESTEDFOR manually queued the build." ;;
            "IndividualCI") echo "This is a CI build for $BUILD_REQUESTEDFOR." ;;
            "BatchedCI") echo "This is a batched CI build for $BUILD_REQUESTEDFOR." ;;
        *) $BUILD_REASON ;;
    esac
  displayName: Hello world

Note

PowerShell is also an option for scripts. The pwsh shortcut runs PowerShell 7.x on macOS, Linux, or Windows. Agents must have PowerShell 7.x installed. Microsoft-hosted agents have PowerShell 7.x installed by default.

Platform-based switching

Platform-specific scripting to duplicate pipeline logic causes extra work and increased error risk. But if you can't avoid platform-specific scripting, you can use conditions to detect what platform you're on.

For example, to get the IP address of the build agent, you must use ifconfig on macOS, ip addr on Ubuntu Linux, and the Get-NetIPAddress cmdlet on Windows PowerShell. The following pipeline gets that information from agents on different platforms by using conditions.

steps:
# Linux
- bash: |
    export IPADDR=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/')
    echo "##vso[task.setvariable variable=IP_ADDR]$IPADDR"
  condition: eq( variables['Agent.OS'], 'Linux' )
  displayName: Get IP on Linux
# macOS
- bash: |
    export IPADDR=$(ifconfig | grep 'en0' -A3 | grep inet | tail -n1 | awk '{print $2}')
    echo "##vso[task.setvariable variable=IP_ADDR]$IPADDR"
  condition: eq( variables['Agent.OS'], 'Darwin' )
  displayName: Get IP on macOS
# Windows
- powershell: |
    Set-Variable -Name IPADDR -Value ((Get-NetIPAddress | ?{ $_.AddressFamily -eq "IPv4" -and !($_.IPAddress -match "169") -and !($_.IPaddress -match "127") } | Select-Object -First 1).IPAddress)
    Write-Host "##vso[task.setvariable variable=IP_ADDR]$IPADDR"
  condition: eq( variables['Agent.OS'], 'Windows_NT' )
  displayName: Get IP on Windows

# use the value
- script: |
    echo The IP address is $(IP_ADDR)