Skip to content

Commit 94f6ec1

Browse files
author
JustinGrote
committed
More CI Fixes and Checks, add Powershell Gallery Publish Support
1 parent 8439274 commit 94f6ec1

4 files changed

Lines changed: 132 additions & 34 deletions

File tree

.vscode/tasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
"-ExecutionPolicy",
101101
"Bypass",
102102
"-Command",
103-
"Invoke-Build Build"
103+
"Invoke-Build"
104104
],
105105
"problemMatcher": "$pester"
106106
},

GitVersion.yml

56 Bytes
Binary file not shown.

PSModule.build.ps1

Lines changed: 127 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,30 @@
44
#Run by changing to the project root directory and run ./Invoke-Build.ps1
55
#Uses a master-always-deploys strategy and semantic versioning - http://nvie.com/posts/a-successful-git-branching-model/
66

7-
#This variable specifies what modules to bootstrap for the build
8-
#It is recommended to only bootstrap BuildHelpers and PSDepend, and use PSDepend for remaining prereqs
9-
$BuildHelperModules = "BuildHelpers", "PSDepend", "Pester", "powershell-yaml", "Microsoft.Powershell.Archive", "PSScriptAnalyzer"
7+
param (
8+
#Skip publishing to various destinations (Appveyor,Github,PowershellGallery,etc.)
9+
[Switch]$SkipPublish,
10+
#Force deployment step even if we are not in master. If you are following GitFlow or GitHubFlow you should never need to do this.
11+
[Switch]$ForceDeploy,
12+
#Show detailed environment variables
13+
[Switch]$ShowEnvironmentVariables,
14+
#Powershell modules required for the build process
15+
[String[]]$BuildHelperModules = @("BuildHelpers","Pester","powershell-yaml","Microsoft.Powershell.Archive","PSScriptAnalyzer"),
16+
#Which build files/folders should be excluded from packaging
17+
[String[]]$BuildFilesToExclude = @("Build","BuildOutput","Tests",".git*","appveyor.yml","gitversion.yml","*.build.ps1",".vscode",".placeholder"),
18+
#NuGet API Key for Powershell Gallery Deployment. Defaults to environment variable of the same name
19+
$NuGetAPIKey = $env:NuGetAPIKey,
20+
#GitHub API Key for Github Releases. Defaults to environment variable of the same name
21+
$GitHubAPIKey = $env:GitHubAPIKey
22+
)
1023

