From f073e2175be686f93c071801a7dbea9f3f4cc630 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 30 Sep 2021 17:09:39 +0200 Subject: [PATCH 1/5] Update repository to use the latest pipeline --- .gitattributes | 10 +- .../ISSUE_TEMPLATE/Problem_with_resource.md | 63 --- .../ISSUE_TEMPLATE/Problem_with_resource.yml | 99 ++++ .github/ISSUE_TEMPLATE/Resource_proposal.md | 21 - .github/ISSUE_TEMPLATE/Resource_proposal.yml | 39 ++ .github/ISSUE_TEMPLATE/config.yml | 6 + .github/PULL_REQUEST_TEMPLATE.md | 12 +- .gitignore | 2 + .vscode/settings.json | 6 +- .vscode/tasks.json | 2 +- CHANGELOG.md | 4 +- CONTRIBUTING.md | 4 + LICENSE | 10 +- README.md | 19 +- RequiredModules.psd1 | 20 +- Resolve-Dependency.ps1 | 350 ++++++++----- SECURITY.md | 30 ++ azure-pipelines.yml | 113 +++-- build.ps1 | 460 ++++++++++++------ build.yaml | 34 +- codecov.yml | 29 ++ source/WikiSource/Home.md | 45 ++ source/xExchange.psd1 | 4 +- 23 files changed, 949 insertions(+), 433 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.md create mode 100644 .github/ISSUE_TEMPLATE/Problem_with_resource.yml delete mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.md create mode 100644 .github/ISSUE_TEMPLATE/Resource_proposal.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 SECURITY.md create mode 100644 codecov.yml create mode 100644 source/WikiSource/Home.md diff --git a/.gitattributes b/.gitattributes index bd003ef1..ef41ffcb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,9 @@ # Needed for publishing of examples, build worker defaults to core.autocrlf=input. -* text eol=crlf +* text eol=autocrlf + +*.mof text eol=crlf +*.sh text eol=lf +*.svg eol=lf # Ensure any exe files are treated as binary *.exe binary @@ -10,3 +14,7 @@ # Allow test .PFX files to be saved as binary files *.pfx binary + +*.png binary +*.dll binary +*.so binary diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.md b/.github/ISSUE_TEMPLATE/Problem_with_resource.md deleted file mode 100644 index 11a6230b..00000000 --- a/.github/ISSUE_TEMPLATE/Problem_with_resource.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Problem with a resource -about: If you have a problem, bug, or enhancement with a resource in this resource module. ---- - -#### Details of the scenario you tried and the problem that is occurring - -#### Verbose logs showing the problem - -#### Suggested solution to the issue - -#### The DSC configuration that is used to reproduce the issue (as detailed as possible) -```powershell -# insert configuration here -``` - -#### Exchange Server edition and version the target node is running - -#### The operating system the target node is running - - -#### Version and build of PowerShell the target node is running - - -#### Version of the DSC module that was used - diff --git a/.github/ISSUE_TEMPLATE/Problem_with_resource.yml b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml new file mode 100644 index 00000000..322c461e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Problem_with_resource.yml @@ -0,0 +1,99 @@ +name: Problem with a resource +description: If you have a problem, bug, or enhancement with a resource in this resource module. +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please prefix the issue title (above) with the resource name, e.g. 'ResourceName: Short description of my issue'! + + Your feedback and support is greatly appreciated, thanks for contributing! + - type: textarea + id: description + attributes: + label: Problem description + description: Details of the scenario you tried and the problem that is occurring. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Verbose logs + description: | + Verbose logs showing the problem. **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as plain text._ + placeholder: | + Paste verbose logs here + render: text + validations: + required: true + - type: textarea + id: configuration + attributes: + label: DSC configuration + description: | + The DSC configuration that is used to reproduce the issue (as detailed as possible). **NOTE! Sensitive information should be obfuscated.** _Will be automatically formatted as PowerShell code._ + placeholder: | + Paste DSC configuration here + render: powershell + validations: + required: true + - type: textarea + id: suggestedSolution + attributes: + label: Suggested solution + description: Do you have any suggestions how to solve the issue? + validations: + required: true + - type: textarea + id: targetNodeOS + attributes: + label: Operating system the target node is running + description: | + Please provide as much as possible about the target node, for example edition, version, build, and language. _Will be automatically formatted as plain text._ + + On OS with WMF 5.1 the following command can help get this information: `Get-ComputerInfo -Property @('OsName','OsOperatingSystemSKU','OSArchitecture','WindowsVersion','WindowsBuildLabEx','OsLanguage','OsMuiLanguages')` + placeholder: | + Add operating system information here + render: text + validations: + required: true + - type: textarea + id: exchangeVersion + attributes: + label: Exchange Server edition and version the target node is running + description: | + Please provide as much as possible about the Exchange Server that runs on the target node, for example edition, version, build, and language. _Will be automatically formatted as plain text._ + + The following command can help get this information: `Get-ExchangeServer | ft ServerRole, Edition, AdminDisplayVersion` + placeholder: | + Add Exchange Server information here + render: text + validations: + required: true + - type: textarea + id: targetNodePS + attributes: + label: PowerShell version and build the target node is running + description: | + Please provide the version and build of PowerShell the target node is running. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `$PSVersionTable` + placeholder: | + Add PowerShell information here + render: text + validations: + required: true + - type: textarea + id: moduleVersion + attributes: + label: xExchange version + description: | + Please provide the version of the xExchange module that was used. _Will be automatically formatted as plain text._ + + To help with this information, please run this command: `Get-Module -Name 'xExchange' -ListAvailable | ft Name,Version,Path` + placeholder: | + Add module information here + render: text + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.md b/.github/ISSUE_TEMPLATE/Resource_proposal.md deleted file mode 100644 index 9f2a069a..00000000 --- a/.github/ISSUE_TEMPLATE/Resource_proposal.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: New resource proposal -about: If you have a new resource proposal that you think should be added to this resource module. ---- - -### Description - -### Proposed properties - -### Special considerations or limitations diff --git a/.github/ISSUE_TEMPLATE/Resource_proposal.yml b/.github/ISSUE_TEMPLATE/Resource_proposal.yml new file mode 100644 index 00000000..2ddd0986 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Resource_proposal.yml @@ -0,0 +1,39 @@ +name: New resource proposal +description: If you have a new resource proposal that you think should be added to this resource module. +title: "NewResourceName: New resource proposal" +labels: [] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please replace `NewResourceName` in the issue title (above) with your proposed resource name. + + Thank you for contributing and making this resource module better! + - type: textarea + id: description + attributes: + label: Resource proposal + description: Provide information how this resource will/should work and how it will help users. + validations: + required: true + - type: textarea + id: proposedProperties + attributes: + label: Proposed properties + description: | + List all the proposed properties that the resource should have (key, required, write, and/or read). For each property provide a detailed description, the data type, if a default value should be used, and if the property is limited to a set of values. + value: | + Property | Type qualifier | Data type | Description | Default value | Allowed values + --- | --- | --- | --- | --- | --- + PropertyName | Key | String | Detailed description | None | None + validations: + required: true + - type: textarea + id: considerations + attributes: + label: Special considerations or limitations + description: | + Provide any considerations or limitations you can think of that a contributor should take in account when coding the proposed resource, and or what limitations a user will encounter or should consider when using the proposed resource. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..9917040e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: false +contact_links: + - name: "Virtual PowerShell User Group #DSC channel" + url: https://dsccommunity.org/community/contact/ + about: "To talk to the community and maintainers of DSC Community, please visit the #DSC channel." + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 458335e6..e9f674de 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,9 @@ You may remove this comment block, and the other comment blocks, but please keep the headers and the task list. --> + #### Pull Request (PR) description + #### This Pull Request (PR) fixes the following issues + #### Task list + + - [ ] Added an entry to the change log under the Unreleased section of the file CHANGELOG.md. Entry should say what was changed and how that affects users (if applicable), and reference the issue being resolved diff --git a/.gitignore b/.gitignore index ea1472ec..ff2de5d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.vs +.vscode output/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 290b1256..8bf1c69c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,10 +10,12 @@ "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", "powershell.codeFormatting.preset": "Custom", "powershell.codeFormatting.alignPropertyValuePairs": true, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, + "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", "powershell.scriptAnalysis.settingsPath": ".vscode\\analyzersettings.psd1", "powershell.scriptAnalysis.enable": true, + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, "files.associations": { "*.ps1xml": "xml" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4085e3e7..29911402 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,7 +41,7 @@ "label": "build", "type": "shell", "command": "&${cwd}/build.ps1", - "args": ["-AutoRestore"], + "args": [], "presentation": { "echo": true, "reveal": "always", diff --git a/CHANGELOG.md b/CHANGELOG.md index 918b3568..519b5de4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,15 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - xExchange - Renamed default branch to `main` ([issue #465](https://github.com/dsccommunity/xExchange/issues/465)). - Merged the historic changelog back into CHANGELOG.md. + - Update repository to use the latest pipeline. - xExchOwaVirtualDirectoryInternal - Added additional parameter SetPhotoEnabled - RequiredModules - Change pester reference to 4.10.1 Pester 5 was released and is a breaking change, so we need to pin 4.10.1. - - Change Sampler reference to 0.109.1 - Sampler 0.110.1 was released and is a breaking change, - so we need to pin 0.109.1. - Add the function Remove-RemoteExchangeSession - xExchAcceptedDomain - Fixing the Get-TargetResource function. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6895ab3..3544bccb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,7 @@ # Contributing Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). + +## Running the Tests + +If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests) diff --git a/LICENSE b/LICENSE index f4c3ee92..0683f758 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The MIT License (MIT) +MIT License Copyright (c) DSC Community contributors. @@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index c6772ca1..d7c3cd95 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,18 @@ # xExchange +This module contains DSC resources for the management and +configuration of Microsoft Exchange Server. + [![Build Status](https://dev.azure.com/dsccommunity/xExchange/_apis/build/status/dsccommunity.xExchange?branchName=main)](https://dev.azure.com/dsccommunity/xExchange/_build/latest?definitionId=15&branchName=main) ![Azure DevOps coverage (branch)](https://img.shields.io/azure-devops/coverage/dsccommunity/xExchange/15/main) +[![codecov](https://codecov.io/gh/dsccommunity/xExchange/branch/main/graph/badge.svg)](https://codecov.io/gh/dsccommunity/xExchange) [![Azure DevOps tests](https://img.shields.io/azure-devops/tests/dsccommunity/xExchange/15/main)](https://dsccommunity.visualstudio.com/xExchange/_test/analytics?definitionId=15&contextType=build) [![PowerShell Gallery (with prereleases)](https://img.shields.io/powershellgallery/vpre/xExchange?label=xExchange%20Preview)](https://www.powershellgallery.com/packages/xExchange/) [![PowerShell Gallery](https://img.shields.io/powershellgallery/v/xExchange?label=xExchange)](https://www.powershellgallery.com/packages/xExchange/) -The **xExchange** module contains many DSC resources for configuring and -managing Exchange 2013, 2016, and 2019 servers including individual -server properties, databases, mount points, and Database Availability Groups. +## Code of Conduct This project has adopted this [Code of Conduct](CODE_OF_CONDUCT.md). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional -questions or comments. ## Releases @@ -26,6 +25,10 @@ full release to [PowerShell Gallery](https://www.powershellgallery.com/). Please check out common DSC Community [contributing guidelines](https://dsccommunity.org/guidelines/contributing). +## Change log + +A full list of changes in each version can be found in the [change log](CHANGELOG.md). + ## Installation To manually install the module, @@ -66,10 +69,6 @@ for more information. You can review the [Examples](/source/Examples) directory in the xExchange module for some general use scenarios for all of the resources that are in the module. -## Change log - -A full list of changes in each version can be found in the [change log](CHANGELOG.md). - ## Resources - **xExchAcceptedDomain** is used to add accepted domains diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 0efc4696..eb9ebde1 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -1,25 +1,27 @@ @{ - # Set up a mini virtual environment... PSDependOptions = @{ AddToPath = $true Target = 'output\RequiredModules' Parameters = @{ - Repository = '' + Repository = 'PSGallery' } } - Sampler = 'latest' - invokeBuild = 'latest' + InvokeBuild = 'latest' PSScriptAnalyzer = 'latest' - pester = '4.10.1' + Pester = '4.10.1' Plaster = 'latest' - ModuleBuilder = '1.0.0' - MarkdownLinkCheck = 'latest' + ModuleBuilder = 'latest' ChangelogManagement = 'latest' - 'DscResource.Test' = '0.14.1' + Sampler = 'latest' + 'Sampler.GitHubTasks' = 'latest' + MarkdownLinkCheck = 'latest' + 'DscResource.Test' = 'latest' 'DscResource.AnalyzerRules' = 'latest' xDscResourceDesigner = 'latest' - 'DscResource.Common' = '0.9.3' + 'DscResource.DocGenerator' = 'latest' + + # Prerequisites modules needed for examples or integration tests 'xPendingReboot' = '0.4.0' 'xWebAdministration' = '3.1.1' } diff --git a/Resolve-Dependency.ps1 b/Resolve-Dependency.ps1 index 4928e624..a8c6b4d1 100644 --- a/Resolve-Dependency.ps1 +++ b/Resolve-Dependency.ps1 @@ -1,108 +1,183 @@ +<# + .DESCRIPTION + Bootstrap script for PSDepend. + + .PARAMETER DependencyFile + Specifies the configuration file for the this script. The default value is + 'RequiredModules.psd1' relative to this script's path. + + .PARAMETER PSDependTarget + Path for PSDepend to be bootstrapped and save other dependencies. + Can also be CurrentUser or AllUsers if you wish to install the modules in + such scope. The default value is './output/RequiredModules' relative to + this script's path. + + .PARAMETER Proxy + Specifies the URI to use for Proxy when attempting to bootstrap + PackageProvider and PowerShellGet. + + .PARAMETER ProxyCredential + Specifies the credential to contact the Proxy when provided. + + .PARAMETER Scope + Specifies the scope to bootstrap the PackageProvider and PSGet if not available. + THe default value is 'CurrentUser'. + + .PARAMETER Gallery + Specifies the gallery to use when bootstrapping PackageProvider, PSGet and + when calling PSDepend (can be overridden in Dependency files). The default + value is 'PSGallery'. + + .PARAMETER GalleryCredential + Specifies the credentials to use with the Gallery specified above. + + .PARAMETER AllowOldPowerShellGetModule + Allow you to use a locally installed version of PowerShellGet older than + 1.6.0 (not recommended). Default it will install the latest PowerShellGet + if an older version than 2.0 is detected. + + .PARAMETER MinimumPSDependVersion + Allow you to specify a minimum version fo PSDepend, if you're after specific + features. + + .PARAMETER AllowPrerelease + Not yet written. + + .PARAMETER WithYAML + Not yet written. + + .NOTES + Load defaults for parameters values from Resolve-Dependency.psd1 if not + provided as parameter. +#> [CmdletBinding()] param ( - [Parameter()] - [String] + [System.String] $DependencyFile = 'RequiredModules.psd1', [Parameter()] - [String] - # Path for PSDepend to be bootstrapped and save other dependencies. - # Can also be CurrentUser or AllUsers if you wish to install the modules in such scope - # Default to $PWD.Path/output/modules - $PSDependTarget = (Join-Path $PSScriptRoot './output/RequiredModules'), + [System.String] + $PSDependTarget = (Join-Path -Path $PSScriptRoot -ChildPath './output/RequiredModules'), [Parameter()] - [uri] - # URI to use for Proxy when attempting to Bootstrap PackageProvider & PowerShellGet + [System.Uri] $Proxy, [Parameter()] - # Credential to contact the Proxy when provided - [PSCredential]$ProxyCredential, + [System.Management.Automation.PSCredential] + $ProxyCredential, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] - [String] - # Scope to bootstrap the PackageProvider and PSGet if not available + [System.String] $Scope = 'CurrentUser', [Parameter()] - [String] - # Gallery to use when bootstrapping PackageProvider, PSGet and when calling PSDepend (can be overridden in Dependency files) + [System.String] $Gallery = 'PSGallery', [Parameter()] - [PSCredential] - # Credentials to use with the Gallery specified above + [System.Management.Automation.PSCredential] $GalleryCredential, - [Parameter()] - [switch] - # Allow you to use a locally installed version of PowerShellGet older than 1.6.0 (not recommended, default to $False) + [System.Management.Automation.SwitchParameter] $AllowOldPowerShellGetModule, [Parameter()] - [String] - # Allow you to specify a minimum version fo PSDepend, if you're after specific features. + [System.String] $MinimumPSDependVersion, [Parameter()] - [Switch] + [System.Management.Automation.SwitchParameter] $AllowPrerelease, [Parameter()] - [Switch] + [System.Management.Automation.SwitchParameter] $WithYAML ) -# Load Defaults for parameters values from Resolve-Dependency.psd1 if not provided as parameter try { - Write-Verbose -Message "Importing Bootstrap default parameters from '$PSScriptRoot/Resolve-Dependency.psd1'." - $ResolveDependencyDefaults = Import-PowerShellDataFile -Path (Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1' -Resolve -ErrorAction Stop) - $ParameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys - if ($ParameterToDefault.Count -eq 0) + if ($PSVersionTable.PSVersion.Major -le 5) { - $ParameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + if (-not (Get-Command -Name 'Import-PowerShellDataFile' -ErrorAction 'SilentlyContinue')) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion '3.1.0.0' + } + + <# + Making sure the imported PackageManagement module is not from PS7 module + path. The VSCode PS extension is changing the $env:PSModulePath and + prioritize the PS7 path. This is an issue with PowerShellGet because + it loads an old version if available (or fail to load latest). + #> + Get-Module -ListAvailable PackageManagement | + Where-Object -Property 'ModuleBase' -NotMatch 'powershell.7' | + Select-Object -First 1 | + Import-Module -Force } - # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option - foreach ($ParamName in $ParameterToDefault) + + Write-Verbose -Message 'Importing Bootstrap default parameters from ''$PSScriptRoot/Resolve-Dependency.psd1''.' + + $resolveDependencyConfigPath = Join-Path -Path $PSScriptRoot -ChildPath '.\Resolve-Dependency.psd1' -Resolve -ErrorAction 'Stop' + + $resolveDependencyDefaults = Import-PowerShellDataFile -Path $resolveDependencyConfigPath + + $parameterToDefault = $MyInvocation.MyCommand.ParameterSets.Where{ $_.Name -eq $PSCmdlet.ParameterSetName }.Parameters.Keys + + if ($parameterToDefault.Count -eq 0) + { + $parameterToDefault = $MyInvocation.MyCommand.Parameters.Keys + } + + # Set the parameters available in the Parameter Set, or it's not possible to choose yet, so all parameters are an option. + foreach ($parameterName in $parameterToDefault) { - if (-Not $PSBoundParameters.Keys.Contains($ParamName) -and $ResolveDependencyDefaults.ContainsKey($ParamName)) + if (-not $PSBoundParameters.Keys.Contains($parameterName) -and $resolveDependencyDefaults.ContainsKey($parameterName)) { - Write-Verbose -Message "Setting $ParamName with $($ResolveDependencyDefaults[$ParamName])" + Write-Verbose -Message "Setting $parameterName with $($resolveDependencyDefaults[$parameterName])." + try { - $variableValue = $ResolveDependencyDefaults[$ParamName] - if ($variableValue -is [string]) + $variableValue = $resolveDependencyDefaults[$parameterName] + + if ($variableValue -is [System.String]) { $variableValue = $ExecutionContext.InvokeCommand.ExpandString($variableValue) } - $PSBoundParameters.Add($ParamName, $variableValue) - Set-Variable -Name $ParamName -value $variableValue -Force -ErrorAction SilentlyContinue + + $PSBoundParameters.Add($parameterName, $variableValue) + + Set-Variable -Name $parameterName -value $variableValue -Force -ErrorAction 'SilentlyContinue' } catch { - Write-Verbose -Message "Error adding default for $ParamName : $($_.Exception.Message)" + Write-Verbose -Message "Error adding default for $parameterName : $($_.Exception.Message)." } } } } catch { - Write-Warning -Message "Error attempting to import Bootstrap's default parameters from $(Join-Path $PSScriptRoot '.\Resolve-Dependency.psd1'): $($_.Exception.Message)." + Write-Warning -Message "Error attempting to import Bootstrap's default parameters from '$resolveDependencyConfigPath': $($_.Exception.Message)." } -Write-Progress -Activity "Bootstrap:" -PercentComplete 0 -CurrentOperation "NuGet Bootstrap" +Write-Progress -Activity 'Bootstrap:' -PercentComplete 0 -CurrentOperation 'NuGet Bootstrap' -if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyContinue)) +# TODO: This should handle the parameter $AllowOldPowerShellGetModule. +$powerShellGetModule = Import-Module -Name 'PowerShellGet' -MinimumVersion '2.0' -ErrorAction 'SilentlyContinue' -PassThru + +# Install the package provider if it is not available. +$nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + +if (-not $powerShellGetModule -and -not $nuGetProvider) { - $providerBootstrapParams = @{ + $providerBootstrapParameters = @{ Name = 'nuget' - force = $true + Force = $true ForceBootstrap = $true ErrorAction = 'Stop' } @@ -111,47 +186,60 @@ if (!(Get-PackageProvider -Name NuGet -ForceBootstrap -ErrorAction SilentlyConti { 'Proxy' { - $providerBootstrapParams.Add('Proxy', $Proxy) + $providerBootstrapParameters.Add('Proxy', $Proxy) } + 'ProxyCredential' { - $providerBootstrapParams.Add('ProxyCredential', $ProxyCredential) + $providerBootstrapParameters.Add('ProxyCredential', $ProxyCredential) } + 'Scope' { - $providerBootstrapParams.Add('Scope', $Scope) + $providerBootstrapParameters.Add('Scope', $Scope) } } if ($AllowPrerelease) { - $providerBootstrapParams.Add('AllowPrerelease', $true) + $providerBootstrapParameters.Add('AllowPrerelease', $true) } - Write-Information "Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed)" + Write-Information -MessageData 'Bootstrap: Installing NuGet Package Provider from the web (Make sure Microsoft addresses/ranges are allowed).' + $null = Install-PackageProvider @providerBootstrapParams - $latestNuGetVersion = (Get-PackageProvider -Name NuGet -ListAvailable | Select-Object -First 1).Version.ToString() - Write-Information "Bootstrap: Importing NuGet Package Provider version $latestNuGetVersion to current session." - $Null = Import-PackageProvider -Name NuGet -RequiredVersion $latestNuGetVersion -Force + + $nuGetProvider = Get-PackageProvider -Name 'NuGet' -ListAvailable | Select-Object -First 1 + + $nuGetProviderVersion = $nuGetProvider.Version.ToString() + + Write-Information -MessageData "Bootstrap: Importing NuGet Package Provider version $nuGetProviderVersion to current session." + + $Null = Import-PackageProvider -Name 'NuGet' -RequiredVersion $nuGetProviderVersion -Force } -Write-Progress -Activity "Bootstrap:" -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" +Write-Progress -Activity 'Bootstrap:' -PercentComplete 10 -CurrentOperation "Ensuring Gallery $Gallery is trusted" + +# Fail if the given PSGallery is not registered. +$previousGalleryInstallationPolicy = (Get-PSRepository -Name $Gallery -ErrorAction 'Stop').InstallationPolicy + +Set-PSRepository -Name $Gallery -InstallationPolicy 'Trusted' -ErrorAction 'Ignore' -# Fail if the given PSGallery is not Registered -$Policy = (Get-PSRepository $Gallery -ErrorAction Stop).InstallationPolicy -Set-PSRepository -Name $Gallery -InstallationPolicy Trusted -ErrorAction Ignore try { - Write-Progress -Activity "Bootstrap:" -PercentComplete 25 -CurrentOperation "Checking PowerShellGet" - # Ensure the module is loaded and retrieve the version you have - $PowerShellGetVersion = (Import-Module PowerShellGet -PassThru -ErrorAction SilentlyContinue).Version + Write-Progress -Activity 'Bootstrap:' -PercentComplete 25 -CurrentOperation 'Checking PowerShellGet' + + # Ensure the module is loaded and retrieve the version you have. + $powerShellGetVersion = (Import-Module -Name 'PowerShellGet' -PassThru -ErrorAction 'SilentlyContinue').Version - Write-Verbose "Bootstrap: The PowerShellGet version is $PowerShellGetVersion" - # Versions below 1.6.0 are considered old, unreliable & not recommended - if (!$PowerShellGetVersion -or ($PowerShellGetVersion -lt [System.version]'1.6.0' -and !$AllowOldPowerShellGetModule)) + Write-Verbose -Message "Bootstrap: The PowerShellGet version is $powerShellGetVersion" + + # Versions below 2.0 are considered old, unreliable & not recommended + if (-not $powerShellGetVersion -or ($powerShellGetVersion -lt [System.Version] '2.0' -and -not $AllowOldPowerShellGetModule)) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 40 -CurrentOperation "Installing newer version of PowerShellGet" - $InstallPSGetParam = @{ + Write-Progress -Activity 'Bootstrap:' -PercentComplete 40 -CurrentOperation 'Installing newer version of PowerShellGet' + + $installPowerShellGetParameters = @{ Name = 'PowerShellGet' Force = $True SkipPublisherCheck = $true @@ -164,49 +252,64 @@ try { 'Proxy' { - $InstallPSGetParam.Add('Proxy', $Proxy) + $installPowerShellGetParameters.Add('Proxy', $Proxy) } + 'ProxyCredential' { - $InstallPSGetParam.Add('ProxyCredential', $ProxyCredential) + $installPowerShellGetParameters.Add('ProxyCredential', $ProxyCredential) } + 'GalleryCredential' { - $InstallPSGetParam.Add('Credential', $GalleryCredential) + $installPowerShellGetParameters.Add('Credential', $GalleryCredential) } } - Install-Module @InstallPSGetParam - Remove-Module PowerShellGet -force -ErrorAction SilentlyContinue - Import-Module PowerShellGet -Force - $NewLoadedVersion = (Get-Module PowerShellGet).Version.ToString() - Write-Information "Bootstrap: PowerShellGet version loaded is $NewLoadedVersion" - Write-Progress -Activity "Bootstrap:" -PercentComplete 60 -CurrentOperation "Installing newer version of PowerShellGet" + Write-Progress -Activity 'Bootstrap:' -PercentComplete 60 -CurrentOperation 'Installing newer version of PowerShellGet' + + Install-Module @installPowerShellGetParameters + + Remove-Module -Name 'PowerShellGet' -Force -ErrorAction 'SilentlyContinue' + Remove-Module -Name 'PackageManagement' -Force + + $powerShellGetModule = Import-Module PowerShellGet -Force -PassThru + + $powerShellGetVersion = $powerShellGetModule.Version.ToString() + + Write-Information -MessageData "Bootstrap: PowerShellGet version loaded is $powerShellGetVersion" + } + + # Try to import the PSDepend module from the available modules. + $getModuleParameters = @{ + Name = 'PSDepend' + ListAvailable = $true } - # Try to import the PSDepend module from the available modules - try + $psDependModule = Get-Module @getModuleParameters + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) { - $ImportPSDependParam = @{ - Name = 'PSDepend' - ErrorAction = 'Stop' - Force = $true + try + { + $psDependModule = $psDependModule | Where-Object -FilterScript { $_.Version -ge $MinimumPSDependVersion } } - - if ($MinimumPSDependVersion) + catch { - $ImportPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + throw ('There was a problem finding the minimum version of PSDepend. Error: {0}' -f $_) } - $null = Import-Module @ImportPSDependParam } - catch + + if (-not $psDependModule) { - # PSDepend module not found, installing or saving it + # PSDepend module not found, installing or saving it. if ($PSDependTarget -in 'CurrentUser', 'AllUsers') { - Write-Debug "PSDepend module not found. Attempting to install from Gallery $Gallery" - Write-Warning "Installing PSDepend in $PSDependTarget Scope" - $InstallPSDependParam = @{ + Write-Debug -Message "PSDepend module not found. Attempting to install from Gallery $Gallery." + + Write-Warning -Message "Installing PSDepend in $PSDependTarget Scope." + + $installPSDependParameters = @{ Name = 'PSDepend' Repository = $Gallery Force = $true @@ -217,73 +320,102 @@ try if ($MinimumPSDependVersion) { - $InstallPSDependParam.add('MinimumVersion', $MinimumPSDependVersion) + $installPSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" - Install-Module @InstallPSDependParam + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Installing PSDepend from $Gallery" + + Install-Module @installPSDependParameters } else { - Write-Debug "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" - $SaveModuleParam = @{ + Write-Debug -Message "PSDepend module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + + $saveModuleParameters = @{ Name = 'PSDepend' Repository = $Gallery Path = $PSDependTarget + Force = $true } if ($MinimumPSDependVersion) { - $SaveModuleParam.add('MinimumVersion', $MinimumPSDependVersion) + $saveModuleParameters.add('MinimumVersion', $MinimumPSDependVersion) } - Write-Progress -Activity "Bootstrap:" -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" - Save-Module @SaveModuleParam + Write-Progress -Activity 'Bootstrap:' -PercentComplete 75 -CurrentOperation "Saving & Importing PSDepend from $Gallery to $Scope" + + Save-Module @saveModuleParameters } } - finally + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 80 -CurrentOperation 'Loading PSDepend' + + $importModulePSDependParameters = @{ + Name = 'PSDepend' + ErrorAction = 'Stop' + Force = $true + } + + if ($PSBoundParameters.ContainsKey('MinimumPSDependVersion')) { - Write-Progress -Activity "Bootstrap:" -PercentComplete 100 -CurrentOperation "Loading PSDepend" - # We should have successfully bootstrapped PSDepend. Fail if not available - Import-Module PSDepend -ErrorAction Stop + $importModulePSDependParameters.Add('MinimumVersion', $MinimumPSDependVersion) } + # We should have successfully bootstrapped PSDepend. Fail if not available. + $null = Import-Module @importModulePSDependParameters + if ($WithYAML) { - if (-Not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) + Write-Progress -Activity 'Bootstrap:' -PercentComplete 82 -CurrentOperation 'Verifying PowerShell module PowerShell-Yaml' + + if (-not (Get-Module -ListAvailable -Name 'PowerShell-Yaml')) { - Write-Verbose "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + Write-Progress -Activity 'Bootstrap:' -PercentComplete 85 -CurrentOperation 'Installing PowerShell module PowerShell-Yaml' + + Write-Verbose -Message "PowerShell-Yaml module not found. Attempting to Save from Gallery $Gallery to $PSDependTarget" + $SaveModuleParam = @{ Name = 'PowerShell-Yaml' Repository = $Gallery Path = $PSDependTarget + Force = $true } Save-Module @SaveModuleParam - Import-Module "PowerShell-Yaml" -ErrorAction Stop } else { Write-Verbose "PowerShell-Yaml is already available" } + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 88 -CurrentOperation 'Importing PowerShell module PowerShell-Yaml' + + Import-Module -Name 'PowerShell-Yaml' -ErrorAction 'Stop' } + Write-Progress -Activity 'Bootstrap:' -PercentComplete 90 -CurrentOperation 'Invoke PSDepend' + Write-Progress -Activity "PSDepend:" -PercentComplete 0 -CurrentOperation "Restoring Build Dependencies" - if (Test-Path $DependencyFile) + + if (Test-Path -Path $DependencyFile) { - $PSDependParams = @{ + $psDependParameters = @{ Force = $true Path = $DependencyFile } - # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified - Invoke-PSDepend @PSDependParams + # TODO: Handle when the Dependency file is in YAML, and -WithYAML is specified. + Invoke-PSDepend @psDependParameters } + Write-Progress -Activity "PSDepend:" -PercentComplete 100 -CurrentOperation "Dependencies restored" -Completed + + Write-Progress -Activity 'Bootstrap:' -PercentComplete 100 -CurrentOperation "Bootstrap complete" -Completed } finally { # Reverting the Installation Policy for the given gallery - Set-PSRepository -Name $Gallery -InstallationPolicy $Policy - Write-Verbose "Project Bootstrapped, returning to Invoke-Build" + Set-PSRepository -Name $Gallery -InstallationPolicy $previousGalleryInstallationPolicy + Write-Verbose -Message "Project Bootstrapped, returning to Invoke-Build" } diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..7bd7bc20 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +## Security + +The DSC Community takes the security of our modules seriously, which includes all source code repositories managed through our GitHub organization. + +If you believe you have found a security vulnerability in any DSC Community owned repository, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to one or several members of the DSC Community organization. +The easiest way to do so is to send us a direct message via twitter or slack. + +You should receive a response within 48 hours. If for some reason you do not, please follow up to other member of the community. + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 4f652a23..549974cc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,4 +1,3 @@ - trigger: branches: include: @@ -12,6 +11,14 @@ trigger: exclude: - "*-*" +variables: + buildFolderName: output + buildArtifactName: output + testResultFolderName: testResults + testArtifactName: testResults + sourceFolderName: source + defaultBranch: main + stages: - stage: Build jobs: @@ -26,7 +33,6 @@ stages: inputs: runtime: 'core' configFilePath: 'GitVersion.yml' - - task: PowerShell@2 name: package displayName: 'Build & Package Module' @@ -35,14 +41,14 @@ stages: arguments: '-ResolveDependency -tasks pack' pwsh: true env: - ModuleVersion: $(gitVersion.InformationalVersion) - - - task: PublishBuildArtifacts@1 + ModuleVersion: $(gitVersion.NuGetVersionV2) + - task: PublishPipelineArtifact@1 displayName: 'Publish Build Artifact' inputs: - pathToPublish: 'output/' - artifactName: 'output' - publishLocation: 'Container' + targetPath: '$(buildFolderName)/' + artifact: $(buildArtifactName) + publishLocation: 'pipeline' + parallel: true - stage: Test dependsOn: Build @@ -53,13 +59,12 @@ stages: vmImage: 'windows-2019' timeoutInMinutes: 0 steps: - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run HQRM Test' @@ -81,44 +86,75 @@ stages: vmImage: 'windows-2019' timeoutInMinutes: 0 steps: - - powershell: | - $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' - echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" - echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" - name: dscBuildVariable - displayName: 'Set Environment Variables' - - task: DownloadBuildArtifacts@0 + - task: DownloadPipelineArtifact@2 displayName: 'Download Build Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' - task: PowerShell@2 name: test displayName: 'Run Unit Test' inputs: filePath: './build.ps1' arguments: "-Tasks test -PesterScript 'tests/Unit'" - pwsh: false + pwsh: true - task: PublishTestResults@2 displayName: 'Publish Test Results' condition: succeededOrFailed() inputs: testResultsFormat: 'NUnit' - testResultsFiles: 'output/testResults/NUnit*.xml' - testRunTitle: 'Unit (Windows Server Core)' + testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml' + # If several pipeline jobs are generating test result, consider renaming this title: + testRunTitle: 'Unit' + - task: PublishPipelineArtifact@1 + displayName: 'Publish Test Artifact' + inputs: + targetPath: '$(buildFolderName)/$(testResultFolderName)/' + artifactName: $(testArtifactName) + # If several pipeline jobs are generating code coverage, replace above with this: + #artifactName: 'CodeCoverageWindows' # Can be any, in the pipeline, unique name + parallel: true + + - job: Code_Coverage + displayName: 'Publish Code Coverage' + dependsOn: Test_Unit + pool: + vmImage: 'ubuntu 16.04' + timeoutInMinutes: 0 + steps: + - pwsh: | + $repositoryOwner,$repositoryName = $env:BUILD_REPOSITORY_NAME -split '/' + echo "##vso[task.setvariable variable=RepositoryOwner;isOutput=true]$repositoryOwner" + echo "##vso[task.setvariable variable=RepositoryName;isOutput=true]$repositoryName" + name: dscBuildVariable + displayName: 'Set Environment Variables' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Build Artifact' + inputs: + buildType: 'current' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)' + - task: DownloadPipelineArtifact@2 + # If several pipeline jobs are generating code coverage, consider renaming this display name: + displayName: 'Download Test Artifact' + inputs: + buildType: 'current' + # If several pipeline jobs are generating code coverage, set the correct artifact name: + artifactName: $(testArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)' - task: PublishCodeCoverageResults@1 - displayName: 'Publish Code Coverage' - condition: succeededOrFailed() + displayName: 'Publish Code Coverage to Azure DevOps' inputs: codeCoverageTool: 'JaCoCo' - summaryFileLocation: 'output/testResults/CodeCov*.xml' - pathToSources: '$(Build.SourcesDirectory)/output/$(dscBuildVariable.RepositoryName)' + summaryFileLocation: '$(Build.SourcesDirectory)/$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml' + pathToSources: '$(Build.SourcesDirectory)/$(sourceFolderName)/' + - script: | + bash <(curl -s https://codecov.io/bash) -f "./$(buildFolderName)/$(testResultFolderName)/JaCoCo_coverage.xml" + displayName: 'Publish Code Coverage to Codecov.io' - stage: Deploy dependsOn: Test - # Only execute deploy stage if we're on main and previous stage succeeded condition: | and( succeeded(), @@ -134,13 +170,12 @@ stages: pool: vmImage: 'ubuntu 16.04' steps: - - task: DownloadBuildArtifacts@0 - displayName: 'Download Build Artifact' + - task: DownloadPipelineArtifact@2 + displayName: 'Download Pipeline Artifact' inputs: buildType: 'current' - downloadType: 'single' - artifactName: 'output' - downloadPath: '$(Build.SourcesDirectory)' + artifactName: $(buildArtifactName) + targetPath: '$(Build.SourcesDirectory)/$(buildArtifactName)' - task: PowerShell@2 name: publishRelease displayName: 'Publish Release' @@ -151,8 +186,8 @@ stages: env: GitHubToken: $(GitHubToken) GalleryApiToken: $(GalleryApiToken) - ReleaseBranch: main - MainGitBranch: main + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) - task: PowerShell@2 name: sendChangelogPR displayName: 'Send Changelog PR' @@ -162,5 +197,5 @@ stages: pwsh: true env: GitHubToken: $(GitHubToken) - ReleaseBranch: main - MainGitBranch: main + ReleaseBranch: $(defaultBranch) + MainGitBranch: $(defaultBranch) diff --git a/build.ps1 b/build.ps1 index 8b20c403..19da5e42 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,365 +1,513 @@ <# + .DESCRIPTION + Bootstrap and build script for PowerShell module CI/CD pipeline -.DESCRIPTION - Bootstrap and build script for PowerShell module pipeline + .PARAMETER Tasks + The task or tasks to run. The default value is '.' (runs the default task). + .PARAMETER CodeCoverageThreshold + The code coverage target threshold to uphold. Set to 0 to disable. + The default value is '' (empty string). + + .PARAMETER BuildConfig + Not yet written. + + .PARAMETER OutputDirectory + Specifies the folder to build the artefact into. The default value is 'output'. + + .PARAMETER BuiltModuleSubdirectory + Subdirectory name to build the module (under $OutputDirectory). The default + value is '' (empty string). + + .PARAMETER RequiredModulesDirectory + Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency + and PSDepend where to save the required modules. It is also possible to use + 'CurrentUser' och 'AllUsers' to install missing dependencies. You can override + the value for PSDepend in the Build.psd1 build manifest. The default value is + 'output/RequiredModules'. + + .PARAMETER PesterScript + One or more paths that will override the Pester configuration in build + configuration file when running the build task Invoke_Pester_Tests. + + If running Pester 5 test, use the alias PesterPath to be future-proof. + + .PARAMETER PesterTag + Filter which tags to run when invoking Pester tests. This is used in the + Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER PesterExcludeTag + Filter which tags to exclude when invoking Pester tests. This is used in + the Invoke-Pester.pester.build.ps1 tasks. + + .PARAMETER DscTestTag + Filter which tags to run when invoking DSC Resource tests. This is used + in the DscResource.Test.build.ps1 tasks. + + .PARAMETER DscTestExcludeTag + Filter which tags to exclude when invoking DSC Resource tests. This is + used in the DscResource.Test.build.ps1 tasks. + + .PARAMETER ResolveDependency + Not yet written. + + .PARAMETER BuildInfo + The build info object from ModuleBuilder. Defaults to an empty hashtable. + + .PARAMETER AutoRestore + Not yet written. #> [CmdletBinding()] param ( [Parameter(Position = 0)] - [string[]]$Tasks = '.', + [System.String[]] + $Tasks = '.', [Parameter()] - [String] + [System.String] $CodeCoverageThreshold = '', [Parameter()] - [validateScript( + [System.String] + [ValidateScript( { Test-Path -Path $_ } )] - $BuildConfig = './build.yaml', + $BuildConfig, [Parameter()] - # A Specific folder to build the artefact into. + [System.String] $OutputDirectory = 'output', [Parameter()] - # Subdirectory name to build the module (under $OutputDirectory) + [System.String] $BuiltModuleSubdirectory = '', - # Can be a path (relative to $PSScriptRoot or absolute) to tell Resolve-Dependency & PSDepend where to save the required modules, - # or use CurrentUser, AllUsers to target where to install missing dependencies - # You can override the value for PSDepend in the Build.psd1 build manifest - # This defaults to $OutputDirectory/modules (by default: ./output/modules) [Parameter()] + [System.String] $RequiredModulesDirectory = $(Join-Path 'output' 'RequiredModules'), [Parameter()] - [string[]] + # This alias is to prepare for the rename of this parameter to PesterPath when Pester 4 support is removed + [Alias('PesterPath')] + [System.Object[]] $PesterScript, - # Filter which tags to run when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $PesterTag, - # Filter which tags to exclude when invoking Pester tests - # This is used in the Invoke-Pester.pester.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $PesterExcludeTag, - # Filter which tags to run when invoking DSC Resource tests - # This is used in the DscResource.Test.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $DscTestTag, - # Filter which tags to exclude when invoking DSC Resource tests - # This is used in the DscResource.Test.build.ps1 tasks [Parameter()] - [string[]] + [System.String[]] $DscTestExcludeTag, [Parameter()] [Alias('bootstrap')] - [switch]$ResolveDependency, + [System.Management.Automation.SwitchParameter] + $ResolveDependency, [Parameter(DontShow)] [AllowNull()] + [System.Collections.Hashtable] $BuildInfo, [Parameter()] - [switch] + [System.Management.Automation.SwitchParameter] $AutoRestore ) -# The BEGIN block (at the end of this file) handles the Bootstrap of the Environment before Invoke-Build can run the tasks -# if the -ResolveDependency (aka Bootstrap) is specified, the modules are already available, and can be auto loaded +<# + The BEGIN block (at the end of this file) handles the Bootstrap of the Environment + before Invoke-Build can run the tasks if the parameter ResolveDependency (or + parameter alias Bootstrap) is specified. +#> process { if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') { - # Only run the process block through InvokeBuild (Look at the Begin block at the bottom of this script) + # Only run the process block through InvokeBuild (look at the Begin block at the bottom of this script). return } - # Execute the Build Process from the .build.ps1 path. - Push-Location -Path $PSScriptRoot -StackName BeforeBuild + # Execute the Build process from the .build.ps1 path. + Push-Location -Path $PSScriptRoot -StackName 'BeforeBuild' try { - Write-Host -ForeGroundColor magenta "[build] Parsing defined tasks" + Write-Host -Object "[build] Parsing defined tasks" -ForeGroundColor Magenta - # Load Default BuildInfo if not provided as parameter - if (!$PSBoundParameters.ContainsKey('BuildInfo')) + # Load the default BuildInfo if the parameter BuildInfo is not set. + if (-not $PSBoundParameters.ContainsKey('BuildInfo')) { try { - if (Test-Path $BuildConfig) + if (Test-Path -Path $BuildConfig) { - $ConfigFile = (Get-Item -Path $BuildConfig) - Write-Host "[build] Loading Configuration from $ConfigFile" - $BuildInfo = switch -Regex ($ConfigFile.Extension) + $configFile = Get-Item -Path $BuildConfig + + Write-Host -Object "[build] Loading Configuration from $configFile" + + $BuildInfo = switch -Regex ($configFile.Extension) { # Native Support for PSD1 '\.psd1' { + if (-not (Get-Command -Name Import-PowerShellDataFile -ErrorAction SilentlyContinue)) + { + Import-Module -Name Microsoft.PowerShell.Utility -RequiredVersion 3.1.0.0 + } + Import-PowerShellDataFile -Path $BuildConfig } + # Support for yaml when module PowerShell-Yaml is available '\.[yaml|yml]' { - Import-Module -ErrorAction Stop -Name 'powershell-yaml' - ConvertFrom-Yaml -Yaml (Get-Content -Raw $ConfigFile) + Import-Module -Name 'powershell-yaml' -ErrorAction Stop + + ConvertFrom-Yaml -Yaml (Get-Content -Raw $configFile) } + # Native Support for JSON and JSONC (by Removing comments) '\.[json|jsonc]' { - $JSONC = (Get-Content -Raw -Path $ConfigFile) - $JSON = $JSONC -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' - # This should probably be converted to hashtable for splatting - $JSON | ConvertFrom-Json + $jsonFile = Get-Content -Raw -Path $configFile + + $jsonContent = $jsonFile -replace '(?m)\s*//.*?$' -replace '(?ms)/\*.*?\*/' + + # Yaml is superset of JSON. + ConvertFrom-Yaml -Yaml $jsonContent } + + # Unknown extension, return empty hashtable. default { - Write-Error "Extension '$_' not supported. using @{}" + Write-Error -Message "Extension '$_' not supported. using @{}" + @{ } } } } else { - Write-Host -Object "Configuration file $BuildConfig not found" -ForegroundColor Red + Write-Host -Object "Configuration file '$($BuildConfig.FullName)' not found" -ForegroundColor Red + + # No config file was found, return empty hashtable. $BuildInfo = @{ } } } catch { - Write-Host -Object "Error loading Config $ConfigFile.`r`n Are you missing dependencies?" -ForegroundColor Yellow - Write-Host -Object "Make sure you run './build.ps1 -ResolveDependency -tasks noop' to restore the Required modules the first time" -ForegroundColor Yellow + $logMessage = "Error loading Config '$($BuildConfig.FullName)'.`r`nAre you missing dependencies?`r`nMake sure you run './build.ps1 -ResolveDependency -tasks noop' before running build to restore the required modules." + + Write-Host -Object $logMessage -ForegroundColor Yellow + $BuildInfo = @{ } - Write-Error $_.Exception.Message + + Write-Error -Message $_.Exception.Message } } - # If the Invoke-Build Task Header is specified in the Build Info, set it + # If the Invoke-Build Task Header is specified in the Build Info, set it. if ($BuildInfo.TaskHeader) { - Set-BuildHeader ([scriptblock]::Create($BuildInfo.TaskHeader)) + Set-BuildHeader -Script ([scriptblock]::Create($BuildInfo.TaskHeader)) } - # Import Tasks from modules via their exported aliases when defined in BUild Manifest - # https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks - if ($BuildInfo.containsKey('ModuleBuildTasks')) + <# + Add BuildModuleOutput to PSModule Path environment variable. + Moved here (not in begin block) because build file can contains BuiltSubModuleDirectory value. + #> + if ($BuiltModuleSubdirectory) + { + if (-not (Split-Path -IsAbsolute -Path $BuiltModuleSubdirectory)) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuiltModuleSubdirectory + } + else + { + $BuildModuleOutput = $BuiltModuleSubdirectory + } + } # test if BuiltModuleSubDirectory set in build config file + elseif ($BuildInfo.ContainsKey('BuiltModuleSubDirectory')) + { + $BuildModuleOutput = Join-Path -Path $OutputDirectory -ChildPath $BuildInfo['BuiltModuleSubdirectory'] + } + else + { + $BuildModuleOutput = $OutputDirectory + } + + # Pre-pending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder. + if ($powerShellModulePaths -notcontains $BuildModuleOutput) { - foreach ($Module in $BuildInfo['ModuleBuildTasks'].Keys) + Write-Host -Object "[build] Pre-pending '$BuildModuleOutput' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $BuildModuleOutput + [System.IO.Path]::PathSeparator + $env:PSModulePath + } + + <# + Import Tasks from modules via their exported aliases when defined in Build Manifest. + https://github.com/nightroman/Invoke-Build/tree/master/Tasks/Import#example-2-import-from-a-module-with-tasks + #> + if ($BuildInfo.ContainsKey('ModuleBuildTasks')) + { + foreach ($module in $BuildInfo['ModuleBuildTasks'].Keys) { try { - Write-Host -ForegroundColor DarkGray -Verbose "Importing tasks from module $Module" - $LoadedModule = Import-Module $Module -PassThru -ErrorAction Stop - foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($Module)) + Write-Host -Object "Importing tasks from module $module" -ForegroundColor DarkGray + + $loadedModule = Import-Module -Name $module -PassThru -ErrorAction Stop + + foreach ($TaskToExport in $BuildInfo['ModuleBuildTasks'].($module)) { - $LoadedModule.ExportedAliases.GetEnumerator().Where{ - # using -like to support wildcard - Write-Host -ForegroundColor DarkGray "`t Loading $($_.Key)..." + $loadedModule.ExportedAliases.GetEnumerator().Where{ + Write-Host -Object "`t Loading $($_.Key)..." -ForegroundColor DarkGray + + # Using -like to support wildcard. $_.Key -like $TaskToExport }.ForEach{ - # Dot sourcing the Tasks via their exported aliases + # Dot-sourcing the Tasks via their exported aliases. . (Get-Alias $_.Key) } } } catch { - Write-Host -ForegroundColor Red -Object "Could not load tasks for module $Module." - Write-Error $_ + Write-Host -Object "Could not load tasks for module $module." -ForegroundColor Red + + Write-Error -Message $_ } } } - # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name) - Get-ChildItem -Path ".build/" -Recurse -Include *.ps1 -ErrorAction Ignore | ForEach-Object { - "Importing file $($_.BaseName)" | Write-Verbose - . $_.FullName - } + # Loading Build Tasks defined in the .build/ folder (will override the ones imported above if same task name). + Get-ChildItem -Path '.build/' -Recurse -Include '*.ps1' -ErrorAction Ignore | + ForEach-Object { + "Importing file $($_.BaseName)" | Write-Verbose - # Synopsis: Empty task, useful to test the bootstrap process + . $_.FullName + } + + # Synopsis: Empty task, useful to test the bootstrap process. task noop { } - # Define default task sequence ("."), can be overridden in the $BuildInfo + # Define default task sequence ("."), can be overridden in the $BuildInfo. task . { - Write-Build Yellow "No sequence currently defined for the default task" + Write-Build -Object 'No sequence currently defined for the default task' -ForegroundColor Yellow } - # Load Invoke-Build task sequences/workflows from $BuildInfo - Write-Host -ForegroundColor DarkGray "Adding Workflow from configuration:" - foreach ($Workflow in $BuildInfo.BuildWorkflow.keys) + Write-Host -Object 'Adding Workflow from configuration:' -ForegroundColor DarkGray + + # Load Invoke-Build task sequences/workflows from $BuildInfo. + foreach ($workflow in $BuildInfo.BuildWorkflow.keys) { - Write-Verbose "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')" - $WorkflowItem = $BuildInfo.BuildWorkflow.($Workflow) - if ($WorkflowItem.Trim() -match '^\{(?[\w\W]*)\}$') + Write-Verbose -Message "Creating Build Workflow '$Workflow' with tasks $($BuildInfo.BuildWorkflow.($Workflow) -join ', ')." + + $workflowItem = $BuildInfo.BuildWorkflow.($workflow) + + if ($workflowItem.Trim() -match '^\{(?[\w\W]*)\}$') { - $WorkflowItem = [ScriptBlock]::Create($Matches['sb']) + $workflowItem = [ScriptBlock]::Create($Matches['sb']) } - Write-Host -ForegroundColor DarkGray " +-> $Workflow" - task $Workflow $WorkflowItem + + Write-Host -Object " +-> $workflow" -ForegroundColor DarkGray + + task $workflow $workflowItem } - Write-Host -ForeGroundColor magenta "[build] Executing requested workflow: $($Tasks -join ', ')" + Write-Host -Object "[build] Executing requested workflow: $($Tasks -join ', ')" -ForeGroundColor Magenta } finally { - Pop-Location -StackName BeforeBuild + Pop-Location -StackName 'BeforeBuild' } } Begin { + # Find build config if not specified. + if (-not $BuildConfig) + { + $config = Get-ChildItem -Path "$PSScriptRoot\*" -Include 'build.y*ml', 'build.psd1', 'build.json*' -ErrorAction Ignore + + if (-not $config -or ($config -is [System.Array] -and $config.Length -le 0)) + { + throw 'No build configuration found. Specify path via parameter BuildConfig.' + } + elseif ($config -is [System.Array]) + { + if ($config.Length -gt 1) + { + throw 'More than one build configuration found. Specify which path to use via parameter BuildConfig.' + } + + $BuildConfig = $config[0] + } + else + { + $BuildConfig = $config + } + } + # Bootstrapping the environment before using Invoke-Build as task runner - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Host -foregroundColor Green "[pre-build] Starting Build Init" - Push-Location $PSScriptRoot -StackName BuildModule + Write-Host -Object "[pre-build] Starting Build Init" -ForegroundColor Green + + Push-Location $PSScriptRoot -StackName 'BuildModule' } if ($RequiredModulesDirectory -in @('CurrentUser', 'AllUsers')) { - # Installing modules instead of saving them - Write-Host -foregroundColor Green "[pre-build] Required Modules will be installed for $RequiredModulesDirectory, not saved." - # Tell Resolve-Dependency to use provided scope as the -PSDependTarget if not overridden in Build.psd1 + # Installing modules instead of saving them. + Write-Host -Object "[pre-build] Required Modules will be installed to the PowerShell module path that is used for $RequiredModulesDirectory." -ForegroundColor Green + + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> $PSDependTarget = $RequiredModulesDirectory } else { - if (-Not (Split-Path -IsAbsolute -Path $OutputDirectory)) + if (-not (Split-Path -IsAbsolute -Path $OutputDirectory)) { $OutputDirectory = Join-Path -Path $PSScriptRoot -ChildPath $OutputDirectory } - # Resolving the absolute path to save the required modules to - if (-Not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) + # Resolving the absolute path to save the required modules to. + if (-not (Split-Path -IsAbsolute -Path $RequiredModulesDirectory)) { $RequiredModulesDirectory = Join-Path -Path $PSScriptRoot -ChildPath $RequiredModulesDirectory } - # Create the output/modules folder if not exists, or resolve the Absolute path otherwise - if (Resolve-Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) + # Create the output/modules folder if not exists, or resolve the Absolute path otherwise. + if (Resolve-Path -Path $RequiredModulesDirectory -ErrorAction SilentlyContinue) { - Write-Debug "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" - $RequiredModulesPath = Convert-Path $RequiredModulesDirectory + Write-Debug -Message "[pre-build] Required Modules path already exist at $RequiredModulesDirectory" + + $requiredModulesPath = Convert-Path -Path $RequiredModulesDirectory } else { - Write-Host -foregroundColor Green "[pre-build] Creating required modules directory $RequiredModulesDirectory." - $RequiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName - } + Write-Host -Object "[pre-build] Creating required modules directory $RequiredModulesDirectory." -ForegroundColor Green - # Prepending $RequiredModulesPath folder to PSModulePath to resolve from this folder FIRST - if ($RequiredModulesDirectory -notIn @('CurrentUser', 'AllUsers') -and - (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $RequiredModulesDirectory)) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$RequiredModulesDirectory' folder to PSModulePath" - $Env:PSModulePath = $RequiredModulesDirectory + [io.path]::PathSeparator + $Env:PSModulePath + $requiredModulesPath = (New-Item -ItemType Directory -Force -Path $RequiredModulesDirectory).FullName } - # Checking if the user should -ResolveDependency - if ((!(Get-Module -ListAvailable powershell-yaml) -or !(Get-Module -ListAvailable InvokeBuild) -or !(Get-Module -ListAvailable PSDepend)) -and !$ResolveDependency) + $powerShellModulePaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator + + # Pre-pending $requiredModulesPath folder to PSModulePath to resolve from this folder FIRST. + if ($RequiredModulesDirectory -notin @('CurrentUser', 'AllUsers') -and + ($powerShellModulePaths -notcontains $RequiredModulesDirectory)) { - if ($AutoRestore -or !$PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') - { - Write-Host -ForegroundColor Yellow "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" - $ResolveDependency = $true - } - else - { - Write-Warning "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter." - Write-Warning "Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." - } + Write-Host -Object "[pre-build] Pre-pending '$RequiredModulesDirectory' folder to PSModulePath" -ForegroundColor Green + + $env:PSModulePath = $RequiredModulesDirectory + [System.IO.Path]::PathSeparator + $env:PSModulePath } - if ($BuiltModuleSubdirectory) + $powerShellYamlModule = Get-Module -Name 'powershell-yaml' -ListAvailable + $invokeBuildModule = Get-Module -Name 'InvokeBuild' -ListAvailable + $psDependModule = Get-Module -Name 'PSDepend' -ListAvailable + + # Checking if the user should -ResolveDependency. + if (-not ($powerShellYamlModule -and $invokeBuildModule -and $psDependModule) -and -not $ResolveDependency) { - if (-Not (Split-Path -IsAbsolute $BuiltModuleSubdirectory)) + if ($AutoRestore -or -not $PSBoundParameters.ContainsKey('Tasks') -or $Tasks -contains 'build') { - $BuildModuleOutput = Join-Path $OutputDirectory $BuiltModuleSubdirectory + Write-Host -Object "[pre-build] Dependency missing, running './build.ps1 -ResolveDependency -Tasks noop' for you `r`n" -ForegroundColor Yellow + + $ResolveDependency = $true } else { - $BuildModuleOutput = $BuiltModuleSubdirectory + Write-Warning -Message "Some required Modules are missing, make sure you first run with the '-ResolveDependency' parameter. Running 'build.ps1 -ResolveDependency -Tasks noop' will pull required modules without running the build task." } } - else - { - $BuildModuleOutput = $OutputDirectory - } - - # Prepending $BuildModuleOutput folder to PSModulePath to resolve built module from this folder - if (($Env:PSModulePath -split [io.path]::PathSeparator) -notContains $BuildModuleOutput) - { - Write-Host -foregroundColor Green "[pre-build] Prepending '$BuildModuleOutput' folder to PSModulePath" - $Env:PSModulePath = $BuildModuleOutput + [io.path]::PathSeparator + $Env:PSModulePath - } - # Tell Resolve-Dependency to use $RequiredModulesPath as -PSDependTarget if not overridden in Build.psd1 - $PSDependTarget = $RequiredModulesPath + <# + The variable $PSDependTarget will be used below when building the splatting + variable before calling Resolve-Dependency.ps1, unless overridden in the + file Resolve-Dependency.psd1. + #> + $PSDependTarget = $requiredModulesPath } if ($ResolveDependency) { - Write-Host -Object "[pre-build] Resolving dependencies." -foregroundColor Green - $ResolveDependencyParams = @{ } + Write-Host -Object "[pre-build] Resolving dependencies." -ForegroundColor Green + $resolveDependencyParams = @{ } - # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency + # If BuildConfig is a Yaml file, bootstrap powershell-yaml via ResolveDependency. if ($BuildConfig -match '\.[yaml|yml]$') { - $ResolveDependencyParams.add('WithYaml', $True) + $resolveDependencyParams.Add('WithYaml', $true) } - $ResolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').parameters.keys - foreach ($CmdParameter in $ResolveDependencyAvailableParams) - { + $resolveDependencyAvailableParams = (Get-Command -Name '.\Resolve-Dependency.ps1').Parameters.Keys + foreach ($cmdParameter in $resolveDependencyAvailableParams) + { # The parameter has been explicitly used for calling the .build.ps1 - if ($MyInvocation.BoundParameters.ContainsKey($CmdParameter)) + if ($MyInvocation.BoundParameters.ContainsKey($cmdParameter)) { - $ParamValue = $MyInvocation.BoundParameters.ContainsKey($CmdParameter) - Write-Debug " adding $CmdParameter :: $ParamValue [from user-provided parameters to Build.ps1]" - $ResolveDependencyParams.Add($CmdParameter, $ParamValue) + $paramValue = $MyInvocation.BoundParameters.ContainsKey($cmdParameter) + + Write-Debug " adding $cmdParameter :: $paramValue [from user-provided parameters to Build.ps1]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } # Use defaults parameter value from Build.ps1, if any else { - if ($ParamValue = Get-Variable -Name $CmdParameter -ValueOnly -ErrorAction Ignore) + $paramValue = Get-Variable -Name $cmdParameter -ValueOnly -ErrorAction Ignore + + if ($paramValue) { - Write-Debug " adding $CmdParameter :: $ParamValue [from default Build.ps1 variable]" - $ResolveDependencyParams.add($CmdParameter, $ParamValue) + Write-Debug " adding $cmdParameter :: $paramValue [from default Build.ps1 variable]" + + $resolveDependencyParams.Add($cmdParameter, $paramValue) } } } - Write-Host -foregroundColor Green "[pre-build] Starting bootstrap process." - .\Resolve-Dependency.ps1 @ResolveDependencyParams + Write-Host -Object "[pre-build] Starting bootstrap process." -ForegroundColor Green + + .\Resolve-Dependency.ps1 @resolveDependencyParams } - if ($MyInvocation.ScriptName -notLike '*Invoke-Build.ps1') + if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { - Write-Verbose "Bootstrap completed. Handing back to InvokeBuild." + Write-Verbose -Message "Bootstrap completed. Handing back to InvokeBuild." + if ($PSBoundParameters.ContainsKey('ResolveDependency')) { - Write-Verbose "Dependency already resolved. Removing task" + Write-Verbose -Message "Dependency already resolved. Removing task." + $null = $PSBoundParameters.Remove('ResolveDependency') } - Write-Host -foregroundColor Green "[build] Starting build with InvokeBuild." + + Write-Host -Object "[build] Starting build with InvokeBuild." -ForegroundColor Green + Invoke-Build @PSBoundParameters -Task $Tasks -File $MyInvocation.MyCommand.Path - Pop-Location -StackName BuildModule + + Pop-Location -StackName 'BuildModule' + return } } diff --git a/build.yaml b/build.yaml index f86385f4..2ec1c0d0 100644 --- a/build.yaml +++ b/build.yaml @@ -3,7 +3,7 @@ # ModuleBuilder Configuration # #################################################### -CopyDirectories: +CopyPaths: - en-US - DSCResources - Modules @@ -23,6 +23,8 @@ BuildWorkflow: - Build_Module_ModuleBuilder - Build_NestedModules_ModuleBuilder - Create_changelog_release_output + - Generate_Conceptual_Help + - Generate_Wiki_Content pack: - build @@ -36,8 +38,10 @@ BuildWorkflow: - Pester_if_Code_Coverage_Under_Threshold publish: - - Publish_release_to_GitHub - publish_module_to_gallery + - Publish_Release_To_GitHub + - Publish_GitHub_Wiki_Content + - Create_ChangeLog_GitHub_PR #################################################### # PESTER Configuration # @@ -48,9 +52,10 @@ Pester: ExcludeFromCodeCoverage: Script: - tests/Unit - ExcludeTag: Tag: CodeCoverageThreshold: 47 + CodeCoverageOutputFile: JaCoCo_coverage.xml + CodeCoverageOutputFileEncoding: ascii DscTest: ExcludeTag: @@ -60,18 +65,16 @@ DscTest: Tag: ExcludeSourceFile: - output - - Examples ExcludeModuleFile: MainGitBranch: main -Resolve-Dependency: - Gallery: 'PSGallery' - AllowPrerelease: false - Verbose: false - ModuleBuildTasks: Sampler: - '*.build.Sampler.ib.tasks' + Sampler.GitHubTasks: + - '*.ib.tasks' + DscResource.DocGenerator: + - 'Task.*' TaskHeader: | param($Path) @@ -90,3 +93,16 @@ GitHubConfig: GitHubConfigUserName: dscbot GitHubConfigUserEmail: dsccommunity@outlook.com UpdateChangelogOnPrerelease: false + +#################################################### +# DscResource.DocGenerator Configuration # +#################################################### +DscResource.DocGenerator: + Generate_Conceptual_Help: + MarkdownCodeRegularExpression: + - '\`(.+?)\`' # Match inline code-block + - '\\(\\)' # Match escaped backslash + - '\[[^\[]+\]\((.+?)\)' # Match markdown URL + - '_(.+?)_' # Match Italic (underscore) + - '\*\*(.+?)\*\*' # Match bold + - '\*(.+?)\*' # Match Italic (asterisk) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..41ec284c --- /dev/null +++ b/codecov.yml @@ -0,0 +1,29 @@ +codecov: + require_ci_to_pass: no + # master should be the baseline for reporting + branch: main + +comment: + layout: "reach, diff, flags, files" + behavior: default + +coverage: + range: 50..80 + round: down + precision: 0 + + status: + project: + default: + # Set the overall project code coverage requirement to 70% + target: 70 + patch: + default: + # Set the pull request requirement to not regress overall coverage by more than 5% + # and let codecov.io set the goal for the code changed in the patch. + target: auto + threshold: 5 + +# This is not needed if the module only contain class-based resources. +fixes: + - '^\d+\.\d+\.\d+::source' # move path "X.Y.Z" => "source" diff --git a/source/WikiSource/Home.md b/source/WikiSource/Home.md new file mode 100644 index 00000000..27112703 --- /dev/null +++ b/source/WikiSource/Home.md @@ -0,0 +1,45 @@ +# Welcome to the xExchange wiki + +*xExchange v#.#.#* + +Here you will find all the information you need to make use of the xExchange +DSC resources in the latest release. This includes details of the resources +that are available, current capabilities, known issues, and information to +help plan a DSC based implementation of xExchange. + +Please leave comments, feature requests, and bug reports for this module in +the [issues section](https://github.com/dsccommunity/xExchange/issues) +for this repository. + +## Getting started + +To get started either: + +- Install from the PowerShell Gallery using PowerShellGet by running the + following command: + +```powershell +Install-Module -Name xExchange -Repository PSGallery +``` + +- Download xExchange from the [PowerShell Gallery](https://www.powershellgallery.com/packages/xExchange) + and then unzip it to one of your PowerShell modules folders (such as + `$env:ProgramFiles\WindowsPowerShell\Modules`). + +To confirm installation, run the below command and ensure you see the xExchange +DSC resources available: + +```powershell +Get-DscResource -Module xExchange +``` + +## Prerequisites + +The minimum Windows Management Framework (PowerShell) version required is 5.0 +or higher, which ships with Windows 10 or Windows Server 2016, +but can also be installed on Windows 7 SP1, Windows 8.1, Windows Server 2012, +and Windows Server 2012 R2. + +## Change log + +A full list of changes in each version can be found in the [change log](https://github.com/dsccommunity/xExchange/blob/main/CHANGELOG.md). diff --git a/source/xExchange.psd1 b/source/xExchange.psd1 index 11ae20e1..208108b8 100644 --- a/source/xExchange.psd1 +++ b/source/xExchange.psd1 @@ -93,6 +93,8 @@ PrivateData = @{ PSData = @{ + Prerelease = '' + IconUri = 'https://dsccommunity.org/images/DSC_Logo_300p.png' # Tags applied to this module. These help with module discovery in online galleries. @@ -106,8 +108,6 @@ # ReleaseNotes of this module ReleaseNotes = '' - - Prerelease = '' } # End of PSData hashtable } # End of PrivateData hashtable From e3418744b9220088dc7125f8503de598d1d8a7d2 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Thu, 30 Sep 2021 17:12:47 +0200 Subject: [PATCH 2/5] Fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 519b5de4..180fb88b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - xExchange - Renamed default branch to `main` ([issue #465](https://github.com/dsccommunity/xExchange/issues/465)). - Merged the historic changelog back into CHANGELOG.md. - - Update repository to use the latest pipeline. + - Update repository to use the latest pipeline ([issue #466](https://github.com/dsccommunity/xExchange/issues/466)). - xExchOwaVirtualDirectoryInternal - Added additional parameter SetPhotoEnabled - RequiredModules From 9ac2a394a22b199126e51ecb487b0ea15a0fca97 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 1 Oct 2021 07:05:39 +0200 Subject: [PATCH 3/5] Fix unit tests --- CHANGELOG.md | 1 + tests/Unit/xExchangeHelper.tests.ps1 | 30 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 180fb88b..850a01e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - xExchange - Fixed casing on folder in repository. + - Fixed unit tests so they can run using PowerShell 7. ## [1.32.0] - 2020-05-13 diff --git a/tests/Unit/xExchangeHelper.tests.ps1 b/tests/Unit/xExchangeHelper.tests.ps1 index f0ca5ced..3003f2c1 100644 --- a/tests/Unit/xExchangeHelper.tests.ps1 +++ b/tests/Unit/xExchangeHelper.tests.ps1 @@ -441,10 +441,35 @@ try } Describe 'xExchangeHelper\Remove-HelperSnapin' -Tag 'Helper' { + BeforeAll { + if (-not (Get-Command -Name 'Get-PSSnapin' -ErrorAction 'SilentlyContinue')) + { + $createdPSSnapinStubs = $true + + function Get-PSSnapin + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + + function Remove-PSSnapin + { + throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand + } + } + } + AfterEach { Assert-VerifiableMock } + AfterAll { + if ($createdPSSnapinStubs) + { + Remove-Item -Path 'function:/Get-PSSnapin' -Force + Remove-Item -Path 'function:/Remove-PSSnapin' -Force + } + } + Context 'When Remove-HelperSnapin is called and a snapin is loaded' { It 'Should remove the snapin' { Mock -CommandName Get-PSSnapin -Verifiable -MockWith { return $true } @@ -3161,6 +3186,10 @@ try } Context 'When it is a new object' { + BeforeAll { + Mock -CommandName Start-Sleep + } + It 'Should throw when the object is not found' { Mock -CommandName 'Get-ADPermission' -Verifiable -ParameterFilter { $Identity -eq 'FakeADObject' } @@ -3271,4 +3300,3 @@ try finally { } - From c0b996a18bdf685be9c27f04562f097632932f05 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 1 Oct 2021 07:57:03 +0200 Subject: [PATCH 4/5] Fix VS Code settings --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8bf1c69c..72f9013f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ "powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.ignoreOneLineBlock": false, - "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationAfterEveryPipeline", + "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", "powershell.codeFormatting.preset": "Custom", "powershell.codeFormatting.alignPropertyValuePairs": true, "powershell.developer.bundledModulesPath": "${cwd}/output/RequiredModules", From 9ca510e6179b42109cd441da9a991ba9428977ef Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sun, 10 Oct 2021 15:47:58 +0200 Subject: [PATCH 5/5] Fix review comment --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2322d0bc..1c586db8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Please check out common DSC Community [contributing guidelines](https://dsccommu ## Running the Tests -If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests) +If want to know how to run this module's tests you can look at the [Testing Guidelines](https://dsccommunity.org/guidelines/testing-guidelines/#running-tests). ## How to Run Integration Tests