38

I'm trying to set a tag with the current version number determined by GitVersion on the GIT commit at the end of a successful build. Feels like I can't be the first one to be doing this, but I'm struggling to find something that works.

Azure Devops Pipeline has a feature in Get Sources to "Tag sources" On Success. I've set this and set to a variable that is set by one of the Agent Tasks I have (GitVersion)

Tag Sources

I can see in the debug logs that this variable is getting set by the GitVersion component that I've added to the pipeline.

2019-12-06T20:54:20.2390794Z ##[debug]Processed: ##vso[task.setvariable variable=GitVersion.MajorMinorPatch;]2.98.0

However if I leave it just as this, I get a tag created as "v$(GitVersion.MajorMinorPatch)" which means that at the time that the tag is being created that that variable no longer exists.

The Tag Format help tooltip says

"Tag format could be a combination of user-defined or pre-defined variables that have a scope of "All". For example: '$(Build.DefinitionName)$(Build.DefinitionVersion)$(Build.BuildId)$(Build.BuildNumber)$(My.Variable)'"

So I guess the problem is that this variable created during the pipeline does not have a scope of All.

I then tried adding a pipeline variable to the pipeline of "GitVersion.MajorMinorPatch" with the hope that this was at the right scope and hoping that when the "task.setvariable" command is run, that this will set the variable value of this higher scoped variable.

enter image description here

However in this case I just got a tag "v" created.

So I am a bit stuck. Somehow I need to be able to dynamically create or set a variable at scope ALL with the value I want to tag here.

I'd be really grateful for any ideas on this.

beatcracker
  • 6,714
  • 1
  • 18
  • 41
Brett
  • 719
  • 1
  • 7
  • 19
  • Have you tried setting the `$(Build.BuildNumber)` to the value you want? Then set that to the tag value? It requires a different logging command to set the buildnumber: `build.updatebuildnumber` instead of `task.setvariable`. See: https://stackoverflow.com/a/37048559/736079 – jessehouwing Dec 07 '19 at 14:19
  • yeah, that would probably be a trick to work around that – 4c74356b41 Dec 07 '19 at 16:14
  • 2
    It isn't a solution to use Build.BuildNumber for what I want to do here. I need BuildNumber to be globally unique (e.g. v2.19.1-b23) and this tag needs to be simpler - just v2.19.1 – Brett Dec 09 '19 at 18:33

4 Answers4

67

If you are doing a yaml pipeline, you can add the following steps

- checkout: self
  persistCredentials: true

## Rest of pipeline ##

- script: |
     git tag $(GitVersion.NugetVersionV2)
     git push origin $(GitVersion.NugetVersionV2)
  workingDirectory: $(Build.SourcesDirectory)

The persistCredentials allows the token to be automatically passed to other git commands. Note the assignment of workingDirectory, otherwise I had an error that the location was not a git repository.

For an annotated tag rather than lightweight tag, the syntax would look like this...

- script: |
    git tag -a <tagname> -m <message>
    git push origin <tagname>

To get a user/date against it you need to set the user name/email as well e.g.

- script: |
    git config --global user.name "BuildService"
    git config --global user.email "autobuild@fabrikam.com"
    git tag -a <tagname> -m <message>
    git push origin <tagname>

For this to work, the Project Collection Build Server account (not the Project Build Service Accounts group) needs to be allocated the Contribute permission for the Repositories

enter image description here

J Scott
  • 781
  • 9
  • 12
Paul Hatcher
  • 7,342
  • 1
  • 54
  • 51
  • 8
    For those wondering how to configure the Project Collection Build Server account, this can be found under Project Settings > Repos > Repositories. When you click on a repo, you should find this account under the listed Users. – akokskis Mar 28 '20 at 22:38
  • 1
    You can also click on Repositories to set it for all repositories in the Project – Paul Hatcher Mar 29 '20 at 14:07
  • I did 5 seconds on reading and found `git push --follow-tags` would that be not more appropriate? – sommmen Nov 18 '20 at 09:33
  • 2
    @sommmen If you have a look at https://stackoverflow.com/questions/3745135/push-git-commits-tags-simultaneously it mentions that -follow-tags only pushes annotated tags not lightweight, so since the answer as stated works for both, I don't think it warrants a change – Paul Hatcher Nov 18 '20 at 14:01
  • `workingDirectory: $(Build.Repository.LocalPath)` is what worked for me. – ChaoticCoder Feb 15 '21 at 07:13
  • Where in the YAML file do I put: - checkout: self persistCredentials: true – Dbloom Aug 11 '21 at 18:46
  • @Dbloom First step of the pipeline – Paul Hatcher Aug 12 '21 at 15:52
  • 1
    I had to use `git push origin HEAD:origin/$(Build.SourceBranchName)` since I was in a headless state. – Klepto Sep 01 '22 at 14:18
3

I can see in the debug logs that this variable is getting set by the GitVersion component that I've added to the pipeline.

The variable GitVersion.MajorMinorPatch you saw from the log is a step-level variable, which means its life cycle is only start from the current GitVersion task.

enter image description here

As the definition you are referring, it scope must to all. This means is must be a global variable. For example, the predefined variables that the system default have, and the customized variables which specified in the Variables tab.


Based on the GitVersion task compile and work logic, in fact, the GitVersion.MajorMinorPatch value is generated and stored as current build's build number:

enter image description here

So, the most convenient method for you to tag the GitVersion.MajorMinorPatch value to repos is using $(Build.BuildNumber):

v$(Build.BuildNumber)

