From 39faaef98e1add5829ccb9ff36428bb97569a6df Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 10 Jul 2026 22:17:54 +0200 Subject: [PATCH 1/8] test: E2E for Import-TestData, section docs, and versioning Point CI at PSModule/Process-PSModule@feat/import-testdata. Push a masked secret (TEST_SECRET) and a non-masked variable (TEST_VARIABLE) into the module test jobs via TestData, asserted in tests/Environment.Tests.ps1. Reorganize public functions into sections (Greetings/ with a named landing page, DateAndTime/ with an index.md landing page) to show documentation on the section nodes, leaving Get-PSModuleTest at the top level. --- .github/workflows/Process-PSModule.yml | 7 ++++- .../{ => DateAndTime}/Get-CurrentDateTime.ps1 | 2 +- src/functions/public/DateAndTime/index.md | 7 +++++ .../public/{ => Greetings}/Get-Greeting.ps1 | 2 +- src/functions/public/Greetings/Greetings.md | 7 +++++ tests/Environment.Tests.ps1 | 30 +++++++++++++++++++ 6 files changed, 52 insertions(+), 3 deletions(-) rename src/functions/public/{ => DateAndTime}/Get-CurrentDateTime.ps1 (97%) create mode 100644 src/functions/public/DateAndTime/index.md rename src/functions/public/{ => Greetings}/Get-Greeting.ps1 (97%) create mode 100644 src/functions/public/Greetings/Greetings.md create mode 100644 tests/Environment.Tests.ps1 diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 95c66fa..3685008 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -27,6 +27,11 @@ permissions: jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e # v5.4.3 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@feat/import-testdata # E2E: temporary branch ref for the Import-TestData test secrets: APIKey: ${{ secrets.APIKey }} + # E2E: push a secret (masked in logs) and a variable (not masked) into the module test jobs. + # Import-TestData exposes each entry as $env:; asserted in tests/Environment.Tests.ps1. + TestData: >- + { "secrets": { "TEST_SECRET": "${{ secrets.TEST_SECRET }}" }, + "variables": { "TEST_VARIABLE": ${{ toJSON(vars.TEST_VARIABLE) }} } } diff --git a/src/functions/public/Get-CurrentDateTime.ps1 b/src/functions/public/DateAndTime/Get-CurrentDateTime.ps1 similarity index 97% rename from src/functions/public/Get-CurrentDateTime.ps1 rename to src/functions/public/DateAndTime/Get-CurrentDateTime.ps1 index eb36af1..c732d1a 100644 --- a/src/functions/public/Get-CurrentDateTime.ps1 +++ b/src/functions/public/DateAndTime/Get-CurrentDateTime.ps1 @@ -23,7 +23,7 @@ Returns the current date in a custom format like "Monday, January 20, 2026". .LINK - https://MariusStorhaug.github.io/MariusTestModule/Functions/Get-CurrentDateTime/ + https://MariusStorhaug.github.io/MariusTestModule/Functions/DateAndTime/Get-CurrentDateTime/ #> [OutputType([string])] [CmdletBinding()] diff --git a/src/functions/public/DateAndTime/index.md b/src/functions/public/DateAndTime/index.md new file mode 100644 index 0000000..03c46dd --- /dev/null +++ b/src/functions/public/DateAndTime/index.md @@ -0,0 +1,7 @@ +# Date and Time + +Functions for retrieving and formatting the current date and time. + +This section's landing page is provided by an `index.md` file. When Process-PSModule builds the +documentation site, this page becomes the landing page for the **Date and Time** group in the +navigation on GitHub Pages. diff --git a/src/functions/public/Get-Greeting.ps1 b/src/functions/public/Greetings/Get-Greeting.ps1 similarity index 97% rename from src/functions/public/Get-Greeting.ps1 rename to src/functions/public/Greetings/Get-Greeting.ps1 index 37712c2..f2ffe64 100644 --- a/src/functions/public/Get-Greeting.ps1 +++ b/src/functions/public/Greetings/Get-Greeting.ps1 @@ -23,7 +23,7 @@ Returns "Good Morning, Alice!" to the user. .LINK - https://MariusStorhaug.github.io/MariusTestModule/Functions/Get-Greeting/ + https://MariusStorhaug.github.io/MariusTestModule/Functions/Greetings/Get-Greeting/ #> [OutputType([string])] [CmdletBinding()] diff --git a/src/functions/public/Greetings/Greetings.md b/src/functions/public/Greetings/Greetings.md new file mode 100644 index 0000000..4c8d79a --- /dev/null +++ b/src/functions/public/Greetings/Greetings.md @@ -0,0 +1,7 @@ +# Greetings + +Functions for composing greeting messages. + +This section's landing page is provided by a file named after its folder (`Greetings.md`). When +Process-PSModule builds the documentation site, this page becomes the landing page for the +**Greetings** group in the navigation on GitHub Pages. diff --git a/tests/Environment.Tests.ps1 b/tests/Environment.Tests.ps1 new file mode 100644 index 0000000..8e783ef --- /dev/null +++ b/tests/Environment.Tests.ps1 @@ -0,0 +1,30 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', '', + Justification = 'Required for Pester tests' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', + Justification = 'Required for Pester tests' +)] +[CmdletBinding()] +param() + +Describe 'TestData is pushed into the module tests' { + # TEST_SECRET (from the "secrets" map, masked) and TEST_VARIABLE (from the "variables" map, not + # masked) are public non-secret fixtures that exist only to prove the calling workflow can push + # secrets and variables into the module test jobs. The calling workflow passes them through a + # single TestData object and Import-TestData (from Install-PSModuleHelpers) exposes them as + # environment variables; these tests confirm they arrive with the expected values. + + It 'Exposes the secret from the "secrets" map' { + $actual = [System.Environment]::GetEnvironmentVariable('TEST_SECRET') + $actual | Should -Not -BeNullOrEmpty + $actual | Should -BeExactly 'mariustestmodule-secret-fixture-value' + } + + It 'Exposes the variable from the "variables" map' { + $actual = [System.Environment]::GetEnvironmentVariable('TEST_VARIABLE') + $actual | Should -Not -BeNullOrEmpty + $actual | Should -BeExactly 'mariustestmodule-variable-fixture-value' + } +} From 5643f7b9daef7ac78b971f58317523f65b52ab0b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 10 Jul 2026 22:22:40 +0200 Subject: [PATCH 2/8] test: print the secret and variable in plain text to show masking Adds an It block that Write-Hosts both values. GitHub Actions masks TEST_SECRET (registered via ::add-mask:: by Import-TestData) to *** in the log, while TEST_VARIABLE is printed verbatim - demonstrating that only the secrets map is masked. --- tests/Environment.Tests.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Environment.Tests.ps1 b/tests/Environment.Tests.ps1 index 8e783ef..1544386 100644 --- a/tests/Environment.Tests.ps1 +++ b/tests/Environment.Tests.ps1 @@ -6,6 +6,10 @@ 'PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Required for Pester tests' )] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSAvoidUsingWriteHost', '', + Justification = 'Deliberately prints the values to the log to demonstrate GitHub Actions masking' +)] [CmdletBinding()] param() @@ -27,4 +31,12 @@ Describe 'TestData is pushed into the module tests' { $actual | Should -Not -BeNullOrEmpty $actual | Should -BeExactly 'mariustestmodule-variable-fixture-value' } + + It 'Masks the secret in the log even when a test prints it in plain text' { + # Deliberately try to leak both values to the log with Write-Host. Because Import-TestData + # registered the secret via ::add-mask::, GitHub Actions redacts it to *** in the log, while + # the variable (not masked) is printed verbatim. Inspect the job log to see the difference. + Write-Host "Secret in plain text: $env:TEST_SECRET" + Write-Host "Variable in plain text: $env:TEST_VARIABLE" + } } From 030d4273d601d927c898989560e1db47bca59f8d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Fri, 10 Jul 2026 22:50:52 +0200 Subject: [PATCH 3/8] chore: re-run pipeline against updated Process-PSModule branch (resolver preview) From 7d62d811197e233db1ab24c20a55d433e121d0a8 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 10:55:47 +0200 Subject: [PATCH 4/8] ci: update Process-PSModule to v6.1.2 The Import-TestData plumbing and always-evaluate-version-labels fix are now released in Process-PSModule v6.1.2 (via PSModule/Process-PSModule#377, IPH v1.0.9 + Resolve-PSModuleVersion v1.1.4), so the temporary feat/import-testdata branch ref is replaced with the released tag. --- .github/workflows/Process-PSModule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 3685008..2d744da 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -27,7 +27,7 @@ permissions: jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@feat/import-testdata # E2E: temporary branch ref for the Import-TestData test + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v6.1.2 secrets: APIKey: ${{ secrets.APIKey }} # E2E: push a secret (masked in logs) and a variable (not masked) into the module test jobs. From 344f3605bf77a8cb708f405551fc3dc15f3cdfbe Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 11:06:23 +0200 Subject: [PATCH 5/8] ci: pin Process-PSModule to commit SHA Per the GitHub Actions coding standard, uses: references are pinned to a full 40-character commit SHA with the human-readable version as a trailing comment. Replaces the v6.1.2 tag pin with its commit SHA (d4020f3). --- .github/workflows/Process-PSModule.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 2d744da..78d5575 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -27,7 +27,7 @@ permissions: jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v6.1.2 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@d4020f3c9c3621cebae5d8bf4ffaf10f55c1784a # v6.1.2 secrets: APIKey: ${{ secrets.APIKey }} # E2E: push a secret (masked in logs) and a variable (not masked) into the module test jobs. From 830ba53b58b945a3fd65f063fdafdb7cfbf9336e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 11:31:38 +0200 Subject: [PATCH 6/8] experiment: pass all secrets and variables into module tests Replaces the two hardcoded TestData fixtures with toJSON(secrets)/toJSON(vars) to dump every secret and variable, and rewrites the environment test to iterate and print every environment variable the job can see. Purpose: observe whether Import-TestData can enumerate a full dump (and how Actions masks secrets). --- .github/workflows/Process-PSModule.yml | 10 +++--- tests/Environment.Tests.ps1 | 42 ++++++++++++-------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 78d5575..8c0f992 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -30,8 +30,10 @@ jobs: uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@d4020f3c9c3621cebae5d8bf4ffaf10f55c1784a # v6.1.2 secrets: APIKey: ${{ secrets.APIKey }} - # E2E: push a secret (masked in logs) and a variable (not masked) into the module test jobs. - # Import-TestData exposes each entry as $env:; asserted in tests/Environment.Tests.ps1. + # E2E EXPERIMENT: push ALL secrets and ALL variables into the module test jobs to see whether + # Import-TestData can enumerate them. toJSON(secrets) serializes every secret and always + # includes the auto-injected github_token; toJSON(vars) serializes every repo/org variable. + # The tests iterate every environment variable and print it so the job log shows what is + # visible (Actions redacts registered secret values to ***). TestData: >- - { "secrets": { "TEST_SECRET": "${{ secrets.TEST_SECRET }}" }, - "variables": { "TEST_VARIABLE": ${{ toJSON(vars.TEST_VARIABLE) }} } } + { "secrets": ${{ toJSON(secrets) }}, "variables": ${{ toJSON(vars) }} } diff --git a/tests/Environment.Tests.ps1 b/tests/Environment.Tests.ps1 index 1544386..4fae86c 100644 --- a/tests/Environment.Tests.ps1 +++ b/tests/Environment.Tests.ps1 @@ -13,30 +13,28 @@ [CmdletBinding()] param() -Describe 'TestData is pushed into the module tests' { - # TEST_SECRET (from the "secrets" map, masked) and TEST_VARIABLE (from the "variables" map, not - # masked) are public non-secret fixtures that exist only to prove the calling workflow can push - # secrets and variables into the module test jobs. The calling workflow passes them through a - # single TestData object and Import-TestData (from Install-PSModuleHelpers) exposes them as - # environment variables; these tests confirm they arrive with the expected values. +Describe 'TestData exposes all secrets and variables' { + # EXPERIMENT: the calling workflow now passes every secret and every variable through the single + # TestData object via toJSON(secrets)/toJSON(vars). Import-TestData (from Install-PSModuleHelpers) + # exposes each entry as an environment variable. These tests iterate every environment variable + # visible to the job and print it, so the job log shows exactly what reached the module tests. + # GitHub Actions redacts any registered secret value to *** in the log; plain variables print + # verbatim. Inspect the job log to see which names arrived and how they are rendered. - It 'Exposes the secret from the "secrets" map' { - $actual = [System.Environment]::GetEnvironmentVariable('TEST_SECRET') - $actual | Should -Not -BeNullOrEmpty - $actual | Should -BeExactly 'mariustestmodule-secret-fixture-value' + It 'Iterates every environment variable and prints what the job can see' { + $all = Get-ChildItem env: | Sort-Object Name + Write-Host "===== BEGIN environment dump ($($all.Count) variables) =====" + foreach ($entry in $all) { + Write-Host ("{0} = {1}" -f $entry.Name, $entry.Value) + } + Write-Host '===== END environment dump =====' + $all.Count | Should -BeGreaterThan 0 } - It 'Exposes the variable from the "variables" map' { - $actual = [System.Environment]::GetEnvironmentVariable('TEST_VARIABLE') - $actual | Should -Not -BeNullOrEmpty - $actual | Should -BeExactly 'mariustestmodule-variable-fixture-value' - } - - It 'Masks the secret in the log even when a test prints it in plain text' { - # Deliberately try to leak both values to the log with Write-Host. Because Import-TestData - # registered the secret via ::add-mask::, GitHub Actions redacts it to *** in the log, while - # the variable (not masked) is printed verbatim. Inspect the job log to see the difference. - Write-Host "Secret in plain text: $env:TEST_SECRET" - Write-Host "Variable in plain text: $env:TEST_VARIABLE" + It 'Reports the fixture secret and variable arrived' { + # These two fixtures are part of the full dump (a repo secret and a repo variable). If the + # full enumeration worked, both are present; the secret prints as *** because it is masked. + Write-Host "TEST_SECRET = $env:TEST_SECRET" + Write-Host "TEST_VARIABLE = $env:TEST_VARIABLE" } } From 9caa0be11205aa3aa2903df2703691f4cb92309a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 11:37:56 +0200 Subject: [PATCH 7/8] experiment: strip github_token via prep job so full dump passes The raw toJSON(secrets) dump failed because Import-TestData rejects the auto-injected github_token (reserved GITHUB_ prefix). Add a Prep-TestData job that serializes all secrets/variables, removes github_token, and forwards the sanitized JSON as the TestData secret so the full enumeration can actually run. --- .github/workflows/Process-PSModule.yml | 35 ++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 8c0f992..08854b2 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -26,14 +26,35 @@ permissions: id-token: write jobs: + # EXPERIMENT: a raw toJSON(secrets) dump fails because Import-TestData rejects the auto-injected + # github_token (reserved GITHUB_ prefix). This prep job serializes every secret and variable, + # strips github_token, and forwards the sanitized JSON to Process-PSModule as the TestData secret. + Prep-TestData: + runs-on: ubuntu-latest + outputs: + testdata: ${{ steps.build.outputs.testdata }} + steps: + - name: Build TestData from all secrets and variables + id: build + shell: pwsh + env: + ALL_SECRETS: ${{ toJSON(secrets) }} + ALL_VARS: ${{ toJSON(vars) }} + run: | + $secrets = $env:ALL_SECRETS | ConvertFrom-Json + $variables = $env:ALL_VARS | ConvertFrom-Json + # Drop the auto-injected github_token; Import-TestData rejects the reserved GITHUB_ prefix. + if ($secrets.PSObject.Properties['github_token']) { + $secrets.PSObject.Properties.Remove('github_token') + } + $payload = [ordered]@{ secrets = $secrets; variables = $variables } | ConvertTo-Json -Compress -Depth 10 + "testdata=$payload" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + Process-PSModule: + needs: Prep-TestData uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@d4020f3c9c3621cebae5d8bf4ffaf10f55c1784a # v6.1.2 secrets: APIKey: ${{ secrets.APIKey }} - # E2E EXPERIMENT: push ALL secrets and ALL variables into the module test jobs to see whether - # Import-TestData can enumerate them. toJSON(secrets) serializes every secret and always - # includes the auto-injected github_token; toJSON(vars) serializes every repo/org variable. - # The tests iterate every environment variable and print it so the job log shows what is - # visible (Actions redacts registered secret values to ***). - TestData: >- - { "secrets": ${{ toJSON(secrets) }}, "variables": ${{ toJSON(vars) }} } + # Forward the sanitized full dump: every secret except github_token, plus every variable. + # Import-TestData exposes each as $env:; tests/Environment.Tests.ps1 iterates and prints. + TestData: ${{ needs.Prep-TestData.outputs.testdata }} From ed18d86c5ce003097efddba1f0e2ce04028e1b11 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 11 Jul 2026 11:43:26 +0200 Subject: [PATCH 8/8] experiment: bulk-pass all variables via toJSON(vars); name secrets explicitly Proves the positive half: variables enumerate fine via toJSON(vars) (no github_token, not secret-blocked, passed inline), while secrets must be named explicitly. --- .github/workflows/Process-PSModule.yml | 36 ++++++-------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/.github/workflows/Process-PSModule.yml b/.github/workflows/Process-PSModule.yml index 08854b2..20afc36 100644 --- a/.github/workflows/Process-PSModule.yml +++ b/.github/workflows/Process-PSModule.yml @@ -26,35 +26,15 @@ permissions: id-token: write jobs: - # EXPERIMENT: a raw toJSON(secrets) dump fails because Import-TestData rejects the auto-injected - # github_token (reserved GITHUB_ prefix). This prep job serializes every secret and variable, - # strips github_token, and forwards the sanitized JSON to Process-PSModule as the TestData secret. - Prep-TestData: - runs-on: ubuntu-latest - outputs: - testdata: ${{ steps.build.outputs.testdata }} - steps: - - name: Build TestData from all secrets and variables - id: build - shell: pwsh - env: - ALL_SECRETS: ${{ toJSON(secrets) }} - ALL_VARS: ${{ toJSON(vars) }} - run: | - $secrets = $env:ALL_SECRETS | ConvertFrom-Json - $variables = $env:ALL_VARS | ConvertFrom-Json - # Drop the auto-injected github_token; Import-TestData rejects the reserved GITHUB_ prefix. - if ($secrets.PSObject.Properties['github_token']) { - $secrets.PSObject.Properties.Remove('github_token') - } - $payload = [ordered]@{ secrets = $secrets; variables = $variables } | ConvertTo-Json -Compress -Depth 10 - "testdata=$payload" | Out-File -FilePath $env:GITHUB_OUTPUT -Append - Process-PSModule: - needs: Prep-TestData uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@d4020f3c9c3621cebae5d8bf4ffaf10f55c1784a # v6.1.2 secrets: APIKey: ${{ secrets.APIKey }} - # Forward the sanitized full dump: every secret except github_token, plus every variable. - # Import-TestData exposes each as $env:; tests/Environment.Tests.ps1 iterates and prints. - TestData: ${{ needs.Prep-TestData.outputs.testdata }} + # EXPERIMENT conclusion: SECRETS cannot be bulk-dumped. toJSON(secrets) always carries + # github_token, which Import-TestData rejects (reserved GITHUB_ prefix); and pre-filtering it + # in a prep job is blocked by GitHub's "skip output may contain secret" guard on job outputs. + # So secrets must be named explicitly. VARIABLES, however, bulk-pass fine via toJSON(vars): + # no github_token, not secret, and passed inline (not through a job output). + TestData: >- + { "secrets": { "TEST_SECRET": "${{ secrets.TEST_SECRET }}" }, + "variables": ${{ toJSON(vars) }} }