Can I substring a variable in Azure Pipelines?
Asked Answered
P

7

21

I'm looking for a way to define a variable in my azure-pipelines.yml file where I can substring the 'Build.SourceVersion' -> Use the first 7 characters only.

Seems like there is no build-in function that can do such string operations in the documentation. Is there something I'm missing?

My other approach would be to use a bash task and overwrite the variable there but finding something build-in that can do this would be way better solution.

Pskov answered 12/6, 2020 at 12:4 Comment(0)
M
14

My other approach would be to use a bash task and overwrite the variable there but finding something build-in that can do this would be way better solution.

I agree with Lucas. There is no such built-in task to get the first 7 characters of $(Build.SourceVersion) in Azure DevOps.

We could use the command line/powershell task to split long sha into short sha:

echo $(Build.SourceVersion)

set TestVar=$(Build.SourceVersion)

set MyCustomVar=%TestVar:~0,7%

echo %MyCustomVar%

echo ##vso[task.setvariable variable=ShortSourceVersion]%MyCustomVar%

In this case, we could get the short versions of Build.SourceVersion and set it as environment variable.

Then we could set this command line task as a task group:

enter image description here

So, we could use this task to set the ShortSourceVersion directly.

Hope this helps.

Mejia answered 15/6, 2020 at 6:17 Comment(1)
Tried the other approaches published here, and this is the only one I could get to work. Kudos.Cowpea
S
6

I know I'm a bit late to this party, but for anyone who comes by here and wants to avoid using Bash, PowerShell and similar, I've managed to do a substring with nothing but Azure Pipelines expressions.

(I'll definitely go straight to hell for writing this code)

Unfortunately, we can not index on string for some reason, so @dsschneidermann's answer does not work. But we can work around this using the split function.

This definitely is more complex than using scripting, but is, on the other hand, run fully at the compile time.

  1. Append some known character after each and every character in the original string using replace. I.e., append _, so that abc becomes a_b_c_.
  2. Split the string using the appended character - split('a_b_c_', '_'). The split function returns an indexable array.
  3. Use indexing on the array to compose the result

This, besides being unreadable, has the disadvantage that you have to specify all characters that might ever be part of the input value. Failing to do so might cause unexpected behavior. But for a git hash, it's enough to specify the whole alphabet, plus numerics.

The result is this:

- name: Build.SourceVersion.PreparedForSplit
  value: ${{ replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(variables['Build.SourceVersion'], 'a', 'a_'), 'b', 'b_'), 'c', 'c_'), 'd', 'd_'), 'e', 'e_'), 'f', 'f_'), 'g', 'g_'), 'h', 'h_'), 'i', 'i_'), 'j', 'j_'), 'k', 'k_'), 'l', 'l_'), 'm', 'm_'), 'n', 'n_'), 'o', 'o_'), 'p', 'p_'), 'q', 'q_'), 'r', 'r_'), 's', 's_'), 't', 't_'), 'u', 'u_'), 'v', 'v_'), 'w', 'w_'), 'x', 'x_'), 'y', 'y_'), 'z', 'z_'), '0', '0_'), '1', '1_'), '2', '2_'), '3', '3_'), '4', '4_'), '5', '5_'), '6', '6_'), '7', '7_'), '8', '8_'), '9', '9_') }}
  readonly: true
- name: Build.SourceVersion.Short
  value: ${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[0] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[1] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[2] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[3] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[4] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[5] }}${{ split(variables['Build.SourceVersion.PreparedForSplit'], '_')[6] }}
  readonly: true

You might put these in a variable template and then, everywhere you need to use them, just include this template, what is a one line of code. So you end up with one hard-to-read code file, but other simple ones, and everything is processed at the template's compile time.


I've also created a PowerShell script for generating the intermediate variable:
# Input variable name
$VariableName = 'Build.SourceVersion'
# Known characters - should be every character that could appear in the input
$Chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
# Character to be appended after every character in the input string
$IntermediateChar = '_'

$output = "variables['$VariableName']"

foreach ($Char in [char[]]$Chars) {
    $output = "replace($output, '$Char', '$Char$IntermediateChar')"
}

