一个新的开始,建立一个模块。

我在很长一段时间内没有从头开始构建模块。我的进程已经发展了,我已经在我的过程中纳入了很多想法。大多数情况下,我复制了现有模块,刚刚取出所有功能。这是工作的,因为每个模块在我上次构建的那个模块中增加了一段时间。

我也意识到我可能会有一些较老的想法烘焙到我的过程中。我在社区中看到了很多好的工作,我没有正式采用,因为我刚刚工作了。它很快和易于运行。

我正在研究一个新的模块,我想重新考虑我在设置一个新模块时所做的很多事情。我特别希望将CI / CD(连续整合和连续交付)的想法纳入我的管道。为了帮助获得新的透视,我将根据沃伦框架的工作来建立它(ramblingcookiemonster)我将使用他的 Psdepend. 项目作为我的参考。他为社区建立了相当多的模块。它还有助于他编写了许多我正在计划使用的模块,并有几个覆盖这些模块的伟大博客文章。

指数

快速概述

在高级别,我们将建立一个新模块并放入构成CI / CD管道的几个组件。

依赖性

构建脚本将处理大多数依赖项,我们将创建帐户 appveyor.com. 电源外壳 Gallery. along the way.

您真正需要的唯一一个遵循的本地组件是Git,PowerShell 5.0和Pester。如果您运行构建脚本,它将拉下所有其他所需的模块,以便本地运行管道。

入门建立我的模块

我一直在合作 graphviz. 最近,我真的很喜欢它。它使我能够生成图形或图表的图表。使用适当的辅助功能,它将使得在飞行中非常容易生成这些图形。

所以,让我们开始。第一件事是在Github上创建一个新的存储库 Psgraph.. 。如果我们要使用源码控制,我们也可以与它开始。一旦创建,我就会克隆到我当地系统。

git clone //github.com/KevinMarquette/PSGraph.git

文件夹结构

我创建了一个像psdepend的文件夹结构。

 Psgraph.. 
├───Examples
├───PSGraph
│   ├───Classes
│   ├───Private
│   └───Public
└───Tests

This already breaks from my approach and I think it does so in a good way. Let’s take a moment to talk about what we have here as a way to understand it better. First off, the module is not the entire git repo. It will be contained in the sub Psgraph.. folder. The PrivatePublic folders will contain the functions for the module.

通过将模块移出存储库的根目录,它允许我们在模块外部具有示例,测试,构建和发布组件。我喜欢这样,因为它们不需要成为模块的一部分,并且需要在部署时不需要模块不需要的其他依赖项。我计划采用它进展。

其他文件和组件

有很多额外的文件我需要走路。

build.ps1.

拥有此文件使其很容易弄清楚从哪里建立。我用过 Psake. 之前和常见模式是将构建文件1文件作为起点,然后调用psake.ps1文件。

param ($Task = 'Default')

# Grab nuget bits, install modules, set build variables, start build.
Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null

Install-Module Psake, PSDeploy, BuildHelpers -force
Install-Module Pester -Force -SkipPublisherCheck
Import-Module Psake, BuildHelpers

Set-BuildEnvironment

Invoke-psake -buildFile .\psake.ps1 -taskList $Task -nologo
exit ( [int]( -not $psake.build_success ) )

In this script, we are defaulting to the ‘default’ task. The psake.ps1 script will have more tasks and the $Task will allow us to run specified build steps if needed. Then it installs and loads all the required modules for the build. I like what I see here, so I’ll reuse this script as it is. Set-BuildEnvironment is new to me, so I’ll to loop back and figure out what that is doing later.

psake.ps1.

This is the psake script that runs all the build tasks. A lot of the magic is happening here. It has 4 tasks defined. init, test, builddeploy.

当我走路时 psake.ps1. 脚本来自psdeploy,它看起来非常平易近。那里有一些钩子有用于在构建系统中运行的钩子,并从纠缠到该构建的发布测试结果。我可以像没有改变任何改变的那样借此借鉴。在这里发布有点久,所以在回购中查看它。

