|
| 1 | +# Workflow API Contract: Unified CI and Release Workflow |
| 2 | + |
| 3 | +**Feature**: 001-merge-ci-release-workflows |
| 4 | +**Date**: 2025-10-02 |
| 5 | +**Workflow File**: `.github/workflows/workflow.yml` |
| 6 | + |
| 7 | +## Contract Overview |
| 8 | + |
| 9 | +The unified workflow maintains the same API surface as the existing workflow.yml to ensure backward compatibility with consuming repositories. |
| 10 | + |
| 11 | +## Workflow Trigger |
| 12 | + |
| 13 | +```yaml |
| 14 | +on: |
| 15 | + workflow_call: |
| 16 | + # Unchanged from v4.x |
| 17 | +``` |
| 18 | + |
| 19 | +**Contract**: Workflow is callable from consuming repository workflows using `uses:` syntax. |
| 20 | + |
| 21 | +**Example Consumer Usage**: |
| 22 | +```yaml |
| 23 | +name: Process-PSModule |
| 24 | + |
| 25 | +on: |
| 26 | + pull_request: |
| 27 | + branches: [main] |
| 28 | + types: [closed, opened, reopened, synchronize, labeled] |
| 29 | + push: |
| 30 | + branches: [main] |
| 31 | + workflow_dispatch: |
| 32 | + |
| 33 | +jobs: |
| 34 | + Process-PSModule: |
| 35 | + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 |
| 36 | + secrets: |
| 37 | + APIKEY: ${{ secrets.APIKEY }} |
| 38 | +``` |
| 39 | +
|
| 40 | +## Inputs |
| 41 | +
|
| 42 | +All inputs remain unchanged from workflow.yml v4.x: |
| 43 | +
|
| 44 | +### Input: Name |
| 45 | +- **Type**: `string` |
| 46 | +- **Description**: The name of the module to process. Scripts default to the repository name if nothing is specified. |
| 47 | +- **Required**: `false` |
| 48 | +- **Default**: Repository name (auto-detected) |
| 49 | + |
| 50 | +### Input: SettingsPath |
| 51 | +- **Type**: `string` |
| 52 | +- **Description**: The path to the settings file. Settings in the settings file take precedence over the action inputs. |
| 53 | +- **Required**: `false` |
| 54 | +- **Default**: `.github/PSModule.yml` |
| 55 | + |
| 56 | +### Input: Debug |
| 57 | +- **Type**: `boolean` |
| 58 | +- **Description**: Enable debug output. |
| 59 | +- **Required**: `false` |
| 60 | +- **Default**: `false` |
| 61 | + |
| 62 | +### Input: Verbose |
| 63 | +- **Type**: `boolean` |
| 64 | +- **Description**: Enable verbose output. |
| 65 | +- **Required**: `false` |
| 66 | +- **Default**: `false` |
| 67 | + |
| 68 | +### Input: Version |
| 69 | +- **Type**: `string` |
| 70 | +- **Description**: Specifies the version of the GitHub module to be installed. The value must be an exact version. |
| 71 | +- **Required**: `false` |
| 72 | +- **Default**: `''` (latest stable) |
| 73 | + |
| 74 | +### Input: Prerelease |
| 75 | +- **Type**: `boolean` |
| 76 | +- **Description**: Whether to use a prerelease version of the 'GitHub' module. |
| 77 | +- **Required**: `false` |
| 78 | +- **Default**: `false` |
| 79 | + |
| 80 | +### Input: WorkingDirectory |
| 81 | +- **Type**: `string` |
| 82 | +- **Description**: The path to the root of the repo. |
| 83 | +- **Required**: `false` |
| 84 | +- **Default**: `'.'` (repository root) |
| 85 | + |
| 86 | +## Secrets |
| 87 | + |
| 88 | +All secrets remain unchanged from workflow.yml v4.x: |
| 89 | + |
| 90 | +### Secret: APIKey |
| 91 | +- **Description**: The API key for the PowerShell Gallery. |
| 92 | +- **Required**: `true` (for Publish-Module job execution) |
| 93 | +- **Used By**: Publish-Module job |
| 94 | +- **Note**: Required even in CI-only mode (job is skipped but secret must be defined) |
| 95 | + |
| 96 | +### Secret: TEST_APP_ENT_CLIENT_ID |
| 97 | +- **Description**: The client ID of an Enterprise GitHub App for running tests. |
| 98 | +- **Required**: `false` |
| 99 | +- **Used By**: Test-ModuleLocal job (if Enterprise App tests are enabled) |
| 100 | + |
| 101 | +### Secret: TEST_APP_ENT_PRIVATE_KEY |
| 102 | +- **Description**: The private key of an Enterprise GitHub App for running tests. |
| 103 | +- **Required**: `false` |
| 104 | +- **Used By**: Test-ModuleLocal job (if Enterprise App tests are enabled) |
| 105 | + |
| 106 | +### Secret: TEST_APP_ORG_CLIENT_ID |
| 107 | +- **Description**: The client ID of an Organization GitHub App for running tests. |
| 108 | +- **Required**: `false` |
| 109 | +- **Used By**: Test-ModuleLocal job (if Organization App tests are enabled) |
| 110 | + |
| 111 | +### Secret: TEST_APP_ORG_PRIVATE_KEY |
| 112 | +- **Description**: The private key of an Organization GitHub App for running tests. |
| 113 | +- **Required**: `false` |
| 114 | +- **Used By**: Test-ModuleLocal job (if Organization App tests are enabled) |
| 115 | + |
| 116 | +### Secret: TEST_USER_ORG_FG_PAT |
| 117 | +- **Description**: The fine-grained personal access token with org access for running tests. |
| 118 | +- **Required**: `false` |
| 119 | +- **Used By**: Test-ModuleLocal job (if org-level tests are enabled) |
| 120 | + |
| 121 | +### Secret: TEST_USER_USER_FG_PAT |
| 122 | +- **Description**: The fine-grained personal access token with user account access for running tests. |
| 123 | +- **Required**: `false` |
| 124 | +- **Used By**: Test-ModuleLocal job (if user-level tests are enabled) |
| 125 | + |
| 126 | +### Secret: TEST_USER_PAT |
| 127 | +- **Description**: The classic personal access token for running tests. |
| 128 | +- **Required**: `false` |
| 129 | +- **Used By**: Test-ModuleLocal job (if PAT-based tests are enabled) |
| 130 | + |
| 131 | +## Outputs |
| 132 | + |
| 133 | +The workflow does not define explicit outputs. Results are communicated through: |
| 134 | + |
| 135 | +1. **Workflow Artifacts**: |
| 136 | + - `module`: Built module artifact (from Build-Module job) |
| 137 | + - `site`: Documentation site artifact (from Build-Site job) |
| 138 | + - `test-results`: Test result files (from test jobs) |
| 139 | + - `code-coverage`: Code coverage reports (from Get-CodeCoverage job) |
| 140 | + |
| 141 | +2. **GitHub Releases**: Created by Publish-Module job (release mode only) |
| 142 | + |
| 143 | +3. **GitHub Pages**: Deployed by Publish-Site job (release mode only) |
| 144 | + |
| 145 | +4. **Workflow Status**: Success/failure status reported to PR and commit status checks |
| 146 | + |
| 147 | +## Permissions |
| 148 | + |
| 149 | +The workflow requires the following permissions: |
| 150 | + |
| 151 | +```yaml |
| 152 | +permissions: |
| 153 | + contents: write # to checkout the repo and create releases on the repo |
| 154 | + pull-requests: write # to write comments to PRs |
| 155 | + statuses: write # to update the status of the workflow from linter |
| 156 | + pages: write # to deploy to Pages |
| 157 | + id-token: write # to verify the deployment originates from an appropriate source |
| 158 | +``` |
| 159 | + |
| 160 | +**Note**: These permissions are required for full functionality (CI + Release mode). In CI-only mode, `pages` and `id-token` permissions are unused but do not cause issues. |
| 161 | + |
| 162 | +## Job Execution Contract |
| 163 | + |
| 164 | +### Always Executed Jobs |
| 165 | + |
| 166 | +The following jobs execute in all modes (CI-only and CI + Release): |
| 167 | + |
| 168 | +1. **Get-Settings**: Load and parse module settings |
| 169 | +2. **Build-Module**: Compile module from source |
| 170 | +3. **Build-Docs**: Generate module documentation |
| 171 | +4. **Build-Site**: Build documentation site |
| 172 | +5. **Test-SourceCode**: Matrix test source code (multiple OS) |
| 173 | +6. **Lint-SourceCode**: Matrix lint source code (multiple OS) |
| 174 | +7. **Test-Module**: Test built module integrity |
| 175 | +8. **BeforeAll-ModuleLocal**: Setup external test resources (if tests/BeforeAll.ps1 exists) |
| 176 | +9. **Test-ModuleLocal**: Matrix test module functionality (ubuntu, windows, macos) |
| 177 | +10. **AfterAll-ModuleLocal**: Teardown external test resources (if tests/AfterAll.ps1 exists) |
| 178 | +11. **Get-TestResults**: Aggregate and validate test results |
| 179 | +12. **Get-CodeCoverage**: Analyze code coverage |
| 180 | + |
| 181 | +### Conditionally Executed Jobs |
| 182 | + |
| 183 | +The following jobs execute only in CI + Release mode: |
| 184 | + |
| 185 | +13. **Publish-Module**: Publish module to PowerShell Gallery |
| 186 | + - **Condition**: Tests pass AND (merged PR OR push to default branch) |
| 187 | + |
| 188 | +14. **Publish-Site**: Deploy documentation to GitHub Pages |
| 189 | + - **Condition**: Tests pass AND Build-Site succeeds AND (merged PR OR push to default branch) |
| 190 | + |
| 191 | +## Conditional Execution Logic |
| 192 | + |
| 193 | +### Publish-Module Condition |
| 194 | + |
| 195 | +```yaml |
| 196 | +if: | |
| 197 | + needs.Get-Settings.result == 'success' && |
| 198 | + needs.Get-TestResults.result == 'success' && |
| 199 | + needs.Get-CodeCoverage.result == 'success' && |
| 200 | + !cancelled() && |
| 201 | + ( |
| 202 | + (github.event_name == 'pull_request' && github.event.pull_request.merged == true) || |
| 203 | + (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) |
| 204 | + ) |
| 205 | +``` |
| 206 | + |
| 207 | +**Publishes When**: |
| 208 | +- All tests pass (Get-TestResults, Get-CodeCoverage success) |
| 209 | +- Workflow not cancelled |
| 210 | +- **Either**: |
| 211 | + - Pull request merged to default branch, **OR** |
| 212 | + - Direct push to default branch |
| 213 | + |
| 214 | +**Skips When**: |
| 215 | +- Tests fail |
| 216 | +- Workflow cancelled |
| 217 | +- Unmerged pull request |
| 218 | +- Manual trigger (`workflow_dispatch`) |
| 219 | +- Scheduled run (`schedule`) |
| 220 | +- Push to non-default branch |
| 221 | + |
| 222 | +### Publish-Site Condition |
| 223 | + |
| 224 | +```yaml |
| 225 | +if: | |
| 226 | + needs.Get-Settings.result == 'success' && |
| 227 | + needs.Get-TestResults.result == 'success' && |
| 228 | + needs.Get-CodeCoverage.result == 'success' && |
| 229 | + needs.Build-Site.result == 'success' && |
| 230 | + !cancelled() && |
| 231 | + ( |
| 232 | + (github.event_name == 'pull_request' && github.event.pull_request.merged == true) || |
| 233 | + (github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)) |
| 234 | + ) |
| 235 | +``` |
| 236 | + |
| 237 | +**Publishes When**: Same as Publish-Module, plus Build-Site must succeed |
| 238 | + |
| 239 | +## Behavioral Contract by Trigger Type |
| 240 | + |
| 241 | +### Trigger: Pull Request (Opened/Synchronized/Reopened) |
| 242 | + |
| 243 | +**Event**: `github.event_name == 'pull_request'` AND `github.event.pull_request.merged == false` |
| 244 | + |
| 245 | +**Behavior**: |
| 246 | +- ✅ Execute all CI jobs (build, test, lint) |
| 247 | +- ❌ Skip Publish-Module |
| 248 | +- ❌ Skip Publish-Site |
| 249 | +- ✅ Report test results as PR status checks |
| 250 | +- ✅ Comment on PR with test/coverage results |
| 251 | + |
| 252 | +**Exit**: Workflow completes after test results |
| 253 | + |
| 254 | +### Trigger: Pull Request (Merged) |
| 255 | + |
| 256 | +**Event**: `github.event_name == 'pull_request'` AND `github.event.pull_request.merged == true` |
| 257 | + |
| 258 | +**Behavior**: |
| 259 | +- ✅ Execute all CI jobs (build, test, lint) |
| 260 | +- ✅ Execute Publish-Module (if tests pass) |
| 261 | +- ✅ Execute Publish-Site (if tests pass and Build-Site succeeds) |
| 262 | +- ✅ Create GitHub release |
| 263 | +- ✅ Deploy documentation to GitHub Pages |
| 264 | + |
| 265 | +**Exit**: Workflow completes after successful publish |
| 266 | + |
| 267 | +### Trigger: Push to Default Branch |
| 268 | + |
| 269 | +**Event**: `github.event_name == 'push'` AND `github.ref == 'refs/heads/main'` |
| 270 | + |
| 271 | +**Behavior**: Same as merged pull request |
| 272 | +- ✅ Execute all CI jobs |
| 273 | +- ✅ Execute Publish-Module (if tests pass) |
| 274 | +- ✅ Execute Publish-Site (if tests pass) |
| 275 | + |
| 276 | +**Note**: Direct pushes bypass PR validation; use with caution |
| 277 | + |
| 278 | +### Trigger: Push to Non-Default Branch |
| 279 | + |
| 280 | +**Event**: `github.event_name == 'push'` AND `github.ref != 'refs/heads/main'` |
| 281 | + |
| 282 | +**Behavior**: |
| 283 | +- ✅ Execute all CI jobs |
| 284 | +- ❌ Skip Publish-Module |
| 285 | +- ❌ Skip Publish-Site |
| 286 | + |
| 287 | +**Use Case**: Feature branch validation without PR |
| 288 | + |
| 289 | +### Trigger: Manual (workflow_dispatch) |
| 290 | + |
| 291 | +**Event**: `github.event_name == 'workflow_dispatch'` |
| 292 | + |
| 293 | +**Behavior**: |
| 294 | +- ✅ Execute all CI jobs |
| 295 | +- ❌ Skip Publish-Module |
| 296 | +- ❌ Skip Publish-Site |
| 297 | + |
| 298 | +**Use Case**: On-demand validation without publishing |
| 299 | + |
| 300 | +### Trigger: Scheduled (schedule) |
| 301 | + |
| 302 | +**Event**: `github.event_name == 'schedule'` |
| 303 | + |
| 304 | +**Behavior**: |
| 305 | +- ✅ Execute all CI jobs |
| 306 | +- ❌ Skip Publish-Module |
| 307 | +- ❌ Skip Publish-Site |
| 308 | + |
| 309 | +**Use Case**: Nightly regression testing |
| 310 | + |
| 311 | +## Breaking Changes from v4.x |
| 312 | + |
| 313 | +### For Consuming Repositories Using workflow.yml |
| 314 | + |
| 315 | +**Impact**: ✅ **None** - Existing behavior preserved |
| 316 | + |
| 317 | +**Changes Required**: None (optional: review trigger conditions in consuming repo workflow) |
| 318 | + |
| 319 | +### For Consuming Repositories Using CI.yml |
| 320 | + |
| 321 | +**Impact**: ⚠️ **Deprecation Warning** - CI.yml marked deprecated |
| 322 | + |
| 323 | +**Changes Required**: Migrate to workflow.yml during v5.x lifecycle |
| 324 | + |
| 325 | +**Migration Path**: |
| 326 | +1. Update consuming repository workflow to call workflow.yml instead of CI.yml |
| 327 | +2. Test both PR and merge workflows |
| 328 | +3. Remove CI.yml reference from consuming repository |
| 329 | + |
| 330 | +### For Process-PSModule Framework |
| 331 | + |
| 332 | +**Impact**: 🌟 **Breaking Change** - Major version bump (v5.0.0) |
| 333 | + |
| 334 | +**Changes**: |
| 335 | +- CI.yml deprecated (removed in v6.0.0) |
| 336 | +- Unified workflow.yml handles all scenarios |
| 337 | +- New conditional execution logic |
| 338 | + |
| 339 | +## Validation Test Cases |
| 340 | + |
| 341 | +Consuming repositories should validate these scenarios after migration: |
| 342 | + |
| 343 | +1. ✅ **PR Opened**: CI runs, no publish |
| 344 | +2. ✅ **PR Synchronized**: CI runs, no publish |
| 345 | +3. ✅ **PR Merged**: CI runs, publish succeeds |
| 346 | +4. ✅ **Direct Push to Main**: CI runs, publish succeeds |
| 347 | +5. ✅ **Push to Feature Branch**: CI runs (if configured), no publish |
| 348 | +6. ✅ **Manual Trigger**: CI runs, no publish |
| 349 | +7. ✅ **Test Failure**: Workflow fails, no publish |
| 350 | +8. ✅ **Coverage Below Threshold**: Workflow fails, no publish |
| 351 | + |
| 352 | +## Support and Compatibility |
| 353 | + |
| 354 | +| Process-PSModule Version | Unified Workflow | CI.yml Support | API Breaking Changes | |
| 355 | +|-------------------------|------------------|----------------|---------------------| |
| 356 | +| v4.x (current) | ❌ No | ✅ Yes | N/A | |
| 357 | +| v5.0 (this feature) | ✅ Yes | ⚠️ Deprecated | No (for workflow.yml users) | |
| 358 | +| v6.0 (future) | ✅ Yes | ❌ Removed | Yes (CI.yml removed) | |
| 359 | + |
| 360 | +## Conclusion |
| 361 | + |
| 362 | +The unified workflow maintains API compatibility with workflow.yml v4.x while adding intelligent conditional execution. Consuming repositories using workflow.yml continue working without changes. Consumers using CI.yml should migrate during the v5.x support period. |
0 commit comments