diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml new file mode 100644 index 000000000..6f0e7f6ff --- /dev/null +++ b/.github/workflows/code-analysis.yml @@ -0,0 +1,67 @@ +name: Code analysis + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +# cSpell: ignore potatoqualitee codeql SARIF +jobs: + pssa: + name: PSScriptAnalyzer + runs-on: windows-latest + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + #actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Build Module + shell: powershell + run: | + Write-Information -MessageData 'Module is being built so that examples can be scanned.' -InformationAction 'Continue' + .\build.ps1 -ResolveDependency -Tasks 'build' + - name: Run PSScriptAnalyzer + shell: powershell + run: | + Write-Information -MessageData 'Prepare the test pipeline.' -InformationAction 'Continue' + .\build.ps1 -Tasks 'noop' + + Write-Information -MessageData 'Load SMO stubs into session.' -InformationAction 'Continue' + Add-Type -Path './tests/Unit/Stubs/SMO.cs' + + Write-Information -MessageData 'Import module ConvertToSARIF into the session.' -InformationAction 'Continue' + Import-Module -Name 'ConvertToSARIF' -Force + + Write-Information -MessageData 'Import module PSScriptAnalyzer into the session.' -InformationAction 'Continue' + Import-Module -Name 'PSScriptAnalyzer' -Force + + $filesToScan = Get-ChildItem -Path './source/' -Recurse -Include @('*.psm1', '*.ps1') -File + Write-Information -MessageData ("Will scan the files:`n`r`t{0}." -f ($filesToScan.FullName -join "`n`r`t")) -InformationAction 'Continue' + + Write-Information -MessageData 'Running PSScriptAnalyzer.' -InformationAction 'Continue' + $pssaError = $filesToScan | + Invoke-ScriptAnalyzer -Settings './.vscode/analyzersettings.psd1' + + $parseErrorTypes = @( + 'TypeNotFound' + 'RequiresModuleInvalid' + ) + Write-Information -MessageData ('Filter out reported parse errors that is unable to be resolved in source files: {0}' -f ($parseErrorTypes -join ', ')) -InformationAction 'Continue' + $pssaError = $pssaError | + Where-Object -FilterScript { + $_.RuleName -notin $parseErrorTypes + } + + Write-Information -MessageData 'Converting PSScriptAnalyzer result to SARIF.' -InformationAction 'Continue' + $pssaError | + ConvertTo-SARIF -FilePath 'results.sarif' + + Write-Information -MessageData 'Analyzing done.' -InformationAction 'Continue' + - name: Upload SARIF results + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: results.sarif diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 index e9a88c343..192f3b679 100644 --- a/.vscode/analyzersettings.psd1 +++ b/.vscode/analyzersettings.psd1 @@ -71,11 +71,15 @@ 'UseSyntacticallyCorrectExamples' ) - # TODO: This is not excluded correctly, see test QA/ScriptAnalyzer.Tests.ps1 for more information. - ExcludeRules = @( - 'TypeNotFound' - 'RequiresModuleInvalid' # Becuase 'using module' in prefix.ps1 cannot be resolved as source file. - ) + <# + The following types are not rules but parse errors reported by PSScriptAnalyzer + so they cannot be ecluded. They need to be filtered out from the result of + Invoke-ScriptAnalyzer. + + TypeNotFound - Because classes in the project cannot be found unless built. + RequiresModuleInvalid - Because 'using module' in prefix.ps1 cannot be resolved as source file. + #> + ExcludeRules = @() Rules = @{ PSUseConsistentWhitespace = @{ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a21f826d..486e4d68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- SqlServerDsc + - New GitHub Actions workflow that run PSScriptAnalyzer for PRs so any + issues are shown directly in the PR's changed files ([issue #1860](https://github.com/dsccommunity/SqlServerDsc/issues/1860)). + ### Changed - SqlServerDsc diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 258412816..730f13427 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -9,6 +9,7 @@ InvokeBuild = 'latest' PSScriptAnalyzer = 'latest' + ConvertToSARIF = 'latest' # cSpell: disable-line <# If preview release of Pester prevents release we should temporary shift diff --git a/tests/QA/ScriptAnalyzer.Tests.ps1 b/tests/QA/ScriptAnalyzer.Tests.ps1 index 6fbe746e6..0590e6eb4 100644 --- a/tests/QA/ScriptAnalyzer.Tests.ps1 +++ b/tests/QA/ScriptAnalyzer.Tests.ps1 @@ -72,17 +72,20 @@ Describe 'Script Analyzer Rules' { It 'Should pass all PS Script Analyzer rules for file ''''' -ForEach $testCases { $pssaError = Invoke-ScriptAnalyzer -Path $ScriptPath -Settings $scriptAnalyzerSettingsPath - <# - Filter out rule TypeNotFound. + $parseErrorTypes = @( + 'TypeNotFound' + 'RequiresModuleInvalid' + ) - TODO: The rules (e.g. "TypeNotFound") are not excluded correctly even if it - is excluded in the file 'analyzersettings.psd1'. This is a workaround - until it is properly excluded for source files, and instead only is - run for the built module script module file (SqlServerDsc.psm1). - #> - $pssaError = $pssaError | Where-Object -FilterScript { $_.RuleName -notin @('TypeNotFound', 'RequiresModuleInvalid') } + # Filter out reported parse errors that is unable to be resolved in source files + $pssaError = $pssaError | + Where-Object -FilterScript { + $_.RuleName -notin $parseErrorTypes + } + + $report = $pssaError | + Format-Table -AutoSize | Out-String -Width 200 - $report = $pssaError | Format-Table -AutoSize | Out-String -Width 200 $pssaError | Should -HaveCount 0 -Because "all script analyzer rules should pass.`r`n`r`n $report`r`n" } }