我有一个包含以下packages.config的项目:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Framework.Infrastructure.Core" version="1.4.0.6" />
<package id="Framework.Infrastructure.Extensions" version="1.4.0.6" />
</packages>
Framework。*包位于我们的本地存储库中。
我启用了Package Restore并将我们的内部回购添加到了源代码中。但是,当我尝试从packages.config(基本上是nuget install packages.config -sources....
)恢复软件包时,我收到以下错误:
error : Unable to find version '1.4.0.6' of package 'Framework.Infrastructure.Extensions'
error : Unable to find version '1.4.0.6' of package 'Framework.Infrastructure.Core'.
存储库不再包含 1.4.0.6 版本的软件包(几个月前相关),而是新版本(例如, 1.5.1.6) )。
为什么NuGet没有找到新版本的软件包?我是否可以在packages.config中指定一些语法以确保下载最新版本?
简而言之,编写自定义脚本以更新我可以执行的程序包有什么不足之处吗?
谢谢。
答案 0 :(得分:39)
我认为有些人误解了Package Restore的意图。此功能仅添加到NuGet,目的是不要将包检入版本控制。很多人都在抱怨提交二进制文件会扩大其存储库的大小,而且当使用像git这样的DVCS时会更糟糕,其中整个存储库都是在本地下载的,包括Foo包的每个版本。
那么Package Restore究竟做了什么?基本上它在每个项目的packages.config中查找并简单地下拉列出的包的特定版本。这就像删除你的包文件夹,然后执行git reset --hard
将它们带回来(假设文件夹已经签入)。
为什么这很重要?为什么不升级到最新的软件包版本?如果您考虑Package Restore的最常见用例,即自动构建,那么应该为您提供线索。构建服务器应该只构建由开发人员测试和提交的项目。如果您让构建服务器决定何时更新包,那么您有一个尚未经过任何人测试的项目。作为开发人员,您应该决定何时进行升级。
请记住,安装或更新软件包并不是简单地下载.nupkg文件并添加引用。很多软件包都有副作用,例如更新.config文件,添加代码等。安装软件包时,所有这些副作用都会发生在本地副本上。您现在可以提交代码并排除包文件。
当另一个开发人员或构建服务器检出代码时,他将使用完全相同的副作用代码减去包文件。 Package Restore只是从NuGet存储库中提取这些文件,现在我们拥有了处理这个项目所需的一切。
NuGet团队承诺维护所有版本的软件包,以便您始终能够下载正确的版本。但是,正如我们几个月前看到的那样,当NuGet服务器出现故障时,它几乎瘫痪了Package Restore,很多人无法构建。
我建议您设置自己的NuGet存储库(一个简单的文件共享),并保留您在那里使用的所有包的副本。这样,您就不依赖于构建的外部服务器。正如NuGet团队所做的那样,你应该保留所有版本的软件包。这样,如果您必须返回并构建项目的旧版本,您将确保拥有正确的软件包版本。
我希望这可以解释该功能的工作原理以及它为何如此工作。
答案 1 :(得分:8)
我建议您阅读NuGet documentation for Versioning。它解释了如何在packages.config
文件中使用版本号(和范围)以允许Update-Package
命令知道要升级到的可接受版本。
话虽这么说,套餐还原功能不会自动更新软件包。
有了这些信息,最好的工作流程IMO是:
Update-Package
如果......
我不鼓励仅仅因为而定期升级包。如果项目运行良好,最好让项目与旧的依赖项保持一致,因为更新任何软件包的版本时存在风险。
NuGet包应该遵循Semantic Versioning,它具有允许最无压力的包升级体验的良好规则,但因为没有强制执行(并且相信我,许多包发布者不遵循SemVer),你不能依赖它。即使只更新了一个版本来更新软件包,您也无法确定(没有经过充分测试)新版本将与您的代码一起使用。
总之,自动升级任何软件包 通常是个坏主意。最好让开发人员明确选择更新任何给定的包,并且只是出于足够的原因。
答案 2 :(得分:0)
如果您只是从nuget中删除并重新安装软件包,则version属性将引用最新版本。
您可能需要手动编辑packages.config以从nuget重新安装之前删除旧引用(因为我最近有一种情况,nuget不允许我安装新包,因为它认为我有旧包礼物)
答案 3 :(得分:0)
如果遇到任何人,我编写了一个PowerShell模块并将其包装在NuGet包中,用户需要在创建模板时运行该包。该脚本遍历解决方案中的每个C#项目,找到其“packages.config”(如果有的话),然后删除并重新安装那里提到的每个包。
显然,在一般方法和小错误方面都有很大的改进空间(例如,安装部分中的nuget命令不会在全名中包含空格的解决方案上运行),但是这是一个开始。
档案 NuGet-RestorePackagesInAllProjects.psm1
$NuGetSources = "https://nuget.org/api/v2/;" # put your internal sources here as needed
function NuGet-RestorePackagesInAllProjects {
# get the solution directory
$solutionDir = (get-childitem $dte.Solution.FullName).DirectoryName
# for each C# project in the solution, process packages.config file, if there is one
$dte.Solution.Projects | Where-Object { $_.Type -eq "C#" } | ForEach-Object {
$currentProject = $_
$currentProjectName = $currentProject.ProjectName
$currentProjectDir = (get-childitem $_.FullName).DirectoryName
Write-Host ******* Starting processing $currentProjectName
# get the packages.config file for the current project
$packagesFile = $currentProject.ProjectItems | Where-Object { $_.Name -eq "packages.config" }
# if there's no packages.config, print a message and continue to the next project
if ($packagesFile -eq $null -or $packagesFile.count -gt 1) {
write-host ------- Project $currentProjectName doesn''t have packages.config
return
}
# read the contents of packages.config file and extract the list of packages in it
$fileName = $currentProjectDir + "\packages.config"
[xml]$content = Get-Content $fileName
$packageList = $content.packages.package | % { $_.id }
# for each package in the packages.config, uninstall the package (or simply remove the line from the file, if the uninstall fails)
$packageList | ForEach-Object {
$currentPackage = $_
write-host Uninstalling $currentPackage from $currentProjectName
try {
Uninstall-Package $currentPackage -ProjectName $currentProjectName -RemoveDependencies -Force
}
catch {
write-host '!!!!!!! $_.Exception.Message is' $_.Exception.Message
$node = $content.SelectSingleNode("//package[@id='$currentPackage']")
[Void]$node.ParentNode.RemoveChild($node)
$content.Save($fileName)
}
}
# download each package into the $(SolutionDir)packages folder, and install it into the current project from there
$packageList | ForEach-Object {
$currentPackage = $_
$localPackagesDir = $solutionDir + "\packages"
$cmd = $solutionDir + "\.nuget\nuget.exe install " + $currentPackage + " -Source """ + $NuGetSources + """ -o " + $localPackagesDir
write-host Installing $currentPackage to $currentProjectName
invoke-expression -command $cmd
Install-Package $currentPackage -ProjectName $currentProjectName -Source $localPackagesDir
}
Write-Host ******* Finished processing $currentProjectName
}
}
Export-ModuleMember NuGet-RestorePackagesInAllProjects
文件 init.ps1
param($installPath, $toolsPath, $package)
Import-Module (Join-Path $toolsPath NuGet-RestorePackagesInAllProjects.psm1)
Enable-PackageRestore
NuGet-RestorePackagesInAllProjects
包的 .nuspec 文件
<?xml version="1.0" encoding="utf-16"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>NuGet-RestorePackagesInAllProjects</id>
<version>0.5.0</version>
<title>Custom NuGet Package Restore</title>
<authors>Me (c) 2012</authors>
<owners />
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Restore all packages in a given solution from packages.config in each project. For each packages.config, uninstalls all packages and then re-install them again from the sources specified in the script.</description>
<dependencies>
<dependency id="NuGetPowerTools" />
</dependencies>
</metadata>
<files>
<file src="init.ps1" target="tools\init.ps1" />
<file src="NuGet-RestorePackagesInAllProjects.psm1" target="tools\NuGet-RestorePackagesInAllProjects.psm1" />
</files>
</package>