Home > Software engineering >  Way to use Azure DevOps Pipeline Expression to loop through String through Split() method?
Way to use Azure DevOps Pipeline Expression to loop through String through Split() method?

Time:12-09

EDIT: I have a workaround since I just got schooled up on runtime versus compile time traits. I output the AllRepos variable as a stage/job variable, and ran a powershell script to split the string based on ',' and loop through the objects and inject the repo name into a JSON object which calls the pipeline template I wanted to use. Here is what I did to get around this limitation:

`

stages:
  - stage: GetAllProjectRepos
    pool:
      name: 'DevOps Pool'
    jobs:
     - job: GetRepos
       displayName: 'Get All Repos for Project'
       steps:
        - checkout: none
        - task: PowerShell@2
          displayName: 'Put all repo names into runtime variable'
          inputs:
            targetType: 'inline'
            script: |
              $env:AZURE_DEVOPS_EXT_PAT = '$(projectPAT)'
              $repos= (az repos list --organization '$(orgUrl)' --project '$(projectName)' | ConvertFrom-Json | Select-Object name).name -join ','

              Write-Host '============================================================================================'
              Write-Host 'Repos: ' $repos
              Write-Host '============================================================================================'

              Write-Host "##vso[task.setvariable variable=AllRepos;isOutput=true]$repos"
          name: ReposList

     - job: CleanUp
       displayName: 'Clean Undeleted PR Branches from Project repos'
       dependsOn: 'GetRepos'
       variables:
         AllRepos: $[ dependencies.GetRepos.outputs['ReposList.AllRepos'] ]
       steps:
        - checkout: none

        - task: PowerShell@2
          displayName: 'Get Pipeline ID'
          inputs:
            targetType: 'inline'
            script: |
                $env:AZURE_DEVOPS_EXT_PAT = '$(projectPAT)'
                az devops configure --defaults organization='$(orgUrl)'
                az devops configure --defaults project='$(projectName)'
                Write-Host '============================================================================================'
                Write-Host 'GitCleanup Pipeline ID (also called Definition)'
                $pipelineId = az pipelines list --query "[?name == 'Git Clean Branches'].id"
                $pipelineId = $pipelineId[1].Trim()
                Write-Host "##vso[task.setvariable variable=GitCleanPipelineId;]$pipelineId"
                Write-Host 'GitCleanup ID: ' $pipelineId

        - task: PowerShell@2
          displayName: 'Calling GitCleanup Pipeline'
          inputs:
            targetType: 'inline'
            script: |
              $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "","$(projectPAT)")))
              $uri = "$(orgUrl)/$(projectName)/_apis/pipelines/$(GitCleanPipelineId)/runs?api-version=6.0-preview.1"
              Write-Host '============================================================================================'
              Write-Host $uri
              Write-Host '============================================================================================'

              $repos = '$(AllRepos)'
              $repos.split(",") | ForEach {
                $jsonHashTable= @{
                    'templateParameters' = @{
                      'repoName' = $_
                      'deletePRBranches' = 'true'
                      'deleteStaleBranches' = 'false'
                    }
                  }

                $JSON = $jsonHashTable | ConvertTo-Json

                Write-Host '============================================================================================'
                Write-Host $JSON
                Write-Host '============================================================================================'
                Write-Host '============================================================================================'
                $reponse = Invoke-RestMethod -Uri $uri -Headers @{Authorization = "Basic $base64AuthInfo"} -Method Post -Body $JSON -ContentType application/json
                Write-Host "API Response"
                Write-Host $response
                Write-Host '============================================================================================'
              }

`

Trying to automate Azure Repos cleanup for stale and merged PR branches that sometimes stick around. Want to make the pipeline generic as possible and leverage templates for easier maintenance and reduced administrative workload.

I have a powershell script which gathers all repositories in a ADO project, converts from an array object to a string, and then joins each repo with a ',' delimiter for looping later in the pipeline. Once this is "working" I'll be using the repo names to inject them into a pipeline template as a parameter.

The powershell script works fine, it creates the comma-delimited string just fine, but the pipeline expression I am using just removes the comma, but treats the entire string as one object and doesn't loop.

`

stages:
  - stage: CleanProjectRepoBranches
    pool:
      name: 'DevOps Pool'
    jobs:
     - job: GetRepos
       displayName: 'Get All Repos for Project'
       steps:
        - checkout: none
        - task: PowerShell@2
          displayName: 'Gather all Repos in $(projectName)'
          inputs:
            targetType: 'inline'
            script: |
              $env:AZURE_DEVOPS_EXT_PAT = '$(projectPAT)'
              $repos= (az repos list --organization '$(orgUrl)' --project '$(projectName)' | ConvertFrom-Json | Select-Object name).name -join ','

              Write-Host '============================================================================================'
              Write-Host 'Repos: ' $repos
              Write-Host '============================================================================================'

              Write-Host "##vso[task.setvariable variable=AllRepos;]$repos"

        - script: echo "AllRepos is $(AllRepos)"

        - ${{ each repo in split('$(AllRepos)', ',') }}:
            - task: PowerShell@2
              displayName: 'Test'
              inputs:
                targetType: 'inline'
                script: |
                  Write-Host '============================================================================================'
                  Write-Host ${{repo}}
                  Write-Host '============================================================================================'

`

The "echo" successfully prints the string I theoretically need:

Log printout

The powershell script and injecting to a pipeline variable works fine, but the pipeline expression isn't behaving as I would think:

`

- ${{ each repo in split('$(AllRepos)', ',') }}:
            - task: PowerShell@2
              displayName: 'Test'
              inputs:
                targetType: 'inline'
                script: |
                  Write-Host '============================================================================================'
                  Write-Host ${{repo}}
                  Write-Host '============================================================================================'

`

The pipeline expression currently behaving in a way I wasn't anticipating, it "ignores" the split() method and when I print the ${{repo}} value it displays it as a string of all the same repo names,with all commas removed:

Log printout

Am I doing something wrong? Is this a limitation of the pipeline expressions MS implements?

CodePudding user response:

You can refer to the document Define variables - Azure Pipelines and learn more details about the three different ways to reference variables: macro, template expression, and runtime expression.

In a pipeline, template expression variables (${{ variables.var }}) get processed at compile time, before runtime starts. Macro syntax variables ($(var)) get processed during runtime before a task runs. Runtime expressions ($[variables.var]) also get processed during runtime but were designed for use with conditions and expressions.

As for ${{ each repo in split('$(AllRepos)', ',') }}, the template expression variable ${{ }} is resolved at template compile time and the macro syntax variable $(AllRepos) is resolved at runtime, which is after the template expression variable has been compiled. You can't use runtime variables in compile-time expressions.

It's recommended that you can try breaking it down into two pipelines to run.

  • Related