Azure pipelines cross-platform scripts

published on Thu, 01 Aug 2024


#Devops  #Azure Devops 

Azure pipelines cross-platform scripts

To keep things simple when setting up a CI/CD pipeline for a cross-platform application we cautiously have to consider our options for running the steps in what scripting language, because we don’t want to end up with scripts that do the same thing functionally, but are different for each platform, which means extra maintenance and complexity for the maintainers.

So we have to consider languages like Powershell, Bash, Zsh or Python for to run on Windows, macOS and Linux.

Bash or Powershell 

By default Linux and macOS come with Bash installed, except for Windows, though its likely that your Windows machine already has Git Bash or Window Subsystem for Linux installed.

I personally prefer Bash, because I find that the amount of text I need to write in Powershell in comparison to Bash to accomplish something is to big. Secondly, Bash is such a commonly used scripting language and it is so easy to find on the internet how to implement something that the productivity is much higher.

# Git for Windows v2.45.2
$ bash --version
GNU bash, version 5.2.26(1)-release (x86_64-pc-msys)

# Bash on Mac Pro 2023 (12.7.5 Monterey)
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin21)
Copyright (C) 2007 Free Software Foundation, Inc.

# Bash on Mac Mini M1 2020 (14.5 Sonoma)
$ bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin23)
Copyright (C) 2007 Free Software Foundation, Inc.

# Bash on Ubuntu (20.24 Focal Fossa)
$ bash --version
GNU bash, version 5.0.6(1)-release (x86_64)

As you can see there are quite some differences in versions between platforms. Especially, macOS comes with an ancient version of Bash, which reduces the amount of modern Bash feature you can use. Depending on your needs this could be a limiting factor, though with you’ll often find that with a limiting amount of changes you can achieve the same thing.

Preparing Git for Windows 

To prepare the Windows agent install Git for Windows and make sure to add the C:\Program Files\Git\bin directory to the PATH.

The pipeline definition 

Let get started by creating a azure-pipelines.yml file and setup our platform parameters so that the pipeline can conditionally be run. Next we’ll utilize a jobs strategy matrix to start the pipeline on the selected platforms' agents.

Resulting in each agent running the step “Getting started” that will print something to our terminal.

# azure-pipelines.yml

name: cross_platform_ci

parameters:
  - name: windows_vs_2022
    type: boolean
    default: true
  - name: macOS_x86_64
    type: boolean
    default: true
  - name: macos_arm64
    type: boolean
    default: true
  - name: ubuntu_x86_64
    type: boolean
    default: true

pool:
  name: $(poolName)

jobs:
  - job:
    displayName: "Pipeline job"
    strategy: # https://learn.microsoft.com/en-us/azure/devops/pipelines/yaml-schema/jobs-job-strategy
      matrix:
        ${{ if eq(parameters.windows_vs_2022, true) }}:
          windows_vs_2022:
            poolName: "windows_vs_2022"

        ${{ if eq(parameters.macOS_x86_64, true) }}:
          macOS_x86_64:
            poolName: "macOS_x86_64"

        ${{ if eq(parameters.macos_arm64, true) }}:
          macOS_arm64:
            poolName: "macos_arm64"

        ${{ if eq(parameters.ubuntu_x86_64, true) }}:
          ubuntu_x86_64:
            poolName: "ubuntu_x86_64"

    steps:
      - bash: |
          set -euo pipefail
          echo "Hello Azure pipeline from $AGENT_OS!"          
        displayName: "Getting started"

Failing a Bash step in Azure pipelines 

By default commands that are executed in a Bash step will not fail the pipeline if they returned a non-zero exit code, unless we explicitly tell bash to fail.

We can set these options using Bash Set.

I personally prefer setting the following options

set -euo pipefail

# -e Exit immediately if a pipeline returns a non-zero status
# -u Treat unset variables and parameters as an error when performing parameter expansion.
# -o pipefail Exit if a pipe returns a non-zero status