4
votes

Our build artifact is an Octopus nuget package. When the build is released it lands into the QA stage where the artifact is deployed through Octopus. This octopus consumes it directly from the Azure Artifacts nuget feed.

If the deployment and the subsequent tests are successful we want to promote the artifact to the Release view of the Azure Artifacts nuget feed, because we think it gives us a different nuget URL that can be used by another Octopus serving the next stage (for historic reasons we have dedicated Octopus per stage - working to change that, but it takes time).

We can promote manually, but we want to do it automatically. How can this be done?

We are testing it on on-premises TFS 2019 RC2.

enter image description here

EDIT 1

The suggested plugin does not seem to install on on-premises TFS 2019 RC2:

enter image description here

3
i suppose you would use rest api call from octopus4c74356b41
The promotion is the Azure DevOps operation. Let me attach a snapshot.mark
i know, thats what I meant, call ADO api from octopus when release succeeds4c74356b41
What is the REST API for feed promotion, I could not find it?mark
right, i dont see anything either. weird. probably feed has got its own api. questionmark4c74356b41

3 Answers

3
votes

Using PowerShell...

$organisationName = '' # Name of organisation
$projectName = '' # Name of project
$feedName = '' # Name of Azure Artifacts feed
$viewName = 'Release' # I believe this can also be Prerelease, but I've not tried it

# List of names of packages within Azure Artifacts feed to be promoted
$packagesToPromote = @('')

# Need a personal access token for this script to work
# PAT token should be assigned to Packaging (Read, Write and Manage) scopes
$azureArtifactsPAT = '' 
$AzureArtifactsPAT_Base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($azureArtifactsPAT)"))
$restAPICallHeader = @{ Authorization = "Basic $AzureArtifactsPAT_Base64" }

$feedBaseURL = "https://feeds.dev.azure.com/$organisationName/$projectName/_apis/packaging/feeds"
$packageBaseURL = "https://pkgs.dev.azure.com/$organisationName/$projectName/_apis/packaging/feeds"

$feedIdURL = "$feedBaseURL/$feedName/?api-version=5.1-preview.1"
$feedIdResponse = (Invoke-RestMethod -Method Get -Uri $feedIdUrl -Headers $restAPICallHeader -ContentType 'application/json')
$feedId = $feedIdResponse.id

$viewIdURL = "$feedBaseURL/$feedId/views/$viewName/?api-version=5.1-preview.1"
$viewIdResponse = (Invoke-RestMethod -Method Get -Uri $viewIdUrl -Headers $restAPICallHeader -ContentType 'application/json')
$viewId = $viewIdResponse.id


$restAPICallBodyJson = @{
    views = @{
        op = 'add'
        path = '/views/-'
        value = "$viewId"
    }
}
$restAPICallBody = (ConvertTo-Json $restAPICallBodyJson)

foreach ($packageName in $packagesToPromote) {

    $packageQueryUrl = "$feedBaseURL/$feedId/packages?api-version=5.1-preview.1&packageNameQuery=$packageName"
    $packagesResponse = (Invoke-RestMethod -Method Get -Uri $packageQueryUrl -Headers $restAPICallHeader -ContentType 'application/json')
    $latestPackageVersion = ($packagesResponse.value.versions | ? { $_.isLatest -eq $True } | Select -ExpandProperty version)
    $encodedPackageVersion = [System.Web.HttpUtility]::UrlEncode($latestPackageVersion)

    Write-Host "Package Name: $packageName"
    Write-Host "Package Version: $latestPackageVersion"

    $releaseViewURL = $packageBaseURL `
    + "/$($feedId)" `
    + "/nuget/packages/$packageName" `
    + "/versions/$encodedPackageVersion" `
    + "?api-version=5.1-preview.1"

    $response = Invoke-RestMethod -Method Patch -Uri $releaseViewURL -Headers $restAPICallHeader -ContentType 'application/json' -Body $restAPICallBody
    Write-Host $response
}

For reference, the script above uses the following API calls:

