diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 894b203..280ea68 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -12,4 +12,4 @@ You may delete the guideline and text above to just leave the following details: - OS: **INSERT OPERATING SYSTEM HERE** - PowerShell Version: **INSERT POWERSHELL VERSION HERE** -- List the steps to reproduce the problem below (if possible attach a screenshot and/or link to the code): **LIST REPRO STEPS BELOW** \ No newline at end of file +- List the steps to reproduce the problem below (if possible attach a screenshot and/or link to the code): **LIST REPRO STEPS BELOW** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 38f21b8..ffe308c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -23,4 +23,4 @@ priorities of ModuleBuild's GitHub code might not match the priorities of the pull request. Don't fret, the open source community thrives on forks and GitHub makes it easy to keep your changes in a forked repo. -After reviewing the guidelines above you can delete this text from the pull request. \ No newline at end of file +After reviewing the guidelines above you can delete this text from the pull request. diff --git a/.gitignore b/.gitignore index 3dcc262..5ecd3bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ # ignore any temp directory (used in build steps) temp/ +# Ignore PSDepend installed build modules dependencies. +build/dependencies/PSDepend/* +!build/dependencies/PSDepend/README.md +!build/dependencies/PSDepend/build.depend.psd1 + # PSGallery publishing file can contain personal API key and paths and thus we want to ignore it if it exists .psgallery @@ -31,4 +36,4 @@ Desktop.ini $RECYCLE.BIN/ # Mac crap -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.vscode/launch.json b/.vscode/launch.json index d3218a5..5471f31 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,3 +18,4 @@ } ] } + diff --git a/.vscode/settings.json b/.vscode/settings.json index 87ffc11..e1d7ee0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,12 +6,24 @@ "search.exclude": { "release": true, - "temp": true + "": true }, + //-------- Indentation -------- + + // The number of spaces a tab is equal to. This setting is overridden + // based on the file contents when `editor.detectIndentation` is true. + "editor.tabSize": 4, + // Insert spaces when pressing Tab. This setting is overriden + // based on the file contents when `editor.detectIndentation` is true. + "editor.insertSpaces": true, + // When opening a file, `editor.tabSize` and `editor.insertSpaces` + // will be detected based on the file contents. Set to false to keep + // the values you've explicitly set, above. + "editor.detectIndentation": false, //-------- PowerShell Configuration -------- // Use a custom PowerShell Script Analyzer settings file for this workspace. // Relative paths for this setting are always relative to the workspace root dir. - "powershell.scriptAnalysis.settingsPath": "ScriptAnalyzerSettings.psd1" -} \ No newline at end of file + "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f9689b9..9eb0102 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,7 +9,7 @@ { // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format - "version": "2.0.0", + "version": "2.0.0", "tasks": [ { "label": "Build Module", @@ -18,10 +18,10 @@ "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -BuildModule" }, "linux": { - "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1" + "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule" }, "osx": { - "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1" + "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule" }, "group": { "kind": "build", @@ -33,16 +33,16 @@ } }, { - "label": "Build, Install, and Load Module", + "label": "Run Tests", "type": "shell", "windows": { - "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule" + "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -Test" }, "linux": { - "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule" + "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -Test" }, "osx": { - "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule" + "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -Test" }, "group": { "kind": "build", @@ -51,20 +51,19 @@ "presentation": { "reveal": "always", "panel": "new" - }, - "problemMatcher": [] + } }, { - "label": "Build, Install, Load, and Publish Module", + "label": "Test, Build, Install and Load Module", "type": "shell", "windows": { - "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule -UploadPSGallery" + "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -TestBuildAndInstallModule" }, "linux": { - "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule -UploadPSGallery" + "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -TestBuildAndInstallModule" }, "osx": { - "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -BuildModule -InstallAndTestModule -UploadPSGallery" + "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -TestBuildAndInstallModule" }, "group": { "kind": "build", @@ -73,20 +72,19 @@ "presentation": { "reveal": "always", "panel": "new" - }, - "problemMatcher": [] + } }, { "label": "Insert Missing Comment Based Help", "type": "shell", "windows": { - "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -InsertMissingCBH" + "command": "powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File ${workspaceRoot}\\Build.ps1 -AddMissingCBH" }, "linux": { - "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -InsertMissingCBH" + "command": "/usr/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -AddMissingCBH" }, "osx": { - "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -InsertMissingCBH" + "command": "/usr/local/bin/powershell -NoProfile -File ${workspaceRoot}\\Build.ps1 -AddMissingCBH" }, "group": { "kind": "build", diff --git a/Build.ps1 b/Build.ps1 index 5a466c0..7015a34 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -3,29 +3,46 @@ param ( [parameter(Position = 0, ParameterSetName = 'Build')] [switch]$BuildModule, - [parameter(Position = 2, ParameterSetName = 'Build')] - [switch]$UploadPSGallery, - [parameter(Position = 3, ParameterSetName = 'Build')] - [switch]$InstallAndTestModule, - [parameter(Position = 4, ParameterSetName = 'Build')] + [parameter(Position = 1,ParameterSetName = 'Build')] + [switch]$TestBuildAndInstallModule, + [parameter(Position = 2, ParameterSetName = 'UpdateRelease')] + [switch]$UpdateRelease, + [parameter(Position = 3, ParameterSetName = 'UpdateRelease')] [version]$NewVersion, - [parameter(Position = 5, ParameterSetName = 'Build')] + [parameter(Position = 4, ParameterSetName = 'UpdateRelease')] [string]$ReleaseNotes, + [parameter(Position = 5, ParameterSetName = 'UpdateRelease')] + [switch]$UploadPSGallery, [parameter(Position = 6, ParameterSetName = 'CBH')] - [switch]$InsertCBH + [switch]$AddMissingCBH, + [parameter(Position = 7, ParameterSetName = 'Tests')] + [switch]$Test, + [parameter(Position = 8,ParameterSetName = 'Tests')] + [switch]$TestMetaOnly, + [parameter(Position = 9,ParameterSetName = 'Tests')] + [switch]$TestUnitOnly, + [parameter(Position = 10,ParameterSetName = 'Tests')] + [switch]$TestIntergrationOnly ) function PrerequisitesLoaded { # Install required modules if missing try { - if ((get-module InvokeBuild -ListAvailable) -eq $null) { - Write-Output "Attempting to install the InvokeBuild module..." - $null = Install-Module InvokeBuild -Scope:CurrentUser + if ((get-module PSDepend -ListAvailable) -eq $null) { + Write-Host "Attempting to install the PSDepend module..." -NoNewLine + $null = Install-Module PSDepend -MinimumVersion 0.3.2 -MaximumVersion 0.3.2 -Scope:CurrentUser + Write-Host 'Installed!' } - if (get-module InvokeBuild -ListAvailable) { - Write-Output -NoNewLine "Importing InvokeBuild module" - Import-Module InvokeBuild -Force - Write-Output '...Loaded!' + if (get-module PSDepend -ListAvailable) { + Write-Host "Importing PSDepend module..." -NoNewLine + Import-Module PSDepend -Force + Write-Host 'Loaded!' + + Write-Host 'Installing dependencies...' -NoNewLine + Invoke-PSDepend -Path $(Join-Path $(Get-Location) 'Requirements.psd1') -Test + Invoke-PSDepend -Path $(Join-Path $(Get-Location) 'Requirements.psd1') -Force + Invoke-PSDepend -Path $(Join-Path $(Get-Location) 'Requirements.psd1') -Import -Force + Write-Host 'Installed!' return $true } else { @@ -39,8 +56,8 @@ function PrerequisitesLoaded { function CleanUp { try { - Write-Output '' - Write-Output 'Attempting to clean up the session (loaded modules and such)...' + Write-Host '' + Write-Host 'Attempting to clean up the session (loaded modules and such)...' Invoke-Build -Task BuildSessionCleanup Remove-Module InvokeBuild } @@ -52,50 +69,49 @@ if (-not (PrerequisitesLoaded)) { } switch ($psCmdlet.ParameterSetName) { - 'CBH' { - if ($InsertCBH) { + 'Build' { + # If no parameters were specified or the build action was manually specified then kick off a standard build + if (($psboundparameters.count -eq 0) -or ($BuildModule)) { try { - Invoke-Build -Task InsertMissingCBH + Invoke-Build } catch { - throw + Write-Host 'Build Failed with the following error:' + throw $_ } } - CleanUp - } - 'Build' { - if ($NewVersion -ne $null) { + # Test, Build Installd and test load the module + if ($TestBuildAndInstallModule) { try { - Invoke-Build -Task UpdateVersion -NewVersion $NewVersion -ReleaseNotes $ReleaseNotes + Invoke-Build -Task TestBuildAndInstallModule } catch { + Write-Host 'Test, Build Installd and test load the module of the module failed:' throw $_ } } - # If no parameters were specified or the build action was manually specified then kick off a standard build - if (($psboundparameters.count -eq 0) -or ($BuildModule)) { + } + 'CBH' { + if ($AddMissingCBH) { try { - Invoke-Build + Invoke-Build -Task AddMissingCBH } catch { - Write-Output 'Build Failed with the following error:' - Write-Output $_ + throw $_ } } - - # Install and test the module? - if ($InstallAndTestModule) { + } + 'UpdateRelease' { + if ($UpdateRelease) { try { - Invoke-Build -Task InstallAndTestModule + Invoke-Build -Task UpdateRelease -NewVersion $NewVersion -ReleaseNotes $ReleaseNotes } catch { - Write-Output 'Install and test of module failed:' - Write-Output $_ + throw $_ } } - # Upload to gallery? if ($UploadPSGallery) { try { Invoke-Build -Task PublishPSGallery @@ -104,7 +120,39 @@ switch ($psCmdlet.ParameterSetName) { throw 'Unable to upload project to the PowerShell Gallery!' } } - - CleanUp } -} \ No newline at end of file + 'Tests' { + if ($test) { + try { + Invoke-Build -Task tests + } + catch { + throw + } + } + if ($TestMetaOnly) { + try { + Invoke-Build -Task RunMetaTests + } + catch { + throw + } + } + if ($TestUnitOnly) { + try { + Invoke-Build -Task RunUnitTests + } + catch { + throw + } + } + if ($TestIntergrationOnly) { + try { + Invoke-Build -Task RunIntergrationTests + } + catch { + throw + } + } + } +} diff --git a/Install.ps1 b/Install.ps1 index ffe9ba3..ead17d3 100644 --- a/Install.ps1 +++ b/Install.ps1 @@ -1,6 +1,6 @@ # Run this in an administrative PowerShell prompt to install the ModuleBuild PowerShell module: # -# iex (New-Object Net.WebClient).DownloadString("https://github.com/zloeber/ModuleBuild/raw/master/Install.ps1") +# iex (New-Object Net.WebClient).DownloadString("https://github.com/zloeber/ModuleBuild/raw/master/Install.ps1") # Some general variables $ModuleName = 'ModuleBuild' diff --git a/License.md b/LICENSE.md similarity index 96% rename from License.md rename to LICENSE.md index 097c10c..05973fb 100644 --- a/License.md +++ b/LICENSE.md @@ -2,7 +2,7 @@
You are free to:
-

Share — copy and redistribute the material in any medium or format

+

Share — copy and redistribute the material in any medium or format

Adapt — remix, transform, and build upon the material