我确实需要获得API键 电源外壳 Gallery.。只需在网站上注册,钥匙就在那里。非常快速简单。我将向您展示在下一节中使用它的位置。

appveyor.yml.

这用于自动构建。再次,这是我想要实现的。沃伦已经有一个指导谈论他如何设置它 与GitHub,Pester和Apveyor有趣.

我去了 appveyor.com. 并创建了一个帐户。几乎没有任何努力,我看过我的GitHub项目。我在Apveyor中创建了一个新项目,并选择了我的GraphViz项目。就像那样,我有一个建立系统准备构建我的模块,我甚至没有检查任何内容。

这是我在当地项目中所需的文件。

# See http://www.appveyor.com/docs/appveyor-yml for many more options

environment:
  NugetApiKey:
    secure: sqj8QGRYue5Vq3vZm2GdcCttqyOkt7NOheKlnmIUq1UcgVrmDezFArp/2Z1+G3oT

# Allow WMF5 (i.e. PowerShellGallery functionality)
os: WMF 5

# Skip on updates to the readme.
# We can force this by adding [skip ci] or [ci skip] anywhere in commit message 
skip_commits:
message: /updated readme.*|update readme.*s/

build: false

#Kick off the CI/CD pipeline
test_script:
- ps: . .\build.ps1

此yaml文件看起来相当基本,其中一个例外,neetapikey(psgallery api键)。我对他如何处理它的沃伦,他告诉我它被加密了。 appvoyer有办法 加密字符串 您可以在您的文件中使用。所以要清楚,这不是你的纯文本psgallery api键。那将是非常糟糕的。这是通过存储在您的存放者帐户中的私钥加密。

我用了所有其他东西(只需要提供我的耐保险服)。

在Readme.md中构建状态

One cool thing that I almost overlooked is that Warren has a realtime buid status icon in his project readme.md. It looks like this  构建状态

To get that, I had to add this line to the top of my readme.md.

![Build status](//ci.appveyor.com/api/projects/status/cgo827o4f74lmf9w/branch/master?svg=true)

There is a project ID embedded in that first URL. cgo827o4f74lmf9w. I found that in AppVeyor.com under project settings in the Webhook URL field. This will be unique for each project.

他还将其包裹在“构建状态”页面的“链接中”。

//ci.appveyor.com/project/kevinmarquette/PSGraph/branch/master

mkdocs.yml.

直到这一点,我已经合作或对这些组件进行了一般的理解。这个文件对我来说是新的。乍一看,它看起来像是用崩扫文件的文档。我喜欢那个想法。我想我的盘子上有足够的碎片,但这对我感兴趣。我将来会循环回到这个。

deploy.psdeploy.ps1.

经过一点审查,这看起来它允许构建系统将模块部署到PS库。 psdeploy它运行时,查找此文件以获取有关要部署和如何运行的说明。

if($ENV:BHProjectName -and $ENV:BHProjectName.Count -eq 1)
{
    Deploy Module {
        By PSGalleryModule {
            FromSource $ENV:BHProjectName
            To PSGallery
            WithOptions @{
                ApiKey = $ENV:NugetApiKey
            }
        }
    }
}

I don’t think I need to even change anything in this file. I like the way it looks and what it does. That $ENV:NugetApiKey is my API key for the PS Gallery. That was securely created and added to the appvoyer.yml.

纠演试验

我也需要我的第一个纠缠测试。当我运行构建脚本时,它取决于我传递测试。我会做一些简单的事情,就像验证PowerShell大多数有效代码。这是我的起动器 tests\Project.Tests.ps1

$projectRoot = Resolve-Path "$PSScriptRoot\.."
$moduleRoot = Split-Path (Resolve-Path "$projectRoot\*\*.psm1")
$moduleName = Split-Path $moduleRoot -Leaf

Describe "General project validation: $moduleName" {

    $scripts = Get-ChildItem $projectRoot -Include *.ps1,*.psm1,*.psd1 -Recurse

    # TestCases are splatted to the script so we need hashtables
    $testCase = $scripts | Foreach-Object{@{file=$_}}         
    It "Script <file> should be valid powershell" -TestCases $testCase {
        param($file)

        $file.fullname | Should Exist

        $contents = Get-Content -Path $file.fullname -ErrorAction Stop
        $errors = $null
        $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors)
        $errors.Count | Should Be 0
    }

    It "Module '$moduleName' can import cleanly" {
        {Import-Module (Join-Path $moduleRoot "$moduleName.psm1") -force } | Should Not Throw
    }
}

