Usage#
To use the workflow, create a new file in the .github/workflows directory of the module repository and add the following content.
Workflow suggestion
name: Process-PSModule
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * *'
pull_request:
branches:
- main
types:
- closed
- opened
- reopened
- synchronize
- labeled
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
statuses: write
pages: write
id-token: write
jobs:
Process-PSModule:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5
secrets:
APIKey: ${{ secrets.APIKey }}
Inputs#
| Name | Type | Description | Required | Default |
|---|---|---|---|---|
SettingsPath |
string |
The path to the settings file. All workflow configuration is controlled through this settings file. | false |
.github/PSModule.yml |
Debug |
boolean |
Enable debug output. | false |
false |
Verbose |
boolean |
Enable verbose output. | false |
false |
Version |
string |
Specifies the version of the GitHub module to be installed. The value must be an exact version. | false |
'' |
Prerelease |
boolean |
Whether to use a prerelease version of the 'GitHub' module. | false |
false |
WorkingDirectory |
string |
The path to the root of the repo. | false |
'.' |
ImportantFilePatterns |
string |
Newline-separated list of regular expression patterns that identify important files. Changes matching these patterns trigger build, test, and publish stages. When set, fully replaces the defaults. | false |
^src/\n^README\.md$ |
Secrets#
The reusable workflow at .github/workflows/workflow.yml declares only two workflow-call secrets,
which keeps the calling workflow in full control of the credentials that are exposed.
secrets: inherit is intentionally not required.
| Name | Location | Description | Required |
|---|---|---|---|
APIKey |
GitHub secrets | The API key for the PowerShell Gallery, used to publish the module. | Yes |
TestData |
GitHub secrets | A single-line JSON object with secrets and variables maps, exposed as environment variables to the module test jobs. Values under secrets are masked; values under variables are not. |
No |
Breaking change: fixed test secrets moved to TestData#
The reusable workflow no longer declares or accepts the old fixed test-secret inputs:
TEST_APP_ENT_CLIENT_IDTEST_APP_ENT_PRIVATE_KEYTEST_APP_ORG_CLIENT_IDTEST_APP_ORG_PRIVATE_KEYTEST_USER_ORG_FG_PATTEST_USER_USER_FG_PATTEST_USER_PAT
If a caller passed any of these secrets directly, move them into the secrets map inside TestData.
The environment variable names used by the tests can stay the same; only the workflow-call interface
changes:
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 }}" } }
Passing test data (secrets and variables) to the tests#
A single TestData secret lets a module expose any number of caller-defined values to its test jobs
(BeforeAll-ModuleLocal, Test-ModuleLocal and AfterAll-ModuleLocal) without changing the shared
workflow. It is one JSON object with two maps, so everything the tests need is visible in one place:
{ "secrets": { "NAME": "value" }, "variables": { "NAME": "value" } }
Values under secrets are masked in the logs; values under variables are not. Build it in the
calling workflow and pass it through the secrets: block (so the whole blob is masked). Reference each
secret directly as "${{ secrets.<name> }}" and each variable as ${{ toJSON(vars.<name>) }}. A
folded >- scalar keeps the source readable while producing a single-line value, as long as the JSON
content lines stay at the same indentation level:
jobs:
Process-PSModule:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5
secrets:
APIKey: ${{ secrets.APIKey }}
TestData: >-
{ "secrets": { "CONFLUENCE_API_TOKEN": "${{ secrets.CONFLUENCE_API_TOKEN }}" },
"variables": { "CONFLUENCE_SITE": ${{ toJSON(vars.CONFLUENCE_SITE) }},
"CONFLUENCE_USERNAME": ${{ toJSON(vars.CONFLUENCE_USERNAME) }},
"CONFLUENCE_SPACE_KEY": ${{ toJSON(vars.CONFLUENCE_SPACE_KEY) }} } }
Each entry becomes an environment variable in the test jobs, so the module's Pester tests read the values directly:
$env:CONFLUENCE_API_TOKEN # from the "secrets" map (masked in logs)
$env:CONFLUENCE_SITE # from the "variables" map (not masked)
Notes:
- The names are caller-defined; no secret or variable names are hard-coded in the shared workflow.
Names must match
^[A-Za-z_][A-Za-z0-9_]*$and must not override reserved variables such asPATH,CI,GITHUB_*,RUNNER_*orACTIONS_*. - The
TestDatavalidation, masking and environment export logic is shared by the ModuleLocal workflows through thePSModule/Install-PSModuleHelpersaction, which installs theImport-TestDatacommand each workflow runs to expose the values. - Reference secrets as
"${{ secrets.<name> }}"(quoted, directly) rather thantoJSON(secrets.<name>). The direct form keeps CodeQL's excessive secrets exposure check happy and works for single-line secret values. It cannot carry values that contain",\or newlines, so base64-encode a multi-line or special-character secret and decode it in the test (for example[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($env:MY_KEY_B64))). - Variables use
toJSON(vars.<name>)so any characters are JSON-encoded safely; they are never masked. You can use the same quoted direct form as secrets ("${{ vars.<name> }}") only for simple values that do not contain",\or newlines. - Provide
TestDataas a single-line value (the folded>-block above does this). Avoid a literal|block: GitHub registers every line of a multi-line secret as its own mask, which over-masks unrelated log output. - Do not pretty-print
TestDatawith nested indentation. YAML preserves more-indented lines inside a folded scalar, so a fully formatted JSON object can still become a multi-line secret. That makes GitHub register each line as its own mask, including brace-only lines such as{,}or},, which can turn unrelated log output into***. Keep the compact form above, or keep every JSON content line at the same indentation level. - Omit
TestDataentirely when the module needs no secrets or variables. Include only the map you need (justsecrets, justvariables, or both). - Because
secrets: inheritis not used, only the values you list are ever exposed. - Organization, repository and GitHub Environment secrets and variables are supported when they are
visible to the calling job. For environment-scoped values, set
environment:on the calling job and explicitly include those values inTestData; they are not exposed automatically.
Permissions#
The following permissions are needed for the workflow to be able to perform all tasks.
permissions:
contents: write # to checkout the repo and create releases on the repo
pull-requests: write # to write comments to PRs
statuses: write # to update the status of the workflow from linter
pages: write # to deploy to Pages
id-token: write # to verify the Pages deployment originates from an appropriate source
For more info, see Deploy GitHub Pages site.
Scenario Matrix#
This table shows when each job runs based on the trigger scenario:
| Job | Open/Updated PR | Merged PR | Abandoned PR | Manual Run |
|---|---|---|---|---|
| Plan | ✅ Always | ✅ Always | ✅ Always | ✅ Always |
| Lint-Repository | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Build-Module | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Build-Docs | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Build-Site | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Test-SourceCode | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Lint-SourceCode | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Test-Module | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| BeforeAll-ModuleLocal | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Test-ModuleLocal | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| AfterAll-ModuleLocal | ✅ Yes | ✅ Yes | ✅ Yes* | ✅ Yes |
| Get-TestResults | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Get-CodeCoverage | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Publish-Site | ❌ No | ✅ Yes | ❌ No | ❌ No |
| Publish-Module | ✅ Yes** | ✅ Yes** | ✅ Yes*** | ✅ Yes** |
- * Runs for cleanup if tests were started
- ** Only when all tests/coverage/build succeed
- *** Cleans up prerelease versions and tags created for the abandoned PR (when
Publish.Module.AutoCleanupis enabled)
Important file change detection#
The workflow automatically detects whether a pull request contains changes to "important" files that warrant a new release. This prevents unnecessary releases when only non-functional files (such as workflow configurations, linter settings, or test files) are modified.
Files that trigger releases#
By default, the following regular expression patterns identify important files:
| Pattern | Description |
|---|---|
^src/ |
Module source code |
^README\.md$ |
Module documentation |
Customizing important file patterns#
To override the default patterns, set ImportantFilePatterns in your settings file (.github/PSModule.yml):
ImportantFilePatterns:
- '^src/'
- '^README\.md$'
- '^examples/'
When configured, the provided list fully replaces the defaults. Include the default patterns in your list if you still want them to trigger releases.
To disable file-change triggering entirely (so that no file changes ever trigger a release), set an empty list in the settings file:
ImportantFilePatterns: []
You can also pass patterns via the workflow input:
jobs:
Process:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5
with:
ImportantFilePatterns: |
^src/
^README\.md$
^examples/
To disable triggering via the workflow input, pass an explicit empty string:
jobs:
process:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5
with:
ImportantFilePatterns: ''
Note that omitting the ImportantFilePatterns key entirely causes the workflow's default patterns (^src/ and
^README\.md$) to be used. The settings file takes priority over the workflow input, so set
ImportantFilePatterns: [] in .github/PSModule.yml to disable triggering regardless of the workflow input.
Resolution order: settings file → workflow input → workflow input default values.
Behavior when no important files are changed#
When a pull request does not contain changes to important files:
- A comment is automatically added to the PR listing the configured patterns and explaining why build/test stages are skipped
Settings.Module.ReleaseTypeis set toNone(andSettings.Module.CreateReleaseisfalse)- Build, test, and publish stages are skipped
- The PR can still be merged for non-release changes (documentation updates, CI improvements, etc.)
This behavior ensures that maintenance PRs (such as updating GitHub Actions versions or fixing typos in comments) don't create unnecessary releases in the PowerShell Gallery.