首页 文章

NuGet的Restore Package坚持特定的软件包版本

提问于
浏览
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 . *包位于我们的本地存储库中 .

我启用了Package Restore并将内部repo添加到了源代码中 . 但是,当我尝试从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 回答

  • 0

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

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

  • 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>
    
  • 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团队所做的那样,你应该保留所有版本的软件包 . 这样,如果您必须返回并构建项目的旧版本,您将确保拥有正确的软件包版本 .

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

  • 8

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

    话虽如此,包恢复功能 will not 会自动为您更新包 .

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

    • 安装要添加的任何新依赖项的最新稳定版本,除非您确实需要较旧(或预发布)版本

    • 在CI版本中使用Package Restore,允许您在您的VCS中签入NuGet包 not

    • Update-Package 如果......

    • 您需要从最新版本获得新的API调用或错误修复

    • 你有一个很有信心的测试套件

    • 你有时间处理潜在的后果

    我不鼓励定期升级包,因为 . 如果项目运行良好,最好让项目与旧的依赖项保持一致,因为在更新任何版本时存在风险包 .

    NuGet包应该遵循Semantic Versioning,它有很好的规则允许最无压力的包升级体验,但因为这没有强制实施(并且相信我,许多包发布者都不依赖它 . 即使包更新了只有少量版本增加,您无法确定(没有经过充分测试)新版本将与您的代码一起使用 .

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

相关问题