diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5fc163a9..8070d866 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -49,4 +49,11 @@ pwsh -Command "ls -Recurse" gh issue create --title "Feature" --body "Description" ``` +## Other instructions + +| Tech | Instruction file | +|------|------------------| +| PowerShell | [pwsh.instructions.md](./instructions/pwsh.instructions.md) | +| Markdown | [md.instructions.md](./instructions/md.instructions.md) | + diff --git a/.github/prompts/implement.prompt.md b/.github/prompts/implement.prompt.md index 3f727a64..8f92b2da 100644 --- a/.github/prompts/implement.prompt.md +++ b/.github/prompts/implement.prompt.md @@ -4,210 +4,170 @@ description: Execute the implementation plan by processing and executing all tas # Implement -The user input can be provided directly by the agent or as a command argument - you **MUST** consider it before proceeding with the prompt (if not empty). +The user input can be provided directly by the agent or as a command argument — you **MUST** consider it before proceeding with the prompt (if not empty). -User input: +**User input:** $ARGUMENTS +--- + **Workflow Modes**: This command supports two modes: + - **Local (default)**: Work with the current repository (origin). No special configuration needed. - **Fork**: Contribute to an upstream repository. Reads `.fork-info.json` created by `/specify`. -**Iteration Support**: This command supports iterative implementation - you can run it multiple times to complete remaining tasks, fix issues, or add refinements. Task completion state is tracked in tasks.md with [X] markers. +**Iteration Support**: This command supports iterative implementation — you can run it multiple times to complete remaining tasks, fix issues, or add refinements. Task completion state is tracked in `tasks.md` with `[X]` markers. -1. **Set Implementing label immediately**: +--- + +## Workflow Steps + +1. **Set Implementing label immediately** - **Determine target repository**: - - Check if `.fork-info.json` exists in the feature directory + - Check if `.fork-info.json` exists in the feature directory. - If it exists: - - Validate required fields: `is_fork` (true), `upstream_owner` (non-empty), `upstream_repo` (non-empty) - - Use `upstream_owner/upstream_repo` for all GitHub operations - - If it doesn't exist, use the current repository (origin) - - Get the issue number associated with the current feature branch - - **Add 'Implementing' label** to the issue and PR immediately in the target repository - - **Remove 'Planning' label** from the issue and PR + - Validate required fields: `is_fork` (true), `upstream_owner` (non-empty), `upstream_repo` (non-empty). + - Use `upstream_owner/upstream_repo` for all GitHub operations. + - If it doesn't exist, use the current repository (origin). + - Get the issue number associated with the current feature branch. + - **Add `Implementing` label** to the issue and PR immediately in the target repository. + - **Remove `Planning` label** from the issue and PR. + **GitHub Integration**: If GitHub tools are available, update labels automatically in the target repository. If not available, use: + ```bash # If fork: gh issue edit --repo / --remove-label "Planning" --add-label "Implementing" # If local: gh issue edit --remove-label "Planning" --add-label "Implementing" gh issue edit --remove-label "Planning" --add-label "Implementing" ``` -2. Run [`.specify/scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks`](../../.specify/scripts/powershell/check-prerequisites.ps1) from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. -3. Load and analyze the implementation context: - - **REQUIRED**: Read tasks.md for the complete task list and execution plan - - **REQUIRED**: Read plan.md for tech stack, architecture, and file structure - - **IF EXISTS**: Read data-model.md for entities and relationships - - **IF EXISTS**: Read contracts/ for API specifications and test requirements - - **IF EXISTS**: Read research.md for technical decisions and constraints - - **IF EXISTS**: Read quickstart.md for integration scenarios -4. Parse tasks.md structure and extract: - - **Detect iteration state**: Check task completion markers - - Tasks marked [X] are complete - skip unless user requests changes - - Tasks marked [ ] are pending - these need implementation - - If all tasks are complete, check if user input requests additional work - - **Task phases**: Setup, Tests, Core, Integration, Polish - - **Task dependencies**: Sequential vs parallel execution rules - - **Task details**: ID, description, file paths, parallel markers [P] - - **Execution flow**: Order and dependency requirements -5. Execute implementation following the task plan: - - **Skip completed tasks**: Don't re-implement tasks marked [X] unless explicitly requested - - **Resume from last incomplete task**: Start with first [ ] task found - - **Phase-by-phase execution**: Complete each phase before moving to the next - - **Respect dependencies**: Run sequential tasks in order, parallel tasks [P] can run together - - **Follow TDD approach**: Execute test tasks before their corresponding implementation tasks - - **File-based coordination**: Tasks affecting the same files must run sequentially - - **Validation checkpoints**: Verify each phase completion before proceeding -6. Implementation execution rules: - - **Setup first**: Initialize project structure, dependencies, and configuration - - **Tests before code**: If you need to write tests for contracts, entities, and integration scenarios - - **Core development**: Implement models, services, CLI commands, and endpoints - - **Integration work**: Database connections, middleware, logging, and external services - - **Polish and validation**: Unit tests, performance optimization, and documentation -7. Progress tracking and error handling: - - Report progress after each completed task - - Halt execution if any non-parallel task fails - - For parallel tasks [P], continue with successful tasks and report failed ones - - Provide clear error messages with context for debugging - - Suggest next steps if implementation cannot proceed - - **CRITICAL - Update task status immediately after completion**: - * After completing each task, mark it as [X] in tasks.md - * Update the PR description to mark the corresponding task checkbox from `- [ ] T###:` to `- [X] T###:` - * This MUST be done task-by-task as you progress, not at the end - * If GitHub tools are available, use them to update the PR description - * If not available, use: `gh pr edit --body ""` - * Ensure task progress is visible in real-time to users watching the PR -8. Completion validation: - - Verify all required tasks are completed - - Check that implemented features match the original specification - - Validate that tests pass and coverage meets requirements - - Confirm the implementation follows the technical plan - - Report final status with summary of completed work -9. Update the constitution: - - Read the [Constitution](../../.specify/memory/constitution.md) file. - - Read the [constitution prompt](./constitution.prompt.md) for guidance on how to update the constitution. - - Update the constitution file with details on what has been implemented in this PR - - Document the functionality that was added or changed, remove the sections that are no longer relevant - - Ensure the constitution reflects the current state of the codebase -10. Update the CHANGELOG: - - **Locate or create CHANGELOG.md** in the repository root - - **Add a new entry** for this feature/change following the Keep a Changelog format - - **Structure the entry** with: - * Version header (use `[Unreleased]` if version isn't determined yet) - * Date (current date) - * Category sections as applicable: - - `### Added` - for new features - - `### Changed` - for changes in existing functionality - - `### Deprecated` - for soon-to-be removed features - - `### Removed` - for now removed features - - `### Fixed` - for any bug fixes - - `### Security` - for vulnerability fixes - * Write entries from the user's perspective, focusing on what changed and why it matters - * Include brief usage examples where helpful - * Link to the PR or issue: `[#]` - - **Keep it concise**: Focus on user-impacting changes, not internal refactoring details -11. Final commit and push: - - **Stage all implemented changes** including: - * All source code files created or modified - * Updated tasks.md with completed task markers [X] - * Updated CHANGELOG.md - * Updated constitution.md - * Any configuration or documentation files - - **Create a descriptive commit message**: - * Use conventional commit format: `(): ` - * Types: feat, fix, docs, refactor, test, chore - * Include reference to issue: `Fixes #` - - **Push the branch** to remote - - Verify the push completed successfully -12. Update PR description with release notes: - - **Determine workflow mode and target repository**: - - Check if `.fork-info.json` exists in the feature directory (same directory as spec.md) - - **If exists** (fork mode): - - Validate required fields: `is_fork` (true), `upstream_owner` (non-empty), `upstream_repo` (non-empty) - - If validation fails, halt and instruct user: "Invalid fork configuration in `.fork-info.json`. Please run `/specify` again with complete fork information: upstream owner, upstream repo." - - Use `upstream_owner/upstream_repo` for all GitHub operations - - **If not exists** (local mode - default): - - Use the current repository (origin) for all GitHub operations - - **Determine PR operation**: - - If PR already exists for this branch, UPDATE it (description, status, labels) - - If no PR exists, CREATE a new one - - **Target branch**: The PR must be against the default branch - - **PR status**: The PR must not be draft, it should be ready for review - - **Retrieve the issue title**: Get the title from the linked GitHub issue (created in `/specify`) from the target repository - - **Use the same title for the PR**: Verify the PR title matches the issue title exactly. If they differ, update the PR title to match the issue. - - If unable to retrieve the issue title, determine the PR type and icon based on the changes: - | Type of change | Icon | Label | - |-|-|-| - | Docs | 📖 | Docs | - | Fix | 🪲 | Fix, Patch | - | Security fix | ⚠️ | Fix | - | Patch | 🩹 | Patch | - | Feature | 🚀 | Minor | - | Breaking change | 🌟 | Major | - - Fallback PR title format (if issue title unavailable): ` [Type of change]: ` - - **REPLACE the entire PR description with release notes**: - * **IMPORTANT**: Clear the existing PR description completely (including task list) and replace it with the release notes - * This ensures the PR description is ready to be used as GitHub Release notes when merged to main - * **Opening summary** (1-2 paragraphs): - - Start with what was accomplished in user-focused language - - Write in past tense: "Added...", "Improved...", "Fixed..." - - Focus on the "why" - what problem does this solve or what capability does it enable? - - Mention the user impact - who benefits and how? - - At the end, add: "Fixes #" - * **What's Changed** section: - - 3-5 bullet points highlighting key changes from the user's perspective - - Focus on capabilities, not implementation details - - Example: "✨ Added support for custom templates" not "Created new Template class" - * **Usage** section (if applicable): - - Brief example showing how to use the new functionality - - Keep it minimal - just enough to get started - - Use code blocks for commands or code snippets - * **Breaking Changes** section (if applicable): - - Clear warning about what breaks - - Migration guidance for users - - What they need to change in their code - * **Technical Notes** section (optional, for maintainers): - - Brief implementation notes if relevant for reviewers - - Dependencies added or updated - - Architecture decisions - * **Tone and style**: - - Professional and concise - - Avoid jargon and internal terminology - - Write for the end user, not just developers - - This description will be used in release notes - - **Apply appropriate label(s)** based on the type of change - - **Link the PR** to the associated issue - - **Update `.fork-info.json`** (if it exists) with the latest PR number (if not already present) - **GitHub Integration**: If GitHub tools or integrations are available (such as GitHub MCP Server or other GitHub integrations), use them to update the PR description in the target repository. If not available, provide this fallback command: - ```bash - # Replace PR description with release notes - # If fork: gh pr edit --repo / --body "" - # If local: gh pr edit --body "" - gh pr edit --body "" + +2. **Run prerequisites** + Run [`check-prerequisites.ps1`](../../.specify/scripts/powershell/check-prerequisites.ps1) from repo root: + + ```powershell + .specify/scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks ``` -13. Mark PR as ready for review: - - **Determine target repository** (same logic as step 12): - - Check if `.fork-info.json` exists in the feature directory - - If it exists and validated, use `upstream_owner/upstream_repo` - - If it doesn't exist, use the current repository (origin) - - **Remove 'Implementing' label** from the linked issue and the PR in the target repository - - **Mark PR as ready for review** (no longer draft) - - **After updates**: Ensure `.fork-info.json` (if it exists) has both issue and PR numbers stored - **GitHub Integration**: If GitHub tools are available, update labels and PR status automatically in the target repository. If not available, use: + + Parse `FEATURE_DIR` and `AVAILABLE_DOCS` list. All paths must be absolute. + +3. **Load and analyze the implementation context** + + * **REQUIRED**: Read `tasks.md` for the complete task list and execution plan. + * **REQUIRED**: Read `plan.md` for tech stack, architecture, and file structure. + * **IF EXISTS**: Read `data-model.md` for entities and relationships. + * **IF EXISTS**: Read `contracts/` for API specifications and test requirements. + * **IF EXISTS**: Read `research.md` for technical decisions and constraints. + * **IF EXISTS**: Read `quickstart.md` for integration scenarios. + +4. **Parse `tasks.md` structure and extract** + + * Detect iteration state (completed `[X]`, pending `[ ]`). + * Identify task phases: Setup, Tests, Core, Integration, Polish. + * Capture dependencies: sequential vs parallel (`[P]`). + * Build execution flow based on order and requirements. + +5. **Execute implementation following the task plan** + + * Skip completed `[X]` tasks. + * Resume from first `[ ]` task. + * Execute phase-by-phase, respecting dependencies. + * Follow TDD: tests before implementation. + * Coordinate tasks by file access. + * Add validation checkpoints. + +6. **Implementation execution rules** + + * Setup first (dependencies, configs). + * Tests before code. + * Core development. + * Integration (DB, middleware, logging, external services). + * Polish (tests, performance, docs). + +7. **Progress tracking and error handling** + + * Report progress after each task. + * Halt on failed sequential tasks; continue parallel `[P]` where possible. + * Provide clear error messages and next-step suggestions. + * **Update task status immediately after completion**: + + * Mark as `[X]` in `tasks.md`. + * Update PR task list (`- [ ] T###:` → `- [X] T###:`). + * Use `gh pr edit --body ""` if needed. + +8. **Completion validation** + + * Verify required tasks. + * Confirm spec compliance. + * Ensure tests pass and coverage is met. + * Check against `plan.md`. + * Report final status. + +9. **Update the constitution** + + * Read [Constitution](../../.specify/memory/constitution.md). + * Read [constitution prompt](./constitution.prompt.md). + * Update constitution with implemented details. + * Remove obsolete sections. + +10. **Update the CHANGELOG** + + * Use `CHANGELOG.md` in root. + * Add entry in [Keep a Changelog](https://keepachangelog.com) format. + * Categories: Added, Changed, Deprecated, Removed, Fixed, Security. + * Keep user-focused and concise. + * Example: + + ```markdown + ## [Unreleased] - 2025-10-03 + + ### Added + - Support for new implementation workflow [#123] + ``` + +11. **Final commit and push** + + * Stage all changes (`tasks.md`, `CHANGELOG.md`, `constitution.md`, code, configs). + * Commit using Conventional Commit format: + + ```markdown + (): + Fixes # + ``` + * Push branch to remote. + +12. **Update PR description with release notes** + + * Replace PR body entirely with release notes. + * Ensure title matches issue. + * Release note structure: + + * Opening summary (what, why, impact). + * **What's Changed** (3–5 bullets). + * **Usage** (if needed). + * **Breaking Changes** (if any). + * **Technical Notes** (optional). + + **Fallback command:** + ```bash - # Mark PR as ready for review - # If fork: gh pr ready --repo / - # If local: gh pr ready - gh pr ready + gh pr edit --body "" + ``` - # Remove Implementing label from issue - # If fork: gh issue edit --repo / --remove-label "Implementing" - # If local: gh issue edit --remove-label "Implementing" - gh issue edit --remove-label "Implementing" +13. **Mark PR as ready for review** + + * Remove `Implementing` label. + * Ensure PR is not draft. + * Sync `.fork-info.json` with issue + PR numbers. - # Remove Implementing label from PR - # If fork: gh pr edit --repo / --remove-label "Implementing" - # If local: gh pr edit --remove-label "Implementing" + **Fallback commands:** + + ```bash + gh pr ready + gh issue edit --remove-label "Implementing" gh pr edit --remove-label "Implementing" ``` -Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/tasks` first to regenerate the task list. +--- + +**Note**: This command assumes a complete task breakdown exists in `tasks.md`. If missing, run `/tasks` first to regenerate. diff --git a/.github/workflows/Build-Docs.yml b/.github/workflows/Build-Docs.yml index edad4380..edc538b6 100644 --- a/.github/workflows/Build-Docs.yml +++ b/.github/workflows/Build-Docs.yml @@ -44,6 +44,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v5 with: + persist-credentials: false fetch-depth: 0 - name: Download module artifact diff --git a/.github/workflows/Build-Module.yml b/.github/workflows/Build-Module.yml index ffd17aa0..328c4d56 100644 --- a/.github/workflows/Build-Module.yml +++ b/.github/workflows/Build-Module.yml @@ -30,6 +30,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Build module uses: PSModule/Build-PSModule@v4 diff --git a/.github/workflows/Build-Site.yml b/.github/workflows/Build-Site.yml index ea063558..b13face7 100644 --- a/.github/workflows/Build-Site.yml +++ b/.github/workflows/Build-Site.yml @@ -44,6 +44,7 @@ jobs: - name: Checkout Code uses: actions/checkout@v5 with: + persist-credentials: false fetch-depth: 0 - name: Install-PSModuleHelpers diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index 397693e9..00000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,324 +0,0 @@ -name: Process-PSModule - CI - -on: - workflow_call: - secrets: - APIKey: - description: The API key for the PowerShell Gallery. - required: true - TEST_APP_ENT_CLIENT_ID: - description: The client ID of an Enterprise GitHub App for running tests. - required: false - TEST_APP_ENT_PRIVATE_KEY: - description: The private key of an Enterprise GitHub App for running tests. - required: false - TEST_APP_ORG_CLIENT_ID: - description: The client ID of an Organization GitHub App for running tests. - required: false - TEST_APP_ORG_PRIVATE_KEY: - description: The private key of an Organization GitHub App for running tests. - required: false - TEST_USER_ORG_FG_PAT: - description: The fine-grained personal access token with org access for running tests. - required: false - TEST_USER_USER_FG_PAT: - description: The fine-grained personal access token with user account access for running tests. - required: false - TEST_USER_PAT: - description: The classic personal access token for running tests. - required: false - inputs: - Name: - type: string - description: The name of the module to process. Scripts default to the repository name if nothing is specified. - required: false - SettingsPath: - type: string - description: The path to the settings file. Settings in the settings file take precedence over the action inputs. - required: false - default: .github/PSModule.yml - Debug: - type: boolean - description: Enable debug output. - required: false - default: false - Verbose: - type: boolean - description: Enable verbose output. - required: false - default: false - Version: - type: string - description: Specifies the version of the GitHub module to be installed. The value must be an exact version. - required: false - default: '' - Prerelease: - type: boolean - description: Whether to use a prerelease version of the 'GitHub' module. - required: false - default: false - WorkingDirectory: - type: string - description: The path to the root of the repo. - required: false - default: '.' - -permissions: - contents: read # to checkout the repository - pull-requests: write # to write comments to PRs - statuses: write # to update the status of the workflow from linter - -jobs: - Get-Settings: - uses: ./.github/workflows/Get-Settings.yml - with: - Name: ${{ inputs.Name }} - SettingsPath: ${{ inputs.SettingsPath }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Build-Module: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Module.Skip != true }} - uses: ./.github/workflows/Build-Module.yml - needs: - - Get-Settings - with: - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Build-Docs: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Docs.Skip != true }} - needs: - - Get-Settings - - Build-Module - uses: ./.github/workflows/Build-Docs.yml - with: - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Build-Site: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Site.Skip != true }} - needs: - - Get-Settings - - Build-Docs - uses: ./.github/workflows/Build-Site.yml - with: - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Test-SourceCode: - if: ${{ needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} - needs: - - Get-Settings - strategy: - fail-fast: false - matrix: - include: ${{ fromJson(needs.Get-Settings.outputs.SourceCodeTestSuites) }} - uses: ./.github/workflows/Test-SourceCode.yml - with: - RunsOn: ${{ matrix.RunsOn }} - OS: ${{ matrix.OSName }} - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Lint-SourceCode: - if: ${{ needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} - needs: - - Get-Settings - strategy: - fail-fast: false - matrix: - include: ${{ fromJson(needs.Get-Settings.outputs.SourceCodeTestSuites) }} - uses: ./.github/workflows/Lint-SourceCode.yml - with: - RunsOn: ${{ matrix.RunsOn }} - OS: ${{ matrix.OSName }} - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - Test-Module: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.PSModuleTestSuites != '[]' }} - needs: - - Build-Module - - Get-Settings - strategy: - fail-fast: false - matrix: - include: ${{ fromJson(needs.Get-Settings.outputs.PSModuleTestSuites) }} - uses: ./.github/workflows/Test-Module.yml - secrets: inherit - with: - RunsOn: ${{ matrix.RunsOn }} - OS: ${{ matrix.OSName }} - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - BeforeAll-ModuleLocal: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} - name: BeforeAll-ModuleLocal - runs-on: ubuntu-latest - needs: - - Build-Module - - Get-Settings - steps: - - name: Checkout Code - uses: actions/checkout@v5 - - - name: Install-PSModuleHelpers - uses: PSModule/Install-PSModuleHelpers@v1 - - - name: Run BeforeAll Setup Scripts - uses: PSModule/GitHub-Script@v1 - with: - Name: BeforeAll-ModuleLocal - ShowInfo: false - ShowOutput: true - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - Script: | - LogGroup "Running BeforeAll Setup Scripts" { - $beforeAllScript = 'tests/BeforeAll.ps1' - - if (-not (Test-Path $beforeAllScript)) { - Write-Host "No BeforeAll.ps1 script found at [$beforeAllScript] - exiting successfully" - exit 0 - } - - Write-Host "Running BeforeAll setup script: $beforeAllScript" - try { - & $beforeAllScript - Write-Host "BeforeAll script completed successfully: $beforeAllScript" - } catch { - Write-Error "BeforeAll script failed: $beforeAllScript - $_" - throw - } - } - - Test-ModuleLocal: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} - needs: - - Build-Module - - Get-Settings - - BeforeAll-ModuleLocal - strategy: - fail-fast: false - matrix: - include: ${{ fromJson(needs.Get-Settings.outputs.ModuleTestSuites) }} - uses: ./.github/workflows/Test-ModuleLocal.yml - secrets: inherit - with: - RunsOn: ${{ matrix.RunsOn }} - OSName: ${{ matrix.OSName }} - TestPath: ${{ matrix.TestPath }} - TestName: ${{ matrix.TestName }} - Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - - AfterAll-ModuleLocal: - if: ${{ needs.Test-ModuleLocal.result != 'skipped' && always() }} - name: AfterAll-ModuleLocal - runs-on: ubuntu-latest - needs: - - Test-ModuleLocal - steps: - - name: Checkout Code - uses: actions/checkout@v5 - - - name: Install-PSModuleHelpers - uses: PSModule/Install-PSModuleHelpers@v1 - - - name: Run AfterAll Teardown Scripts - if: always() - uses: PSModule/GitHub-Script@v1 - with: - Name: AfterAll-ModuleLocal - ShowInfo: false - ShowOutput: true - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - WorkingDirectory: ${{ inputs.WorkingDirectory }} - Script: | - LogGroup "Running AfterAll Teardown Scripts" { - $afterAllScript = 'tests/AfterAll.ps1' - - if (-not (Test-Path $afterAllScript)) { - Write-Host "No AfterAll.ps1 script found at [$afterAllScript] - exiting successfully" - exit 0 - } - - Write-Host "Running AfterAll teardown script: $afterAllScript" - try { - & $afterAllScript - Write-Host "AfterAll script completed successfully: $afterAllScript" - } catch { - Write-Warning "AfterAll script failed: $afterAllScript - $_" - # Don't throw for teardown scripts to ensure other cleanup scripts can run - } - } - - Get-TestResults: - if: needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.TestResults.Skip && (needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' || needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) - needs: - - Get-Settings - - Test-SourceCode - - Lint-SourceCode - - Test-Module - - Test-ModuleLocal - uses: ./.github/workflows/Get-TestResults.yml - secrets: inherit - with: - ModuleTestSuites: ${{ needs.Get-Settings.outputs.ModuleTestSuites }} - SourceCodeTestSuites: ${{ needs.Get-Settings.outputs.SourceCodeTestSuites }} - PSModuleTestSuites: ${{ needs.Get-Settings.outputs.PSModuleTestSuites }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} - - Get-CodeCoverage: - if: needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.CodeCoverage.Skip && (needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) - needs: - - Get-Settings - - Test-Module - - Test-ModuleLocal - uses: ./.github/workflows/Get-CodeCoverage.yml - secrets: inherit - with: - CodeCoveragePercentTarget: ${{ fromJson(needs.Get-Settings.outputs.Settings).Test.CodeCoverage.PercentTarget }} - StepSummary_Mode: ${{ fromJson(needs.Get-Settings.outputs.Settings).Test.CodeCoverage.StepSummaryMode }} - Debug: ${{ inputs.Debug }} - Prerelease: ${{ inputs.Prerelease }} - Verbose: ${{ inputs.Verbose }} - Version: ${{ inputs.Version }} diff --git a/.github/workflows/Get-Settings.yml b/.github/workflows/Get-Settings.yml index 1c79b4e5..2f502542 100644 --- a/.github/workflows/Get-Settings.yml +++ b/.github/workflows/Get-Settings.yml @@ -66,6 +66,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Get-Settings uses: PSModule/GitHub-Script@v1 diff --git a/.github/workflows/Lint-SourceCode.yml b/.github/workflows/Lint-SourceCode.yml index f726ab0b..21ef6db1 100644 --- a/.github/workflows/Lint-SourceCode.yml +++ b/.github/workflows/Lint-SourceCode.yml @@ -51,6 +51,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Lint-SourceCode uses: PSModule/Invoke-ScriptAnalyzer@v3 diff --git a/.github/workflows/Linter.yml b/.github/workflows/Linter.yml index f9aa698d..c7c86a51 100644 --- a/.github/workflows/Linter.yml +++ b/.github/workflows/Linter.yml @@ -21,6 +21,7 @@ jobs: - name: Checkout repo uses: actions/checkout@v5 with: + persist-credentials: false fetch-depth: 0 - name: Lint code base diff --git a/.github/workflows/Test-Module.yml b/.github/workflows/Test-Module.yml index 5944827b..01b9fa24 100644 --- a/.github/workflows/Test-Module.yml +++ b/.github/workflows/Test-Module.yml @@ -82,6 +82,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Download module artifact uses: actions/download-artifact@v5 diff --git a/.github/workflows/Test-ModuleLocal.yml b/.github/workflows/Test-ModuleLocal.yml index f7f6ac87..68f4e3f2 100644 --- a/.github/workflows/Test-ModuleLocal.yml +++ b/.github/workflows/Test-ModuleLocal.yml @@ -93,6 +93,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Download module artifact uses: actions/download-artifact@v5 diff --git a/.github/workflows/Test-SourceCode.yml b/.github/workflows/Test-SourceCode.yml index 241ca1e9..3d36e736 100644 --- a/.github/workflows/Test-SourceCode.yml +++ b/.github/workflows/Test-SourceCode.yml @@ -51,6 +51,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Test-SourceCode uses: PSModule/Test-PSModule@v3 diff --git a/.github/workflows/Workflow-Test-Default-CI.yml b/.github/workflows/Workflow-Test-Default-CI.yml deleted file mode 100644 index 10ba430a..00000000 --- a/.github/workflows/Workflow-Test-Default-CI.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Workflow-Test CI [Default] - -run-name: "Workflow-Test CI [Default] - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - workflow_dispatch: - pull_request: - schedule: - - cron: '0 0 * * *' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - pull-requests: write - statuses: write - -jobs: - WorkflowTestCIDefault: - uses: ./.github/workflows/CI.yml - secrets: inherit - with: - WorkingDirectory: tests/srcTestRepo - Name: PSModuleTest2 diff --git a/.github/workflows/Workflow-Test-WithManifest-CI.yml b/.github/workflows/Workflow-Test-WithManifest-CI.yml deleted file mode 100644 index 8c9de2e1..00000000 --- a/.github/workflows/Workflow-Test-WithManifest-CI.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Workflow-Test CI [WithManifest] - -run-name: "Workflow-Test CI [WithManifest] - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - workflow_dispatch: - pull_request: - schedule: - - cron: '0 0 * * *' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - pull-requests: write - statuses: write - -jobs: - WorkflowTestCIWithManifest: - uses: ./.github/workflows/CI.yml - secrets: inherit - with: - WorkingDirectory: tests/srcWithManifestTestRepo - Name: PSModuleTest2 - SettingsPath: .github/PSModule.json diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 9cdc910b..ddc50dcf 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -71,6 +71,11 @@ permissions: id-token: write # to verify the deployment originates from an appropriate source jobs: + # Runs on: + # - ✅ Open/Updated PR - Always runs to load configuration + # - ✅ Merged PR - Always runs to load configuration + # - ✅ Abandoned PR - Always runs to load configuration + # - ✅ Manual run - Always runs to load configuration Get-Settings: uses: ./.github/workflows/Get-Settings.yml with: @@ -82,8 +87,42 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Lints code changes in active PRs + # - ❌ Merged PR - No need to lint after merge + its a merge commit that causes issues with super-linter + # - ❌ Abandoned PR - No need to lint abandoned changes + # - ❌ Manual run - Only runs for PR events + Lint-Repository: + name: Lint code base + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && github.event.pull_request.merged != true && github.event.action != 'closed' + needs: + - Get-Settings + steps: + - name: Checkout repo + uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Lint code base + uses: super-linter/super-linter@latest + env: + GITHUB_TOKEN: ${{ github.token }} + VALIDATE_BIOME_FORMAT: false + VALIDATE_GITHUB_ACTIONS_ZIZMOR: false + VALIDATE_JSCPD: false + VALIDATE_JSON_PRETTIER: false + VALIDATE_MARKDOWN_PRETTIER: false + VALIDATE_YAML_PRETTIER: false + + # Runs on: + # - ✅ Open/Updated PR - Builds module for testing + # - ✅ Merged PR - Builds module for publishing + # - ❌ Abandoned PR - Skips building abandoned changes + # - ✅ Manual run - Builds module when manually triggered Build-Module: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Module.Skip != true }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && fromJson(needs.Get-Settings.outputs.Settings).Build.Module.Skip != true }} uses: ./.github/workflows/Build-Module.yml needs: - Get-Settings @@ -91,8 +130,13 @@ jobs: Name: ${{ fromJson(needs.Get-Settings.outputs.Settings).Name }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Builds documentation for review + # - ✅ Merged PR - Builds documentation for publishing + # - ❌ Abandoned PR - Skips building docs for abandoned changes + # - ✅ Manual run - Builds documentation when manually triggered Build-Docs: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Docs.Skip != true }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && fromJson(needs.Get-Settings.outputs.Settings).Build.Docs.Skip != true }} needs: - Get-Settings - Build-Module @@ -105,8 +149,13 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Builds site for preview + # - ✅ Merged PR - Builds site for publishing + # - ❌ Abandoned PR - Skips building site for abandoned changes + # - ✅ Manual run - Builds site when manually triggered Build-Site: - if: ${{ fromJson(needs.Get-Settings.outputs.Settings).Build.Site.Skip != true }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && fromJson(needs.Get-Settings.outputs.Settings).Build.Site.Skip != true }} needs: - Get-Settings - Build-Docs @@ -119,8 +168,13 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Tests source code changes + # - ✅ Merged PR - Tests source code before publishing + # - ❌ Abandoned PR - Skips testing abandoned changes + # - ✅ Manual run - Tests source code when manually triggered Test-SourceCode: - if: ${{ needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} needs: - Get-Settings strategy: @@ -138,8 +192,13 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Lints source code changes + # - ✅ Merged PR - Lints source code before publishing + # - ❌ Abandoned PR - Skips linting abandoned changes + # - ✅ Manual run - Lints source code when manually triggered Lint-SourceCode: - if: ${{ needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' }} needs: - Get-Settings strategy: @@ -157,8 +216,13 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Tests built module + # - ✅ Merged PR - Tests built module before publishing + # - ❌ Abandoned PR - Skips testing abandoned changes + # - ✅ Manual run - Tests built module when manually triggered Test-Module: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.PSModuleTestSuites != '[]' }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.PSModuleTestSuites != '[]' }} needs: - Build-Module - Get-Settings @@ -178,8 +242,13 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Runs setup scripts before local module tests + # - ✅ Merged PR - Runs setup scripts before local module tests + # - ❌ Abandoned PR - Skips setup for abandoned changes + # - ✅ Manual run - Runs setup scripts when manually triggered BeforeAll-ModuleLocal: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} name: BeforeAll-ModuleLocal runs-on: ubuntu-latest needs: @@ -188,9 +257,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 - - - name: Install-PSModuleHelpers - uses: PSModule/Install-PSModuleHelpers@v1 + with: + persist-credentials: false + fetch-depth: 0 - name: Run BeforeAll Setup Scripts uses: PSModule/GitHub-Script@v1 @@ -222,8 +291,13 @@ jobs: } } + # Runs on: + # - ✅ Open/Updated PR - Tests module in local environment + # - ✅ Merged PR - Tests module in local environment before publishing + # - ❌ Abandoned PR - Skips testing abandoned changes + # - ✅ Manual run - Tests module in local environment when manually triggered Test-ModuleLocal: - if: ${{ needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Build-Module.result == 'success' && !cancelled() && needs.Get-Settings.outputs.ModuleTestSuites != '[]' }} needs: - Build-Module - Get-Settings @@ -246,6 +320,11 @@ jobs: Version: ${{ inputs.Version }} WorkingDirectory: ${{ inputs.WorkingDirectory }} + # Runs on: + # - ✅ Open/Updated PR - Runs teardown scripts after local module tests + # - ✅ Merged PR - Runs teardown scripts after local module tests + # - ✅ Abandoned PR - Runs teardown if tests were started (cleanup) + # - ✅ Manual run - Runs teardown scripts after local module tests AfterAll-ModuleLocal: if: ${{ needs.Test-ModuleLocal.result != 'skipped' && always() }} name: AfterAll-ModuleLocal @@ -255,9 +334,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 - - - name: Install-PSModuleHelpers - uses: PSModule/Install-PSModuleHelpers@v1 + with: + persist-credentials: false + fetch-depth: 0 - name: Run AfterAll Teardown Scripts if: always() @@ -290,8 +369,13 @@ jobs: } } + # Runs on: + # - ✅ Open/Updated PR - Collects and reports test results + # - ✅ Merged PR - Collects and reports test results before publishing + # - ❌ Abandoned PR - Skips collecting results for abandoned changes + # - ✅ Manual run - Collects and reports test results when manually triggered Get-TestResults: - if: needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.TestResults.Skip && (needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' || needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.TestResults.Skip && (needs.Get-Settings.outputs.SourceCodeTestSuites != '[]' || needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) }} needs: - Get-Settings - Test-SourceCode @@ -309,8 +393,13 @@ jobs: Verbose: ${{ inputs.Verbose }} Version: ${{ inputs.Version }} + # Runs on: + # - ✅ Open/Updated PR - Calculates and reports code coverage + # - ✅ Merged PR - Calculates and reports code coverage before publishing + # - ❌ Abandoned PR - Skips coverage for abandoned changes + # - ✅ Manual run - Calculates and reports code coverage when manually triggered Get-CodeCoverage: - if: needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.CodeCoverage.Skip && (needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Get-Settings.result == 'success' && !fromJson(needs.Get-Settings.outputs.Settings).Test.CodeCoverage.Skip && (needs.Get-Settings.outputs.PSModuleTestSuites != '[]' || needs.Get-Settings.outputs.ModuleTestSuites != '[]') && (always() && !cancelled()) }} needs: - Get-Settings - Test-Module @@ -325,8 +414,13 @@ jobs: Verbose: ${{ inputs.Verbose }} Version: ${{ inputs.Version }} + # Runs on: + # - ❌ Open/Updated PR - Site not published for PRs in progress + # - ✅ Merged PR - Deploys site to GitHub Pages after successful merge + # - ❌ Abandoned PR - Site not published for abandoned changes + # - ❌ Manual run - Only publishes on merged PRs, not manual runs Publish-Site: - if: ${{ needs.Get-TestResults.result == 'success' && needs.Get-CodeCoverage.result == 'success' && needs.Build-Site.result == 'success' && !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.merged == true }} + if: ${{ !(github.event.action == 'closed' && github.event.pull_request.merged != true) && needs.Get-TestResults.result == 'success' && needs.Get-CodeCoverage.result == 'success' && needs.Build-Site.result == 'success' && !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.merged == true }} needs: - Get-Settings - Get-TestResults @@ -346,8 +440,18 @@ jobs: id: deployment uses: actions/deploy-pages@v4 + # Runs on: + # - ✅ Open/Updated PR - Publishes prerelease when all tests/coverage/build succeed + # - ✅ Merged PR - Publishes release when all tests/coverage/build succeed + # - ✅ Abandoned PR - Publishes cleanup/retraction version + # - ✅ Manual run - Publishes when all tests/coverage/build succeed + # Publish-Module: - if: ${{ needs.Get-Settings.result == 'success' && needs.Get-TestResults.result == 'success' && needs.Get-CodeCoverage.result == 'success' && needs.Build-Site.result == 'success' && !cancelled() && github.event_name == 'pull_request' }} + if: | + needs.Get-Settings.result == 'success' && !cancelled() && github.event_name == 'pull_request' && ( + (github.event.action == 'closed' && github.event.pull_request.merged != true) || + (needs.Get-TestResults.result == 'success' && needs.Get-CodeCoverage.result == 'success' && needs.Build-Site.result == 'success') + ) needs: - Get-Settings - Get-TestResults @@ -357,6 +461,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v5 + with: + persist-credentials: false + fetch-depth: 0 - name: Download module artifact uses: actions/download-artifact@v5 diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index d3ebb533..4bc59ab8 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -622,6 +622,8 @@ jobs: - 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 +- **Git credential handling**: Workflows MUST use `persist-credentials: false` for checkout actions to prevent credential leakage +- **Repository depth**: Workflows SHOULD use `fetch-depth: 0` for full git history when needed for versioning or changelog generation ### Documentation @@ -641,9 +643,9 @@ jobs: - `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 +- **CI validation workflows** (`.github/workflows/Workflow-Test-*.yml`) MUST be maintained for integration testing +- **Unified production workflow** (`.github/workflows/workflow.yml`) is the primary consumer-facing workflow +- Consuming repositories SHOULD use CI validation workflows for nightly regression testing ## Development Workflow @@ -690,9 +692,12 @@ The standard execution order for Process-PSModule workflows MUST be: **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 +- **Unified Production Workflow** (`.github/workflows/workflow.yml`) - Single workflow handling both CI and CD for consuming repositories + - Intelligently executes appropriate jobs based on PR state (open/merged/abandoned) + - Eliminates need for separate CI and release workflows + - Uses conditional execution to optimize for different scenarios +- **CI Validation Workflows** (`.github/workflows/Workflow-Test-*.yml`) - Integration tests for framework development +- Consuming repositories use the unified production workflow for all scenarios ### Configuration @@ -745,4 +750,4 @@ For agent-specific runtime development guidance **when developing the framework* **For Consuming Repositories**: Follow the Required Module Structure and Workflow Integration Requirements documented in the Product Overview section. Start with [Template-PSModule](https://github.com/PSModule/Template-PSModule). -**Version**: 1.6.0 | **Ratified**: TODO(RATIFICATION_DATE) | **Last Amended**: 2025-10-01 +**Version**: 1.6.1 | **Ratified**: TODO(RATIFICATION_DATE) | **Last Amended**: 2025-10-03 diff --git a/README.md b/README.md index 207228f5..f912c330 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Depending on the labels in the pull requests, the workflow will result in differ - Publishes the module to the PowerShell Gallery. - Creates a release on the GitHub repository. +## Usage + To use the workflow, create a new file in the `.github/workflows` directory of the module repository and add the following content.
@@ -104,6 +106,116 @@ jobs: ```
+### Inputs + +| Name | Type | Description | Required | Default | +| ---- | ---- | ----------- | -------- | ------- | +| `Name` | `string` | The name of the module to process. This defaults to the repository name if nothing is specified. | `false` | N/A | +| `SettingsPath` | `string` | The path to the settings file. Settings in the settings file take precedence over the action inputs. | `false` | `.github/PSModule.yml` | +| `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` | +| `Debug` | `boolean` | Whether to enable debug output. Adds a `debug` step to every job. | `false` | `false` | +| `Verbose` | `boolean` | Whether to enable verbose output. | `false` | `false` | +| `WorkingDirectory` | `string` | The path to the root of the repo. | `false` | `.` | + +### Setup and Teardown Scripts + +The workflow supports automatic execution of setup and teardown scripts for module tests: + +- Scripts are automatically detected and executed if present +- If no scripts are found, the workflow continues normally + +#### Setup - `BeforeAll.ps1` + +- Place in your test directories (`tests/BeforeAll.ps1`) +- Runs once before all test matrix jobs to prepare the test environment +- Deploy test infrastructure, download test data, initialize databases, or configure services +- Has access to the same environment variables as your tests (secrets, GitHub token, etc.) + +##### Example - `BeforeAll.ps1` + +```powershell +Write-Host "Setting up test environment..." +# Deploy test infrastructure +# Download test data +# Initialize test databases +Write-Host "Test environment ready!" +``` + +#### Teardown - `AfterAll.ps1` + +- Place in your test directories (`tests/AfterAll.ps1`) +- Runs once after all test matrix jobs complete to clean up the test environment +- Remove test resources, clean up databases, stop services, or upload artifacts +- Has access to the same environment variables as your tests + +##### Example - `AfterAll.ps1` + +```powershell +Write-Host "Cleaning up test environment..." +# Remove test resources +# Cleanup databases +# Stop services +Write-Host "Cleanup completed!" +``` + +### Secrets + +The following secrets are used by the workflow. They can be automatically provided (if available) by setting the `secrets: inherit` +in the workflow file. + +| Name | Location | Description | Default | +| ---- | -------- | ----------- | ------- | +| `APIKEY` | GitHub secrets | The API key for the PowerShell Gallery. | N/A | +| `TEST_APP_ENT_CLIENT_ID` | GitHub secrets | The client ID of an Enterprise GitHub App for running tests. | N/A | +| `TEST_APP_ENT_PRIVATE_KEY` | GitHub secrets | The private key of an Enterprise GitHub App for running tests. | N/A | +| `TEST_APP_ORG_CLIENT_ID` | GitHub secrets | The client ID of an Organization GitHub App for running tests. | N/A | +| `TEST_APP_ORG_PRIVATE_KEY` | GitHub secrets | The private key of an Organization GitHub App for running tests. | N/A | +| `TEST_USER_ORG_FG_PAT` | GitHub secrets | The fine-grained personal access token with org access for running tests. | N/A | +| `TEST_USER_USER_FG_PAT` | GitHub secrets | The fine-grained personal access token with user account access for running tests. | N/A | +| `TEST_USER_PAT` | GitHub secrets | The classic personal access token for running tests. | N/A | + +### Permissions + +The following permissions are needed for the workflow to be able to perform all tasks. + +```yaml +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](https://github.com/marketplace/actions/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 | +|-----|-----------------|-----------|--------------|------------| +| **Get-Settings** | ✅ 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 +\*\*\* Publishes cleanup/retraction version + ## Configuration The workflow is configured using a settings file in the module repository. @@ -253,103 +365,9 @@ Build: Skip: true ``` -## Usage - -### Inputs - -| Name | Type | Description | Required | Default | -| ---- | ---- | ----------- | -------- | ------- | -| `Name` | `string` | The name of the module to process. This defaults to the repository name if nothing is specified. | `false` | N/A | -| `SettingsPath` | `string` | The path to the settings file. Settings in the settings file take precedence over the action inputs. | `false` | `.github/PSModule.yml` | -| `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` | -| `Debug` | `boolean` | Whether to enable debug output. Adds a `debug` step to every job. | `false` | `false` | -| `Verbose` | `boolean` | Whether to enable verbose output. | `false` | `false` | -| `WorkingDirectory` | `string` | The path to the root of the repo. | `false` | `.` | - -### Setup and Teardown Scripts - -The workflow supports automatic execution of setup and teardown scripts for module tests: - -- Scripts are automatically detected and executed if present -- If no scripts are found, the workflow continues normally - -#### Setup - `BeforeAll.ps1` - -- Place in your test directories (`tests/BeforeAll.ps1`) -- Runs once before all test matrix jobs to prepare the test environment -- Deploy test infrastructure, download test data, initialize databases, or configure services -- Has access to the same environment variables as your tests (secrets, GitHub token, etc.) - -##### Example - `BeforeAll.ps1` - -```powershell -Write-Host "Setting up test environment..." -# Deploy test infrastructure -# Download test data -# Initialize test databases -Write-Host "Test environment ready!" -``` - -#### Teardown - `AfterAll.ps1` - -- Place in your test directories (`tests/AfterAll.ps1`) -- Runs once after all test matrix jobs complete to clean up the test environment -- Remove test resources, clean up databases, stop services, or upload artifacts -- Has access to the same environment variables as your tests - -##### Example - `AfterAll.ps1` - -```powershell -Write-Host "Cleaning up test environment..." -# Remove test resources -# Cleanup databases -# Stop services -Write-Host "Cleanup completed!" -``` - -### Secrets - -The following secrets are used by the workflow. They can be automatically provided (if available) by setting the `secrets: inherit` -in the workflow file. - -| Name | Location | Description | Default | -| ---- | -------- | ----------- | ------- | -| `APIKEY` | GitHub secrets | The API key for the PowerShell Gallery. | N/A | -| `TEST_APP_ENT_CLIENT_ID` | GitHub secrets | The client ID of an Enterprise GitHub App for running tests. | N/A | -| `TEST_APP_ENT_PRIVATE_KEY` | GitHub secrets | The private key of an Enterprise GitHub App for running tests. | N/A | -| `TEST_APP_ORG_CLIENT_ID` | GitHub secrets | The client ID of an Organization GitHub App for running tests. | N/A | -| `TEST_APP_ORG_PRIVATE_KEY` | GitHub secrets | The private key of an Organization GitHub App for running tests. | N/A | -| `TEST_USER_ORG_FG_PAT` | GitHub secrets | The fine-grained personal access token with org access for running tests. | N/A | -| `TEST_USER_USER_FG_PAT` | GitHub secrets | The fine-grained personal access token with user account access for running tests. | N/A | -| `TEST_USER_PAT` | GitHub secrets | The classic personal access token for running tests. | N/A | - -## Permissions - -If running the action in a restrictive mode, the following permissions need to be granted to the action: - -```yaml -permissions: - contents: write # Create releases - pull-requests: write # Create comments on the PRs - statuses: write # Update the status of the PRs from the linter -``` - -### Publishing to GitHub Pages - -To publish the documentation to GitHub Pages, the action requires the following permissions: - -```yaml -permissions: - pages: write # Deploy to Pages - id-token: write # Verify the deployment originates from an appropriate source -``` - -For more info see [Deploy GitHub Pages site](https://github.com/marketplace/actions/deploy-github-pages-site). - ## Specifications and practices -Process-PSModule follows: +The process is compatible with: - [Test-Driven Development](https://testdriven.io/test-driven-development/) using [Pester](https://pester.dev) and [PSScriptAnalyzer](https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/overview?view=ps-modules) - [GitHub Flow specifications](https://docs.github.com/en/get-started/using-github/github-flow)