Write-Host $output
Seduction answered 6/3, 2023 at 14:58 Comment(1)
Good answer, creative. Just want to point out that you don't need the full alphabet for hashes, just the first 6 letters, because it's hexadecimal. It'll be enough changing 0-9 and a-f. Then the PreparedForSplit reduces a little bit: ${{ replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(variables['Build.SourceVersion'], '0', '0_'), '1', '1_'), '2', '2_'), '3', '3_'), '4', '4_'), '5', '5_'), '6', '6_'), '7', '7_'), '8', '8_'), '9', '9_'), 'a', 'a_'), 'b', 'b_'), 'c', 'c_'), 'd', 'd_'), 'e', 'e_'), 'f', 'f_') }}Distichous
F
5

Here is the shortest version that I use for this job;

- bash: |
    longcommithash=$(Build.SourceVersion)
    echo "##vso[task.setvariable variable=shorthash;]$(echo ${longcommithash::9})"

It gives you the output as shown below;

enter image description here

Futilitarian answered 9/4, 2022 at 13:39 Comment(2)
It can be a oneliner: echo "##vso[task.setvariable variable=shorthash]${BUILD_SOURCEVERSION::9}"Obturate
But is the variable globally available? I need to set my release to the short hashed git version in my following task: AzureRmWebAppDeploymentMartins
R
4

On Azure devops it's possible to be done using batch technique indeed - like other answers propose in here - however I wanted to remove string from the end of string - and with batch this gets more complex ( See following link ).

Then I've concluded to use Azure's built-in expressions, for example like this:

- name: sourceBranchName
  value: ${{ replace(variables['Build.SourceBranch'], 'refs/heads/') }}

replace will have similar effect to substring - so it will remove unnecessary part from original string.

If however you want to concatenate some string and then remove it from original name - it's possible to be done using for instance format function:

- name: originBranchName
  value: ${{ replace(variables.sourceBranchName, format('-to-{0}', variables.dynamicvar ) ) }}

If dynamicvar is a - then function will remove -to-a from branch name.

Roath answered 18/8, 2022 at 6:2 Comment(0)
S
2

You are right, there is no native way to do it. You will have to write a script to transform the variable.

Here is an example:

trigger:
- master

resources:
- repo: self

stages:
- stage: Build
  displayName: Build image
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - task: CmdLine@2
      inputs:
        script: ' x=`echo "$(Build.SourceVersion)" | head -c 7`; echo "##vso[task.setvariable variable=MyVar]$x"'
    - task: CmdLine@2
      inputs:
        script: 'echo "$(MyVar)"'
Sokul answered 12/6, 2020 at 17:56 Comment(4)
Is it to possible modify env variable inside in a template? If I use an expression function I get: "Mapping values are not allowed in this context."Marhtamari
Could you give more context about what you are trying to do? What variable were you trying to change? a system variables?Sokul
Ok, I see that I can use some functions: value: ${{ upper(parameters.ENV_NAME) }} . I'm not sure that I can combine with other functions. I don't know for example how upper only first character from parameter and join with other values, strings etc.Marhtamari
But is the variable globally available? I need to set my release to the short hashed git version in my following task: AzureRmWebAppDeployment..Martins
R
0

E.g.

variables:
- name: Version
  value: ${{ parameters.version }}
- name: Environment
  value: Production

  - job: Tag
    displayName: "Make tag in git"
    continueOnError: true
    steps:
     - script: |
         set Env=$(Environment)
         set Abbr=%Env:~0,1%
         git tag %Abbr%.$(Version)
         git push origin %Abbr%.$(Version)
Rimskykorsakov answered 21/11, 2023 at 13:57 Comment(0)
T
-2

Sure you can! If you absolutely must. Here is a runtime computation taking the first 7 characters of the Build.SourceVersion variable.

variables:
  example: ${{ format('{0}{1}{2}{3}{4}{5}{6}', variables['Build.SourceVersion'][0], variables['Build.SourceVersion'][1], variables['Build.SourceVersion'][2], variables['Build.SourceVersion'][3], variables['Build.SourceVersion'][4], variables['Build.SourceVersion'][5], variables['Build.SourceVersion'][6]) }}

NB: I can't get it to work with $[...] syntax as the variable is apparently empty at the initial.

Terbium answered 16/7, 2021 at 8:10 Comment(1)
Does not work, while variables['Build.SourceVersion'] prints the full hash, variables['Build.SourceVersion'][0] does print an empty string, NOT the first character of the hash.Colosseum

© 2022 - 2025 — McMap. All rights reserved.