resolve merge conflicts

This commit is contained in:
Kevin Marquette
2018-12-28 14:46:55 -08:00
29 changed files with 1038 additions and 19 deletions

43
.gitignore vendored Normal file
View File

@@ -0,0 +1,43 @@
/output/
/debug*.ps1
/temp*.ps1
# Most of these heavily cannibalized by Travis Drake from https://gist.github.com/kmorcinek/2710267
# C# VS build detritus
/.vs/
/*/[Bb]in/
/*/[Oo]bj/
**/packages/*
# Except this, this needs to be checked in when present
!**/packages/build/
# More C# / Visual Studio detritus
*.suo
*.user
*.sln.docstates
*.psess
*.vsp
*.vspx
project.lock.json
_UpgradeReport_Files/
UpgradeLog*.XML
UpgradeLog*.htm
# SASS Compiler cache
.sass-cache
# Mac OS stuff
.DS_Store*
Icon?
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/

26
.vscode/settings.json vendored
View File

@@ -1,4 +1,28 @@
// Place your settings in this file to overwrite default and user settings. // Place your settings in this file to overwrite default and user settings.
{ {
"powershell.codeFormatting.preset": "Allman" //-------- Editor configuration --------
"editor.insertSpaces": true,
"editor.tabSize": 4,
//-------- Files configuration --------
"files.autoGuessEncoding": false,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"search.exclude": {
"**/Tests/Data*": true
},
//-------- PowerShell configuration --------
"powershell.codeFormatting.alignPropertyValuePairs": true,
"powershell.codeFormatting.preset": "Allman",
"powershell.scriptAnalysis.settingsPath": "./ScriptAnalyzerSettings.psd1",
//-------- Language configuration --------
"[json]": {
"editor.tabSize": 2
},
"[xml]": {
"editor.tabSize": 2
}
} }

95
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,95 @@
// Available variables which can be used inside of strings:
// ${workspaceRoot}: The root folder of the team
// ${file}: The current opened file
// ${relativeFile}: The current opened file relative to workspaceRoot
// ${fileBasename}: The current opened file's basename
// ${fileDirname}: The current opened file's dirname
// ${fileExtname}: The current opened file's extension
// ${cwd}: The current working directory of the spawned process
{
"version": "2.0.0",
"windows": {
"options": {
"shell": {
"executable": "powershell.exe",
"args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command" ]
}
}
},
"linux": {
"options": {
"shell": {
"executable": "/usr/bin/pwsh",
"args": [ "-NoProfile", "-Command" ]
}
}
},
"osx": {
"options": {
"shell": {
"executable": "/usr/local/bin/pwsh",
"args": [ "-NoProfile", "-Command" ]
}
}
},
"tasks": [
{
"label": "Default",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"group": {
"kind": "build",
"isDefault": true
},
"command": "Invoke-Build -Task Default -File './Module.build.ps1'"
},
{
"label": "Analyze",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Analyze -File './Module.build.ps1'"
},
{
"label": "Build",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Build -File './Module.build.ps1'"
},
{
"label": "Clean",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Clean -File './Module.build.ps1'"
},
{
"label": "Helpify",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Helpify -File './Module.build.ps1'"
},
{
"label": "Install",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Install -File './Module.build.ps1'"
},
{
"label": "Test",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Test -File './Module.build.ps1'"
},
{
"label": "Uninstall",
"type": "shell",
"problemMatcher": [ "$msCompile" ],
"command": "Invoke-Build -Task Uninstall -File './Module.build.ps1'"
},
{
"label": "?",
"type": "shell",
"problemMatcher": [],
"command": "Invoke-Build -Task ? -File './Module.build.ps1'"
}
]
}

View File

@@ -0,0 +1,17 @@
task Analyze {
$params = @{
IncludeDefaultRules = $true
Path = $ManifestPath
Settings = "$BuildRoot\ScriptAnalyzerSettings.psd1"
Severity = 'Warning'
}
"Analyzing $ManifestPath..."
$results = Invoke-ScriptAnalyzer @params
if ($results)
{
'One or more PSScriptAnalyzer errors/warnings were found.'
'Please investigate or add the required SuppressMessage attribute.'
$results | Format-Table -AutoSize
}
}

