├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── ScriptAnalyzerSettings.psd1 ├── debugHarness.ps1 ├── src ├── PlasterBuild.psd1 ├── PlasterBuild.psm1 ├── SettingCommands.ps1 ├── Templates │ └── NewAdvancedPowerShellModule │ │ ├── ReleaseNotes.md │ │ ├── ScriptAnalyzerSettings.psd1 │ │ ├── _gitignore │ │ ├── build.ps1 │ │ ├── build.psake.ps1 │ │ ├── build.settings.ps1 │ │ ├── docs │ │ └── en-US │ │ │ ├── about_Module.help.md │ │ │ └── about_Module.help.txt │ │ ├── editor │ │ └── VSCode │ │ │ ├── settings.json │ │ │ ├── tasks_pester.json │ │ │ ├── tasks_psake.json │ │ │ └── tasks_psake_pester.json │ │ ├── license │ │ ├── Apache.txt │ │ └── MIT.txt │ │ ├── plasterManifest.xml │ │ ├── src │ │ └── Module.psm1 │ │ └── test │ │ ├── Module.T.ps1 │ │ └── Shared.ps1 └── en-US │ └── PlasterBuild.Resources.psd1 └── test ├── ModuleManifest.Tests.ps1 └── Shared.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | Release/ 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "PowerShell", 6 | "type": "PowerShell", 7 | "request": "launch", 8 | "program": "${file}", 9 | "args": [], 10 | "cwd": "${file}" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // When enabled, will trim trailing whitespace when you save a file. 3 | "files.trimTrailingWhitespace": true, 4 | 5 | "search.exclude": { 6 | "Release": true 7 | }, 8 | 9 | // Use a custom PowerShell Script Analyzer settings file for this workspace. 10 | // Relative paths for this setting are always relative to the workspace root dir. 11 | "powershell.scriptAnalysis.settingsPath": "ScriptAnalyzerSettings.psd1" 12 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "0.1.0", 13 | 14 | // Start PowerShell 15 | "windows": { 16 | "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" 17 | }, 18 | "linux": { 19 | "command": "/usr/bin/powershell" 20 | }, 21 | "osx": { 22 | "command": "/usr/local/bin/powershell" 23 | }, 24 | 25 | // The command is a shell script 26 | "isShellCommand": true, 27 | 28 | // Show the output window always 29 | "showOutput": "always", 30 | 31 | "args": [ 32 | "-NoProfile", "-ExecutionPolicy", "Bypass" 33 | ], 34 | 35 | // Associate with test task runner 36 | "tasks": [ 37 | { 38 | "taskName": "Clean", 39 | "suppressTaskName": true, 40 | "showOutput": "always", 41 | "args": [ 42 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Clean'; Invoke-psake build.psake.ps1 -taskList Clean;", 43 | "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }" 44 | ] 45 | }, 46 | { 47 | "taskName": "Build", 48 | "suppressTaskName": true, 49 | "isBuildCommand": true, 50 | "showOutput": "always", 51 | "args": [ 52 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Build'; Invoke-psake build.psake.ps1 -taskList Build;", 53 | "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" 54 | ] 55 | }, 56 | { 57 | "taskName": "BuildHelp", 58 | "suppressTaskName": true, 59 | "showOutput": "always", 60 | "args": [ 61 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList BuildHelp'; Invoke-psake build.psake.ps1 -taskList BuildHelp;", 62 | "Invoke-Command { Write-Host 'Completed BuildHelp task in task runner.' }" 63 | ] 64 | }, 65 | { 66 | "taskName": "Analyze", 67 | "suppressTaskName": true, 68 | "showOutput": "always", 69 | "args": [ 70 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Analyze'; Invoke-psake build.psake.ps1 -taskList Analyze;", 71 | "Invoke-Command { Write-Host 'Completed Analyze task in task runner.' }" 72 | ] 73 | }, 74 | { 75 | "taskName": "Install", 76 | "suppressTaskName": true, 77 | "showOutput": "always", 78 | "args": [ 79 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Install'; Invoke-psake build.psake.ps1 -taskList Install;", 80 | "Invoke-Command { Write-Host 'Completed Install task in task runner.' }" 81 | ] 82 | }, 83 | { 84 | "taskName": "Publish", 85 | "suppressTaskName": true, 86 | "showOutput": "always", 87 | "args": [ 88 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Publish'; Invoke-psake build.psake.ps1 -taskList Publish;", 89 | "Invoke-Command { Write-Host 'Completed Publish task in task runner.' }" 90 | ] 91 | }, 92 | { 93 | "taskName": "Test", 94 | "suppressTaskName": true, 95 | "isTestCommand": true, 96 | "showOutput": "always", 97 | "args": [ 98 | "Write-Host 'Invoking Pester src'; cd test; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};", 99 | "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" 100 | ], 101 | "problemMatcher": [ 102 | { 103 | "owner": "powershell", 104 | "fileLocation": ["absolute"], 105 | "severity": "error", 106 | "pattern": [ 107 | { 108 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 109 | "message": 1 110 | }, 111 | { 112 | "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", 113 | "file": 1, 114 | "line": 2 115 | } 116 | ] 117 | } 118 | ] 119 | } 120 | ] 121 | } 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 PowerShell Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlasterBuild 2 | 3 | The PlasterBuild module provides following functionality: 4 | 5 | * Commands commonly used during the "build" of a PowerShell module. 6 | * A versioned build script that can be use to build, test, install and publish your PowerShell module. 7 | * Plaster templates to create an advanced PowerShell module project that uses the PlasterBuild build scripts. 8 | 9 | > **NOTE:** This project is at a very early phase of development. We have not officially 10 | > launched this project yet but we're opening up the repo for early feedback from the 11 | > PowerShell community. Please try it out and let us know what you think! 12 | 13 | ## Trying it Out 14 | 15 | Under construction. Check back later. 16 | 17 | ## Maintainers 18 | 19 | - [David Wilson](https://github.com/daviwil) - [@daviwil](http://twitter.com/daviwil) 20 | - [Keith Hill](https://github.com/rkeithhill) - [@r_keith_hill](http://twitter.com/r_keith_hill) 21 | - [Dave Green](https://github.com/davegreen) - [@neongreenie](http://twitter.com/neongreenie) 22 | 23 | ## License 24 | 25 | This project is [licensed under the MIT License](LICENSE). 26 | -------------------------------------------------------------------------------- /ScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Use Severity when you want to limit the generated diagnostic records to a 3 | # subset of: Error, Warning and Information. 4 | # Uncomment the following line if you only want Errors and Warnings but 5 | # not Information diagnostic records. 6 | #Severity = @('Error','Warning') 7 | 8 | # Use IncludeRules when you want to run only a subset of the default rule set. 9 | #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 10 | # 'PSMissingModuleManifestField', 11 | # 'PSReservedCmdletChar', 12 | # 'PSReservedParams', 13 | # 'PSShouldProcess', 14 | # 'PSUseApprovedVerbs', 15 | # 'PSUseDeclaredVarsMoreThanAssigments') 16 | 17 | # Use ExcludeRules when you want to run most of the default set of rules except 18 | # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules 19 | # and ExcludeRules, the rule will be excluded. 20 | #ExcludeRules = @('PSAvoidUsingWriteHost','PSMissingModuleManifestField') 21 | 22 | # You can use the following entry to supply parameters to rules that take parameters. 23 | # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you 24 | # want to allow. 25 | #Rules = @{ 26 | # Do not flag 'cd' alias. 27 | # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} 28 | 29 | # Check if your script uses cmdlets that are compatible on PowerShell Core, 30 | # version 6.0.0-alpha, on Linux. 31 | # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} 32 | #} 33 | } 34 | -------------------------------------------------------------------------------- /debugHarness.ps1: -------------------------------------------------------------------------------- 1 | # Use this file to debug the module. 2 | Import-Module $PSScriptRoot\src\PlasterBuild.psd1 3 | -------------------------------------------------------------------------------- /src/PlasterBuild.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Script module or binary module file associated with this manifest. 3 | RootModule = 'PlasterBuild.psm1' 4 | 5 | # ID used to uniquely identify this module 6 | GUID = '47439453-9009-4d0f-bf97-2be7fed33a1a' 7 | 8 | # Version number of this module. 9 | ModuleVersion = '0.1.0' 10 | 11 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a 12 | # PSData hashtable with additional module metadata used by PowerShell. 13 | PrivateData = @{ 14 | PSData = @{ 15 | # Tags applied to this module. These help with module discovery in online galleries. 16 | Tags = @('PlasterBuild', 'build', 'psake', 'sign', 'publish') 17 | 18 | # A URL to the license for this module. 19 | LicenseUri = 'https://github.com/PowerShell/PlasterBuild/blob/master/LICENSE' 20 | 21 | # A URL to the main website for this project. 22 | ProjectUri = 'https://github.com/PowerShell/PlasterBuild' 23 | 24 | # A URL to an icon representing this module. 25 | #IconUri = 'https://github.com/PowerShell/PlasterBuild/icon.png' 26 | 27 | # ReleaseNotes of this module - our ReleaseNotes are in 28 | # the file ReleaseNotes.md 29 | # ReleaseNotes = '' 30 | } 31 | } 32 | 33 | # Author of this module 34 | Author = 'Microsoft Corporation' 35 | 36 | # Company or vendor of this module 37 | CompanyName = 'Microsoft Corporation' 38 | 39 | # Copyright statement for this module 40 | Copyright = '(c) Microsoft Corporation 2016. All rights reserved.' 41 | 42 | # Description of the functionality provided by this module 43 | Description = 'PlasterBuild provides build-related commmands and canned build scripts for PowerShell modules.' 44 | 45 | # Minimum version of the Windows PowerShell engine required by this module 46 | PowerShellVersion = '3.0' 47 | 48 | # Functions to export from this module - explicitly list each function that should be 49 | # exported. This improves performance of PowerShell when discovering the commands in 50 | # module. 51 | FunctionsToExport = @( 52 | ) 53 | 54 | # HelpInfo URI of this module 55 | # HelpInfoURI = '' 56 | } 57 | -------------------------------------------------------------------------------- /src/PlasterBuild.psm1: -------------------------------------------------------------------------------- 1 | data LocalizedData { 2 | # culture="en-US" 3 | ConvertFrom-StringData @' 4 | '@ 5 | } 6 | 7 | Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -FileName Plaster.Resources.psd1 -ErrorAction SilentlyContinue 8 | 9 | # Module variables 10 | 11 | if (($PSVersionTable.PSVersion.Major -le 5) -or ($PSVersionTable.PSEdition -eq 'Desktop') -or $IsWindows) { 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 13 | $ParameterDefaultValueStoreRootPath = "$env:LOCALAPPDATA\PlasterBuild" 14 | } 15 | elseif ($IsLinux) { 16 | # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 18 | $ParameterDefaultValueStoreRootPath = if ($XDG_DATA_HOME) { "$XDG_DATA_HOME/plasterbuild" } else { "$Home/.local/share/plasterbuild" } 19 | } 20 | else { 21 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 22 | $ParameterDefaultValueStoreRootPath = "$Home/.plasterbuild" 23 | } 24 | 25 | # Dot source the individual module command scripts. 26 | . $PSScriptRoot\SettingCommands.ps1 27 | 28 | Export-ModuleMember -Function *-* 29 | -------------------------------------------------------------------------------- /src/SettingCommands.ps1: -------------------------------------------------------------------------------- 1 | function Add-BuildSetting { 2 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function')] 3 | param( 4 | [Parameter(Mandatory)] 5 | [string]$Key, 6 | 7 | [Parameter(Mandatory)] 8 | [string]$Path, 9 | 10 | [Parameter(Mandatory)] 11 | [ValidateNotNull()] 12 | [object]$Value 13 | ) 14 | 15 | switch ($type = $Value.GetType().Name) { 16 | 'securestring' { $setting = $Value | ConvertFrom-SecureString } 17 | default { $setting = $Value } 18 | } 19 | 20 | if (Test-Path -LiteralPath $Path) { 21 | $storedSettings = Import-Clixml -Path $Path 22 | $storedSettings.Add($Key, @($type, $setting)) 23 | $storedSettings | Export-Clixml -Path $Path 24 | } 25 | else { 26 | $parentDir = Split-Path -Path $Path -Parent 27 | if (!(Test-Path -LiteralPath $parentDir)) { 28 | New-Item $parentDir -ItemType Directory > $null 29 | } 30 | 31 | @{$Key = @($type, $setting)} | Export-Clixml -Path $Path 32 | } 33 | } 34 | 35 | function Get-BuildSetting { 36 | param( 37 | [Parameter(Mandatory)] 38 | [string]$Key, 39 | 40 | [Parameter(Mandatory)] 41 | [string]$Path 42 | ) 43 | 44 | if (Test-Path -LiteralPath $Path) { 45 | $securedSettings = Import-Clixml -Path $Path 46 | if ($securedSettings.$Key) { 47 | switch ($securedSettings.$Key[0]) { 48 | 'securestring' { 49 | $value = $securedSettings.$Key[1] | ConvertTo-SecureString 50 | $cred = New-Object -TypeName PSCredential -ArgumentList 'jpgr', $value 51 | $cred.GetNetworkCredential().Password 52 | } 53 | default { 54 | $securedSettings.$Key[1] 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | function Set-BuildSetting { 62 | param( 63 | [Parameter(Mandatory)] 64 | [string]$Key, 65 | 66 | [Parameter(Mandatory)] 67 | [string]$Path, 68 | 69 | [Parameter(Mandatory)] 70 | [ValidateNotNull()] 71 | [object]$Value 72 | ) 73 | 74 | if (GetSetting -Key $Key -Path $Path) { 75 | RemoveSetting -Key $Key -Path $Path 76 | } 77 | 78 | AddSetting -Key $Key -Value $Value -Path $Path 79 | } 80 | 81 | function Remove-BuildSetting { 82 | param( 83 | [Parameter(Mandatory)] 84 | [string]$Key, 85 | 86 | [Parameter(Mandatory)] 87 | [string]$Path 88 | ) 89 | 90 | if (Test-Path -LiteralPath $Path) { 91 | $storedSettings = Import-Clixml -Path $Path 92 | $storedSettings.Remove($Key) 93 | if ($storedSettings.Count -eq 0) { 94 | Remove-Item -Path $Path 95 | } 96 | else { 97 | $storedSettings | Export-Clixml -Path $Path 98 | } 99 | } 100 | else { 101 | Write-Warning "The build setting file '$Path' has not been created yet." 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/ReleaseNotes.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/PlasterBuild/f3108a310bf94ee9b9c7c5a5f75632be682b4b64/src/Templates/NewAdvancedPowerShellModule/ReleaseNotes.md -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/ScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Use Severity when you want to limit the generated diagnostic records to a 3 | # subset of: Error, Warning and Information. 4 | # Uncomment the following line if you only want Errors and Warnings but 5 | # not Information diagnostic records. 6 | #Severity = @('Error','Warning') 7 | 8 | # Use IncludeRules when you want to run only a subset of the default rule set. 9 | #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 10 | # 'PSMisleadingBacktick', 11 | # 'PSMissingModuleManifestField', 12 | # 'PSReservedCmdletChar', 13 | # 'PSReservedParams', 14 | # 'PSShouldProcess', 15 | # 'PSUseApprovedVerbs', 16 | # 'PSUseDeclaredVarsMoreThanAssigments') 17 | 18 | # Use ExcludeRules when you want to run most of the default set of rules except 19 | # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules 20 | # and ExcludeRules, the rule will be excluded. 21 | #ExcludeRules = @('PSAvoidUsingWriteHost') 22 | 23 | # You can use the following entry to supply parameters to rules that take parameters. 24 | # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you 25 | # want to allow. 26 | #Rules = @{ 27 | # Do not flag 'cd' alias. 28 | # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} 29 | 30 | # Check if your script uses cmdlets that are compatible on PowerShell Core, 31 | # version 6.0.0-alpha, on Linux. 32 | # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} 33 | #} 34 | } 35 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/_gitignore: -------------------------------------------------------------------------------- 1 | # Don't checkin the Release output dir. 2 | Release/ 3 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/build.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules psake 2 | 3 | # Builds the module by invoking psake on the build.psake.ps1 script. 4 | Invoke-PSake $PSScriptRoot\build.psake.ps1 -taskList Build 5 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/build.psake.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules psake 2 | 3 | ############################################################################## 4 | # DO NOT MODIFY THIS FILE! Modify build.settings.ps1 instead. 5 | ############################################################################## 6 | 7 | ############################################################################## 8 | # This is the PowerShell Module psake build script. It defines the following tasks: 9 | # 10 | # Clean, Build, Sign, BuildHelp, Install, Test and Publish. 11 | # 12 | # The default task is Build. This task copies the appropriate files from the 13 | # $SrcRootDir under the $OutDir. Later, other tasks such as Sign and BuildHelp 14 | # will further modify the contents of $OutDir and add new files. 15 | # 16 | # The Sign task will only sign scripts if the $SignScripts variable is set to 17 | # $true. A code-signing certificate is required for this task to complete. 18 | # 19 | # The BuildHelp task invokes platyPS to generate markdown files from 20 | # comment-based help for your exported commands. platyPS then generates 21 | # a help file for your module from the markdown files. 22 | # 23 | # The Install task simplies copies the module folder under $OutDir to your 24 | # profile's Modules folder. 25 | # 26 | # The Test task invokes Pester on the $TestRootDir. 27 | # 28 | # The Publish task uses the Publish-Module command to publish 29 | # to either the PowerShell Gallery (the default) or you can change 30 | # the $PublishRepository property to the name of an alternate repository. 31 | # Note: the Publish task requires that the Test task execute without failures. 32 | # 33 | # You can exeute a specific task, such as the Test task by running the 34 | # following command: 35 | # 36 | # PS C:\> invoke-psake build.psake.ps1 -taskList Test 37 | # 38 | # You can execute the Publish task with the following command. 39 | # The first time you execute the Publish task, you will be prompted to enter 40 | # your PowerShell Gallery NuGetApiKey. After entering the key, it is encrypted 41 | # and stored so you will not have to enter it again. 42 | # 43 | # PS C:\> invoke-psake build.psake.ps1 -taskList Publish 44 | # 45 | # You can verify the stored and encrypted NuGetApiKey by running the following 46 | # command which will display a portion of your NuGetApiKey in plain text. 47 | # 48 | # PS C:\> invoke-psake build.psake.ps1 -taskList ShowApiKey 49 | # 50 | # You can store a new NuGetApiKey with this command. You can leave off 51 | # the -properties parameter and you'll be prompted for the key. 52 | # 53 | # PS C:\> invoke-psake build.psake.ps1 -taskList StoreApiKey -properties @{NuGetApiKey='test123'} 54 | # 55 | 56 | ############################################################################### 57 | # Dot source the user's customized properties and extension tasks. 58 | ############################################################################### 59 | . $PSScriptRoot\build.settings.ps1 60 | 61 | ############################################################################### 62 | # Private properties. 63 | ############################################################################### 64 | Properties { 65 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 66 | $ModuleOutDir = "$OutDir\$ModuleName" 67 | 68 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 69 | $UpdatableHelpOutDir = "$OutDir\UpdatableHelp" 70 | 71 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 72 | $SharedProperties = @{} 73 | 74 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 75 | $LineSep = "-" * 78 76 | } 77 | 78 | ############################################################################### 79 | # Core task implementations. Avoid modifying these tasks. 80 | ############################################################################### 81 | Task default -depends Build 82 | 83 | Task Init -requiredVariables OutDir { 84 | if (!(Test-Path -LiteralPath $OutDir)) { 85 | New-Item $OutDir -ItemType Directory -Verbose:$VerbosePreference > $null 86 | } 87 | else { 88 | Write-Verbose "$($psake.context.currentTaskName) - directory already exists '$OutDir'." 89 | } 90 | } 91 | 92 | Task Clean -depends Init -requiredVariables OutDir { 93 | # Maybe a bit paranoid but this task nuked \ on my laptop. Good thing I was not running as admin. 94 | if ($OutDir.Length -gt 3) { 95 | Get-ChildItem $OutDir | Remove-Item -Recurse -Force -Verbose:$VerbosePreference 96 | } 97 | else { 98 | Write-Verbose "$($psake.context.currentTaskName) - `$OutDir '$OutDir' must be longer than 3 characters." 99 | } 100 | } 101 | 102 | Task StageFiles -depends Init, Clean, BeforeStageFiles, CoreStageFiles, AfterStageFiles { 103 | } 104 | 105 | Task CoreStageFiles -requiredVariables ModuleOutDir, SrcRootDir { 106 | if (!(Test-Path -LiteralPath $ModuleOutDir)) { 107 | New-Item $ModuleOutDir -ItemType Directory -Verbose:$VerbosePreference > $null 108 | } 109 | else { 110 | Write-Verbose "$($psake.context.currentTaskName) - directory already exists '$ModuleOutDir'." 111 | } 112 | 113 | Copy-Item -Path $SrcRootDir\* -Destination $ModuleOutDir -Recurse -Exclude $Exclude -Verbose:$VerbosePreference 114 | } 115 | 116 | Task Build -depends Init, Clean, BeforeBuild, StageFiles, Analyze, Sign, AfterBuild { 117 | } 118 | 119 | Task Analyze -depends StageFiles ` 120 | -requiredVariables ModuleOutDir, ScriptAnalysisEnabled, ScriptAnalysisFailBuildOnSeverityLevel, ScriptAnalyzerSettingsPath { 121 | if (!$ScriptAnalysisEnabled) { 122 | "Script analysis is not enabled. Skipping $($psake.context.currentTaskName) task." 123 | return 124 | } 125 | 126 | if (!(Get-Module PSScriptAnalyzer -ListAvailable)) { 127 | "PSScriptAnalyzer module is not installed. Skipping $($psake.context.currentTaskName) task." 128 | return 129 | } 130 | 131 | "ScriptAnalysisFailBuildOnSeverityLevel set to: $ScriptAnalysisFailBuildOnSeverityLevel" 132 | 133 | $analysisResult = Invoke-ScriptAnalyzer -Path $ModuleOutDir -Settings $ScriptAnalyzerSettingsPath -Recurse -Verbose:$VerbosePreference 134 | $analysisResult | Format-Table 135 | switch ($ScriptAnalysisFailBuildOnSeverityLevel) { 136 | 'None' { 137 | return 138 | } 139 | 'Error' { 140 | Assert -conditionToCheck ( 141 | ($analysisResult | Where-Object Severity -eq 'Error').Count -eq 0 142 | ) -failureMessage 'One or more ScriptAnalyzer errors were found. Build cannot continue!' 143 | } 144 | 'Warning' { 145 | Assert -conditionToCheck ( 146 | ($analysisResult | Where-Object { 147 | $_.Severity -eq 'Warning' -or $_.Severity -eq 'Error' 148 | }).Count -eq 0) -failureMessage 'One or more ScriptAnalyzer warnings were found. Build cannot continue!' 149 | } 150 | default { 151 | Assert -conditionToCheck ( 152 | $analysisResult.Count -eq 0 153 | ) -failureMessage 'One or more ScriptAnalyzer issues were found. Build cannot continue!' 154 | } 155 | } 156 | } 157 | 158 | Task Sign -depends StageFiles -requiredVariables CertPath, SettingsPath, ScriptSigningEnabled { 159 | if (!$ScriptSigningEnabled) { 160 | "Script signing is not enabled. Skipping $($psake.context.currentTaskName) task." 161 | return 162 | } 163 | 164 | $validCodeSigningCerts = Get-ChildItem -Path $CertPath -CodeSigningCert -Recurse | Where-Object NotAfter -ge (Get-Date) 165 | if (!$validCodeSigningCerts) { 166 | throw "There are no non-expired code-signing certificates in $CertPath. You can either install " + 167 | "a code-signing certificate into the certificate store or disable script analysis in build.settings.ps1." 168 | } 169 | 170 | $certSubjectNameKey = "CertSubjectName" 171 | $storeCertSubjectName = $true 172 | 173 | # Get the subject name of the code-signing certificate to be used for script signing. 174 | if (!$CertSubjectName -and ($CertSubjectName = GetSetting -Key $certSubjectNameKey -Path $SettingsPath)) { 175 | $storeCertSubjectName = $false 176 | } 177 | elseif (!$CertSubjectName) { 178 | "A code-signing certificate has not been specified." 179 | "The following non-expired, code-signing certificates are available in your certificate store:" 180 | $validCodeSigningCerts | Format-List Subject,Issuer,Thumbprint,NotBefore,NotAfter 181 | 182 | $CertSubjectName = Read-Host -Prompt 'Enter the subject name (case-sensitive) of the certificate to use for script signing' 183 | } 184 | 185 | # Find a code-signing certificate that matches the specified subject name. 186 | $certificate = $validCodeSigningCerts | 187 | Where-Object { $_.SubjectName.Name -cmatch [regex]::Escape($CertSubjectName) } | 188 | Sort-Object NotAfter -Descending | Select-Object -First 1 189 | 190 | if ($certificate) { 191 | $SharedProperties.CodeSigningCertificate = $certificate 192 | 193 | if ($storeCertSubjectName) { 194 | SetSetting -Key $certSubjectNameKey -Value $certificate.SubjectName.Name -Path $SettingsPath 195 | "The new certificate subject name has been stored in ${SettingsPath}." 196 | } 197 | else { 198 | "Using stored certificate subject name $CertSubjectName from ${SettingsPath}." 199 | } 200 | 201 | $LineSep 202 | "Using code-signing certificate: $certificate" 203 | $LineSep 204 | 205 | $files = @(Get-ChildItem -Path $ModuleOutDir\* -Recurse -Include *.ps1,*.psm1) 206 | foreach ($file in $files) { 207 | $setAuthSigParams = @{ 208 | FilePath = $file.FullName 209 | Certificate = $certificate 210 | Verbose = $VerbosePreference 211 | } 212 | 213 | $result = Microsoft.PowerShell.Security\Set-AuthenticodeSignature @setAuthSigParams 214 | if ($result.Status -ne 'Valid') { 215 | throw "Failed to sign script: $($file.FullName)." 216 | } 217 | 218 | "Successfully signed script: $($file.Name)" 219 | } 220 | } 221 | else { 222 | $expiredCert = Get-ChildItem -Path $CertPath -CodeSigningCert -Recurse | 223 | Where-Object { ($_.SubjectName.Name -cmatch [regex]::Escape($CertSubjectName)) -and 224 | ($_.NotAfter -lt (Get-Date)) } 225 | Sort-Object NotAfter -Descending | Select-Object -First 1 226 | 227 | if ($expiredCert) { 228 | throw "The code-signing certificate `"$($expiredCert.SubjectName.Name)`" EXPIRED on $($expiredCert.NotAfter)." 229 | } 230 | 231 | throw 'No valid certificate subject name supplied or stored.' 232 | } 233 | } 234 | 235 | Task BuildHelp -depends Build, BeforeBuildHelp, GenerateMarkdown, GenerateHelpFiles, AfterBuildHelp { 236 | } 237 | 238 | Task GenerateMarkdown -requiredVariables DefaultLocale, DocsRootDir, ModuleName, ModuleOutDir { 239 | if (!(Get-Module platyPS -ListAvailable)) { 240 | "platyPS module is not installed. Skipping $($psake.context.currentTaskName) task." 241 | return 242 | } 243 | 244 | $moduleInfo = Import-Module $ModuleOutDir\$ModuleName.psd1 -Global -Force -PassThru 245 | 246 | try { 247 | if ($moduleInfo.ExportedCommands.Count -eq 0) { 248 | "No commands have been exported. Skipping $($psake.context.currentTaskName) task." 249 | return 250 | } 251 | 252 | if (!(Test-Path -LiteralPath $DocsRootDir)) { 253 | New-Item $DocsRootDir -ItemType Directory > $null 254 | } 255 | 256 | if (Get-ChildItem -LiteralPath $DocsRootDir -Filter *.md -Recurse) { 257 | Get-ChildItem -LiteralPath $DocsRootDir -Directory | ForEach-Object { 258 | Update-MarkdownHelp -Path $_.FullName -Verbose:$VerbosePreference > $null 259 | } 260 | } 261 | 262 | # ErrorAction set to SilentlyContinue so this command will not overwrite an existing MD file. 263 | New-MarkdownHelp -Module $ModuleName -Locale $DefaultLocale -OutputFolder $DocsRootDir\$DefaultLocale ` 264 | -WithModulePage -ErrorAction SilentlyContinue -Verbose:$VerbosePreference > $null 265 | } 266 | finally { 267 | Remove-Module $ModuleName 268 | } 269 | } 270 | 271 | Task GenerateHelpFiles -requiredVariables DocsRootDir, ModuleName, ModuleOutDir, OutDir { 272 | if (!(Get-Module platyPS -ListAvailable)) { 273 | "platyPS module is not installed. Skipping $($psake.context.currentTaskName) task." 274 | return 275 | } 276 | 277 | if (!(Get-ChildItem -LiteralPath $DocsRootDir -Filter *.md -Recurse -ErrorAction SilentlyContinue)) { 278 | "No markdown help files to process. Skipping $($psake.context.currentTaskName) task." 279 | return 280 | } 281 | 282 | $helpLocales = (Get-ChildItem -Path $DocsRootDir -Directory).Name 283 | 284 | # Generate the module's primary MAML help file. 285 | foreach ($locale in $helpLocales) { 286 | New-ExternalHelp -Path $DocsRootDir\$locale -OutputPath $ModuleOutDir\$locale -Force ` 287 | -ErrorAction SilentlyContinue -Verbose:$VerbosePreference > $null 288 | } 289 | } 290 | 291 | Task BuildUpdatableHelp -depends BuildHelp, BeforeBuildUpdatableHelp, CoreBuildUpdatableHelp, AfterBuildUpdatableHelp { 292 | } 293 | 294 | Task CoreBuildUpdatableHelp -requiredVariables DocsRootDir, ModuleName, UpdatableHelpOutDir { 295 | if (!(Get-Module platyPS -ListAvailable)) { 296 | "platyPS module is not installed. Skipping $($psake.context.currentTaskName) task." 297 | return 298 | } 299 | 300 | $helpLocales = (Get-ChildItem -Path $DocsRootDir -Directory).Name 301 | 302 | # Create updatable help output directory. 303 | if (!(Test-Path -LiteralPath $UpdatableHelpOutDir)) { 304 | New-Item $UpdatableHelpOutDir -ItemType Directory -Verbose:$VerbosePreference > $null 305 | } 306 | else { 307 | Write-Verbose "$($psake.context.currentTaskName) - directory already exists '$UpdatableHelpOutDir'." 308 | Get-ChildItem $UpdatableHelpOutDir | Remove-Item -Recurse -Force -Verbose:$VerbosePreference 309 | } 310 | 311 | # Generate updatable help files. Note: this will currently update the version number in the module's MD 312 | # file in the metadata. 313 | foreach ($locale in $helpLocales) { 314 | New-ExternalHelpCab -CabFilesFolder $ModuleOutDir\$locale -LandingPagePath $DocsRootDir\$locale\$ModuleName.md ` 315 | -OutputFolder $UpdatableHelpOutDir -Verbose:$VerbosePreference > $null 316 | } 317 | } 318 | 319 | Task GenerateFileCatalog -depends Build, BuildHelp, BeforeGenerateFileCatalog, CoreGenerateFileCatalog, AfterGenerateFileCatalog { 320 | } 321 | 322 | Task CoreGenerateFileCatalog -requiredVariables CatalogGenerationEnabled, CatalogVersion, ModuleName, ModuleOutDir, OutDir { 323 | if (!$CatalogGenerationEnabled) { 324 | "FileCatalog generation is not enabled. Skipping $($psake.context.currentTaskName) task." 325 | return 326 | } 327 | 328 | if (!(Get-Command Microsoft.PowerShell.Security\New-FileCatalog -ErrorAction SilentlyContinue)) { 329 | "FileCatalog commands not available on this version of PowerShell. Skipping $($psake.context.currentTaskName) task." 330 | return 331 | } 332 | 333 | $catalogFilePath = "$OutDir\$ModuleName.cat" 334 | 335 | $newFileCatalogParams = @{ 336 | Path = $ModuleOutDir 337 | CatalogFilePath = $catalogFilePath 338 | CatalogVersion = $CatalogVersion 339 | Verbose = $VerbosePreference 340 | } 341 | 342 | Microsoft.PowerShell.Security\New-FileCatalog @newFileCatalogParams > $null 343 | 344 | if ($ScriptSigningEnabled) { 345 | if ($SharedProperties.CodeSigningCertificate) { 346 | $setAuthSigParams = @{ 347 | FilePath = $catalogFilePath 348 | Certificate = $SharedProperties.CodeSigningCertificate 349 | Verbose = $VerbosePreference 350 | } 351 | 352 | $result = Microsoft.PowerShell.Security\Set-AuthenticodeSignature @setAuthSigParams 353 | if ($result.Status -ne 'Valid') { 354 | throw "Failed to sign file catalog: $($catalogFilePath)." 355 | } 356 | 357 | "Successfully signed file catalog: $($catalogFilePath)" 358 | } 359 | else { 360 | "No code-signing certificate was found to sign the file catalog." 361 | } 362 | } 363 | else { 364 | "Script signing is not enabled. Skipping signing of file catalog." 365 | } 366 | 367 | Move-Item -LiteralPath $newFileCatalogParams.CatalogFilePath -Destination $ModuleOutDir 368 | } 369 | 370 | Task Install -depends Build, BuildHelp, GenerateFileCatalog, BeforeInstall, CoreInstall, AfterInstall { 371 | } 372 | 373 | Task CoreInstall -requiredVariables ModuleOutDir { 374 | if (!(Test-Path -LiteralPath $InstallPath)) { 375 | Write-Verbose 'Creating install directory' 376 | New-Item -Path $InstallPath -ItemType Directory -Verbose:$VerbosePreference > $null 377 | } 378 | 379 | Copy-Item -Path $ModuleOutDir\* -Destination $InstallPath -Verbose:$VerbosePreference -Recurse -Force 380 | "Module installed into $InstallPath" 381 | } 382 | 383 | Task Test -depends Build -requiredVariables TestRootDir, ModuleName, CodeCoverageEnabled, CodeCoverageFiles { 384 | if (!(Get-Module Pester -ListAvailable)) { 385 | "Pester module is not installed. Skipping $($psake.context.currentTaskName) task." 386 | return 387 | } 388 | 389 | Import-Module Pester 390 | 391 | try { 392 | Microsoft.PowerShell.Management\Push-Location -LiteralPath $TestRootDir 393 | 394 | if ($TestOutputFile) { 395 | $testing = @{ 396 | OutputFile = $TestOutputFile 397 | OutputFormat = $TestOutputFormat 398 | PassThru = $true 399 | Verbose = $VerbosePreference 400 | } 401 | } 402 | else { 403 | $testing = @{ 404 | PassThru = $true 405 | Verbose = $VerbosePreference 406 | } 407 | } 408 | 409 | # To control the Pester code coverage, a boolean $CodeCoverageEnabled is used. 410 | if ($CodeCoverageEnabled) { 411 | $testing.CodeCoverage = $CodeCoverageFiles 412 | } 413 | 414 | $testResult = Invoke-Pester @testing 415 | 416 | Assert -conditionToCheck ( 417 | $testResult.FailedCount -eq 0 418 | ) -failureMessage "One or more Pester tests failed, build cannot continue." 419 | 420 | if ($CodeCoverageEnabled) { 421 | $testCoverage = [int]($testResult.CodeCoverage.NumberOfCommandsExecuted / 422 | $testResult.CodeCoverage.NumberOfCommandsAnalyzed * 100) 423 | "Pester code coverage on specified files: ${testCoverage}%" 424 | } 425 | } 426 | finally { 427 | Microsoft.PowerShell.Management\Pop-Location 428 | Remove-Module $ModuleName -ErrorAction SilentlyContinue 429 | } 430 | } 431 | 432 | Task Publish -depends Build, Test, BuildHelp, GenerateFileCatalog, BeforePublish, CorePublish, AfterPublish { 433 | } 434 | 435 | Task CorePublish -requiredVariables SettingsPath, ModuleOutDir { 436 | $publishParams = @{ 437 | Path = $ModuleOutDir 438 | NuGetApiKey = $NuGetApiKey 439 | } 440 | 441 | # Publishing to the PSGallery requires an API key, so get it. 442 | if ($NuGetApiKey) { 443 | "Using script embedded NuGetApiKey" 444 | } 445 | elseif ($NuGetApiKey = GetSetting -Path $SettingsPath -Key NuGetApiKey) { 446 | "Using stored NuGetApiKey" 447 | } 448 | else { 449 | $promptForKeyCredParams = @{ 450 | DestinationPath = $SettingsPath 451 | Message = 'Enter your NuGet API key in the password field' 452 | Key = 'NuGetApiKey' 453 | } 454 | 455 | $cred = PromptUserForCredentialAndStorePassword @promptForKeyCredParams 456 | $NuGetApiKey = $cred.GetNetworkCredential().Password 457 | "The NuGetApiKey has been stored in $SettingsPath" 458 | } 459 | 460 | $publishParams = @{ 461 | Path = $ModuleOutDir 462 | NuGetApiKey = $NuGetApiKey 463 | } 464 | 465 | # If an alternate repository is specified, set the appropriate parameter. 466 | if ($PublishRepository) { 467 | $publishParams['Repository'] = $PublishRepository 468 | } 469 | 470 | # Consider not using -ReleaseNotes parameter when Update-ModuleManifest has been fixed. 471 | if ($ReleaseNotesPath) { 472 | $publishParams['ReleaseNotes'] = @(Get-Content $ReleaseNotesPath) 473 | } 474 | 475 | "Calling Publish-Module..." 476 | Publish-Module @publishParams 477 | } 478 | 479 | ############################################################################### 480 | # Secondary/utility tasks - typically used to manage stored build settings. 481 | ############################################################################### 482 | 483 | Task ? -description 'Lists the available tasks' { 484 | "Available tasks:" 485 | $psake.context.Peek().Tasks.Keys | Sort-Object 486 | } 487 | 488 | Task RemoveApiKey -requiredVariables SettingsPath { 489 | if (GetSetting -Path $SettingsPath -Key NuGetApiKey) { 490 | RemoveSetting -Path $SettingsPath -Key NuGetApiKey 491 | } 492 | } 493 | 494 | Task StoreApiKey -requiredVariables SettingsPath { 495 | $promptForKeyCredParams = @{ 496 | DestinationPath = $SettingsPath 497 | Message = 'Enter your NuGet API key in the password field' 498 | Key = 'NuGetApiKey' 499 | } 500 | 501 | PromptUserForCredentialAndStorePassword @promptForKeyCredParams 502 | "The NuGetApiKey has been stored in $SettingsPath" 503 | } 504 | 505 | Task ShowApiKey -requiredVariables SettingsPath { 506 | $OFS = "" 507 | if ($NuGetApiKey) { 508 | "The embedded (partial) NuGetApiKey is: $($NuGetApiKey[0..7])" 509 | } 510 | elseif ($NuGetApiKey = GetSetting -Path $SettingsPath -Key NuGetApiKey) { 511 | "The stored (partial) NuGetApiKey is: $($NuGetApiKey[0..7])" 512 | } 513 | else { 514 | "The NuGetApiKey has not been provided or stored." 515 | return 516 | } 517 | 518 | "To see the full key, use the task 'ShowFullApiKey'" 519 | } 520 | 521 | Task ShowFullApiKey -requiredVariables SettingsPath { 522 | if ($NuGetApiKey) { 523 | "The embedded NuGetApiKey is: $NuGetApiKey" 524 | } 525 | elseif ($NuGetApiKey = GetSetting -Path $SettingsPath -Key NuGetApiKey) { 526 | "The stored NuGetApiKey is: $NuGetApiKey" 527 | } 528 | else { 529 | "The NuGetApiKey has not been provided or stored." 530 | } 531 | } 532 | 533 | Task RemoveCertSubjectName -requiredVariables SettingsPath { 534 | if (GetSetting -Path $SettingsPath -Key CertSubjectName) { 535 | RemoveSetting -Path $SettingsPath -Key CertSubjectName 536 | } 537 | } 538 | 539 | Task StoreCertSubjectName -requiredVariables SettingsPath { 540 | $certSubjectName = 'CN=' 541 | $certSubjectName += Read-Host -Prompt 'Enter the certificate subject name for script signing. Use exact casing, CN= prefix will be added' 542 | SetSetting -Key CertSubjectName -Value $certSubjectName -Path $SettingsPath 543 | "The new certificate subject name '$certSubjectName' has been stored in ${SettingsPath}." 544 | } 545 | 546 | Task ShowCertSubjectName -requiredVariables SettingsPath { 547 | $CertSubjectName = GetSetting -Path $SettingsPath -Key CertSubjectName 548 | "The stored certificate is: $CertSubjectName" 549 | 550 | $cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert | 551 | Where-Object { $_.Subject -eq $CertSubjectName -and $_.NotAfter -gt (Get-Date) } | 552 | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 553 | 554 | if ($cert) { 555 | "A valid certificate for the subject $CertSubjectName has been found" 556 | } 557 | else { 558 | 'A valid certificate has not been found' 559 | } 560 | } 561 | 562 | ############################################################################### 563 | # Helper functions 564 | ############################################################################### 565 | 566 | function PromptUserForCredentialAndStorePassword { 567 | [Diagnostics.CodeAnalysis.SuppressMessage("PSProvideDefaultParameterValue", '')] 568 | param( 569 | [Parameter()] 570 | [ValidateNotNullOrEmpty()] 571 | [string] 572 | $DestinationPath, 573 | 574 | [Parameter(Mandatory)] 575 | [string] 576 | $Message, 577 | 578 | [Parameter(Mandatory, ParameterSetName = 'SaveSetting')] 579 | [string] 580 | $Key 581 | ) 582 | 583 | $cred = Get-Credential -Message $Message -UserName "ignored" 584 | if ($DestinationPath) { 585 | SetSetting -Key $Key -Value $cred.Password -Path $DestinationPath 586 | } 587 | 588 | $cred 589 | } 590 | 591 | function AddSetting { 592 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function')] 593 | param( 594 | [Parameter(Mandatory)] 595 | [string]$Key, 596 | 597 | [Parameter(Mandatory)] 598 | [string]$Path, 599 | 600 | [Parameter(Mandatory)] 601 | [ValidateNotNull()] 602 | [object]$Value 603 | ) 604 | 605 | switch ($type = $Value.GetType().Name) { 606 | 'securestring' { $setting = $Value | ConvertFrom-SecureString } 607 | default { $setting = $Value } 608 | } 609 | 610 | if (Test-Path -LiteralPath $Path) { 611 | $storedSettings = Import-Clixml -Path $Path 612 | $storedSettings.Add($Key, @($type, $setting)) 613 | $storedSettings | Export-Clixml -Path $Path 614 | } 615 | else { 616 | $parentDir = Split-Path -Path $Path -Parent 617 | if (!(Test-Path -LiteralPath $parentDir)) { 618 | New-Item $parentDir -ItemType Directory > $null 619 | } 620 | 621 | @{$Key = @($type, $setting)} | Export-Clixml -Path $Path 622 | } 623 | } 624 | 625 | function GetSetting { 626 | param( 627 | [Parameter(Mandatory)] 628 | [string]$Key, 629 | 630 | [Parameter(Mandatory)] 631 | [string]$Path 632 | ) 633 | 634 | if (Test-Path -LiteralPath $Path) { 635 | $securedSettings = Import-Clixml -Path $Path 636 | if ($securedSettings.$Key) { 637 | switch ($securedSettings.$Key[0]) { 638 | 'securestring' { 639 | $value = $securedSettings.$Key[1] | ConvertTo-SecureString 640 | $cred = New-Object -TypeName PSCredential -ArgumentList 'jpgr', $value 641 | $cred.GetNetworkCredential().Password 642 | } 643 | default { 644 | $securedSettings.$Key[1] 645 | } 646 | } 647 | } 648 | } 649 | } 650 | 651 | function SetSetting { 652 | param( 653 | [Parameter(Mandatory)] 654 | [string]$Key, 655 | 656 | [Parameter(Mandatory)] 657 | [string]$Path, 658 | 659 | [Parameter(Mandatory)] 660 | [ValidateNotNull()] 661 | [object]$Value 662 | ) 663 | 664 | if (GetSetting -Key $Key -Path $Path) { 665 | RemoveSetting -Key $Key -Path $Path 666 | } 667 | 668 | AddSetting -Key $Key -Value $Value -Path $Path 669 | } 670 | 671 | function RemoveSetting { 672 | param( 673 | [Parameter(Mandatory)] 674 | [string]$Key, 675 | 676 | [Parameter(Mandatory)] 677 | [string]$Path 678 | ) 679 | 680 | if (Test-Path -LiteralPath $Path) { 681 | $storedSettings = Import-Clixml -Path $Path 682 | $storedSettings.Remove($Key) 683 | if ($storedSettings.Count -eq 0) { 684 | Remove-Item -Path $Path 685 | } 686 | else { 687 | $storedSettings | Export-Clixml -Path $Path 688 | } 689 | } 690 | else { 691 | Write-Warning "The build setting file '$Path' has not been created yet." 692 | } 693 | } 694 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/build.settings.ps1: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Customize these properties and tasks for your module. 3 | ############################################################################### 4 | 5 | Properties { 6 | # ----------------------- Basic properties -------------------------------- 7 | 8 | # The root directories for the module's docs, src and test. 9 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 10 | $DocsRootDir = "$PSScriptRoot\docs" 11 | $SrcRootDir = "$PSScriptRoot\src" 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 13 | $TestRootDir = "$PSScriptRoot\test" 14 | 15 | # The name of your module should match the basename of the PSD1 file. 16 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 17 | $ModuleName = Get-Item $SrcRootDir/*.psd1 | 18 | Where-Object { $null -ne (Test-ModuleManifest -Path $_ -ErrorAction SilentlyContinue) } | 19 | Select-Object -First 1 | Foreach-Object BaseName 20 | 21 | # The $OutDir is where module files and updatable help files are staged for signing, install and publishing. 22 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 23 | $OutDir = "$PSScriptRoot\Release" 24 | 25 | # The local installation directory for the install task. Defaults to your home Modules location. 26 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 27 | $InstallPath = Join-Path (Split-Path $profile.CurrentUserAllHosts -Parent) ` 28 | "Modules\$ModuleName\$((Test-ModuleManifest -Path $SrcRootDir\$ModuleName.psd1).Version.ToString())" 29 | 30 | # Default Locale used for help generation, defaults to en-US. 31 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 32 | $DefaultLocale = 'en-US' 33 | 34 | # Items in the $Exclude array will not be copied to the $OutDir e.g. $Exclude = @('.gitattributes') 35 | # Typically you wouldn't put any file under the src dir unless the file was going to ship with 36 | # the module. However, if there are such files, add their $SrcRootDir relative paths to the exclude list. 37 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 38 | $Exclude = @() 39 | 40 | # ------------------ Script analysis properties --------------------------- 41 | 42 | # Enable/disable use of PSScriptAnalyzer to perform script analysis. 43 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 44 | $ScriptAnalysisEnabled = $false 45 | 46 | # When PSScriptAnalyzer is enabled, control which severity level will generate a build failure. 47 | # Valid values are Error, Warning, Information and None. "None" will report errors but will not 48 | # cause a build failure. "Error" will fail the build only on diagnostic records that are of 49 | # severity error. "Warning" will fail the build on Warning and Error diagnostic records. 50 | # "Any" will fail the build on any diagnostic record, regardless of severity. 51 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 52 | [ValidateSet('Error', 'Warning', 'Any', 'None')] 53 | $ScriptAnalysisFailBuildOnSeverityLevel = 'Error' 54 | 55 | # Path to the PSScriptAnalyzer settings file. 56 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 57 | $ScriptAnalyzerSettingsPath = "$PSScriptRoot\ScriptAnalyzerSettings.psd1" 58 | 59 | # ------------------- Script signing properties --------------------------- 60 | 61 | # Set to $true if you want to sign your scripts. You will need to have a code-signing certificate. 62 | # You can specify the certificate's subject name below. If not specified, you will be prompted to 63 | # provide either a subject name or path to a PFX file. After this one time prompt, the value will 64 | # saved for future use and you will no longer be prompted. 65 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 66 | $ScriptSigningEnabled = $false 67 | 68 | # Specify the Subject Name of the certificate used to sign your scripts. Leave it as $null and the 69 | # first time you build, you will be prompted to enter your code-signing certificate's Subject Name. 70 | # This variable is used only if $SignScripts is set to $true. 71 | # 72 | # This does require the code-signing certificate to be installed to your certificate store. If you 73 | # have a code-signing certificate in a PFX file, install the certificate to your certificate store 74 | # with the command below. You may be prompted for the certificate's password. 75 | # 76 | # Import-PfxCertificate -FilePath .\myCodeSigingCert.pfx -CertStoreLocation Cert:\CurrentUser\My 77 | # 78 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 79 | $CertSubjectName = $null 80 | 81 | # Certificate store path. 82 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 83 | $CertPath = "Cert:\" 84 | 85 | # -------------------- File catalog properties ---------------------------- 86 | 87 | # Enable/disable generation of a catalog (.cat) file for the module. 88 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 89 | $CatalogGenerationEnabled = $true 90 | 91 | # Select the hash version to use for the catalog file: 1 for SHA1 (compat with Windows 7 and 92 | # Windows Server 2008 R2), 2 for SHA2 to support only newer Windows versions. 93 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 94 | $CatalogVersion = 2 95 | 96 | # ---------------------- Testing properties ------------------------------- 97 | 98 | # Enable/disable Pester code coverage reporting. 99 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 100 | $CodeCoverageEnabled = $true 101 | 102 | # CodeCoverageFiles specifies the files to perform code coverage analysis on. This property 103 | # acts as a direct input to the Pester -CodeCoverage parameter, so will support constructions 104 | # like the ones found here: https://github.com/pester/Pester/wiki/Code-Coverage. 105 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 106 | $CodeCoverageFiles = "$SrcRootDir\*.ps1", "$SrcRootDir\*.psm1" 107 | 108 | # -------------------- Publishing properties ------------------------------ 109 | 110 | # Your NuGet API key for the PSGallery. Leave it as $null and the first time you publish, 111 | # you will be prompted to enter your API key. The build will store the key encrypted in the 112 | # settings file, so that on subsequent publishes you will no longer be prompted for the API key. 113 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 114 | $NuGetApiKey = $null 115 | 116 | # Name of the repository you wish to publish to. If $null is specified the default repo (PowerShellGallery) is used. 117 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 118 | $PublishRepository = $null 119 | 120 | # Path to the release notes file. Set to $null if the release notes reside in the manifest file. 121 | # The contents of this file are used during publishing for the ReleaseNotes parameter. 122 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 123 | $ReleaseNotesPath = "$PSScriptRoot\ReleaseNotes.md" 124 | 125 | # ----------------------- Misc properties --------------------------------- 126 | 127 | # In addition, PFX certificates are supported in an interactive scenario only, 128 | # as a way to import a certificate into the user personal store for later use. 129 | # This can be provided using the CertPfxPath parameter. PFX passwords will not be stored. 130 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 131 | $SettingsPath = "$env:LOCALAPPDATA\Plaster\NewModuleTemplate\SecuredBuildSettings.clixml" 132 | 133 | # Specifies an output file path to send to Invoke-Pester's -OutputFile parameter. 134 | # This is typically used to write out test results so that they can be sent to a CI 135 | # system like AppVeyor. 136 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 137 | $TestOutputFile = $null 138 | 139 | # Specifies the test output format to use when the TestOutputFile property is given 140 | # a path. This parameter is passed through to Invoke-Pester's -OutputFormat parameter. 141 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 142 | $TestOutputFormat = "NUnitXml" 143 | } 144 | 145 | ############################################################################### 146 | # Customize these tasks for performing operations before and/or after file staging. 147 | ############################################################################### 148 | 149 | # Executes before the StageFiles task. 150 | Task BeforeStageFiles { 151 | } 152 | 153 | # Executes after the StageFiles task. 154 | Task AfterStageFiles { 155 | } 156 | 157 | ############################################################################### 158 | # Customize these tasks for performing operations before and/or after Build. 159 | ############################################################################### 160 | 161 | # Executes before the BeforeStageFiles phase of the Build task. 162 | Task BeforeBuild { 163 | } 164 | 165 | # Executes after the Build task. 166 | Task AfterBuild { 167 | } 168 | 169 | ############################################################################### 170 | # Customize these tasks for performing operations before and/or after BuildHelp. 171 | ############################################################################### 172 | 173 | # Executes before the BuildHelp task. 174 | Task BeforeBuildHelp { 175 | } 176 | 177 | # Executes after the BuildHelp task. 178 | Task AfterBuildHelp { 179 | } 180 | 181 | ############################################################################### 182 | # Customize these tasks for performing operations before and/or after BuildUpdatableHelp. 183 | ############################################################################### 184 | 185 | # Executes before the BuildUpdatableHelp task. 186 | Task BeforeBuildUpdatableHelp { 187 | } 188 | 189 | # Executes after the BuildUpdatableHelp task. 190 | Task AfterBuildUpdatableHelp { 191 | } 192 | 193 | ############################################################################### 194 | # Customize these tasks for performing operations before and/or after GenerateFileCatalog. 195 | ############################################################################### 196 | 197 | # Executes before the GenerateFileCatalog task. 198 | Task BeforeGenerateFileCatalog { 199 | } 200 | 201 | # Executes after the GenerateFileCatalog task. 202 | Task AfterGenerateFileCatalog { 203 | } 204 | 205 | ############################################################################### 206 | # Customize these tasks for performing operations before and/or after Install. 207 | ############################################################################### 208 | 209 | # Executes before the Install task. 210 | Task BeforeInstall { 211 | } 212 | 213 | # Executes after the Install task. 214 | Task AfterInstall { 215 | } 216 | 217 | ############################################################################### 218 | # Customize these tasks for performing operations before and/or after Publish. 219 | ############################################################################### 220 | 221 | # Executes before the Publish task. 222 | Task BeforePublish { 223 | } 224 | 225 | # Executes after the Publish task. 226 | Task AfterPublish { 227 | } 228 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/docs/en-US/about_Module.help.md: -------------------------------------------------------------------------------- 1 | # <%=$PLASTER_PARAM_ModuleName%> 2 | ## about_<%=$PLASTER_PARAM_ModuleName%> 3 | 4 | ``` 5 | ABOUT TOPIC NOTE: 6 | The first header of the about topic should be the topic name. 7 | The second header contains the lookup name used by the help system. 8 | 9 | IE: 10 | # Some Help Topic Name 11 | ## SomeHelpTopicFileName 12 | 13 | This will be transformed into the text file 14 | as `about_SomeHelpTopicFileName`. 15 | Do not include file extensions. 16 | The second header should have no spaces. 17 | ``` 18 | 19 | # SHORT DESCRIPTION 20 | {{ Short Description Placeholder }} 21 | 22 | ``` 23 | ABOUT TOPIC NOTE: 24 | About topics can be no longer than 80 characters wide when rendered to text. 25 | Any topics greater than 80 characters will be automatically wrapped. 26 | The generated about topic will be encoded UTF-8. 27 | ``` 28 | 29 | # LONG DESCRIPTION 30 | {{ Long Description Placeholder }} 31 | 32 | ## Optional Subtopics 33 | {{ Optional Subtopic Placeholder }} 34 | 35 | # EXAMPLES 36 | {{ Code or descriptive examples of how to leverage the functions described. }} 37 | 38 | # NOTE 39 | {{ Note Placeholder - Additional information that a user needs to know.}} 40 | 41 | # TROUBLESHOOTING NOTE 42 | {{ Troubleshooting Placeholder - Warns users of bugs}} 43 | 44 | {{ Explains behavior that is likely to change with fixes }} 45 | 46 | # SEE ALSO 47 | {{ See also placeholder }} 48 | 49 | {{ You can also list related articles, blogs, and video URLs. }} 50 | 51 | # KEYWORDS 52 | {{List alternate names or titles for this topic that readers might use.}} 53 | 54 | - {{ Keyword Placeholder }} 55 | - {{ Keyword Placeholder }} 56 | - {{ Keyword Placeholder }} 57 | - {{ Keyword Placeholder }} 58 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/docs/en-US/about_Module.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | <%=$PLASTER_PARAM_ModuleName%> 3 | 4 | SHORT DESCRIPTION 5 | You should considering providing an introduction and overview of your 6 | module here. 7 | 8 | LONG DESCRIPTION 9 | Discuss what the "primary" commands are in your module especially if 10 | there are many commands. Discuss how to get started using this module 11 | providing examples where appropriate. 12 | 13 | YOUR HEADER HERE 14 | About topics in PowerShell are free-form text. Add headers to your 15 | about topic as necessary. -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/editor/VSCode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | //-------- Files configuration -------- 3 | 4 | // When enabled, will trim trailing whitespace when you save a file. 5 | "files.trimTrailingWhitespace": true, 6 | 7 | 8 | //-------- PowerShell Configuration -------- 9 | 10 | // Use a custom PowerShell Script Analyzer settings file for this workspace. 11 | // Relative paths for this setting are always relative to the workspace root dir. 12 | "powershell.scriptAnalysis.settingsPath": "src/ScriptAnalyzerSettings.psd1" 13 | } -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/editor/VSCode/tasks_pester.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "0.1.0", 13 | 14 | // Start PowerShell 15 | "windows": { 16 | "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" 17 | }, 18 | "linux": { 19 | "command": "/usr/bin/powershell" 20 | }, 21 | "osx": { 22 | "command": "/usr/local/bin/powershell" 23 | }, 24 | 25 | // The command is a shell script 26 | "isShellCommand": true, 27 | 28 | // Show the output window always 29 | "showOutput": "always", 30 | 31 | "args": [ 32 | "-NoProfile", "-ExecutionPolicy", "Bypass" 33 | ], 34 | 35 | // Associate with test task runner 36 | "tasks": [ 37 | { 38 | "taskName": "Test", 39 | "suppressTaskName": true, 40 | "isTestCommand": true, 41 | "showOutput": "always", 42 | "args": [ 43 | "Write-Host 'Invoking Pester'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};", 44 | "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" 45 | ], 46 | "problemMatcher": [ 47 | { 48 | "owner": "powershell", 49 | "fileLocation": ["absolute"], 50 | "severity": "error", 51 | "pattern": [ 52 | { 53 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 54 | "message": 1 55 | }, 56 | { 57 | "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", 58 | "file": 1, 59 | "line": 2 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/editor/VSCode/tasks_psake.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "0.1.0", 13 | 14 | // Start PowerShell 15 | "windows": { 16 | "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" 17 | }, 18 | "linux": { 19 | "command": "/usr/bin/powershell" 20 | }, 21 | "osx": { 22 | "command": "/usr/local/bin/powershell" 23 | }, 24 | 25 | // The command is a shell script 26 | "isShellCommand": true, 27 | 28 | // Show the output window always 29 | "showOutput": "always", 30 | 31 | "args": [ 32 | "-NoProfile", "-ExecutionPolicy", "Bypass" 33 | ], 34 | 35 | // Associate with test task runner 36 | "tasks": [ 37 | { 38 | "taskName": "Clean", 39 | "suppressTaskName": true, 40 | "showOutput": "always", 41 | "args": [ 42 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Clean'; Invoke-psake build.psake.ps1 -taskList Clean;", 43 | "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }" 44 | ] 45 | }, 46 | { 47 | "taskName": "Build", 48 | "suppressTaskName": true, 49 | "isBuildCommand": true, 50 | "showOutput": "always", 51 | "args": [ 52 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Build'; Invoke-psake build.psake.ps1 -taskList Build;", 53 | "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" 54 | ] 55 | }, 56 | { 57 | "taskName": "BuildHelp", 58 | "suppressTaskName": true, 59 | "showOutput": "always", 60 | "args": [ 61 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList BuildHelp'; Invoke-psake build.psake.ps1 -taskList BuildHelp;", 62 | "Invoke-Command { Write-Host 'Completed BuildHelp task in task runner.' }" 63 | ] 64 | }, 65 | { 66 | "taskName": "Analyze", 67 | "suppressTaskName": true, 68 | "showOutput": "always", 69 | "args": [ 70 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Analyze'; Invoke-psake build.psake.ps1 -taskList Analyze;", 71 | "Invoke-Command { Write-Host 'Completed Analyze task in task runner.' }" 72 | ] 73 | }, 74 | { 75 | "taskName": "Install", 76 | "suppressTaskName": true, 77 | "showOutput": "always", 78 | "args": [ 79 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Install'; Invoke-psake build.psake.ps1 -taskList Install;", 80 | "Invoke-Command { Write-Host 'Completed Install task in task runner.' }" 81 | ] 82 | }, 83 | { 84 | "taskName": "Publish", 85 | "suppressTaskName": true, 86 | "showOutput": "always", 87 | "args": [ 88 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Publish'; Invoke-psake build.psake.ps1 -taskList Publish;", 89 | "Invoke-Command { Write-Host 'Completed Publish task in task runner.' }" 90 | ] 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/editor/VSCode/tasks_psake_pester.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${relativeFile}: the current opened file relative to workspaceRoot 5 | // ${fileBasename}: the current opened file's basename 6 | // ${fileDirname}: the current opened file's dirname 7 | // ${fileExtname}: the current opened file's extension 8 | // ${cwd}: the current working directory of the spawned process 9 | { 10 | // See https://go.microsoft.com/fwlink/?LinkId=733558 11 | // for the documentation about the tasks.json format 12 | "version": "0.1.0", 13 | 14 | // Start PowerShell 15 | "windows": { 16 | "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" 17 | }, 18 | "linux": { 19 | "command": "/usr/bin/powershell" 20 | }, 21 | "osx": { 22 | "command": "/usr/local/bin/powershell" 23 | }, 24 | 25 | // The command is a shell script 26 | "isShellCommand": true, 27 | 28 | // Show the output window always 29 | "showOutput": "always", 30 | 31 | "args": [ 32 | "-NoProfile", "-ExecutionPolicy", "Bypass" 33 | ], 34 | 35 | // Associate with test task runner 36 | "tasks": [ 37 | { 38 | "taskName": "Clean", 39 | "suppressTaskName": true, 40 | "showOutput": "always", 41 | "args": [ 42 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Clean'; Invoke-psake build.psake.ps1 -taskList Clean;", 43 | "Invoke-Command { Write-Host 'Completed Clean task in task runner.' }" 44 | ] 45 | }, 46 | { 47 | "taskName": "Build", 48 | "suppressTaskName": true, 49 | "isBuildCommand": true, 50 | "showOutput": "always", 51 | "args": [ 52 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Build'; Invoke-psake build.psake.ps1 -taskList Build;", 53 | "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" 54 | ] 55 | }, 56 | { 57 | "taskName": "BuildHelp", 58 | "suppressTaskName": true, 59 | "showOutput": "always", 60 | "args": [ 61 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList BuildHelp'; Invoke-psake build.psake.ps1 -taskList BuildHelp;", 62 | "Invoke-Command { Write-Host 'Completed BuildHelp task in task runner.' }" 63 | ] 64 | }, 65 | { 66 | "taskName": "Analyze", 67 | "suppressTaskName": true, 68 | "showOutput": "always", 69 | "args": [ 70 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Analyze'; Invoke-psake build.psake.ps1 -taskList Analyze;", 71 | "Invoke-Command { Write-Host 'Completed Analyze task in task runner.' }" 72 | ] 73 | }, 74 | { 75 | "taskName": "Install", 76 | "suppressTaskName": true, 77 | "showOutput": "always", 78 | "args": [ 79 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Install'; Invoke-psake build.psake.ps1 -taskList Install;", 80 | "Invoke-Command { Write-Host 'Completed Install task in task runner.' }" 81 | ] 82 | }, 83 | { 84 | "taskName": "Publish", 85 | "suppressTaskName": true, 86 | "showOutput": "always", 87 | "args": [ 88 | "Write-Host 'Invoking psake on build.psake.ps1 -taskList Publish'; Invoke-psake build.psake.ps1 -taskList Publish;", 89 | "Invoke-Command { Write-Host 'Completed Publish task in task runner.' }" 90 | ] 91 | }, 92 | { 93 | "taskName": "Test", 94 | "suppressTaskName": true, 95 | "isTestCommand": true, 96 | "showOutput": "always", 97 | "args": [ 98 | "Write-Host 'Invoking Pester'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};", 99 | "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" 100 | ], 101 | "problemMatcher": [ 102 | { 103 | "owner": "powershell", 104 | "fileLocation": ["absolute"], 105 | "severity": "error", 106 | "pattern": [ 107 | { 108 | "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", 109 | "message": 1 110 | }, 111 | { 112 | "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", 113 | "file": 1, 114 | "line": 2 115 | } 116 | ] 117 | } 118 | ] 119 | } 120 | ] 121 | } 122 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/license/Apache.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright <%=$PLASTER_YEAR%> <%=$PLASTER_PARAM_FullName%> 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/license/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) <%=$PLASTER_YEAR%> <%=$PLASTER_PARAM_FullName%> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/plasterManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | d9385057-1b55-4e97-8e37-10eab8ad33db 5 | NewAdvancedPowerShellModule 6 | New Advanced PowerShell Module 7 | Scaffolds the files required for a PowerShell module with build support. 8 | 1.0.0 9 | Module, ModuleManifest, Build 10 | 11 | 12 | 13 | 16 | 17 | 20 | 21 | 25 | 26 | 30 | 31 | 36 | 39 | 42 | 45 | 46 | 47 | 52 | 55 | 58 | 61 | 64 | 67 | 70 | 71 | 72 | 77 | 80 | 83 | 84 | 85 | 86 | 87 | Scaffolding your PowerShell Module... 88 | 89 | 95 | 96 | 99 | 102 | 104 | 106 | 108 | 109 | 112 | 115 | 118 | 121 | 125 | 128 | 129 | 132 | 133 | 136 | 137 | 140 | 141 | 144 | 145 | 147 | 148 | 150 | 151 | 153 | 154 | 156 | 157 | 158 | 159 | Your new PowerShell module project '$PLASTER_PARAM_ModuleName' has been created. 160 | 161 | 162 | 163 | 164 | A Pester test has been created to validate the module's manifest file. Add additional tests to the test directory. 165 | You can run the Pester tests in your project by executing the 'test' task. Press Ctrl+P, then type 'task test'. 166 | 167 | 168 | 169 | 170 | You can build your project by executing the 'build' task. Press Ctrl+P, then type 'task build'. 171 | You can publish your project to the PSGallery by pressing Ctrl+P, then type 'task publish'. 172 | 173 | 174 | 175 | 176 | You can generate help and additional documentation using platyPS by running the 'build help' task. Press Ctrl+P, 177 | then type 'task build help'. Add additional documentation written in platyPS markdown to the docs directory. You can 178 | update the help by running the 'build help' task again. 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/src/Module.psm1: -------------------------------------------------------------------------------- 1 | # Implement your module commands in this script. 2 | 3 | 4 | # Export only the functions using PowerShell standard verb-noun naming. 5 | Export-ModuleMember -Function *-* 6 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/test/Module.T.ps1: -------------------------------------------------------------------------------- 1 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '', Scope='*', Target='SuppressImportModule')] 2 | $SuppressImportModule = $true 3 | . $PSScriptRoot\Shared.ps1 4 | 5 | Describe 'Module Manifest Tests' { 6 | It 'Passes Test-ModuleManifest' { 7 | Test-ModuleManifest -Path $ModuleManifestPath 8 | $? | Should Be $true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Templates/NewAdvancedPowerShellModule/test/Shared.ps1: -------------------------------------------------------------------------------- 1 | # Dot source this script in any Pester test script that requires the module to be imported. 2 | 3 | $ModuleManifestName = '<%=$PLASTER_PARAM_ModuleName%>.psd1' 4 | $ModuleManifestPath = "$PSScriptRoot\..\src\$ModuleManifestName" 5 | 6 | if (!$SuppressImportModule) { 7 | # -Scope Global is needed when running tests from inside of psake, otherwise 8 | # the module's functions cannot be found in the <%=$PLASTER_PARAM_ModuleName%>\ namespace 9 | Import-Module $ModuleManifestPath -Scope Global 10 | } 11 | -------------------------------------------------------------------------------- /src/en-US/PlasterBuild.Resources.psd1: -------------------------------------------------------------------------------- 1 | # Localized PlasterBuild Resources 2 | 3 | ConvertFrom-StringData @' 4 | ###PSLOC 5 | 6 | ###PSLOC 7 | '@ 8 | -------------------------------------------------------------------------------- /test/ModuleManifest.Tests.ps1: -------------------------------------------------------------------------------- 1 | [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')] 2 | $SuppressImportModule = $true 3 | . $PSScriptRoot\Shared.ps1 4 | 5 | Describe 'Module Manifest Tests' { 6 | It 'Passes Test-ModuleManifest' { 7 | Test-ModuleManifest -Path $ModuleManifestPath 8 | $? | Should Be $true 9 | } 10 | } -------------------------------------------------------------------------------- /test/Shared.ps1: -------------------------------------------------------------------------------- 1 | $ModuleManifestName = 'PlasterBuild.psd1' 2 | $ModuleManifestPath = "$PSScriptRoot\..\src\$ModuleManifestName" 3 | 4 | if (!$SuppressImportModule) { 5 | # -Scope Global is needed when running tests from inside of psake, otherwise 6 | # the module's functions cannot be found in the Plaster\ namespace 7 | Import-Module $ModuleManifestPath -Scope Global 8 | } 9 | --------------------------------------------------------------------------------