enter image description here

And this is my result:

enter image description here


Update:

To add the GitVersion.MajorMinorPatch which generated by the GitVersion task into Variables, please apply below scripts into PowerShell task:

$connectionToken="{PAT Token}"
$urlget = "https://dev.azure.com/{org}/{project}/_apis/build/definitions/$(System.DefinitionId)?api-version=5.1"
$base64AuthInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$getdef = Invoke-RestMethod -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method GET -ContentType application/json -Uri $urlget 
Write-Host Pipeline = $($getdef | ConvertTo-Json -Depth 100)
$bvalue=@"
    {
      "value": "$(GitVersion.MajorMinorPatch)"
    }
"@
$getdef.variables | add-member -Name "GitVersion.MajorMinorPatch" -value (Convertfrom-Json $bvalue) -MemberType NoteProperty -Force -PassThru

$getdef = $getdef | ConvertTo-Json -Depth 100
$getdef | clip
$urlput = "https://dev.azure.com/{org}/{project}/_apis/build/definitions/$(System.DefinitionId)?api-version=5.1"
$putdef = Invoke-RestMethod -Uri $urlput -Method PUT -Body $getdef -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}

As I mentioned previously, I still don't think it is available to specify $(GitVersion.MajorMinorPatch) in Tag format.

Still strongly suggest you by calling $(Build.BuildNumber) to tag the $(GitVersion.MajorMinorPatch) value

David Gardiner
  • 16,892
  • 20
  • 80
  • 117
Mengdi Liang
  • 17,577
  • 2
  • 28
  • 35
  • The "Major.Minor.Patch" is not globally unique, so isn't appropriate for the buildnumber, there are lots of commits that will have the same value.. GitVersion.MajorMinorPatch, is available to all future steps in the job as I can create a PS task and output the current $(GitVersion.MajorMinorPatch). It is just not available. Seems an unnecessarily limitation to the Tag format that you can't use any user defined variables at all (that isn't what the doc implies)? – Brett Dec 09 '19 at 15:21
  • @Brett, (Need say apologize for my point on my answer, has made some changes). Yes, the `"Major.Minor.Patch"` is not unitque in pipeline, but for the pipeline which is using `GitVersion.MajorMinorPatch`, its value is unique to `GitVersion.yml`. For the puzzle you concerned: Seems an unnecessarily limitation to the Tag format that you can't use any user defined variables at all. NO, we did not set this limitation on pipeline, you can customized a variable in Variables tab and **set value** to it. Then apply this variable in the tag blank. – Mengdi Liang Dec 09 '19 at 16:27
  • You could see that the variable value been tagged into the repos. For why your second try has no value, it is because you did not set value in Variables tab. Here the variable which can be tagged onto the repos must be a **pre-defined** variable include the customized variables defined in the Variables tab. When you using GitVersion, its value will override the build number. This is the logic of this task. So here I suggest you use calling $(Build.BuildNumber) to tag the repos. – Mengdi Liang Dec 09 '19 at 16:30
  • 1
    Thanks for the updates. Is there no way I can create a variable with scope ALL at runtime or create in advance and change the value of this variable at runtime? If I could do that then I could solve this problem and in fact if I can't do this then being able to use a user defined variable is limited as you need a human to set the value before execution which means it is difficult to automate? It isn't a solution to use Build.BuildNumber for what I want to do here. I need BuildNumber to be globally unique (e.g. v2.19.1-b23) and this tag needs to be simpler - just v2.19.1 – Brett Dec 09 '19 at 18:32
  • @Brett Of course has this way, but it need you call one api to create this variable which scope to all. You know, if you create or override value to the existing one, it only scope to agent job. We called that runtime variable. To modify the existing variable value, or create the new one, we need to use Api to do that. Does this suitable for you? If yes, I will share you sample on how to achieve that by using powershell. – Mengdi Liang Dec 09 '19 at 18:37
  • Yes please Merlin, I'd be interested in how I can create/manipulate these wider scoped variables. It is a shame that set-variable doesn't have this capability – Brett Dec 09 '19 at 19:13
  • @Brett, sorry about delay reply for personal busy work. See my updated scripts. But, as I mentioned, I still don't think specify `$(GitVersion.MajorMinorPatch)` would available in tag. – Mengdi Liang Dec 12 '19 at 13:27
2
- pwsh: |
    # Construct PAT authentication header
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "user",$env:SYSTEM_ACCESSTOKEN)))
    $headers = @{Authorization=("Basic {0}" -f $base64AuthInfo)}
    $url="$(System.CollectionUri)/$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.ID)/annotatedtags?api-version=5.0-preview.1"
    $body = @{name = "$(GitVersion.MajorMinorPatch)"
              message = "automatically added"
              taggedObject = @{
                objectId = "$(Build.SourceVersion)"
              }
            } | ConvertTo-Json


    Invoke-RestMethod -Uri $url -Headers $headers -Method Post -ContentType "application/json" -Body ($body)
  env:
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)
  displayName: 'Add tag'
0

I had similar issue and I am able fix it by below method

  1. Add and Set your custom variable in YAML
trigger:
- none

parameters: # parameters are shown up in ADO UI in a build queue time
- name: 'debug'
  displayName: 'Enable debug output'
  type: boolean
  default: false

variables:
  ROOT: $(Build.SourcesDirectory)
  REPOROOT: $(Build.SourcesDirectory)
  MyCustomVariable: 'Set required value here'

And then set the same variable in Build Trigger

BuildTagCustom

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77