View File

@@ -0,0 +1,18 @@
taskx BuildManifest @{
Inputs = (Get-ChildItem -Path $Source -Recurse -File)
Outputs = $ManifestPath
Jobs = {
"Updating [$ManifestPath]..."
Copy-Item -Path "$Source\$ModuleName.psd1" -Destination $ManifestPath
$functions = Get-ChildItem -Path "$ModuleName\Public\*.ps1" -ErrorAction 'Ignore' |
Where-Object 'Name' -notmatch 'Tests'
if ($functions)
{
'Setting FunctionsToExport...'
Set-ModuleFunctions -Name $ManifestPath -FunctionsToExport $functions.BaseName
}
}
}

View File

@@ -0,0 +1,214 @@
# namespaces for Move-Statement
using namespace System.Collections.Generic
using namespace System.IO
using namespace System.Management.Automation
function Import-ClassOrder
{
[cmdletbinding()]
param($cName,$Map)
Write-Verbose "Checking on [$cName]"
if($Map.ContainsKey($cName) -and $Map[$cName].Imported -ne $true)
{
if($Map[$cName].Base)
{
Write-Verbose " Base class [$($Map[$cName].Base)]"
Import-ClassOrder $Map[$cName].Base $Map
}
$cPath = $Map[$cName].Path
Write-Verbose "Dot Sourcing [$cPath]"
$cPath
$Map[$cName].Imported = $true
}
}
# Temporarily added this here to be refactored/replaced by LDModuleBuilder Module
function Move-Statement
{
<#
.SYNOPSIS
Moves statements containing a specified token to the specified index in a file.
.DESCRIPTION
Move-Statement moves statements containing a specified token, to the specified index
in a file. This can be used when building a module to move any using directives and
#Requires statements to the top of a file.
.PARAMETER Path
Specifies the path to an item to get its contents.
.PARAMETER Type
Specifies the type of tokens to examine. Accepted values include "Comment" and "Keyword".
.PARAMETER Token
Specifies the contents to filter on when examining a supplied token.
.PARAMETER Index
Specifies the line to move a statement to. Each line in an item has a corresponding
index, starting from 0.
.EXAMPLE
Move-Statement -Path $Path -Type 'Comment', 'Keyword' -Token '#Requires', 'using' -Index 0
Moves any using directives or #Requires statements to the top of a file.
.NOTES
Copy/Paste from LDModuleBuilder
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory,
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateScript({ Test-Path -Path $PSItem })]
[string] $Path,
[Parameter(Position = 1,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Comment', 'Keyword')]
[string[]] $Type = ('Comment', 'Keyword'),
[Parameter(Position = 2,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[string[]] $Token = ('#Requires', 'using'),
[Parameter(Position = 3,
ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[int] $Index = 0
)
process
{
try
{
$statements = [SortedSet[String]]::new(
[StringComparer]::InvariantCultureIgnoreCase
)
Write-Verbose -Message "Reading content from $Path..."
$content = [List[String]] ([File]::ReadLines($Path))
Write-Verbose -Message "Tokenizing content from $Path..."
$tokens = [PSParser]::Tokenize($content, [ref] $null)
$match = $Token -join '|'
Write-Verbose -Message 'Matching tokens...'
Write-Verbose -Message "Type = [$Type]; Token = [$Token]"
$keywords = $tokens.Where({
$PSItem.Type -in $Type -and
$PSItem.Content -imatch "^(?:$match)"
})
if (-not $keywords) {
Write-Verbose -Message 'No matching tokens found! Returning...'
return
}
$offset = 1
foreach ($keyword in $keywords)
{
$line = $keyword.StartLine - $offset
Write-Verbose -Message "Moving [$($content[$line])] to Index [$Index]..."
$null = $statements.Add($content[$line]),
$content.RemoveAt($line)
$offset++
}
[string[]] $comments, [string[]] $statements = $statements.Where({
$PSItem -match '^#'
}, 'Split')
foreach ($item in ($statements, $comments))
{
$content.Insert($Index, '')
$content.InsertRange($Index, $item)
}
if ($PSCmdlet.ShouldProcess($Path, $MyInvocation.MyCommand.Name))
{
Write-Verbose -Message "Writing content to $Path..."
[File]::WriteAllLines($Path, $content)
}
}
catch
{
$PSCmdlet.ThrowTerminatingError($PSItem)
}
}
}
taskx BuildModule @{
Inputs = (Get-ChildItem -Path $Source -Recurse -Filter *.ps1)
Outputs = $ModulePath
Jobs = {
$sb = [Text.StringBuilder]::new()
$null = $sb.AppendLine('$Script:PSModuleRoot = $PSScriptRoot')
# Class importer
$root = Join-Path -Path $source -ChildPath 'Classes'
"Load classes from [$root]"
$classFiles = Get-ChildItem -Path $root -Filter '*.ps1' -Recurse |
Where-Object Name -notlike '*.Tests.ps1'
$classes = @{}
foreach($file in $classFiles)
{
$name = $file.BaseName
$classes[$name] = @{
Name = $name
Path = $file.FullName
}
$data = Get-Content $file.fullname
foreach($line in $data)
{
if($line -match "\s+($Name)\s*(:|requires)\s*(?<baseclass>\w*)")
{
$classes[$name].Base = $Matches.baseclass
}
}
}
$importOrder = foreach($className in $classes.Keys)
{
Import-ClassOrder $className $classes
}
foreach($class in $importOrder)
{
"Importing [$class]..."
$null = $sb.AppendLine("# .$class")
$null = $sb.AppendLine([IO.File]::ReadAllText($class))
}
foreach ($folder in ($Folders -ne 'Classes'))
{
if (Test-Path -Path "$Source\$folder")
{
$null = $sb.AppendLine("# Importing from [$Source\$folder]")
$files = Get-ChildItem -Path "$Source\$folder\*.ps1" |
Where-Object 'Name' -notlike '*.Tests.ps1'
foreach ($file in $files)
{
$name = $file.Fullname.Replace($buildroot, '')
"Importing [$($file.FullName)]..."
$null = $sb.AppendLine("# .$name")
$null = $sb.AppendLine([IO.File]::ReadAllText($file.FullName))
}
}
}
"Creating Module [$ModulePath]..."
$null = New-Item -Path (Split-path $ModulePath) -ItemType Directory -ErrorAction SilentlyContinue -Force
Set-Content -Path $ModulePath -Value $sb.ToString() -Encoding 'UTF8'
'Moving "#Requires" statements and "using" directives...'
Move-Statement -Path $ModulePath -Type 'Comment', 'Keyword' -Token '#Requires', 'using' -Index 0
}
}

12
BuildTasks/Clean.Task.ps1 Normal file
View File

@@ -0,0 +1,12 @@
task Clean {
if (Test-Path $Output)
{
"Cleaning Output files in [$Output]..."
$null = Get-ChildItem -Path $Output -File -Recurse |
Remove-Item -Force -ErrorAction 'Ignore'
"Cleaning Output directories in [$Output]..."
$null = Get-ChildItem -Path $Output -Directory -Recurse |
Remove-Item -Recurse -Force -ErrorAction 'Ignore'
}
}

View File

@@ -0,0 +1,14 @@
taskx Compile @{
If = (Get-ChildItem -Path $BuildRoot -Include *.csproj -Recurse)
Inputs = {
Get-ChildItem $BuildRoot -Recurse -File -Include *.cs
}
Outputs = "$Destination\bin\$ModuleName.dll"
Jobs = {
# This build command requires .Net Core
"Building Module"
$csproj = Get-ChildItem -Path $BuildRoot -Include *.csproj -Recurse
$folder = Split-Path $csproj
dotnet build $folder -c Release -o $Destination\bin
}
}

29
BuildTasks/Copy.Task.ps1 Normal file
View File

@@ -0,0 +1,29 @@
task Copy {
"Creating Directory [$Destination]..."
$null = New-Item -ItemType 'Directory' -Path $Destination -ErrorAction 'Ignore'
$files = Get-ChildItem -Path $Source -File |
Where-Object 'Name' -notmatch "$ModuleName\.ps[dm]1"
foreach ($file in $files)
{
'Creating [.{0}]...' -f $file.FullName.Replace($buildroot, '')
Copy-Item -Path $file.FullName -Destination $Destination -Force
}
$directories = Get-ChildItem -Path $Source -Directory |
Where-Object 'Name' -notin $Folders
foreach ($directory in $directories)
{
'Creating [.{0}]...' -f $directory.FullName.Replace($buildroot, '')
Copy-Item -Path $directory.FullName -Destination $Destination -Recurse -Force
}
$license = Join-Path -Path $buildroot -ChildPath 'LICENSE'
if ( Test-Path -Path $license -PathType Leaf )
{
Copy-Item -Path $license -Destination $Destination
}
}

View File

@@ -0,0 +1,23 @@
task GenerateHelp {
if (-not(Get-ChildItem -Path $DocsPath -Filter '*.md' -Recurse -ErrorAction 'Ignore'))
{
"No Markdown help files to process. Skipping help file generation..."
return
}
$locales = (Get-ChildItem -Path $DocsPath -Directory).Name
foreach ($locale in $locales)
{
$params = @{
ErrorAction = 'SilentlyContinue'
Force = $true
OutputPath = "$Destination\en-US"
Path = "$DocsPath\en-US"
}
# Generate the module's primary MAML help file.
"Creating new External help for [$ModuleName]..."
$null = New-ExternalHelp @params
}
}

View File

@@ -0,0 +1,41 @@
task GenerateMarkdown {
$module = Import-Module -FullyQualifiedName $ManifestPath -Force -PassThru
try
{
if ($module.ExportedFunctions.Count -eq 0)
{
'No functions have been exported for this module. Skipping Markdown generation...'
return
}
if (Get-ChildItem -Path $DocsPath -Filter '*.md' -Recurse)
{
$items = Get-ChildItem -Path $DocsPath -Directory -Recurse
foreach ($item in $items)
{
"Updating Markdown help in [$($item.BaseName)]..."
$null = Update-MarkdownHelp -Path $item.FullName -AlphabeticParamsOrder
}
}
$params = @{
AlphabeticParamsOrder = $true
ErrorAction = 'SilentlyContinue'
Locale = 'en-US'
Module = $ModuleName
OutputFolder = "$DocsPath\en-US"
WithModulePage = $true
}
# ErrorAction is set to SilentlyContinue so this
# command will not overwrite an existing Markdown file.
"Creating new Markdown help for [$ModuleName]..."
$null = New-MarkdownHelp @params
}
finally
{
Remove-Module -Name $ModuleName -Force
}
}

View File

@@ -0,0 +1,4 @@
task ImportDevModule {
ImportModule -Path "$Source\$ModuleName.psd1" -Force
}

View File

@@ -0,0 +1,33 @@
function ImportModule
{
param(
[string]$path,
[switch]$PassThru
)
if (-not(Test-Path -Path $path))
{
"Cannot find [$path]."
Write-Error -Message "Could not find module manifest [$path]"
}
else
{
$file = Get-Item $path
$name = $file.BaseName
$loaded = Get-Module -Name $name -All -ErrorAction Ignore
if ($loaded)
{
"Unloading Module [$name] from a previous import..."
$loaded | Remove-Module -Force
}
"Importing Module [$name] from [$($file.fullname)]..."
Import-Module -Name $file.fullname -Force -PassThru:$PassThru
}
}
task ImportModule {
ImportModule -Path $ManifestPath
}

View File

@@ -0,0 +1,21 @@
task Install Uninstall, {
$version = [version] (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion')
$path = $env:PSModulePath.Split(';').Where({
$_ -like 'C:\Users\*'
}, 'First', 1)
if ($path -and (Test-Path -Path $path))
{
"Using [$path] as base path..."
$path = Join-Path -Path $path -ChildPath $ModuleName
$path = Join-Path -Path $path -ChildPath $version
"Creating directory at [$path]..."
New-Item -Path $path -ItemType 'Directory' -Force -ErrorAction 'Ignore'
"Copying items from [$Destination] to [$path]..."
Copy-Item -Path "$Destination\*" -Destination $path -Recurse -Force
}
}

View File

@@ -0,0 +1,31 @@
Write-Verbose "Initializing build variables" -Verbose
Write-Verbose " Existing BuildRoot [$BuildRoot]" -Verbose
$Script:DocsPath = Join-Path -Path $BuildRoot -ChildPath 'Docs'
Write-Verbose " DocsPath [$DocsPath]" -Verbose
$Script:Output = Join-Path -Path $BuildRoot -ChildPath 'Output'
Write-Verbose " Output [$Output]" -Verbose
$Script:Source = Join-Path -Path $BuildRoot -ChildPath $ModuleName
Write-Verbose " Source [$Source]" -Verbose
$Script:Destination = Join-Path -Path $Output -ChildPath $ModuleName
Write-Verbose " Destination [$Destination]" -Verbose
$Script:ManifestPath = Join-Path -Path $Destination -ChildPath "$ModuleName.psd1"
Write-Verbose " ManifestPath [$ManifestPath]" -Verbose
$Script:ModulePath = Join-Path -Path $Destination -ChildPath "$ModuleName.psm1"
Write-Verbose " ModulePath [$ModulePath]" -Verbose
$Script:Folders = 'Classes', 'Includes', 'Internal', 'Private', 'Public', 'Resources'
Write-Verbose " Folders [$Folders]" -Verbose
$Script:TestFile = "$BuildRoot\Output\TestResults_PS$PSVersion`_$TimeStamp.xml"
Write-Verbose " TestFile [$TestFile]" -Verbose
$Script:PSRepository = 'PSGallery'
Write-Verbose " PSRepository [$TestFile]" -Verbose
function taskx($Name, $Parameters) { task $Name @Parameters -Source $MyInvocation }

View File

@@ -0,0 +1,34 @@
task Pester {
$requiredPercent = $Script:CodeCoveragePercent
$params = @{
OutputFile = $testFile
OutputFormat = 'NUnitXml'
PassThru = $true
Path = 'Tests'
Show = 'Failed', 'Fails', 'Summary'
Tag = 'Build'
}
if($requiredPercent -gt 0.00)
{
$params['CodeCoverage'] = 'Output\*\*.psm1'
$params['CodeCoverageOutputFile'] = 'Output\codecoverage.xml'
}
$results = Invoke-Pester @params
if ($results.FailedCount -gt 0)
{
Write-Error -Message "Failed [$($results.FailedCount)] Pester tests."
}
if($results.codecoverage.NumberOfCommandsAnalyzed -gt 0)
{
$codeCoverage = $results.codecoverage.NumberOfCommandsExecuted / $results.codecoverage.NumberOfCommandsAnalyzed
if($codeCoverage -lt $requiredPercent)
{
Write-Error ("Failed Code Coverage [{0:P}] below {1:P}" -f $codeCoverage,$requiredPercent)
}
}
}

View File

@@ -0,0 +1,31 @@
task PublishModule {
if ( $ENV:BHBuildSystem -ne 'Unknown' -and
$ENV:BHBranchName -eq "master" -and
-not [string]::IsNullOrWhiteSpace($ENV:nugetapikey))
{
$publishModuleSplat = @{
Path = $Destination
NuGetApiKey = $ENV:nugetapikey
Verbose = $true
Force = $true
Repository = $PSRepository
ErrorAction = 'Stop'
}
"Files in module output:"
Get-ChildItem $Destination -Recurse -File |
Select-Object -Expand FullName
"Publishing [$Destination] to [$PSRepository]"
Publish-Module @publishModuleSplat
}
else
{
"Skipping deployment: To deploy, ensure that...`n" +
"`t* You are in a known build system (Current: $ENV:BHBuildSystem)`n" +
"`t* You are committing to the master branch (Current: $ENV:BHBranchName) `n" +
"`t* The repository APIKey is defined in `$ENV:nugetapikey (Current: $(![string]::IsNullOrWhiteSpace($ENV:nugetapikey))) `n" +
"`t* This is not a pull request"
}
}

View File

@@ -0,0 +1,7 @@
task PublishVersion {
[version] $sourceVersion = (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion')
"##vso[build.updatebuildnumber]$sourceVersion"
# Do the same for appveyor
# https://www.appveyor.com/docs/build-worker-api/#update-build-details
}

View File

@@ -0,0 +1,125 @@
function GetModulePublicInterfaceMap
{
param($Path)
$module = ImportModule -Path $Path -PassThru
$exportedCommands = @(
$module.ExportedFunctions.values
$module.ExportedCmdlets.values
$module.ExportedAliases.values
)
foreach($command in $exportedCommands)
{
foreach ($parameter in $command.Parameters.Keys)
{
if($false -eq $command.Parameters[$parameter].IsDynamic)
{
'{0}:{1}' -f $command.Name, $command.Parameters[$parameter].Name
foreach ($alias in $command.Parameters[$parameter].Aliases)
{
'{0}:{1}' -f $command.Name, $alias
}
}
}
}
}
task SetVersion {
$version = [version]"0.1.0"
$publishedModule = $null
$bumpVersionType = 'Patch'
$versionStamp = (git rev-parse origin/master) + (git rev-parse head)
"Load current version"
[version] $sourceVersion = (Get-Metadata -Path $manifestPath -PropertyName 'ModuleVersion')
" Source version [$sourceVersion]"
$downloadFolder = Join-Path -Path $output downloads
$null = New-Item -ItemType Directory -Path $downloadFolder -Force -ErrorAction Ignore
$versionFile = Join-Path $downloadFolder versionfile
if(Test-Path $versionFile)
{
$versionFileData = Get-Content $versionFile -raw
if($versionFileData -eq $versionStamp)
{
continue
}
}
"Checking for published version"
$publishedModule = Find-Module -Name $ModuleName -ErrorAction 'Ignore' |
Sort-Object -Property {[version]$_.Version} -Descending |
Select -First 1
if($null -ne $publishedModule)
{
[version] $publishedVersion = $publishedModule.Version
" Published version [$publishedVersion]"
$version = $publishedVersion
"Downloading published module to check for breaking changes"
$publishedModule | Save-Module -Path $downloadFolder
[System.Collections.Generic.HashSet[string]] $publishedInterface = @(GetModulePublicInterfaceMap -Path (Join-Path $downloadFolder $ModuleName))
[System.Collections.Generic.HashSet[string]] $buildInterface = @(GetModulePublicInterfaceMap -Path $ManifestPath)
if (-not $publishedInterface.IsSubsetOf($buildInterface))
{
$bumpVersionType = 'Major'
}
elseif ($publishedInterface.count -ne $buildInterface.count)
{
$bumpVersionType = 'Minor'
}
}
if ($version -lt ([version] '1.0.0'))
{
"Module is still in beta; don't bump major version."
if ($bumpVersionType -eq 'Major')
{
$bumpVersionType = 'Minor'
}
else
{
$bumpVersionType = 'Patch'
}
}
" Steping version [$bumpVersionType]"
$version = [version] (Step-Version -Version $version -Type $bumpVersionType)
" Comparing to source version [$sourceVersion]"
if($sourceVersion -gt $version)
{
" Using existing version"
$version = $sourceVersion
}
if ( -not [string]::IsNullOrEmpty( $env:Build_BuildID ) )
{
$build = $env:Build_BuildID
$version = [version]::new($version.Major, $version.Minor, $version.Build, $build)
}
elseif ( -not [string]::IsNullOrEmpty( $env:APPVEYOR_BUILD_ID ) )
{
$build = $env:APPVEYOR_BUILD_ID
$version = [version]::new($version.Major, $version.Minor, $version.Build, $build)
}
" Setting version [$version]"
Update-Metadata -Path $ManifestPath -PropertyName 'ModuleVersion' -Value $version
(Get-Content -Path $ManifestPath -Raw -Encoding UTF8) |
ForEach-Object {$_.TrimEnd()} |
Set-Content -Path $ManifestPath -Encoding UTF8
Set-Content -Path $versionFile -Value $versionStamp -NoNewline -Encoding UTF8
if(Test-Path $BuildRoot\fingerprint)
{
Remove-Item $BuildRoot\fingerprint
}
}

View File

@@ -0,0 +1,28 @@
task Uninstall {
'Unloading Modules...'
Get-Module -Name $ModuleName -ErrorAction 'Ignore' | Remove-Module -Force
'Uninstalling Module packages...'
$modules = Get-Module $ModuleName -ErrorAction 'Ignore' -ListAvailable
foreach ($module in $modules)
{
Uninstall-Module -Name $module.Name -RequiredVersion $module.Version -ErrorAction 'Ignore'
}
'Cleaning up manually installed Modules...'
$path = $env:PSModulePath.Split(';').Where({
$_ -like 'C:\Users\*'
}, 'First', 1)
$path = Join-Path -Path $path -ChildPath $ModuleName
if ($path -and (Test-Path -Path $path))
{
'Removing files... (This may fail if any DLLs are in use.)'
Get-ChildItem -Path $path -File -Recurse |
Remove-Item -Force | ForEach-Object 'FullName'
'Removing folders... (This may fail if any DLLs are in use.)'
Remove-Item $path -Recurse -Force
}
}

View File

@@ -0,0 +1,6 @@
task UpdateSource {
Copy-Item -Path $ManifestPath -Destination "$Source\$ModuleName.psd1"
$content = Get-Content -Path "$Source\$ModuleName.psd1" -Raw -Encoding UTF8
$content.Trim() | Set-Content -Path "$Source\$ModuleName.psd1" -Encoding UTF8
}

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2017 Kevin Marquette Copyright (c) 2019 Kevin Marquette
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

15
Module.build.ps1 Normal file
View File

@@ -0,0 +1,15 @@
$Script:ModuleName = Get-ChildItem .\*\*.psm1 | Select-object -ExpandProperty BaseName
$Script:CodeCoveragePercent = 0.0 # 0 to 1
. $psscriptroot\BuildTasks\InvokeBuildInit.ps1
task Default Build, Helpify, Test, UpdateSource
task Build Copy, Compile, BuildModule, BuildManifest, SetVersion
task Helpify GenerateMarkdown, GenerateHelp
task Test Build, ImportModule, Pester
task Publish Build, PublishVersion, Helpify, Test, PublishModule
task TFS Clean, Build, PublishVersion, Helpify, Test
task DevTest ImportDevModule, Pester
Write-Host 'Import common tasks'
Get-ChildItem -Path $buildroot\BuildTasks\*.Task.ps1 |
ForEach-Object {Write-Host $_.FullName;. $_.FullName}

View File

@@ -0,0 +1,34 @@
@{
# Use Severity when you want to limit the generated diagnostic records to a
# subset of: Error, Warning and Information.
# Uncomment the following line if you only want Errors and Warnings but
# not Information diagnostic records.
Severity = @('Error','Warning')
# Use IncludeRules when you want to run only a subset of the default rule set.
#IncludeRules = @('PSAvoidDefaultValueSwitchParameter',
# 'PSMisleadingBacktick',
# 'PSMissingModuleManifestField',
# 'PSReservedCmdletChar',
# 'PSReservedParams',
# 'PSShouldProcess',
# 'PSUseApprovedVerbs',
# 'PSUseDeclaredVarsMoreThanAssigments')
# Use ExcludeRules when you want to run most of the default set of rules except
# for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules
# and ExcludeRules, the rule will be excluded.
ExcludeRules = @('PSUseToExportFieldsInManifest','PSMissingModuleManifestField')
# You can use the following entry to supply parameters to rules that take parameters.
# For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you
# want to allow.
Rules = @{
# Do not flag 'cd' alias.
PSAvoidUsingCmdletAliases = @{Whitelist = @('Where','Select')}
# Check if your script uses cmdlets that are compatible on PowerShell Core,
# version 6.0.0-alpha, on Linux.
# PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")}
}
}

View File

@@ -0,0 +1,33 @@
$Script:ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
$Script:ModuleName = $Script:ModuleName = Get-ChildItem $ModuleRoot\*\*.psm1 | Select-object -ExpandProperty BaseName
Describe "Public commands have comment-based or external help" -Tags 'Build' {
$functions = Get-Command -Module $ModuleName
$help = foreach ($function in $functions) {
Get-Help -Name $function.Name
}
foreach ($node in $help)
{
Context $node.Name {
It "Should have a Description or Synopsis" {
($node.Description + $node.Synopsis) | Should Not BeNullOrEmpty
}
It "Should have an Example" {
$node.Examples | Should Not BeNullOrEmpty
$node.Examples | Out-String | Should -Match ($node.Name)
}
foreach ($parameter in $node.Parameters.Parameter)
{
if ($parameter -notmatch 'WhatIf|Confirm')
{
It "Should have a Description for Parameter [$($parameter.Name)]" {
$parameter.Description.Text | Should Not BeNullOrEmpty
}
}
}
}
}
}

View File

@@ -0,0 +1,45 @@
$Script:ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent
$Script:ModuleName = $Script:ModuleName = Get-ChildItem $ModuleRoot\*\*.psm1 | Select-object -ExpandProperty BaseName
$Script:SourceRoot = Join-Path -Path $ModuleRoot -ChildPath $ModuleName
Describe "All commands pass PSScriptAnalyzer rules" -Tag 'Build' {
$rules = "$ModuleRoot\ScriptAnalyzerSettings.psd1"
$scripts = Get-ChildItem -Path $SourceRoot -Include '*.ps1', '*.psm1', '*.psd1' -Recurse |
Where-Object FullName -notmatch 'Classes'
foreach ($script in $scripts)
{
Context $script.FullName {
$results = Invoke-ScriptAnalyzer -Path $script.FullName -Settings $rules
if ($results)
{
foreach ($rule in $results)
{
It $rule.RuleName {
$message = "{0} Line {1}: {2}" -f $rule.Severity, $rule.Line, $rule.Message
$message | Should Be ""
}
}
}
else
{
It "Should not fail any rules" {
$results | Should BeNullOrEmpty
}
}
}
}
}
Describe "Public commands have Pester tests" -Tag 'Build' {
$commands = Get-Command -Module $ModuleName
foreach ($command in $commands.Name)
{
$file = Get-ChildItem -Path "$ModuleRoot\Tests" -Include "$command.Tests.ps1" -Recurse
It "Should have a Pester test for [$command]" {
$file.FullName | Should Not BeNullOrEmpty
}
}
}

25
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,25 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
#resources:
#- repo: self
# clean: true
# fetchDepth: 1
trigger:
batch: true
branches:
include:
- master
pool:
vmImage: 'Ubuntu 16.04'
steps:
- script: pwsh -File build.ps1 Publish
displayName: 'Build and Publish Module'
env:
nugetapikey: $(nugetapikey)

View File

@@ -1,21 +1,38 @@
<# [CmdletBinding()]
.Description
Installs and loads all the required modules for the build.
.Author
Warren F. (RamblingCookieMonster)
#>
[cmdletbinding()]
param($Task = 'Default') param($Task = 'Default')
# Grab nuget bits, install modules, set build variables, start build. $Script:Modules = @(
Get-PackageProvider -Name NuGet -ForceBootstrap | Out-Null 'BuildHelpers',
'InvokeBuild',
'Pester',
'platyPS',
'PSScriptAnalyzer',
'DependsOn'
)
Install-Module Psake, PSDeploy, BuildHelpers, PSScriptAnalyzer -force $Script:ModuleInstallScope = 'CurrentUser'
Install-Module Pester -Force -SkipPublisherCheck
Import-Module Psake, BuildHelpers, PSScriptAnalyzer 'Starting build...'
'Installing module dependencies...'
Get-PackageProvider -Name 'NuGet' -ForceBootstrap | Out-Null
Install-Module -Name $Script:Modules -Scope $Script:ModuleInstallScope -Force -SkipPublisherCheck
Set-BuildEnvironment Set-BuildEnvironment
Get-ChildItem Env:BH*
Get-ChildItem Env:APPVEYOR*
Invoke-psake -buildFile .\psake.ps1 -taskList $Task -nologo $Error.Clear()
exit ( [int]( -not $psake.build_success ) )
'Invoking build...'
Invoke-Build $Task -Result 'Result'
if ($Result.Error)
{
$Error[-1].ScriptStackTrace | Out-String
exit 1
}
exit 0