【问题标题】:Run Azure Pipelines job only when certain files are changed仅在更改某些文件时运行 Azure Pipelines 作业
【发布时间】:2020-08-11 14:27:28
【问题描述】:

我有一个包含 Dockerfile 和其他代码的存储库。无论发生了什么变化,我们都希望运行某些步骤,但只想在 **/Dockerfile 发生变化时运行 docker build 作业。

我已经查看了 Conditions documentationExpressions documentation,但我不清楚如何(如果可能的话)如何将它们结合起来或以其他方式达到预期的结果。

我意识到在 bash(例如 git rev-list...git diff --name-only <previous_commit>..HEAD | grep <pattern>)中是可能的,但这有点麻烦,它仍然在 Azure Pipelines 中显示该作业已运行,只是短路了。理想情况下,它会(适当地)显示该作业被一起跳过。

我也意识到 Docker 部分和代码部分可以位于具有单独构建触发器的不同存储库中,但如果可能的话,我希望将它们放在同一个存储库中。

【问题讨论】:

  • 我不确定您所描述的是否可行。也许如果 docker build 作业依赖于另一个根据 Dockerfile 是否已更改而成功或失败的作业?但即使文件本身没有改变,它的 输入 可能已经改变,不清楚为什么要跳过检查它是否可以构建。
  • @jonrsharpe 这主要是为了节省构建时间——就输入达成一致,没有考虑到这一点。现在倾向于将 docker 部分提取到另一个 repo。感谢您的意见。
  • 您可以创建一个名为 Should run docker? 的作业并在其中设置一个变量,然后在作业 Building docker container 上使用该变量,这样就可以清楚地知道是否构建了 docker。

标签: git azure-pipelines azure-repos azure-pipelines-yaml


【解决方案1】:

您可以在每个触发器上定义 paths/includepaths/exclude 过滤器。我在常规文档网站上找不到这个,但是 the YAML repo clearly explains it:

例子:

trigger:
  batch: true
  branches:
    include:
    - features/*
    exclude:
    - features/experimental/*
  paths:
    include:
    - **/Dockerfile

PS:不完全确定是否支持通配符以及使用什么语法。

【讨论】:

  • 原谅我的无知(我对 Azure Pipelines 还很陌生),但是有没有办法为每个作业附加一个触发器?从文档中可以看出,触发器与整个管道相关联,而不是管道中的作业或阶段。
  • @lukiffer 设计上我们不能为每个工作设置触发器,看看我的想法是否有帮助:)
【解决方案2】:

抱歉,每个作业都没有触发器。触发器用于管道范围。

根据您的要求,您可以将此结构作为解决方法:

jobs: 
  - job: OtherSteps
    steps:
    Your other steps in this job.
    - task: PowerShell@2
      inputs:
        targetType: 'inline'
        script: |
          $changedfiles = git diff ... (Choose right git command depending on your needs.)
          Write-Host $changedfiles
          If ($changedfiles.Contains("Dockerfile"))  {
            echo "##vso[task.setvariable variable=IfRunDockerBuild;isOutput=true]run"
          }
      name: 'DetermineIfRunNextJob'

  - job: DockerBuild
    dependsOn: OtherSteps
    condition: eq(dependencies.OtherSteps.outputs['DetermineIfRunNextJob.IfRunDockerBuild'],'run')
    steps:
    - script: echo Only run this job when IfRunDockerBuild=run instead of Null!

1.假设你有job1和job2(docker build),你只需要在job1的末尾添加一个类似上面的PS任务。然后它输出一个变量来决定我们是否需要运行 job2 或跳过它。

2.Powershell task 可以在 Linux、macOS 或 Windows 上运行。

2.这个变通方法的核心来自这个特性:Use the output variable from a job in a condition in a subsequent job

【讨论】:

  • 这很好。试图避免我在原始问题中提到的git diff 部分,但这是假设它是一步完成的。将逻辑提取到前一个步骤中似乎是合乎逻辑的。
  • 请记住:如果您的构建配置为进行批处理,您可能会构建超过 1 个提交。在这种情况下,您可能需要使用 REST API 来获取最后一次成功的提交哈希来进行比较。拥有一个单独的构建定义并不是一个坏主意;)。
  • 我们使用semantic-release,它会在成功构建时添加一个 semver 标签。我正在通过树回顾最新的标签并针对它运行差异。但是,是的,仍然强烈考虑仅提取这些位。
【解决方案3】:

虽然这个问题很老,但我遇到了同样的问题,我认为我有一个很好的解决方案。挑战在于确保解决方案在同时推送多个提交、构建失败(因此未部署)或仅在主分支上进行部署的 PR 合并时也能正常工作。

我已在此要点中完整描述了我的解决方案:https://gist.github.com/Myrddraal/f5a84cf3242e3b3804fa727005ed2786

它利用了管道 API,它可以提供自上次成功执行管道以来所有提交的列表。这确保了它即使在一次推送多个提交时,或者在基础架构更改的构建失败时也能正常工作。管道 API 会努力确定哪些提交需要检查。

逻辑在这个powershell中:

[CmdletBinding()]
param (
  $authorisation,
  $pathFilter,
  $collectionUri,
  $project,
  $buildId
)

$changesUrl = "$collectionUri/$project/_apis/build/builds/$buildId/changes?api-version=6.0"
$changesResponse = Invoke-RestMethod -Uri $changesUrl -Headers @{Authorization = $authorisation } -Method Get
$commits = @($changesResponse.value | ForEach-Object { $_.id })

Write-Host "##vso[task.setvariable variable=filesUpdated;isOutput=true]False"
Write-Host "Checking $($commits.Length) commits for changes matching path $pathFilter"
for ($j = 0; $j -lt $commits.Length; $j++) {
  Write-Host "Checking commit: $($commits[$j]) with its parent"
  $files = $(git diff "$($commits[$j])~" $commits[$j] --name-only)
  Write-Host $files
  if ($files -like "*$pathFilter/*") {
    Write-Host "Found file matching path filter in commit $($commits[$j])"
    Write-Host "##vso[task.setvariable variable=filesUpdated;isOutput=true]True"
    break
  }
}

使用以下 YAML 调用它(在拉取存储库后的构建作业中):

  - task: PowerShell@2
    inputs:
      filePath: "azure-pipelines/Test-ChangesMadeInPath.ps1"
      arguments: >-
        -authorisation "Bearer $(system.accesstoken)" 
        -pathFilter "azure-pipelines/deployment" 
        -buildId $(Build.BuildId)'
        -collectionUri $(System.CollectionUri)
        -project $(System.TeamProject)
    name: DetermineChangesMadeInDeploymentFolder
    env:
      SYSTEM_ACCESSTOKEN: $(system.accesstoken)

然后将以下条件添加到您的部署作业中:

  - deployment: DeployInfrastructure
    condition: eq(stageDependencies.Build.BuildJob.outputs['DetermineChangesMadeInDeploymentFolder.filesUpdated'], 'True')
    displayName: Deploy infrastructure
    environment: "prod"
    strategy:
      runOnce:
        deploy:
          steps:
            - template: deployment/YAML/deploy-infrastructure.yml
              parameters:
                environment: $(Environment.Name)

【讨论】:

    猜你喜欢
    • 2021-04-02
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-31
    • 2011-09-10
    • 1970-01-01
    相关资源
    最近更新 更多