From f2a0b1b4410933aa948eea70df14a2e4c7726108 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 23 Jul 2026 23:21:32 +0200 Subject: [PATCH 1/3] Rename plan stage to orchestrator Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/Plan.yml | 15 +++--- .github/workflows/workflow.yml | 91 +++++++++++++++++----------------- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/.github/workflows/Plan.yml b/.github/workflows/Plan.yml index 3fad50eb..91b2b95d 100644 --- a/.github/workflows/Plan.yml +++ b/.github/workflows/Plan.yml @@ -1,6 +1,6 @@ -name: Plan +name: Orchestrator -# The Plan job is the single decision point for the workflow. +# The Orchestrator job is the single decision point for the workflow. # It runs two steps: # 1. Get-PSModuleSettings - loads and resolves configuration # 2. Resolve-PSModuleVersion - calculates the next version from settings + PR labels @@ -42,8 +42,9 @@ on: ImportantFilePatterns: type: string description: | - Newline-separated list of regex patterns that identify important files. - Changes matching these patterns trigger build, test, and publish stages. + Newline-separated list of regex patterns that identify module-impacting files. + Changes matching these patterns trigger module build, test, and release stages. + Docs/site orchestration also includes documentation/layout defaults. When set, fully replaces the defaults (^src/ and ^README\.md$). required: false default: | @@ -53,15 +54,15 @@ on: outputs: Settings: description: The complete settings object including test suites and resolved module version. - value: ${{ jobs.Plan.outputs.Settings }} + value: ${{ jobs.Orchestrator.outputs.Settings }} permissions: contents: read # to checkout the repo pull-requests: write # to add labels / comments to PRs jobs: - Plan: - name: Plan + Orchestrator: + name: Orchestrator runs-on: ubuntu-latest outputs: Settings: ${{ steps.Enrich-Settings.outputs.Settings }} diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 099afc34..62eb43f1 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -54,8 +54,9 @@ on: ImportantFilePatterns: type: string description: | - Newline-separated list of regex patterns that identify important files. - Changes matching these patterns trigger build, test, and publish stages. + Newline-separated list of regex patterns that identify module-impacting files. + Changes matching these patterns trigger module build, test, and release stages. + Docs/site orchestration also includes documentation/layout defaults. When set, fully replaces the defaults (^src/ and ^README\.md$). required: false default: | @@ -75,7 +76,7 @@ jobs: # - ✅ Merged PR - Always runs to load configuration # - ✅ Abandoned PR - Always runs to load configuration # - ✅ Manual run - Always runs to load configuration - Plan: + Orchestrator: uses: ./.github/workflows/Plan.yml with: SettingsPath: ${{ inputs.SettingsPath }} @@ -92,12 +93,12 @@ jobs: # - ❌ Abandoned PR - No need to lint abandoned changes # - ❌ Manual run - Only runs for PR events Lint-Repository: - if: fromJson(needs.Plan.outputs.Settings).Linter.Repository.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Linter.Repository.Enabled needs: - - Plan + - Orchestrator uses: ./.github/workflows/Lint-Repository.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Builds module for testing @@ -105,12 +106,12 @@ jobs: # - ❌ Abandoned PR - Skips building abandoned changes # - ✅ Manual run - Builds module when manually triggered Build-Module: - if: fromJson(needs.Plan.outputs.Settings).Build.Module.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Build.Module.Enabled uses: ./.github/workflows/Build-Module.yml needs: - - Plan + - Orchestrator with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Tests source code changes @@ -118,12 +119,12 @@ jobs: # - ❌ Abandoned PR - Skips testing abandoned changes # - ✅ Manual run - Tests source code when manually triggered Test-SourceCode: - if: fromJson(needs.Plan.outputs.Settings).Test.SourceCode.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Test.SourceCode.Enabled needs: - - Plan + - Orchestrator uses: ./.github/workflows/Test-SourceCode.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Lints source code changes @@ -131,12 +132,12 @@ jobs: # - ❌ Abandoned PR - Skips linting abandoned changes # - ✅ Manual run - Lints source code when manually triggered Lint-SourceCode: - if: fromJson(needs.Plan.outputs.Settings).Linter.SourceCode.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Linter.SourceCode.Enabled needs: - - Plan + - Orchestrator uses: ./.github/workflows/Lint-SourceCode.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Tests built module @@ -144,13 +145,13 @@ jobs: # - ❌ Abandoned PR - Skips testing abandoned changes # - ✅ Manual run - Tests built module when manually triggered Test-Module: - if: fromJson(needs.Plan.outputs.Settings).Test.PSModule.Enabled && needs.Build-Module.result == 'success' && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.PSModule.Enabled && needs.Build-Module.result == 'success' && !cancelled() needs: - Build-Module - - Plan + - Orchestrator uses: ./.github/workflows/Test-Module.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Runs setup scripts before local module tests @@ -158,15 +159,15 @@ jobs: # - ❌ Abandoned PR - Skips setup for abandoned changes # - ✅ Manual run - Runs setup scripts when manually triggered BeforeAll-ModuleLocal: - if: fromJson(needs.Plan.outputs.Settings).Test.Module.BeforeAllEnabled && needs.Build-Module.result == 'success' && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.Module.BeforeAllEnabled && needs.Build-Module.result == 'success' && !cancelled() uses: ./.github/workflows/BeforeAll-ModuleLocal.yml secrets: TestData: ${{ secrets.TestData }} needs: - Build-Module - - Plan + - Orchestrator with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Tests module in local environment @@ -174,16 +175,16 @@ jobs: # - ❌ Abandoned PR - Skips testing abandoned changes # - ✅ Manual run - Tests module in local environment when manually triggered Test-ModuleLocal: - if: fromJson(needs.Plan.outputs.Settings).Test.Module.MainEnabled && needs.Build-Module.result == 'success' && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.Module.MainEnabled && needs.Build-Module.result == 'success' && !cancelled() needs: - Build-Module - - Plan + - Orchestrator - BeforeAll-ModuleLocal uses: ./.github/workflows/Test-ModuleLocal.yml secrets: TestData: ${{ secrets.TestData }} with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Runs teardown scripts after local module setup/tests @@ -191,16 +192,16 @@ jobs: # - ✅ Abandoned PR - Runs teardown if local module setup/tests were started (cleanup) # - ✅ Manual run - Runs teardown scripts after local module setup/tests AfterAll-ModuleLocal: - if: fromJson(needs.Plan.outputs.Settings).Test.Module.AfterAllEnabled && needs.BeforeAll-ModuleLocal.result != 'skipped' && always() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.Module.AfterAllEnabled && needs.BeforeAll-ModuleLocal.result != 'skipped' && always() uses: ./.github/workflows/AfterAll-ModuleLocal.yml secrets: TestData: ${{ secrets.TestData }} needs: - - Plan + - Orchestrator - BeforeAll-ModuleLocal - Test-ModuleLocal with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Collects and reports test results @@ -208,9 +209,9 @@ jobs: # - ❌ Abandoned PR - Skips collecting results for abandoned changes # - ✅ Manual run - Collects and reports test results when manually triggered Get-TestResults: - if: fromJson(needs.Plan.outputs.Settings).Test.TestResults.Enabled && needs.Plan.result == 'success' && always() && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.TestResults.Enabled && needs.Orchestrator.result == 'success' && always() && !cancelled() needs: - - Plan + - Orchestrator - Test-SourceCode - Lint-SourceCode - Test-Module @@ -218,7 +219,7 @@ jobs: - Test-ModuleLocal uses: ./.github/workflows/Get-TestResults.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} BeforeAllModuleLocalResult: ${{ needs.BeforeAll-ModuleLocal.result }} # Runs on: @@ -227,14 +228,14 @@ jobs: # - ❌ Abandoned PR - Skips coverage for abandoned changes # - ✅ Manual run - Calculates and reports code coverage when manually triggered Get-CodeCoverage: - if: fromJson(needs.Plan.outputs.Settings).Test.CodeCoverage.Enabled && needs.Plan.result == 'success' && always() && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Test.CodeCoverage.Enabled && needs.Orchestrator.result == 'success' && always() && !cancelled() needs: - - Plan + - Orchestrator - Test-Module - Test-ModuleLocal uses: ./.github/workflows/Get-CodeCoverage.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Only with prerelease label: publishes prerelease version @@ -242,17 +243,17 @@ jobs: # - ✅ Abandoned PR - Cleans up prereleases for the abandoned branch (no version published) # - ❌ Manual run - Only runs for PR events Publish-Module: - if: fromJson(needs.Plan.outputs.Settings).Publish.Module.Enabled && needs.Plan.result == 'success' && !cancelled() && (needs.Get-TestResults.result == 'success' || needs.Get-TestResults.result == 'skipped') && (needs.Get-CodeCoverage.result == 'success' || needs.Get-CodeCoverage.result == 'skipped') && (needs.Build-Site.result == 'success' || needs.Build-Site.result == 'skipped') + if: fromJson(needs.Orchestrator.outputs.Settings).Publish.Module.Enabled && needs.Orchestrator.result == 'success' && !cancelled() && (needs.Get-TestResults.result == 'success' || needs.Get-TestResults.result == 'skipped') && (needs.Get-CodeCoverage.result == 'success' || needs.Get-CodeCoverage.result == 'skipped') && (needs.Build-Site.result == 'success' || needs.Build-Site.result == 'skipped') uses: ./.github/workflows/Publish-Module.yml secrets: APIKey: ${{ secrets.APIKey }} needs: - - Plan + - Orchestrator - Get-TestResults - Get-CodeCoverage - Build-Site with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Builds documentation for review @@ -260,13 +261,13 @@ jobs: # - ❌ Abandoned PR - Skips building docs for abandoned changes # - ✅ Manual run - Builds documentation when manually triggered Build-Docs: - if: fromJson(needs.Plan.outputs.Settings).Build.Docs.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Build.Docs.Enabled needs: - - Plan + - Orchestrator - Build-Module uses: ./.github/workflows/Build-Docs.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ✅ Open/Updated PR - Builds site for preview @@ -274,13 +275,13 @@ jobs: # - ❌ Abandoned PR - Skips building site for abandoned changes # - ✅ Manual run - Builds site when manually triggered Build-Site: - if: fromJson(needs.Plan.outputs.Settings).Build.Site.Enabled + if: fromJson(needs.Orchestrator.outputs.Settings).Build.Site.Enabled needs: - - Plan + - Orchestrator - Build-Docs uses: ./.github/workflows/Build-Site.yml with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} # Runs on: # - ❌ Open/Updated PR - Site not published for PRs in progress @@ -288,12 +289,12 @@ jobs: # - ❌ Abandoned PR - Site not published for abandoned changes # - ❌ Manual run - Only publishes on merged PRs to default branch Publish-Site: - if: fromJson(needs.Plan.outputs.Settings).Publish.Site.Enabled && needs.Get-TestResults.result == 'success' && needs.Get-CodeCoverage.result == 'success' && needs.Build-Site.result == 'success' && !cancelled() + if: fromJson(needs.Orchestrator.outputs.Settings).Publish.Site.Enabled && (needs.Get-TestResults.result == 'success' || needs.Get-TestResults.result == 'skipped') && (needs.Get-CodeCoverage.result == 'success' || needs.Get-CodeCoverage.result == 'skipped') && needs.Build-Site.result == 'success' && !cancelled() uses: ./.github/workflows/Publish-Site.yml needs: - - Plan + - Orchestrator - Get-TestResults - Get-CodeCoverage - Build-Site with: - Settings: ${{ needs.Plan.outputs.Settings }} + Settings: ${{ needs.Orchestrator.outputs.Settings }} From 32aebf046e03d4d0228dd2ef98934f0d65b48479 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 23 Jul 2026 23:21:36 +0200 Subject: [PATCH 2/3] Route module and docs orchestration by change type Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../actions/Get-PSModuleSettings/action.yml | 5 +- .../src/Settings.schema.json | 12 +- .../actions/Get-PSModuleSettings/src/main.ps1 | 115 +++++++++++------- 3 files changed, 84 insertions(+), 48 deletions(-) diff --git a/.github/actions/Get-PSModuleSettings/action.yml b/.github/actions/Get-PSModuleSettings/action.yml index 86a49b3a..88710e09 100644 --- a/.github/actions/Get-PSModuleSettings/action.yml +++ b/.github/actions/Get-PSModuleSettings/action.yml @@ -33,8 +33,9 @@ inputs: default: ${{ github.workspace }} ImportantFilePatterns: description: | - Newline-separated list of regex patterns that identify important files. - Changes matching these patterns trigger build, test, and publish stages. + Newline-separated list of regex patterns that identify module-impacting files. + Changes matching these patterns trigger module build, test, and release stages. + Docs/site orchestration also includes documentation/layout defaults. When set, fully replaces the defaults. required: false default: | diff --git a/.github/actions/Get-PSModuleSettings/src/Settings.schema.json b/.github/actions/Get-PSModuleSettings/src/Settings.schema.json index fdbc9013..b013376c 100644 --- a/.github/actions/Get-PSModuleSettings/src/Settings.schema.json +++ b/.github/actions/Get-PSModuleSettings/src/Settings.schema.json @@ -11,7 +11,7 @@ }, "ImportantFilePatterns": { "type": "array", - "description": "Regex patterns that identify important files. Changes matching these patterns trigger build, test, and publish stages. Defaults to ['^src/', '^README\\.md$'] when not configured.", + "description": "Regex patterns that identify module-impacting files. Changes matching these patterns trigger module build, test, and release stages. Docs/site orchestration adds documentation/layout defaults on top of these patterns. Defaults to ['^src/', '^README\\.md$'] when not configured.", "items": { "type": "string", "minLength": 1 @@ -258,7 +258,15 @@ }, "HasImportantChanges": { "type": "boolean", - "description": "Indicates if important files have changed in the PR (src/**, examples/**, README.md, .github/workflows/Process-PSModule.yml)" + "description": "Backward-compatible alias for HasModuleChanges." + }, + "HasModuleChanges": { + "type": "boolean", + "description": "Indicates whether module-impacting files changed in the PR." + }, + "HasDocsChanges": { + "type": "boolean", + "description": "Indicates whether docs-impacting files changed in the PR (for example docs content, docs config, or site layout files)." }, "Run": { "type": "null", diff --git a/.github/actions/Get-PSModuleSettings/src/main.ps1 b/.github/actions/Get-PSModuleSettings/src/main.ps1 index 36aeba1a..291f43c4 100644 --- a/.github/actions/Get-PSModuleSettings/src/main.ps1 +++ b/.github/actions/Get-PSModuleSettings/src/main.ps1 @@ -255,11 +255,23 @@ LogGroup 'Calculate Job Run Conditions:' { $hasPrereleaseLabel = ($prLabels | Where-Object { $prereleaseLabels -contains $_ }).Count -gt 0 $isOpenOrLabeledPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled') - # Check if important files have changed in the PR - # Important files are determined by the configured ImportantFilePatterns setting - $hasImportantChanges = $false + # Classify changed files for orchestration decisions. + # Module-impacting files come from ImportantFilePatterns. + # Docs-impacting files include module-impacting files plus docs/layout/config defaults. + $hasModuleChanges = $false + $hasDocsChanges = $false + $modulePatterns = $settings.ImportantFilePatterns + $docsOnlyPatterns = @( + '^docs/', + '^\.github/zensical\.toml$', + '^zensical\.toml$', + '^mkdocs\.yml$', + '^docs\.(ya?ml|json|toml)$' + ) + $docsPatterns = @($modulePatterns + $docsOnlyPatterns | Select-Object -Unique) + if ($isPR -and $pullRequest.Number) { - LogGroup 'Check for Important File Changes' { + LogGroup 'Classify Changed Files' { $owner = $env:GITHUB_REPOSITORY_OWNER $repo = $env:GITHUB_REPOSITORY_NAME $prNumber = $pullRequest.Number @@ -272,29 +284,35 @@ LogGroup 'Calculate Job Run Conditions:' { Write-Host "Changed files ($($changedFiles.Count)):" $changedFiles | ForEach-Object { Write-Host " - $_" } - # Use configured important file patterns - $importantPatterns = $settings.ImportantFilePatterns - - # Check if any changed file matches an important pattern + # Check module-impacting and docs-impacting patterns independently. foreach ($file in $changedFiles) { - foreach ($pattern in $importantPatterns) { + foreach ($pattern in $modulePatterns) { + if ($file -match $pattern) { + $hasModuleChanges = $true + Write-Host "Module-impacting file changed: [$file] (matches pattern: $pattern)" + break + } + } + + foreach ($pattern in $docsPatterns) { if ($file -match $pattern) { - $hasImportantChanges = $true - Write-Host "Important file changed: [$file] (matches pattern: $pattern)" + $hasDocsChanges = $true + Write-Host "Docs-impacting file changed: [$file] (matches pattern: $pattern)" break } } - if ($hasImportantChanges) { break } + + if ($hasModuleChanges -and $hasDocsChanges) { break } } - if ($hasImportantChanges) { - Write-Host '✓ Important files have changed - build/test stages will run' + if ($hasModuleChanges) { + Write-Host '✓ Module-impacting files have changed - module build/test/release stages will run' } else { - Write-Host '✗ No important files changed - build/test stages will be skipped' + Write-Host '✗ No module-impacting files changed - module build/test/release stages will be skipped' - # Add a comment to open PRs explaining why build/test is skipped (best-effort, may fail if permissions not granted) + # Add a comment to open PRs explaining why module stages are skipped (best-effort, may fail if permissions not granted) if ($isOpenOrUpdatedPR) { - $patternRows = ($importantPatterns | ForEach-Object { + $patternRows = ($modulePatterns | ForEach-Object { $escapedPattern = $_.Replace('|', '\|') $backtickMatches = [regex]::Matches($escapedPattern, '`+') $maxRun = 0 @@ -305,15 +323,17 @@ LogGroup 'Calculate Job Run Conditions:' { "| ${codeDelimiter}${escapedPattern}${codeDelimiter} | Matches files where path matches this pattern |" }) -join "`n" $commentBody = @" -### No Significant Changes Detected +### No Module-Impacting Changes Detected -This PR does not contain changes to files that would trigger a new release: +This PR does not contain changes to files that trigger module build/test/release stages: | Pattern | Description | | :--- | :---------- | $patternRows -**Build, test, and publish stages will be skipped** for this PR. +**Module build, test, and release stages will be skipped** for this PR. + +Documentation/site stages may still run when docs/layout/config files change. If you believe this is incorrect, please verify that your changes are in the correct locations. "@ @@ -333,20 +353,21 @@ If you believe this is incorrect, please verify that your changes are in the cor } } } else { - # Not a PR event or no PR number - consider as having important changes (e.g., workflow_dispatch, schedule) - $hasImportantChanges = $true - Write-Host 'Not a PR event or missing PR number - treating as having important changes' + # Not a PR event or no PR number - run both module and docs orchestration paths. + $hasModuleChanges = $true + $hasDocsChanges = $true + Write-Host 'Not a PR event or missing PR number - treating as module/docs impacting changes' } - # Prerelease requires both: prerelease label AND important file changes + # Prerelease requires both: prerelease label AND module-impacting changes # No point creating a prerelease if only non-module files changed - $shouldPrerelease = $isOpenOrLabeledPR -and $hasPrereleaseLabel -and $hasImportantChanges + $shouldPrerelease = $isOpenOrLabeledPR -and $hasPrereleaseLabel -and $hasModuleChanges # Determine ReleaseType - what type of release to create # Values: 'Release', 'Prerelease', 'None' - # Release only happens when important files changed (actual module code/docs) - # Merged PRs without important changes should only trigger cleanup, not a new release - $releaseType = if ($isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges) { + # Release only happens when module-impacting files changed. + # Merged PRs without module-impacting changes should only trigger cleanup, not a new release. + $releaseType = if ($isMergedPR -and $isTargetDefaultBranch -and $hasModuleChanges) { 'Release' } elseif ($shouldPrerelease) { 'Prerelease' @@ -365,7 +386,9 @@ If you believe this is incorrect, please verify that your changes are in the cor hasPrereleaseLabel = $hasPrereleaseLabel shouldPrerelease = $shouldPrerelease ReleaseType = $releaseType - HasImportantChanges = $hasImportantChanges + HasImportantChanges = $hasModuleChanges + HasModuleChanges = $hasModuleChanges + HasDocsChanges = $hasDocsChanges } | Format-List | Out-String } @@ -545,10 +568,10 @@ LogGroup 'Calculate Job Run Conditions:' { $settings.Publish.Module | Add-Member -MemberType NoteProperty -Name ReleaseType -Value $releaseType -Force $settings.Publish.Module.AutoCleanup = $shouldAutoCleanup - # For open PRs, we only want to run build/test stages if important files changed. - # For merged PRs, workflow_dispatch, schedule - $hasImportantChanges is already true. - # Note: $shouldPrerelease already requires $hasImportantChanges, so no separate check needed. - $shouldRunBuildTest = $isNotAbandonedPR -and $hasImportantChanges + # Route execution by change classification. + $shouldRunModulePipeline = $isNotAbandonedPR -and $hasModuleChanges + $shouldRunDocsPipeline = $isNotAbandonedPR -and $hasDocsChanges + $docsNeedModuleBuild = $shouldRunDocsPipeline -and (-not $settings.Build.Docs.Skip) # Check if setup/teardown scripts exist in the repository $hasBeforeAllScript = Test-Path -Path 'tests/BeforeAll.ps1' @@ -557,9 +580,9 @@ LogGroup 'Calculate Job Run Conditions:' { Write-Host " tests/BeforeAll.ps1 exists: $hasBeforeAllScript" Write-Host " tests/AfterAll.ps1 exists: $hasAfterAllScript" - $sourceCodeEnabled = $shouldRunBuildTest -and ($null -ne $settings.Test.SourceCode.Suites) - $psModuleEnabled = $shouldRunBuildTest -and ($null -ne $settings.Test.PSModule.Suites) - $moduleLocalEnabled = $shouldRunBuildTest -and ($null -ne $settings.Test.Module.Suites) + $sourceCodeEnabled = $shouldRunModulePipeline -and ($null -ne $settings.Test.SourceCode.Suites) + $psModuleEnabled = $shouldRunModulePipeline -and ($null -ne $settings.Test.PSModule.Suites) + $moduleLocalEnabled = $shouldRunModulePipeline -and ($null -ne $settings.Test.Module.Suites) $beforeAllEnabled = $moduleLocalEnabled -and $hasBeforeAllScript $afterAllEnabled = $moduleLocalEnabled -and $hasAfterAllScript @@ -575,12 +598,12 @@ LogGroup 'Calculate Job Run Conditions:' { $settings.Build.Module | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Build.Module.Skip) -Force $settings.Build.Module | Add-Member -MemberType NoteProperty -Name Enabled -Value ( - $shouldRunBuildTest -and (-not $settings.Build.Module.Skip) + (-not $settings.Build.Module.Skip) -and ($shouldRunModulePipeline -or $docsNeedModuleBuild) ) -Force $settings.Build.Docs | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Build.Docs.Skip) -Force - $settings.Build.Docs | Add-Member -MemberType NoteProperty -Name Enabled -Value ($shouldRunBuildTest -and (-not $settings.Build.Docs.Skip)) -Force + $settings.Build.Docs | Add-Member -MemberType NoteProperty -Name Enabled -Value ($shouldRunDocsPipeline -and (-not $settings.Build.Docs.Skip)) -Force $settings.Build.Site | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Build.Site.Skip) -Force - $settings.Build.Site | Add-Member -MemberType NoteProperty -Name Enabled -Value ($shouldRunBuildTest -and (-not $settings.Build.Site.Skip)) -Force + $settings.Build.Site | Add-Member -MemberType NoteProperty -Name Enabled -Value ($shouldRunDocsPipeline -and (-not $settings.Build.Site.Skip)) -Force $settings.Test.SourceCode | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Test.SourceCode.Skip) -Force $settings.Test.SourceCode | Add-Member -MemberType NoteProperty -Name Enabled -Value $sourceCodeEnabled -Force @@ -593,13 +616,13 @@ LogGroup 'Calculate Job Run Conditions:' { $settings.Test.TestResults | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Test.TestResults.Skip) -Force $settings.Test.TestResults | Add-Member -MemberType NoteProperty -Name Enabled -Value ( - $shouldRunBuildTest -and (-not $settings.Test.TestResults.Skip) -and ( + $shouldRunModulePipeline -and (-not $settings.Test.TestResults.Skip) -and ( ($null -ne $settings.Test.SourceCode.Suites) -or ($null -ne $settings.Test.PSModule.Suites) -or ($null -ne $settings.Test.Module.Suites) ) ) -Force $settings.Test.CodeCoverage | Add-Member -MemberType NoteProperty -Name Desired -Value (-not $settings.Test.CodeCoverage.Skip) -Force $settings.Test.CodeCoverage | Add-Member -MemberType NoteProperty -Name Enabled -Value ( - $shouldRunBuildTest -and (-not $settings.Test.CodeCoverage.Skip) -and ( + $shouldRunModulePipeline -and (-not $settings.Test.CodeCoverage.Skip) -and ( ($null -ne $settings.Test.PSModule.Suites) -or ($null -ne $settings.Test.Module.Suites) ) ) -Force @@ -607,14 +630,18 @@ LogGroup 'Calculate Job Run Conditions:' { $settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Desired -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force $settings.Publish.Module | Add-Member -MemberType NoteProperty -Name Enabled -Value (($releaseType -ne 'None') -or $shouldAutoCleanup) -Force $settings.Publish | Add-Member -MemberType NoteProperty -Name Site -Value ([pscustomobject]@{ - Desired = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges - Enabled = $isMergedPR -and $isTargetDefaultBranch -and $hasImportantChanges + Desired = $isMergedPR -and $isTargetDefaultBranch -and $hasDocsChanges + Enabled = $isMergedPR -and $isTargetDefaultBranch -and $hasDocsChanges }) -Force - $settings | Add-Member -MemberType NoteProperty -Name HasImportantChanges -Value $hasImportantChanges + $settings | Add-Member -MemberType NoteProperty -Name HasImportantChanges -Value $hasModuleChanges -Force + $settings | Add-Member -MemberType NoteProperty -Name HasModuleChanges -Value $hasModuleChanges -Force + $settings | Add-Member -MemberType NoteProperty -Name HasDocsChanges -Value $hasDocsChanges -Force Write-Host 'Phase execution state:' [pscustomobject]@{ + HasModuleChanges = $settings.HasModuleChanges + HasDocsChanges = $settings.HasDocsChanges LintRepository = $settings.Linter.Repository.Enabled BuildModule = $settings.Build.Module.Enabled TestSourceCode = $settings.Test.SourceCode.Enabled From 4b4fb204cb8c5fbf14d278cf91f88440fcd52e76 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Thu, 23 Jul 2026 23:50:52 +0200 Subject: [PATCH 3/3] Add separate code/docs important change patterns Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../actions/Get-PSModuleSettings/action.yml | 10 ++- .../src/Settings.schema.json | 43 +++++++++-- .../actions/Get-PSModuleSettings/src/main.ps1 | 72 +++++++++++++------ .github/workflows/Plan.yml | 10 ++- .github/workflows/workflow.yml | 10 ++- 5 files changed, 100 insertions(+), 45 deletions(-) diff --git a/.github/actions/Get-PSModuleSettings/action.yml b/.github/actions/Get-PSModuleSettings/action.yml index 88710e09..0adad706 100644 --- a/.github/actions/Get-PSModuleSettings/action.yml +++ b/.github/actions/Get-PSModuleSettings/action.yml @@ -33,14 +33,12 @@ inputs: default: ${{ github.workspace }} ImportantFilePatterns: description: | - Newline-separated list of regex patterns that identify module-impacting files. - Changes matching these patterns trigger module build, test, and release stages. - Docs/site orchestration also includes documentation/layout defaults. - When set, fully replaces the defaults. + Newline-separated list of regex patterns for module-impacting/code changes. + This input configures the Code pattern list only. + Docs patterns are configured in settings via ImportantFilePatterns.Docs. required: false default: | - ^src/ - ^README\.md$ + ^src/(?!.*\.md$).* outputs: Settings: diff --git a/.github/actions/Get-PSModuleSettings/src/Settings.schema.json b/.github/actions/Get-PSModuleSettings/src/Settings.schema.json index b013376c..5acd13e0 100644 --- a/.github/actions/Get-PSModuleSettings/src/Settings.schema.json +++ b/.github/actions/Get-PSModuleSettings/src/Settings.schema.json @@ -10,12 +10,20 @@ "description": "The name of the module" }, "ImportantFilePatterns": { - "type": "array", - "description": "Regex patterns that identify module-impacting files. Changes matching these patterns trigger module build, test, and release stages. Docs/site orchestration adds documentation/layout defaults on top of these patterns. Defaults to ['^src/', '^README\\.md$'] when not configured.", - "items": { - "type": "string", - "minLength": 1 - } + "description": "Patterns that classify important code/docs changes for orchestration.", + "oneOf": [ + { + "type": "array", + "description": "Backward-compatible format. Treated as code patterns.", + "items": { + "type": "string", + "minLength": 1 + } + }, + { + "$ref": "#/definitions/importantFilePatternGroups" + } + ] }, "Test": { "type": "object", @@ -347,6 +355,29 @@ "TestPath", "TestName" ] + }, + "importantFilePatternGroups": { + "type": "object", + "description": "Pattern groups used by the orchestrator.", + "properties": { + "Code": { + "type": "array", + "description": "Regex patterns for module-impacting/code changes.", + "items": { + "type": "string", + "minLength": 1 + } + }, + "Docs": { + "type": "array", + "description": "Regex patterns for docs/layout/config changes.", + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": false } } } diff --git a/.github/actions/Get-PSModuleSettings/src/main.ps1 b/.github/actions/Get-PSModuleSettings/src/main.ps1 index 291f43c4..fbf4b06f 100644 --- a/.github/actions/Get-PSModuleSettings/src/main.ps1 +++ b/.github/actions/Get-PSModuleSettings/src/main.ps1 @@ -91,20 +91,49 @@ LogGroup 'Name' { } LogGroup 'ImportantFilePatterns' { - $defaultImportantFilePatterns = @('^src/', '^README\.md$') + $defaultCodePatterns = @( + '^src/(?!.*\.md$).*' + ) + $defaultDocsPatterns = @( + '^README\.md$', + '^src/.*\.md$', + '^docs/', + '^\.github/zensical\.toml$', + '^zensical\.toml$', + '^mkdocs\.yml$', + '^docs\.(ya?ml|json|toml)$' + ) + if ($null -ne $settings.ImportantFilePatterns) { - $importantFilePatterns = @($settings.ImportantFilePatterns) - Write-Host "Using ImportantFilePatterns from settings file: [$($importantFilePatterns -join ', ')]" + $configuredCodePatterns = @($settings.ImportantFilePatterns.Code | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + $configuredDocsPatterns = @($settings.ImportantFilePatterns.Docs | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) + if (($configuredCodePatterns.Count -gt 0) -or ($configuredDocsPatterns.Count -gt 0)) { + $codeFilePatterns = if ($configuredCodePatterns.Count -gt 0) { $configuredCodePatterns } else { $defaultCodePatterns } + $docsFilePatterns = if ($configuredDocsPatterns.Count -gt 0) { $configuredDocsPatterns } else { $defaultDocsPatterns } + Write-Host "Using code patterns from ImportantFilePatterns.Code: [$($codeFilePatterns -join ', ')]" + Write-Host "Using docs patterns from ImportantFilePatterns.Docs: [$($docsFilePatterns -join ', ')]" + } else { + # Backward-compatible format: array is treated as code patterns. + $codeFilePatterns = @($settings.ImportantFilePatterns) + $docsFilePatterns = $defaultDocsPatterns + Write-Host "Using code patterns from legacy ImportantFilePatterns array: [$($codeFilePatterns -join ', ')]" + Write-Host "Using default docs patterns: [$($docsFilePatterns -join ', ')]" + } } elseif (-not [string]::IsNullOrWhiteSpace($importantFilePatternsInput)) { - $importantFilePatterns = @($importantFilePatternsInput -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ }) - Write-Host "Using ImportantFilePatterns from action input: [$($importantFilePatterns -join ', ')]" + # Action input remains code-focused for backward compatibility. + $codeFilePatterns = @($importantFilePatternsInput -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ }) + $docsFilePatterns = $defaultDocsPatterns + Write-Host "Using code patterns from action input: [$($codeFilePatterns -join ', ')]" + Write-Host "Using default docs patterns: [$($docsFilePatterns -join ', ')]" } else { - $importantFilePatterns = $defaultImportantFilePatterns - Write-Host "Using default ImportantFilePatterns: [$($importantFilePatterns -join ', ')]" + $codeFilePatterns = $defaultCodePatterns + $docsFilePatterns = $defaultDocsPatterns + Write-Host "Using default code patterns: [$($codeFilePatterns -join ', ')]" + Write-Host "Using default docs patterns: [$($docsFilePatterns -join ', ')]" } - # Validate that all patterns are valid regular expressions - foreach ($pattern in $importantFilePatterns) { + # Validate that all patterns are valid regular expressions. + foreach ($pattern in @($codeFilePatterns + $docsFilePatterns | Select-Object -Unique)) { try { [void][regex]::new($pattern) } catch { @@ -115,7 +144,10 @@ LogGroup 'ImportantFilePatterns' { $settings = [pscustomobject]@{ Name = $name - ImportantFilePatterns = $importantFilePatterns + ImportantFilePatterns = [pscustomobject]@{ + Code = $codeFilePatterns + Docs = $docsFilePatterns + } Test = [pscustomobject]@{ Skip = $settings.Test.Skip ?? $false Linux = [pscustomobject]@{ @@ -256,19 +288,12 @@ LogGroup 'Calculate Job Run Conditions:' { $isOpenOrLabeledPR = $isPR -and $pullRequestAction -in @('opened', 'reopened', 'synchronize', 'labeled') # Classify changed files for orchestration decisions. - # Module-impacting files come from ImportantFilePatterns. - # Docs-impacting files include module-impacting files plus docs/layout/config defaults. + # Module-impacting files come from ImportantFilePatterns.Code. + # Docs-impacting files come from ImportantFilePatterns.Docs and always include module changes. $hasModuleChanges = $false $hasDocsChanges = $false - $modulePatterns = $settings.ImportantFilePatterns - $docsOnlyPatterns = @( - '^docs/', - '^\.github/zensical\.toml$', - '^zensical\.toml$', - '^mkdocs\.yml$', - '^docs\.(ya?ml|json|toml)$' - ) - $docsPatterns = @($modulePatterns + $docsOnlyPatterns | Select-Object -Unique) + $modulePatterns = @($settings.ImportantFilePatterns.Code) + $docsPatterns = @($settings.ImportantFilePatterns.Docs) if ($isPR -and $pullRequest.Number) { LogGroup 'Classify Changed Files' { @@ -305,6 +330,11 @@ LogGroup 'Calculate Job Run Conditions:' { if ($hasModuleChanges -and $hasDocsChanges) { break } } + # Source-code changes are docs-impacting because generated docs depend on code. + if ($hasModuleChanges) { + $hasDocsChanges = $true + } + if ($hasModuleChanges) { Write-Host '✓ Module-impacting files have changed - module build/test/release stages will run' } else { diff --git a/.github/workflows/Plan.yml b/.github/workflows/Plan.yml index 91b2b95d..d9c3fe72 100644 --- a/.github/workflows/Plan.yml +++ b/.github/workflows/Plan.yml @@ -42,14 +42,12 @@ on: ImportantFilePatterns: type: string description: | - Newline-separated list of regex patterns that identify module-impacting files. - Changes matching these patterns trigger module build, test, and release stages. - Docs/site orchestration also includes documentation/layout defaults. - When set, fully replaces the defaults (^src/ and ^README\.md$). + Newline-separated list of regex patterns for module-impacting/code changes. + This input configures the Code pattern list only. + Docs patterns are configured in .github/PSModule.yml via ImportantFilePatterns.Docs. required: false default: | - ^src/ - ^README\.md$ + ^src/(?!.*\.md$).* outputs: Settings: diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 62eb43f1..75d35bdb 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -54,14 +54,12 @@ on: ImportantFilePatterns: type: string description: | - Newline-separated list of regex patterns that identify module-impacting files. - Changes matching these patterns trigger module build, test, and release stages. - Docs/site orchestration also includes documentation/layout defaults. - When set, fully replaces the defaults (^src/ and ^README\.md$). + Newline-separated list of regex patterns for module-impacting/code changes. + This input configures the Code pattern list only. + Docs patterns are configured in .github/PSModule.yml via ImportantFilePatterns.Docs. required: false default: | - ^src/ - ^README\.md$ + ^src/(?!.*\.md$).* permissions: contents: write # to checkout the repo and create releases on the repo