基于子文件夹的更改触发Azure DevOps构建

Sli*_*icc 13 git azure-devops

我有一个包含多个项目的Visual Studio解决方案,每个项目都是一个单独的微服务。对于开发团队来说,将所有服务都放在同一解决方案和git repo中非常方便,因为服务可以相互调用。

Master.sln - SubFolderA - MicroserviceA.sln
           - SubFolderB - MicroserviceB.sln
           - SubFolderC - MicroserviceC.sln
Run Code Online (Sandbox Code Playgroud)

但是,我想在Azure DevOps发生更改时独立构建/发布单个微服务,因此,如果ServiceA是唯一要更改的服务,则ServiceA是唯一构建和部署的服务。

为此,我创建了一个新的构建管道定义,其中设置了“路径过滤器”,以在微服务文件夹的内容更改时触发构建(因此,要监视的每个微服务都添加了一个路径过滤器)。

我的问题是,当触发构建时(例如,基于对SubFolderA的更改),我无法告诉构建定义仅在SubFolderA中构建.sln文件。

我可以为每个微服务创建一个单独的构建定义,并在单独的子文件夹上触发每个构建,但这会产生很大的开销,即我需要维护15个单独的构建定义(对于我构建的每个分支也是如此),以及所需的存储空间在我们的自托管构建代理上,现在将是NumberOfService x NumberOfBranchesBeingBuild x SizeOfRepo。

有没有一种方法可以使用带有git“路径过滤器”和多个定义路径的单个构建定义,这反过来又启动了多个构建实例,并将触发构建的路径的值输入到构建定义中,因此告诉构建实例要建立哪个.sln文件?

我希望这是有道理的!

小智 29

在触发器选项卡上,有一个选项可以指定要构建的项目的路径。指定该路径后,只有包含与包含/排除规则匹配的修改的提交才会触发构建。

就我而言,这是一个比 PowerShell 脚本更好的解决方案,PowerShell 脚本仍然会触发所有项目的构建和发布,这些项目会向我们的 Slack 发送垃圾邮件,并在我们的项目历史中填充垃圾。

在此处输入图片说明

  • 无论您添加多少个路径过滤器,它仍然会构建整个解决方案而不是指定的解决方案,对吗?如果是这样,那就无法满足用户的要求,即在存储库中从多个解决方案中构建一个解决方案。 (3认同)
  • @格雷格是正确的。如果我设置 3 个路径过滤器(libraryA、libraryB 和libraryC),则每次对这些文件夹中的任何一个进行更改时,所有三个文件夹都将被重建。我相信OP只想重建实际上已经改变的那个。所以这个解决方案实际上并不能满足OP的要求...... (3认同)
  • 这确实是最优雅的解决方案。但是,应该注意的是,非云托管源不支持路径过滤器。因此,如果您有“Azure Repos Git”、“GitHub”、“Bitbucket Cloud”等作为源,则可以使用上面的解决方案。但是,如果您使用“其他 Git”选项作为源(像我一样),则不幸的是,上述解决方案不可用。 (2认同)

小智 17

这篇文章对我帮助很大,所以我想对我的流程进行一些有用的修改。

我发现的第一个主要问题是这个 git diff 命令不能一次处理多个提交。

git diff HEAD HEAD~ --name-only
Run Code Online (Sandbox Code Playgroud)

HEAD~ 只查看 1 次提交,因为一次推送可以一次包含多个提交。

我意识到自从管道上次成功运行以来,我需要在 HEAD 和提交 id之间进行比较。

git diff HEAD [commit id of last successful build] --name-only
Run Code Online (Sandbox Code Playgroud)

此提交 ID 可通过在 /build/latest 端点 sourceVersion 调用 Azure DevOps API 来获得。

$response = (Invoke-RestMethod -Uri $url -Method GET -Headers $AzureDevOpsAuthenicationHeader)
$editedFiles = (git diff HEAD $response.sourceVersion --name-only)
Run Code Online (Sandbox Code Playgroud)

我还对查找更改的项目/模块文件夹的逻辑进行了修改。我不想每次通过硬编码项目名称添加新项目时都必须修改我的 PowerShell 脚本。

$editedFiles | ForEach-Object { 
    $sepIndex = $_.IndexOf('/')
    if($sepIndex -gt 0) {
        $projectName = $_.substring(0, $sepIndex)
        AppendQueueVariable $projectName
    }
}
Run Code Online (Sandbox Code Playgroud)

AppendQueueVariable 将维护所有更改项目的列表以返回到管道。

最后,我获取排队项目的列表并将它们传递到我的 Maven 多模块构建管道任务中。

mvn -amd -pl [list returned from PS task] clean install
Run Code Online (Sandbox Code Playgroud)

  • 最佳答案在这里。现在在我们的项目中做完全相同的事情。 (2认同)

Jay*_*ran 15

你可以像下面这样

  1. 根据您的微服务创建变量,其值为“ False”

例如,MicroserviceAUpdated=“ False”,MicroserviceBUpdated=“ False”等,

  1. 在构建定义的开头添加Powershell脚本任务。powershell脚本将执行以下操作:

在构建中获取变更集/提交,以检查哪些文件已更改。

  • MicroserviceAUpdated如果仅在下更改任何文件,请将变量更新为“ true” SubFolderA
  • MicroserviceBUpdated如果仅
    在下更改任何文件,请将变量更新为“ true” SubFolderA

等等...

  1. 为每个微服务创建单独的构建任务,配置构建任务以在如下所示的自定义条件下运行

对于MicroserviceA构建任务

“自定义条件”: and(succeeded(), eq(variables['MicroserviceAUpdated'], 'True'))

对于MicroserviceB构建任务

“自定义条件”: and(succeeded(), eq(variables['MicroserviceBUpdated'], 'True'))

等等...

如果变量的值为,则将跳过MicoserviceTask False

对于步骤2

$files=$(git diff HEAD HEAD~ --name-only)
$temp=$files -split ' '
$count=$temp.Length
echo "Total changed $count files"
For ($i=0; $i -lt $temp.Length; $i++)
{
  $name=$temp[$i]
  echo "this is $name file"
  if ($name -like "SubFolderA/*")
    {
      Write-Host "##vso[task.setvariable variable=MicroserviceAUpdated]True"
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我多年来一直使用相同的技术,但是,有些事情发生了变化,`git diff HEAD HEAD~ --name-only` 抛出错误:`致命:模棱两可的参数 'HEAD~':未知修订版或路径不在工作树中.` 不确定这是否与 AzDO 或 Git 代理相关。 (2认同)

小智 11

解决方案是每个服务都有一个 azure-pipelines.yml,位于子文件夹中。子文件夹内的每个 azure-pipelines.yml 必须具有以下触发器定义。

trigger:
  branches:
    include:
      - master
  paths:
    include:
      - <service subfolder name>/*
Run Code Online (Sandbox Code Playgroud)

yaml 的 paths -> include 部分告诉 azure pipeline 仅在该特定路径发生更改时才触发。

无需为子文件夹内的 azure-pipelines.yml 指定不同的名称,可以保留相同的名称。同样,没有必要将 azure-pipelines.yml 添加到存储库的根目录中,除非需要在子文件夹之外的根目录中构建一些代码。在这种情况下,必须将以下触发器部分添加到存储库根目录的 azure-pipelines.yml 中。

trigger:
  branches:
    include:
      - master
  paths:
    exclude:
      - <service subfolder name 1>/*
      - <service subfolder name 2>/*
Run Code Online (Sandbox Code Playgroud)

paths -> except 部分排除已包含自己的 azure-pipelines.yml 文件的子文件夹,并且仅当子文件夹外部的存储库根目录发生更改时才会触发。

博客更详细地解释了 monorepo 管道。


Tau*_*aul 9

Jayendran的回答非常好!这是执行步骤2的另一种PowerShell-y方法:

$editedFiles = git diff HEAD HEAD~ --name-only
$editedFiles | ForEach-Object {
    Switch -Wildcard ($_ ) {
        'SubFolderA/*' { Write-Output "##vso[task.setvariable variable=MicroserviceA]True" }
        # The rest of your path filters
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我注意到的一件事是,如果您一次推送多个提交,管道只会考虑最近的提交。因此,如果我编辑三个不同的项目并为每个项目进行单独的提交,则该脚本将仅评估最近的提交,从而导致仅构建一个项目。有没有办法编辑脚本或管道以针对所有推送的提交运行 git diff ? (5认同)

Laz*_*zer 9

为了补充 deleb 的答案,这里是设置路径触发器的 YAML 代码:

trigger:
  branches:
    include:
    - master
  paths:
    include:
    - /path/to/src*
Run Code Online (Sandbox Code Playgroud)

请注意,您还需要拥有分支触发器才能使用路径触发器。

https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/azure-repos-git?view=azure-devops&tabs=yaml#paths

  • 请注意,Azure DevOps 仅允许与 `^[^\/~\^\: \[\]\\]+(\/[^\/~\^\: \[\]\\ ]+)*$`,所以 `include` 路径应该是 `path/to/src*` (2认同)

Ste*_*ier 7

在 bash 中,您可以执行以下操作:

  - task: Bash@3
    displayName: 'Determine which apps were updated'
    inputs:
      targetType: 'inline'
      script: |
        DIFFS="$(git diff HEAD HEAD~ --name-only)"
        [[ "${DIFFS[@]}" =~ "packages/shared" ]] && echo "##vso[task.setvariable variable=SHARED_UPDATED]True"
        [[ "${DIFFS[@]}" =~ "packages/mobile" ]] && echo "##vso[task.setvariable variable=MOBILE_UPDATED]True"
        [[ "${DIFFS[@]}" =~ "packages/web" ]] && echo "##vso[task.setvariable variable=WEB_UPDATED]True"
Run Code Online (Sandbox Code Playgroud)

  • 您将需要添加 set +e,因为如果文件未更改,测试将返回退出代码 1,并且任务将在 Azure Pipelines 中失败 (3认同)