1124
#Initialize Build Environment
1225
Enter-Build {
26+
#Initialize Script-scope variables
27+
New-Variable ArtifactPaths
28+
New-Variable ProjectVersion
29+
New-Variable ProjectBuildPath
30+
1331
$lines = '----------------------------------------------------------------'
1432
function Write-VerboseHeader ([String]$Message) {
1533
#Simple function to add lines around a header
@@ -21,7 +39,7 @@ Enter-Build {
2139

2240
#Detect if we are in a continuous integration environment (Appveyor, etc.) or otherwise running noninteractively
2341
if ($ENV:CI -or ([Environment]::GetCommandLineArgs() -like '-noni*')) {
24-
write-build Green 'Detected a Noninteractive or CI environment, disabling prompt confirmations'
42+
write-build Green 'Build Initialization: Detected a Noninteractive or CI environment, disabling prompt confirmations'
2543
$SCRIPT:CI = $true
2644
$ConfirmPreference = 'None'
2745
$ProgressPreference = "SilentlyContinue"
@@ -34,7 +52,7 @@ Enter-Build {
3452

3553
foreach ($BuildHelperModuleItem in $BuildHelperModules) {
3654
if (-not (Get-module $BuildHelperModuleItem -listavailable)) {
37-
write-verbose "Installing $BuildHelperModuleItem from Powershell Gallery to your currentuser module directory"
55+
write-verbose "Build Initialization: Installing $BuildHelperModuleItem from Powershell Gallery to your currentuser module directory"
3856
if ($PSVersionTable.PSVersion.Major -lt 5) {
3957
write-verboseheader "Bootstrapping Powershell Module: $BuildHelperModuleItem"
4058
Invoke-Command -ArgumentList @(, $BuildHelperModules) -ScriptBlock ([scriptblock]::Create((new-object net.webclient).DownloadString('https://git.io/PSModBootstrap')))
@@ -70,23 +88,20 @@ Enter-Build {
7088
write-build Green "Current Branch Name: $BranchName"
7189

7290
if ( ($VerbosePreference -ne 'SilentlyContinue') -or ($CI -and ($BranchName -ne 'master')) ) {
73-
write-build Green "Verbose Build Logging Enabled"
91+
write-build Green "Build Initialization: Verbose Build Logging Enabled"
7492
$SCRIPT:VerbosePreference = "Continue"
7593
$PassThruParams.Verbose = $true
7694
}
7795

96+
7897
write-verboseheader "Build Environment Prepared! Environment Information:"
7998
Get-BuildEnvironment | format-list | out-string | write-verbose
99+
if ($ShowEnvironmentVariables) {
100+
write-verboseheader "Current Environment Variables"
101+
get-childitem env: | out-string | write-verbose
80102

81-
write-verboseheader "Current Environment Variables"
82-
get-childitem env: | out-string | write-verbose
83-
84-
write-verboseheader "Powershell Variables"
85-
Get-Variable | select-object name, value, visibility | format-table -autosize | out-string | write-verbose
86-
87-
if ($ENV:APPVEYOR) {
88-
write-verboseheader "Detected that we are running in Appveyor! Appveyor Environment Info:"
89-
get-item env:/Appveyor* | out-string | write-verbose
103+
write-verboseheader "Powershell Variables"
104+
Get-Variable | select-object name, value, visibility | format-table -autosize | out-string | write-verbose
90105
}
91106

92107
#Register Nuget
@@ -143,15 +158,16 @@ task Version {
143158
#Fetch GitVersion
144159
#TODO: Use Nuget.exe to fetch to make this v3/v4 compatible
145160
$GitVersionCMDPackageName = "gitversion.commandline"
146-
if (!(Get-Package $GitVersionCMDPackageName -erroraction SilentlyContinue)) {
161+
$GitVersionCMDPackage = Get-Package $GitVersionCMDPackageName -erroraction SilentlyContinue
162+
if (!($GitVersionCMDPackage)) {
147163
write-verbose "Package $GitVersionCMDPackageName Not Found Locally, Installing..."
148-
write-verboseheader "Nuget.Org Package Source Info for fetching Gitversion"
164+
write-verboseheader "Nuget.Org Package Source Info for fetching GitVersion"
149165
Get-PackageSource | Format-Table | out-string | write-verbose
150166

151167
#Fetch GitVersion
152-
Install-Package $GitVersionCMDPackageName -scope currentuser -source 'nuget.org' -force @PassThruParams | Out-Null
168+
$GitVersionCMDPackage = Install-Package $GitVersionCMDPackageName -scope currentuser -source 'nuget.org' -force @PassThruParams
153169
}
154-
$GitVersionEXE = ((get-package $GitVersionCMDPackageName).source | split-path -Parent) + "\tools\GitVersion.exe"
170+
$GitVersionEXE = (($GitVersionCMDPackage).source | split-path -Parent) + "\tools\GitVersion.exe"
155171

156172
#Does this project have a module manifest? Use that as the Gitversion starting point (will use this by default unless project is tagged higher)
157173
#Uses Powershell-YAML module to read/write the GitVersion.yaml config file
@@ -172,7 +188,8 @@ task Version {
172188

173189
#Calcuate the GitVersion
174190
write-verbose "Executing GitVersion to determine version info"
175-
$GitVersionOutput = & $GitVersionEXE $buildRoot
191+
$GitVersionCommand = "$GitVersionEXE $buildRoot"
192+
$GitVersionOutput = Invoke-BuildExec { & $GitVersionEXE $buildRoot}
176193

177194
#Since GitVersion doesn't return error exit codes, we look for error text in the output in the output
178195
if ($GitVersionOutput -match '^[ERROR|INFO] \[') {throw "An error occured when running GitVersion.exe $buildRoot"}
@@ -196,8 +213,8 @@ task Version {
196213

197214

198215
$SCRIPT:ProjectSemVersion = $($GitVersionInfo.fullsemver)
199-
write-build Green "Using Project Version: $ProjectBuildVersion"
200-
write-build Green "Using Project Version (Extended): $($GitVersionInfo.fullsemver)"
216+
write-build Green "Task $($task.name)`: Using Project Version: $ProjectBuildVersion"
217+
write-build Green "Task $($task.name)`: Using Project Version (Extended): $($GitVersionInfo.fullsemver)"
201218
}
202219

203220
#Copy all powershell module "artifacts" to Build Directory
@@ -208,7 +225,7 @@ task CopyFilesToBuildDir {
208225
#The file or file paths to copy, excluding the powershell psm1 and psd1 module and manifest files which will be autodetected
209226
#TODO: Move this somewhere higher in the hierarchy into a settings file, or rather go the "exclude" route
210227
$FilesToCopy = "lib","Public","Private","Types","LICENSE","README.md","$($Env:BHProjectName).psm1","$($Env:BHProjectName).psd1"
211-
copy-item -Recurse -Path $FilesToCopy -Destination $ProjectBuildPath @PassThruParams
228+
copy-item -Recurse -Path $buildRoot\* -Exclude $BuildFilesToExclude -Destination $ProjectBuildPath @PassThruParams
212229
}
213230

214231
#Update the Metadata of the Module with the latest Version
@@ -218,7 +235,7 @@ task UpdateMetadata CopyFilesToBuildDir,Version,{
218235
# TODO: Find a cleaner solution, like update Set-ModuleFunctions to use a separate runspace or include a market to know we are in ModuleFunctions so when loading the module we can copy the assemblies to temp files first
219236
$ProjectBuildManifest = ($ProjectBuildPath + "\" + (split-path $env:BHPSModuleManifest -leaf))
220237
$tempModuleDir = [System.IO.Path]::GetTempFileName()
221-
Remove-Item $tempModuleDir
238+
Remove-Item $tempModuleDir -verbose:$false
222239
New-Item -Type Directory $tempModuleDir | out-null
223240
copy-item -recurse $ProjectBuildPath/* $tempModuleDir
224241

@@ -232,7 +249,7 @@ task UpdateMetadata CopyFilesToBuildDir,Version,{
232249

233250
# Are we in the master or develop/development branch? Bump the version based on the powershell gallery if so, otherwise add a build tag
234251
if ($BranchName -match '^(master|dev(elop)?(ment)?)$') {
235-
write-build Green "In Master/Develop branch, adding Tag Version $ProjectBuildVersion to this build"
252+
write-build Green "Task $($task.name)`: In Master/Develop branch, adding Tag Version $ProjectBuildVersion to this build"
236253
$Script:ProjectVersion = $ProjectBuildVersion
237254
if (-not (git tag -l $ProjectBuildVersion)) {
238255
git tag "$ProjectBuildVersion"
@@ -250,7 +267,7 @@ task UpdateMetadata CopyFilesToBuildDir,Version,{
250267
}
251268
#>
252269
} else {
253-
write-build Green "Not in Master/Develop branch, marking this as a feature prelease build"
270+
write-build Green "Task $($task.name)`: Not in Master/Develop branch, marking this as a feature prelease build"
254271
$Script:ProjectVersion = $ProjectSemVersion
255272
#Set an email address for tag commit to work if it isn't already present
256273
if (-not (git config user.email)) {
@@ -323,20 +340,101 @@ task Pester {
323340
}
324341

325342
task Package Version,{
343+
326344
$ZipArchivePath = (join-path $env:BHBuildOutput "$env:BHProjectName-$ProjectVersion.zip")
327-
write-build green "Writing Finished Module to $ZipArchivePath"
345+
write-build green "Task $($task.name)`: Writing Finished Module to $ZipArchivePath"
328346
#Package the Powershell Module
329347
Compress-Archive -Path $ProjectBuildPath -DestinationPath $ZipArchivePath -Force @PassThruParams
330348

349+
$Artifacts += $ZipArchivePath
331350
#If we are in Appveyor, push completed zip to Appveyor Artifact
332351
if ($env:APPVEYOR) {
333-
write-build Green "Detected Appveyor, pushing Powershell Module archive to Artifacts"
352+
write-build Green "Task $($task.name)`: Detected Appveyor, pushing Powershell Module archive to Artifacts"
334353
Push-AppveyorArtifact $ZipArchivePath
335354
}
336355
}
337356

357+
task PreDeploymentChecks {
358+
#Do not proceed if the most recent Pester test is not passing.
359+
$CurrentErrorActionPreference = $ErrorActionPreference
360+
try {
361+
$ErrorActionPreference = "Stop"
362+
$MostRecentPesterTestResult = [xml]((Get-Content -raw (get-item "$ENV:BHBuildOutput/*-TestResults*.xml" | sort lastwritetime | select -last 1)))
363+
$MostRecentPesterTestResult = $MostRecentPesterTestResult."test-results"
364+
if (
365+
$MostRecentPesterTestResult -isnot [System.XML.XMLElement] -or
366+
$MostRecentPesterTestResult.errors -gt 0 -or
367+
$MostRecentPesterTestResult.failures -gt 0
368+
) {throw "Fail!"}
369+
} catch {
370+
throw "Unable to detect a clean passing Pester Test xml in the $env:BHBuildOutput directory. Ensure you were successful in the Build and Test phases first."
371+
}
372+
finally {
373+
$ErrorActionPreference = $CurrentErrorActionPreference
374+
}
375+
376+
if ((-not $env:BHBranchName -eq 'Master') -or ($ForceDeploy -ne $true)) {
377+
write-build Magenta "Task $($task.name)`: We are not in master branch, skipping publish. If you wish to deploy anyways such as for testing, run {InvokeBuild Deploy -ForceDeploy:$true}"
378+
$script:SkipPublish=$true
379+
} else {
380+
if (-not (Get-Item $ProjectBuildPath/*.psd1 -erroraction silentlycontinue)) {throw "No Powershell Module Found in $ProjectBuildPath. Skipping deployment. Did you remember to build it first with {Invoke-Build Build}?"}
381+
}
382+
}
383+
384+
task PublishPSGallery {
385+
if (-not $SkipPublish) {
386+
if ($AppVeyor -and -not $NuGetAPIKey) {
387+
write-build DarkYellow "Couldn't find NuGetAPIKey in the Appveyor secure environment variables. Did you save your NuGet/Powershell Gallery API key as an Appveyor Secure Variable? https://docs.microsoft.com/en-us/powershell/gallery/psgallery/creating-and-publishing-an-item and https://www.appveyor.com/docs/build-configuration/"
388+
$SkipPublish = $true
389+
}
390+
if (-not $env:NuGetAPIKey) {
391+
#TODO: Add Windows Credential Store support and some kind of Linux secure storage or caching option
392+
write-build DarkYellow '$env:NuGetAPIKey was not found as an environment variable. Please specify it or use {Invoke-Build Deploy -NuGetAPIKey "MyAPIKeyString"}. Have you registered for a Powershell Gallery API key yet? https://docs.microsoft.com/en-us/powershell/gallery/psgallery/creating-and-publishing-an-item'
393+
$SkipPublish = $true
394+
}
395+
}
396+
397+
if ($SkipPublish) {
398+
Write-Build Magenta "Task $($task.name)`: Skipping Powershell Gallery Publish"
399+
} else {
400+
401+
$publishParams = @{
402+
Path = $ProjectBuildPath
403+
NuGetApiKey = $NuGetAPIKey
404+
Repository = 'PSGallery'
405+
Force = $true
406+
ErrorAction = 'Stop'
407+
Confirm = $false
408+
}
409+
#TODO: Add Prerelease Logic when message commit says "!prerelease"
410+
Publish-Module @publishParams @PassThruParams
411+
}
412+
}
413+
414+
task PublishGitHubRelease Package,{
415+
416+
#TODO: Add Prerelease Logic when message commit says "!prerelease"
417+
if (-not $SkipPublish) {
418+
if ($AppVeyor -and -not $GitHubAPIKey) {
419+
write-build DarkYellow "Task PublishGitHubRelease: Couldn't find GitHubAPIKey in the Appveyor secure environment variables. Did you save your Github API key as an Appveyor Secure Variable? https://docs.microsoft.com/en-us/powershell/gallery/psgallery/creating-and-publishing-an-item and https://github.com/settings/tokens"
420+
$SkipPublish = $true
421+
}
422+
if (-not $env:GitHubAPIKey) {
423+
#TODO: Add Windows Credential Store support and some kind of Linux secure storage or caching option
424+
write-build DarkYellow 'Task PublishGitHubRelease: $env:GitHubAPIKey was not found as an environment variable. Please specify it or use {Invoke-Build Deploy -NuGetAPIKey "MyAPIKeyString"}. Have you created a GitHub API key with minimum public_repo scope permissions yet? https://github.com/settings/tokens'
425+
$SkipPublish = $true
426+
}
427+
}
428+
if ($SkipPublish) {
429+
write-build Magenta "Task $($task.name): Skipping Publish to GitHub Releases"
430+
} else {
431+
#TODO: Add GitHubRelease Logic
432+
}
433+
434+
}
435+
338436
#Deploy Supertask
339-
task Deploy Package
437+
task Deploy PreDeploymentChecks,Package,PublishPSGallery,PublishGitHubRelease
340438

341439
#Build SuperTask
342440
task Build Clean,CopyFilesToBuildDir,UpdateMetadata

Tests/00-PowershellModule.Tests.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#requires -module BuildHelpers
2-
if (-not (import-module BuildHelpers -PassThru -erroraction silentlycontinue)) {
2+
if (-not (import-module BuildHelpers -PassThru -verbose:$false -erroraction silentlycontinue)) {
33
install-module buildhelpers -scope currentuser -erroraction stop -force
4-
import-module BuildHelpers -erroraction stop
4+
import-module BuildHelpers -erroraction stop -verbose:$false
55
}
66
Set-BuildEnvironment -force
77
$PSVersion = $PSVersionTable.PSVersion.Major
@@ -23,7 +23,7 @@ Describe 'Powershell Module' {
2323
$TempModuleManifestPath = [System.IO.Path]::GetTempFileName() + '.psd1'
2424
copy-item $ModuleManifestPath $TempModuleManifestPath
2525
$Script:Manifest = Test-ModuleManifest $TempModuleManifestPath
26-
remove-item $TempModuleManifestPath
26+
remove-item $TempModuleManifestPath -verbose:$false
2727
}
2828
}
2929

@@ -58,7 +58,7 @@ Describe 'Powershell Module' {
5858
}
5959
It 'Can be imported as a module successfully' {
6060
Remove-Module $ModuleName -ErrorAction SilentlyContinue
61-
Import-Module $BuildOutputProject -PassThru -OutVariable BuildOutputModule | Should BeOfType System.Management.Automation.PSModuleInfo
61+
Import-Module $BuildOutputProject -PassThru -verbose:$false -OutVariable BuildOutputModule | Should BeOfType System.Management.Automation.PSModuleInfo
6262
$BuildOutputModule.Name | Should Be $ModuleName
6363
}
6464
It 'Is visible in Get-Module' {

0 commit comments

Comments
 (0)