Skip to content

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_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

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 as PATH, CI, GITHUB_*, RUNNER_* or ACTIONS_*.
  • The TestData validation, masking and environment export logic is shared by the ModuleLocal workflows through the PSModule/Install-PSModuleHelpers action, which installs the Import-TestData command each workflow runs to expose the values.
  • Reference secrets as "${{ secrets.<name> }}" (quoted, directly) rather than toJSON(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 TestData as 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 TestData with 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 TestData entirely when the module needs no secrets or variables. Include only the map you need (just secrets, just variables, or both).
  • Because secrets: inherit is 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 in TestData; 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.AutoCleanup is 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:

  1. A comment is automatically added to the PR listing the configured patterns and explaining why build/test stages are skipped
  2. Settings.Module.ReleaseType is set to None (and Settings.Module.CreateRelease is false)
  3. Build, test, and publish stages are skipped
  4. 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.