19

我有一个包含以下 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 中指定一些语法以确保下载最新版本吗?

简而言之,除了编写自定义脚本来更新我可以做的包之外还有什么?

谢谢你。

4

4 回答 4

39

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

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

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

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

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

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

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

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

于 2012-07-24T14:32:31.037 回答
8

我建议您阅读有关版本控制的 NuGet 文档。它解释了如何在文件中使用版本号(和范围)packages.config以允许Update-Package命令知道升级到哪些可接受的版本。

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

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

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

我不鼓励仅仅因为. 如果旧依赖项运行良好,最好让项目按原样运行,因为更新任何包的版本时存在风险。

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

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

于 2013-01-02T23:07:34.290 回答
0

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

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

于 2012-07-24T12:03:18.337 回答
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>
于 2012-07-26T05:22:30.783 回答