We no longer would like to use upstream sources in our artifacts feed, but for some reason the packages still exist even after removing the upstream connection. Is there a way to remove these packages? I have tried removing latest version on all packages, but that does what it suggests: It only removes the first version and leaves all the other package versions intact.
How do I remove all upstream packages in DevOps artifacts?
Indeed, there is no such out of the box way to remove all upstream packages (including all versions) in DevOps artifacts at this moment.
There is a closed User Voice about it: Delete Upstream Source Cached Packages. If you are interested in this feature, you can open a new User Voice suggestion; I am willing to vote for you.
As workaround, we could loop through the REST API (NuGet - Delete Package Version) to delete all versions of upstream packages:
DELETE https://pkgs.dev.azure.com/{organization}/{project}/_apis/packaging/feeds/{feedId}/nuget/packages/{packageName}/versions/{packageVersion}?api-version=6.0-preview.1
First, we need to get the FeedId
for the NuGet feed by the REST API (Feed Management - Get Feed) and all packages in that feed (Artifact Details - Get Packages).
An important point here is that we need to determine which of these packages are from upstream sources. There is property directUpstreamSourceId
for the REST API (Artifact Details - Get Packages):
Now, we can judge whether the package is an upstream source package based on whether this field exists.
Second, after we getting the Id
of all upstream source packages, we can loop through the REST API (Artifact Details - Get Package Versions) to get all versions for the package.
Third, after getting the all the versions, we can loop through the REST API (NuGet - Delete Package Version) to delete all versions of that packages.
Below is my PowerShell script for testing this:
$connectionToken="Your PAT Here"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$PackagesUrl = "https://feeds.dev.azure.com/{organization}/{project}/_apis/packaging/Feeds/{feedId}/packages?api-version=6.0-preview.1"
Write-Host "URL: $PackagesUrl"
$PackagesInFeed = (Invoke-RestMethod -Uri $PackagesUrl -Method Get -UseDefaultCredential -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)})
$packagesId = $PackagesInFeed.value.id
$UpstreamSourceParameters= $PackagesInFeed.value.versions | Get-Member -MemberType Properties
$UpstreamSource = $UpstreamSourceParameters[0].name
Write-Host "The Upstream Source Parameter name: $UpstreamSource"
ForEach ($PI in $packagesId)
{
if ($UpstreamSource -ne $null)
{
$PackageVersionUrl = "https://feeds.dev.azure.com/{organization}/{project}/_apis/packaging/Feeds/{feedId}/packages/$($PI)/versions?api-version=6.0-preview.1"
$PackageVersions = (Invoke-RestMethod -Uri $PackageVersionUrl -Method Get -UseDefaultCredential -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)})
$PackageNameUrl = "https://feeds.dev.azure.com/{organization}/{project}/_apis/packaging/Feeds/{feedId}/packages/$($PI)?api-version=6.0-preview.1"
$PackageNames = (Invoke-RestMethod -Uri $PackageNameUrl -Method Get -UseDefaultCredential -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)})
$VersionsId = $PackageVersions.value.version
$packageName = $PackageNames.name
Write-Host "Package Versions: $VersionsId"
Write-Host "Package Name: $packageName"
ForEach ($VI in $VersionsId)
{
$DeleteUrl = "https://pkgs.dev.azure.com/{organization}/{project}/_apis/packaging/feeds/{feedId}/nuget/packages/$($packageName)/versions/$($VI)?api-version=6.0-preview.1"
$Member = (Invoke-RestMethod -Uri $DeleteUrl -Method DELETE -UseDefaultCredential -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)})
}
}
elseif($targetTask.result -eq "Failed")
{
Write-Host ("This is package not from upstream source!")
}
}
Note: If your feed scope is organization, remove the field /{project}
in the REST API URL.
The following are my test results. The initial number was 317
, as you can see in the previous screenshot. I aborted the script to avoid deleting all my upstream packages.