Skip to content

Instantly share code, notes, and snippets.

@amis92
Last active January 18, 2024 14:22
Show Gist options
  • Save amis92/90bdaee50331bfa50bb0fff8edcfcf27 to your computer and use it in GitHub Desktop.
Save amis92/90bdaee50331bfa50bb0fff8edcfcf27 to your computer and use it in GitHub Desktop.
Script to update nugets using dotnet list package --outdated JSON output, with Allegro Pay filtering possible, and support for WhatIf+Confirm
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter()]
[switch]
$InternalOnly
)
$ErrorActionPreference = 'Stop'
function Invoke-Native {
param(
[Parameter(Mandatory, Position = 0)]
[scriptblock]
$Call
)
foreach ($item in $Call) {
$invocation = $ExecutionContext.InvokeCommand.ExpandString($item).Trim()
Write-Verbose "Calling $invocation"
& $item | Write-Output -OutVariable result
if ($LASTEXITCODE -ne 0) {
$invocation, $result | Join-String -Separator "`n" | Write-Error
throw "Failed to call native command - exit code $LASTEXITCODE."
}
}
}
$dotnetSdks = Invoke-Native { dotnet --list-sdks } | Select-String -Pattern "^8\."
if (-not $dotnetSdks) {
throw "This script requires .NET SDK v8 or higher to be installed."
}
[semver]$sdkVersion = Invoke-Native { dotnet --version }
[semver]$minSdkVersion = "8.0.100"
if ($sdkVersion -lt $minSdkVersion) {
# https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-list-package#:~:text=Security%20Vulnerabilities.-,%2D%2Dformat,-%3Cconsole%7Cjson%3E
throw "This script requires at least version $minSdkVersion of .NET SDK to be used. Currently running: $sdkVersion"
}
Invoke-Native { dotnet restore }
Write-Verbose "Looking for GlobalPackageReference items"
# take sample project (but not nuke's build)
$sampleProject = Invoke-Native { dotnet sln list } | Select-String "^.*?\.\w+proj(?<!_build.csproj)$" -Raw | Select-Object -First 1
$buildEval = Invoke-Native { dotnet build $sampleProject --getItem:GlobalPackageReference --getProperty:ManagePackageVersionsCentrally } | ConvertFrom-Json
$globals = @($buildEval.Items.GlobalPackageReference.Identity)
$isCpm = $buildEval.Properties.ManagePackageVersionsCentrally -eq 'true'
$json = Invoke-Native { dotnet list package --outdated --format json --output-version 1 } | ConvertFrom-Json
$allUpdates = $json.projects | ForEach-Object {
$project = $_
$projectPath = Resolve-Path -Relative $project.path
Write-Verbose "Processing updates in $projectPath"
if (-not $isCpm) {
# restore and check outdated again, maybe other projects already updated
Invoke-Native { dotnet restore $projectPath }
# Resolve-Path $projectPath necessary because of https://github.com/NuGet/Home/issues/12954
$project = (Invoke-Native { dotnet list $(Resolve-Path $projectPath) package --outdated --format json --output-version 1 } | ConvertFrom-Json).projects
}
$updates = $project.frameworks
| Where-Object { $_.topLevelPackages -and $_.topLevelPackages.Count -gt 0 }
| ForEach-Object {
$tfmItem = $_
$tfmItem.topLevelPackages | Add-Member NoteProperty tfm $tfmItem.framework -PassThru
}
| ForEach-Object { $_ | Add-Member isGlobal $globals.Contains($_.id) -PassThru }
# | Select-Object @{ n='isGlobal'; e={ $globals.Contains($_.id) }}
| Where-Object { $_.latestVersion -ne $_.resolvedVersion }
| Where-Object { (-not $InternalOnly) -or ($_.id -match "^(FinAi|Vabank|Allegro|AllegroPay|AlleCare|Deposits)\.") }
if ($updates.Count -eq 0) {
Write-Verbose "No updates found for $projectPath"
return
}
[int]$tfmCount = $project.frameworks.Count
if ($tfmCount -gt 1) {
$updates = $updates | Group-Object id, resolvedVersion | ForEach-Object {
if ($_.Count -ne $tfmCount) {
Write-Warning "Skipped update for package $($_.Name) because it's not present in all $tfmCount TFMs, only $($_.Group.tfm -join ';')."
}
else {
$_.Group[0]
}
}
}
if ($isCpm) {
$updates | Write-Output
}
else {
$updates | ForEach-Object {
$pkg = $_
$pkgid = $pkg.id
$pkgversion = $pkg.latestVersion
if ($PSCmdlet.ShouldProcess("$pkgid v$($pkg.resolvedVersion) -> v$pkgversion ($projectPath)", "update package")) {
if ($pkg.isGlobal) {
$packagesProps = Get-Item Directory.Packages.props
(Get-Content $packagesProps -Raw) -replace "<GlobalPackageReference Include=`"$pkgid`" Version=`"[^`"]+`"\s?/>", "<GlobalPackageReference Include=`"$pkgid`" Version=`"$pkgversion`" />"
| Set-Content $packagesProps -NoNewline -Force -Confirm:$false
}
else {
Invoke-Native { dotnet add $projectPath package $pkgid --version $pkgversion }
}
}
}
}
}
if ($isCpm -and $allUpdates.Count -gt 0) {
$packagesProps = Get-Item Directory.Packages.props
$text = (Get-Content $packagesProps -Raw)
$allUpdates | Group-Object id | ForEach-Object {
$pkg = $_.Group[0]
$pkgid = $pkg.id
$pkgversion = $pkg.latestVersion
if ($PSCmdlet.ShouldProcess("$pkgid v$($pkg.resolvedVersion) -> v$pkgversion", "update package")) {
Write-Verbose "$pkgid isGlobal=$($pkg.isGlobal)"
if ($pkg.isGlobal) {
$text = $text -replace "<GlobalPackageReference Include=`"$pkgid`" Version=`"[^`"]+`"\s?/>", "<GlobalPackageReference Include=`"$pkgid`" Version=`"$pkgversion`" />"
}
else {
$text = $text -replace "<PackageVersion Include=`"$pkgid`" Version=`"[^`"]+`"", "<PackageVersion Include=`"$pkgid`" Version=`"$pkgversion`""
}
}
}
$text | Set-Content $packagesProps -NoNewline -Force -Confirm:$false
}
Invoke-Native { dotnet restore }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment