如何修复Azure DevOps YAML管道中的“Unexpected state while attempting to read the mapping end”错误
在配置Azure DevOps多步骤YAML管道时,遇到Unexpected state while attempting to read the mapping end错误,报错指向steps区域,具体提示如下:
解析管道YAML时遇到错误:/azure-pipelines.yml
(行: 16, 列: 3): 映射中至少需要一个键值对 /azure-pipelines.yml: Unexpected state while attempting to read the mapping end. State:MappingState: IsStart: True Index: 0 IsKey: False IsEnd: False
SequenceState: IsStart: False Index: 1 IsEnd: False
MappingState: IsStart: False Index: 3 IsKey: False
IsEnd: False
错误的YAML代码示例:
name: $(Date:yyyyMMdd)$(Rev:.r) resources: repositories: - repository: Infrastructure type: git name: Infrastructure trigger: paths: include: - source/project.test/** - source/project.test2/** steps: - ${{ if contains(variables['Build.SourceBranch'], 'refs/heads/main') }}: - template: Build/build-templateone.yml@Infrastructure parameters: appName: 'example' condition: eq(variables['Build.Reason'], 'IndividualCI') - ${{ if contains(variables['Build.SourceBranch'], 'refs/heads/main') }}: - template: Build/build-templatetwo.yml@Infrastructure parameters: appName: 'example' condition: eq(variables['Build.Reason'], 'IndividualCI')
错误原因
问题出在steps区域的结构写法:将condition字段放在了${{ if }}条件块的外部。YAML解析器无法识别这种结构,因为${{ if }}块本身是用来条件化插入步骤序列的,块外的condition没有对应的父级映射,导致解析时出现映射结构不完整的错误。
修复方案
方案1:合并条件到编译阶段判断
将分支判断和构建原因判断合并到${{ if }}的逻辑中,在编译阶段就决定是否插入步骤:
name: $(Date:yyyyMMdd)$(Rev:.r) resources: repositories: - repository: Infrastructure type: git name: Infrastructure trigger: paths: include: - source/project.test/** - source/project.test2/** steps: - ${{ if and(contains(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.Reason'], 'IndividualCI')) }}: - template: Build/build-templateone.yml@Infrastructure parameters: appName: 'example' - ${{ if and(contains(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.Reason'], 'IndividualCI')) }}: - template: Build/build-templatetwo.yml@Infrastructure parameters: appName: 'example'
方案2:将condition作为步骤属性放在块内
保持${{ if }}的分支判断,同时把condition移到块内的步骤定义中,作为步骤的运行阶段判断条件:
name: $(Date:yyyyMMdd)$(Rev:.r) resources: repositories: - repository: Infrastructure type: git name: Infrastructure trigger: paths: include: - source/project.test/** - source/project.test2/** steps: - ${{ if contains(variables['Build.SourceBranch'], 'refs/heads/main') }}: - template: Build/build-templateone.yml@Infrastructure parameters: appName: 'example' condition: eq(variables['Build.Reason'], 'IndividualCI') - ${{ if contains(variables['Build.SourceBranch'], 'refs/heads/main') }}: - template: Build/build-templatetwo.yml@Infrastructure parameters: appName: 'example' condition: eq(variables['Build.Reason'], 'IndividualCI')
方案区别
- 方案1:编译阶段就完成条件判断,只有当两个条件都满足时,步骤才会被加入到管道中,不会在管道视图中显示被跳过的步骤。
- 方案2:步骤会先被插入管道,在运行阶段再通过
condition判断是否执行,被跳过的步骤会在管道视图中显示为“已跳过”状态。
内容的提问来源于stack exchange,提问作者johnny3210

