2012-07-24 87 views
19

我有一个项目具有以下packages.config:的NuGet的恢复套餐坚持特定的包版本

<?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> 

其中该构架*包坐在我们的本地仓库。

我已启用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中指定,以确保最新版本将被下载?

简而言之,是否有什么书写自定义脚本来更新我可以做的软件包?

谢谢。

回答

39

我认为有些人误解了Package Restore的含义。 NuGet仅将此功能添加到不需要将包检入版本控制的目的中。很多人都抱怨说,提交二进制文件正在扩大其存储库的规模,而使用像git这样的DVCS时甚至更糟糕,其中整个repo在本地下载并包含每个版本的包Foo。

那么Package Restore究竟做了什么?基本上它会在每个项目的packages.config中查找,并简单地下拉列出的特定版本的软件包。这就像删除你的包文件夹,然后做git reset --hard将它们带回(假设文件夹已签入)。

为什么这很重要?为什么不升级到最新的软件包版本?如果你考虑Package Restore最常见的用例,那就是自动构建,这应该给你一个线索。构建服务器应该只构建由开发人员测试并提交的项目。如果您让构建服务器决定何时更新软件包,那么您有一个未经任何人测试的项目。作为开发人员,您应该是决定何时进行升级的人员。

请记住,安装或更新软件包不是简单地拉下.nupkg文件并添加引用。许多软件包都有副作用,比如更新.config文件,添加代码等。安装软件包时,所有这些副作用都发生在本地副本上。您现在可以提交代码并排除软件包文件。

当另一个开发人员或构建服务器检出代码时,他会得到与您的包文件完全相同的副作用代码。 Package Restore只需将这些文件从NuGet存储库中取出,现在我们拥有了处理该项目所需的一切。

NuGet团队已承诺维护所有版本的软件包,以便您始终能够下拉正确的版本。然而,正如我们在几个月前看到的那样,当NuGet服务器出现故障时,它几乎损坏了Package Restore,很多人无法构建。

我建议您设置您自己的NuGet存储库(一个简单的文件共享会这样做)并保留您在那里使用的所有软件包的副本。这样你就不依赖于你的构建的外部服务器。和NuGet团队一样,你应该保留所有版本的软件包。通过这种方式,如果您不得不返回并构建项目的旧版本,那么您一定会获得正确的软件包版本。

我希望这解释了该功能是如何工作的以及为什么它以这种方式工作。

0

如果您只是从nuget中删除并重新安装软件包,version属性将引用最新版本。

您可能需要手动编辑packages.config以在从nuget重新安装之前删除旧的参考(因为我最近有一种情况,nuget不允许我安装新包,因为它以为我有旧包装礼物)

+0

这意味着包恢复,不是吗? – 2012-07-24 12:08:00

+0

是的,它的确如此,但OP提到如果有任何包语法配置来获取最新版本。完成一次后,使用存储库的其他人将能够使用软件包恢复获取该项目。 – dougajmcdonald 2012-07-24 12:13:29

+1

有问题的项目是我为内部项目执行的自定义解决方案模板的一部分。这个想法是,开发人员可以从模板创建一个新的解决方案,并获得整个结构(包括现成的基础设施软件包的参考)并准备就绪。我没有问题写一个自定义脚本,可以覆盖解决方案中的所有项目,并根据需要重新安装软件包,但告诉人们开始在模板中混淆以使其工作是错误的... – 2012-07-24 12:21:21

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> 
8

我建议你阅读NuGet documentation for Versioning。它解释了如何在packages.config文件中使用版本号(和范围),以便Update-Package命令知道哪些版本可以升级到可接受的版本。

这就是说,包恢复功能不会自动更新包。

有了这些信息,最好的工作流程IMO是:

  • 安装要添加任何新的依赖的最新稳定版本,除非你真的需要一个较旧的(或预发布)版本
  • 在您的CI版本中使用软件包恢复,允许您将而不是签入NuGet软件包到您的VCS中
  • 只有Update-Package if ...
    • 你需要从最新版本
    • 你有一个很大的测试套件信心
    • 你有时间来处理潜在后果

我一个新的API调用或错误修复不要鼓励定期升级套餐只是因为。如果项目工作良好,最好让项目按照原有的依赖关系运行,因为在更新任何软件包的版本时存在风险。

NuGet软件包应该遵循Semantic Versioning,它具有良好的规则以允许最无压力的软件包升级体验,但由于这不是强制执行的(并且相信我,许多软件包发布者不遵循SemVer)不依赖于它。即使只更新了一个小版本来更新包,也不能确定(没有足够的测试)新版本将与您的代码一起工作。

总之,升级任何包自动通常是一个坏主意。最好让开发人员明确选择更新任何给定的软件包,并且只是出于一个足够好的理由。

+3

它的问题在于,基于版本规范的'版本=“1.4.0.6”'版本(默认在packages.config中)应该被翻译成'version> = 1.4.0.6',但事实并非如此。我认为这是nuget恢复中的一个错误,因为“update”根本不使用packages.config(它似乎只使用'packages'目录中的文件夹 - 所以如果你没有包,那么“update”甚至不会做任何事情如果你有packages.config文件)。 – 2013-04-16 14:11:08

+4

@FuriCuri您描述的版本范围仅用于'.nuspec'文件,而不是'packages.config'。它们用于描述包之间的依赖关系。当在你的项目中指定依赖项时,你可以使用总是引用特定版本的'packages.config'文件。是的,你是对的,除非你最近建立了你的项目并且所有的Nu​​Get依赖包存在'/ packages'目录中,'Update-Package'命令将不起作用。这可能不方便或不直观,但我不相信这是一个错误;这只是NuGet的运作方式。 – 2013-04-16 14:30:50

+1

我明白了。那么,我在这里描述了它https://nuget.codeplex.com/workitem/3264最大的问题是,如果你试图恢复项目并使用旧版本引用一些不再可用的软件包(但是有一个更新的版本)没有办法在该软件包的packages.config和.proj文件中生成nuget更新版本。 – 2013-04-17 06:37:08