2012-07-24 83 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