我触及了基础的想法 使用NuGet服务器进行PSRepository 几天前。这是将模块分配给其他系统的理想方式。这可能包括社区模块。今天,我将介绍我用来将模块从PSGallery重新发布到内部PSRepository的过程。

我已经为我的模块使用了内部存储库,我喜欢将所有模块发布到一个地方的想法。

使用内部存储库有几个原因。主机是当您的服务器或系统无法访问外部互联网时。另一个原因是控制在您的团队和服务器中使用的模块的版本。我的主要原因是我可以在他们被引入我的环境之前测试它们。

指数

入门

这是计划。我们编写一个脚本来下载我们关心的模块。运行一些测试。然后发布到我们的Nuget服务器。您可以通过手头快速执行此操作,但模块一直在更新。如果您不自动化此操作,则不会经常更新它们。

列表

We need to have a way to define the list of modules that we will manage. I use a JSON document called config.json that looks like this:

    {
        "模块":[
            {
                "Name":"Pester",
                "RequiredVersion":"4.3.1"
            },
            {
                "Name":"PSScriptAnalyzer"
            },
            {
                "Name":"Plaster"
            }
        ],
        "TestOrder":[
            "MyProject1",
            "MyProject2"
        ]
    }

The first section is the list of modules to republish. The plan is that we would update every module on that list when our process runs. We can lock a module to a specific version with the RequiredVersion property if that is what we need.

最后一节是如何定义要测试的项目和测试它们的哪些项目。我会解释为什么这一点很重要。

下载模块

My script will walk the module list and execute Save-Module for each one. In this example, I am saving them to a downloads folder.

    $config = Get-Content -Path .\config.json | ConvertFrom-Json
    foreach ( $module in $config.模块 )
    {
        $saveParam = @{
            Name = $module.Name
            Path = '.\downloads'
            Repository = 'PSGallery'
        }

        if ( $null -ne $module.RequiredVersion )
        {
            $saveParam.RequiredVersion = $module.RequiredVersion
        }

        Save-Module @saveParam
    }

目前,我即使我不需要更新它们,我也下载了整个模块列表。我这样做的原因是我需要它们可测试。运行后,我们有每个模块的本地副本。

PS> Get-ChildItem .\downloads\

Directory: .\downloads

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         3/5/2018   4:54 PM                Pester
d-----         3/5/2018   4:54 PM                Plaster
d-----         3/5/2018   4:54 PM                PSScriptAnalyzer

导入模块

下载模块后,我以相同的顺序导入它们。

    foreach( $module in $config.模块 )
    {
        $path = Join-Path .\downloads $module.Name
        Import-Module $path -Force
    }

This is the first sanity check on these modules. You should be able to call Import-Module on every module you download. You may find that a newer version of one module breaks another. Or a module added an important dependency that you missed. Windows Defender was deleting files out of some important modules a few weeks ago. You never know what could go wrong. If a module will not import, then you don’t want to be publishing it to your repository.

测试所有的东西

This is the point where I test all my projects that depend on these modules. All of my modules have a build.ps1 script that processes my project and runs the Pester tests. This step looks something like this:

    foreach($project in $config.TestOrder)
    {
        $repo = '//github.com/KevinMarquette/{0}.git' -f $project
        $buildScript = '{0}\build.ps1' -f $project
        git clone $repo
        & $buildScript -Task 'DependencyTest'
    }

I get a local copy of the project and execute the build.ps1 script. I pass DependencyTest to my build script so the build script knows that it is running as part of this process. I use that parameter to suppress any module management that my build script may be doing (we already loaded the modules we want it to use). I run all my tests, but this parameter could define what tests need to run if you cannot run them all.

You don’t have to have a build script for this to work. Calling Invoke-Pester on each project could be all that you need.

为什么这很重要

我重建一切并运行所有测试的原因是知道是否有任何模块会在将其介绍到我的环境之前导致我的问题。在其他人在他们的机器上发现它,或者在服务器上更新模块后,我想在这里捕获此故障。

我运行完整的构建,因为我的构建过程取决于很多社区模块。我的构建和发布的模块可能很好,但我仍然希望建立在每个系统上都没有问题。在CI / CD管道上控制版本控制很容易,但这允许我早期检测这些问题。

When the tests fail, I get to intervene and root cause the issue before anything actually breaks. I can pin the version of the module in the config.json while I remediate the tests or the project that does not work with the updated module.

有机会运行这些测试是我建立了这个管道的整个原因。我已经有了测试,这将最大化它们的价值。

发布模块

如果一切都会检查,那么我们可以将模块发布到内部存储库。

    foreach($module in $config.模块)
    {
        $path = '.\downloads\{0}\*\{0}.psd1' -f $module.Name

        $publishParam = @{
            Path        = $path
            Repository  = 'MyRepository'
            NuGetApiKey = $apiKey
            Force       = $true
        }
        Publish-Module @publishParam
    }

The $apiKey is defined when you set up your NuGet server feed for secured publishing.

Nuget / PowerShellett问题

我希望一切顺利,因为我的帖子意味着它应该。在这些工具的已发布版本中有2个大问题。我从艰难的方式学到了他们。

发布 - 模块-Force

Publish-Module uses string comparisons instead of version comparisons when publishing modules. So if you publish version 1.3.9 and then try to publish 1.3.12, the publish will fail. Thankfully you can ignore the version check with -Force. This is already resolved in the PowerShellGet source.

Nuget版标准化

在某些时候,Nuget开始归一化版本号。当您开始发布模块时,Nuget可能会决定它不喜欢模块使用的版本号,并将发布它决定的任何版本最适合该模块。

If you decide to republish PackageManagement version 1.1.7.0, NuGet decides that the trailing zero should not be there and removes it. It gets published with version 1.1.7 instead. The published version in your gallery will not match the version of the module in the PSGallery. This makes it hard to compare the two galleries for drift.

This issue is unresolved for 电源外壳Get 1.6.0 and NuGet 4.4.1.4656.

But all is not lost, there is a workaround. You can downgrade your NuGet version to NuGet 2.8.60717.93 (//www.NuGet.org/NuGet.exe)它将起作用。这是他们开始更改版本号之前的Nuget的最后一个版本之一。

结束思想

将所有模块直接从Psgallery一直从PSGallery中拉动所有模块更容易。但是一旦我开始在测试它们后开始发布自己的模块,我就会喜欢为我们现在依赖的所有社区模块做同样的想法。

通过以这种方式测试它们,我的服务器可以使用DSC从我的内部存储库中拉动最新版本。由于我们自己的内部模块,我的团队已经进行了频繁的模块更新。我现在正在通过相同的更新管道提供社区模块。我们更容易消耗社区模块。它还让我们全部更新,所有这些都在同一版本上,更重要的是所有在验证的社区模块中的兼容版本。