Process-PSModule is a reusable workflow product that provides an opinionated flow and structure for building PowerShell modules using GitHub Actions. It is NOT a library or toolkit; it is a complete CI/CD workflow framework designed to be consumed by PowerShell module repositories.
- Opinionated Architecture: Defines a specific workflow execution order and module structure
- Reusable Workflows: Consuming repositories call Process-PSModule workflows via
uses:syntax - Configurable via Settings: Behavior customized through
.github/PSModule.yml(or JSON/PSD1) in consuming repos - Structure Requirements: Consuming repos MUST follow documented structure in GitHub Actions README files
- Not for Local Development: Designed exclusively for GitHub Actions execution environment
Repositories that consume Process-PSModule workflows MUST:
- Follow the module source structure documented in framework actions (see Required Module Structure below)
- Provide configuration file (
.github/PSModule.yml) with appropriate settings - Adhere to the opinionated workflow execution order
- Reference Process-PSModule workflows using stable version tags (e.g.,
@v4) - Review action README documentation for structure and configuration requirements
- Use the Template-PSModule repository as a starting point
Process-PSModule enforces an opinionated module structure. Consuming repositories MUST organize their PowerShell module following this complete structure:
<ModuleName>/ # Repository root
├── .github/ # GitHub Actions configuration
│ ├── linters/ # Linter configuration files
│ │ ├── .jscpd.json # Copy/paste detector settings
│ │ ├── .markdown-lint.yml # Markdown linter settings
│ │ ├── .powershell-psscriptanalyzer.psd1 # PSScriptAnalyzer rules
│ │ └── .textlintrc # Text linter settings
│ ├── workflows/ # GitHub Actions workflows
│ │ ├── Linter.yml # Linting workflow (optional)
│ │ ├── Nightly-Run.yml # Scheduled validation (optional)
│ │ └── Process-PSModule.yml # Main workflow (REQUIRED)
│ ├── CODEOWNERS # Code ownership and review assignments
│ ├── dependabot.yml # Dependency update automation
│ ├── mkdocs.yml # Material for MkDocs site configuration
│ ├── PSModule.yml # Process-PSModule settings (YAML/JSON/PSD1 supported)
│ └── release.yml # GitHub release configuration
├── examples/ # Usage examples (optional)
│ └── General.ps1 # Example script
├── icon/ # Module icon (optional, referenced in manifest)
│ └── icon.png # PNG icon file
├── src/ # MODULE SOURCE CODE (REQUIRED)
│ ├── assemblies/ # .NET assemblies (.dll) to load
│ ├── classes/ # PowerShell classes and enums
│ │ ├── private/ # Private (not exported)
│ │ └── public/ # Public (exported)
│ ├── data/ # Configuration data files loaded as private variables (.psd1)
│ │ ├── Config.psd1 # Example configuration file
│ │ └── Settings.psd1 # Example settings file
│ ├── formats/ # Format definition files (.ps1xml)
│ ├── functions/ # The functions for the PowerShell module
│ │ ├── private/ # Private (not exported)
│ │ └── public/ # Public (exported)
│ │ └── <SubFolder>/ # Optional: Group functions by category
│ │ ├── <SubFolder>.md # Optional: Category documentation
│ │ └── Get-*.ps1 # Example: Get commands
│ ├── init/ # Initialization scripts
│ │ └── initializer.ps1 # Example initialization script
│ ├── modules/ # Nested PowerShell modules (.psm1)
│ ├── scripts/ # Script files that are loaded to the users runtime on import (.ps1)
│ │ └── loader.ps1 # Example script file
│ ├── types/ # Type definition files (.ps1xml)
│ ├── variables/ # Variables for the PowerShell module
│ │ ├── private/ # Private (not exported)
│ │ │ └── PrivateVariables.ps1
│ │ └── public/ # Public (exported)
│ │ ├── Moons.ps1
│ │ ├── Planets.ps1
│ │ └── SolarSystems.ps1
│ ├── finally.ps1 # Code executed at module load end (optional)
│ ├── header.ps1 # Code executed at module load start (optional)
│ ├── manifest.psd1 # PowerShell module manifest (optional, auto-generated if missing)
│ └── README.md # Documentation reference (points to Build-PSModule)
├── tests/ # Module tests (REQUIRED for Test-ModuleLocal)
│ ├── Environments/ # Optional: Test environment configurations
│ │ └── Environment.Tests.ps1
│ ├── MyTests/ # Optional: Additional test suites
│ │ └── <ModuleName>.Tests.ps1
│ ├── AfterAll.ps1 # Teardown script (optional, runs once after all test matrix jobs)
│ ├── BeforeAll.ps1 # Setup script (optional, runs once before all test matrix jobs)
│ ├── <ModuleName>.Tests.ps1 # Module functional tests (Pester)
│ └── Environment.Tests.ps1 # Environment validation tests (optional)
├── .gitattributes # Git line ending configuration
├── .gitignore # Git ignore patterns
├── LICENSE # License file (referenced in manifest)
└── README.md # Module documentation and usage
The src/ folder contains the module source code that Build-PSModule compiles into a production-ready module:
Required Files/Folders:
- At least one
.ps1file infunctions/public/to export functionality tests/folder at repository root with at least one Pester test file
Optional Configuration Files:
manifest.psd1- PowerShell module manifest (auto-generated if missing with GitHub metadata)header.ps1- Code executed at module load start (before any other code)finally.ps1- Code executed at module load end (after all other code)README.md- Documentation pointer (typically references Build-PSModule for structure)
Source Folders (all optional, include only what your module needs):
assemblies/- .NET assemblies (.dll) loaded into module sessionclasses/private/- Private PowerShell classes (not exported)classes/public/- Public PowerShell classes (exported via TypeAccelerators)data/- Configuration data files (.psd1) loaded as module variablesformats/- Format definition files (.ps1xml) for object displayfunctions/private/- Private functions (internal implementation)- Supports subdirectories for grouping (e.g.,
functions/public/ComponentA/,functions/public/ComponentB/)
- Supports subdirectories for grouping (e.g.,
functions/public/- Public functions (exported to module consumers)- Supports subdirectories for grouping (e.g.,
functions/public/ComponentA/,functions/public/ComponentB/) - Optional category documentation files (e.g.,
functions/public/PSModule/PSModule.md)
- Supports subdirectories for grouping (e.g.,
init/- Initialization scripts (executed first during module load)modules/- Nested PowerShell modules (.psm1) or additional assembliesscripts/- Script files (.ps1) to process in caller's scopetypes/- Type definition files (.ps1xml) for custom type extensionsvariables/private/- Private variables (module scope only)variables/public/- Public variables (exported to module consumers)
Build Processing:
- Build-PSModule compiles
src/into a single root module file (<ModuleName>.psm1) - Source folders are removed from output after processing (only compiled module remains)
- Files processed in alphabetical order within each folder
- See "Build Process Requirements" section for detailed compilation flow
GitHub Actions Configuration (.github/ folder):
PSModule.yml(or.json/.psd1) - REQUIRED configuration file controlling Process-PSModule behaviorworkflows/Process-PSModule.yml- REQUIRED workflow file calling reusable Process-PSModule workflowmkdocs.yml- Material for MkDocs configuration for GitHub Pages documentationlinters/- Linter configuration files (optional, uses framework defaults if missing)- Other workflows (Linter, Nightly-Run) are optional supplementary workflows
Documentation Assets:
LICENSE- Referenced in module manifestLicenseUripropertyicon/icon.png- Referenced in module manifestIconUriproperty (public URL)README.md- Project documentation, referenced in GitHub repository metadataexamples/- Usage examples for module consumers
Testing Requirements:
tests/folder at repository root (NOT insidesrc/)- Pester test files (
.Tests.ps1) for module validation - Optional
BeforeAll.ps1andAfterAll.ps1scripts for external test resource management (see below) - Tests executed by Test-ModuleLocal workflow across all platforms
- See "Workflow Execution Order" section for BeforeAll/AfterAll/Test workflow sequence
BeforeAll/AfterAll Test Scripts (Optional):
Process-PSModule supports optional test setup and teardown scripts that execute once per workflow run (not per platform):
-
tests/BeforeAll.ps1- Runs once before all Test-ModuleLocal matrix jobs- Purpose: Setup external test resources independent of test platform/OS
- Intended Use: Deploy cloud infrastructure via APIs, create external database instances, initialize test data in third-party services
- NOT Intended For: OS-specific dependencies, platform-specific test files, test-specific resources for individual matrix combinations
- Execution: Runs in
tests/directory on ubuntu-latest with full access to environment secrets - Error Handling: Script failures halt the testing workflow (setup must succeed)
- Example Use Cases: Deploy Azure/AWS resources via APIs, create external PostgreSQL databases, initialize SaaS test accounts
-
tests/AfterAll.ps1- Runs once after all Test-ModuleLocal matrix jobs complete- Purpose: Cleanup external test resources independent of test platform/OS
- Intended Use: Remove cloud infrastructure via APIs, delete external database instances, cleanup test data in third-party services
- NOT Intended For: OS-specific cleanup, platform-specific file removal, test-specific cleanup for individual matrix combinations
- Execution: Runs in
tests/directory on ubuntu-latest with full access to environment secrets - Error Handling: Script failures logged as warnings but don't halt workflow (cleanup is best-effort)
- Always Executes: Runs even if tests fail (via
if: always()condition) - Example Use Cases: Delete Azure/AWS resources via APIs, remove external databases, cleanup SaaS test accounts
Key Distinction: BeforeAll/AfterAll are for managing external resources via APIs that exist outside GitHub Actions execution environment. Test-specific resources for individual OS/platform combinations should be created within the tests themselves using Pester BeforeAll/AfterAll blocks.
Key Points:
- Private vs Public:
private/folders contain internal implementations;public/folders contain exported elements - Optional Components: Not all folders are required; include only what your module needs
- Function Organization: Functions can be organized in subdirectories with optional category documentation
- Manifest Generation: If
manifest.psd1is not provided, Build-PSModule auto-generates with GitHub metadata - Minimal Structure: At minimum, provide
src/functions/public/<Function>.ps1andtests/<ModuleName>.Tests.ps1 - Template Reference: Use Template-PSModule as starting point
Documentation References:
- Build-PSModule README - Complete build process details
- Template-PSModule - Reference implementation
- Process-PSModule Configuration - Settings file documentation
Consuming repositories MUST create a workflow file (e.g., .github/workflows/Process-PSModule.yml) that calls the reusable Process-PSModule workflow:
name: Process-PSModule
on:
pull_request:
branches: [main]
types: [closed, opened, reopened, synchronize, labeled]
jobs:
Process-PSModule:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v4
secrets:
APIKEY: ${{ secrets.APIKEY }} # PowerShell Gallery API keyConfiguration Requirements:
- Configuration file at
.github/PSModule.yml(YAML, JSON, or PSD1 format supported) - Reference: Process-PSModule Configuration Documentation
- Use Template-PSModule as starting point: Template-PSModule
Process-PSModule uses the Build-PSModule action to compile module source code into a production-ready PowerShell module. The build process is automated and opinionated, following a specific execution flow:
- Execute Custom Build Scripts (Optional)
- Build-PSModule searches for
*build.ps1files anywhere in the repository - Scripts are executed in alphabetical order by filename (path-independent)
- Allows custom pre-build logic (e.g., code generation, asset processing, configuration setup)
- Example:
1-build.ps1runs before2-build.ps1regardless of directory location - Custom scripts can modify source files before the build process continues
- Build-PSModule searches for
- Copy Source Code
- All files from
src/folder are copied to the output folder - Existing root module file (
<ModuleName>.psm1) is excluded (recreated in step 4) - Creates a clean build environment for compilation
- All files from
- Build Module Manifest (
<ModuleName>.psd1)- Searches for existing
manifest.psd1or<ModuleName>.psd1in source - If found, uses it as base (preserving specified properties)
- If not found, creates new manifest from scratch
- Automatically Derived Properties:
RootModule: Set to<ModuleName>.psm1ModuleVersion: Temporary value (999.0.0) - updated by Publish-PSModule during releaseAuthor: GitHub repository owner (or preserved from source manifest)CompanyName: GitHub repository owner (or preserved from source manifest)Copyright: Generated as(c) YYYY <Owner>. All rights reserved.(or preserved from source manifest)Description: GitHub repository description (or preserved from source manifest)GUID: New GUID generated by New-ModuleManifestFileList: All files in the module output folderRequiredAssemblies: All*.dllfiles fromassemblies/andmodules/(depth = 1)NestedModules: All*.psm1,*.ps1,*.dllfiles frommodules/(one level down)ScriptsToProcess: All*.ps1files fromscripts/folder (loaded into caller session)TypesToProcess: All*.Types.ps1xmlfiles (searched recursively)FormatsToProcess: All*.Format.ps1xmlfiles (searched recursively)DscResourcesToExport: All*.psm1files fromresources/folderFunctionsToExport: All functions fromfunctions/public/(determined by AST parsing)CmdletsToExport: Empty array (or preserved from source manifest)VariablesToExport: All variables fromvariables/public/(determined by AST parsing)AliasesToExport: All aliases fromfunctions/public/(determined by AST parsing)ModuleList: All*.psm1files in source folder (excluding root module)RequiredModules: Gathered from#Requires -Modulesstatements in source filesPowerShellVersion: Gathered from#Requires -Versionstatements in source filesCompatiblePSEditions: Gathered from#Requires -PSEditionstatements (defaults to@('Core','Desktop'))Tags: GitHub repository topics plus compatibility tags from source filesLicenseUri: Public URL toLICENSEfile (or preserved from source manifest)ProjectUri: GitHub repository URL (or preserved from source manifest)IconUri: Public URL toicon/icon.png(or preserved from source manifest)
- Preserved from Source Manifest (if provided):
PowerShellHostName,PowerShellHostVersion,DotNetFrameworkVersion,ClrVersion,ProcessorArchitectureRequireLicenseAcceptance(defaults tofalseif not specified)ExternalModuleDependencies,HelpInfoURI,DefaultCommandPrefixReleaseNotes(not automated - can be set via PR/release description)Prerelease(managed by Publish-PSModule during release)
- Searches for existing
- Build Root Module (
<ModuleName>.psm1)- Creates new root module file (ignoring any existing
.psm1in source) - Compilation Order:
- Module Header:
- Adds content from
header.ps1if exists (then removes file) - If no
header.ps1, adds default[CmdletBinding()]parameter block - Adds PSScriptAnalyzer suppression for cross-platform compatibility
- Adds content from
- Post-Header Initialization:
- Loads module manifest information (
$script:PSModuleInfo) - Adds platform detection (
$IsWindowsfor PS 5.1 compatibility)
- Loads module manifest information (
- Data Loader (if
data/folder exists):- Recursively imports all
*.psd1files fromdata/folder - Creates module-scoped variables:
$script:<filename>for each data file - Example:
data/Config.psd1becomes$script:Config
- Recursively imports all
- Source File Integration (in this specific order):
- Processes each folder alphabetically within the folder, files on root first, then subfolders
- Files are wrapped with debug logging regions
- After processing, source folders are removed from output:
init/- Initialization scripts (executed first during module load)classes/private/- Private PowerShell classes (not exported)classes/public/- Public PowerShell classes (exported via TypeAccelerators)functions/private/- Private functions (not exported)functions/public/- Public functions (exported to module consumers)variables/private/- Private variables (not exported)variables/public/- Public variables (exported to module consumers)- Any remaining
*.ps1files on module root
- Class and Enum Exporter (if
classes/public/exists):- Uses
System.Management.Automation.TypeAcceleratorsfor type registration - Exports enums from
classes/public/as type accelerators - Exports classes from
classes/public/as type accelerators - Adds
OnRemovehandler to clean up type accelerators when module is removed - Provides Write-Verbose output for each exported type
- Uses
- Export-ModuleMember:
- Adds
Export-ModuleMembercall with explicit lists - Only exports items from
public/folders:- Functions: From
functions/public/ - Cmdlets: From manifest (usually empty for script modules)
- Variables: From
variables/public/ - Aliases: From functions in
functions/public/
- Functions: From
- Adds
- Format with PSScriptAnalyzer:
- Entire root module content is formatted using
Invoke-Formatter - Uses PSScriptAnalyzer settings from
Build/PSScriptAnalyzer.Tests.psd1 - Ensures consistent code style and UTF-8 BOM encoding
- Entire root module content is formatted using
- Module Header:
- Creates new root module file (ignoring any existing
- Update Manifest Aliases
- Re-analyzes root module to extract actual aliases defined
- Updates
AliasesToExportin manifest with discovered aliases
- Upload Module Artifact
- Built module is packaged and uploaded as workflow artifact
- Artifact name defaults to
module(configurable via action input) - Available for subsequent workflow steps (testing, publishing)
- No Manual Root Module: Any existing
.psm1file insrc/is ignored and replaced - Source Folder Removal: Processed source folders are removed from output (only compiled root module remains)
- Alphabetical Processing: Files within each folder are processed alphabetically
- Manifest Precedence: Source manifest values take precedence over generated values
- UTF-8 BOM Encoding: Final root module uses UTF-8 with BOM encoding
- PowerShell 7.4+ Target: Build process and generated code target PowerShell 7.4+
- Build-PSModule Action
- PowerShell Gallery Publishing Guidelines
- PowerShell Module Manifests
- PowerShell Module Authoring
Every feature MUST be implemented as a reusable GitHub Actions workflow component. This is NOT a local development framework; it is designed for CI/CD execution. Workflows MUST:
- Be composable and callable from other workflows using
uses:syntax - Use clearly defined inputs and outputs with proper documentation
- Follow the single responsibility principle (one workflow = one concern)
- Support matrix strategies for parallel execution where appropriate
- Be independently testable via the CI validation workflow (
.github/workflows/ci.yml) - Delegate logic to reusable GitHub Actions (from github.com/PSModule organization)
- Avoid inline PowerShell code in workflow YAML; use action-based script files instead
- Reference actions by specific versions or tags for stability
Rationale: Reusable workflow architecture enables maintainability, reduces duplication, and allows consuming repositories to leverage Process-PSModule capabilities consistently. Action-based scripts provide better testability, reusability, and version control than inline code.
All code changes MUST follow strict TDD practices using Pester and PSScriptAnalyzer:
- Tests MUST be written before implementation
- Tests MUST fail initially (Red phase)
- Implementation proceeds only after failing tests exist (Green phase)
- Code MUST be refactored while maintaining passing tests (Refactor phase)
- PSScriptAnalyzer rules MUST pass for all PowerShell code
- Manual testing procedures MUST be documented when automated testing is insufficient
- Workflow functionality MUST be validated through CI workflow tests (
.github/workflows/ci.yml) - Consuming module repositories SHOULD use CI workflow for nightly validation
Rationale: TDD ensures code quality, prevents regressions, and creates living documentation through tests. This is fundamental to project reliability. CI workflow validation ensures the entire framework functions correctly in real-world scenarios.
Modules MUST be built to be cross-platform. All workflows, features, and consuming modules MUST support cross-platform execution (Linux, macOS, Windows) using PowerShell 7.4 or newer:
- Use platform-agnostic PowerShell Core 7.4+ constructs exclusively
- Modules MUST function identically on Linux, macOS, and Windows
- Cross-platform compatibility is verified through Test-ModuleLocal workflow
- Test-ModuleLocal executes module tests on:
ubuntu-latest,windows-latest,macos-latest - BeforeAll/AfterAll scripts execute on
ubuntu-latestonly (external resource setup) - Implement matrix testing across all supported operating systems for all workflow components
- Document any platform-specific behaviors or limitations explicitly
- Test failures on any platform MUST block merging
- Provide skip mechanisms for platform-specific tests when justified (with clear rationale)
- No backward compatibility required for Windows PowerShell 5.1 or earlier PowerShell Core versions
Rationale: PowerShell 7.4+ provides consistent cross-platform behavior and modern language features. Focusing on a single modern version reduces complexity and maintenance burden. Modules built with Process-PSModule framework must work seamlessly across all platforms, verified through automated matrix testing in Test-ModuleLocal, ensuring maximum compatibility for consuming projects on contemporary platforms.
Every workflow execution MUST produce verifiable quality metrics:
- Test results MUST be captured in structured formats (JSON reports)
- Code coverage MUST be measured and reported
- Linting results MUST be captured and enforced
- All quality gates MUST fail the workflow if thresholds are not met
- Workflow steps MUST produce clear, actionable error messages
- Debug mode MUST be available for troubleshooting
Rationale: Measurable quality gates prevent degradation over time and provide clear feedback. Observability enables rapid debugging and continuous improvement.
Release management MUST be automated and follow SemVer 2.0.0:
- Version bumps MUST be determined by PR labels (major, minor, patch)
- Releases MUST be automated on merge to main branch
- PowerShell Gallery publishing MUST be automatic for labeled releases
- GitHub releases MUST be created with complete changelogs
- Prerelease versioning MUST support incremental or date-based formats
- Documentation MUST be published to GitHub Pages automatically
Rationale: Automated releases reduce human error, ensure consistency, and enable rapid iteration while maintaining clear version semantics.
Process-PSModule implements an automated publishing workflow triggered by pull request events. The workflow behavior is controlled by PR labels following Semantic Versioning (SemVer 2.0.0) conventions.
Pull requests MUST use labels to determine release behavior:
-
major- Breaking changes, incompatible API changes- Increments major version:
1.2.3→2.0.0 - Resets minor and patch to zero
- Increments major version:
-
minor- New features, backward-compatible functionality additions- Increments minor version:
1.2.3→1.3.0 - Resets patch to zero
- Increments minor version:
-
patch- bugfixes, backward-compatible patches (default)- Increments patch version:
1.2.3→1.2.4 - Applied by default when no version label specified (if AutoPatching enabled)
- Increments patch version:
-
prerelease- Creates prerelease version (unmerged PR publishing)- Publishes module to PowerShell Gallery with prerelease tag
- Creates GitHub Release marked as prerelease
- Prerelease tag format:
<version>-<branchname><increment> - Example:
1.2.4-featureauth001for branchfeature/auth - Only applies to unmerged PRs (opened, reopened, synchronized, labeled)
- When merged to main, normal release takes precedence (prerelease label ignored)
-
NoRelease- Skips all publishing- Workflow runs build and test jobs
- Publishing jobs (Publish-Module, Publish-Site) are skipped
- Used for documentation-only changes or work-in-progress validation
The Process-PSModule workflow uses dynamic conditions to determine job execution:
- Get-Settings - Configuration loading
- Build-Module - Module compilation
- Build-Docs - Documentation generation
- Build-Site - Static site generation
- Test-SourceCode - Source code validation
- Lint-SourceCode - Code quality checks
- Test-Module - Built module validation
- Test-ModuleLocal - Pester tests across platforms
- Get-TestResults - Test aggregation
- Get-CodeCoverage - Coverage analysis
Publish-Site (GitHub Pages deployment):
- Executes when: PR is merged to default branch AND tests pass
- Skipped when: PR is open/synchronized OR not merged OR scheduled run OR manual trigger
- Condition:
github.event_name == 'pull_request' AND github.event.pull_request.merged == true
Publish-Module (PowerShell Gallery publishing):
- Executes when:
- PR is merged to default branch AND tests pass (normal release), OR
- PR has
prereleaselabel AND PR is not merged AND tests pass (prerelease)
- Skipped when:
- PR has
NoReleaselabel, OR - Scheduled run (cron trigger), OR
- Manual run (workflow_dispatch), OR
- Tests fail
- PR has
- Condition:
(github.event_name == 'pull_request' AND github.event.pull_request.merged == true) OR (labels contains 'prerelease' AND NOT merged)
| PR State | Labels | Build/Test | Publish-Module | Publish-Site | Version |
|---|---|---|---|---|---|
| Opened | minor |
✅ Yes | ❌ No | ❌ No | N/A (not published) |
| Opened | prerelease |
✅ Yes | ✅ Yes (prerelease) | ❌ No | 1.3.0-branchname001 |
| Opened | prerelease, minor |
✅ Yes | ✅ Yes (prerelease) | ❌ No | 1.3.0-branchname001 |
| Synchronized | major |
✅ Yes | ❌ No | ❌ No | N/A (not published) |
| Synchronized | prerelease |
✅ Yes | ✅ Yes (prerelease) | ❌ No | 1.3.0-branchname002 |
| Merged | minor |
✅ Yes | ✅ Yes (normal) | ✅ Yes | 1.3.0 |
| Merged | major |
✅ Yes | ✅ Yes (normal) | ✅ Yes | 2.0.0 |
| Merged | patch |
✅ Yes | ✅ Yes (normal) | ✅ Yes | 1.2.4 |
| Merged | (no label) | ✅ Yes | ✅ Yes (if AutoPatching) | ✅ Yes | 1.2.4 (patch) |
| Merged | NoRelease |
✅ Yes | ❌ No | ❌ No | N/A (skipped) |
| Merged | prerelease, minor |
✅ Yes | ✅ Yes (normal) | ✅ Yes | 1.3.0 (prerelease ignored) |
| Scheduled (cron) | N/A | ✅ Yes | ❌ No | ❌ No | N/A (validation only) |
| Manual (workflow_dispatch) | N/A | ✅ Yes | ❌ No | ❌ No | N/A (validation only) |
The Publish-PSModule action determines the new version using this process:
- Get Latest Version
- Query PowerShell Gallery for latest published version
- Query GitHub Releases for latest release version
- Use the higher of the two as base version
- Determine Version Increment
- Check PR labels for
major,minor, orpatch - If no version label and AutoPatching enabled, default to
patch - If no version label and AutoPatching disabled, skip publishing
- Check PR labels for
- Calculate New Version
- Apply SemVer increment based on label
- Major:
1.2.3→2.0.0 - Minor:
1.2.3→1.3.0 - Patch:
1.2.3→1.2.4
- Add Prerelease Tag (if
prereleaselabel present on unmerged PR)- Extract branch name, sanitize to alphanumeric only
- Query existing prerelease versions with same branch name
- Increment prerelease counter
- Format:
<version>-<branchname><increment> - Example:
1.3.0-featureauth001,1.3.0-featureauth002
- Publish to PowerShell Gallery
- Upload module with calculated version
- Set prerelease flag if prerelease tag present
- Validate publication success
- Create GitHub Release
- Generate release notes from PR description and commits
- Create release with version tag (e.g.,
v1.3.0orv1.3.0-featureauth001) - Mark as prerelease if prerelease tag present
- Attach module artifact
Repositories can configure publishing behavior in .github/PSModule.yml:
Publish:
Module:
AutoPatching: true # Auto-apply patch when no label
AutoCleanup: false # Remove old prereleases
IncrementalPrerelease: true # Use incremental prerelease counter
DatePrereleaseFormat: '' # Alternative: date-based prerelease (yyyyMMddHHmmss)
VersionPrefix: 'v' # Prefix for git tags (e.g., v1.2.3)
MajorLabels: ['major', 'breaking'] # Labels that trigger major bump
MinorLabels: ['minor', 'feature'] # Labels that trigger minor bump
PatchLabels: ['patch', 'fix', 'bug'] # Labels that trigger patch bump
IgnoreLabels: ['NoRelease', 'skip'] # Labels that skip publishingConsuming repositories MUST configure their workflow file to trigger on appropriate PR events:
name: Process-PSModule
on:
pull_request:
branches: [main]
types:
- closed # Detect merged PRs
- opened # Initial PR creation
- reopened # Reopened PR
- synchronize # New commits pushed
- labeled # Label added/changed
jobs:
Process-PSModule:
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v4
secrets:
APIKEY: ${{ secrets.APIKEY }} # Required for publishingKey Points:
closedevent withgithub.event.pull_request.merged == truetriggers normal releaseslabeledevent allows immediate prerelease publishing whenprereleaselabel addedsynchronizeevent withprereleaselabel publishes new prerelease on each push- Secrets MUST include
APIKEYfor PowerShell Gallery publishing (optional for CI-only runs)
- API Key Required: PowerShell Gallery publishing requires valid API key in secrets
- Test Pass Requirement: Publishing jobs only execute if all tests pass
- Branch Protection: Recommended to protect main branch and require PR reviews
- Label Discipline: Teams MUST follow label conventions for predictable versioning
- Prerelease Cleanup: Consider enabling AutoCleanup to remove old prerelease versions
- Version Conflicts: Publishing fails if version already exists in PowerShell Gallery
- Incremental Prereleases: Each push to prerelease PR increments counter (001, 002, 003...)
- Branch Name Sanitization: Prerelease tags use alphanumeric-only branch names
- PowerShell Version: 7.4 or newer (no backward compatibility with 5.1 or older Core versions)
- Execution Environment: GitHub Actions runners (not designed for local development)
- Code Organization: Action-based scripts preferred over inline workflow code
- Action References: Use PSModule organization actions (github.com/PSModule) with version tags
- Workflow Structure: Reusable workflows in
.github/workflows/usingworkflow_calltrigger
- All PowerShell code MUST pass PSScriptAnalyzer with project-defined rules
- Source code structure MUST follow PSModule framework conventions
- Code coverage target MUST be configurable per repository (default 0% for flexibility)
- All workflow YAML MUST be valid and pass linting
- Action scripts MUST be testable and maintainable
- Inline code in workflows SHOULD be avoided; extract to action scripts
- README MUST provide clear setup instructions and workflow usage examples
- All workflows MUST include descriptive comments explaining inputs, outputs, and purpose
- Changes MUST update relevant documentation in the same PR
- GitHub Pages documentation MUST be generated automatically using Material for MkDocs
- Source code tests MUST validate framework compliance
- Module tests MUST validate built module integrity
- Local module tests (Pester) MUST validate functional behavior across all platforms
- Test-ModuleLocal workflow verifies cross-platform module compatibility on:
ubuntu-latest(Linux)windows-latest(Windows)macos-latest(macOS)
- BeforeAll/AfterAll setup and teardown scripts MUST be supported for test environments
- Test matrices MUST be configurable via repository settings
- CI validation workflow (
.github/workflows/ci.yml) MUST be maintained for integration testing - Production workflow (
.github/workflows/workflow.yml) is the primary consumer-facing workflow - Consuming repositories SHOULD use CI workflow for nightly regression testing
- Follow GitHub Flow: feature branches → PR → main
- PR MUST be opened for all changes
- CI workflows MUST execute on PR synchronize, open, reopen, label events
- PR labels determine release behavior:
major,minor,patch,prerelease,NoRelease prereleaselabel enables publishing of prerelease versions from unmerged PRs- Merged PRs trigger normal releases (major/minor/patch based on labels)
- Unmerged PRs with
prereleaselabel trigger prerelease publishing with incremental tags NoReleaselabel skips publishing but runs all build and test jobs- AutoPatching (if enabled) applies patch increment when no version label present
- Prerelease tags format:
<version>-<branchname><increment>(e.g.,1.3.0-featureauth001) - Version labels follow SemVer:
major(breaking),minor(features),patch(fixes) - See "Pull Request Workflow and Publishing Process" section for detailed behavior
The standard execution order for Process-PSModule workflows MUST be:
- Get-Settings - Reads configuration and prepares test matrices
- Build-Module - Compiles source into module
- Test-SourceCode - Parallel matrix testing of source code standards
- Lint-SourceCode - Parallel matrix linting of source code
- Test-Module - Framework validation and linting of built module
- BeforeAll-ModuleLocal - Optional: Execute tests/BeforeAll.ps1 setup script once before all test matrix jobs
- Runs on ubuntu-latest only (external resource setup via APIs)
- Script failures halt workflow execution
- Skipped if tests/BeforeAll.ps1 does not exist
- Test-ModuleLocal - Runs Pester tests across platform matrix (ubuntu-latest, windows-latest, macos-latest)
- Verifies cross-platform module compatibility
- Tests module functionality across all supported platforms
- Depends on BeforeAll-ModuleLocal (waits for external resource setup)
- AfterAll-ModuleLocal - Optional: Execute tests/AfterAll.ps1 teardown script once after all test matrix jobs
- Always runs even if tests fail (via
if: always()condition) - Runs on ubuntu-latest only (external resource cleanup via APIs)
- Script failures logged as warnings but don't halt workflow
- Skipped if tests/AfterAll.ps1 does not exist
- Always runs even if tests fail (via
- Get-TestResults - Aggregates and validates test results
- Get-CodeCoverage - Validates coverage thresholds
- Build-Docs and Build-Site - Generates documentation
- Publish-Module and Publish-Site - Automated publishing on release
Workflow Types:
- Production Workflow (
.github/workflows/workflow.yml) - Main workflow for consuming repositories - CI Validation Workflow (
.github/workflows/ci.yml) - Integration tests for framework development - Consuming repositories use production workflow for releases, CI workflow for nightly validation
- Settings MUST be stored in
.github/PSModule.yml(or JSON/PSD1 format) in consuming repositories - Skip flags MUST be available for all major workflow steps
- OS-specific skip flags MUST be supported (Linux, macOS, Windows)
- Settings MUST support test configuration, build options, and publish behavior
- Consuming repositories configure behavior through settings file (opinionated defaults provided)
- Structure requirements documented in GitHub Actions README files MUST be followed by consumers
- Configuration options MUST be backward compatible within major versions
This constitution supersedes all other development practices for Process-PSModule framework development. When conflicts arise between this document and other guidance, the constitution takes precedence.
For Consuming Repositories: This constitution defines how the Process-PSModule framework is built and maintained. Consuming repositories follow the opinionated structure and configuration documented in framework action README files.
Changes to this constitution require:
- Documentation of the proposed change with clear rationale
- Review and approval by project maintainers
- Migration plan for existing code/workflows if applicable
- Version bump according to impact:
- MAJOR: Backward incompatible principle removals or redefinitions
- MINOR: New principles or materially expanded guidance
- PATCH: Clarifications, wording fixes, non-semantic refinements
- All PRs MUST be validated against constitutional principles for framework development
- Workflow design MUST align with Workflow-First Design principle
- Test-First principle compliance is NON-NEGOTIABLE and enforced by review
- Platform Independence MUST be verified through Test-ModuleLocal matrix testing (ubuntu-latest, windows-latest, macos-latest)
- Modules MUST function identically across all platforms
- Quality Gates MUST be enforced by workflow automation
- PowerShell 7.4+ compatibility MUST be verified
- Action-based implementation preferred over inline workflow code
- CI validation workflow MUST pass before merging changes to core workflows
- Consuming repositories MUST follow the Required Module Structure documented in Product Overview
For agent-specific runtime development guidance when developing the framework, agents should reference:
- GitHub Copilot:
.github/copilot-instructions.md(if exists) - Other agents: Check for
AGENTS.md,CLAUDE.md,GEMINI.md, orQWEN.md
For Consuming Repositories: Follow the Required Module Structure and Workflow Integration Requirements documented in the Product Overview section. Start with Template-PSModule.
Version: 1.6.0 | Ratified: TODO(RATIFICATION_DATE) | Last Amended: 2025-10-01