Azure DevOps 发布流程中的条件性流程和手动干预

34次阅读
没有评论

问题描述

在使用Azure DevOps进行发布定义时,遇到了一个需求:在部署过程中,当部署SQL Server的DACPAC文件用于创建或更新数据库时,有时会因为潜在的数据丢失问题导致部署失败。而用户希望在这种情况下,不是绝对失败,而是触发手动干预,以便DBA(数据库管理员)审查更改。如果DBA确认后继续,希望重新尝试部署,但这次部署会使用一个标志来忽略潜在的数据丢失问题。最后,如果第二次数据库部署成功,希望流程继续进行到其他的部署任务。

用户希望的流程示意图如下:

Azure DevOps 发布流程中的条件性流程和手动干预

此外,用户还有其他一些要求:
1. 如果数据库部署失败,但手动干预成功,希望整个部署被标记为成功。
2. 希望在手动干预后的流程能够合并到在数据库部署成功后要运行的现有任务中,即不想维护具有相同任务的双重流程。

用户当前使用的是设计器(Web UI)而不是YAML来进行构建和发布,但也对YAML是否有解决方案感兴趣。

用户已经查看了Azure DevOps中的条件(Conditions)部分,但觉得条件的数量有限。他还注意到变量可以在YAML中设置,但不确定如何在阶段(phases)/作业(jobs)或任务(tasks)的末尾设置条件来实现分支行为。

解决方案

请注意以下操作可能存在版本差异,建议在操作前备份相关数据。

为了满足用户的需求,我们可以采用以下方案:

方案1:使用自定义条件和变量传递状态

用户可以在Azure DevOps的发布定义中使用自定义条件来实现这个需求。同时,可以通过使用变量来传递状态,确保手动干预的结果被后续任务正确处理。

  1. 设置自定义条件: 用户可以在某个Agentless job中设置一个自定义条件,仅当前面的任务全部成功并且Release.ReleaseDescription变量包含指定的标志时,此作业才会执行。这个标志用来传递手动干预是否已经成功。

  2. 传递状态信息: 由于Web Designer中无法直接从一个Agent job传递信息到一个Agentless job,用户可以使用Azure DevOps的REST API,通过更新Release.ReleaseDescription参数来传递状态信息。这个参数可以被下一个作业读取以决定如何继续。用户可以在一个Powershell脚本中调用Get ReleaseUpdate Release的API来实现这个步骤。

  3. 设置强制标志: 如果手动干预成功,用户可以在第三个作业中再次尝试部署数据库,但这次部署会使用一个-Force标志来忽略潜在的数据丢失问题。如果这次部署成功,可以运行一个快速的清理任务来调用Update Release API并从Release.ReleaseDescription中清除标志。

  4. 合并回正常流程: 最后,其他任务可以继续运行,因为已经通过自定义条件和状态传递确保了手动干预的结果,以及数据库部署的结果。

下面是一个大致的步骤:

  1. 运行数据库部署。
  2. 如果部署有错误,不要使任务失败,而是设置一个代理变量。
  3. 下一个任务检查变量是否被设置,如果是,则调用Update Release API将标志追加到Release Description中。
  4. 未来的作业和任务现在可以分析Release Description变量的状态。

这个方案可以保持数据库部署脚本的清晰和可维护性,同时将API调用的责任转移到一个专用脚本中。

方案2:使用YAML来实现

另一种方法是使用YAML来定义发布流程。YAML提供了更高的灵活性和表达能力,可以更容易地实现复杂的条件和流程控制。用户可以在YAML中定义自定义条件、变量以及作业的执行顺序。这样可以更精确地控制流程,满足用户的所有要求。

不过需要注意,方案2涉及使用YAML来定义发布流程,需要一定的YAML编写能力,如果用户之前不熟悉YAML,可能需要一些学习和适应的时间。

示例:YAML解决方案

以下是一个可能的YAML解决方案的示例,使用自定义条件和变量来实现用户的需求。请注意,这只是一个示例,具体的YAML代码需要根据用户的实际情况和发布定义进行调整。

jobs:
- job: DeployDB
  steps:
  - script: RunDatabaseDeploymentScript
    displayName: 'Deploy Database'
    continueOnError: true

- job: ManualIntervention
  condition: succeeded()
  dependsOn: DeployDB
  steps:
  - script: PerformManualIntervention
    displayName: 'Manual Intervention'
    continueOnError: true

- job: DeployDBAfterIntervention
  condition: and(succeeded(), eq(dependencies.ManualIntervention.result, 'Succeeded'))
  dependsOn: ManualIntervention
  steps:
  - script: RunDatabaseDeploymentScript -Force
    displayName: 'Deploy Database with Force Flag'

这个示例中定义了三个作业(jobs):

正文完