Feed Management - Get Feed

https://docs.microsoft.com/en-us/rest/api/azure/devops/artifacts/feed%20%20management/get%20feed?view=azure-devops-rest-5.1

Feed Management - Get Feed View

https://docs.microsoft.com/en-us/rest/api/azure/devops/artifacts/feed%20%20management/get%20feed%20view?view=azure-devops-rest-5.1

Artifact Details - Get Packages

https://docs.microsoft.com/en-us/rest/api/azure/devops/artifacts/artifact%20%20details/get%20packages?view=azure-devops-rest-5.1

NuGet - Update Package Version

https://docs.microsoft.com/en-us/rest/api/azure/devops/artifactspackagetypes/nuget/update%20package%20version?view=azure-devops-rest-5.1

1
votes

I also recently struggled with trying to implement version using TFS. I've produced some PowerShell scripts (adapting other scripts out on the web) to do package versioning.

https://gist.github.com/oceanexplorer/6a91930419b35c1923974af265777a5f

https://gist.github.com/oceanexplorer/35e0f26962018dc8578c745060365c15

The first step is my build pipeline I use the "Update AssemblyInfo" task to set the build version which then gets embedded into the DLL's.

https://marketplace.visualstudio.com/items?itemName=sebastianlux.UpdateAssemblyInfo

Initially I embedded the above scripts with my project to get things going but eventually in my release pipeline I then have a task that deploys these build scripts via a "NuGet Install" task which effectively pulls them from a feed and unzips them.

In the release pipeline I then have a task "Version Package" which is a custom PowerShell script that calls functions defined in the two gists above, what these do is to unzip the NuGet packages that have been created from the build pipeline and placed in the artifact directory, applies the correct versioning to the package and zips it back up. I have used the following build number format in my build pipeline:

$(version.major).$(version.minor).$(version.patch).$(Date:yyyyMMdd)$(Rev:r)-CI

1.0.0.201902051-CI

This will produce a semantic build number format of:

1.0.0-alpha.201902051

I call the scripts using an inline PowerShell task

##-------------------------------------------
## Import Build Scripts
##-------------------------------------------
gci -Recurse "$(System.DefaultWorkingDirectory)\scripts\*.psm1" | ForEach-Object { Import-Module $_.FullName }

##-------------------------------------------
## Version Files
##-------------------------------------------
Expand-NugetPackages -packagesDirectory "$(artifact.directory)" -Verbose
Add-VersionToAssemblies -suffix "$(Release.EnvironmentName)" -semVer "2.0" -artifactsToApplyTo "nuspec" -isRelease $(isRelease) -Verbose
Compress-NugetPackages -packagesDirectory "$(artifact.directory)" -Verbose

Then a NuGet push task to push the package

Nuget Push task Them another inline PowerShell script which sets the release view for the package feed:

##-------------------------------------------
## Import Build Scripts
##-------------------------------------------
gci -Recurse "$(System.DefaultWorkingDirectory)\scripts\*.psm1" | ForEach-Object { Import-Module $_.FullName }

##-------------------------------------------
## Set Package Quality
##-------------------------------------------
Set-PackageQuality -feedName "Libraries" -packageId $(nuget.packageId) -packageVersion $env:semanticVersion -packageQuality $(Release.EnvironmentName)
0
votes

As per Azure DevOps documentation the marketplace task Promote package to Release View is the recommended way to accomplish this from a CI/CD pipeline.

The repository can be found on Github.

enter image description here


Edit:

Since you are on-prem with a version that this task doesn't support. I would say that the comments about using the REST api would be the route you need to go in something like a powershell script.

Having never used the REST Api for this task I'm not exactly sure how the body is supposed to look for the request. However, it seems to be documented here.

My understanding of the JSON Patch object is limited, but I would think you might use the replace operation.

{ "op": "replace", "path": "/view", "value": "@Release" }

This article may also be helpful, but I still don't see anything that would relate to the from identifier on the JsonPatchObject definition in the REST Api documentation.