运行跨平台脚本

Azure Pipelines | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018

利用 Azure Pipelines,你可以在 macOS、Linux 和 Windows 计算机上运行生成。 如果你在跨平台技术(例如 .NET Core、Node.js 和 Python)上进行开发,则这些功能带来了好处和挑战。

例如,大多数管道包含一个或多个要在生成过程中运行的脚本。 但在不同的平台上,脚本通常不会以相同的方式运行。 下面是有关如何处理此类问题的一些提示。

使用脚本步骤运行跨平台工具

Script 关键字是 命令行任务的快捷方式。 script关键字在 Linux 和 macOS 上运行 Bash,并在 Windows 上运行 cmd.exe。

script当任务只将参数传递给跨平台工具时,使用会非常有用。 例如, npm 可以通过一个步骤轻松地完成使用一组参数调用 scriptscript在每个平台的本机脚本解释器中运行: Bash on macOS 和 Linux 上,cmd.exe Windows。

steps:
- script: |
    npm install
    npm test

处理环境变量

环境变量将引发首个用于编写跨平台脚本的工作。 命令行、PowerShell 和 Bash 都具有读取环境变量的不同方式。 如果需要访问操作系统提供的值(如路径),则需要为每个平台提供不同的技术。

但 Azure Pipelines 提供了一种跨平台的方法来引用它知道的有关被调用宏语法的变量。 在中将变量名称括 $( ) 起来后,它将在平台 shell 看到它之前进行扩展。 例如,如果你想要回显管道的 ID,则以下脚本是跨平台友好的:

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

这也适用于在管道中指定的变量。

variables:
  Example: 'myValue'

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

考虑 Bash 或 pwsh

如果你的脚本编写需求比上面显示的示例更复杂,请考虑在 Bash 中编写它们。 大多数 macOS 和 Linux 代理具有 Bash 作为可用的 shell,Windows 代理包括 Git bash 或适用于 Linux 的 Windows 子系统Bash。

对于 Azure Pipelines,Microsoft 托管的代理始终提供 Bash。

例如,如果需要根据这是否为拉取请求生成做出决定:

trigger:
    batch: true
    branches:
        include:
        - master
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

PowerShell Core (pwsh) 也是一个选项。 它要求每个代理都安装了 PowerShell Core。

基于平台进行切换

通常,我们建议你避免特定于平台的脚本,以避免出现重复的管道逻辑等问题。 重复会导致额外的工作和 bug 的额外风险。 但是,如果无法避免使用特定于平台的脚本,则可以使用来检测所使用的 condition 平台。

例如,由于某种原因,你需要生成代理的 IP 地址。 在 Windows 上,将 ipconfig 获取该信息。 在 macOS 上,它是 ifconfig 。 Ubuntu Linux,就是这样 ip addr

设置以下管道,然后尝试针对不同平台上的代理运行该管道。

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

# now we use the value, no matter where we got it
- script: |
    echo The IP address is $(IP_ADDR)