【问题标题】:NuGet's Restore Package insists on specific package versionsNuGet 的还原包坚持特定的包版本
【发布时间】:2012-07-24 12:00:31
【问题描述】:

我有一个包含以下 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.* 包在我们本地存储库中的位置。

我已启用包还原并将我们的内部存储库添加到源中。但是,当我尝试从 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 中指定一些语法以确保下载最新版本吗?

简而言之,有没有什么可以写一个自定义脚本来更新我能做的包?

谢谢。

【问题讨论】:

    标签: .net nuget nuget-package


    【解决方案1】:

    我认为有些人误解了包还原的目的。将此功能添加到 NuGet 只是为了不需要将包签入版本控制。很多人抱怨提交二进制文件会爆炸性地增加他们的存储库的大小,使用 git 之类的 DVCS 时更糟,其中整个 repo 都在本地下载并包含包 Foo 的每个版本。

    那么包还原到底是做什么的呢?基本上,它在每个项目的 packages.config 中查找,并简单地拉下列出的包的特定版本。这就像删除您的包文件夹,然后执行git reset --hard 将它们带回来(假设该文件夹已签入)。

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

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

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

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

    我建议您设置自己的 NuGet 存储库(一个简单的文件共享即可)并保留您在其中使用的所有包的副本。这样,您的构建就不会依赖外部服务器。正如 NuGet 团队所做的那样,您应该保留包的所有版本。这样,如果您必须返回并构建项目的旧版本,您将确保有正确的包版本可用。

    我希望这能解释该功能的工作原理以及它为何如此工作。

    【讨论】:

      【解决方案2】:

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

      话虽如此,包恢复功能不会自动为您更新包。

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

      • 安装您正在添加的任何新依赖项的最新、稳定版本,除非您确实需要较旧(或预发布)版本
      • 在您的 CI 构建中使用包还原,允许您将 NuGet 包签入您的 VCS
      • Update-Package 如果...
        • 您需要新版本的 API 调用或错误修复
        • 你有一个很棒的测试套件来增强信心
        • 您有时间处理潜在的后果

      我不鼓励定期升级软件包只是因为。如果旧依赖项运行良好,最好让项目按原样运行,因为更新任何包的版本时存在风险。

      NuGet 包应该遵循Semantic Versioning,它具有允许最轻松的包升级体验的良好规则,但因为这不是强制执行的(相信我,许多包发布者不遵循 SemVer),你可以不要依赖它。即使更新的包只增加了较小的版本,您也无法确定(没有足够的测试)新版本是否可以与您的代码一起使用。

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

      【讨论】:

      • 它的问题在于,基于版本控制规范,version="1.4.0.6" 版本(默认情况下在 packages.config 中)应该被翻译成 version &gt;= 1.4.0.6,但事实并非如此。而且我认为这是 nuget restore 中的一个错误,因为“更新”根本不使用 packages.config(它似乎只使用 packages 目录中的文件夹 - 所以如果你没有包,那么“更新”甚至不会做任何事情如果你有 packages.config 文件)。
      • @FuriCuri 您描述的版本范围仅用于.nuspec 文件,而不是packages.config。它们用于描述包之间的依赖关系。在项目中指定依赖项时,您使用packages.config 文件,该文件将始终引用特定版本。是的,你是对的,Update-Package 命令将不起作用,除非你最近构建了你的项目并且所有 NuGet 依赖项都存在于/packages 目录中。这可能不方便或不直观,但我不认为这是一个错误;这就是 NuGet 的操作方式。
      • 我明白了。好吧,我已经在这里描述了nuget.codeplex.com/workitem/3264 最大的问题是,如果您尝试 nuget restore 项目并使用存储库中不再可用的旧版本引用某些包(但有更新版本),则没有在该包的 packages.config 和 .proj 文件中制作 nuget 更新版本的方法。
      • @FuriCuri 我认为您将包还原和更新包的想法混为一谈。最初,NuGet 甚至没有包还原;您必须将 /packages 目录的内容提交到版本控制中。他们引入了包还原以允许用户避免这种情况。如果您处于从本地存储库中删除软件包的情况下,如您的问题所述,您可能需要考虑不使用软件包还原。不要与框架抗争。
      • 讨论中的非常微妙的点。我想知道为什么范围不能通过 pakages.config 文件工作。
      【解决方案3】:

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

      在从 nuget 重新安装之前,您可能需要手动编辑 packages.config 以删除旧引用(因为我最近遇到了一种情况,即 nuget 不允许我安装新包,因为它认为我有旧包存在)

      【讨论】:

      • 是的,确实如此,但是 OP 提到是否有任何包语法配置来获取最新版本。一旦完成一次,使用存储库的任何其他人都可以使用包恢复来获取项目
      • 有问题的项目是我为我们的内部项目做的自定义解决方案模板的一部分。这个想法是,开发人员将从模板中创建一个新的解决方案,并获得整个结构(包括对基础设施包的现成引用)并准备就绪。我可以编写一个自定义脚本来遍历解决方案中的所有项目并根据需要重新安装软件包,但是告诉人们开始在模板中乱搞以使其正常工作是错误的......
      • 无论如何,我发现 NuGet 不会尝试从存储库中获取最新包的情况非常很奇怪。
      • 包还原的重点是还原包,而不是更新它们。与 nuget 兼容的工作流程是保留旧包,以便 nuget 可以恢复它们,然后运行更新。
      • @ilyawur 如果您有一个包含 NuGet 包的解决方案模板,我将包含一个 README 文件,说明包含哪些包,并添加提醒开发人员可以运行 Update-Package 以获取最新版本。
      【解决方案4】:

      万一有人遇到这种情况,我编写了一个 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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-28
        • 2017-09-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-25
        • 1970-01-01
        • 2013-07-15
        相关资源
        最近更新 更多