Search code examples
azure-devopsyamlazure-pipelinesazure-pipelines-yaml

Azure DevOps auto conversion of value to integer type in yaml


Given this Azure DevOps yaml for a test pipeline:

trigger:
- master

pool:
  vmImage: ubuntu-latest

parameters:
  - name: dotnetVersion
    type: string
    default: '8.0'
  - name: build_config
    type: object
    default:
      - solution:
          name: TestProject
          vsBuild: false
          dotnetBuild: true
          buildConfiguration: Debug
          buildPlatform: Any CPU
          dotNetVersion: '8.0'

steps:
  - ${{ each sln in parameters.build_config }}:
    - script: |
        echo "Dotnet version is: ${{sln.solution.dotNetVersion}}"
      displayName: First output dotnet version

  - script: |
      echo "Dotnet version is: ${{parameters.dotNetVersion}}"
    displayName: Second output dotnet version

why does the first task output:

Dotnet version is: 8

but the second task output

Dotnet version is: 8.0

This is a problem for us because we have a fairly large build_config object for the majority of our pipelines. However, I've noticed that some fail depending on which parameter style is used to pass the dotnet version into the underlying templates.

How do I prevent what appears to be auto-conversion or casting to an integer in Azure DevOps yaml? I have tried using:

  • "8.0"
  • '8.0"
  • '8.0'

none seem to produce the expected result. I still get 8 in the underlying template. I have also tried using quotation marks around the parameter output like this "${{parameters.dotnetVersion}}". This doesn't appear to work either.


Solution

  • TL;DR I believe there is a bug in the Azure DevOps portal when queuing a new build.

    Using Azure DevOps CLI to queue a new build works as expected:

    az pipelines build queue --definition-name "my-pipeline" --branch "my-branch"
    

    The problem

    The dotnetVersion parameter is defined as a string and will always be treated as one.

    The build_config parameter is defined as an object, but unfortunately there is no way to define the schema for the parameter and setting the type of properties such as solution.dotNetVersion.

    The problem here is that even if you put quotes around values such as '8.0':

    parameters:
      - name: dotnetVersion
        type: string
        default: '8.0' # <-------------- Has quotes and parameter is set as string
      - name: build_config
        type: object
        default:
          - solution:
              name: TestProject
              vsBuild: false
              dotnetBuild: true
              buildConfiguration: Debug
              buildPlatform: Any CPU
              dotNetVersion: '8.0' # <------------- Has quotes but there's no way to define the type of a property
    

    Azure DevOps will remove all quotes when you queue a new build using the portal:

    Queue pipeline - parameters

    So solution.dotnetVersion will be converted into a numeric value:

    Pipeline logs

    Queuing a new pipeline and manually putting single quotes in solution.dotnetVersion:

    Queue pipeline

    Value is displayed as expected:

    Pipeline logs

    Workaround

    Add a pipeline variable (e.g. DOTNET_VERSION) whose value will depend on whether the corresponding parameter has a . or not:

    trigger: none
    
    pool:
      vmImage: ubuntu-latest
    
    parameters:
      - name: dotnetVersion
        type: string
        default: '8.0'
    
      - name: build_config
        type: object
        default:
          - solution:
              name: 'TestProject'
              vsBuild: false
              dotnetBuild: true
              buildConfiguration: 'Debug'
              buildPlatform: 'Any CPU'
              dotNetVersion: '8' # Try with '8.0', '8.1', etc
    
    jobs:
      - ${{ each sln in parameters.build_config }}:
        - job: build_${{ sln.solution.name }}
          displayName: Build ${{ sln.solution.name }}
          variables:
            ${{ if contains(sln.solution.dotNetVersion, '.') }}:
              DOTNET_VERSION: "${{ sln.solution.dotNetVersion }}"
            ${{ else }}:
              DOTNET_VERSION: "${{ sln.solution.dotNetVersion }}.0"
          steps:
            - checkout: none
            - script: |
                echo "Dotnet version is: $(DOTNET_VERSION)"
              displayName: First output dotnet version