diff --git a/CHANGELOG.md b/CHANGELOG.md index bb40e7016..07e06d7d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- SqlServerDsc + - Added build tasks to generate Wiki documentation for public commands. + ### Fixed - SqlServerDsc @@ -35,6 +40,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update markdown highlights with newly supported keywords. - Bump GitHub Action _Stale_ to v9. - Bump GitHub Action _CodeQL-Action_ to v3. + - Documentation is now built using a separate meta task `docs`. + It is run with the meta task `pack` which is run by the pipeline. + To run the meta task `docs` the SMO assemblies must be loaded into the + session, either by importing SqlServer module or loading SMO stubs. + - QA test improved to speed up quality testing. - SqlSetup - Updated integration tests to use PSResourceGet to download required modules. diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index e50a83f4a..4ba54f838 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -30,7 +30,6 @@ MarkdownLinkCheck = 'latest' 'DscResource.Test' = 'latest' xDscResourceDesigner = 'latest' - 'DscResource.DocGenerator' = 'latest' # Build dependencies needed for using the module 'DscResource.Base' = 'latest' @@ -46,9 +45,19 @@ # Need to pin this to 3.7.2 because 4.0.0 made the integration tests fail. PSPKI = '3.7.2' - # Prerequisites modules needed for examples or integration tests + # Prerequisite modules needed for examples or integration tests xPSDesiredStateConfiguration = '9.1.0' StorageDsc = '5.1.0' NetworkingDsc = '9.0.0' WSManDsc = '3.1.1' + + # Prerequisite modules for documentation. + #'DscResource.DocGenerator' = 'latest' + 'DscResource.DocGenerator' = @{ + Version = 'latest' + Parameters = @{ + AllowPrerelease = $true + } + } + PlatyPS = 'latest' } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ade6ce3e9..d3db63d44 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -37,6 +37,21 @@ stages: } Write-Host -Object "##vso[build.updatebuildnumber]$($gitVersionObject.FullSemVer)" displayName: Calculate ModuleVersion (GitVersion) + - pwsh: | + <# + This is intentionally not saved to RequiredModules. It is only + used for creating documentation and should not be part of the + build artifact. + #> + Write-Information -MessageData 'Removing SqlServer and SQLPS from the CI worker.' -InformationAction Continue + Import-Module -Name ./tests/TestHelpers/CommonTestHelper.psm1 + Remove-PowerShellModuleFromCI -Name @('SqlServer', 'SQLPS') + Remove-Module -Name CommonTestHelper + Write-Information -MessageData 'Installing latest SqlServer on the CI worker' -InformationAction Continue + Install-Module -Name SqlServer -Force -Confirm:$false -Verbose + Get-Module -Name SqlServer -ListAvailable | + Select-Object -Property Name,Version,@{Name='Prerelease'; Expression={$_.PrivateData.PSData.Prerelease}}, Path + displayName: Install SqlServer module - task: PowerShell@2 name: package displayName: 'Build & Package Module' diff --git a/build.yaml b/build.yaml index 28a7db0a6..542ba55d7 100644 --- a/build.yaml +++ b/build.yaml @@ -12,17 +12,21 @@ BuildWorkflow: - Build_Module_ModuleBuilder - Build_NestedModules_ModuleBuilder - Create_Changelog_Release_Output + + docs: + - Check_SqlServer_Availability - Generate_Conceptual_Help - Generate_Wiki_Content + - Generate_Markdown_For_Public_Commands + - Generate_External_Help_File_For_Public_Commands + - Clean_Markdown_Of_Public_Commands + - Generate_Wiki_Sidebar + - Clean_Markdown_Metadata - Package_WikContent - fastbuild: - - Clean - - Build_Module_ModuleBuilder - - Build_NestedModules_ModuleBuilder - pack: - build + - docs - package_module_nupkg # cSpell: disable-line hqrmtest: # cSpell: disable-line @@ -38,15 +42,22 @@ BuildWorkflow: - Publish_Module_To_gallery - Publish_GitHub_Wiki_Content + Check_SqlServer_Availability: | + { + Import-Module -Name SqlServer -ErrorAction 'SilentlyContinue' + + try + { + [Microsoft.SqlServer.Management.Smo.Server] -as [Type] | Out-Null + } + catch + { + throw 'The SqlServer types are not available. Please install the SqlServer module or load SMO stubs types and try again.' + } + } + Package_WikContent: | { - <# - This is quick fix for issue https://github.com/PoshCode/ModuleBuilder/issues/103. - Aliases need to be added to module manifest, and we can only use - New-Alias or Set-Alias together with build tasks. This module - already automatically exports aliases using [Alias()] so this quick - fix is meant as a workaround until the above issue is resolved. - #> param ( $OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), @@ -207,3 +218,18 @@ DscResource.DocGenerator: - '_(.+?)_' # Match Italic (underscore) - '\*\*(.+?)\*\*' # Match bold - '\*(.+?)\*' # Match Italic (asterisk) + Publish_GitHub_Wiki_Content: + Debug: false + Generate_Wiki_Content: + MofResourceMetadata: + Type: MofResource + Category: Resources + ClassResourceMetadata: + Type: ClassResource + Category: Resources + CompositeResourceMetadata: + Type: CompositeResource + Category: Resources + Generate_Wiki_Sidebar: + Debug: false + AlwaysOverwrite: true diff --git a/tests/QA/module.tests.ps1 b/tests/QA/module.tests.ps1 index 21c019cca..3db37dce3 100644 --- a/tests/QA/module.tests.ps1 +++ b/tests/QA/module.tests.ps1 @@ -102,7 +102,7 @@ Describe 'Changelog Management' -Tag 'Changelog' { } It 'Changelog should have an Unreleased header' -Skip:$skipTest { - (Get-ChangelogData -Path (Join-Path -Path $ProjectPath -ChildPath 'CHANGELOG.md') -ErrorAction Stop).Unreleased | Should -Not -BeNullOrEmpty + (Get-ChangelogData -Path (Join-Path -Path $ProjectPath -ChildPath 'CHANGELOG.md') -ErrorAction Stop).Unreleased | Should -Not -BeNullOrEmpty } } @@ -125,14 +125,25 @@ BeforeDiscovery { $allModuleFunctions = & $mut { Get-Command -Module $args[0] -CommandType Function } $script:moduleName # Build test cases. - $testCases = @() + $testCasesAllModuleFunction = @() foreach ($function in $allModuleFunctions) { - $testCases += @{ + $testCasesAllModuleFunction += @{ Name = $function.Name } } + + $allPublicCommand = (Get-Command -Module $script:moduleName).Name + + $testCasesPublicCommand = @() + + foreach ($command in $allPublicCommand) + { + $testCasesPublicCommand += @{ + Name = $command + } + } } Describe 'Quality for module' -Tags 'TestQuality' { @@ -154,11 +165,11 @@ Describe 'Quality for module' -Tags 'TestQuality' { } } - It 'Should have a unit test for ' -ForEach $testCases { + It 'Should have a unit test for ' -ForEach $testCasesAllModuleFunction { Get-ChildItem -Path 'tests\' -Recurse -Include "$Name.Tests.ps1" | Should -Not -BeNullOrEmpty } - It 'Should pass Script Analyzer for ' -ForEach $testCases -Skip:(-not $scriptAnalyzerRules) { + It 'Should pass Script Analyzer for ' -ForEach $testCasesAllModuleFunction -Skip:(-not $scriptAnalyzerRules) { $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" $pssaResult = (Invoke-ScriptAnalyzer -Path $functionFile.FullName) @@ -169,88 +180,46 @@ Describe 'Quality for module' -Tags 'TestQuality' { } Describe 'Help for module' -Tags 'helpQuality' { - It 'Should have .SYNOPSIS for ' -ForEach $testCases { - $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" - - $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName - - $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + Context 'Validating help for ' -ForEach $testCasesAllModuleFunction -Tag 'helpQuality' { + BeforeAll { + $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" - $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName - $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | - Where-Object -FilterScript { - $_.Name -eq $Name - } + $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) - $functionHelp = $parsedFunction.GetHelpContent() + $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } - $functionHelp.Synopsis | Should -Not -BeNullOrEmpty - } - - It 'Should have a .DESCRIPTION with length greater than 40 characters for ' -ForEach $testCases { - $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" - - $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName - - $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) - - $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } - - $parsedFunction = $abstractSyntaxTree.FindAll($astSearchDelegate, $true) | - Where-Object -FilterScript { - $_.Name -eq $Name - } - - $functionHelp = $parsedFunction.GetHelpContent() - - $functionHelp.Description.Length | Should -BeGreaterThan 40 - } + $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | + Where-Object -FilterScript { + $_.Name -eq $Name + } - It 'Should have at least one (1) example for ' -ForEach $testCases { - $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" - - $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName - - $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) - - $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } - - $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | - Where-Object -FilterScript { - $_.Name -eq $Name - } - - $functionHelp = $parsedFunction.GetHelpContent() - - $functionHelp.Examples.Count | Should -BeGreaterThan 0 - $functionHelp.Examples[0] | Should -Match ([regex]::Escape($function.Name)) - $functionHelp.Examples[0].Length | Should -BeGreaterThan ($function.Name.Length + 10) - - } + $script:functionHelp = $parsedFunction.GetHelpContent() + } - It 'Should have described all parameters for ' -ForEach $testCases { - $functionFile = Get-ChildItem -Path $sourcePath -Recurse -Include "$Name.ps1" + It 'Should have .SYNOPSIS' { + $functionHelp.Synopsis | Should -Not -BeNullOrEmpty + } - $scriptFileRawContent = Get-Content -Raw -Path $functionFile.FullName + It 'Should have a .DESCRIPTION with length greater than 40 characters for ' { + $functionHelp.Description.Length | Should -BeGreaterThan 40 + } - $abstractSyntaxTree = [System.Management.Automation.Language.Parser]::ParseInput($scriptFileRawContent, [ref] $null, [ref] $null) + It 'Should have at least one (1) example for ' { + $functionHelp.Examples.Count | Should -BeGreaterThan 0 + $functionHelp.Examples[0] | Should -Match ([regex]::Escape($function.Name)) + $functionHelp.Examples[0].Length | Should -BeGreaterThan ($function.Name.Length + 10) + } - $astSearchDelegate = { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } + It 'Should have described all parameters for ' { + $parameters = $parsedFunction.Body.ParamBlock.Parameters.Name.VariablePath.ForEach({ $_.ToString() }) - $parsedFunction = $abstractSyntaxTree.FindAll( $astSearchDelegate, $true ) | - Where-Object -FilterScript { - $_.Name -eq $Name + foreach ($parameter in $parameters) + { + $functionHelp.Parameters.($parameter.ToUpper()) | Should -Not -BeNullOrEmpty -Because ('the parameter {0} must have a description' -f $parameter) + $functionHelp.Parameters.($parameter.ToUpper()).Length | Should -BeGreaterThan 25 -Because ('the parameter {0} must have descriptive description' -f $parameter) } - - $functionHelp = $parsedFunction.GetHelpContent() - - $parameters = $parsedFunction.Body.ParamBlock.Parameters.Name.VariablePath.ForEach({ $_.ToString() }) - - foreach ($parameter in $parameters) - { - $functionHelp.Parameters.($parameter.ToUpper()) | Should -Not -BeNullOrEmpty -Because ('the parameter {0} must have a description' -f $parameter) - $functionHelp.Parameters.($parameter.ToUpper()).Length | Should -BeGreaterThan 25 -Because ('the parameter {0} must have descriptive description' -f $parameter) } } }