PowerShell 小工具 - 自動補齊離線安裝 NuGet 套件
3 | 3,170 |
在一般情況下,NuGet 套件會在編譯時自動從網路下載安裝,不需我們費心。但現實世界不如想像美好,有時你需要在無法上網的環境編譯專案,簡單解法是開個本地資料夾當成 NuGet 套件來源參考,手動下載預先放入 .nupkg 檔以實現離線安裝 NuGet 套件。
在 NuGet Gallery 每個套件首頁都有個 Download Package,點下去可下載格式如 dapper.2.1.24.nupkg 的套件檔,將其放入本機資料夾即可用於離線安裝。
如果你耐心破表,人工比對參照清單或遇到編譯有缺再去查網頁另存 .nupkg,日子其實也能過,但我是現代王藍田哪可能受得了?要我重複做個三次,肯定又會上演氣到摔滑鼠,用腳踩再咬破吐掉的戲碼(喂)。
所以,小工具來了,我寫了一支 Download-Nupkg.ps1,有三種用法:
# 指定套件名稱及版號
.\Download-Nupkg.ps1 Dapper 2.1.1
# 指定套件名稱,列舉版號,預設下載最新版
.\Download-Nupkg.ps1 Newtonsoft.Json
# 掃瞄 packages.config (.NET Framework) 或 .csproj (.NET 6+) 列出有參照的 NuGet 套件
# 比對本機資料夾是否已存在該 .nupkg ,若沒有才下載
.\Download-Nupkg.ps1 "X:\Github\WebApiWithAspNetMvcDemo\src\DemoWeb\packages.config"
附上程式碼,有需要的同學請自取:(註:程式用 NuGet API 查詢套件可用版號及下載 .nupkg 檔)
param (
[Parameter(Mandatory = $true)]
[string]$pkgNameOrPkgConfig,
[string]$version,
[string]$saveFolder = '.'
)
Function GenNupkgPath($pkgName, $version) {
return Join-Path $saveFolder "$pkgName.$version.nupkg"
}
Function DownloadNupkg {
param (
[Parameter(Mandatory = $true)]
[string]$packageName, [string]$version
)
# https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource
$lowerId = $packageName.ToLower()
$url = "https://api.nuget.org/v3-flatcontainer/$lowerId/index.json"
try {
$json = (Invoke-WebRequest -Uri $url).Content
}
catch {
Write-Host "Package not found - $packageName" -ForegroundColor Red
return
}
$versions = ($json | ConvertFrom-Json).versions
$latestVersion = $versions[-1]
if ([string]::IsNullOrEmpty($version)) {
$versionList = $versions -join ", "
Write-Host "Versions: $versionList"
$selVer = Read-Host "Select (default: $latestVersion)"
if ([string]::IsNullOrEmpty($selVer)) {
$selVer = $latestVersion
}
$version = $selVer
}
# Convert x.x.x.0 to x.x.x
$verParts = $version.Split('.')
if ($verParts.Length -eq 4 -and $verParts[-1] -eq '0') {
$version = $verParts[0..2] -join '.'
}
if (!$versions.Contains($version)) {
Write-Host "Failed to download $packageName $version" -ForegroundColor Red
return
}
else {
$lowerVersion = $version.ToLower()
[string]$downloadUrl = "https://api.nuget.org/v3-flatcontainer/$lowerId/$lowerVersion/$lowerId.$lowerVersion.nupkg"
$path = GenNupkgPath $packageName $version
try {
Invoke-WebRequest -Uri $downloadUrl -OutFile $path
Write-Host " $path saved" -ForegroundColor Gray
}
catch {
Write-Host "Failed to download $downloadUrl" -ForegroundColor Red
}
}
}
$pkgList = @()
if ($pkgNameOrPkgConfig -match '(.config|.csproj)$') {
try {
[xml]$xml = Get-Content $pkgNameOrPkgConfig
if ($xml.Project) {
$xml.Project.ItemGroup | ForEach-Object {
if ($_.PackageReference) {
$_.PackageReference | ForEach-Object {
if (!(Test-Path (GenNupkgPath $_.Include $_.Version))) {
$pkgList += [PSCustomObject]@{
Name = $_.Include
Version = $_.Version
}
}
}
}
}
}
elseif ($xml.packages) {
$xml.packages.package | ForEach-Object {
if (!(Test-Path (GenNupkgPath $_.id $_.version))) {
$pkgList += [PSCustomObject]@{
Name = $_.id
Version = $_.version
}
}
}
}
else {
Write-Host "Unknown XML schema" -ForegroundColor Red
return
}
if ($pkgList.Count -gt 0) {
Write-Host "Missing pakcages found:"
$idx = 1
$pkgList | ForEach-Object {
Write-Host "$idx. $($_.Name) $($_.Version)"
$idx++
}
$confirm = Read-Host "Download? (Y/n)"
if (![string]::IsNullOrEmpty($confirm) -and $confirm -ine 'y') {
Write-Host 'Abort'
return
}
}
}
catch {
Write-Host "Failed to load $pkgNameOrPkgConfig - $_" -ForegroundColor Red
return
}
}
else {
$pkgList += [PSCustomObject]@{
Name = $pkgNameOrPkgConfig
Version = $version
}
}
$pkgList | ForEach-Object {
Write-Host "Downloading $($_.Name) $($_.Version)..." -ForegroundColor Yellow
DownloadNupkg $_.Name $_.Version
}
My PowerShell tool to scan packages.config or .csproj to download .nupkg to local folder for offline install.
Comments
# by Jason
王藍田?我居然看不懂黑大的用典? 想不到不但技術跟不上,連中文造詣都跟不上! 趕緊搜尋惡補一下,繼續裝成看懂的樣子😂
# by isarhoo
使用時有發現,如有多個 ItemGroup 且 ItemGroup 中沒有 PackageReference 項目時會出錯,以下修正僅供參考 if ($xml.Project) { $xml.Project.ItemGroup | ForEach-Object { #跳掉沒有 PackageReference 的 ItemGroup if (-not $_.PackageReference) { return } #跳掉沒有 PackageReference 的 ItemGroup $_.PackageReference | ForEach-Object { if (!(Test-Path (GenNupkgPath $_.Include $_.Version))) { $pkgList += [PSCustomObject]@{ Name = $_.Include Version = $_.Version } } } } }
# by Jeffrey
to isarhoo, 感謝回報,已修正程式。