Skip to content

Releases: PSModule/Process-PSModule

v6.1.7

Choose a tag to compare

@github-actions github-actions released this 16 Jul 20:35
8b1a261

🪲 [Fix]: TestData keys reach setup and teardown phases (#394)

TestData values now reach every module-local phase through the same export path. Setup scripts, module tests, and teardown scripts can rely on identical environment variable names for caller-provided secrets and variables, with secret masking preserved.

Fixed: Setup and teardown scripts receive TestData keys

BeforeAll-ModuleLocal, Test-ModuleLocal, and AfterAll-ModuleLocal now all call the same local Expose-TestData action after installing the shared helper module. This keeps TestData parsing, validation, masking, and GITHUB_ENV export behavior identical before each phase runs.

Callers continue to use the existing TestData workflow secret; no interface change is required.

Changed: TestData phase parity is documented

The README now states that the same TestData keys are available in setup, test, and teardown phases, and includes troubleshooting guidance for callers that use secrets: inherit without explicitly creating a TestData JSON payload.

Technical Details

  • Added .github/actions/Expose-TestData/action.yml as the single workflow step wrapper around Import-TestData.
  • Updated BeforeAll-ModuleLocal.yml, Test-ModuleLocal.yml, and AfterAll-ModuleLocal.yml to use the shared action.
  • Added fixture assertions to both workflow test repositories so PSMODULE_TEST_SINGLELINE_SECRET and PSMODULE_TEST_VARIABLE are checked in BeforeAll.ps1, Pester tests, and AfterAll.ps1.
  • Implementation plan progress: core shared export path, parity validation, setup/teardown regression coverage, and README guidance are complete.
  • Local validation: helper test scripts passed; setup/teardown fixture assertions passed for both test repositories; touched workflow files passed actionlint with the repository's known job.workflow_repository/job.workflow_sha context warnings ignored.

v6.1.6

Choose a tag to compare

@github-actions github-actions released this 16 Jul 18:10
33f7f5b

Bump PSModule/Invoke-Pester from 4.2.6 to 5.1.0 in the github-actions group across 1 directory (#393)

Bumps the github-actions group with 1 update in the / directory: PSModule/Invoke-Pester.

Updates PSModule/Invoke-Pester from 4.2.6 to 5.1.0

Release notes

Sourced from PSModule/Invoke-Pester's releases.

v5.1.0

🚀 [Minor]: Add optional GUID identity pinning to Pester selection (#73)

Adds an optional Guid input so a workflow can pin Pester by module identity (GUID), validated at install time. Combined with a #Requires GUID pin in test files, identity is validated the whole way — on the developer's machine and in CI at discovery, and now also in CI at the action's install step (shifted left, one clear failure point).

Added: Guid input for module-identity pinning

The optional Guid input pins Pester by module identity. After resolving and installing the version, the action validates the loaded module's GUID and fails fast if it does not match — guarding against a different module named Pester on the runner's PSModulePath.

- uses: PSModule/Invoke-Pester@v5
  with:
    Version: '6.0.0'
    Guid: 'a699dea5-2c73-4616-a270-1f7abb777e71'

Validation now happens at every layer:

  • Developer machine / CI at test discovery — via #Requires -Modules @{ ...; GUID = ... } in test files.
  • CI at the action's init/install step — via the new Guid input (earliest single point of failure).

Technical Details

  • action.yml: new optional Guid input, passed to both the init and exec phases via PSMODULE_INVOKE_PESTER_INPUT_Guid.
  • Install-PSResourceWithRetry: new -Guid parameter; after import, it validates the loaded module's Guid and throws a clear error on mismatch. Install-PSResource cannot select by GUID (gallery identity is name + version), so identity is enforced at import.
  • init.ps1 / exec.ps1: read the input and pass -Guid through.
  • Tests:
    • PesterGuidPin pins via both the Guid input and #Requires (validating the whole way).
    • PesterGuidMatch pins via the Guid input only (version-only #Requires), asserting a matching GUID lets the run succeed.
    • PesterGuidMismatch passes a wrong Guid and asserts the action fails.
  • Docs: README input table updated.
  • Verified locally: a correct GUID imports; a wrong GUID throws Loaded 'Pester' does not match the required GUID ....

This closes the last remaining item on #68 — the version-selection core shipped in #71 (v5.0.0), and this adds the optional GUID identity pin.

v5.0.0

🌟 [Major]: Version and Prerelease inputs now control Pester (#71)

Invoke-Pester now treats Version and Prerelease as Pester controls. Workflows that previously used those inputs to choose the GitHub PowerShell module used by the init bootstrap step must rename them to GitHubVersion and GitHubPrerelease. Workflows that did not set Version or Prerelease keep installing the latest available Pester by default.

Breaking Changes

Version and Prerelease now apply to Pester, not the GitHub PowerShell module used internally during init.

Before this change, a workflow like this selected the GitHub module version:

... (truncated)

Commits
  • 4ff3319 🚀 [Minor]: Add optional GUID identity pinning to Pester selection (#73)
  • 8a4e652 🌟 [Major]: Version and Prerelease inputs now control Pester (#71)
  • 0a4e7b3 Bump actions/checkout from 6.0.2 to 7.0.0 (#66)
  • 3299427 Bump super-linter/super-linter from 8.6.0 to 8.7.0 (#67)
  • See full diff in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions

v6.1.5

Choose a tag to compare

@github-actions github-actions released this 16 Jul 17:48
6dfddf4

🩹 [Patch]: Actions are internalized and automatically follow the workflow version (#385)

When you reference Process-PSModule at a release tag, run it from a branch during development, or test it from a fork, the actions it calls now automatically match that same version. There's no more keeping a workflow and its actions in sync by hand — pin the workflow, and the actions follow.

New: Actions automatically follow the workflow version in use

Every stage workflow now checks out its own source at the exact commit it is running from, then calls its actions from that local copy instead of a separately pinned action reference. In practice this means:

  • Reference a release tag → you get that tag's actions.
  • Run a development branch → the branch's actions run together with it, with no separate action release or pin update needed to test a change.
  • Fork the repository → your fork's actions are used, not upstream's.

This is possible using job.workflow_repository and job.workflow_sha, a set of GitHub Actions context properties that let a reusable workflow discover its own source repository and commit. This capability did not exist before — it was introduced by GitHub in April 2026 (shipped in Actions Runner v2.334.0, see actions/runner#4335) and is documented in the job context reference. No official GitHub changelog announcement could be found for this change — the runner release and context documentation are the primary references.

GitHub-Script, Invoke-Pester, and Invoke-ScriptAnalyzer continue to be consumed from their own repositories at pinned commits, as before.

No changes are required to consumer workflows. Inputs, outputs, and secrets are unchanged.

Changed: Internalized actions have moved out of their standalone repositories

Build-PSModule, Document-PSModule, Get-PSModuleSettings, Get-PesterCodeCoverage, Get-PesterTestResults, Initialize-PSModule, Install-PSModuleHelpers, Publish-PSModule, Resolve-PSModuleVersion, and Test-PSModule now ship directly inside Process-PSModule instead of living in their own PSModule repositories.

Those standalone repositories are now archived and will be deleted soon. If you reference any of them directly, switch to Process-PSModule instead. The old actions will also be the ones older versions of Process-PSModule will use, so be sure to update to be able to use the framework.

Technical Details

  • Ten composite actions are now bundled under .github/actions/: Build-PSModule, Document-PSModule, Get-PSModuleSettings, Get-PesterCodeCoverage, Get-PesterTestResults, Initialize-PSModule, Install-PSModuleHelpers, Publish-PSModule, Resolve-PSModuleVersion, Test-PSModule.
  • All stage workflows add a self-checkout step before any action call: repository: ${{ job.workflow_repository }}, ref: ${{ job.workflow_sha }}, path: _wf.
  • Action call sites change from PSModule/<Name>@<sha> to ./_wf/.github/actions/<Name>.
  • Nested dependencies on Install-PSModuleHelpers resolve from the same bundled revision.
  • .github/actionlint.yaml is updated to recognize the self-checkout pattern and the job.workflow_* properties.
  • Action READMEs and test-suite consolidation are out of scope for this change.

Validation

v6.1.4

Choose a tag to compare

@github-actions github-actions released this 11 Jul 15:00
da180ba

🪲 [Fix]: Resolve the current version on non-PR runs (bump Resolve-PSModuleVersion to v1.1.5) (#375)

The Plan job no longer fails on schedule and workflow_dispatch events. Resolve-PSModuleVersion is bumped to v1.1.5, which resolves the current published version on non–pull-request events instead of throwing.

Fixed: non–pull-request runs (schedule / workflow_dispatch)

Since v6.1.0 the Plan job runs Resolve-Version on every event, and the action threw "...must be run from a pull_request event" on non-PR events — failing the whole run (every job needs: Plan). Resolve-PSModuleVersion v1.1.5 (PSModule/Resolve-PSModuleVersion#10) fixes this at the source: on a non-PR event it returns the current published version — no bump, no prerelease, nothing published — floored at 0.0.0 for a module that has never been released. Pull-request and merge-to-default-branch behavior is unchanged: labels drive the bump (patch default), and the version preview on regular PRs is retained.

Technical Details

  • .github/workflows/Plan.yml: bump the Resolve-Version pin 8d1dac7 (v1.1.4) → 6a59a88 (v1.1.5). One-line change — no Plan gate or Test-ModuleLocal fallback is needed, because v1.1.5 always populates Settings.Module.Version on non-PR events.
  • This supersedes this PR's earlier Plan.yml gate + 999.0.0 fallback approach, replaced by the root-cause fix in the shared action.
  • Validated: a workflow_dispatch self-test run on this branch completes (previously failed at Plan); the PR self-test resolves a version as before.

v6.1.3

Choose a tag to compare

@github-actions github-actions released this 11 Jul 14:43
06fd9ad

🩹 [Patch]: Bump Invoke-ScriptAnalyzer to v5.0.0 and Test-PSModule to v3.0.14 (#380)

Brings Process-PSModule's lint/test action dependencies to their latest releases: PSModule/Invoke-ScriptAnalyzer v4.1.3 → v5.0.0 and PSModule/Test-PSModule v3.0.13 → v3.0.14. Both preserve the reusable workflow's Settings.Version/Settings.Prerelease contract for consumers.

Changed: Invoke-ScriptAnalyzer upgraded to v5.0.0

Invoke-ScriptAnalyzer v5.0.0 is a major release that repurposed its Version/Prerelease inputs to select the PSScriptAnalyzer module version and moved the GitHub bootstrap-module controls to GitHubVersion/GitHubPrerelease.

The Lint-SourceCode and Lint-Module steps pass Settings.Version/Settings.Prerelease, which in this ecosystem select the GitHub module (the same values feed the Invoke-Pester and GitHub-Script steps). They are now wired to Invoke-ScriptAnalyzer's GitHubVersion/GitHubPrerelease, so those settings keep controlling the GitHub module exactly as before. No change to the Settings contract.

With v5, the action also installs PSScriptAnalyzer itself (latest, since its Version is left unset) instead of relying on the runner's preinstalled copy — so consumer linting now runs against the latest PSScriptAnalyzer.

Changed: Test-PSModule upgraded to v3.0.14

Patch release; Test-PSModule v3.0.14 (which internally bumped Invoke-Pester to v5.1.0) preserves its own Version/Prerelease (GitHub module) contract, so the Test-SourceCode and Test-Module steps need only a SHA update — no input remap.

Technical Details

  • Lint-SourceCode.yml, Test-Module.yml: Invoke-ScriptAnalyzer 6aeb1bc (v4.1.3) → 4d633e4 (v5.0.0); remapped VersionGitHubVersion, PrereleaseGitHubPrerelease.
  • Test-Module.yml, Test-SourceCode.yml: Test-PSModule 25c9cd8 (v3.0.13) → 902c5e5 (v3.0.14).
  • Pester and PSScriptAnalyzer versions left at the v5 defaults (latest); not wired to any Settings key.
  • Scope: the Invoke-Pester step (Test-ModuleLocal.yml, still v4.2.6) is intentionally untouched.
  • Label note: the Settings contract is preserved (hence Patch), but consumer linting now runs against the latest PSScriptAnalyzer — bump to Minor if you'd rather signal that behavior change.

Release notes: Invoke-ScriptAnalyzer v5.0.0

v6.1.2

Choose a tag to compare

@github-actions github-actions released this 11 Jul 08:51
d4020f3

🪲 [Fix]: Test data reaches module tests in every consumer repository (#377)

Module test workflows now receive the caller-provided test data (secrets and variables) in every repository that consumes Process-PSModule, and version resolution once again honors the bump label on pull requests. Previously the BeforeAll, Test, and AfterAll module jobs failed at the "Expose caller-provided test data" step because they ran a script that shipped only inside Process-PSModule's own repository, so no consumer could actually use the TestData secret introduced in v6.0.0.

Fixed: Test data now reaches module tests in consumer repositories

The TestData secret (a single-line JSON object of secrets and variables) is now exposed to the BeforeAll, Test, and AfterAll module test jobs in any repository that consumes Process-PSModule. Each job installs the shared helpers and runs the Import-TestData command, which reads the payload, masks the secrets, and publishes every entry as an environment variable for the tests.

The consumer interface is unchanged — callers already pass TestData to workflow.yml (added in v6.0.0), so no migration is required:

secrets:
  TestData: >-
    { "secrets": { "TEST_SECRET": "${{ secrets.TEST_SECRET }}" },
      "variables": { "TEST_VARIABLE": ${{ toJSON(vars.TEST_VARIABLE) }} } }

Secrets arrive masked in the logs; variables arrive verbatim.

Fixed: Bump labels are honored during version resolution on pull requests

Version resolution now always evaluates the Major/Minor/Patch bump label, so a pull request labeled Minor previews the correct next minor version instead of defaulting to a patch prerelease. This is picked up by adopting Resolve-PSModuleVersion v1.1.4.

Technical Details

  • Removed .github/scripts/Expose-TestData.ps1 (138 lines); the exposure logic moved into the shared PSModule/Install-PSModuleHelpers module as the Import-TestData command, so it no longer has to be checked out from the caller's repository.
  • BeforeAll-ModuleLocal.yml, Test-ModuleLocal.yml, and AfterAll-ModuleLocal.yml each now install PSModule/Install-PSModuleHelpers@v1.0.9 and run Import-TestData (env PSMODULE_TEST_DATA: ${{ secrets.TestData }}) in place of the deleted script.
  • Plan.yml: PSModule/Resolve-PSModuleVersion pinned to v1.1.4, which contains the always-evaluate-labels fix.
  • Build-Site.yml: Install-PSModuleHelpers bumped v1.0.8 → v1.0.9 so the action resolves to a single version across the repo.
  • README.md documents that test data is exposed through Install-PSModuleHelpers / Import-TestData.
  • All action references are pinned to release commit SHAs (IPH v1.0.9 = 8bfb84d557755c67d9b5643efe573bdcae4c1a4a, Resolve v1.1.4 = 8d1dac7f326a45ba08060c1e24a5dd6f6f00b3ab); actionlint is clean.
  • Validated end-to-end on a consumer module: all module test matrix jobs (Linux/Windows/macOS) pass, with a masked secret and a verbatim variable observed in the tests.

v6.1.1

Choose a tag to compare

@github-actions github-actions released this 10 Jul 07:37
ea19a3e

🩹 [Patch]: Render group overview pages as section landing pages (#372)

Module documentation now renders a command group's overview page as that group's section landing page — the content shown when the group is selected in the navigation — instead of a separate page nested under it. This is delivered by bumping the Document-PSModule action, and the behavior is documented in the repository and module-source structure guidance.

Changed: Group overview pages are the section landing page

Bumped PSModule/Document-PSModule to v1.0.18 in Build-Docs.yml. v1.0.18 publishes a group's overview page as the section index (/Functions/<Group>/) rather than a page nested under the group. Authors can name the overview after its folder (<Category>/<Category>.md) or provide <Category>/index.md directly; either becomes the section landing page.

Changed: Documentation

Documented the behavior in README.md: added a bullet under the repository expectations and updated the Category.md annotation in the module source-structure tree.

Technical Details

  • .github/workflows/Build-Docs.yml: Document-PSModule@fb5d349 # v1.0.17 -> @349090c # v1.0.18.
  • main already carries v1.0.17 (install built modules at their real version) from #342; this PR advances the pin to v1.0.18, which delivers the section-index behavior.
  • The site config already enables navigation.indexes, so no mkdocs.yml change is required.

v6.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 21:04
0d7a2f0

🚀 [Feature]: Plan job decides version before build so tested artifact equals published artifact (#342)

The version a module ships with is now decided in a new Plan job that runs before the module is built. The same artifact then flows through tests and publish without being mutated, so the artifact you tested is the artifact that lands in the PowerShell Gallery and on the GitHub Release.

New: Plan job replaces Get-Settings

Get-Settings.yml is renamed to Plan.yml. The Plan job runs two steps in sequence and exposes everything downstream jobs need:

  1. Get-PSModuleSettings — loads .github/PSModule.yml and emits the resolved Settings JSON.
  2. Resolve-PSModuleVersion — reads settings + PR labels, queries existing releases and the PowerShell Gallery, and emits the next version.

Plan job output: a single Settings JSON. The resolved version is merged into it as Settings.Module.{Version, Prerelease, FullVersion, ReleaseType, CreateRelease}, so every downstream job receives one self-contained object with no separate version outputs.

flowchart LR
  A[Plan<br/>Get-PSModuleSettings + Resolve-PSModuleVersion] --> B[Build-Module<br/>stamps version into manifest]
  B --> C[Test-* / Coverage]
  C --> D[Publish-Module<br/>publishes artifact unchanged]

Changed: Build-Module now stamps the real version

Build-Module.yml reads the version from Settings.Module.Version / Settings.Module.Prerelease and passes them to Build-PSModule. The built manifest contains the version the module will ship with before any test runs. The 999.0.0 placeholder only appears when no version is provided (for example, direct callers that bypass the Plan job).

Changed: Publish-Module no longer calculates or mutates versions

Publish-Module.yml drops every input that used to drive version calculation:

  • AutoPatching
  • DatePrereleaseFormat
  • IgnoreLabels
  • IncrementalPrerelease
  • MajorLabels / MinorLabels / PatchLabels
  • ReleaseType
  • VersionPrefix

Publish-Module now reads the version straight from the manifest that arrived from Build-Module and pushes it to the PowerShell Gallery as-is. The PR-title / PR-body / heading inputs are unchanged. The job also gains permission to upload release assets so the zipped module can be attached to the GitHub Release.

Fixed: Tested artifact equals published artifact

The placeholder-then-rewrite flow is gone. The bytes tested in Test-Module / Test-ModuleLocal are the same bytes published to the PowerShell Gallery and attached to the GitHub Release.

Technical Details

  • Plan.yml (renamed from Get-Settings.yml): adds a Resolve-Version step (runs unconditionally — it computes the release decision, including ReleaseType) followed by an Enrich-Settings step that merges the resolved version into Settings.Module.*. Plan exposes a single Settings output on both the job and workflow_call.
  • workflow.yml: the Get-Settings job is renamed to Plan and every downstream needs: / with: reference is repointed. Downstream jobs consume the single needs.Plan.outputs.Settings object (version available under Settings.Module.*).
  • Build-Module.yml: reads Settings.Module.Version / Settings.Module.Prerelease and passes them as Version / Prerelease to Build-PSModule, falling back to the 999.0.0 placeholder when a direct caller bypasses Plan and omits Settings.Module.
  • Publish-Module.yml: removes the seven version-calculation inputs and grants contents: write permission for release-asset uploads.
  • README.md: replaces the Get-Settings section with a Plan section that documents both steps and the artifact-integrity guarantee, and notes the new release-asset upload.
  • PSModule/Build-PSModule pinned to @v5.0.0 (SHA 672aaa7a91a379c4c6cd14494d03ab5e87e13c52).
  • PSModule/Publish-PSModule pinned to @v3.0.0 (SHA 03c0f8b53d0367c85a0f121f98af9b40c817b0e3).

Companion repos

Repo Change Link
PSModule/Resolve-PSModuleVersion New action — v1.1.0 released https://github.com/PSModule/Resolve-PSModuleVersion/releases/tag/v1.1.0
PSModule/Build-PSModule Major — adds Version / Prerelease inputs — v5.0.0 released https://github.com/PSModule/Build-PSModule/releases/tag/v5.0.0
PSModule/Publish-PSModule Major / BREAKING — drops version calculation, publishes pre-stamped artifact, uploads module zip to release — v3.0.0 released https://github.com/PSModule/Publish-PSModule/releases/tag/v3.0.0

Implementation plan progress

  • Rename Get-Settings job to Plan and add Resolve-PSModuleVersion step
  • Expose a single Settings output (resolved version merged into Settings.Module.{Version, Prerelease, FullVersion, ReleaseType, CreateRelease})
  • Update Build-Module.yml to read the version from Settings.Module.* and stamp it
  • Strip version-calculation inputs from Publish-Module.yml
  • Update needs / with wiring across workflow.yml
  • Update README.md (Plan section + release-asset note)
  • Resolve-PSModuleVersion action implementation (v1.1.0 released)
  • Build-PSModule Version/Prerelease inputs (v5.0.0 released)
  • Publish-PSModule v3 release (v3.0.0 released — PSModule/Publish-PSModule#71)
  • Repin @main references in Build-Module.yml and Publish-Module.yml to released SHAs
  • End-to-end run on a labeled PR

Notes

  • The public input/output contract of workflow.yml is unchanged for external consumers — this is internal wiring.
  • #339 covered the same scope and was closed; this PR restarts the work cleanly off the current main.

v6.0.1

Choose a tag to compare

@github-actions github-actions released this 09 Jul 10:52
54e2677

Bump PSModule/GitHub-Script from 1.8.0 to 1.9.0 (#367)

Bumps PSModule/GitHub-Script from 1.8.0 to 1.9.0.

v6.0.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 02:18
364ddd6

🌟 [Major]: Fixed test secret inputs replaced by TestData (#365)

Module test jobs now use a TestData object for optional test secrets and non-secret variables. This replaces the previous fixed TEST_* workflow secret inputs: callers that used those inputs must pass the same names inside TestData, while callers that need different names can expose them without changing the shared workflow.

Breaking Changes

Removed: fixed TEST_* workflow secret inputs

The reusable workflow no longer declares or accepts these individual test-secret inputs:

  • TEST_APP_ENT_CLIENT_ID
  • TEST_APP_ENT_PRIVATE_KEY
  • TEST_APP_ORG_CLIENT_ID
  • TEST_APP_ORG_PRIVATE_KEY
  • TEST_USER_ORG_FG_PAT
  • TEST_USER_USER_FG_PAT
  • TEST_USER_PAT

Callers using any of these inputs must move them into the secrets map inside TestData. The names can stay the same, so existing Pester tests can continue reading the same environment variables.

jobs:
  Process-PSModule:
    uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5
    secrets:
      APIKey: ${{ secrets.APIKey }}
      TestData: >-
        {
          "secrets": {
            "TEST_USER_PAT": "${{ secrets.TEST_USER_PAT }}",
            "TEST_APP_ORG_CLIENT_ID": "${{ secrets.TEST_APP_ORG_CLIENT_ID }}"
          }
        }
$env:TEST_USER_PAT
$env:TEST_APP_ORG_CLIENT_ID

New: Caller-selected TestData

TestData is an optional single-line JSON object with secrets and variables maps. Entries from both maps become environment variables in BeforeAll-ModuleLocal, Test-ModuleLocal, and AfterAll-ModuleLocal.

TestData: >-
  {
    "secrets": {
      "CONFLUENCE_API_TOKEN": "${{ secrets.CONFLUENCE_API_TOKEN }}"
    },
    "variables": {
      "CONFLUENCE_SITE": ${{ toJSON(vars.CONFLUENCE_SITE) }},
      "CONFLUENCE_SPACE_KEY": ${{ toJSON(vars.CONFLUENCE_SPACE_KEY) }}
    }
  }
$env:CONFLUENCE_API_TOKEN # from the secrets map, masked in logs
$env:CONFLUENCE_SITE      # from the variables map, not masked

Values under secrets are masked before being exposed to the test jobs. Values under variables are exposed without masking for normal, non-sensitive configuration. Modules that do not need secrets or variables can omit TestData entirely.

Because the workflow does not use secrets: inherit, only the values explicitly listed in TestData are exposed to module tests.

New: Safe TestData validation and formatting

TestData must parse as a JSON object containing only optional secrets and variables maps. Values in those maps must be scalar values. Keys must be safe environment-variable names matching ^[A-Za-z_][A-Za-z0-9_]*$, must not be duplicated across secrets and variables, and must not override reserved variables such as PATH, CI, GITHUB_*, RUNNER_* or ACTIONS_*.

Pass TestData as a single-line value after YAML folding. The folded >- form is safe when the JSON is compact, or when every JSON content line stays at the same indentation level inside the block.

Avoid normal pretty-printed JSON with nested indentation inside >-. YAML preserves more-indented lines instead of folding them, so that shape can still produce a multi-line secret value. Keeping TestData single-line after folding avoids GitHub treating line fragments as separate mask values.

Use the direct quoted form for single-line secrets:

"CONFLUENCE_API_TOKEN": "${{ secrets.CONFLUENCE_API_TOKEN }}"

Using the JSON-encoded approach, github-advanced security will warn that all secrets available to the runner gets exposed.

"CONFLUENCE_API_TOKEN": ${{ toJSON(vars.CONFLUENCE_API_TOKEN) }}

Use toJSON(vars.NAME) for variables so normal configuration values are JSON-encoded safely:

"CONFLUENCE_SITE": ${{ toJSON(vars.CONFLUENCE_SITE) }}

For multi-line secrets, or secrets containing quotes or backslashes, base64-encode the secret and decode it in the test.

Technical Details

  • workflow.yml declares and forwards a single optional TestData secret instead of the seven fixed TEST_* secrets.
  • .github/scripts/Expose-TestData.ps1 contains the shared TestData parsing, validation, masking, and UTF-8 GITHUB_ENV export logic used by the ModuleLocal workflows.
  • BeforeAll-ModuleLocal.yml, Test-ModuleLocal.yml, and AfterAll-ModuleLocal.yml call the shared helper instead of duplicating the security-sensitive exposure logic.
  • Test-Module.yml no longer declares or exports the obsolete fixed TEST_* secrets.
  • The fixture-backed self-test workflows skip forked pull_request runs where repository secrets and variables are unavailable, while still running for trusted PRs, schedules, and manual dispatches.
  • The parse failure path uses stable messages that do not echo the raw secret payload, and secret keys are no longer printed in logs.
  • The self-test workflows pass dedicated fixture data through TestData, and the environment Pester tests assert the exact exposed secret and variable values.
  • README.md documents the breaking migration path, the TestData contract, safe YAML folding, safe key names, environment-scoped value usage, the difference between masked secrets and unmasked variables, and base64 guidance for complex secret values.
  • The diff also addresses #322's duplicated fixed-secret pass-through without adopting broad secrets: inherit.