我在开始时定义了几个变量,我觉得我可能需要。我试图真的很难没有纠结路径或项目名称。我这样做,所以我可以轻松地重用它在其他项目中。

其余的看起来比它更复杂。它首先散步项目中的每个PowerShell文件,并尝试授权它。如果发生故障,则某个语法错误。然后它执行模块的导入。这只是一些拼写错误的快速安全网,并为任何项目进行了良好的启动试验。

如果我们现在运行这个,只有模块导入应该失败,因为我们还没有构建该部分。

模块清单

毕竟,我仍然没有模块清单。我本可以开始清单,但在所有这些其他文件中丢弃相当快。我认为已经有很多一般模块信息可用。

这是一个快速模块清单。

$module = @{
    Author = 'Kevin Marquette' 
    Description = 'GraphViz helper module for generating graph images' 
    RootModule = 'PSGraph.psm1'
    Path = 'PSGraph.psd1'
    ModuleVersion = '0.0.1'
}
New-ModuleManifest @module

我从模块根文件夹中运行它来构建实际清单。我正在考虑以后将其移动到构建过程中。

PSM1模块

现在我们需要为模块创建PSM1文件。通常,您将在.psm1文件中放置所有模块代码。从技术上讲,你需要的只是一个.psm1文件来拥有一个模块。因为我喜欢将我的代码分解为较小的文件,所以PSM1将用于在运行时加载其他文件。

这是我长期所做的事情,并且已经有一个很好的模块加载器。

Write-Verbose "Importing Functions"

# Import everything in these folders
foreach($folder in @('private', 'public', 'classes'))
{
    
    $root = Join-Path -Path $PSScriptRoot -ChildPath $folder
    if(Test-Path -Path $root)
    {
        Write-Verbose "processing folder $root"
        $files = Get-ChildItem -Path $root -Filter *.ps1

        # dot source each file
        $files | where-Object{ $_.name -NotLike '*.Tests.ps1'} | 
            ForEach-Object{Write-Verbose $_.name; . $_.FullName}
    }
}

Export-ModuleMember -Function (Get-ChildItem -Path "$PSScriptRoot\public\*.ps1").basename

因为我有一个构建过程,我可以结束添加一个构建步骤将所有这些外部文件拉到PSM1文件中发布时间。如果这样做,模块将加载更快。

功能

Now that we have a loader, we need to add a few functions. I created a wrapper for the primary executable in GraphViz. Go check out the project if you want to see how I did that one. I called it Invoke-GraphViz for now and placed it into the public folder Psgraph.. \public\invoke-graphviz.ps1.

Normally I would add this to the 功能 ToExport in the module manifest by hand. After everything is up and running, the build script should take care of that. That is one of the advantages of having a build process.

源码控制

我一直在本地省份源。这已经是我工作流程的一部分。

#Add all new files
git add -A

#Commit changes
git commit -a -m 'updated project tests'

有时我已经在shell上,只是在命令行上提交。我也做了很多工作 vscode. 它具有很大的Git集成。我在vscode中轻松地确实95%的git提交。

我还在我的项目中使用一些基本分支,但这不是我们目前需要潜入的东西。

测试和出版

随着我们所有的组件到位并具有我们实际使用的功能,我需要查看所有工作的碎片。

纠缠试验

May as well start with our Pester tests. If I run the Tests\Project.Tests.ps1 that we created earlier, we should see everything pass. We just have basic test coverage at this point.

