From 77117a75c47ea95289030f7a304d79fa07340f70 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 18:35:45 +0200 Subject: [PATCH 1/6] Isolate analyzer reports and documentation artifacts --- .github/workflows/Build-Docs.yml | 27 ------------- .github/workflows/Lint-SourceCode.yml | 1 + .github/workflows/Test-Module.yml | 1 + .github/workflows/Workflow-Test-Default.yml | 42 +++++++++++++++++++++ 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.github/workflows/Build-Docs.yml b/.github/workflows/Build-Docs.yml index a8d2fe6b..fbbcdf42 100644 --- a/.github/workflows/Build-Docs.yml +++ b/.github/workflows/Build-Docs.yml @@ -49,33 +49,6 @@ jobs: if-no-files-found: error retention-days: 1 - - name: Commit all changes - uses: PSModule/GitHub-Script@8083ec1f733f00357ee4d0db0c6056686e483bc0 # v1.9.0 - with: - Debug: ${{ fromJson(inputs.Settings).Debug }} - Prerelease: ${{ fromJson(inputs.Settings).Prerelease }} - Verbose: ${{ fromJson(inputs.Settings).Verbose }} - Version: ${{ fromJson(inputs.Settings).Version }} - WorkingDirectory: ${{ fromJson(inputs.Settings).WorkingDirectory }} - Script: | - # Rename the gitignore file to .gitignore.bak - if (Test-Path -Path .gitignore) { - Rename-Item -Path '.gitignore' -NewName '.gitignore.bak' -Force - } - - try { - # Add all changes to the repository - git add . - git commit -m 'Update documentation' - } catch { - Write-Host "No changes to commit" - } - - # Restore the gitignore file - if (Test-Path -Path .gitignore.bak) { - Rename-Item -Path '.gitignore.bak' -NewName '.gitignore' -Force - } - - name: Lint documentation id: super-linter uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0 diff --git a/.github/workflows/Lint-SourceCode.yml b/.github/workflows/Lint-SourceCode.yml index 1b3db811..bc873964 100644 --- a/.github/workflows/Lint-SourceCode.yml +++ b/.github/workflows/Lint-SourceCode.yml @@ -35,4 +35,5 @@ jobs: Path: src WorkingDirectory: ${{ fromJson(inputs.Settings).WorkingDirectory }} TestResult_Enabled: true + TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-SourceCode-${{ runner.os }} diff --git a/.github/workflows/Test-Module.yml b/.github/workflows/Test-Module.yml index 1d48d2f6..e9937c18 100644 --- a/.github/workflows/Test-Module.yml +++ b/.github/workflows/Test-Module.yml @@ -79,4 +79,5 @@ jobs: Verbose: ${{ fromJson(inputs.Settings).Verbose }} WorkingDirectory: ${{ fromJson(inputs.Settings).WorkingDirectory }} TestResult_Enabled: true + TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-Module-${{ runner.os }} diff --git a/.github/workflows/Workflow-Test-Default.yml b/.github/workflows/Workflow-Test-Default.yml index 5b3da9fb..c66115e2 100644 --- a/.github/workflows/Workflow-Test-Default.yml +++ b/.github/workflows/Workflow-Test-Default.yml @@ -76,6 +76,48 @@ jobs: persist-credentials: false fetch-depth: 0 + - name: Verify generated artifacts remain isolated + shell: pwsh + run: | + $expectedAnalyzerReportPaths = @{ + '.github/workflows/Test-Module.yml' = 'TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml' + '.github/workflows/Lint-SourceCode.yml' = 'TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml' + } + + foreach ($workflowPath in $expectedAnalyzerReportPaths.Keys) { + $content = Get-Content -Path $workflowPath -Raw + $expectedPath = $expectedAnalyzerReportPaths[$workflowPath] + + if (-not $content.Contains($expectedPath)) { + throw "Expected isolated analyzer report path was not found in $workflowPath." + } + } + + $buildDocsWorkflow = Get-Content -Path '.github/workflows/Build-Docs.yml' -Raw + $forbiddenStagingPatterns = @( + 'Rename-Item\s+-Path\s+[''"]\.gitignore[''"]' + 'git add \.' + 'PSModule/GitHub-Script' + ) + + foreach ($pattern in $forbiddenStagingPatterns) { + if ($buildDocsWorkflow -match $pattern) { + throw "Build-Docs.yml must not use generated-artifact staging: $pattern" + } + } + + $legacyArtifactPaths = @( + 'tests/srcTestRepo/TestResult' + 'tests/srcTestRepo/CodeCoverage' + 'tests/srcTestRepo/.temp' + ) + + foreach ($path in $legacyArtifactPaths) { + if (Test-Path -Path $path) { + throw "Legacy root artifact path was created: $path" + } + } + - name: Download docs artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: From 192ca04e99cad1a0389aef5bb2cfab01f03f9479 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 18:36:40 +0200 Subject: [PATCH 2/6] Verify analyzer report artifact isolation --- .github/workflows/Lint-SourceCode.yml | 21 +++++++++++++++++++++ .github/workflows/Test-Module.yml | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/.github/workflows/Lint-SourceCode.yml b/.github/workflows/Lint-SourceCode.yml index bc873964..81033f49 100644 --- a/.github/workflows/Lint-SourceCode.yml +++ b/.github/workflows/Lint-SourceCode.yml @@ -37,3 +37,24 @@ jobs: TestResult_Enabled: true TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-SourceCode-${{ runner.os }} + + - name: Verify analyzer report location + shell: pwsh + working-directory: ${{ fromJson(inputs.Settings).WorkingDirectory }} + run: | + $reportPath = '.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml' + if (-not (Test-Path -Path $reportPath)) { + throw "Expected analyzer report was not created at $reportPath." + } + + $legacyArtifactPaths = @( + 'TestResult' + 'CodeCoverage' + '.temp' + ) + + foreach ($path in $legacyArtifactPaths) { + if (Test-Path -Path $path) { + throw "Legacy root artifact path was created: $path" + } + } diff --git a/.github/workflows/Test-Module.yml b/.github/workflows/Test-Module.yml index e9937c18..8d2b8cb4 100644 --- a/.github/workflows/Test-Module.yml +++ b/.github/workflows/Test-Module.yml @@ -81,3 +81,24 @@ jobs: TestResult_Enabled: true TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-Module-${{ runner.os }} + + - name: Verify analyzer report location + shell: pwsh + working-directory: ${{ fromJson(inputs.Settings).WorkingDirectory }} + run: | + $reportPath = '.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml' + if (-not (Test-Path -Path $reportPath)) { + throw "Expected analyzer report was not created at $reportPath." + } + + $legacyArtifactPaths = @( + 'TestResult' + 'CodeCoverage' + '.temp' + ) + + foreach ($path in $legacyArtifactPaths) { + if (Test-Path -Path $path) { + throw "Legacy root artifact path was created: $path" + } + } From 33e52f5b8ff175fee0eed0bf31788e60d3a3f848 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 21:17:03 +0200 Subject: [PATCH 3/6] Use released analyzer report path support --- .github/workflows/Lint-SourceCode.yml | 2 +- .github/workflows/Test-Module.yml | 2 +- .github/workflows/Workflow-Test-Default.yml | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Lint-SourceCode.yml b/.github/workflows/Lint-SourceCode.yml index 81033f49..6f074ef1 100644 --- a/.github/workflows/Lint-SourceCode.yml +++ b/.github/workflows/Lint-SourceCode.yml @@ -26,7 +26,7 @@ jobs: persist-credentials: false - name: Lint-SourceCode - uses: PSModule/Invoke-ScriptAnalyzer@4d633e4df1f1fa575949a328839d33c3a0838765 # v5.0.0 + uses: PSModule/Invoke-ScriptAnalyzer@9acddbd55ff4634b738be3a219c76e3676aaf1cd # v5.0.1 with: Debug: ${{ fromJson(inputs.Settings).Debug }} GitHubPrerelease: ${{ fromJson(inputs.Settings).Prerelease }} diff --git a/.github/workflows/Test-Module.yml b/.github/workflows/Test-Module.yml index 8d2b8cb4..427be53e 100644 --- a/.github/workflows/Test-Module.yml +++ b/.github/workflows/Test-Module.yml @@ -70,7 +70,7 @@ jobs: path: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/module - name: Lint-Module - uses: PSModule/Invoke-ScriptAnalyzer@4d633e4df1f1fa575949a328839d33c3a0838765 # v5.0.0 + uses: PSModule/Invoke-ScriptAnalyzer@9acddbd55ff4634b738be3a219c76e3676aaf1cd # v5.0.1 with: Path: .PSModule/module Debug: ${{ fromJson(inputs.Settings).Debug }} diff --git a/.github/workflows/Workflow-Test-Default.yml b/.github/workflows/Workflow-Test-Default.yml index c66115e2..00918935 100644 --- a/.github/workflows/Workflow-Test-Default.yml +++ b/.github/workflows/Workflow-Test-Default.yml @@ -91,6 +91,11 @@ jobs: if (-not $content.Contains($expectedPath)) { throw "Expected isolated analyzer report path was not found in $workflowPath." } + + $expectedAction = 'uses: PSModule/Invoke-ScriptAnalyzer@9acddbd55ff4634b738be3a219c76e3676aaf1cd # v5.0.1' + if (-not $content.Contains($expectedAction)) { + throw "Expected released analyzer action pin was not found in $workflowPath." + } } $buildDocsWorkflow = Get-Content -Path '.github/workflows/Build-Docs.yml' -Raw From fa5612548319d60e4bb078921bc2376e31d0b63c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 21:23:10 +0200 Subject: [PATCH 4/6] Resolve analyzer reports from the consumer root --- .github/workflows/Lint-SourceCode.yml | 2 +- .github/workflows/Test-Module.yml | 2 +- .github/workflows/Workflow-Test-Default.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/Lint-SourceCode.yml b/.github/workflows/Lint-SourceCode.yml index 6f074ef1..ce1cffe5 100644 --- a/.github/workflows/Lint-SourceCode.yml +++ b/.github/workflows/Lint-SourceCode.yml @@ -35,7 +35,7 @@ jobs: Path: src WorkingDirectory: ${{ fromJson(inputs.Settings).WorkingDirectory }} TestResult_Enabled: true - TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml + TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-SourceCode-${{ runner.os }} - name: Verify analyzer report location diff --git a/.github/workflows/Test-Module.yml b/.github/workflows/Test-Module.yml index 427be53e..5f1cbb67 100644 --- a/.github/workflows/Test-Module.yml +++ b/.github/workflows/Test-Module.yml @@ -79,7 +79,7 @@ jobs: Verbose: ${{ fromJson(inputs.Settings).Verbose }} WorkingDirectory: ${{ fromJson(inputs.Settings).WorkingDirectory }} TestResult_Enabled: true - TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml + TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml TestResult_TestSuiteName: PSModuleLint-Module-${{ runner.os }} - name: Verify analyzer report location diff --git a/.github/workflows/Workflow-Test-Default.yml b/.github/workflows/Workflow-Test-Default.yml index 00918935..83b0cafa 100644 --- a/.github/workflows/Workflow-Test-Default.yml +++ b/.github/workflows/Workflow-Test-Default.yml @@ -80,8 +80,8 @@ jobs: shell: pwsh run: | $expectedAnalyzerReportPaths = @{ - '.github/workflows/Test-Module.yml' = 'TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml' - '.github/workflows/Lint-SourceCode.yml' = 'TestResult_OutputPath: ${{ fromJson(inputs.Settings).WorkingDirectory }}/.PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml' + '.github/workflows/Test-Module.yml' = 'TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml' + '.github/workflows/Lint-SourceCode.yml' = 'TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml' } foreach ($workflowPath in $expectedAnalyzerReportPaths.Keys) { From 16077b909245356754319df78eb09cea7644ccd9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 21:29:19 +0200 Subject: [PATCH 5/6] Preserve literal workflow expression in fixture check --- .github/workflows/Workflow-Test-Default.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Workflow-Test-Default.yml b/.github/workflows/Workflow-Test-Default.yml index 83b0cafa..132c3d5e 100644 --- a/.github/workflows/Workflow-Test-Default.yml +++ b/.github/workflows/Workflow-Test-Default.yml @@ -79,9 +79,10 @@ jobs: - name: Verify generated artifacts remain isolated shell: pwsh run: | + $runnerOsExpression = '$' + '{{ runner.os }}' $expectedAnalyzerReportPaths = @{ - '.github/workflows/Test-Module.yml' = 'TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-Module-${{ runner.os }}-TestResult-Report.xml' - '.github/workflows/Lint-SourceCode.yml' = 'TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-SourceCode-${{ runner.os }}-TestResult-Report.xml' + '.github/workflows/Test-Module.yml' = "TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-Module-$runnerOsExpression-TestResult-Report.xml" + '.github/workflows/Lint-SourceCode.yml' = "TestResult_OutputPath: .PSModule/TestResult/PSModuleLint-SourceCode-$runnerOsExpression-TestResult-Report.xml" } foreach ($workflowPath in $expectedAnalyzerReportPaths.Keys) { From 2001551566898e59efc764e68450c2238667d607 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 10 Sep 2026 22:00:25 +0200 Subject: [PATCH 6/6] Document consumer artifact root --- docs/content/reference/pipeline-stages.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/content/reference/pipeline-stages.md b/docs/content/reference/pipeline-stages.md index 7611b9d5..29d53918 100644 --- a/docs/content/reference/pipeline-stages.md +++ b/docs/content/reference/pipeline-stages.md @@ -11,6 +11,25 @@ implements it. For which of these jobs run in a given trigger scenario, see the [scenario matrix](scenario-matrix.md). +## Consumer artifact root + +The framework stores consumer build, documentation, test, and coverage artifacts +under `/.PSModule/`. This keeps generated output separate from +the module source. Consumer repositories SHOULD ignore `.PSModule/`. + +| Path | Contents | +| --- | --- | +| `.PSModule/module/` | The compiled, versioned module artifact. | +| `.PSModule/docs/` | Generated command documentation passed to the site build. | +| `.PSModule/site/` | Assembled site input and generated static site output at `_site/`. | +| `.PSModule/TestResult/` | Per-suite Pester and analyzer test-result reports. | +| `.PSModule/CodeCoverage/` | Per-suite code-coverage reports. | + +Pester stores its temporary execution state outside the consumer checkout. +Runner-only output, including `TestResults/`, `CodeCoverage/`, +`CodeCoverage-MissedPaths/`, and `super-linter-output/`, is not a consumer +repository artifact. + ## Plan [workflow](https://github.com/PSModule/Process-PSModule/blob/main/.github/workflows/Plan.yml)