diff --git a/ModuleBuild.build.ps1 b/ModuleBuild.build.ps1 index 5a8de8f..6e92931 100644 --- a/ModuleBuild.build.ps1 +++ b/ModuleBuild.build.ps1 @@ -1,11 +1,11 @@ param ( [parameter(Position = 0)] [string]$BuildFile = @(Get-ChildItem 'build\*.buildenvironment.ps1')[0].FullName, - [parameter(Position = 1)] + [parameter()] [version]$NewVersion = $null, - [parameter(Position = 2)] + [parameter()] [string]$ReleaseNotes, - [parameter(Position = 3)] + [parameter()] [switch]$Force ) @@ -45,68 +45,52 @@ if ($Script:BuildEnv.OptionTranscriptEnabled) { Start-Transcript -Path $TranscriptLog -Append -WarningAction:SilentlyContinue } +#$Script:BuildRoot = $BuildRoot +#region Configure #Synopsis: Validate system requirements are met task ValidateRequirements { Write-Description White 'Validating System Requirements for Build' -accent assert ($PSVersionTable.PSVersion.Major.ToString() -eq '5') 'Powershell 5 is required for this build to function properly (you can comment this assert out if you are able to work around this requirement)' } -#Synopsis: Load required modules if available. Otherwise try to install, then load it. -task LoadRequiredModules { - Write-Description White 'Loading all required modules for the build framework' -accent - - # These are required for a full build process and will be automatically installed if they aren't available - $Script:RequiredModules = @('PlatyPS') - - # Some optional modules - if ($Script:BuildEnv.OptionAnalyzeCode) { - $Script:RequiredModules += 'PSScriptAnalyzer' - } - if ($Script:BuildEnv.OptionGenerateReadTheDocs) { - $Script:RequiredModules += 'Powershell-YAML' - } - if ($Script:BuildEnv.OptionCodeHealthReport) { - $RequiredModules += 'PSCodeHealth' - } - - $Script:RequiredModules | Foreach-Object { - if ((get-module $_ -ListAvailable) -eq $null) { - Write-Description White "Installing $($_) Module" -Level 2 - $null = Install-Module $_ -Scope:CurrentUser - } - if (get-module $_ -ListAvailable) { - Write-Description White "Importing $($_) Module" -Level 2 - Import-Module $_ -Force - } - else { - throw 'How did you even get here?' - } - } -} - -#Synopsis: Load dot sourced functions into this build session -task LoadBuildTools { - Write-Description White 'Sourcing all of the required build functions' -accent +# Synopsis: Run pre-build scripts (such as other builds) +task PreBuildTasks { + Write-Description White 'Running any Pre-Build scripts' -accent $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder - $BuildTools = Join-Path $BuildToolPath 'dotSource' - # Dot source any build script functions we need to use - Get-ChildItem $BuildTools -Recurse -Filter "*.ps1" -File | ForEach-Object { - Write-Description White "Dot sourcing script file: $($_.Name)" -Level 2 + $PreBuildPath = Join-Path $BuildToolPath 'startup' + # Dot source any pre build scripts. + Get-ChildItem -Path $PreBuildPath -Recurse -Filter "*.ps1" -File | Foreach { + Write-Description White "Dot sourcing pre-build script file: $($_.Name)" -level 2 . $_.FullName } } -# Synopsis: Create new module manifest with explicit function exports included -task CreateModuleManifest CreateModulePSM1, { - Write-Description White 'Module manifest file updates' -accent +# Synopsis: Import the current module manifest file for processing +task LoadModuleManifest { + Write-Description White 'Loading the existing module manifest for this module' -accent + $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" - $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder + assert (test-path $ModuleManifestFullPath) "Unable to locate the module manifest file: $ModuleManifestFullPath" + $Script:Manifest = Test-ModuleManifest -Path $ModuleManifestFullPath +} - $PSD1OutputFile = Join-Path $StageReleasePath "$($Script:BuildEnv.ModuleToBuild).psd1" - $ThisPSD1OutputFile = ".\$($Script:BuildEnv.ScratchFolder)\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleToBuild).psd1" - Write-Description White "Attempting to update the release module manifest file: $ThisPSD1OutputFile" -Level 2 - $null = Copy-Item -Path $ModuleManifestFullPath -Destination $PSD1OutputFile -Force - Update-ModuleManifest -Path $PSD1OutputFile -FunctionsToExport $Script:FunctionsToExport +#Synopsis: Load required build modules using PSDepend +task LoadRequiredModules { + Write-Description White 'Loading all required modules for the build framework' -accent + + if ((Get-Module PSDepend -ListAvailable) -eq $null) { + Write-Description White "Installing PSDepend Module" -Level 2 + $null = Install-Module PSDepend -Scope:CurrentUser + } + + $PSDependFolder = $(Join-Path $Script:BuildEnv.BuildToolFolder 'dependencies\PSDepend') + $PSDependBuildFile = $(Join-Path $PSDependFolder 'build.depend.psd1') + Invoke-PSDepend -Path $PSDependBuildFile -Force + $Script:PSDependBuildModules = Invoke-PSDepend -Path $PSDependBuildFile -Test | Select-Object Version,DependencyName,DependencyType + Invoke-PSDepend -Path $PSDependBuildFile -Import -Force + $Script:PSDependBuildModules | ForEach-Object { + Write-Description White "Installed version $($_.Version) of $($_.DependencyName) from $($_.DependencyType)" -Level 2 + } } # Synopsis: Load the module project @@ -122,15 +106,6 @@ task LoadModule { } } -# Synopsis: Import the current module manifest file for processing -task LoadModuleManifest { - Write-Description White 'Loading the existing module manifest for this module' -accent - - $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" - assert (test-path $ModuleManifestFullPath) "Unable to locate the module manifest file: $ModuleManifestFullPath" - $Script:Manifest = Test-ModuleManifest -Path $ModuleManifestFullPath -} - # Synopsis: Valiates there is no version mismatch. task VersionCheck LoadModuleManifest, { Write-Description White 'Validating manifest, build, and gallery module versions.' -accent @@ -148,19 +123,28 @@ task VersionCheck LoadModuleManifest, { } } +#Synopsis: Validate script requirements are met, load required modules, load project manifest and module. +task Configure ValidateRequirements, PreBuildTasks, LoadRequiredModules, LoadModuleManifest, LoadModule, VersionCheck, { + # If we made it this far then we are configured! + Write-Description White 'Configuring build environment' -accent +} +#endregion + +#region Helpers #Synopsis: Create a CodeHealthReport of your existing code task CodeHealthReport -if {$Script:BuildEnv.OptionCodeHealthReport} ValidateRequirements, LoadRequiredModules, { - $BuildReportsFolder = Join-Path $BuildRoot $Script:BuildEnv.BuildReportsFolder - Write-Description White "Creating a prebuild code health report of your public functions to $($Script:BuildEnv.BuildReportsFolder)" -accent + $BuildReportsFolder = Join-Path $BuildRoot $(Join-Path $Script:BuildEnv.BuildReportsFolder $Script:BuildEnv.ModuleVersion) + Write-Description White "Creating a prebuild code health report of your public functions to $($BuildReportsFolder)" -accent if (-not (Test-Path $BuildReportsFolder)) { - New-Item -Path $BuildReportsFolder -ItemType:Directory + [void](New-Item -Path $BuildReportsFolder -ItemType:Directory) } Write-Description White 'Creating a code health report of your public functions' -level 2 $CodeHealthScanPathPublic = Join-Path $BuildRoot $Script:BuildEnv.PublicFunctionSource + $CodeHealthScanTestPathPublic = $CodeHealthScanPathPublic -replace 'src', 'tests\\unit' $CodeHealthReportPublic = Join-Path $BuildReportsFolder 'CodeHealthReport-Public.html' - Invoke-PSCodeHealth -Path $CodeHealthScanPathPublic -HtmlReportPath $CodeHealthReportPublic + Invoke-PSCodeHealth -Path $CodeHealthScanPathPublic -HtmlReportPath $CodeHealthReportPublic -TestsPath $CodeHealthScanTestPathPublic if (Test-Path $CodeHealthReportPublic) { (Get-Content -Path $CodeHealthReportPublic -raw) -replace [regex]::escape((Resolve-Path $CodeHealthScanPathPublic)), $Script:BuildEnv.PublicFunctionSource | Out-File -FilePath $CodeHealthReportPublic -Encoding $Script:BuildEnv.Encoding -Force @@ -168,93 +152,17 @@ task CodeHealthReport -if {$Script:BuildEnv.OptionCodeHealthReport} ValidateRequ Write-Description White 'Creating a code health report of your private functions' -level 2 $CodeHealthScanPathPrivate = Join-Path $BuildRoot $Script:BuildEnv.PrivateFunctionSource + $CodeHealthScanTestPathPrivate = $CodeHealthScanPathPrivate -replace 'src', 'tests\\unit' $CodeHealthReportPrivate = Join-Path $BuildReportsFolder 'CodeHealthReport-Private.html' - Invoke-PSCodeHealth -Path $CodeHealthScanPathPrivate -HtmlReportPath $CodeHealthReportPrivate + Invoke-PSCodeHealth -Path $CodeHealthScanPathPrivate -HtmlReportPath $CodeHealthReportPrivate -TestsPath $CodeHealthScanTestPathPrivate if (Test-Path $CodeHealthReportPrivate) { (Get-Content -Path $CodeHealthReportPrivate -raw) -replace [regex]::escape((Resolve-Path $CodeHealthScanPathPrivate)), $Script:BuildEnv.PrivateFunctionSource | Out-File -FilePath $CodeHealthReportPrivate -Encoding $Script:BuildEnv.Encoding -Force } } -#Synopsis: Validate script requirements are met, load required modules, load project manifest and module, and load additional build tools. -task Configure ValidateRequirements, PreBuildTasks, LoadRequiredModules, LoadModuleManifest, LoadModule, VersionCheck, LoadBuildTools, { - # If we made it this far then we are configured! - Write-Description White 'Configuring build environment' -accent -} - -# Synopsis: Set a new version of the module -task NewVersion LoadBuildTools, LoadModuleManifest, { - Write-Description White 'Updating module build version' -accent - $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" - $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder - $AllReleases = @((Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -match '^([0-9].[0-9].[0-9])$'} | Select-Object).Name | ForEach-Object {[version]$_}) - - # if a new version wasn't passed as a parameter then prompt for one - if ($null -eq $NewVersion) { - do { - $NewVersion = Read-Host -Prompt 'Enter the version (ie 0.0.8) for your build release' - if ([string]::IsNullOrEmpty($NewVersion)) { - Write-Build Red "You need to enter a valid module version (ie. 1.2.0)" -level 2 - } - } while ( [string]::IsNullOrEmpty($NewVersion) -and ($NewVersion -notmatch '\d+\.\d+\.\d+') ) - } - if ($AllReleases -contains $NewVersion) { - if ((-not $Force) -and (-not $Script:BuildEnv.Force)) { - throw 'The module version already has been released (the folder exists within the releases folder. In order to set your build project to this version you will need to pass the -Force switch to this build script with the -NewVersion parameter' - } - } - - $Script:BuildEnv.ModuleVersion = $NewVersion.ToString() - - Write-Description White 'Saving persistent build file with new module version' -level 2 - Save-BuildData - - Write-Description White 'Updating module manifest with new module version' -level 2 - try { - Update-ModuleManifest -Path $ModuleManifestFullPath -ModuleVersion $NewVersion - } - catch { - throw $_ - } -} - -# Synopsis: Update current module manifest with the version defined in the build config file (if they differ) -task UpdateRelease LoadBuildTools, LoadModuleManifest, { - Write-Description White 'Updating the release notes of this module' -accent - - $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" - # If there is a version mismatch then we need to put in some release notes and update the module manifest - if ($null -eq $ReleaseNotes) { - do { - $ReleaseNotes = Read-Host -Prompt 'Enter brief release notes for this new version' - if ([string]::IsNullOrEmpty($ReleaseNotes)) { - Write-Build Red "You need to enter some kind of notes for your new release to update the manifest with!" -level 2 - } - } while ([string]::IsNullOrEmpty($ReleaseNotes)) - } - - try { - Update-ModuleManifest -Path $ModuleManifestFullPath -ReleaseNotes $ReleaseNotes - } - catch { - throw $_ - } -} - -# Synopsis: Update the current working module version and release notes -task UpdateVersion NewVersion, UpdateRelease - -# Synopsis: Regenerate scratch staging directory -task Clean { - Write-Description White "Clean up our scratch/staging directory $($Script:BuildEnv.ScratchFolder)" -accent - - $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder - $null = Remove-Item $ScratchPath -Force -Recurse -ErrorAction 0 - $null = New-Item $ScratchPath -ItemType:Directory -Force -} - # Synopsis: Create base content tree in scratch staging area -task PrepareStage { +task PrepareStage CleanScratchDirectory, { Write-Description White 'Creating staging folder structure' -accent $BuildDocsPath = Join-Path $BuildRoot "$($Script:BuildEnv.BuildToolFolder)\docs\" @@ -272,55 +180,10 @@ task PrepareStage { Copy-Item -Path "$($BuildRoot)\$($Script:BuildEnv.OtherModuleSource)" -Recurse -Destination "$($ScratchPath)\$($Script:BuildEnv.OtherModuleSource)" Copy-Item -Path (Join-Path $BuildDocsPath 'en-US') -Recurse -Destination $ScratchPath $Script:BuildEnv.AdditionalModulePaths | ForEach-Object { - Copy-Item -Path $_ -Recurse -Destination $ScratchPath -Force - } -} - -# Synopsis: Update public functions to include a template comment based help. -task UpdateCBHtoScratch { - Write-Description White "Attempting to insert comment based help into functions" -accent - - $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder - $CBHPattern = "(?ms)(^\s*\<#.*\.SYNOPSIS.*?#>)" - $CBHUpdates = 0 - - # Create the directories - $null = New-Item (Join-Path $ScratchPath "src") -ItemType:Directory -Force - $null = New-Item (Join-Path $ScratchPath $Script:BuildEnv.PublicFunctionSource) -ItemType:Directory -Force - Get-ChildItem (Join-Path $BuildRoot $Script:BuildEnv.PublicFunctionSource) -Filter *.ps1 | ForEach-Object { - $FileName = $_.Name - $FullFilePath = $_.FullName - Write-Description White "Public function - $($FileName)" -level 2 - $currscript = Get-Content $FullFilePath -Raw - $CBH = $currscript | New-CommentBasedHelp - $currscriptblock = [scriptblock]::Create($currscript) - . $currscriptblock - $currfunct = get-command $CBH.FunctionName - - - if ($currfunct.definition -notmatch $CBHPattern) { - $CBHUpdates++ - Write-Description White "Inserting template CBH and writing to : $($Script:BuildEnv.ScratchFolder)\$($Script:BuildEnv.PublicFunctionSource)\$($FileName)" -Level 3 - $UpdatedFunct = 'Function ' + $currfunct.Name + ' {' + "`r`n" + $CBH.CBH + "`r`n" + $currfunct.definition + "`r`n" + '}' - $UpdatedFunct | Out-File "$($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)\$($FileName)" -Encoding $Script:BuildEnv.Encoding -force - } - else { - Write-Description Yellow 'Comment based help already exists!' -Level 2 + if (Test-Path $_) { + Copy-Item -Path $_ -Recurse -Destination $ScratchPath -Force } - - Remove-Item Function:\$($currfunct.Name) - } - Write-Build White '' - Write-Build Yellow '****************************************************************************************************' - Write-Build Yellow " Updated Functions: $CBHUpdates" - if ($CBHUpdates -gt 0) { - Write-Build White '' - Write-Build Yellow " Updated Function Location: $($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)" - Write-Build White '' - Write-Build Yellow " NOTE: Please inspect these files closely. If they look good merge them back into your project" } - Write-Build Yellow '****************************************************************************************************' - $null = Read-Host 'Press Enter to continue...' } # Synopsis: Collect a list of our public methods for later module manifest updates @@ -338,66 +201,21 @@ task GetPublicFunctions { Write-Description White "Number of exported functions found = $($Exported.Count)" -Level 2 } -# Synopsis: Assemble the module for release -task CreateModulePSM1 RemoveScriptSignatures, UpdateCBH, { - Write-Description White "Assembling module psm1 file" -Accent +# Synopsis: Validate that sensitive strings are not found in your code +task SanitizeCode -if {$Script:BuildEnv.OptionSanitizeSensitiveTerms} { + Write-Description White 'Attempting to find sensitive terms in the code' -accent $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder $StageReleasePath = Join-Path $ScratchPath $Script:BuildEnv.BaseReleaseFolder - $ReleaseModule = "$($StageReleasePath)\$($Script:BuildEnv.ModuleToBuild).psm1" - - if ($Script:BuildEnv.OptionCombineFiles) { - Write-Description White "Option to combine PSM1 is enabled, combining source files now..." -Level 2 - - $CombineFiles = '' - $PreloadFilePath = (Join-Path $ScratchPath "$($Script:BuildEnv.OtherModuleSource)\PreLoad.ps1") - if (Test-Path $PreloadFilePath) { - Write-Description White 'Starting with Preload.ps1' -Level 3 - $CombineFiles += "## Pre-Loaded Module code ##`r`n`r`n" - Get-childitem $PreloadFilePath | ForEach-Object { - $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" - } - } - Write-Description White 'Adding the private source files:' -Level 3 - - $CombineFiles += "## PRIVATE MODULE FUNCTIONS AND DATA ##`r`n`r`n" - - Get-childitem (Join-Path $ScratchPath "$($Script:BuildEnv.PrivateFunctionSource)\*.ps1") | ForEach-Object { - Write-Description White $_.Name -Level 4 - $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" - } - - Write-Description White 'Adding the public source files:' -Level 3 - - $CombineFiles += "## PUBLIC MODULE FUNCTIONS AND DATA ##`r`n`r`n" - Get-childitem (Join-Path $ScratchPath "$($Script:BuildEnv.PublicFunctionSource)\*.ps1") | ForEach-Object { - Write-Description White $_.Name -Level 4 - $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" - } - Write-Description White 'Finishing with Postload.ps1' -Level 3 - $CombineFiles += "## Post-Load Module code ##`r`n`r`n" - $PostLoadPath = (Join-Path $ScratchPath "$($Script:BuildEnv.OtherModuleSource)\PostLoad.ps1") - if (Test-Path $PostLoadPath) { - Get-childitem $PostLoadPath | ForEach-Object { - $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" + ForEach ($Term in $Script:BuildEnv.OptionSensitiveTerms) { + Write-Description White "Checking Files for sensitive string: $Term" -level 2 + $TermsFound = Get-ChildItem -Path $ScratchPath -Recurse -File | Where-Object {$_.FullName -notlike "$($StageReleasePath)*"} | Select-String -Pattern $Term + if ($TermsFound.Count -gt 0) { + Write-Description white "Sensitive string found in the following files:" -Level 3 + $TermsFound | ForEach-Object { + Write-Description yellow $_ -level 4 } - } - - Set-Content -Path $ReleaseModule -Value $CombineFiles -Encoding $Script:BuildEnv.Encoding - } - else { - Write-Description White 'Option to combine PSM1 is NOT enabled, copying over file structure now...' -Level 2 - Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.OtherModuleSource) -Recurse -Destination $StageReleasePath -Force - Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.PrivateFunctionSource) -Recurse -Destination $StageReleasePath -Force - Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.PublicFunctionSource) -Recurse -Destination $StageReleasePath -Force - Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.ModuleToBuild) -Destination $StageReleasePath -Force - } - - if (($Script:BuildEnv.AdditionalModulePaths).Count -gt 0) { - Write-Description White 'Copying over additional module paths now.' -Level 2 - $Script:BuildEnv.AdditionalModulePaths | ForEach-Object { - Write-Description White "Copying $_" -Level 3 - Copy-Item -Path $_ -Recurse -Destination $StageReleasePath -Force + throw "Sensitive Terms found!" } } } @@ -410,90 +228,72 @@ task RemoveScriptSignatures { if ($Script:BuildEnv.OptionCombineFiles) { Write-Description White 'Remove script signatures from all files' -Level 2 - Get-ChildItem -Path "$($ScratchPath)\$($Script:BuildEnv.BaseSourceFolder)" -Recurse -File | ForEach-Object {Remove-Signature -FilePath $_.FullName} + Get-ChildItem -Path "$($ScratchPath)\$($Script:BuildEnv.BaseSourceFolder)" -Recurse -File | ForEach-Object {Remove-MBTSignature -FilePath $_.FullName} } } -# Synopsis: Validate that sensitive strings are not found in your code -task SanitizeCode -if {$Script:BuildEnv.OptionSanitizeSensitiveTerms} { - Write-Description White 'Attempting to find sensitive terms in the code' -accent - $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder - $StageReleasePath = Join-Path $ScratchPath $Script:BuildEnv.BaseReleaseFolder +# Synopsis: Run PSScriptAnalyzer against the public source files. +task RunPSScriptAnalyzeOnPublicSrcFunctions { + Write-Description White "Analyzing the public source files with ScriptAnalyzer." -accent + $Analysis = Invoke-ScriptAnalyzer -Path (Join-Path $BuildRoot $Script:BuildEnv.PublicFunctionSource) -Settings (Join-Path $BuildRoot "PSScriptAnalyzerSettings.psd1") + if ($Analysis.Count) { + Write-Description White "Note that this was from the script analysis run against $($Script:BuildEnv.PublicFunctionSource)" -level 2 + Write-Description Red "$($Analysis.Count) linting errors or warnings were found:" -level 2 + $Analysis | Format-Table -AutoSize + } +} +#endregion - ForEach ($Term in $Script:BuildEnv.OptionSensitiveTerms) { - Write-Description White "Checking Files for sensitive string: $Term" -level 2 - $TermsFound = Get-ChildItem -Path $ScratchPath -Recurse -File | Where-Object {$_.FullName -notlike "$($StageReleasePath)*"} | Select-String -Pattern $Term - if ($TermsFound.Count -gt 0) { - Write-Description white "Sensitive string found in the following files:" -Level 3 - $TermsFound | ForEach-Object { - Write-Description yellow $_ -level 4 - } - throw "Sensitive Terms found!" - } +#region Tests +task RunMetaTests LoadRequiredModules, { + Write-Description White 'Running meta tests with Pester' -accent + $ENV:BuildRoot = $BuildRoot + $invokePesterParams = @{ + Strict = $true + PassThru = $true + Verbose = $false + EnableExit = $false } + $testResults = Invoke-Pester -Tag 'MetaTest' $(Join-Path $BuildRoot 'Tests') @invokePesterParams + $numberFails = $testResults.FailedCount + assert($numberFails -eq 0) ('Failed "{0}" meta tests.' -f $numberFails) } -# Synopsis: Replace comment based help with external help in all public functions for this project -task UpdateCBH { - Write-Description White 'Updating Comment Based Help in functions to point to external help links' -accent +task RunUnitTests LoadRequiredModules, { + Write-Description White 'Running Unit tests with Pester' -accent + $ENV:BuildRoot = $BuildRoot + $invokePesterParams = @{ + Strict = $true + PassThru = $true + Verbose = $false + EnableExit = $false + } + $testResults = Invoke-Pester -tag 'UnitTest' $(Join-Path $BuildRoot 'Tests') @invokePesterParams + $numberFails = $testResults.FailedCount + assert($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) +} - $ExternalHelp = @" -<# - .EXTERNALHELP $($Script:BuildEnv.ModuleToBuild)-help.xml - .LINK - {{LINK}} - #> -"@ - - $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder - [Regex]$CBHPattern = '(?ms)\<\#(\#(?!\>)|[^#])*\#\>' - Get-ChildItem -Path "$($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)\*.ps1" -File | ForEach-Object { - $FormattedOutFile = $_.FullName - $FileName = $_.Name - Write-Description White "Replacing CBH in file: $($FileName)" -level 2 - $FunctionName = $FileName -replace '.ps1', '' - $NewExternalHelp = $ExternalHelp -replace '{{LINK}}', ($Script:BuildEnv.ModuleWebsite + "/tree/master/$($Script:BuildEnv.BaseReleaseFolder)/$($Script:BuildEnv.ModuleVersion)/docs/Functions/$($FunctionName).md") - $UpdatedFile = $CBHPattern.Replace( (Get-Content $FormattedOutFile -raw), $NewExternalHelp, 1) - $UpdatedFile | Out-File -FilePath $FormattedOutFile -force -Encoding $Script:BuildEnv.Encoding - } -} - -# Synopsis: Run PSScriptAnalyzer against the assembled module -task AnalyzeModuleRelease -if {$Script:BuildEnv.OptionAnalyzeCode} { - Write-Description White 'Analyzing the project with ScriptAnalyzer' -accent - $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - $Analysis = Invoke-ScriptAnalyzer -Path $StageReleasePath - $AnalysisErrors = @($Analysis | Where-Object {@('Information', 'Warning') -notcontains $_.Severity}) - if ($AnalysisErrors.Count -ne 0) { - Write-Build White 'The following errors came up in the script analysis:' -level 2 - $AnalysisErrors - Write-Build - Write-Build White "Note that this was from the script analysis run against $StageReleasePath" -Level 2 - Prompt-ForBuildBreak -CustomError $AnalysisErrors - } -} - -# Synopsis: Run PSScriptAnalyzer against the public source files. -task AnalyzePublic { - Write-Description White 'Analyzing the public source files with ScriptAnalyzer.' -accent - $Analysis = Invoke-ScriptAnalyzer -Path (Join-Path $BuildRoot $Script:BuildEnv.PublicFunctionSource) - $AnalysisErrors = @($Analysis | Where-Object {@('Information', 'Warning') -notcontains $_.Severity}) - - if ($AnalysisErrors.Count -ne 0) { - Write-Description White 'The following errors came up in the script analysis:' -level 2 - $AnalysisErrors - Write-Description - Write-Description White "Note that this was from the script analysis run against $($Script:BuildEnv.PublicFunctionSource)" -level 2 - } -} - -# Synopsis: Build help files for module -task CreateHelp CreateMarkdownHelp, CreateExternalHelp, CreateUpdateableHelpCAB, CreateProjectHelp, AddAdditionalDocFiles +task RunIntergrationTests LoadRequiredModules, { + Write-Description White 'Running Intergration tests with Pester' -accent + $ENV:BuildRoot = $BuildRoot + $invokePesterParams = @{ + Strict = $true + PassThru = $true + Verbose = $false + EnableExit = $false + } + $testResults = Invoke-Pester -tag 'IntergrationTest' $(Join-Path $BuildRoot 'Tests') @invokePesterParams + $numberFails = $testResults.FailedCount + assert($numberFails -eq 0) ('Failed "{0}" unit tests.' -f $numberFails) +} +#endregion +#region Documentation/Help files # Synopsis: Build the markdown help files with PlatyPS task CreateMarkdownHelp GetPublicFunctions, { Write-Description White 'Creating markdown documentation with PlatyPS' -accent + $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder $StageReleasePath = Join-Path $ScratchPath $Script:BuildEnv.BaseReleaseFolder Copy-Item -Path (Join-Path $ScratchPath "en-US") -Recurse -Destination $StageReleasePath -Force @@ -501,22 +301,38 @@ task CreateMarkdownHelp GetPublicFunctions, { $FwLink = "$($OnlineModuleLocation)/$($Script:BuildEnv.ModuleToBuild)/docs/$($Script:BuildEnv.ModuleToBuild).md" $ModulePage = "$($StageReleasePath)\docs\$($Script:BuildEnv.ModuleToBuild).md" + # If the current module we are building is also loaded as a dependency with PSDepend, + # Unload it temporally and reload it from the scratchpath to build markdown files from. + if (Get-Module $Script:BuildEnv.ModuleToBuild | Where-Object {$_.Path -like "*PSDepend\$($Script:BuildEnv.ModuleToBuild)*"}) { + $TempDeload = $true + #Write-Verbose "$($(Get-Module $Script:BuildEnv.ModuleToBuild).Path)" + #Get-Command | Where-Object { $_.source -eq 'ModuleBuildTools'} | ForEach-Object { + # Remove-Item -Path Function:\$($_.Name) + #} + #while ($(Get-Module $Script:BuildEnv.ModuleToBuild)) { + Get-Module $Script:BuildEnv.ModuleToBuild | Remove-Module + #} + $TempModule = Join-Path $ScratchPath "$($Script:BuildEnv.ModuleToBuild).psd1" + try { + [void](Import-Module $TempModule -Force) + } + catch { + throw "Unable to load the project module: $($TempModule)" + } + Write-Verbose $($(Get-Module $Script:BuildEnv.ModuleToBuild).Path) + + } + # Create the function .md files and the generic module page md as well for the distributable module $null = New-MarkdownHelp -module $Script:BuildEnv.ModuleToBuild -OutputFolder "$($StageReleasePath)\docs\" -Force -WithModulePage -Locale 'en-US' -FwLink $FwLink -HelpVersion $Script:BuildEnv.ModuleVersion -Encoding ([System.Text.Encoding]::($Script:BuildEnv.Encoding)) + $null = Update-MarkdownHelpModule -Path "$($StageReleasePath)\docs\" -Force -RefreshModulePage <# Replace each missing element we need for a proper generic module page .md file Also replace the blank Guid 00000000-0000-0000-0000-000000000000 with the actual module guid (PlatyPS won't know what this is as our module in memory is loaded from the psm1 file not the actual manifest file) #> $ModulePageFileContent = Get-Content -raw $ModulePage - $ModulePageFileContent = $ModulePageFileContent -replace '{{Manually Enter Description Here}}', $Script:Manifest.Description - $Script:FunctionsToExport | Foreach-Object { - Write-Description White "Updating definition for the following function: $($_)" -Level 2 - $TextToReplace = "{{Manually Enter $($_) Description Here}}" - $ReplacementText = (Get-Help -Detailed $_).Synopsis - $ModulePageFileContent = $ModulePageFileContent -replace $TextToReplace, $ReplacementText ` - -replace '00000000-0000-0000-0000-000000000000', ($Script:Manifest.Guid).ToString() - } + $ModulePageFileContent = $ModulePageFileContent -replace '{{ Fill in the Description }}', $Script:Manifest.Description -replace '00000000-0000-0000-0000-000000000000', ($Script:Manifest.Guid).ToString() $ModulePageFileContent | Out-File $ModulePage -Force -Encoding $Script:BuildEnv.Encoding $MissingDocumentation = Select-String -Path "$($StageReleasePath)\docs\*.md" -Pattern "({{.*}})" @@ -534,6 +350,13 @@ task CreateMarkdownHelp GetPublicFunctions, { throw 'Missing documentation. Please review and rebuild.' } + # If modules where unloaded, reload them with PSDepend + if ($TempDeload) { + while ($(Get-Module $Script:BuildEnv.ModuleToBuild)) { + Get-Module $Script:BuildEnv.ModuleToBuild | Remove-Module + } + Invoke-PSDepend -Path $(Join-Path $BuildToolPath 'dependencies\PSDepend\build.depend.psd1') -Import -Force + } } # Synopsis: Build the markdown help files with PlatyPS @@ -564,79 +387,45 @@ task CreateUpdateableHelpCAB { $null = New-ExternalHelpCab -CabFilesFolder "$($StageReleasePath)\en-US\" -LandingPagePath $LandingPage -OutputFolder "$($StageReleasePath)\en-US\" @PlatyPSVerbose } -# Synopsis: Build help files for module and ignore missing section errors -task TestCreateHelp Configure, CreateMarkdownHelp, CreateExternalHelp, CreateUpdateableHelpCAB, { - Write-Description White 'Create help files' -accent -} - -# Synopsis: Create a new version release directory for our release and copy our contents to it -task PushVersionRelease { - Write-Description White "Attempting to push a version release of the module" -accent - - $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder - $ThisReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleVersion - $ThisBuildReleasePath = ".\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleVersion)" - $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - - $null = Remove-Item $ThisReleasePath -Force -Recurse -ErrorAction 0 - $null = New-Item $ThisReleasePath -ItemType:Directory -Force - Copy-Item -Path "$($StageReleasePath)\*" -Destination $ThisReleasePath -Recurse - #Out-Zip $StageReleasePath (Join-Path $ReleasePath "$($Script:BuildEnv.ModuleToBuild)-$($Script:BuildEnv.ModuleVersion).zip") -overwrite -} - -# Synopsis: Create the current release directory and copy this build to it. -task PushCurrentRelease { - Write-Description White "Attempting to push a current release of the module" -accent - - $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder - $CurrentReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleToBuild - $ThisBuildCurrentReleasePath = ".\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleToBuild)" - $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - - $MostRecentRelease = (Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -like "*.*.*"} | Select-Object Name).name | ForEach-Object {[version]$_} | Sort-Object -Descending | Select-Object -First 1 - $ProcessCurrentRelease = $true - if ($MostRecentRelease) { - if ($MostRecentRelease -gt [version]$Script:BuildEnv.ModuleVersion) { - $ProcessCurrentRelease = $false - } - } - if ($ProcessCurrentRelease -or $Force -or $Script:BuildEnv.Force) { - Write-Description White "Pushing a version release to $($ThisBuildCurrentReleasePath)" -level 2 - $null = Remove-Item $CurrentReleasePath -Force -Recurse -ErrorAction 0 - $null = New-Item $CurrentReleasePath -ItemType:Directory -Force - Copy-Item -Path "$($StageReleasePath)\*" -Destination $CurrentReleasePath -Recurse -force - Out-Zip $StageReleasePath "$ReleasePath\$($Script:BuildEnv.ModuleToBuild)-current.zip" -overwrite - } - else { - Write-Warning 'Unable to push this version as a current release as it is not the most recent version in the release directory! Re-run this task with the -Force flag to overwrite it.' - } -} - -# Synopsis: Populate the function markdown help for the project documentation -task CreateProjectFunctionHelp LoadRequiredModules, { - Write-Description White 'Creating markdown documentation with PlatyPS for the core project' -accent - if ((Test-Path $Script:CurrentReleasePath)) { - $OnlineModuleLocation = "$($Script:BuildEnv.ModuleWebsite)/tree/master/docs" - - $FwLink = "$($OnlineModuleLocation)/docs/Functions/$($Script:BuildEnv.ModuleToBuild).md" - $ProjectDocPath = Join-Path $BuildRoot 'docs\Functions\' - - # Create the function .md files for the core project documentation - $null = New-MarkdownHelp -module $Script:BuildEnv.ModuleToBuild -OutputFolder $ProjectDocPath -Force -Locale 'en-US' -FwLink $FwLink -HelpVersion $Script:BuildEnv.ModuleVersion -Encoding ([System.Text.Encoding]::($Script:BuildEnv.Encoding)) #-OnlineVersionUrl "$($Script:BuildEnv.ModuleWebsite)/docs/Functions" - } - else { - Write-Build Yellow 'There is no current release to pull the documents from. First build the project at least once.' -Level 2 - } -} - # Synopsis: Build the markdown help for the functions using PlatyPS for the core project docs. task BuildProjectHelpFiles { Write-Description White 'Creating markdown documentation with PlatyPS for the core project' -accent + $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder + $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder + $OnlineModuleLocation = "$($Script:BuildEnv.ModuleWebsite)/$($Script:BuildEnv.BaseReleaseFolder)" $FwLink = "$($OnlineModuleLocation)/docs/Functions/$($Script:BuildEnv.ModuleToBuild).md" + # If the current module we are building is also loaded as a dependency with PSDepend, + # Unload it temporally and reload it from the scratchpath to build markdown files from. + if (Get-Module $Script:BuildEnv.ModuleToBuild | Where-Object {$_.Path -like "*PSDepend\$($Script:BuildEnv.ModuleToBuild)*"}) { + $TempDeload = $true + #Write-Verbose "$($(Get-Module $Script:BuildEnv.ModuleToBuild).Path)" + #Get-Command | Where-Object { $_.source -eq 'ModuleBuildTools'} | ForEach-Object { + # Remove-Item -Path Function:\$($_.Name) + #} + #while ($(Get-Module $Script:BuildEnv.ModuleToBuild)) { + Get-Module $Script:BuildEnv.ModuleToBuild | Remove-Module + #} + $TempModule = Join-Path $ScratchPath "$($Script:BuildEnv.ModuleToBuild).psd1" + try { + [void](Import-Module $TempModule -Force) + } + catch { + throw "Unable to load the project module: $($TempModule)" + } + } + # Create the function .md files for the core project documentation $null = New-MarkdownHelp -module $Script:BuildEnv.ModuleToBuild -OutputFolder "$($BuildRoot)\docs\Functions\" -Force -Locale 'en-US' -FwLink $FwLink -HelpVersion $Script:BuildEnv.ModuleVersion -Encoding ([System.Text.Encoding]::($Script:BuildEnv.Encoding)) #-OnlineVersionUrl "$($Script:BuildEnv.ModuleWebsite)/docs/Functions" + + # If modules where unloaded, reload them with PSDepend + if ($TempDeload) { + while ($(Get-Module $Script:BuildEnv.ModuleToBuild)) { + Get-Module $Script:BuildEnv.ModuleToBuild | Remove-Module + } + Invoke-PSDepend -Path $(Join-Path $BuildToolPath 'dependencies\PSDepend\build.depend.psd1') -Import -Force + } } # Synopsis: Add additional doc files to the final project document folder @@ -670,11 +459,10 @@ task CreateReadTheDocsYML -if {$Script:BuildEnv.OptionGenerateReadTheDocs} Confi $DocsReleasePath = Join-Path $Script:BuildEnv.BaseReleaseFolder $Script:BuildEnv.ModuleToBuild $ProjectDocsPath = Join-Path $BuildRoot 'docs' - $ProjectFunctionDocsPath = Join-Path $ProjectDocsPath 'Functions' + $YMLFile = Join-Path $BuildRoot 'mkdocs.yml' if (-not (Test-Path $YMLFile)) { - # If the yml file doesn't exist then go ahead and create it from scratch $Pages = @() $RTDFolders = Get-ChildItem -Path $ProjectDocsPath -Directory | Sort-Object -Property Name @@ -704,81 +492,251 @@ task CreateReadTheDocsYML -if {$Script:BuildEnv.OptionGenerateReadTheDocs} Confi repo_url = $Script:BuildEnv.ModuleWebsite use_directory_urls = $false theme = "readthedocs" - copyright = "$($Script:BuildEnv.ModuleToBuild) is licensed under this license" + copyright = "$($Script:BuildEnv.ModuleToBuild) is licensed under this license" pages = $Pages } - $RTD | ConvertTo-Yaml | Out-File -Encoding $Script:BuildEnv.Encoding -FilePath $YMLFile -Force + $RTD | ConvertTo-Yaml | Out-File -Encoding $Script:BuildEnv.Encoding -FilePath $YMLFile -Force } else { - Write-Warning 'The mkdocs.yml file already exists. If you want this regenerated you will need to delete it first.' + Write-Warning "Skipping ReadTheDocs manifest file creation as it already exists. Please remove the following file if you want it to be regenerated: $YMLFile" } } -# Synopsis: Put together all the various projecet help files -task CreateProjectHelp BuildProjectHelpFiles, AddAdditionalDocFiles, UpdateReadTheDocs, CreateReadTheDocsYML +# Synopsis: Replace comment based help with external help in all public functions for this project +task UpdateCBH { + Write-Description White 'Updating Comment Based Help in functions to point to external help links' -accent -# Synopsis: Push the current release of the project to PSScriptGallery -task PublishPSGallery LoadBuildTools, InstallModule, { - Write-Description White 'Publishing recent module release to the PowerShell Gallery' -accent + $ExternalHelp = @" +<# + .EXTERNALHELP $($Script:BuildEnv.ModuleToBuild)-help.xml + .LINK + {{LINK}} + #> +"@ - $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder - $CurrentReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleToBuild - if (Get-Module $Script:BuildEnv.ModuleToBuild) { - # If the module is already loaded then unload it. - Remove-Module $Script:BuildEnv.ModuleToBuild + $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder + $CBHPattern = "(?ms)(\<`#.*\.SYNOPSIS.*?`#>)" + Get-ChildItem -Path "$($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)\*.ps1" -File | ForEach-Object { + $FormattedOutFile = $_.FullName + $FileName = $_.Name + Write-Description White "Replacing CBH in file: $($FileName)" -level 2 + $FunctionName = $FileName -replace '.ps1', '' + $NewExternalHelp = $ExternalHelp -replace '{{LINK}}', ($Script:BuildEnv.ModuleWebsite + "/tree/master/$($Script:BuildEnv.BaseReleaseFolder)/$($Script:BuildEnv.ModuleVersion)/docs/$($FunctionName).md") + $UpdatedFile = (get-content $FormattedOutFile -raw) -replace $CBHPattern, $NewExternalHelp + $UpdatedFile | Out-File -FilePath $FormattedOutFile -force -Encoding $Script:BuildEnv.Encoding } +} - # Try to import the current module - $CurrentModule = Join-Path $CurrentReleasePath "$($Script:BuildEnv.ModuleToBuild).psd1" - if (Test-Path $CurrentModule) { - Import-Module -Name $CurrentModule +# Synopsis: Update public functions to include a template comment based help. +task InsertCBHInPublicFunctions { + Write-Description White "Attempting to insert comment based help into functions" -accent - Write-Description White "Uploading project to PSGallery: $($Script:BuildEnv.ModuleToBuild)" - Upload-ProjectToPSGallery -Name $Script:BuildEnv.ModuleToBuild -NuGetApiKey $Script:BuildEnv.NuGetApiKey - } + $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder + $CBHPattern = "(?ms)(^\s*\<`#.*\.SYNOPSIS.*?`#>)" + $CBHUpdates = 0 - else { - Write-Warning "Unable to publish the module as a current release is not available in $CurrentModule" + # Create the directories + $null = New-Item (Join-Path $ScratchPath "src") -ItemType:Directory -Force + $null = New-Item (Join-Path $ScratchPath $Script:BuildEnv.PublicFunctionSource) -ItemType:Directory -Force + Get-ChildItem (Join-Path $BuildRoot $Script:BuildEnv.PublicFunctionSource) -Filter *.ps1 | ForEach-Object { + $FileName = $_.Name + $FullFilePath = $_.FullName + Write-Description White "Public function - $($FileName)" -level 2 + $currscript = Get-Content $FullFilePath -Raw + $CBH = $currscript | New-MBTCommentBasedHelp + $currscriptblock = [scriptblock]::Create($currscript) + . $currscriptblock + if([string]::IsNullOrEmpty($CBH)) + { + Write-Error "Could not Add CBH, possibly duo having no parameters in $filename" -ErrorAction Stop + } else { + $currfunct = get-command $CBH.FunctionName + if ($currfunct.definition -notmatch $CBHPattern) { + $CBHUpdates++ + Write-Description White "Inserting template CBH and writing to : $($Script:BuildEnv.ScratchFolder)\$($Script:BuildEnv.PublicFunctionSource)\$($FileName)" -Level 3 + $UpdatedFunct = 'Function ' + $currfunct.Name + ' {' + "`r`n" + $CBH.CBH + "`r`n" + $currfunct.definition + "`r`n" + '}' + $UpdatedFunct | Out-File "$($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)\$($FileName)" -Encoding $Script:BuildEnv.Encoding -force + } + else { + Write-Description Yellow 'Comment based help already exists!' -Level 2 + } + } + Remove-Item Function:\$($currfunct.Name) + } + Write-Build White '' + Write-Build Yellow '****************************************************************************************************' + Write-Build Yellow " Updated Functions: $CBHUpdates" + if ($CBHUpdates -gt 0) { + Write-Build White '' + Write-Build Yellow " Updated Function Location: $($ScratchPath)\$($Script:BuildEnv.PublicFunctionSource)" + Write-Build White '' + Write-Build Yellow " NOTE: Please inspect these files closely. If they look good merge them back into your project" } + Write-Build Yellow '****************************************************************************************************' + $null = Read-Host 'Press Enter to continue...' } -# Synopsis: Update the current build patch version -task AutoIncreaseVersionBuildLevel -after PublishPSGallery -if {$Script:BuildEnv.OptionUpdateVersionAfterPublishing} { - Write-Description White 'Attempting to update the module build version' -accent +# Synopsis: Put together all the various project help files +task CreateProjectHelp BuildProjectHelpFiles, AddAdditionalDocFiles, UpdateReadTheDocs, CreateReadTheDocsYML + +# Synopsis: Build help files for module +task CreateHelp CreateMarkdownHelp, CreateExternalHelp, CreateUpdateableHelpCAB, CreateProjectHelp, AddAdditionalDocFiles +#endregion + +#region Prepair module release +# Synopsis: Assemble the module for release +task CreateModulePSM1 RemoveScriptSignatures, UpdateCBH, { + Write-Description White "Assembling module psm1 file" -Accent + $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder + $StageReleasePath = Join-Path $ScratchPath $Script:BuildEnv.BaseReleaseFolder + $ReleaseModule = "$($StageReleasePath)\$($Script:BuildEnv.ModuleToBuild).psm1" + + if ($Script:BuildEnv.OptionCombineFiles) { + Write-Description White "Option to combine PSM1 is enabled, combining source files now..." -Level 2 + + $CombineFiles = '' + $PreloadFilePath = (Join-Path $ScratchPath "$($Script:BuildEnv.OtherModuleSource)\PreLoad.ps1") + if (Test-Path $PreloadFilePath) { + Write-Description White 'Starting with Preload.ps1' -Level 3 + $CombineFiles += "## Pre-Loaded Module code ##`r`n`r`n" + Get-childitem $PreloadFilePath | ForEach-Object { + $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" + } + } + Write-Description White 'Adding the private source files:' -Level 3 + + $CombineFiles += "## PRIVATE MODULE FUNCTIONS AND DATA ##`r`n`r`n" + + Get-childitem (Join-Path $ScratchPath "$($Script:BuildEnv.PrivateFunctionSource)\*.ps1") | ForEach-Object { + Write-Description White $_.Name -Level 4 + $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" + } + + Write-Description White 'Adding the public source files:' -Level 3 + + $CombineFiles += "## PUBLIC MODULE FUNCTIONS AND DATA ##`r`n`r`n" + Get-childitem (Join-Path $ScratchPath "$($Script:BuildEnv.PublicFunctionSource)\*.ps1") | ForEach-Object { + Write-Description White $_.Name -Level 4 + $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" + } + + Write-Description White 'Finishing with Postload.ps1' -Level 3 + $CombineFiles += "## Post-Load Module code ##`r`n`r`n" + $PostLoadPath = (Join-Path $ScratchPath "$($Script:BuildEnv.OtherModuleSource)\PostLoad.ps1") + if (Test-Path $PostLoadPath) { + Get-childitem $PostLoadPath | ForEach-Object { + $CombineFiles += (Get-content $_ -Raw) + "`r`n`r`n" + } + } + Set-Content -Path $ReleaseModule -Value $CombineFiles -Encoding $Script:BuildEnv.Encoding + } + else { + Write-Description White 'Option to combine PSM1 is NOT enabled, copying over file structure now...' -Level 2 + Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.OtherModuleSource) -Recurse -Destination $StageReleasePath -Force + Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.PrivateFunctionSource) -Recurse -Destination $StageReleasePath -Force + Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.PublicFunctionSource) -Recurse -Destination $StageReleasePath -Force + Copy-Item -Path (Join-Path $ScratchPath $Script:BuildEnv.ModuleToBuild) -Destination $StageReleasePath -Force + } + + if (($Script:BuildEnv.AdditionalModulePaths).Count -gt 0) { + Write-Description White 'Copying over additional module paths now (if they exist).' -Level 2 + $Script:BuildEnv.AdditionalModulePaths | ForEach-Object { + if (Test-Path $_) { + Write-Description White "Copying $_" -Level 3 + Copy-Item -Path $_ -Recurse -Destination $StageReleasePath -Force + } + } + } +} +# Synopsis: Create new module manifest with explicit function exports included +task CreateModuleManifest CreateModulePSM1, { + Write-Description White 'Module manifest file updates' -accent $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" - $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder - $AllReleases = @((Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -match '^([0-9].[0-9].[0-9])$'} | Select-Object).Name | ForEach-Object {[version]$_}) + $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - $NewestRelease = $AllReleases | Sort-Object -Descending | Select-Object -First 1 - $ProposedNewRelease = [version]("$($NewestRelease.Major).$($NewestRelease.Minor).$($NewestRelease.Build + 1)") + $PSD1OutputFile = Join-Path $StageReleasePath "$($Script:BuildEnv.ModuleToBuild).psd1" + $ThisPSD1OutputFile = "$BuildRoot\$($Script:BuildEnv.ScratchFolder)\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleToBuild).psd1" + Write-Description White "Attempting to update the release module manifest file: $ThisPSD1OutputFile" -Level 2 + $null = Copy-Item -Path $ModuleManifestFullPath -Destination $PSD1OutputFile -Force + Update-ModuleManifest -Path $PSD1OutputFile -FunctionsToExport $Script:FunctionsToExport +} +# Synopsis: Run PSScriptAnalyzer against the assembled module +task AnalyzeModuleRelease -if {$Script:BuildEnv.OptionAnalyzeCode} { + Write-Description White 'Analyzing the project with ScriptAnalyzer' -accent + $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder + $Analysis = Invoke-ScriptAnalyzer -Path $StageReleasePath -Settings (Join-Path $BuildRoot "PSScriptAnalyzerSettings.psd1") + if ($Analysis.Count) { + Write-Description White "Note that this was from the script analysis run against $StageReleasePath" -level 2 + Write-Description Red "$($Analysis.Count) linting errors or warnings were found:" -level 2 + $Analysis | Format-Table -AutoSize + Write-Error "$($Analysis.Count) linting errors or warnings were found. The build cannot continue." -ErrorAction Stop + } +} +# Synopsis: Create a new version release directory for our release and copy our contents to it +task PushVersionRelease { + Write-Description White "Attempting to push a version release of the module" -accent - $Script:BuildEnv.ModuleVersion = $ProposedNewRelease.ToString() + $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder + $ThisReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleVersion + $ThisBuildReleasePath = ".\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleVersion)" + $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - Write-Description White 'Saving persistent build file with new module version' -level 2 - Save-BuildData + $null = Remove-Item $ThisReleasePath -Force -Recurse -ErrorAction 0 + $null = New-Item $ThisReleasePath -ItemType:Directory -Force + Copy-Item -Path "$($StageReleasePath)\*" -Destination $ThisReleasePath -Recurse + #Out-MBTZip $StageReleasePath (Join-Path $ReleasePath "$($Script:BuildEnv.ModuleToBuild)-$($Script:BuildEnv.ModuleVersion).zip") -overwrite +} +# Synopsis: Create the current release directory and copy this build to it. +task PushCurrentRelease { + Write-Description White "Attempting to push a current release of the module" -accent - $NewReleaseNotes = "$($ProposedNewRelease.ToString()) release" + $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder + $CurrentReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleToBuild + $ThisBuildCurrentReleasePath = ".\$($Script:BuildEnv.BaseReleaseFolder)\$($Script:BuildEnv.ModuleToBuild)" + $StageReleasePath = Join-Path (Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder) $Script:BuildEnv.BaseReleaseFolder - Write-Description White 'Updating module manifest with new module version' -level 2 - try { - Update-ModuleManifest -Path $ModuleManifestFullPath -ModuleVersion $ProposedNewRelease -ReleaseNotes $NewReleaseNotes + $MostRecentRelease = (Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -like "*.*.*"} | Select-Object Name).name | ForEach-Object {[version]$_} | Sort-Object -Descending | Select-Object -First 1 + $ProcessCurrentRelease = $true + if ($MostRecentRelease) { + if ($MostRecentRelease -gt [version]$Script:BuildEnv.ModuleVersion) { + $ProcessCurrentRelease = $false + } } - catch { - throw $_ + if ($ProcessCurrentRelease -or $Force -or $Script:BuildEnv.Force) { + Write-Description White "Pushing a version release to $($ThisBuildCurrentReleasePath)" -level 2 + $null = Remove-Item $CurrentReleasePath -Force -Recurse -ErrorAction 0 + $null = New-Item $CurrentReleasePath -ItemType:Directory -Force + Copy-Item -Path "$($StageReleasePath)\*" -Destination $CurrentReleasePath -Recurse -force + Out-MBTZip $StageReleasePath "$ReleasePath\$($Script:BuildEnv.ModuleToBuild)-current.zip" -overwrite + } + else { + Write-Warning 'Unable to push this version as a current release as it is not the most recent version in the release directory! Re-run this task with the -Force flag to overwrite it.' } +} +#endregion - Write-Description Green "Module build version has been updated to $($ProposedNewRelease.ToString())" -level 2 +#region Postbuild PSGallery and Version management +# Synopsis: Run post-build scripts (such as file transformations or deployments) +task PostBuildTasks { + Write-Description White 'Running any Post-Build scripts' -accent + $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder + $CleanupPath = Join-Path $BuildToolPath 'shutdown' + # Dot source any post build cleanup scripts. + Get-ChildItem -Path $CleanupPath -Recurse -Filter "*.ps1" -File | Foreach { + Write-Description White "Dot sourcing shutdown script file: $($_.Name)" -Level 3 + . $_.FullName + } } # Synopsis: Install the current built module to the local machine -task InstallModule VersionCheck, LoadBuildTools, { +task InstallModule VersionCheck, { Write-Description White "Attempting to install the current module" -accent $CurrentModulePath = Join-Path $Script:BuildEnv.BaseReleaseFolder $Script:BuildEnv.ModuleVersion assert (Test-Path $CurrentModulePath) 'The current version module has not been built yet!' - $MyModulePath = "$((Get-SpecialPaths)['MyDocuments'])\WindowsPowerShell\Modules\" + $MyModulePath = "$((Get-MBTSpecialPath)['MyDocuments'])\WindowsPowerShell\Modules\" $ModuleInstallPath = "$($MyModulePath)$($Script:BuildEnv.ModuleToBuild)" if (Test-Path $ModuleInstallPath) { Write-Description White "Removing installed module $($Script:BuildEnv.ModuleToBuild)" -Level 2 @@ -798,7 +756,7 @@ task InstallModule VersionCheck, LoadBuildTools, { } # Synopsis: Test import the current module -task TestInstalledModule VersionCheck, { +task TestImportInstalledModule VersionCheck, { Write-Description White "Test importing the current module version $($Script:BuildEnv.ModuleVersion)" -accent $InstalledModules = @(Get-Module -ListAvailable $Script:BuildEnv.ModuleToBuild) @@ -809,58 +767,120 @@ task TestInstalledModule VersionCheck, { Import-Module -Name $Script:BuildEnv.ModuleToBuild -MinimumVersion $Script:BuildEnv.ModuleVersion -Force } -# Synopsis: Run pre-build scripts (such as other builds). Should be run as a subtask of the configure task. -task PreBuildTasks { - Write-Description White 'Running any Pre-Build scripts' -accent - $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder - $PreBuildPath = Join-Path $BuildToolPath 'startup' - # Dot source any pre build scripts. - Get-ChildItem -Path $PreBuildPath -Recurse -Filter "*.ps1" -File | Foreach { - Write-Description White "Dot sourcing pre-build script file: $($_.Name)" -level 2 - . $_.FullName +# Synopsis: Push the current release of the project to PSScriptGallery +task PublishPSGallery LoadRequiredModules, InstallModule, { + Write-Description White 'Publishing recent module release to the PowerShell Gallery' -accent + + $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder + $CurrentReleasePath = Join-Path $ReleasePath $Script:BuildEnv.ModuleToBuild + if (Get-Module $Script:BuildEnv.ModuleToBuild) { + Write-Description White "This module is already installed $($Script:BuildEnv.ModuleToBuild). Removing it" -Level 2 + Write-Verbose "$($(Get-Module $Script:BuildEnv.ModuleToBuild).Path)" + Get-Module $Script:BuildEnv.ModuleToBuild | ForEach-Object { + Remove-Module $_.Name + } } -} -# Synopsis: Run post-build scripts (such as file transformations or deployments) -task PostBuildTasks { - Write-Description White 'Running any Post-Build scripts' -accent - $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder - $CleanupPath = Join-Path $BuildToolPath 'shutdown' - # Dot source any post build cleanup scripts. - Get-ChildItem -Path $CleanupPath -Recurse -Filter "*.ps1" -File | Foreach { - Write-Description White "Dot sourcing shutdown script file: $($_.Name)" -Level 3 - . $_.FullName + # Try to import the current module + $CurrentModule = Join-Path $CurrentReleasePath "$($Script:BuildEnv.ModuleToBuild).psd1" + if (Test-Path $CurrentModule) { + Import-Module -Name $CurrentModule + + Write-Description White "Uploading project to PSGallery: $($Script:BuildEnv.ModuleToBuild)" + Publish-MBTProjectToPSGallery -Name $Script:BuildEnv.ModuleToBuild -NuGetApiKey $Script:BuildEnv.NuGetApiKey -RequiredVersion $Script:BuildEnv.ModuleVersion + } + + else { + Write-Warning "Unable to publish the module as a current release is not available in $CurrentModule" } } -# Synopsis: Remove session artifacts like loaded modules and variables -task BuildSessionCleanup LoadRequiredModules, { - Write-Description White 'Cleaning up the build session' -accent +# Synopsis: Set a new version of the module +task NewVersion LoadRequiredModules, LoadModuleManifest, { + Write-Description White 'Updating module build version' -accent + $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" + $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder + $AllReleases = @((Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -match '^([0-9].[0-9].[0-9])$'} | Select-Object).Name | ForEach-Object {[version]$_}) - $BuildToolPath = Join-Path $BuildRoot $Script:BuildEnv.BuildToolFolder - # Clean up loaded modules if they are loaded - Write-Description White "Removing modules" -Level 2 - $Script:RequiredModules | Foreach-Object { - Write-Description White "Removing $($_) module (if loaded)." -Level 3 - Remove-Module $_ -Erroraction Ignore + # if a new version wasn't passed as a parameter then prompt for one + if ($null -eq $NewVersion) { + do { + $NewVersion = Read-Host -Prompt 'Enter the version (ie 0.0.8) for your build release' + if ([string]::IsNullOrEmpty($NewVersion)) { + Write-Build Red "You need to enter a valid module version (ie. 1.2.0)" -level 2 + } + } while ( [string]::IsNullOrEmpty($NewVersion) -and ($NewVersion -notmatch '\d+\.\d+\.\d+') ) + } + if ($AllReleases -contains $NewVersion) { + if ((-not $Force) -and (-not $Script:BuildEnv.Force)) { + throw 'The module version already has been released (the folder exists within the releases folder. In order to set your build project to this version you will need to pass the -Force switch to this build script with the -NewVersion parameter' + } } - Write-Description White "Removing $($Script:BuildEnv.ModuleToBuild) module (if loaded)." -Level 3 - Remove-Module $Script:BuildEnv.ModuleToBuild -Erroraction Ignore + $Script:BuildEnv.ModuleVersion = $NewVersion.ToString() - if ($Script:BuildEnv.OptionTranscriptEnabled) { - Stop-Transcript -WarningAction:Ignore + Write-Description White 'Saving persistent build file with new module version' -level 2 + Save-BuildData + + Write-Description White 'Updating module manifest with new module version' -level 2 + try { + Update-ModuleManifest -Path $ModuleManifestFullPath -ModuleVersion $NewVersion + } + catch { + throw $_ } } -# Synopsis: Push with a version tag. -task GitPushRelease VersionCheck, { - $changes = exec { git status --short } - assert (-not $changes) "Please, commit changes." +# Synopsis: Update current module manifest with the version defined in the build config file (if they differ) +task UpdateRelease NewVersion, LoadRequiredModules, LoadModuleManifest, { + Write-Description White 'Updating the release notes of this module' -accent - exec { git push } - exec { git tag -a "v$($Script:BuildEnv.ModuleVersion)" -m "v$($Script:BuildEnv.ModuleVersion)" } - exec { git push origin "v$($Script:BuildEnv.ModuleVersion)" } + $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" + # If there is a version mismatch then we need to put in some release notes and update the module manifest + if ($null -eq $ReleaseNotes) { + do { + $ReleaseNotes = Read-Host -Prompt 'Enter brief release notes for this new version' + if ([string]::IsNullOrEmpty($ReleaseNotes)) { + Write-Build Red "You need to enter some kind of notes for your new release to update the manifest with!" -level 2 + } + } while ([string]::IsNullOrEmpty($ReleaseNotes)) + } + + try { + Update-ModuleManifest -Path $ModuleManifestFullPath -ReleaseNotes $ReleaseNotes + } + catch { + throw $_ + } +} + +# Synopsis: Update the current build patch version +task AutoIncreaseVersionBuildLevel -after PublishPSGallery -if {$Script:BuildEnv.OptionUpdateVersionAfterPublishing} { + Write-Description White 'Attempting to update the module build version' -accent + + $ModuleManifestFullPath = Join-Path $BuildRoot "$($Script:BuildEnv.ModuleToBuild).psd1" + $ReleasePath = Join-Path $BuildRoot $Script:BuildEnv.BaseReleaseFolder + $AllReleases = @((Get-ChildItem $ReleasePath -Directory | Where-Object {$_.Name -match '^([0-9].[0-9].[0-9])$'} | Select-Object).Name | ForEach-Object {[version]$_}) + + $NewestRelease = $AllReleases | Sort-Object -Descending | Select-Object -First 1 + $ProposedNewRelease = [version]("$($NewestRelease.Major).$($NewestRelease.Minor).$($NewestRelease.Build + 1)") + + $Script:BuildEnv.ModuleVersion = $ProposedNewRelease.ToString() + + Write-Description White 'Saving persistent build file with new module version' -level 2 + Save-BuildData + + $NewReleaseNotes = "$($ProposedNewRelease.ToString()) release" + + Write-Description White 'Updating module manifest with new module version' -level 2 + try { + Update-ModuleManifest -Path $ModuleManifestFullPath -ModuleVersion $ProposedNewRelease -ReleaseNotes $NewReleaseNotes + } + catch { + throw $_ + } + + Write-Description Green "Module build version has been updated to $($ProposedNewRelease.ToString())" -level 2 } # Synopsis: Push to github @@ -877,17 +897,72 @@ task GithubPush VersionCheck, { assert (-not $changes) "Please, commit changes." } +# Synopsis: Push with a version tag. +task GitPushRelease VersionCheck, { + $changes = exec { git status --short } + assert (-not $changes) "Please, commit changes." + + exec { git push } + exec { git tag -a "v$($Script:BuildEnv.ModuleVersion)" -m "v$($Script:BuildEnv.ModuleVersion)" } + exec { git push origin "v$($Script:BuildEnv.ModuleVersion)" } +} +#endregion + +#region Cleanup +# Synopsis: Regenerate scratch staging directory +task CleanScratchDirectory { + Write-Description White "Clean up our scratch/staging directory $($Script:BuildEnv.ScratchFolder)" -accent + + $ScratchPath = Join-Path $BuildRoot $Script:BuildEnv.ScratchFolder + $null = Remove-Item $ScratchPath -Force -Recurse -ErrorAction 0 + $null = New-Item $ScratchPath -ItemType:Directory -Force +} + +# Synopsis: Remove session artifacts like loaded modules and variables +task BuildSessionCleanup CleanScratchDirectory, { + Write-Description White 'Cleaning up the build session' -accent + + Write-Description White "Removing $($Script:BuildEnv.ModuleToBuild) module (if loaded)." -Level 3 + Remove-Module $Script:BuildEnv.ModuleToBuild -Erroraction Ignore + + $Script:PSDependBuildModules | Foreach-Object { + Write-Description White "Removing $($_.DependencyName) module (if loaded)." -Level 3 + Remove-Module $_ -Erroraction Ignore -Force + } + + if ($Script:BuildEnv.OptionTranscriptEnabled) { + Stop-Transcript -WarningAction:Ignore + } +} +#endregion + +#region Main tasks +# Synopsis: Run all tests +task Tests RunMetaTests, RunUnitTests, RunIntergrationTests, { + +} # Synopsis: Build the module -task . Configure, CodeHealthReport, Clean, PrepareStage, GetPublicFunctions, SanitizeCode, CreateHelp, CreateModulePSM1, CreateModuleManifest, AnalyzeModuleRelease, PushVersionRelease, PushCurrentRelease, CreateProjectHelp, PostBuildTasks, BuildSessionCleanup +task Build Configure, CodeHealthReport, PrepareStage, GetPublicFunctions, SanitizeCode, CreateHelp, CreateModulePSM1, CreateModuleManifest, AnalyzeModuleRelease, PushVersionRelease, PushCurrentRelease, CreateProjectHelp, PostBuildTasks, BuildSessionCleanup, { + +} + +# Synopsis: Test, Build, install and Test load the module. +task TestBuildAndInstallModule Tests, Build, InstallModule, TestImportInstalledModule, BuildSessionCleanup, { -# Synopsis: Install and test load the module. -task InstallAndTestModule InstallModule, TestInstalledModule +} + +# Synopsis: Test, Build, Install, Test load and Publish the module +task BuildInstallTestAndPublishModule TestBuildAndInstallModule, PublishPSGallery, BuildSessionCleanup, { + +} + +# Synopsis: Insert Comment Based Help where it doesn't already exist (output to scratch directory) +task AddMissingCBH Configure, CleanScratchDirectory, InsertCBHInPublicFunctions, BuildSessionCleanup, { + +} -# Synopsis: Build, Install, and Test the module -task BuildInstallAndTestModule Configure, CodeHealthReport, Clean, PrepareStage, GetPublicFunctions, SanitizeCode, CreateHelp, CreateModulePSM1, CreateModuleManifest, AnalyzeModuleRelease, PushVersionRelease, PushCurrentRelease, CreateProjectHelp, InstallModule, TestInstalledModule, PostBuildTasks, BuildSessionCleanup +# Synopsis: Default task when running Invoke-Build +task . Build +#endregion -# Synopsis: Build, Install, Test, and Publish the module -task BuildInstallTestAndPublishModule Configure, CodeHealthReport, Clean, PrepareStage, GetPublicFunctions, SanitizeCode, CreateHelp, CreateModulePSM1, CreateModuleManifest, AnalyzeModuleRelease, PushVersionRelease, PushCurrentRelease, CreateProjectHelp, InstallModule, TestInstalledModule, PublishPSGallery, PostBuildTasks, BuildSessionCleanup -# Synopsis: Instert Comment Based Help where it doesn't already exist (output to scratch directory) -task InsertMissingCBH Configure, Clean, UpdateCBHtoScratch, BuildSessionCleanup \ No newline at end of file diff --git a/ModuleBuild.psd1 b/ModuleBuild.psd1 index c21623b..4445756 100644 --- a/ModuleBuild.psd1 +++ b/ModuleBuild.psd1 @@ -1,9 +1,9 @@ # -# Module manifest for module 'PSGet_ModuleBuild' +# Module manifest for module 'ModuleBuild' # # Generated by: Zachary Loeber # -# Generated on: 2/4/2018 +# Generated on: 4/25/2020 # @{ @@ -12,25 +12,25 @@ RootModule = 'ModuleBuild.psm1' # Version number of this module. -ModuleVersion = '0.2.3' +ModuleVersion = '0.3.1' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module -GUID = '8f6090b4-6411-4949-a717-96d64a1cc5b3' +GUID = '2b53a392-3b0f-4a7c-b934-7c995252070e' # Author of this module Author = 'Zachary Loeber' # Company or vendor of this module -CompanyName = 'Zachary Loeber' +CompanyName = '' # Copyright statement for this module -Copyright = '(c) 2017 Zachary Loeber. All rights reserved.' +Copyright = '(c) 2020 Zachary Loeber. All rights reserved.' # Description of the functionality provided by this module -Description = 'A scaffolding framework which can be used to kickstart a generic PowerShell module project.' +Description = 'A scaffolding framework which can be used to kickstart a generic PowerShell module project. ' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0.0' @@ -69,10 +69,10 @@ PowerShellVersion = '5.0.0' # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. -FunctionsToExport = @() +FunctionsToExport = '*' # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. -CmdletsToExport = @() +CmdletsToExport = '*' # Variables to export from this module # VariablesToExport = @() @@ -98,7 +98,7 @@ PrivateData = @{ Tags = 'scaffold','Module','Invoke-Build' # A URL to the license for this module. - LicenseUri = 'https://github.com/zloeber/ModuleBuild/raw/master/license.md' + LicenseUri = 'https://github.com/zloeber/ModuleBuild/raw/master/LICENSE.md' # A URL to the main website for this project. ProjectUri = 'https://github.com/zloeber/ModuleBuild' @@ -107,19 +107,19 @@ PrivateData = @{ IconUri = 'https://github.com/zloeber/ModuleBuild/raw/master/src/other/powershell-project.png' # ReleaseNotes of this module - ReleaseNotes = '0.2.3 release' + ReleaseNotes = '0.3.1 release' # Prerelease string of this module # Prerelease = '' - # Flag to indicate whether the module requires explicit user acceptance for install/update + # Flag to indicate whether the module requires explicit user acceptance for install/update/save # RequireLicenseAcceptance = $false # External dependent modules of this module # ExternalModuleDependencies = @() } # End of PSData hashtable - + } # End of PrivateData hashtable # HelpInfo URI of this module diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 new file mode 100644 index 0000000..bde2636 --- /dev/null +++ b/PSScriptAnalyzerSettings.psd1 @@ -0,0 +1,26 @@ +@{ + IncludeRules=@('PSUseApprovedVerbs', + 'PSReservedCmdletChar', + 'PSReservedParams', + 'PSShouldProcess', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSUseSingularNouns', + 'PSMissingModuleManifestField', + 'PSAvoidDefaultValueSwitchParameter', + 'PSAvoidUsingCmdletAliases', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingEmptyCatchBlock', + 'PSUseCmdletCorrectly', + 'PSUseShouldProcessForStateChangingFunctions', + 'PSAvoidUsingPositionalParameters', + 'PSAvoidGlobalVars', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSAvoidUsingInvokeExpression', + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingComputerNameHardcoded', + 'PSAvoidUsingConvertToSecureStringWithPlainText', + 'PSUsePSCredentialType', + 'PSAvoidUsingUserNameAndPasswordParams', + 'PSDSC*' + ) +} \ No newline at end of file diff --git a/Readme.md b/README.md similarity index 85% rename from Readme.md rename to README.md index d3ea4f9..5dc73de 100644 --- a/Readme.md +++ b/README.md @@ -26,17 +26,20 @@ and import the module to your session to test, but not install this module. ## Features This build framework for PowerShell modules comes with several appealing baked in features which include; + - Fully portable project directory structure and build process. So portable that you can copy it to another PowerShell 5.0 capable system and it should run the same. - Automatically combine your public and private functions into one clean psm1 file at build time. - Automatically update your psd1 file with public functions at build time. - Automatically scan your module release with PSScriptAnalyzer - Automatically upload your script to the PowerShell Gallery (with appropriate API key) - Automatically create project documentation folder structure and yml definition file for ReadTheDocs.org integration +- Automatically start Pester tests during build process - Visual Studio Code integration (tasks) - Easy to manage build configuration with forward compatible design and easy to use commands - Includes ability to scan for sensitive terms (like your company domain name or other items that you may not want published) - Functions for importing public and private functions from other projects into a ModuleBuild project - Add new public functions to your project based on easy to create templates. + ## Documentation Visit the [ReadTheDocs.org documentation](http://modulebuild.readthedocs.io/en/latest/) that this module created a manifest for automatically. @@ -47,13 +50,17 @@ Please feel free to contribute by opening new issues or providing pull requests. For the best development experience, open this project as a folder in Visual Studio Code and ensure that the PowerShell extension is installed. -* [Visual Studio Code] -* [PowerShell Extension] +* [Visual Studio Code](https://code.visualstudio.com/) +* [PowerShell Extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.PowerShell) More contributing information can be found [here](https://github.com/zloeber/ModuleBuild/blob/master/docs/Contributing.md). +This module is tested with the PowerShell testing framework Pester. To run all tests, just start the included build script with the test param `.\Build.ps1 -test`. + ## Other Information -**Author:** [Zachary Loeber](https://www.the-little-things.net) +**Authors:** +- [Zachary Loeber](https://www.the-little-things.net) +- [Justin Perdok](https://github.com/justin-p) **Website:** https://github.com/zloeber/ModuleBuild diff --git a/Requirements.psd1 b/Requirements.psd1 new file mode 100644 index 0000000..61d6b47 --- /dev/null +++ b/Requirements.psd1 @@ -0,0 +1,7 @@ +@{ + InvokeBuild = @{ + version = 'latest' + source = 'PSGalleryModule' + target = 'CurrentUser' + } +} \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..b3c447a --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +# See http://www.appveyor.com/docs/appveyor-yml for many more options +# 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: + files: + - "**/*.md" + message: /updated readme.*|update readme.*s|update docs.*|update version.*|update appveyor.*/ + +build: false + +# Kick off the CI/CD pipeline +test_script: + - ps: . .\Build.ps1 -TestBuildAndInstallModule \ No newline at end of file diff --git a/build/ModuleBuild.buildenvironment.ps1 b/build/ModuleBuild.buildenvironment.ps1 index 911faa9..8923aed 100644 --- a/build/ModuleBuild.buildenvironment.ps1 +++ b/build/ModuleBuild.buildenvironment.ps1 @@ -3,9 +3,7 @@ param ( [switch]$ForcePersist ) <# - Update $Script:BuildEnv to suit your PowerShell module build. These variables get dot sourced into - the build at every run and are exported to an external xml file for persisting through possible build - engine upgrades. + Update $Script:BuildEnv to suit your PowerShell module build. If you make sufficient changes to your build environment json file (via the module commands or otherwise) you should update this as well so those that inherit your project in the future are able to rebuild it from scratch. #> # If the variable is already defined then essentially do nothing. @@ -15,21 +13,21 @@ if ((Get-Variable 'BuildEnv' -ErrorAction:SilentlyContinue) -eq $null) { $Script:BuildEnv = New-Object -TypeName PSObject -Property @{ FirstRun = $True Force = $False - ForceInstallModule = $true + ForceInstallModule = $False Encoding = 'utf8' ModuleToBuild = 'ModuleBuild' - ModuleVersion = '0.0.1' + ModuleVersion = '0.3.0' ModuleWebsite = 'https://github.com/zloeber/ModuleBuild' ModuleCopyright = "(c) $((get-date).Year.ToString()) Zachary Loeber. All rights reserved." ModuleLicenseURI = 'https://github.com/zloeber/ModuleBuild/LICENSE.md' ModuleTags = 'scaffold, Module, Invoke-Build' -split ',' ModuleAuthor = 'Zachary Loeber' - ModuleDescription = 'A scaffolding framework which can be used to kickstart a generic PowerShell module project.' + ModuleDescription = 'A scaffolding framework which can be used to kickstart a generic PowerShell module project. ' - # Options - These affect how your eventual build will be run. + # Options - These affect how your build will be run. OptionAnalyzeCode = $True OptionCodeHealthReport = $True - OptionCombineFiles = $True + OptionCombineFiles = $TRUE OptionTranscriptEnabled = $false OptionTranscriptLogFile = 'BuildTranscript.Log' @@ -38,33 +36,34 @@ if ((Get-Variable 'BuildEnv' -ErrorAction:SilentlyContinue) -eq $null) { # If you want to prescan and fail a build upon finding any proprietary strings # enable this option and define some strings. - OptionSanitizeSensitiveTerms = $True + OptionSanitizeSensitiveTerms = $False OptionSensitiveTerms = @($env:username, $env:userdomain, $env:userdnsdomain) | Where {$null -ne $_} OptionSensitiveTermsInitialized = $false # If you want to update your current module build version automatically # after a successful psgallery publish set this to $true - OptionUpdateVersionAfterPublishing = $true + OptionUpdateVersionAfterPublishing = $True # Additional paths in the source module which should be copied over to the final build release - AdditionalModulePaths = @('plaster','plugins') + AdditionalModulePaths = @('plugins') + # Generate a yml file in the root folder of this project for readthedocs.org integration OptionGenerateReadTheDocs = $True # Most of the following options you probably don't need to change - BaseSourceFolder = 'src' # Base source path - PublicFunctionSource = "src\public" # Public functions (to be exported by file name as the function name) - PrivateFunctionSource = "src\private" # Private function source - OtherModuleSource = "src\other" # Other module source - BaseReleaseFolder = 'release' # Releases directory. - BuildReportsFolder = 'buildreports' - BuildToolFolder = 'build' # Build tool path (these scripts are dot sourced) - ScratchFolder = 'temp' # Scratch path - this is where all our scratch work occurs. It will be cleared out at every run. - FunctionTemplates = "src\templates" # Location of function template files (*.tem) + BaseSourceFolder = 'src' # Base source path + PublicFunctionSource = "src\public" # Public functions (to be exported by file name as the function name) + PrivateFunctionSource = "src\private" # Private function source. + OtherModuleSource = "src\other" # Other module source. + BaseReleaseFolder = 'release' # Releases directory. + BuildToolFolder = 'build' # Build tool path. + BuildReportsFolder = 'build\reports' # Location where PSCodeHealth stores its reports. + ScratchFolder = 'temp' # Scratch path - this is where all our scratch work occurs. It will be cleared out at every run. + FunctionTemplates = "src\templates" # Location of function template files (*.tem) # If you will be publishing to the PowerShell Gallery you will need a Nuget API key (can get from the website) - # You should not actually enter this key here but should manually enter it in the ModuleBuild.buildenvironment.json file + # You should NOT enter this key here but rather manually enter it in the ModuleBuild.buildenvironment.json file with: Set-MBBuildEnvironment -NugetAPIKey '' - NugetAPIKey = $null + NugetAPIKey = '' } ######################################## @@ -119,4 +118,4 @@ if ((Get-Variable 'BuildEnv' -ErrorAction:SilentlyContinue) -eq $null) { Write-Verbose "Exporting the BuildEnv data!" $Script:BuildEnv | ConvertTo-Json | Out-File -FilePath $PersistentBuildFile -Encoding $Script:BuildEnv.Encoding -Force } -} \ No newline at end of file +} diff --git a/build/cleanup/README.md b/build/cleanup/README.md index ce64ff6..b10b410 100644 --- a/build/cleanup/README.md +++ b/build/cleanup/README.md @@ -1,2 +1,2 @@ -# Note +# Note Any ps1 files in this directory are run at the end of your build. These should only include post build clean up tasks. \ No newline at end of file diff --git a/build/dependencies/PSDepend/README.md b/build/dependencies/PSDepend/README.md new file mode 100644 index 0000000..27f4985 --- /dev/null +++ b/build/dependencies/PSDepend/README.md @@ -0,0 +1,3 @@ +# PSDepend + +PSDepend will use this folder to install build dependencies \ No newline at end of file diff --git a/build/dependencies/PSDepend/build.depend.psd1 b/build/dependencies/PSDepend/build.depend.psd1 new file mode 100644 index 0000000..9eb80a1 --- /dev/null +++ b/build/dependencies/PSDepend/build.depend.psd1 @@ -0,0 +1,26 @@ +@{ + PSDependOptions = @{ + Target = '$DependencyFolder' + AddToPath = $True + } + PSScriptAnalyzer = @{ + version = '1.18.3' + source = 'PSGalleryModule' + } + PlatyPS = @{ + version = '0.14.0' + source = 'PSGalleryModule' + } + 'Powershell-YAML' = @{ + version = '0.4.1' + source = 'PSGalleryModule' + } + PSCodeHealth = @{ + version = '0.2.26' + source = 'PSGalleryModule' + } + ModuleBuildTools = @{ + version = '0.0.1' + source = 'PSGalleryModule' + } +} \ No newline at end of file diff --git a/build/docs/Additional/Acknowledgements.md b/build/docs/Additional/Acknowledgements.md index 6ad626d..7b22f5f 100644 --- a/build/docs/Additional/Acknowledgements.md +++ b/build/docs/Additional/Acknowledgements.md @@ -2,7 +2,7 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloeber/ModuleBuild) -This project owes some acknowlegement to other projects. Here are some other authors or projects which have made this project possible. If you believe you or a project should be included in this list please let us know. +This project owes some acknowledgements to other projects. Here are some other authors or projects which have made this project possible. If you believe you or a project should be included in this list please let us know. [Invoke-Build](https://github.com/nightroman/Invoke-Build) - A kick ass build automation tool written in PowerShell. It is the primary engine behind this little project. @@ -10,6 +10,8 @@ This project owes some acknowlegement to other projects. Here are some other aut [PSCodeHealth](https://github.com/MathieuBuisson/PSCodeHealth) - PowerShell code health reporting. +[PSDepend](https://github.com/RamblingCookieMonster/PSDepend) - PowerShell Dependency Handler. + [Hitchhikers Guide to the PowerShell Pipeline](https://xainey.github.io/2017/powershell-module-pipeline/) [Write the Faq'n Manual](https://get-powershellblog.blogspot.com/2017/03/write-faq-n-manual-part1.html) diff --git a/build/docs/Additional/ChangeLog.md b/build/docs/Additional/ChangeLog.md index bfdc27c..c87f5ed 100644 --- a/build/docs/Additional/ChangeLog.md +++ b/build/docs/Additional/ChangeLog.md @@ -2,56 +2,96 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloeber/ModuleBuild) +## Version 0.3.0 + +- Fixed issue with NLog by updating PlaterContent.ps1 +- Fixed issue with Make.ps1 By adding --tags --always +- Fixed issue with Make.ps1. Write-Description -level 3 should be used instead of Write-Output +- Fixed issue with Get-SpecialPaths function by adding LoadBuildTools on InstallAndTestModule +- Fixed issue with -SourceModule parameter on Initialize-MBModuleBuild +- Fixed issue with the -AddCBH when there are no Params. +- Fixed issue with URLS in README.md. It was missing links to VSCode and the PS Extension. +- Fixed issue with the link to ChangeLog. ChangeLog in index.md was plural. +- Fixed issue that where caused when you where not using PlatyPS 0.12.0. +- Fixed issue with syntax highlight that was caused by the CommentBasedHelp function. +- Update ModuleBuild to adhere to the PSGallery ScriptAnalyzer ruleset. +- ModuleBuild no uses a standalone PSScriptAnalyzerSettings.psd1 +- Updated ModuleBuild/.vscode/settings.json to include default indent size, set indent to space and use the project specific PSScriptAnalyzerSettings.psd1 file. +- Build reports are now saved on the following path `build\reports\$ModuleVersion\`. +- Included functions that are used during the build process are moved to its own repository. +- ModuleBuild now uses PSDepend to manage Build dependencies. +- ModuleBuild now uses PSDepend to manage Plugins. +- Revamped included tests. +- Make.ps1 is no longer dotsourced but included in `MakePlasterManifest.ps1` +- Updated the build scripts to better support CI/CD. +- The included version of Plater is moved to the Plugin folder. +- Added a AppVeyor.yml template to the scaffold. +- All functions are renamed to avoid conflicts with other modules. +- Cleaned up repo. Deleted unused code. Renamed/Moved existing files. +- Updated documentation to reflect above changes. + ## Version 0.2.3 -- Added ability to pull in some basic information about an existing module manifest files when running initialize-modulebuild. + +- Added ability to pull in some basic information about an existing module manifest files when running Initialize-MBModuleBuild. - Fixed ReadTheDocs generation issues by updating the template to include build\docs\ReadTheDocs in the initialization process. - Eliminated any customization requirements within the modulename.build.ps1 script to help pave the way for easier modulebuild upgrades to projects. - Removed some superfluous code in the base build environment script around the RequiredModules variable. + ## Version 0.2.2 + - Updated vscode tasks.json to fix depreciated syntax. -- Fixed `-Force` switch processing on Add-PublicFunction to still create the function if the provided name is detected as plural. +- Fixed `-Force` switch processing on Add-MBPublicFunction to still create the function if the provided name is detected as plural. - Fixed [cleanup script modulebuild execution issue](https://github.com/zloeber/ModuleBuild/issues/5) by separating out the postbuildtask into its own Invoke-Build code block (I had done this a while ago and never rolled up the changes into the Plaster template). - Fixed a glaring issue with the PlatyPS output where any Guids were 00000000-0000-0000-0000-000000000000 instead of the actual module manifest Guid [reported via issue #6](https://github.com/zloeber/ModuleBuild/issues/6). This happens because we build the module help from the psm1 load of the module in memory, not the psd1 file as that psd1 manifest gets recreated at build time with the appropriate exported functions and such. Basically a chicken/egg scenario. For now we just manually replace the output markdown files with the correct Guid before moving on to the help file packaging. ## Version 0.2.1 + - Merged pull request #3 to resolve initialization issues for new modules in directories with spaces in their path. - Added additional github integration (via .github folder creation with pull and issue template markdown files) - Fixed improper spelling of 'license' in license creation templating. ## Version 0.1.10 -- Fixed the -InsertCBH build task. + +- Fixed the -AddCBH build task. - Fixed the missing documentation platyps output to show the actual found line that indicates missing CBH. ## Version 0.1.9 -- Removed plaster option to choose to combine the module source at build time (and simply made that behavior the default that can be changed later via Set-BuildEnvironment -OptionCombineFiles $false) + +- Removed plaster option to choose to combine the module source at build time (and simply made that behavior the default that can be changed later via Set-MBBuildEnvironment -OptionCombineFiles $false) - Added option to run a code health report (via PSCodeHealth) against your public and private function directories prior to starting the build - Added 'Module plugin' capability. This adds base functionality to the module project itself. The first included module plugin is the nlogmodule logging functionality. ## Version 0.1.6 + - Added New-PublicFunction to module along with a template folder and basic function template to start with. Any src\templates\\\.tem file is able to be used for this new feature and any build environment variable surrounded by double percentage symbols will be automatically replaced (ie. %%ModuleName%%). - Fixed ReadTheDocs yml creation issue with the licensing link. ## Version 0.1.5 + - Fixed awful .gitignore settings included in the default scaffolding - Fixed documentation links to be self-referencing -- Removed AdditionalModulePaths from initial plaster manifest (can just set this with set-buildenvironment after creation) +- Removed AdditionalModulePaths from initial plaster manifest (can just set this with Set-MBBuildEnvironment after creation) ## Version 0.1.4 + - Fixed invalid mkdocs.yml license link reference - Fixed invalid reference to acknowledgements folder in plaster manifest (thanks Roberto Desideri!) ## Version 0.1.3 + - Fixes to mkdocs.yml formatting - Fixed temp build directory exclusion in .gitignore file ## Version 0.1.2 + - Updated vs code task names -- Fixed an issue with a null build environment variable causing dynamic parameters in set-buildenvironment to fail +- Fixed an issue with a null build environment variable causing dynamic parameters in Set-MBBuildEnvironment to fail - Several small scaffolding clean ups. ## Version 0.1.1 -- Removed prompts for the nuget api key when running initialize-modulebuild. -- Initialize-ModuleBuild now automatically runs the build environment powershell script for the first time to create the modulebuild json settings file. + +- Removed prompts for the nuget api key when running Initialize-MBModuleBuild. +- Initialize-MBModuleBuild now automatically runs the build environment powershell script for the first time to create the modulebuild json settings file. - More documentation. - Fixed some minor scaffolding creation issues. - Added a 'ForceInstallModule' setting to eliminate build prompt when running the install and test module build tasks when the module is already installed. @@ -62,6 +102,7 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloebe - Fixed initial sensitive term settings generation to work on non-domain joined machines. ## Version 0.0.6 + - Changed all template files from .ps1 or psm1 to .template and changed the plaster manifest accordingly - Change all . files to remove the period so uploads to the gallery will work properly - Combined the UpdateRelease and NewVersion tasks and made both promptable @@ -69,12 +110,15 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloebe - Removed options to prompt for different folder names (like public/private/other/temp) and updated all template files and plaster manifest file accordinly. ## Version 0.0.5 + - fixes to plaster template creation ## Version 0.0.4 + - fixes to psgallery upload ## Version 0.0.3 + - Eliminated all '-Before' and '-After' in task definitions - Added 'Write-Description' helper function and converted all write-build lines to use it instead (for a quick indented output that is easier on the eyes) - Eliminated a large number of global variables in favor of simply redefining them in local tasks when required @@ -86,7 +130,9 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloebe - Updated much of the documentation. ## Version 0.0.2 + - Structural changes ## Version 0.0.1 + - Initial release diff --git a/build/docs/Additional/Contributing.md b/build/docs/Additional/Contributing.md index 93d7595..75020e9 100644 --- a/build/docs/Additional/Contributing.md +++ b/build/docs/Additional/Contributing.md @@ -5,17 +5,19 @@ Project Site: [https://github.com/zloeber/ModuleBuild](https://github.com/zloebe There are some important things to be aware of if you plan on contributing to this project. ## Documentation + All base project documentation changes should be made against the .\build\docs\Additional markdown files. These will populate and overwrite existing document files within the .\docs folder at build time. Additionally, you should update the .\build\docs\ReadTheDocs markdown files. Note that each folder becomes its own section within ReadTheDocs and its own folder within the .\docs directory. Finally, the Function documentation gets generated automatically based on the comment based help on each public/exported function. The function documentation markdown automatically gets populated within the .\docs\Functions folder as well as with the module release under its own docs folder. Private function CBH is not required but is encouraged. ## Development Environment -While any text editor will work well there are included task and setting json files explicitly for Visual Studio Code included with this project. I used VS Code Insiders edition but standard edition should be fine as ewll. The following tasks have been defined to make things a bit easier. First access the 'Pallette' (Shift+Ctrl+P or Shift+Cmd+P) and start typing in any of the following tasks to find and run them: -- Build -> Runs the Build task (also can use Shift+Ctrl+B or Shift+Cmd+B) -- Analyze -> Runs PSScriptAnalyzer against the src/public files. -- CreateProjectHelp - Creates the project level help. -- InsertMissingCBH - Analyzes the existing public functions and inserts a template CBH if no CBH already exists and saves it into your scratch folder. +While any text editor will work well there are included task and setting json files explicitly for Visual Studio Code included with this project. I used VS Code Insiders edition but standard edition should be fine as well. The following tasks have been defined to make things a bit easier. Open the 'configured tasks'-menu (use Shift+Ctrl+B or Shift+Cmd+B) and start typing in any of the following tasks to find and run them: + +- Build Module -> Runs the Build task +- Insert Missing COmment Based Help -> Analyzes the existing public functions and inserts a template CBH if no CBH already exists and saves it into your scratch folder. +- Run Tests -> Run Pester tests +- Test, Build, Install and Load Module -> Run the Test, build tasks but also install and try to load the module. The plaster manifest file gets automatically recreated at build time so all you need to do is update the plasterparams.ps1 and/or plastercontent.ps1 in the plaster directory to include any required changes you need to make. diff --git a/build/docs/Additional/ToDo.md b/build/docs/Additional/ToDo.md index 7c4bf85..0263c9b 100644 --- a/build/docs/Additional/ToDo.md +++ b/build/docs/Additional/ToDo.md @@ -13,7 +13,6 @@ There are many items which are possible and should probably be added to this pro - Automatic clean up of additional loaded modules when uploading to the gallery - Powershell Core compatibility - Automatic updates of the ReleaseNotes.md file -- Appveyor integration? - PSDeploy integration? - Github releases integration? - Smoother path to upgrade ModuleBuild for existing projects diff --git a/build/docs/ReadTheDocs/Usage/1 - Initialization.md b/build/docs/ReadTheDocs/Usage/1 - Initialization.md index 9ea09bd..b63a2da 100644 --- a/build/docs/ReadTheDocs/Usage/1 - Initialization.md +++ b/build/docs/ReadTheDocs/Usage/1 - Initialization.md @@ -1,13 +1,13 @@ # Step 1 - Initialization -Simply download this project and run the Initialize-ModuleBuild exported function. You will be prompted for a destination folder and the rest of the project settings. The destination folder will be the home of your future project but is completely portable. This is simply a wrapper for a custom version of Plaster that calls a template file I created for the project. It is fairly simple to deconstruct and use plaster directly via invoke-plaster and pass all the parameters via command line if desired. +Simply download this project and run the Initialize-MBModuleBuild exported function. You will be prompted for a destination folder and the rest of the project settings. The destination folder will be the home of your future project but is completely portable. This is simply a wrapper for a custom version of Plaster that calls a template file I created for the project. It is fairly simple to deconstruct and use plaster directly via invoke-plaster and pass all the parameters via command line if desired. `Import-Module ModuleBuild` -`Initialize-ModuleBuild` +`Initialize-MBModuleBuild` or -`Initialize-ModuleBuild -Path 'c:\temp\mymodule'` +`Initialize-MBModuleBuild -Path 'c:\temp\mymodule'` Once this has been kicked off and all answers have been entered, the initialization of your new project directory will start. Several template files are copied out to appropriate locations. Additionally, the default module manifest file gets created. @@ -17,12 +17,13 @@ Once this has been kicked off and all answers have been entered, the initializat ## Quick Start With your new module folder all created there are several steps still left to take in order to make your new project more world class. There are several sections of the following documentation that go over fleshing out the module with public functions, building a release, testing things, out and starting another release. Here are some quick next step tips if you aren't feeling like reading all of that. -1. Add public functions, one per file, to .\src\public (You can use the ModuleBuild function 'Add-PublicFunction' for this task) +1. Add public functions, one per file, to '.\src\public' 2. Update your default readme.md file at the root project directory -3. Update the about_ModuleName.help.txt file within .\build\docs\en-US -4. Doing ReadTheDocs integration? Cool, update .\build\docs\ReadTheDocs by creating folders representing sections and putting markdown files within them for the pages within those sections. -5. But remember that the markdown files in .\build\docs\Additional need some love too. These get dropped into your project .\docs directory at every build (overwriting anything there in the process!) +3. Update the about_ModuleName.help.txt file within '.\build\docs\en-US' +4. Doing ReadTheDocs integration? Cool, update '.\build\docs\ReadTheDocs' by creating folders representing sections and putting markdown files within them for the pages within those sections. +5. But remember that the markdown files in '.\build\docs\Additional' need some love too. These get dropped into your project '.\docs' directory at build time (overwriting anything there in the process!) 6. Update any bits within your *.psd1 that are appropriate to your module but don't mess with the exported function names as those get handled automatically when you do the build. -7. If you enabled sensitive terminology scanning then review and update your terms defined in your buildenvironment.json file (using get-buildenvironment & set-buildenvironment). -8. Build your project with by running .\Build.ps1, running the build task in VS Code, or running Invoke-Build at your project root. -9. If you have ReadTheDocs integration enabled make sure to re-organize the generated mkdocs.yml to be ordered how you like before pushing your code to github. +7. If you enabled sensitive terminology scanning then review and update your terms defined in your '.\build\YourModule.buildenvironment.json' file or use the 'Set-MBBuildEnvironment.ps1' function. +8. Change your project logo at '.\src\other\powershell-project.png' +9. Build your project with '.\Build.ps1' +10. Enter a PowerShell Gallery (aka Nuget) API key to the '.\build\YourModule.buildenvironment.json' file or use the 'Set-MBBuildEnvironment.ps1' function. Without this you will not be able to upload your module to the PSGallery. \ No newline at end of file diff --git a/build/docs/ReadTheDocs/Usage/10 - Converting To ModuleBuild.md b/build/docs/ReadTheDocs/Usage/10 - Converting To ModuleBuild.md index 03a132d..b3da841 100644 --- a/build/docs/ReadTheDocs/Usage/10 - Converting To ModuleBuild.md +++ b/build/docs/ReadTheDocs/Usage/10 - Converting To ModuleBuild.md @@ -2,15 +2,15 @@ If you want to convert your existing module to a modulebuild based project then there are a few functions that have been created to help you along. These are a bit new and should be considered a feature in testing. -## Public Functions: Import-ModulePublicFunction +## Public Functions: Import-MBModulePublicFunction Assuming your existing module is loadable it is pretty simple to determine public functions and then search through all of the module code for them. That is what this function will do. Once the public function has been found it will then (by default) attempt to insert the bare minimum comment based help necessary for PlatyPS to be able to autogenerate the module documentation and save the file into your ModuleBuild project public directory (as defined by your modulebuild project definition file). This will not overwrite any files that exist by the same name. -## Private Functions: Import-ModulePrivateFunction +## Private Functions: Import-MBModulePrivateFunction -Private functions within a module are infinately more tricky to isolate. But based on what is exported and what we find within the module source directory, it is not impossible to guess some of the private functions at least. That is what Import-ModulePrivateFunction command will do. It will always prompt you before importing any found top-level function (that isn't in the list of exported functions). +Private functions within a module are infinately more tricky to isolate. But based on what is exported and what we find within the module source directory, it is not impossible to guess some of the private functions at least. That is what Import-MBModulePrivateFunction command will do. It will always prompt you before importing any found top-level function (that isn't in the list of exported functions). Like the public function import, this will not overwrite any files that exist in your public source directory. @@ -20,13 +20,14 @@ So in order to convert an existing project to a modulebuild project you will fol 1. Initialize a new ModuleBuild project folder 2. ~~**Build** the project (**and it will fail the first time**). This ensures that your modulebuild project settings have been exported at least once.~~ -3. From your new modulebuild project folder run the Import-ModulePrivateFunction command against your existing module. -4. From your new modulebuild project folder run the Import-ModulePublicFunction command against your existing module. +3. From your new modulebuild project folder run the Import-MBModulePrivateFunction command against your existing module. +4. From your new modulebuild project folder run the Import-MBModulePublicFunction command against your existing module. 5. Add any required preload or postload code in src\other\preload.ps1 or src\other\postload.ps1 ## Very Important Notes + - This will generally work well for easy projects. Modules with many dependencies or higher complexity levels will likely be more work to get converted. - It is very important that, when specifying your module source, that you choose the right file. There are BIG differences in behavior between importing a psm1 file and a psd1 file and whichever one you pass in will be the one that gets imported. - I'd only run these commands against a newly initialized project but you could theoretically use this to split up some monolithic module for development purposes. Conversely, you could also use these to merge the public/private functions of multiple disperate modules as well. - These helper functions are also to be used for migrating existing modulebuild projects to updated versions of modulebuild in the future. All you have to copy over from an existing modulebuild project after importing the private/public functions is the json configuration file, your preload/postload customizations, and additional paths. -- If you tend to use your module project directories as dumping grounds for ideas and scratch code you may want to clean things up a bit before importing your private functions. Otherwise you could be in for a long process of yes/no prompts. \ No newline at end of file +- If you tend to use your module project directories as dumping grounds for ideas and scratch code you may want to clean things up a bit before importing your private functions. Otherwise you could be in for a long process of yes/no prompts. diff --git a/build/docs/ReadTheDocs/Usage/11 - Module Plugins.md b/build/docs/ReadTheDocs/Usage/11 - Module Plugins.md index c8dcd90..43efeb0 100644 --- a/build/docs/ReadTheDocs/Usage/11 - Module Plugins.md +++ b/build/docs/ReadTheDocs/Usage/11 - Module Plugins.md @@ -5,6 +5,7 @@ As of version 0.1.7 template module plugins are supported. These are optional pl Currently the following plugins are available: ## NLog + This includes all the code needed to automatically log all of the following commands within your module: - Write-Host diff --git a/build/docs/ReadTheDocs/Usage/12 - Writing Pester tests.md b/build/docs/ReadTheDocs/Usage/12 - Writing Pester tests.md new file mode 100644 index 0000000..6aa46cc --- /dev/null +++ b/build/docs/ReadTheDocs/Usage/12 - Writing Pester tests.md @@ -0,0 +1,55 @@ +# Writing Pester tests with ModuleBuild + +We won't go deep into how to write pester tests, just how they are integrated into ModuleBuild. + +ModuleBuild sorts Pester tests in 3 flavours. Meta, Unit and Intergration tests. + +- Meta are very basics tests such as file encoding or Tabs vs Spaces +- Unit testing is a type of testing to check if the small piece of code is doing what it is suppose to do +- Integration testing is a type of testing to check if different pieces of the modules are working together + +You will most likely only write Unit tests for your powershell module. + +## Matching the folder structure + +When writing tests you are expected to match the folder structure of the src folder. +For example, if you want to unit test `src\public\Write-SomeTestModule.ps1` you are expected to create the following test file at `tests\unit\public\Write-SomeTestModule.Tests.ps1`. + +The `tests\unit\public\Write-SomeTestModule.Tests.ps1` would look something like this. +The first 8 lines do some 'magic' to replace the path of the current file and match it to the correct source file. + +```powershell +#Requires -Modules Pester +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.' + +# Since we match the srs/tests stucture we can use this to dotsource the function. +$here = $here -replace 'tests\\unit', 'src' + +. "$here\$sut" + +Describe "Testing Write-SomeTestModule" -Tags @('UnitTest') { + it "Should return a specific string: (Yerp. This is a function.)" { + $result = Write-SomeTestModule + $result | Should -Be "Yerp. This is a function." + } +} +``` + +## Tagging your tests + +To support different kind of tests ModuleBuild uses Tags in the pester files. These are specified in `Describe` as following: + +```powershell +Describe "Testing Write-SomeTestModule" -Tags @('UnitTest') { + ... +} +``` + +During the build process pester is started multiple times and told to run tests with tag X. This ensure only specific type of tests are ran when we want them to. + +Possible tags: + +- UnitTest +- MetaTest +- IntergrationTest diff --git a/build/docs/ReadTheDocs/Usage/2 - Make Your Module.md b/build/docs/ReadTheDocs/Usage/2 - Make Your Module.md index 2769ece..3aa71dd 100644 --- a/build/docs/ReadTheDocs/Usage/2 - Make Your Module.md +++ b/build/docs/ReadTheDocs/Usage/2 - Make Your Module.md @@ -6,9 +6,9 @@ After the initialization has completed this directory should be all setup and 'b Any exportable/public functions for your module should be dropped into the .\src\public folder. Ideally each function will be self contained with a file name that matches the function name. Any other private functions can be dropped into .\src\private. -You can easily add a new public function using templates of your own devising with the `Add-PublicFunction` command. To add a new public function with a rather bare template function run the following: +You can easily add a new public function using templates of your own devising with the `Add-MBPublicFunction` command. To add a new public function with a rather bare template function run the following: -`Add-PublicFunction -Name 'New-TestFunction' -TemplateName:PlainPublicFunction` +`Add-MBPublicFunction -Name 'New-TestFunction' -TemplateName:PlainPublicFunction` The allowed template names are dynamically pulled directly from the module src\templates directory. Look in this folder for an example template and feel free to add more of your own to suit your project needs. diff --git a/build/docs/ReadTheDocs/Usage/3 - Build A Release.md b/build/docs/ReadTheDocs/Usage/3 - Build A Release.md index ccf5c1a..d801016 100644 --- a/build/docs/ReadTheDocs/Usage/3 - Build A Release.md +++ b/build/docs/ReadTheDocs/Usage/3 - Build A Release.md @@ -10,9 +10,10 @@ or or in VS Code -Press 'Ctrl+Shift+B' then select 'Build' +Press 'Ctrl+Shift+B' then select `Build Module` This is the heart of the ModuleBuild project. The build will go through the process of combining the source files into a monolithic psm1 file, populating the exportable functions of the release module manifest, creating online help files, analyze the script, scan for sensitive terms, and more. If everything builds without errors you will see the results populated in the release directory in two areas: + 1. In the release directory in a folder with the same name as the module (which is best practice for a module) 2. In the release directory in a folder with the version number of the release. @@ -23,4 +24,5 @@ At this point you should have a working release you could theoretically have som **Note:** *The build will pause if you didn't have enough comment based help to create the help file. Now is a chance to look at the created markdown files it references in the temp\docs directory to see what is missing. Use this as a chance to round back on your CBH in the source function files and update it to fill in the gaps. Then restart the build process again. Alternately, you can update the markdown files directly then continue the build process but this is NOT recommended as it is a temporary solution at best.* ## Pre and Post Build Steps -If you have some things you need to do before or after your build simply drop ps1 scripts in to build\startup or build\cleanup (respectively) to have them run. This project uses build\startup\makeplastermanifest.ps1 to go through the custom plaster manifest build process for instance. \ No newline at end of file + +If you have some things you need to do before or after your build simply drop ps1 scripts in to build\startup or build\cleanup (respectively) to have them run. This project uses build\startup\MakePlasterManifest.ps1 to go through the custom plaster manifest build process for instance. \ No newline at end of file diff --git a/build/docs/ReadTheDocs/Usage/4 - Test A Release.md b/build/docs/ReadTheDocs/Usage/4 - Test A Release.md index 0834a32..f2aaba7 100644 --- a/build/docs/ReadTheDocs/Usage/4 - Test A Release.md +++ b/build/docs/ReadTheDocs/Usage/4 - Test A Release.md @@ -1,4 +1,5 @@ # Step 4 - Test a Release + You may get a build to complete without errors but that doesn't mean that the module will behave as expected. You can do a quick module install and load test if you like: `.\Build.ps1 -InstallAndTestModule` @@ -10,7 +11,7 @@ If the module path already exists, the build script will ask for confirmation be You can combine the build with the install and test of the module if you so desire: -`.\Build.ps1 -BuildModule -InstallAndTestModule` +`.\Build.ps1 -TestBuildAndInstallModule` or @@ -18,4 +19,4 @@ or or in VS Code -`Ctrl+Shift+B (then select "Build, Install, and Test Module")` \ No newline at end of file +`Ctrl+Shift+B (then select "Test, Build, Install and Load Module")` \ No newline at end of file diff --git a/build/docs/ReadTheDocs/Usage/5 - Publish A Release.md b/build/docs/ReadTheDocs/Usage/5 - Publish A Release.md index b9c8b21..2c593d4 100644 --- a/build/docs/ReadTheDocs/Usage/5 - Publish A Release.md +++ b/build/docs/ReadTheDocs/Usage/5 - Publish A Release.md @@ -1,28 +1,26 @@ # Step 5 - Upload A PowerShell Gallery Release (Optional) -If you have plans to upload your module to the PowerShell Gallery then this build script can help automate the process a bit. You will still need to create an account and attain an API key from the PowerShell Gallery [website](https://www.powershellgallery.com/). - -Once you have attained your API key you will need to update your build configuration file with it. From witin your project root directory do the following: - -`Set-BuildEnvironment -NugetAPIKey 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'` - -Now when you are ready to upload to the psgallery simply run the following: - -`.\Build.ps1 -UploadPSGallery` -Assuming you have a valid NugetAPI key defined and your PowerShell manifest file has everything the gallary requires then this build step will automatically update the the upload the recent release directory module to the PowerShell Gallery for you. +If you have plans to upload your module to the PowerShell Gallery then this build script can help automate the process a bit. You will still need to create an account and attain an API key from the PowerShell Gallery [website](https://www.powershellgallery.com/). -**Note:** *I've not figured out yet how to reset versions when uploading to the gallery. You always have to upload a newer version than what is already there so be extra certain you are ready to publish the module before doing this step.* +Once you have attained your API key you will need to update your build configuration file with it. From witin your project root directory open the following file: +`build\YourModuleName.buildenvironment.json` -Hey, one more point, you can chain things together and do a build, install and test, and upload to the powershell gallery in one fell swoop: +Here you are able to update the NugetAPIKey. -`.\Build.ps1 -BuildModule -InstallAndTestModule -UploadPSGallery -ReleaseNotes 'First Upload'` +```json +... + "FirstRun": false, + "NugetAPIKey": "Add-Your-Key-Here", + "OptionTranscriptLogFile": "BuildTranscript.Log", +... +``` -or +**Note:** *Don't add the NugetAPIKey to the `build\YourModuleName.buildenvironment.ps1` file. This file is NOT ignored by the included .gitignore.* -`Invoke-Build -Task BuildInstallTestAndPublishModule` +Now when you are ready to upload to the PSGallery simply run the following: -or in VS Code +`.\Build.ps1 -UploadPSGallery` -`Ctrl+Shift+B (then select "Build, Install, Test, and Publish Module")` +Assuming you have a valid NugetAPI key defined and your PowerShell manifest file has everything the PSGallary requires then this build step will automatically update the the upload the recent release directory module to the PowerShell Gallery for you. -After this has completed the build version number will automatically be incremented by 1 and updated in your build configuration and the main module manifest file. \ No newline at end of file +**Note:** *I've not figured out yet how to reset versions when uploading to the gallery. You always have to upload a newer version than what is already there so be extra certain you are ready to publish the module before doing this step.* diff --git a/build/docs/ReadTheDocs/Usage/6 - Your Next Release.md b/build/docs/ReadTheDocs/Usage/6 - Your Next Release.md index d61db08..b3d5966 100644 --- a/build/docs/ReadTheDocs/Usage/6 - Your Next Release.md +++ b/build/docs/ReadTheDocs/Usage/6 - Your Next Release.md @@ -2,14 +2,8 @@ To start working on your next release (or roll back to a prior release) you will need to update the version of your module. This is easily done: -`.\Build.ps1 -NewVersion '0.0.5'` +`.\Build.ps1 -UpdateRelease -NewVersion '0.0.5'` Once this has been done you can proceed to build your module again: `.\Build.ps1` - -Oh, and if you have been paying attention up to this point you will have seen this coming. You can chain all this crap together into one command: - -`.\Build.ps1 -NewVersion '0.0.5' -BuildModule -InstallAndTestModule -UploadPSGallery -ReleaseNotes '0.0.5 release'` - -**Note:** It should be noted that performing the InstallAndTestModule build step is a bit superfluous as that gets done prior to uploading to the PSGallery as well. Also, you usually will be working on a release/build a bit before going straight to releasing to the gallery so I generally don't recommend doing everything in one fell swoop like this. You also get a much cleaner build experience if you simply use the invoke-build command to string the build steps together. \ No newline at end of file diff --git a/build/docs/ReadTheDocs/Usage/7 - Project Documentation.md b/build/docs/ReadTheDocs/Usage/7 - Project Documentation.md index c780d42..49fa5b8 100644 --- a/build/docs/ReadTheDocs/Usage/7 - Project Documentation.md +++ b/build/docs/ReadTheDocs/Usage/7 - Project Documentation.md @@ -1,4 +1,5 @@ # Maintaining Project Documentation + Keeping documentation updated for your project comes in two forms with the module code scaffolding that this project creates. 1. You keep your comment based help for public functions updated. PlatyPS will use this to generate function documentation. @@ -13,14 +14,16 @@ Your module documentation also comes in a few different forms and locations that >**NOTE:** If you get one thing from this section it is that you should **NOT** be updating your project documentation in the .\docs folder directly. Instead, use the build\docs folder to update your documentation and it will automatically populate to your project .\docs directory at build time. ## Comment Based Help (aka. CBH) + This build project assumes that the comment based help in your public functions are the single source of truth for your documentation. ModuleBuild uses PlatyPS to generate the relevant help files at every build. The build will fail if "{{ blah blah blah }}" is found within the PlatyPS markdown output. PlatyPS puts this marker text in place when you have missed parameters, synopsis, descriptions, or other essential CBH sections. It is possible to setup the build process in a manner that would allow us to manually make updates to the .md files and then continue processing. But I've purposefully decided against this so you are encouraged to go back to your original functions, fix/change the offending CBH, and rebuild. -Once PlatyPS autodocumentation is complete the CBH for each function gets replaced with the generated module documentation link. I base this replacement code on '.SYNOPSIS' existing in the comment based help. This is done in the following task: -``` +Once PlatyPS auto-documentation is complete the CBH for each function gets replaced with the generated module documentation link. I base this replacement code on '.SYNOPSIS' existing in the comment based help. This is done in the following task: + +```powershell task UpdateCBH -Before CreateModulePSM1 { $CBHPattern = "(?ms)(\<#.*\.SYNOPSIS.*?#>)" Get-ChildItem -Path "$($ScratchPath)\$($PublicFunctionSource)\*.ps1" -File | ForEach { @@ -37,21 +40,25 @@ As you might expect this will remove the entire CBH block which may or may not b >**NOTE:** ModuleBuild recreates the documentation markdown files every time the documentation gets generated. This includes the module landing page. PlatyPS doesn't seem to automatically pull in function description information (or I'm missing something in the usage of this module) so I do so within another task behind the scenes. ## CBH Helper -I've included a special task called 'UpdateCBHToScratch' that will recurse through your public functions and look for those that don't have CBH and add it. These files get spit out to your scratch directory and can be inspected for possible reintegration into your project. You can run this process at anytime with the following switch: -`.\Build.ps1 -InsertCBH` +I've included a special task called 'AddMissingCBH' that will search through your public functions and look for those that don't have CBH and add it. These files get spit out to your scratch directory and can be inspected for possible reintegration into your project. You can run this process at anytime with the following switch: + +`.\Build.ps1 -AddMissingCBH` If you have a large project with little CBH this can be a good way to kickstart the process so the build process will start working. Additionally, if you add a bunch of public functions without CBH you can use this process to fill things out temporarily until you have time to round back and add better details. >**NOTE:** This process requires you to copy the resulting files back into your project to be of any use. ## Generic Documentation + Most of your documentation will come from two sources, the auto-generated function markdown that PlatyPS spits out and the build\docs\Additional folder. ## ReadTheDocs.org Integration + If you have enabled readthedocs.org integration in the ModuleBuild configuration then a mkdocs.yml file will get updated automatically at the root of your project directory. It is up to you to setup the integration between your github.com account and readthedocs.org for this to be of any use in your project. When setting up your project at the ReadTheDocs website remember to set the advanced settings for 'mkdocs' processing. ### ReadTheDocs YAML Configuration + The ReadTheDocs manifest file gets generated from three locations: 1. The folder structure in .\build\docs\ReadTheDocs - Each subfolder becomes a category with each markdown document within becoming a specific page within it. diff --git a/build/docs/ReadTheDocs/Usage/8 - Special Files.md b/build/docs/ReadTheDocs/Usage/8 - Special Files.md index 0944d87..89c8cd4 100644 --- a/build/docs/ReadTheDocs/Usage/8 - Special Files.md +++ b/build/docs/ReadTheDocs/Usage/8 - Special Files.md @@ -1,6 +1,7 @@ # Special ModuleBuild Files ## src\other + There are two special files in the src\other directory: **src\other\PreLoad.ps1** - dot sourced at the beginning of the module (and in the build it is the first file to populate the final combined psm1 file). @@ -13,4 +14,6 @@ This is meant to help a little with some difference scenarios and could easily b - A default skeleton for the module about help txt file is created in a default en-US directory. This file should be updated with a real life example of how to use the module. -- I include a set of scripts in the build\dotsource directory that get used in various build tasks. If you want to add another script and task just beware that the scope of the functions are manually defined at the script level so that they remain available to other tasks after the task that dot sources them is completed. It's weird but, hey... at least I'm not using global scoping anywhere right? \ No newline at end of file +## build\dependencies\PSDepend + +**build\dependencies\PSDepend\build.depend.psd1** ModuleBuild uses PSDepend to install version tagged modules for its build process. We do this to avoid breaking changes from upstream modules. If you update your build scripts and require other external functions you can add them to this file. \ No newline at end of file diff --git a/build/docs/ReadTheDocs/Usage/9 - ModuleBuild Configuration.md b/build/docs/ReadTheDocs/Usage/9 - ModuleBuild Configuration.md index d7e3f6f..21f979b 100644 --- a/build/docs/ReadTheDocs/Usage/9 - ModuleBuild Configuration.md +++ b/build/docs/ReadTheDocs/Usage/9 - ModuleBuild Configuration.md @@ -1,14 +1,17 @@ # ModuleBuild Configuration + Each project has a ModuleBuild configuration file that will get dot sourced into the build engine. This file will, in turn, pull in settings from a json file in the same directory. **build\ModuleName.buildenvironment.ps1** - The initial dot sourced configuration script for your project. This file gets pulled into the build session at each invocation. You **MUST** update this file if you want to share any build settings with the community at large. -**build\ModuleName.buildenvironment.json** - This gets automatically updated after a first run of the build and will forever after be the single source of truth moving forward for your build settings (unless you run the prior buildenvironment.ps1 script with the -ForcePersist option or update the 'FirstRun' option to be $true). This file is setup to be ignored in the git config file that the project includes so it should be considered a generally safe place to keep things like your Nuget API key or other local settings. There is little preventing you from adding additional settings here manually. When added they will automatically be available within the set-buildenvironment and get-buildenvironment commands included with this module. +**build\ModuleName.buildenvironment.json** - This gets automatically updated after a first run of the build and will forever after be the single source of truth moving forward for your build settings (unless you run the prior buildenvironment.ps1 script with the -ForcePersist option or update the 'FirstRun' option to be $true). This file is setup to be ignored in the git config file that the project includes so it should be considered a generally safe place to keep things like your Nuget API key or other local settings. There is little preventing you from adding additional settings here manually. When added they will automatically be available within the Set-MBBuildEnvironment and Get-MBBuildEnvironment commands included with this module. You can add items directly to this file if you like and they will be loaded and are able the be read and set with the modulebuild module. Additionally, if there are new settings in the buildenvironment.ps1 BuildEnv definitions then those settings will automatically be saved to this file when detected (so when the next build runs). This is further described in the next section. ## Configuration Process + Whenever you kick off a process involving any of your build steps the ModuleName.buildenvironment.ps1 file is dot sourced into the session. When this file is invoked a few things happen: + 1. The default settings that are populated from initializing the project scaffolding are assigned to the global build environment variable that is aptly called 'BuildEnv'. 2. We then look for ModuleName.buildenvironment.json. If it exists, we load and use any settings within the file over any of the default values previously defined in the buildenvironment.ps1 file. 3. We then optionally update the json config file if any of the following conditions are met: @@ -20,22 +23,29 @@ Whenever you kick off a process involving any of your build steps the ModuleName What this means is that effectively the base settings defined in modulename.buildenvironment.ps1 don't really matter after the first run. After the first run, any changes will need to happen within the json file directly or using some of the helper functions included in this module. # Helper Functions -There are a few functions you can use to update or view the configuration (if you are not willing to update the json file manually that is). -## Get-BuildEnvironment +There are a few functions you can use to update or view the configuration. + +## Get-MBBuildEnvironment + Use this against a .buildenvironment.json file to pull in and display all the settings within as a psobject. If you don't specify a json file then it will attempt to guess the correct one to use based on the current directory. -## Set-BuildEnvironment -Use this against a .buildenvironment.json file to set any of the settings within. Dynamic parameters are used to ensure that this function is effectively forward compatible with any new settings/features/changes to the buildenvironment definitions. If you don't specify a json file then it will attempt to guess the correct one to use based on the current directory. +## Set-MBBuildEnvironment + +Use this against a .buildenvironment.json file to set any of the settings within. Dynamic parameters are used to ensure that this function is effectively forward compatible with any new settings/features/changes to the buildenvironment definitions. If you don't specify a json file then it will attempt to guess the correct one to use based on the current directory. + +## Import-MBModulePublicFunction -## Import-ModulePublicFunction Use this against an existing module directory to load the module into memory and attempt to extract any defined script functions that have been exported into the public source directory of your ModuleBuild project. No existing file will be overwritten. -## Import-ModulePrivateFunction +## Import-MBModulePrivateFunction + Use this against an existing module directory to load the module into memory to determine the exported functions. Then the whole module folder will then be crawled for any non-embedded function definitions that do not exist in the exported function list. Finally, you will be prompted to import each of the found candidate private functions into the modulebuild defined private folder for your project. No files will be overwritten. -## Add-PublicFunction +## Add-MBPublicFunction + This function will parse your build environment config file for the location of your function templates directory (default = src\templates) and allow you to use one of them to create a new public function for your module. Some basic validation of the function name will be done including: + - Verb-Noun format - Noun is singular, not plural - Function doesn't already exist diff --git a/build/docs/en-US/about_ModuleName.help.txt b/build/docs/en-US/about_ModuleName.help.txt index a3c3f36..4e5deaa 100644 --- a/build/docs/en-US/about_ModuleName.help.txt +++ b/build/docs/en-US/about_ModuleName.help.txt @@ -8,7 +8,7 @@ LONG DESCRIPTION A scaffolding framework which can be used to kickstart a generic PowerShell module project. EXAMPLES - Initialize-ModuleBuild -Path 'c:\temp\MyModule' + Initialize-MBModuleBuild -Path 'c:\temp\MyModule' This kicks off the initialization questions and creation of a new ModuleBuild based project in c:\temp\MyModule diff --git a/build/dotsource/Convert-ArrayToString.ps1 b/build/dotsource/Convert-ArrayToString.ps1 deleted file mode 100644 index 8cc0b27..0000000 --- a/build/dotsource/Convert-ArrayToString.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -function Script:Convert-ArrayToString { - [cmdletbinding()] - - Param ( - [Parameter(Mandatory=$true,Position=0)] - [AllowEmptyCollection()] - [Array]$Array, - - [Parameter(Mandatory=$False)] - [switch]$Flatten - ) - - Begin{ - If ($Flatten) { - $Mode = 'Append' - } - Else { - $Mode = 'AppendLine' - } - - If($Flatten -or $Array.Count -eq 0){ - $Indenting = '' - $RecursiveIndenting = '' - } - Else { - $Indenting = ' ' - $RecursiveIndenting = ' ' * (Get-PSCallStack).Where({$_.Command -match 'Convert-ArrayToString|Convert-HashToSTring' -and $_.InvocationInfo.CommandOrigin -eq 'Internal' -and $_.InvocationInfo.Line -notmatch '\$This'}).Count - } - } - - Process{ - $StringBuilder = [System.Text.StringBuilder]::new() - - If ($Array.Count -ge 1){ - [void]$StringBuilder.$Mode("@(") - } - Else { - [void]$StringBuilder.Append("@(") - } - - For($i = 0; $i -lt $Array.Count; $i++) { - $Item = $Array[$i] - - If($Item -is [String]){ - [void]$StringBuilder.Append($Indenting + $RecursiveIndenting + "'$Item'") - } - ElseIf($Item -is [int] -or $Value -is [double]){ - [void]$StringBuilder.Append($Indenting + $RecursiveIndenting + "$($Item.ToString())") - } - ElseIf($Item -is [bool]){ - [void]$StringBuilder.Append($Indenting + $RecursiveIndenting + "`$$Item") - } - ElseIf($Item -is [array]){ - $Value = Convert-ArrayToString -Array $Item -Flatten:$Flatten - - [void]$StringBuilder.Append($Indenting + $RecursiveIndenting + $Value) - } - ElseIf($Item -is [hashtable]){ - $Value = Convert-HashToSTring -Hashtable $Item -Flatten:$Flatten - - [void]$StringBuilder.Append($Indenting + $RecursiveIndenting + $Value) - } - Else { - Throw "Array element is not of known type." - } - - If ($i -lt ($Array.Count - 1)){ - [void]$StringBuilder.$Mode(', ') - } - ElseIf(-not $Flatten){ - [void]$StringBuilder.AppendLine('') - } - } - - [void]$StringBuilder.Append($RecursiveIndenting + ')') - $StringBuilder.ToString() - } -} \ No newline at end of file diff --git a/build/dotsource/Convert-HashToString.ps1 b/build/dotsource/Convert-HashToString.ps1 deleted file mode 100644 index a16fd26..0000000 --- a/build/dotsource/Convert-HashToString.ps1 +++ /dev/null @@ -1,88 +0,0 @@ -function Script:Convert-HashToString -{ - [cmdletbinding()] - - Param ( - [Parameter(Mandatory=$true,Position=0)] - [Hashtable]$Hashtable, - - [Parameter(Mandatory=$False)] - [switch]$Flatten - ) - - Begin{ - If($Flatten -or $Hashtable.Keys.Count -eq 0) - { - $Mode = 'Append' - $Indenting = '' - $RecursiveIndenting = '' - } - Else{ - $Mode = 'Appendline' - $Indenting = ' ' - $RecursiveIndenting = ' ' * (Get-PSCallStack).Where({$_.Command -match 'Convert-ArrayToString|Convert-HashToSTring' -and $_.InvocationInfo.CommandOrigin -eq 'Internal' -and $_.InvocationInfo.Line -notmatch '\$This'}).Count - } - } - - Process{ - $StringBuilder = [System.Text.StringBuilder]::new() - - If($Hashtable.Keys.Count -ge 1) - { - [void]$StringBuilder.$Mode("@{") - } - Else - { - [void]$StringBuilder.Append("@{") - } - - Foreach($Key in $Hashtable.Keys) - { - $Value = $Hashtable[$Key] - - If($Key -match '\s') - { - $Key = "'$Key'" - } - - If($Value -is [String]) - { - [void]$StringBuilder.$Mode($Indenting + $RecursiveIndenting + "$Key = '$Value'") - } - ElseIf($Value -is [int] -or $Value -is [double]) - { - [void]$StringBuilder.$Mode($Indenting + $RecursiveIndenting + "$Key = $($Value.ToString())") - } - ElseIf($Value -is [bool]) - { - [void]$StringBuilder.$Mode($Indenting + $RecursiveIndenting + "$Key = `$$Value") - } - ElseIf($Value -is [array]) - { - $Value = Convert-ArrayToString -Array $Value -Flatten:$Flatten - - [void]$StringBuilder.$Mode($Indenting + $RecursiveIndenting + "$Key = $Value") - } - ElseIf($Value -is [hashtable]) - { - $Value = Convert-HashToSTring -Hashtable $Value -Flatten:$Flatten - [void]$StringBuilder.$Mode($Indenting + $RecursiveIndenting + "$Key = $Value") - } - Else - { - Throw "Key value is not of known type." - } - - If($Flatten){[void]$StringBuilder.Append("; ")} - } - - [void]$StringBuilder.Append($RecursiveIndenting + "}") - - $StringBuilder.ToString().Replace("; }",'}') - } - - End{} -} - -#Remove-TypeData -TypeName System.Collections.HashTable -ErrorAction SilentlyContinue -#Update-TypeData -TypeName System.Collections.HashTable -MemberType ScriptMethod -MemberName ToString -Value {Convert-HashToString $This} \ No newline at end of file diff --git a/build/dotsource/Get-BuildEnvironment.ps1 b/build/dotsource/Get-BuildEnvironment.ps1 deleted file mode 100644 index e76752b..0000000 --- a/build/dotsource/Get-BuildEnvironment.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -function Script:Get-BuildEnvironment { - <# - .SYNOPSIS - Retrieves all the stored settings in a buildenvironment.json file. - - .DESCRIPTION - Retrieves all the stored settings in a buildenvironment.json file. - - .PARAMETER Path - Specifies the path to a buildenvironment.json file. - - .LINK - https://github.com/zloeber/ModuleBuild - - .EXAMPLE - TBD - #> - - [CmdletBinding()] - param( - [parameter(Position = 0, ValueFromPipeline = $TRUE)] - [String]$Path - ) - - process { - # If no path was specified take a few guesses - if ([string]::IsNullOrEmpty($Path)) { - $Path = (Get-ChildItem -File -Filter "*.buildenvironment.json" -Path '.\','..\','.\build\' | select -First 1).FullName - - if ([string]::IsNullOrEmpty($Path)) { - throw 'Unable to locate a *.buildenvironment.json file to parse!' - } - } - if (-not (Test-Path $Path)) { - throw "Unable to find the file: $Path" - } - - try { - $LoadedBuildEnv = Get-Content $Path | ConvertFrom-Json - $LoadedBuildEnv - } - catch { - throw "Unable to load the build file in $Path" - } - } -} \ No newline at end of file diff --git a/build/dotsource/Get-ErrorDetail.ps1 b/build/dotsource/Get-ErrorDetail.ps1 deleted file mode 100644 index d4982ad..0000000 --- a/build/dotsource/Get-ErrorDetail.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -function Get-ErrorDetail -{ - param - ( - [Parameter(Mandatory,ValueFromPipeline)] - $e - ) - process - { - if ($e -is [Management.Automation.ErrorRecord]) - { - [PSCustomObject]@{ - Reason = $e.CategoryInfo.Reason - Exception = $e.Exception.Message - Target = $e.CategoryInfo.TargetName - Script = $e.InvocationInfo.ScriptName - Line = $e.InvocationInfo.ScriptLineNumber - Column = $e.InvocationInfo.OffsetInLine - Datum = Get-Date - User = $env:USERNAME - } - } - } -} \ No newline at end of file diff --git a/build/dotsource/Get-ErrorInfo.ps1 b/build/dotsource/Get-ErrorInfo.ps1 deleted file mode 100644 index dc8e839..0000000 --- a/build/dotsource/Get-ErrorInfo.ps1 +++ /dev/null @@ -1,21 +0,0 @@ -function Script:Get-ErrorInfo { - param ( - [Parameter(ValueFrompipeline)] - [Management.Automation.ErrorRecord]$errorRecord - ) - - process { - $info = [PSCustomObject]@{ - Exception = $errorRecord.Exception.Message - Reason = $errorRecord.CategoryInfo.Reason - Target = $errorRecord.CategoryInfo.TargetName - Script = $errorRecord.InvocationInfo.ScriptName - Line = $errorRecord.InvocationInfo.ScriptLineNumber - Column = $errorRecord.InvocationInfo.OffsetInLine - Date = Get-Date - User = $env:username - } - - $info - } -} diff --git a/build/dotsource/Get-SpecialPaths.ps1 b/build/dotsource/Get-SpecialPaths.ps1 deleted file mode 100644 index 353ecfd..0000000 --- a/build/dotsource/Get-SpecialPaths.ps1 +++ /dev/null @@ -1,11 +0,0 @@ -Function Script:Get-SpecialPaths { - $SpecialFolders = @{} - - $names = [Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) - - foreach ($name in $names) { - $SpecialFolders[$name] = [Environment]::GetFolderPath($name) - } - - $SpecialFolders -} \ No newline at end of file diff --git a/build/dotsource/New-CommentBasedHelp.ps1 b/build/dotsource/New-CommentBasedHelp.ps1 deleted file mode 100644 index 22d1190..0000000 --- a/build/dotsource/New-CommentBasedHelp.ps1 +++ /dev/null @@ -1,218 +0,0 @@ -function Script:New-CommentBasedHelp { - <# - .SYNOPSIS - Create comment based help for a function. - .DESCRIPTION - Create comment based help for a function. - .PARAMETER Code - Multi-line or piped lines of code to process. - .PARAMETER ScriptParameters - Process the script parameters as the source of the comment based help. - .EXAMPLE - PS > $testfile = 'C:\temp\test.ps1' - PS > $test = Get-Content $testfile -raw - PS > $test | New-CommentBasedHelp | clip - - Takes C:\temp\test.ps1 as input, creates basic comment based help and puts the result in the clipboard - to be pasted elsewhere for review. - .EXAMPLE - PS > $CBH = Get-Content 'C:\EWSModule\Get-EWSContact.ps1' -Raw | New-CommentBasedHelp -Verbose -Advanced - PS > ($CBH | Where {$FunctionName -eq 'Get-EWSContact'}).CBH - - Consumes Get-EWSContact.ps1 and generates advanced CBH templates for all functions found within. Print out to the screen the advanced - CBH for just the Get-EWSContact function. - .NOTES - Author: Zachary Loeber - Site: http://www.the-little-things.net/ - Requires: Powershell 3.0 - - Version History - 1.0.0 - Initial release - 1.0.1 - Updated for ModuleBuild - #> - [CmdletBinding()] - param( - [parameter(Position=0, ValueFromPipeline=$true, HelpMessage='Lines of code to process.')] - [string[]]$Code, - [parameter(Position=1, HelpMessage='Process the script parameters as the source of the comment based help.')] - [switch]$ScriptParameters - ) - begin { - $FunctionName = $MyInvocation.MyCommand.Name - Write-Verbose "$($FunctionName): Begin." - - function Get-FunctionParameter { - <# - .SYNOPSIS - Return all parameters for each function found in a code block. - .DESCRIPTION - Return all parameters for each function found in a code block. - .PARAMETER Code - Multi-line or piped lines of code to process. - .PARAMETER Name - Name of fuction to process. If no funciton is given first the entire script will be processed for general parameters. If none are found every function in the script will be processed. - .PARAMETER ScriptParameters - Parse for script parameters only. - .EXAMPLE - PS > $testfile = 'C:\temp\test.ps1' - PS > $test = Get-Content $testfile -raw - PS > $test | Get-FunctionParameter -ScriptParameters - - Takes C:\temp\test.ps1 as input, gathers any script's parameters and prints the output to the screen. - - .NOTES - Author: Zachary Loeber - Site: http://www.the-little-things.net/ - Requires: Powershell 3.0 - - Version History - 1.0.0 - Initial release - 1.0.1 - Updated function name to remove plural format - Added Name parameter and logic for getting script parameters if no function is defined. - Added ScriptParameters parameter to include parameters for a script (not just ones associated with defined functions) - #> - [CmdletBinding()] - param( - [parameter(ValueFromPipeline=$true, HelpMessage='Lines of code to process.')] - [string[]]$Code, - [parameter(Position=1, HelpMessage='Name of function to process.')] - [string]$Name, - [parameter(Position=2, HelpMessage='Try to parse for script parameters as well.')] - [switch]$ScriptParameters - ) - begin { - $FunctionName = $MyInvocation.MyCommand.Name - Write-Verbose "$($FunctionName): Begin." - - $Codeblock = @() - $ParseError = $null - $Tokens = $null - - # These are essentially our AST filters - $functionpredicate = { ($args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]) } - $parampredicate = { ($args[0] -is [System.Management.Automation.Language.ParameterAst]) } - $typepredicate = { ($args[0] -is [System.Management.Automation.Language.TypeConstraintAst]) } - $paramattributes = { ($args[0] -is [System.Management.Automation.Language.NamedAttributeArgumentAst]) } - $output = @() - } - process { - $Codeblock += $Code - } - end { - $ScriptText = ($Codeblock | Out-String).trim("`r`n") - Write-Verbose "$($FunctionName): Attempting to parse AST." - - $AST = [System.Management.Automation.Language.Parser]::ParseInput($ScriptText, [ref]$Tokens, [ref]$ParseError) - - if($ParseError) { - $ParseError | Write-Error - throw "$($FunctionName): Will not work properly with errors in the script, please modify based on the above errors and retry." - } - - if (-not $ScriptParameters) { - $functions = $ast.FindAll($functionpredicate, $true) - if (-not [string]::IsNullOrEmpty($Name)) { - $functions = $functions | where {$_.Name -eq $Name} - } - - - # get the begin and end positions of every for loop - foreach ($function in $functions) { - Write-Verbose "$($FunctionName): Processing function - $($function.Name.ToString())" - $Parameters = $function.FindAll($parampredicate, $true) - foreach ($p in $Parameters) { - $ParamType = $p.FindAll($typepredicate, $true) - Write-Verbose "$($FunctionName): Processing Parameter of type [$($ParamType.typeName.FullName)] - $($p.Name.VariablePath.ToString())" - $OutProps = @{ - 'FunctionName' = $function.Name.ToString() - 'ParameterName' = $p.Name.VariablePath.ToString() - 'ParameterType' = $ParamType[0].typeName.FullName - } - # This will add in any other parameter attributes if they are specified (default attributes are thus not included and output may not be normalized) - $p.FindAll($paramattributes, $true) | Foreach { - $OutProps.($_.ArgumentName) = $_.Argument.Value - } - $Output += New-Object -TypeName PSObject -Property $OutProps - } - } - } - else { - Write-Verbose "$($FunctionName): Processing Script parameters" - if ($ast.ParamBlock -ne $null) { - $scriptparams = $ast.ParamBlock - $Parameters = $scriptparams.FindAll($parampredicate, $true) - foreach ($p in $Parameters) { - $ParamType = $p.FindAll($typepredicate, $true) - Write-Verbose "$($FunctionName): Processing Parameter of type [$($ParamType.typeName.FullName)] - $($p.Name.VariablePath.ToString())" - $OutProps = @{ - 'FunctionName' = 'Script' - 'ParameterName' = $p.Name.VariablePath.ToString() - 'ParameterType' = $ParamType[0].typeName.FullName - } - # This will add in any other parameter attributes if they are specified (default attributes are thus not included and output may not be normalized) - $p.FindAll($paramattributes, $true) | Foreach { - $OutProps.($_.ArgumentName) = $_.Argument.Value - } - $Output += New-Object -TypeName PSObject -Property $OutProps - } - } - else { - Write-Verbose "$($FunctionName): There were no script parameters found" - } - } - - $Output - Write-Verbose "$($FunctionName): End." - } - } - $CBH_PARAM = @' -.PARAMETER %%PARAM%% -%%PARAMHELP%% - -'@ - - $CBHTemplate = @' -<# -.SYNOPSIS -TBD -.DESCRIPTION -TBD -%%PARAMETER%% -.EXAMPLE -TBD -#> -'@ - - $Codeblock = @() - } - process { - $Codeblock += $Code - } - end { - $ScriptText = ($Codeblock | Out-String).trim("`r`n") - Write-Verbose "$($FunctionName): Attempting to parse parameters." - $FuncParams = @{} - if ($ScriptParameters) { - $FuncParams.ScriptParameters = $true - } - $AllParams = Get-FunctionParameter @FuncParams -Code $Codeblock | Sort-Object -Property FunctionName - $AllFunctions = @($AllParams.FunctionName | Select -unique) - - foreach ($f in $AllFunctions) { - $OutCBH = @{} - $OutCBH.FunctionName = $f - [string]$OutParams = '' - $fparams = @($AllParams | Where {$_.FunctionName -eq $f} | Sort-Object -Property Position) - $fparams | foreach { - $ParamHelpMessage = if ([string]::IsNullOrEmpty($_.HelpMessage)) {$_.ParameterName + " explanation`n`r"} else { $_.HelpMessage + "`n`r"} - $OutParams += $CBH_PARAM -replace '%%PARAM%%',$_.ParameterName -replace '%%PARAMHELP%%',$ParamHelpMessage - } - - $OutCBH.CBH = $CBHTemplate -replace '%%PARAMETER%%',$OutParams - - New-Object PSObject -Property $OutCBH - } - - Write-Verbose "$($FunctionName): End." - } -} \ No newline at end of file diff --git a/build/dotsource/New-PSGalleryProjectProfile.ps1 b/build/dotsource/New-PSGalleryProjectProfile.ps1 deleted file mode 100644 index 7c69e18..0000000 --- a/build/dotsource/New-PSGalleryProjectProfile.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -#Requires -version 5 -function Script:New-PSGalleryProjectProfile { - <# - .SYNOPSIS - Create a powershell Gallery module upload profile - .DESCRIPTION - Create a powershell Gallery module upload profile. Some items (like Name) are inferred from the module manifest and are left out. - .PARAMETER Path - Path of module project files to upload. - .PARAMETER ProjectUri - Module project website. - .PARAMETER Tags - Tags used to search for the module (separated by commas) - .PARAMETER Repository - Destination gallery (default is PSGallery) - .PARAMETER ReleaseNotes - Release notes. - .PARAMETER LicenseUri - License website. - .PARAMETER IconUri - Icon web path. - .PARAMETER NuGetApiKey - API key for the powershellgallery.com site. - .PARAMETER OutputFile - OutputFile (default is .psgallery) - - .EXAMPLE - .NOTES - Author: Zachary Loeber - Site: http://www.the-little-things.net/ - Version History - 1.0.0 - Initial release - #> - [CmdletBinding()] - param( - [parameter(Position=0, Mandatory=$true, HelpMessage='Path of module project files to upload.')] - [string]$Path, - [parameter(Position=1, HelpMessage='Module project website.')] - [string]$ProjectUri = '', - [parameter(Position=2, HelpMessage='Tags used to search for the module (separated by commas)')] - [string]$Tags = '', - [parameter(Position=3, HelpMessage='Destination gallery (default is PSGallery)')] - [string]$Repository = 'PSGallery', - [parameter(Position=4, HelpMessage='Release notes.')] - [string]$ReleaseNotes = '', - [parameter(Position=5, HelpMessage=' License website.')] - [string]$LicenseUri = '', - [parameter(Position=6, HelpMessage='Icon web path.')] - [string]$IconUri = '', - [parameter(Position=7, HelpMessage='NugetAPI key for the powershellgallery.com site.')] - [string]$NuGetApiKey = '', - [parameter(Position=8, HelpMessage='OutputFile (default is .psgallery)')] - [string]$OutputFile = '.psgallery' - ) - - $PublishParams = @{ - Path = $Path - NuGetApiKey = $NuGetApiKey - ProjectUri = $ProjectUri - Tags = $Tags - Repository = $Repository - ReleaseNotes = $ReleaseNotes - LicenseUri = $LicenseUri - IconUri = $IconUri - } - - if (Test-Path $OutputFile) { - $PublishParams | Export-Clixml -Path $OutputFile -confirm - } - else { - $PublishParams | Export-Clixml -Path $OutputFile - } -} \ No newline at end of file diff --git a/build/dotsource/Out-Zip.ps1 b/build/dotsource/Out-Zip.ps1 deleted file mode 100644 index e33bd03..0000000 --- a/build/dotsource/Out-Zip.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -function Script:Out-Zip { - param ( - [Parameter(Position=0, Mandatory=$true)] - [string] $Directory, - [Parameter(Position=1, Mandatory=$true)] - [string] $FileName, - [Parameter(Position=2)] - [switch] $overwrite - ) - Add-Type -Assembly System.IO.Compression.FileSystem - $compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal - if (-not $FileName.EndsWith('.zip')) {$FileName += '.zip'} - if ($overwrite) { - if (Test-Path $FileName) { - Remove-Item $FileName - } - } - [System.IO.Compression.ZipFile]::CreateFromDirectory($Directory, $FileName, $compressionLevel, $false) -} \ No newline at end of file diff --git a/build/dotsource/Out-ZipFromFile.ps1 b/build/dotsource/Out-ZipFromFile.ps1 deleted file mode 100644 index 925ddfc..0000000 --- a/build/dotsource/Out-ZipFromFile.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -function Script:Out-ZipFromFile { - [cmdletbinding()] - param ( - [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)] - [string[]]$Files, - [Parameter(Position=1, Mandatory=$true)] - [string]$ZipFile, - [Parameter(Position=2)] - [switch]$overwrite - ) - begin { - #Prepare zip file - if (($Overwrite) -and (test-path($ZipFile)) ) { - try { - Remove-Item -Path $ZipFile -Force - } - catch { - throw - } - } - if (-not (test-path($ZipFile))) { - try { - set-content $ZipFile ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18)) - $ThisZipFile = Get-ChildItem $ZipFile - $ThisZipFile.IsReadOnly = $false - } - catch { - throw - } - } - - $shellApplication = new-object -com shell.application - $zipPackage = $shellApplication.NameSpace($ThisZipFile.FullName) - $AllFiles = @() - } - process { - $AllFiles += $Files - } - end { - foreach($file in $AllFiles) { - $ThisFile = Get-ChildItem -Path $File -File - $zipPackage.CopyHere($ThisFile.FullName) - while($zipPackage.Items().Item($ThisFile.name) -eq $null){ - Start-sleep -seconds 1 - } - } - } -} \ No newline at end of file diff --git a/build/dotsource/Prompt-ForBuildBreak.ps1 b/build/dotsource/Prompt-ForBuildBreak.ps1 deleted file mode 100644 index 4a1cefe..0000000 --- a/build/dotsource/Prompt-ForBuildBreak.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -Function Script:Prompt-ForBuildBreak { - param ( - [Parameter(Position=0)] - [System.Object]$LastError, - [Parameter(Position=1)] - $CustomError = $null - ) - $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "End the build." - $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Stop the build." - $ContinueBuildPrompt = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) - if (($host.ui.PromptForChoice('Stop the build?', 'Should the build stop here?', $ContinueBuildPrompt, 0)) -eq 0) { - if ($CustomError -ne $null) { - throw $CustomError - } - else { - throw $LastError - } - } - else { - Write-Output "Contining the build process despite the following error:" - if ($CustomError -ne $null) { - Write-Output $CustomError - } - else { - Write-Output $LastError.Exception - } - } -} \ No newline at end of file diff --git a/build/dotsource/README.md b/build/dotsource/README.md deleted file mode 100644 index a7a3292..0000000 --- a/build/dotsource/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Note -These are extra functions used in the build script. They are purposefully scoped to the script level so as to be useable outside of the task level they are dot sourced within (the global scope would leave them in the session after build script is called so we cannot use that). \ No newline at end of file diff --git a/build/dotsource/Remove-Signature.ps1 b/build/dotsource/Remove-Signature.ps1 deleted file mode 100644 index 4763d48..0000000 --- a/build/dotsource/Remove-Signature.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -#requires -Version 2 -function Script:Remove-Signature -{ - <# - .SYNOPSIS - Finds all signed ps1 and psm1 files recursively from the current or defined path and removes any digital signatures attached to them. - .DESCRIPTION - Finds all signed ps1 and psm1 files recursively from the current or defined path and removes any digital signatures attached to them. - .PARAMETER Path - Path you want to parse for digital signatures. - .PARAMETER Recurse - Recurse through all subdirectories of the path provided. - .EXAMPLE - PS> Remove-Signature -Recurse - - Removes all digital signatures from ps1/psm1 files found in the current path. - - .NOTES - Author: Zachary Loeber - .LINK - http://www.the-little-things.net - #> - - [CmdletBinding( SupportsShouldProcess = $true )] - Param ( - [Parameter(ValueFromPipeline = $True,ValueFromPipelineByPropertyName = $True)] - [Alias('FilePath')] - [string]$Path = $(Get-Location).Path, - [Parameter()] - [switch]$Recurse - ) - Begin { - $RecurseParam = @{} - if ($Recurse) { - $RecurseParam.Recurse = $true - } - } - - Process { - $FilesToProcess = Get-ChildItem -Path $Path -File -Include '*.psm1','*.ps1','*.psd1','*.ps1xml' @RecurseParam - - $FilesToProcess | ForEach-Object -Process { - $SignatureStatus = (Get-AuthenticodeSignature $_).Status - $ScriptFileFullName = $_.FullName - if ($SignatureStatus -ne 'NotSigned') { - try { - $Content = Get-Content $ScriptFileFullName -ErrorAction Stop - $StringBuilder = New-Object -TypeName System.Text.StringBuilder -ErrorAction Stop - - Foreach ($Line in $Content) { - if ($Line -match '^# SIG # Begin signature block|^') { - Break - } - else { - $null = $StringBuilder.AppendLine($Line) - } - } - if ($pscmdlet.ShouldProcess( "$ScriptFileFullName")) { - Set-Content -Path $ScriptFileFullName -Value $StringBuilder.ToString() - Write-Output "$ScriptFileFullName -> Removed Signature!" - } - } - catch { - Write-Output "$ScriptFileFullName -> Unable to process signed file!" - Write-Error -Message $_.Exception.Message - } - } - else { - Write-Verbose "$ScriptFileFullName -> No signature, nothing done." - } - } - } -} \ No newline at end of file diff --git a/build/dotsource/Replace-FileString.ps1 b/build/dotsource/Replace-FileString.ps1 deleted file mode 100644 index 56c38c0..0000000 --- a/build/dotsource/Replace-FileString.ps1 +++ /dev/null @@ -1,237 +0,0 @@ -function Script:Replace-FileString { - <# - .SYNOPSIS - Replaces strings in files using a regular expression. - - .DESCRIPTION - Replaces strings in files using a regular expression. Supports - multi-line searching and replacing. - - .PARAMETER Pattern - Specifies the regular expression pattern. - - .PARAMETER Replacement - Specifies the regular expression replacement pattern. - - .PARAMETER Path - Specifies the path to one or more files. Wildcards are permitted. Each - file is read entirely into memory to support multi-line searching and - replacing, so performance may be slow for large files. - - .PARAMETER LiteralPath - Specifies the path to one or more files. The value of the this - parameter is used exactly as it is typed. No characters are interpreted - as wildcards. Each file is read entirely into memory to support - multi-line searching and replacing, so performance may be slow for - large files. - - .PARAMETER CaseSensitive - Specifies case-sensitive matching. The default is to ignore case. - - .PARAMETER Multiline - Changes the meaning of ^ and $ so they match at the beginning and end, - respectively, of any line, and not just the beginning and end of the - entire file. The default is that ^ and $, respectively, match the - beginning and end of the entire file. - - .PARAMETER UnixText - Causes $ to match only linefeed (\n) characters. By default, $ matches - carriage return+linefeed (\r\n). (Windows-based text files usually use - \r\n as line terminators, while Unix-based text files usually use only - \n.) - - .PARAMETER Overwrite - Overwrites a file by creating a temporary file containing all - replacements and then replacing the original file with the temporary - file. The default is to output but not overwrite. - - .PARAMETER Force - Allows overwriting of read-only files. Note that this parameter cannot - override security restrictions. - - .PARAMETER Encoding - Specifies the encoding for the file when -Overwrite is used. Possible - values are: ASCII, BigEndianUnicode, Unicode, UTF32, UTF7, or UTF8. The - default value is ASCII. - - .INPUTS - System.IO.FileInfo. - - .OUTPUTS - System.String without the -Overwrite parameter, or nothing with the - -Overwrite parameter. - - .LINK - about_Regular_Expressions - - .EXAMPLE - C:\>Replace-FileString.ps1 '(Ferb) and (Phineas)' '$2 and $1' Story.txt - This command replaces the string 'Ferb and Phineas' with the string - 'Phineas and Ferb' in the file Story.txt and outputs the file. Note - that the pattern and replacement strings are enclosed in single quotes - to prevent variable expansion. - - .EXAMPLE - C:\>Replace-FileString.ps1 'Perry' 'Agent P' Ferb.txt -Overwrite - This command replaces the string 'Perry' with the string 'Agent P' in - the file Ferb.txt and overwrites the file. - #> - - [CmdletBinding(DefaultParameterSetName="Path", - SupportsShouldProcess=$TRUE)] - param( - [parameter(Mandatory=$TRUE,Position=0)] - [String] $Pattern, - [parameter(Mandatory=$TRUE,Position=1)] - [String] [AllowEmptyString()] $Replacement, - [parameter(Mandatory=$TRUE,ParameterSetName="Path", - Position=2,ValueFromPipeline=$TRUE)] - [String[]] $Path, - [parameter(Mandatory=$TRUE,ParameterSetName="LiteralPath", - Position=2)] - [String[]] $LiteralPath, - [Switch] $CaseSensitive, - [Switch] $Multiline, - [Switch] $UnixText, - [Switch] $Overwrite, - [Switch] $Force, - [String] $Encoding="ASCII" - ) - - begin { - # Throw an error if $Encoding is not valid. - $encodings = @("ASCII","BigEndianUnicode","Unicode","UTF32","UTF7", - "UTF8") - if ($encodings -notcontains $Encoding) { - throw "Encoding must be one of the following: $encodings" - } - - # Extended test-path: Check the parameter set name to see if we - # should use -literalpath or not. - function test-pathEx($path) { - switch ($PSCmdlet.ParameterSetName) { - "Path" { - test-path $path - } - "LiteralPath" { - test-path -literalpath $path - } - } - } - - # Extended get-childitem: Check the parameter set name to see if we - # should use -literalpath or not. - function get-childitemEx($path) { - switch ($PSCmdlet.ParameterSetName) { - "Path" { - get-childitem $path -force - } - "LiteralPath" { - get-childitem -literalpath $path -force - } - } - } - - # Outputs the full name of a temporary file in the specified path. - function get-tempname($path) { - do { - $tempname = join-path $path ([IO.Path]::GetRandomFilename()) - } - while (test-path $tempname) - $tempname - } - - # Use '\r$' instead of '$' unless -UnixText specified because - # '$' alone matches '\n', not '\r\n'. Ignore '\$' (literal '$'). - if (-not $UnixText) { - $Pattern = $Pattern -replace '(? - - [CmdletBinding()] - param( - [parameter(Position = 0, ValueFromPipeline = $TRUE)] - [String]$Path - ) - DynamicParam { - # Create dictionary - $DynamicParameters = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary - if ([String]::isnullorempty($Path)) { - $BuildPath = (Get-ChildItem -File -Filter "*.buildenvironment.json" -Path '.\','..\','.\build\' | select -First 1).FullName - } - else { - $BuildPath = $Path - } - - if ((Test-Path $BuildPath) -and ($BuildPath -like "*.buildenvironment.json")) { - try { - $LoadedBuildEnv = Get-Content $BuildPath | ConvertFrom-Json - $NewParams = (Get-Member -Type 'NoteProperty' -InputObject $LoadedBuildEnv).Name - $NewParams | ForEach-Object { - - $NewParamSettings = @{ - Name = $_ - Type = $LoadedBuildEnv.$_.gettype().Name.toString() - ValueFromPipeline = $TRUE - HelpMessage = "Update the setting for $($_)" - } - - # Add new dynamic parameter to dictionary - New-DynamicParameter @NewParamSettings -Dictionary $DynamicParameters - } - } - catch { - #throw "Unable to load the build file in $BuildPath" - } - } - - # Return dictionary with dynamic parameters - $DynamicParameters - } - - begin { - if ($script:ThisModuleLoaded -eq $true) { - Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState - } - if ([String]::isnullorempty($Path)) { - $BuildPath = (Get-ChildItem -File -Filter "*.buildenvironment.json" -Path '.\','..\','.\build\' | select -First 1).FullName - } - else { - $BuildPath = $Path - } - - Write-Verbose "Using build file: $BuildPath" - } - process { - New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters - - if ((Test-Path $BuildPath) -and ($BuildPath -like "*.buildenvironment.json")) { - try { - $LoadedBuildEnv = Get-BuildEnvironment -Path $BuildPath - Foreach ($ParamKey in ($PSBoundParameters.Keys | Where-Object {$_ -ne 'Path'})) { - $LoadedBuildEnv.$ParamKey = $PSBoundParameters[$ParamKey] - Write-Output "Updating $ParamKey to be $($PSBoundParameters[$ParamKey])" - } - $LoadedBuildEnv.PSObject.Properties.remove('Path') - $LoadedBuildEnv | ConvertTo-Json | Out-File -FilePath $BuildPath -Encoding:utf8 -Force - Write-Output "Saved configuration file - $BuildPath" - } - catch { - throw "Unable to load the build file in $BuildPath" - } - } - else { - Write-Error "Unable to find or process a buildenvironment.json file!" - } - } -} \ No newline at end of file diff --git a/build/dotsource/Upload-ProjectToPSGallery.ps1 b/build/dotsource/Upload-ProjectToPSGallery.ps1 deleted file mode 100644 index 7b6ab7d..0000000 --- a/build/dotsource/Upload-ProjectToPSGallery.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -#Requires -version 5 -function Script:Upload-ProjectToPSGallery { - <# - .SYNOPSIS - Upload module project to Powershell Gallery - .DESCRIPTION - Upload module project to Powershell Gallery - .PARAMETER Name - Path to module to upload. - .PARAMETER Repository - Destination gallery (default is PSGallery) - .PARAMETER NuGetApiKey - API key for the powershellgallery.com site. - .EXAMPLE - .\Upload-ProjectToPSGallery.ps1 - .NOTES - Author: Zachary Loeber - Site: http://www.the-little-things.net/ - #> - [CmdletBinding()] - param( - [parameter(Mandatory=$true, HelpMessage='Name of the module to upload.')] - [string]$Name, - [parameter(HelpMessage='Destination gallery (default is PSGallery)')] - [string]$Repository = 'PSGallery', - [parameter(HelpMessage='API key for the powershellgallery.com site.')] - [string]$NuGetApiKey - ) - # if no API key is defined then look for psgalleryapi.txt in the local profile directory and try to use it instead. - if ([string]::IsNullOrEmpty($NuGetApiKey)) { - $psgalleryapipath = "$(Split-Path $Profile)\psgalleryapi.txt" - Write-Verbose "No PSGallery API key specified. Attempting to load one from the following location: $($psgalleryapipath)" - if (-not (test-path $psgalleryapipath)) { - Write-Error "$psgalleryapipath wasn't found and there was no defined API key, please rerun script with a defined APIKey parameter." - return - } - else { - $NuGetApiKey = get-content -raw $psgalleryapipath - } - } - - Publish-Module -Name $Name -NuGetApiKey $NuGetApiKey -Repository $Repository -} \ No newline at end of file diff --git a/buildreports/CodeHealthReport-Private.html b/build/reports/0.3.0/CodeHealthReport-Private.html similarity index 62% rename from buildreports/CodeHealthReport-Private.html rename to build/reports/0.3.0/CodeHealthReport-Private.html index a3defc8..c479e03 100644 --- a/buildreports/CodeHealthReport-Private.html +++ b/build/reports/0.3.0/CodeHealthReport-Private.html @@ -189,7 +189,7 @@

Analyzed path : src\private
- Analysis date : 2018-04-01 21:07:32Z + Analysis date : 2020-04-25 14:08:55Z
@@ -213,7 +213,7 @@

Number of Files

-

13

+

11

@@ -223,7 +223,7 @@

13

Number of Functions

-

13

+

11

@@ -233,7 +233,7 @@

13

Lines of Code - Total

-

798

+

778

@@ -246,9 +246,9 @@