PS:> Invoke-Pester

Describing General project validation: PSGraph
[+] Script C:\workspace\PSGraph\PSGraph\Public\Install-GraphViz.ps1 should be valid powershell 112ms
[+] Script C:\workspace\PSGraph\PSGraph\Public\Invoke-GraphViz.ps1 should be valid powershell 66ms
[+] Script C:\workspace\PSGraph\PSGraph\PSGraph.psd1 should be valid powershell 69ms
[+] Script C:\workspace\PSGraph\PSGraph\PSGraph.psm1 should be valid powershell 51ms
[+] Script C:\workspace\PSGraph\Tests\Project.Tests.ps1 should be valid powershell 53ms
[+] Script C:\workspace\PSGraph\build.ps1 should be valid powershell 55ms
[+] Script C:\workspace\PSGraph\psake.ps1 should be valid powershell 58ms
[+] Script C:\workspace\PSGraph\PSGraph.PSDeploy.ps1 should be valid powershell 54ms
[+] Script C:\workspace\PSGraph\requirements.psd1 should be valid powershell 51ms
[+] Module 'PSGraph' can import cleanly 88ms
Tests completed in 662ms
Passed: 10 Failed: 0 Skipped: 0 Pending: 0 Inconclusive: 0

一切都像预期一样奔跑。

第一本地建设

现在我们运行建设。它应该首先从PSGallery安装所有构建相关的依赖项。构建运行但未发布。我收到此消息:

PS:> .\build.ps1

Executing Build
----------------------------------------------------------------------
Executing Deploy
----------------------------------------------------------------------
Skipping deployment: To deploy, ensure that...
        * You are in a known build system (Current: Unknown)
        * You are committing to the master branch (Current: master)
        * Your commit message includes !deploy (Current: Working on the AppVeyor text upload component )

Build Succeeded!

----------------------------------------------------------------------
Build Time Report
----------------------------------------------------------------------
Name   Duration
----   --------
Init   00:00:00.0640305
Test   00:00:01.6234376
Build  00:00:04.3823107
Deploy 00:00:00.0711113
Total: 00:00:06.2215688

我得到一个干净的构建,但没有任何公布的psgallery。我很好默认行为。

First Appveyor Build.

I am ready to see the AppVeyor system in action. I need a commit that contains !deploy. Then we need to push it to github.

PS:> git commit -m '!deploy'
PS:> git push

Counting objects: 23, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (21/21), done.
Writing objects: 100% (23/23), 5.62 KiB | 0 bytes/s, done.
Total 23 (delta 8), reused 0 (delta 0)
remote: Resolving deltas: 100% (8/8), completed with 2 local objects.
To //github.com/KevinMarquette/PSGraph.git
af9aaba..e959142  master -> master

将其提供几分钟,我们应该在Appyeor.com登录时看到在Appveyor.com上排队。一旦开始,我们可以单击它以查看构建输出。

完成后唯一剩下的是检查模块的PSGallery。

PS:> Find-Module PSGraph

Version Name       Repository Description
------- ----       ---------- -----------
0.0.1   PSGraph PSGallery  GraphViz helper module

我想一切都在工作。我喜欢这种情况。

在结束时

这真的很令人兴奋,一切都很好地结合在一起。让我们一再反思这一点。我从一开始就拿了一个项目,它在这篇文章结束时自动发布到PSGallery。我知道我覆盖了很多地面并撇去了许多这些事情的细节。我更想向您展示我的工作流程和方法来弄明地。

这是我第一次触及这些组件中的几个,这一切都变得比我预期的更容易。你没有看到的是我沿途所做的错误。我有错误的东西或不同时间丢失的东西。在一天结束时,他们很容易理清。

我确实不得不给予很大的感谢 ramblingcookiemonster 分享他所有的工作。他在的代码 GitHub. 和各种博客帖子是让我如此轻松这样做。

我有一个模块,但现在我需要做真正的工作。我发布工作职能只是一开始。

指数