From 6ff3218550cfd958620a74986281c46937966f2a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 12:54:01 +0200 Subject: [PATCH 01/26] Add Process workflow inventory tooling Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 658 ++++++++++++++++++ ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 131 ++++ 2 files changed, 789 insertions(+) create mode 100644 .github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 create mode 100644 .github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 new file mode 100644 index 00000000..087c82aa --- /dev/null +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -0,0 +1,658 @@ +#Requires -Modules powershell-yaml + +<# + .SYNOPSIS + Inventories caller workflows that use the Process-PSModule reusable workflow. + + .DESCRIPTION + Discovers Process-PSModule caller workflows from either the authenticated GitHub + organization or local Git checkouts. Outputs structured objects and can refresh + JSON and Markdown reports. + + GitHub mode uses the GitHub CLI. Authenticate with GH_TOKEN or gh auth login. + Local mode accepts repository paths or parent directories containing repositories. + + .EXAMPLE + ./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` + -Organization PSModule ` + -JsonPath ./output/process-workflows.json ` + -MarkdownPath ./output/process-workflows.md + + Inventories the PSModule organization through the GitHub API and writes both report formats. + + .EXAMPLE + ./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` + -Path C:\Repos, C:\Users\me\.copilot\repos ` + -MarkdownPath ./output/process-workflows.md + + Recursively discovers local Git repositories below the supplied paths. +#> +[CmdletBinding(DefaultParameterSetName = 'GitHub')] +param( + [Parameter(ParameterSetName = 'GitHub')] + [ValidateNotNullOrEmpty()] + [string] $Organization = 'PSModule', + + [Parameter(ParameterSetName = 'GitHub')] + [ValidateNotNullOrEmpty()] + [string[]] $Repository, + + [Parameter(Mandatory, ParameterSetName = 'Local')] + [ValidateNotNullOrEmpty()] + [string[]] $Path, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $WorkflowReference = 'PSModule/Process-PSModule/.github/workflows/workflow.yml', + + [Parameter()] + [string] $JsonPath, + + [Parameter()] + [string] $MarkdownPath, + + [Parameter(ParameterSetName = 'GitHub')] + [switch] $IncludeArchived +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +function Invoke-GhCommand { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string[]] $ArgumentList + ) + + $output = (& gh @ArgumentList 2>&1) -join "`n" + if ($LASTEXITCODE -eq 0) { + return $output + } + + throw "gh $($ArgumentList -join ' ') failed:`n$output" +} + +function ConvertFrom-JsonResponse { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string] $Content + ) + + if ([string]::IsNullOrWhiteSpace($Content)) { + return @() + } + + @($Content | ConvertFrom-Json -Depth 100) +} + +function Get-GitHubRepository { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $Owner, + + [Parameter()] + [string[]] $NameWithOwner, + + [Parameter()] + [switch] $IncludeArchivedRepository + ) + + if ($NameWithOwner) { + $repositories = foreach ($name in $NameWithOwner) { + $fullName = if ($name.Contains('/')) { $name } else { "$Owner/$name" } + $response = Invoke-GhCommand -ArgumentList @( + 'repo', 'view', $fullName, + '--json', 'nameWithOwner,defaultBranchRef,isArchived,url' + ) + ConvertFrom-JsonResponse -Content $response + } + } else { + $response = Invoke-GhCommand -ArgumentList @( + 'repo', 'list', $Owner, + '--limit', '1000', + '--json', 'nameWithOwner,defaultBranchRef,isArchived,url' + ) + $repositories = ConvertFrom-JsonResponse -Content $response + } + + @($repositories | + Where-Object { $IncludeArchivedRepository -or -not $_.isArchived } | + Sort-Object nameWithOwner) +} + +function Get-GitHubMatchingWorkflowFile { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [psobject[]] $RepositoryInfo, + + [Parameter(Mandatory)] + [string] $Owner, + + [Parameter(Mandatory)] + [string] $ExpectedReference + ) + + $query = "$ExpectedReference org:$Owner path:.github/workflows" + $response = Invoke-GhCommand -ArgumentList @( + 'api', + '--paginate', + '--slurp', + '-X', + 'GET', + 'search/code', + '-f', + "q=$query", + '-f', + 'per_page=100' + ) + $pages = @($response | ConvertFrom-Json -Depth 100) + $searchResults = @($pages | ForEach-Object { $_.items }) + if (-not $searchResults) { + throw "GitHub code search returned no matches for [$query]." + } + + $repositoryByName = @{} + foreach ($item in $RepositoryInfo) { + $repositoryByName[$item.nameWithOwner] = $item + } + + @($searchResults | + Where-Object { $repositoryByName.ContainsKey($_.repository.full_name) } | + Sort-Object { $_.repository.full_name }, path -Unique | + ForEach-Object { + $match = $_ + $repository = $repositoryByName[$match.repository.full_name] + $branch = [uri]::EscapeDataString($repository.defaultBranchRef.name) + [pscustomobject]@{ + Repository = $repository.nameWithOwner + DefaultBranch = $repository.defaultBranchRef.name + Archived = $repository.isArchived + RepositoryUrl = $repository.url + WorkflowPath = $match.path + WorkflowUrl = $match.html_url + SearchQuery = $query + Content = Invoke-GhCommand -ArgumentList @( + 'api', + "repos/$($repository.nameWithOwner)/contents/$($match.path)?ref=$branch", + '-H', + 'Accept: application/vnd.github.raw+json' + ) + } + }) +} + +function Get-LocalRepositoryRoot { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string[]] $InputPath + ) + + $roots = foreach ($candidate in $InputPath) { + $resolvedPath = (Resolve-Path -LiteralPath $candidate).Path + if (Test-Path -LiteralPath (Join-Path $resolvedPath '.git')) { + $resolvedPath + continue + } + + Get-ChildItem -LiteralPath $resolvedPath -Filter '.git' -Force -Recurse | + ForEach-Object { + if ($_.PSIsContainer) { + $_.Parent.FullName + } else { + $_.DirectoryName + } + } + } + + @($roots | Sort-Object -Unique) +} + +function Get-LocalRepositoryName { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $RepositoryRoot + ) + + $remote = (& git -C $RepositoryRoot config --get remote.origin.url 2>$null) -join '' + if ($LASTEXITCODE -eq 0 -and $remote -match '(?[^/:]+)/(?[^/]+?)(?:\.git)?$') { + return "$($Matches.owner)/$($Matches.repo)" + } + + Split-Path -Path $RepositoryRoot -Leaf +} + +function Get-LocalDefaultBranch { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $RepositoryRoot + ) + + $originHead = (& git -C $RepositoryRoot symbolic-ref refs/remotes/origin/HEAD --short 2>$null) -join '' + if ($LASTEXITCODE -eq 0 -and $originHead) { + return $originHead -replace '^origin/', '' + } + + $branch = (& git -C $RepositoryRoot branch --show-current 2>$null) -join '' + if ($LASTEXITCODE -eq 0 -and $branch) { + return $branch + } + + $null +} + +function Get-LocalWorkflowFile { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string[]] $InputPath + ) + + foreach ($repositoryRoot in Get-LocalRepositoryRoot -InputPath $InputPath) { + $workflowRoot = Join-Path $repositoryRoot '.github/workflows' + if (-not (Test-Path -LiteralPath $workflowRoot -PathType Container)) { + continue + } + + $repositoryName = Get-LocalRepositoryName -RepositoryRoot $repositoryRoot + $defaultBranch = Get-LocalDefaultBranch -RepositoryRoot $repositoryRoot + Get-ChildItem -LiteralPath $workflowRoot -File | + Where-Object { $_.Extension -in @('.yml', '.yaml') } | + ForEach-Object { + [pscustomobject]@{ + Repository = $repositoryName + DefaultBranch = $defaultBranch + Archived = $false + RepositoryUrl = $null + WorkflowPath = [IO.Path]::GetRelativePath($repositoryRoot, $_.FullName).Replace('\', '/') + WorkflowUrl = $null + SearchQuery = $null + Content = Get-Content -LiteralPath $_.FullName -Raw + } + } + } +} + +function Get-MapKey { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Map + ) + + if ($null -eq $Map) { + return @() + } + + if ($Map -is [Collections.IDictionary]) { + return @($Map.Keys | ForEach-Object { "$_" }) + } + + @($Map.PSObject.Properties.Name) +} + +function Get-MapValue { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Map, + + [Parameter(Mandatory)] + [string] $Name + ) + + if ($null -eq $Map) { + return $null + } + + if ($Map -is [Collections.IDictionary]) { + return $Map[$Name] + } + + $Map.PSObject.Properties[$Name].Value +} + +function ConvertTo-StringMap { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Map + ) + + $result = [ordered]@{} + foreach ($key in Get-MapKey -Map $Map) { + $value = Get-MapValue -Map $Map -Name $key + $result[$key] = if ($null -eq $value) { $null } else { "$value" } + } + $result +} + +function ConvertTo-StringArray { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Value + ) + + if ($null -eq $Value) { + return @() + } + + @($Value | ForEach-Object { "$_" }) +} + +function Get-WorkflowInventoryItem { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [psobject] $WorkflowFile, + + [Parameter(Mandatory)] + [string] $ExpectedReference + ) + + if ($WorkflowFile.Content -notmatch [regex]::Escape($ExpectedReference)) { + return + } + + try { + $workflow = ConvertFrom-Yaml -Yaml $WorkflowFile.Content -Ordered + } catch { + return [pscustomobject]@{ + Repository = $WorkflowFile.Repository + DefaultBranch = $WorkflowFile.DefaultBranch + Archived = $WorkflowFile.Archived + RepositoryUrl = $WorkflowFile.RepositoryUrl + WorkflowPath = $WorkflowFile.WorkflowPath + WorkflowUrl = $WorkflowFile.WorkflowUrl + SearchQuery = $WorkflowFile.SearchQuery + Status = 'ParseError' + Error = $_.Exception.Message + } + } + + $jobs = Get-MapValue -Map $workflow -Name 'jobs' + $processJobs = foreach ($jobName in Get-MapKey -Map $jobs) { + $job = Get-MapValue -Map $jobs -Name $jobName + $uses = Get-MapValue -Map $job -Name 'uses' + if ("$uses" -notlike "$ExpectedReference@*") { + continue + } + + $secrets = Get-MapValue -Map $job -Name 'secrets' + $secretMode = if ($secrets -is [string]) { "$secrets" } elseif ($null -eq $secrets) { 'none' } else { 'explicit' } + [pscustomobject]@{ + Name = $jobName + Uses = "$uses" + Reference = "$uses".Substring("$ExpectedReference@".Length) + Inputs = ConvertTo-StringMap -Map (Get-MapValue -Map $job -Name 'with') + SecretMode = $secretMode + SecretMappings = if ($secretMode -eq 'explicit') { + ConvertTo-StringMap -Map $secrets + } else { + [ordered]@{} + } + Environment = Get-MapValue -Map $job -Name 'environment' + } + } + + if (-not $processJobs) { + return + } + + $trigger = Get-MapValue -Map $workflow -Name 'on' + $pullRequest = Get-MapValue -Map $trigger -Name 'pull_request' + $push = Get-MapValue -Map $trigger -Name 'push' + $schedule = Get-MapValue -Map $trigger -Name 'schedule' + $concurrency = Get-MapValue -Map $workflow -Name 'concurrency' + $allJobNames = Get-MapKey -Map $jobs + $processJobNames = @($processJobs.Name) + + $versionComments = @( + [regex]::Matches( + $WorkflowFile.Content, + "(?m)^\s*uses:\s*$([regex]::Escape($ExpectedReference))@(?[^\s#]+)\s*(?:#\s*(?\S+))?" + ) | ForEach-Object { + [pscustomobject]@{ + Reference = $_.Groups['reference'].Value + Version = $_.Groups['version'].Value + } + } + ) + + [pscustomobject]@{ + Repository = $WorkflowFile.Repository + DefaultBranch = $WorkflowFile.DefaultBranch + Archived = $WorkflowFile.Archived + RepositoryUrl = $WorkflowFile.RepositoryUrl + WorkflowPath = $WorkflowFile.WorkflowPath + WorkflowUrl = $WorkflowFile.WorkflowUrl + SearchQuery = $WorkflowFile.SearchQuery + Status = 'Parsed' + Error = $null + WorkflowName = Get-MapValue -Map $workflow -Name 'name' + Events = @(Get-MapKey -Map $trigger | Sort-Object) + Schedules = @($schedule | ForEach-Object { Get-MapValue -Map $_ -Name 'cron' }) + PushBranches = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'branches') + PushBranchesIgnore = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'branches-ignore') + PushPaths = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'paths') + PushPathsIgnore = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'paths-ignore') + PullRequestBranches = ConvertTo-StringArray -Value (Get-MapValue -Map $pullRequest -Name 'branches') + PullRequestTypes = ConvertTo-StringArray -Value (Get-MapValue -Map $pullRequest -Name 'types') + ConcurrencyGroup = Get-MapValue -Map $concurrency -Name 'group' + CancelInProgress = Get-MapValue -Map $concurrency -Name 'cancel-in-progress' + Permissions = ConvertTo-StringMap -Map (Get-MapValue -Map $workflow -Name 'permissions') + ProcessJobs = @($processJobs) + AdditionalJobs = @($allJobNames | Where-Object { $_ -notin $processJobNames }) + VersionComments = $versionComments + } +} + +function ConvertTo-MarkdownCell { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Value + ) + + if ($null -eq $Value) { + return '' + } + + (($Value -join ', ') -replace '\|', '\|' -replace '\r?\n', '
') +} + +function ConvertTo-WorkflowInventoryMarkdown { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [psobject[]] $Inventory, + + [Parameter(Mandatory)] + [ValidateSet('GitHub', 'Local')] + [string] $Source + ) + + $parsed = @($Inventory | Where-Object Status -eq 'Parsed') + $parseErrors = @($Inventory | Where-Object Status -eq 'ParseError') + $references = @( + $parsed | + ForEach-Object { $_.ProcessJobs.Reference } | + Group-Object | + Sort-Object @{ Expression = 'Count'; Descending = $true }, Name + ) + $eventSets = @( + $parsed | + ForEach-Object { $_.Events -join ', ' } | + Group-Object | + Sort-Object @{ Expression = 'Count'; Descending = $true }, Name + ) + + $lines = [Collections.Generic.List[string]]::new() + $lines.Add('# Process-PSModule workflow inventory') + $lines.Add('') + $lines.Add("Generated: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK')") + $lines.Add('') + $lines.Add("- Source: $Source") + $lines.Add("- Workflow files: $($Inventory.Count)") + $lines.Add("- Parsed: $($parsed.Count)") + $lines.Add("- Parse errors: $($parseErrors.Count)") + $lines.Add('') + $lines.Add('## Reference distribution') + $lines.Add('') + $lines.Add('| Reference | Workflows |') + $lines.Add('| --- | ---: |') + foreach ($group in $references) { + $lines.Add("| $(ConvertTo-MarkdownCell $group.Name) | $($group.Count) |") + } + $lines.Add('') + $lines.Add('## Trigger distribution') + $lines.Add('') + $lines.Add('| Events | Workflows |') + $lines.Add('| --- | ---: |') + foreach ($group in $eventSets) { + $lines.Add("| $(ConvertTo-MarkdownCell $group.Name) | $($group.Count) |") + } + $lines.Add('') + $lines.Add('## Workflow files') + $lines.Add('') + $lines.Add( + '| Repository | File | Name | Events | Reference | Version | PR types | Push branches | Schedule |' + + ' Concurrency | Cancel | Permissions | Secrets | Inputs | Extra jobs |' + ) + $lines.Add( + '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' + ) + + foreach ($item in $Inventory | Sort-Object Repository, WorkflowPath) { + if ($item.Status -eq 'ParseError') { + $lines.Add( + "| $(ConvertTo-MarkdownCell $item.Repository) " + + "| $(ConvertTo-MarkdownCell $item.WorkflowPath) | parse error | | | | | | | | | | | | |" + ) + continue + } + + $referencesForItem = @($item.ProcessJobs.Reference | Sort-Object -Unique) + $versionsForItem = @($item.VersionComments.Version | Where-Object { $_ } | Sort-Object -Unique) + $secretSummary = @( + $item.ProcessJobs | ForEach-Object { + if ($_.SecretMode -eq 'explicit') { + "explicit: $(($_.SecretMappings.Keys | Sort-Object) -join ', ')" + } else { + $_.SecretMode + } + } + ) | Sort-Object -Unique + $inputSummary = @( + $item.ProcessJobs | + ForEach-Object { $_.Inputs.Keys } | + Sort-Object -Unique + ) + $permissionSummary = @( + $item.Permissions.GetEnumerator() | + Sort-Object Key | + ForEach-Object { "$($_.Key)=$($_.Value)" } + ) + + $lines.Add( + "| $(ConvertTo-MarkdownCell $item.Repository) " + + "| $(ConvertTo-MarkdownCell $item.WorkflowPath) " + + "| $(ConvertTo-MarkdownCell $item.WorkflowName) " + + "| $(ConvertTo-MarkdownCell $item.Events) " + + "| $(ConvertTo-MarkdownCell $referencesForItem) " + + "| $(ConvertTo-MarkdownCell $versionsForItem) " + + "| $(ConvertTo-MarkdownCell $item.PullRequestTypes) " + + "| $(ConvertTo-MarkdownCell $item.PushBranches) " + + "| $(ConvertTo-MarkdownCell $item.Schedules) " + + "| $(ConvertTo-MarkdownCell $item.ConcurrencyGroup) " + + "| $(ConvertTo-MarkdownCell $item.CancelInProgress) " + + "| $(ConvertTo-MarkdownCell $permissionSummary) " + + "| $(ConvertTo-MarkdownCell $secretSummary) " + + "| $(ConvertTo-MarkdownCell $inputSummary) " + + "| $(ConvertTo-MarkdownCell $item.AdditionalJobs) |" + ) + } + + if ($parseErrors) { + $lines.Add('') + $lines.Add('## Parse errors') + $lines.Add('') + foreach ($item in $parseErrors) { + $lines.Add("- **$($item.Repository)/$($item.WorkflowPath):** $(ConvertTo-MarkdownCell $item.Error)") + } + } + + $lines -join "`n" +} + +$workflowFiles = if ($PSCmdlet.ParameterSetName -eq 'GitHub') { + if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { + throw 'GitHub mode requires the GitHub CLI (gh). Install it and authenticate with gh auth login or GH_TOKEN.' + } + + $repositories = Get-GitHubRepository ` + -Owner $Organization ` + -NameWithOwner $Repository ` + -IncludeArchivedRepository:$IncludeArchived + if (-not $repositories) { + throw "No repositories were found for organization [$Organization]." + } + + @( + Get-GitHubMatchingWorkflowFile ` + -RepositoryInfo $repositories ` + -Owner $Organization ` + -ExpectedReference $WorkflowReference + ) +} else { + @(Get-LocalWorkflowFile -InputPath $Path) +} + +if (-not $workflowFiles) { + throw "No workflow files containing [$WorkflowReference] were discovered." +} + +$inventory = @( + $workflowFiles | + ForEach-Object { + Get-WorkflowInventoryItem -WorkflowFile $_ -ExpectedReference $WorkflowReference + } +) + +if (-not $inventory) { + throw "No reusable workflow jobs using [$WorkflowReference] were found in the discovered files." +} + +if ($JsonPath) { + $parent = Split-Path -Path $JsonPath -Parent + if ($parent) { + New-Item -ItemType Directory -Path $parent -Force | Out-Null + } + $inventory | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $JsonPath -Encoding utf8 +} + +if ($MarkdownPath) { + $parent = Split-Path -Path $MarkdownPath -Parent + if ($parent) { + New-Item -ItemType Directory -Path $parent -Force | Out-Null + } + ConvertTo-WorkflowInventoryMarkdown ` + -Inventory $inventory ` + -Source $PSCmdlet.ParameterSetName | + Set-Content -LiteralPath $MarkdownPath -Encoding utf8 +} + +$inventory diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 new file mode 100644 index 00000000..a1c92a7f --- /dev/null +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -0,0 +1,131 @@ +[CmdletBinding()] +param() + +BeforeAll { + $scriptPath = Join-Path $PSScriptRoot '../Get-ProcessPSModuleWorkflowInventory.ps1' + $testRoot = Join-Path $TestDrive 'repositories' + $repositoryRoot = Join-Path $testRoot 'Example' + $workflowRoot = Join-Path $repositoryRoot '.github/workflows' + New-Item -ItemType Directory -Path (Join-Path $repositoryRoot '.git') -Force | Out-Null + New-Item -ItemType Directory -Path $workflowRoot -Force | Out-Null + + @' +name: Process-PSModule + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + push: + branches: + - main + pull_request: + branches: + - main + types: + - opened + - synchronize + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + Process-PSModule: + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@0123456789012345678901234567890123456789 # v8.0.0 + with: + Debug: true + secrets: + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} + GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} + GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} +'@ | Set-Content -LiteralPath (Join-Path $workflowRoot 'Process-PSModule.yml') + + @' +name: Unrelated +on: + workflow_dispatch: +jobs: + Test: + runs-on: ubuntu-latest + steps: + - run: echo test +'@ | Set-Content -LiteralPath (Join-Path $workflowRoot 'Unrelated.yml') +} + +Describe 'Get-ProcessPSModuleWorkflowInventory' { + It 'inventories matching local workflows and their compatibility dimensions' { + $result = @(& $scriptPath -Path $testRoot) + + $result.Count | Should -Be 1 + $result[0].Repository | Should -Be 'Example' + $result[0].WorkflowName | Should -Be 'Process-PSModule' + $result[0].Events | Should -Be @('pull_request', 'push', 'schedule', 'workflow_dispatch') + $result[0].PushBranches | Should -Be @('main') + $result[0].PullRequestTypes | Should -Be @('opened', 'synchronize') + $result[0].ConcurrencyGroup | Should -Be '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' + $result[0].CancelInProgress | Should -BeFalse + $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + $result[0].ProcessJobs[0].Inputs.Keys | Should -Contain 'Debug' + $result[0].ProcessJobs[0].SecretMappings.Keys | Should -Be @( + 'PSGALLERY_API_KEY' + 'GitHubAppClientId' + 'GitHubAppPrivateKey' + ) + } + + It 'writes JSON and Markdown refresh artifacts' { + $jsonPath = Join-Path $TestDrive 'inventory.json' + $markdownPath = Join-Path $TestDrive 'inventory.md' + + & $scriptPath -Path $repositoryRoot -JsonPath $jsonPath -MarkdownPath $markdownPath | Out-Null + + Test-Path -LiteralPath $jsonPath | Should -BeTrue + Test-Path -LiteralPath $markdownPath | Should -BeTrue + Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Example' + Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0123456789012345678901234567890123456789' + } + + It 'records a parse error for a matching malformed workflow' { + $malformedRoot = Join-Path $testRoot 'Malformed' + $malformedWorkflowRoot = Join-Path $malformedRoot '.github/workflows' + New-Item -ItemType Directory -Path (Join-Path $malformedRoot '.git') -Force | Out-Null + New-Item -ItemType Directory -Path $malformedWorkflowRoot -Force | Out-Null + @' +name: Broken +jobs: + Process: + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + invalid: [ +'@ | Set-Content -LiteralPath (Join-Path $malformedWorkflowRoot 'Process.yml') + + $result = @(& $scriptPath -Path $malformedRoot) + + $result.Count | Should -Be 1 + $result[0].Status | Should -Be 'ParseError' + $result[0].Error | Should -Not -BeNullOrEmpty + } + + It 'fails closed when no matching workflow is found' { + $emptyRoot = Join-Path $testRoot 'Empty' + New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.git') -Force | Out-Null + New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.github/workflows') -Force | Out-Null + Set-Content -LiteralPath (Join-Path $emptyRoot '.github/workflows/Unrelated.yml') -Value @' +name: Unrelated +on: + workflow_dispatch: +jobs: + Test: + runs-on: ubuntu-latest + steps: + - run: echo test +'@ + + { & $scriptPath -Path $emptyRoot } | + Should -Throw 'No reusable workflow jobs using*' + } +} From 64b640a596f76deed27c61eec78b83488f8365e1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 13:04:22 +0200 Subject: [PATCH 02/26] Harden workflow inventory discovery Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 157 +++++++++++++++--- ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 73 +++++++- 2 files changed, 204 insertions(+), 26 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 087c82aa..19f22e1a 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -237,15 +237,29 @@ function Get-LocalDefaultBranch { $originHead = (& git -C $RepositoryRoot symbolic-ref refs/remotes/origin/HEAD --short 2>$null) -join '' if ($LASTEXITCODE -eq 0 -and $originHead) { - return $originHead -replace '^origin/', '' + return [pscustomobject]@{ + Name = $originHead -replace '^origin/', '' + Ref = $originHead + } + } + + & git -C $RepositoryRoot show-ref --verify --quiet refs/heads/main + if ($LASTEXITCODE -eq 0) { + return [pscustomobject]@{ + Name = 'main' + Ref = 'main' + } } $branch = (& git -C $RepositoryRoot branch --show-current 2>$null) -join '' if ($LASTEXITCODE -eq 0 -and $branch) { - return $branch + return [pscustomobject]@{ + Name = $branch + Ref = $branch + } } - $null + throw "Could not determine a default or current branch for local repository [$RepositoryRoot]." } function Get-LocalWorkflowFile { @@ -255,26 +269,39 @@ function Get-LocalWorkflowFile { [string[]] $InputPath ) + $seenRepositories = [Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($repositoryRoot in Get-LocalRepositoryRoot -InputPath $InputPath) { - $workflowRoot = Join-Path $repositoryRoot '.github/workflows' - if (-not (Test-Path -LiteralPath $workflowRoot -PathType Container)) { + $repositoryName = Get-LocalRepositoryName -RepositoryRoot $repositoryRoot + if (-not $seenRepositories.Add($repositoryName)) { continue } - $repositoryName = Get-LocalRepositoryName -RepositoryRoot $repositoryRoot $defaultBranch = Get-LocalDefaultBranch -RepositoryRoot $repositoryRoot - Get-ChildItem -LiteralPath $workflowRoot -File | - Where-Object { $_.Extension -in @('.yml', '.yaml') } | + $workflowPaths = @( + (& git -C $repositoryRoot ls-tree -r --name-only $defaultBranch.Ref -- '.github/workflows' 2>&1) -join "`n" + ) + if ($LASTEXITCODE -ne 0) { + throw "Could not list workflows from [$repositoryName] at [$($defaultBranch.Ref)]:`n$workflowPaths" + } + + @($workflowPaths -split '\r?\n') | + Where-Object { [IO.Path]::GetExtension($_) -in @('.yml', '.yaml') } | ForEach-Object { + $workflowPath = $_ + $content = (& git -C $repositoryRoot show "$($defaultBranch.Ref):$workflowPath" 2>&1) -join "`n" + if ($LASTEXITCODE -ne 0) { + throw "Could not read [$workflowPath] from [$repositoryName] at [$($defaultBranch.Ref)]:`n$content" + } + [pscustomobject]@{ Repository = $repositoryName - DefaultBranch = $defaultBranch + DefaultBranch = $defaultBranch.Name Archived = $false RepositoryUrl = $null - WorkflowPath = [IO.Path]::GetRelativePath($repositoryRoot, $_.FullName).Replace('\', '/') + WorkflowPath = $workflowPath WorkflowUrl = $null SearchQuery = $null - Content = Get-Content -LiteralPath $_.FullName -Raw + Content = $content } } } @@ -318,7 +345,47 @@ function Get-MapValue { return $Map[$Name] } - $Map.PSObject.Properties[$Name].Value + $property = $Map.PSObject.Properties[$Name] + if ($null -eq $property) { + return $null + } + + $property.Value +} + +function ConvertTo-TriggerMap { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Trigger + ) + + if ($null -eq $Trigger) { + return [ordered]@{} + } + + if ($Trigger -is [Collections.IDictionary]) { + return $Trigger + } + + $result = [ordered]@{} + if ($Trigger -is [string]) { + $result[$Trigger] = $null + return $result + } + + if ($Trigger -is [Collections.IEnumerable]) { + foreach ($eventName in $Trigger) { + if ($eventName -isnot [string]) { + throw "Unsupported workflow trigger value type [$($eventName.GetType().FullName)]." + } + $result[$eventName] = $null + } + return $result + } + + throw "Unsupported workflow trigger type [$($Trigger.GetType().FullName)]." } function ConvertTo-StringMap { @@ -404,6 +471,7 @@ function Get-WorkflowInventoryItem { [ordered]@{} } Environment = Get-MapValue -Map $job -Name 'environment' + Condition = Get-MapValue -Map $job -Name 'if' } } @@ -411,7 +479,21 @@ function Get-WorkflowInventoryItem { return } - $trigger = Get-MapValue -Map $workflow -Name 'on' + try { + $trigger = ConvertTo-TriggerMap -Trigger (Get-MapValue -Map $workflow -Name 'on') + } catch { + return [pscustomobject]@{ + Repository = $WorkflowFile.Repository + DefaultBranch = $WorkflowFile.DefaultBranch + Archived = $WorkflowFile.Archived + RepositoryUrl = $WorkflowFile.RepositoryUrl + WorkflowPath = $WorkflowFile.WorkflowPath + WorkflowUrl = $WorkflowFile.WorkflowUrl + SearchQuery = $WorkflowFile.SearchQuery + Status = 'ParseError' + Error = $_.Exception.Message + } + } $pullRequest = Get-MapValue -Map $trigger -Name 'pull_request' $push = Get-MapValue -Map $trigger -Name 'push' $schedule = Get-MapValue -Map $trigger -Name 'schedule' @@ -442,6 +524,7 @@ function Get-WorkflowInventoryItem { Status = 'Parsed' Error = $null WorkflowName = Get-MapValue -Map $workflow -Name 'name' + RunName = Get-MapValue -Map $workflow -Name 'run-name' Events = @(Get-MapKey -Map $trigger | Sort-Object) Schedules = @($schedule | ForEach-Object { Get-MapValue -Map $_ -Name 'cron' }) PushBranches = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'branches') @@ -493,6 +576,13 @@ function ConvertTo-WorkflowInventoryMarkdown { Group-Object | Sort-Object @{ Expression = 'Count'; Descending = $true }, Name ) + $versions = @( + $parsed | + ForEach-Object { $_.VersionComments.Version } | + Where-Object { $_ } | + Group-Object | + Sort-Object @{ Expression = 'Count'; Descending = $true }, Name + ) $eventSets = @( $parsed | ForEach-Object { $_.Events -join ', ' } | @@ -501,6 +591,11 @@ function ConvertTo-WorkflowInventoryMarkdown { ) $lines = [Collections.Generic.List[string]]::new() + $lines.Add('---') + $lines.Add('title: Process-PSModule workflow fleet inventory') + $lines.Add('description: Generated inventory of PSModule repositories that call the Process-PSModule reusable workflow.') + $lines.Add('---') + $lines.Add('') $lines.Add('# Process-PSModule workflow inventory') $lines.Add('') $lines.Add("Generated: $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssK')") @@ -518,6 +613,14 @@ function ConvertTo-WorkflowInventoryMarkdown { $lines.Add("| $(ConvertTo-MarkdownCell $group.Name) | $($group.Count) |") } $lines.Add('') + $lines.Add('## Version distribution') + $lines.Add('') + $lines.Add('| Version comment | Workflows |') + $lines.Add('| --- | ---: |') + foreach ($group in $versions) { + $lines.Add("| $(ConvertTo-MarkdownCell $group.Name) | $($group.Count) |") + } + $lines.Add('') $lines.Add('## Trigger distribution') $lines.Add('') $lines.Add('| Events | Workflows |') @@ -529,18 +632,29 @@ function ConvertTo-WorkflowInventoryMarkdown { $lines.Add('## Workflow files') $lines.Add('') $lines.Add( - '| Repository | File | Name | Events | Reference | Version | PR types | Push branches | Schedule |' + - ' Concurrency | Cancel | Permissions | Secrets | Inputs | Extra jobs |' + '| Repository | File | Name | Run name | Events | Reference | Version | PR types | Push branches | Schedule |' + + ' Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs |' ) $lines.Add( - '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' + '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' ) foreach ($item in $Inventory | Sort-Object Repository, WorkflowPath) { + $repositoryCell = if ($item.RepositoryUrl) { + "[$($item.Repository)]($($item.RepositoryUrl))" + } else { + $item.Repository + } + $workflowCell = if ($item.WorkflowUrl) { + "[$($item.WorkflowPath)]($($item.WorkflowUrl))" + } else { + $item.WorkflowPath + } + if ($item.Status -eq 'ParseError') { $lines.Add( - "| $(ConvertTo-MarkdownCell $item.Repository) " + - "| $(ConvertTo-MarkdownCell $item.WorkflowPath) | parse error | | | | | | | | | | | | |" + "| $(ConvertTo-MarkdownCell $repositoryCell) " + + "| $(ConvertTo-MarkdownCell $workflowCell) | parse error | | | | | | | | | | | | | | |" ) continue } @@ -561,6 +675,7 @@ function ConvertTo-WorkflowInventoryMarkdown { ForEach-Object { $_.Inputs.Keys } | Sort-Object -Unique ) + $conditionSummary = @($item.ProcessJobs.Condition | Where-Object { $_ } | Sort-Object -Unique) $permissionSummary = @( $item.Permissions.GetEnumerator() | Sort-Object Key | @@ -568,9 +683,10 @@ function ConvertTo-WorkflowInventoryMarkdown { ) $lines.Add( - "| $(ConvertTo-MarkdownCell $item.Repository) " + - "| $(ConvertTo-MarkdownCell $item.WorkflowPath) " + + "| $(ConvertTo-MarkdownCell $repositoryCell) " + + "| $(ConvertTo-MarkdownCell $workflowCell) " + "| $(ConvertTo-MarkdownCell $item.WorkflowName) " + + "| $(ConvertTo-MarkdownCell $item.RunName) " + "| $(ConvertTo-MarkdownCell $item.Events) " + "| $(ConvertTo-MarkdownCell $referencesForItem) " + "| $(ConvertTo-MarkdownCell $versionsForItem) " + @@ -580,6 +696,7 @@ function ConvertTo-WorkflowInventoryMarkdown { "| $(ConvertTo-MarkdownCell $item.ConcurrencyGroup) " + "| $(ConvertTo-MarkdownCell $item.CancelInProgress) " + "| $(ConvertTo-MarkdownCell $permissionSummary) " + + "| $(ConvertTo-MarkdownCell $conditionSummary) " + "| $(ConvertTo-MarkdownCell $secretSummary) " + "| $(ConvertTo-MarkdownCell $inputSummary) " + "| $(ConvertTo-MarkdownCell $item.AdditionalJobs) |" diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index a1c92a7f..90b932ae 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -3,10 +3,12 @@ param() BeforeAll { $scriptPath = Join-Path $PSScriptRoot '../Get-ProcessPSModuleWorkflowInventory.ps1' - $testRoot = Join-Path $TestDrive 'repositories' + $testRoot = Join-Path ([IO.Path]::GetTempPath()) "process-workflow-inventory-$([guid]::NewGuid())" $repositoryRoot = Join-Path $testRoot 'Example' $workflowRoot = Join-Path $repositoryRoot '.github/workflows' - New-Item -ItemType Directory -Path (Join-Path $repositoryRoot '.git') -Force | Out-Null + & git init --quiet --initial-branch=main $repositoryRoot + & git -C $repositoryRoot config user.email 'inventory-tests@example.invalid' + & git -C $repositoryRoot config user.name 'Inventory Tests' New-Item -ItemType Directory -Path $workflowRoot -Force | Out-Null @' @@ -36,6 +38,7 @@ permissions: jobs: Process-PSModule: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@0123456789012345678901234567890123456789 # v8.0.0 with: Debug: true @@ -55,6 +58,26 @@ jobs: steps: - run: echo test '@ | Set-Content -LiteralPath (Join-Path $workflowRoot 'Unrelated.yml') + + & git -C $repositoryRoot add . + & git -C $repositoryRoot commit --quiet -m 'Add test workflows' + & git -C $repositoryRoot update-ref refs/remotes/origin/main HEAD + & git -C $repositoryRoot symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main + & git -C $repositoryRoot switch --quiet -c feature + $featureContent = Get-Content -LiteralPath (Join-Path $workflowRoot 'Process-PSModule.yml') -Raw + $featureContent.Replace( + '0123456789012345678901234567890123456789', + 'ffffffffffffffffffffffffffffffffffffffff' + ) | + Set-Content -LiteralPath (Join-Path $workflowRoot 'Process-PSModule.yml') +} + +AfterAll { + if (Test-Path -LiteralPath $testRoot) { + Get-ChildItem -LiteralPath $testRoot -Recurse -Force | + ForEach-Object { $_.Attributes = [IO.FileAttributes]::Normal } + Remove-Item -LiteralPath $testRoot -Recurse -Force + } } Describe 'Get-ProcessPSModuleWorkflowInventory' { @@ -70,6 +93,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { $result[0].ConcurrencyGroup | Should -Be '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' $result[0].CancelInProgress | Should -BeFalse $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + $result[0].ProcessJobs[0].Condition | Should -Match 'head.repo.full_name' $result[0].ProcessJobs[0].Inputs.Keys | Should -Contain 'Debug' $result[0].ProcessJobs[0].SecretMappings.Keys | Should -Be @( 'PSGALLERY_API_KEY' @@ -78,9 +102,16 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { ) } + It 'reads the remote default branch instead of feature-worktree changes' { + $result = @(& $scriptPath -Path $repositoryRoot) + + $result[0].DefaultBranch | Should -Be 'main' + $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + } + It 'writes JSON and Markdown refresh artifacts' { - $jsonPath = Join-Path $TestDrive 'inventory.json' - $markdownPath = Join-Path $TestDrive 'inventory.md' + $jsonPath = Join-Path $testRoot 'inventory.json' + $markdownPath = Join-Path $testRoot 'inventory.md' & $scriptPath -Path $repositoryRoot -JsonPath $jsonPath -MarkdownPath $markdownPath | Out-Null @@ -93,7 +124,9 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { It 'records a parse error for a matching malformed workflow' { $malformedRoot = Join-Path $testRoot 'Malformed' $malformedWorkflowRoot = Join-Path $malformedRoot '.github/workflows' - New-Item -ItemType Directory -Path (Join-Path $malformedRoot '.git') -Force | Out-Null + & git init --quiet --initial-branch=main $malformedRoot + & git -C $malformedRoot config user.email 'inventory-tests@example.invalid' + & git -C $malformedRoot config user.name 'Inventory Tests' New-Item -ItemType Directory -Path $malformedWorkflowRoot -Force | Out-Null @' name: Broken @@ -102,6 +135,8 @@ jobs: uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 invalid: [ '@ | Set-Content -LiteralPath (Join-Path $malformedWorkflowRoot 'Process.yml') + & git -C $malformedRoot add . + & git -C $malformedRoot commit --quiet -m 'Add malformed workflow' $result = @(& $scriptPath -Path $malformedRoot) @@ -112,7 +147,9 @@ jobs: It 'fails closed when no matching workflow is found' { $emptyRoot = Join-Path $testRoot 'Empty' - New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.git') -Force | Out-Null + & git init --quiet --initial-branch=main $emptyRoot + & git -C $emptyRoot config user.email 'inventory-tests@example.invalid' + & git -C $emptyRoot config user.name 'Inventory Tests' New-Item -ItemType Directory -Path (Join-Path $emptyRoot '.github/workflows') -Force | Out-Null Set-Content -LiteralPath (Join-Path $emptyRoot '.github/workflows/Unrelated.yml') -Value @' name: Unrelated @@ -124,8 +161,32 @@ jobs: steps: - run: echo test '@ + & git -C $emptyRoot add . + & git -C $emptyRoot commit --quiet -m 'Add unrelated workflow' { & $scriptPath -Path $emptyRoot } | Should -Throw 'No reusable workflow jobs using*' } + + It 'normalizes shorthand trigger lists' { + $shorthandRoot = Join-Path $testRoot 'Shorthand' + $shorthandWorkflowRoot = Join-Path $shorthandRoot '.github/workflows' + & git init --quiet --initial-branch=main $shorthandRoot + & git -C $shorthandRoot config user.email 'inventory-tests@example.invalid' + & git -C $shorthandRoot config user.name 'Inventory Tests' + New-Item -ItemType Directory -Path $shorthandWorkflowRoot -Force | Out-Null + Set-Content -LiteralPath (Join-Path $shorthandWorkflowRoot 'Process.yml') -Value @' +name: Shorthand +on: [push, workflow_dispatch] +jobs: + Process: + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 +'@ + & git -C $shorthandRoot add . + & git -C $shorthandRoot commit --quiet -m 'Add shorthand workflow' + + $result = @(& $scriptPath -Path $shorthandRoot) + + $result[0].Events | Should -Be @('push', 'workflow_dispatch') + } } From 25b8c7e9bd27c42ee308f6be36cf55749258d50c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 13:05:06 +0200 Subject: [PATCH 03/26] Document Process workflow fleet standard Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/get-started/repository-setup.md | 3 +- docs/content/guides/calling-the-workflow.md | 25 ++- .../guides/github-app-authentication.md | 3 +- .../process-workflow-fleet-inventory.md | 112 +++++++++++ .../process-workflow-fleet-standard.md | 185 ++++++++++++++++++ docs/content/reference/repository-standard.md | 1 + 6 files changed, 319 insertions(+), 10 deletions(-) create mode 100644 docs/content/reference/process-workflow-fleet-inventory.md create mode 100644 docs/content/reference/process-workflow-fleet-standard.md diff --git a/docs/content/get-started/repository-setup.md b/docs/content/get-started/repository-setup.md index d8fbabaa..8686efcc 100644 --- a/docs/content/get-started/repository-setup.md +++ b/docs/content/get-started/repository-setup.md @@ -65,7 +65,8 @@ permissions: jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/guides/calling-the-workflow.md b/docs/content/guides/calling-the-workflow.md index e9bb9e85..dd319005 100644 --- a/docs/content/guides/calling-the-workflow.md +++ b/docs/content/guides/calling-the-workflow.md @@ -48,7 +48,8 @@ permissions: jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -67,6 +68,10 @@ the PowerShell Gallery, GitHub Releases, and tags, so later runs must queue rath The reusable workflow uses its own prefixed concurrency group, so it cannot queue behind the caller while the caller waits for it to finish. +The job condition skips fork-originated pull requests because GitHub does not expose the required repository secrets to +forks. Use a separate secret-free, read-only workflow if the repository accepts contributions from forks and requires +fork CI. + ## Passing test data The reusable workflow at `.github/workflows/workflow.yml` declares four workflow-call secrets, @@ -94,7 +99,8 @@ changes: ```yaml jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -123,7 +129,8 @@ content lines stay at the same indentation level: ```yaml jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -183,9 +190,9 @@ Notes: - If using `secrets: inherit` in a caller workflow, remember that GitHub only forwards secrets that already exist by name. It does not assemble a `TestData` JSON payload from individual secrets such as `TEST_USER_PAT`; the caller must still create and pass the `TestData` value explicitly. -- Organization, repository and GitHub *Environment* secrets and variables are supported when they are - visible to the calling job. For environment-scoped values, set `environment:` on the calling job and - explicitly include those values in `TestData`; they are not exposed automatically. +- Organization and repository secrets and variables are supported when they are visible to the calling job. + GitHub Environment secrets are not supported by this caller contract because a job that calls a reusable + workflow cannot declare `environment:`. ## Important file change detection @@ -235,7 +242,8 @@ You can also pass patterns via the workflow input: ```yaml jobs: Process: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 with: ImportantFilePatterns: | ^src/ @@ -248,7 +256,8 @@ To disable triggering via the workflow input, pass an explicit empty string: ```yaml jobs: process: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 with: ImportantFilePatterns: '' ``` diff --git a/docs/content/guides/github-app-authentication.md b/docs/content/guides/github-app-authentication.md index 7be1620b..5f0dcb2b 100644 --- a/docs/content/guides/github-app-authentication.md +++ b/docs/content/guides/github-app-authentication.md @@ -23,7 +23,8 @@ names. Map the caller's secrets explicitly: ```yaml jobs: Process-PSModule: - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/reference/process-workflow-fleet-inventory.md b/docs/content/reference/process-workflow-fleet-inventory.md new file mode 100644 index 00000000..85eeb46b --- /dev/null +++ b/docs/content/reference/process-workflow-fleet-inventory.md @@ -0,0 +1,112 @@ +--- +title: Process-PSModule workflow fleet inventory +description: Generated inventory of PSModule repositories that call the Process-PSModule reusable workflow. +--- + +# Process-PSModule workflow inventory + +Generated: 2026-08-15T13:04:06+02:00 + +- Source: GitHub +- Workflow files: 60 +- Parsed: 60 +- Parse errors: 0 + +## Reference distribution + +| Reference | Workflows | +| --- | ---: | +| 205d193f34cbbaf9992955c21d842bcf98a1859f | 35 | +| fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | 6 | +| da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | 5 | +| 11117919e65242d3388727819a751f74ad24ea9e | 4 | +| 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | 4 | +| ce64918acc96dda73eb78f827036b794bfa6fa1a | 3 | +| 1653be8d36607d9535f600278c44789979477813 | 1 | +| 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | 1 | +| bf67cd90269ca5ce25cd76b203678907dc2984b4 | 1 | + +## Version distribution + +| Version comment | Workflows | +| --- | ---: | +| v5.4.6 | 35 | +| v6.1.13 | 6 | +| v6.1.4 | 5 | +| v5.5.0 | 4 | +| v6.1.15 | 4 | +| v5.5.7 | 3 | +| v5.4.3 | 1 | +| v6.1.16 | 1 | +| v6.1.19 | 1 | + +## Trigger distribution + +| Events | Workflows | +| --- | ---: | +| pull_request, schedule, workflow_dispatch | 60 | + +## Workflow files + +| Repository | File | Name | Run name | Events | Reference | Version | PR types | Push branches | Schedule | Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| [PSModule/Admin](https://github.com/PSModule/Admin) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Admin/blob/c21efa2de875b25775cad332641b7509db2274b2/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Anthropic](https://github.com/PSModule/Anthropic) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Anthropic/blob/507a3fea65c13965fc550d1eec209db300436e49/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Ast](https://github.com/PSModule/Ast) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Ast/blob/769af9815cc948c21860f7392b0538df2065b20e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Base64](https://github.com/PSModule/Base64) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Base64/blob/f8a7942f4f857b26cdb63199670e480fba5e9d61/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Bluesky](https://github.com/PSModule/Bluesky) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Bluesky/blob/18503ebdf04e401434df2028d75899eb39cdc5db/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CasingStyle](https://github.com/PSModule/CasingStyle) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CasingStyle/blob/fc26c170059b8012bdbde34031ffe47ac5cc53ec/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Claude](https://github.com/PSModule/Claude) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Claude/blob/081aae987ee37fc6d1f0142b376f92688dfcf2b0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Confluence](https://github.com/PSModule/Confluence) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Confluence/blob/3e5a057dca611ae1036bdd5731cc7031f2657144/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/Context](https://github.com/PSModule/Context) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context/blob/c80a0a0d97b88f6140ea351962ddf257a4f02b90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Context7](https://github.com/PSModule/Context7) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context7/blob/e56e0118107fae5e5cf1385711df8659d67dfde0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CurseForge](https://github.com/PSModule/CurseForge) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CurseForge/blob/41373542ae348296a1ac5b74730946350afedfb0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | v5.4.3 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DateTime](https://github.com/PSModule/DateTime) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DateTime/blob/17b99ed2aad7b63df512b61267a4f00436897e92/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/DeepSeek](https://github.com/PSModule/DeepSeek) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DeepSeek/blob/a12f3fe69db3c12cff2b22db017516df10db610e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Discord](https://github.com/PSModule/Discord) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Discord/blob/560a78d92a33ecdb080c33c1e28f6094da5c5d1e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Dns](https://github.com/PSModule/Dns) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Dns/blob/58558ff6c0bef552d087360bf5b0d6ad37eecf7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Domeneshop](https://github.com/PSModule/Domeneshop) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Domeneshop/blob/2ca6f788c4b68a72c63d6472ae19720bc90cc8b9/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DynamicParams](https://github.com/PSModule/DynamicParams) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DynamicParams/blob/092726b82bf38bf8a49bf98cb1349dc6be691fde/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/ElvUI](https://github.com/PSModule/ElvUI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/ElvUI/blob/892a68211feb229698cfabc6e36abfee56727a30/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Fonts](https://github.com/PSModule/Fonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Fonts/blob/d665c51dc39cd4404ea2da2c9f4efb2cf932faa5/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Gemini](https://github.com/PSModule/Gemini) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Gemini/blob/eadf88ffc09f311d71ff398d36f27f07d188e64c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/GitHub](https://github.com/PSModule/GitHub) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GitHub/blob/3e1f9e7651797091830338ca4c36fb6814bef69f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/GoogleFonts](https://github.com/PSModule/GoogleFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GoogleFonts/blob/bb329c6912eaa9861d285a7f9915879d72a60567/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/GraphQL](https://github.com/PSModule/GraphQL) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GraphQL/blob/051ed470d8c81170c062a719a4d3a4343e3bd691/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Guid](https://github.com/PSModule/Guid) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Guid/blob/9ea7942021dc21307f774f6c5e63425529501233/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/GZip](https://github.com/PSModule/GZip) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GZip/blob/4422836a1cb68a8f06884c791ca22be62b808e79/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hashtable](https://github.com/PSModule/Hashtable) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hashtable/blob/680c3e8291dfc1696dfad1658b50a0f28cf5a86f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hcl](https://github.com/PSModule/Hcl) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hcl/blob/aa15ae16894757e8d2659cc1b0de42fc922178b3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/IPv4](https://github.com/PSModule/IPv4) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv4/blob/7c63729742f68e985d2216eac79d0ae4d097a756/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/IPv6](https://github.com/PSModule/IPv6) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv6/blob/f53d3e9b081f07557bb6542a916f41f42abbbb55/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Json](https://github.com/PSModule/Json) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Json/blob/4a996b7af4a354a90a2753fc1d00d31e9676fd11/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Jwt](https://github.com/PSModule/Jwt) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Jwt/blob/aa64677452cdd4cee62520439f48cdec1ec8621d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/LinkedIn](https://github.com/PSModule/LinkedIn) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/LinkedIn/blob/e129428630585da0c27d7c3466f4f3599a7be209/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Lovdata](https://github.com/PSModule/Lovdata) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lovdata/blob/dfe0f79562fecb8f99ed6e172ad3e4baa35cf821/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Lua](https://github.com/PSModule/Lua) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lua/blob/d532ebfca1c2d042b1a80846af3038ef2ff87386/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Markdown](https://github.com/PSModule/Markdown) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Markdown/blob/3377a6c9bd507a1ad25225fb8c2bb209114215eb/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/MemoryMappedFile](https://github.com/PSModule/MemoryMappedFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/MemoryMappedFile/blob/3f5eb7de7484558696ba9a633c7b39e084e2358c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/NerdFonts](https://github.com/PSModule/NerdFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/NerdFonts/blob/5a9abcb31663bb9d0e7ac58f904efa31a35e80b1/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | bf67cd90269ca5ce25cd76b203678907dc2984b4 | v6.1.19 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Net](https://github.com/PSModule/Net) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Net/blob/2facaf6bfa442a92f45b71094952e89999bf1024/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Object](https://github.com/PSModule/Object) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Object/blob/c385cfd09ec0ee9483a3123cbfd70cd1c26432cf/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/OpenAI](https://github.com/PSModule/OpenAI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/OpenAI/blob/d44c807117fda26311a1de0d14ef7fb071767597/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Path](https://github.com/PSModule/Path) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Path/blob/ddf6ba8a813819b6815411deffbe28fa678640a4/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellDataFile](https://github.com/PSModule/PowerShellDataFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellDataFile/blob/d256fea8410477e8b68e6bffa0b8085bb856ab0c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellGallery](https://github.com/PSModule/PowerShellGallery) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellGallery/blob/ea0734cf47e6b5957f61d6e9cb53f0ea3a9eb1ad/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCredential](https://github.com/PSModule/PSCredential) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCredential/blob/87cdfd19eceef381faf37bb124d02dba7fa5f3c6/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCustomObject](https://github.com/PSModule/PSCustomObject) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCustomObject/blob/25dd9dc1872f0cc7386e977d713b918738fcbb71/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSSemVer](https://github.com/PSModule/PSSemVer) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSSemVer/blob/1a621b14286331569f4d1ffd4643999c2a2a6ca8/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/PublicIP](https://github.com/PSModule/PublicIP) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PublicIP/blob/82f70c40a309c9b7390160035a6f836bd29da626/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Retry](https://github.com/PSModule/Retry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Retry/blob/2ecfcb46c3204a167a011202a663639996fa1895/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Sodium](https://github.com/PSModule/Sodium) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Sodium/blob/3d96d48c63758298616ab80215f59d4fed7cb7d3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Telemetry](https://github.com/PSModule/Telemetry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Telemetry/blob/e85cf2df6611ba056eafa46610b17c06674a71de/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Template-PSModule](https://github.com/PSModule/Template-PSModule) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Template-PSModule/blob/4f525ab008d2d616f2f4e4e20ee2d96d6f76ec67/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/TimeSpan](https://github.com/PSModule/TimeSpan) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/TimeSpan/blob/8700bcdc8340b52f7eab7b83414def47c36ed3a0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Tls](https://github.com/PSModule/Tls) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Tls/blob/11ea777e89668c1c4fd6280b312294db01a5ed4d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Toml](https://github.com/PSModule/Toml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Toml/blob/f8937f27af3c663c80fc3ca986a3aaa40a191a90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Twitch](https://github.com/PSModule/Twitch) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Twitch/blob/9d25274b23af02c45ecc6fb28f682883afa47027/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Uri](https://github.com/PSModule/Uri) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Uri/blob/fe821cf5c13498a092d919f9d3f8a207912afe96/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Utilities](https://github.com/PSModule/Utilities) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Utilities/blob/3583a87c377650bc4eacff70cef4fcb993c2117d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/WoW](https://github.com/PSModule/WoW) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/WoW/blob/fd432f8ea872d546eabb20488bc5adab15dbaa7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Yaml](https://github.com/PSModule/Yaml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yaml/blob/8e37203720719528495da3bef5273c674a4d2e0a/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | ImportantFilePatterns | | +| [PSModule/Yml](https://github.com/PSModule/Yml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yml/blob/93d2656563a719d99416b9e13a05e65f6f815498/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 1653be8d36607d9535f600278c44789979477813 | v6.1.16 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md new file mode 100644 index 00000000..0a568b65 --- /dev/null +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -0,0 +1,185 @@ +--- +title: Process-PSModule caller workflow fleet standard +description: Fleet research and proposed required and optional caller workflow elements for Process-PSModule consumers. +--- + +# Process-PSModule caller workflow fleet standard + +This page records the 2026-08-15 fleet research used to propose a common caller workflow for PowerShell module +repositories. It is a proposal for review before the consumer repositories are changed. + +The generated [workflow fleet inventory](process-workflow-fleet-inventory.md) lists every matching repository and +workflow. Refresh it with: + +```powershell +./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` + -Organization PSModule ` + -JsonPath ./output/process-workflows.json ` + -MarkdownPath ./docs/content/reference/process-workflow-fleet-inventory.md +``` + +GitHub mode uses the authenticated `gh` session, including `GH_TOKEN`. To inventory checked-out repositories without +GitHub discovery, use the local parameter set: + +```powershell +./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` + -Path C:\Repos, C:\Users\me\.copilot\repos ` + -JsonPath ./output/process-workflows.json ` + -MarkdownPath ./output/process-workflows.md +``` + +## Current fleet + +The authenticated organization scan found 60 default-branch caller workflows among 85 active repositories. Every +caller has the same structural baseline: + +| Aspect | Observed value | Coverage | +| --- | --- | ---: | +| File | `.github/workflows/Process-PSModule.yml` | 60/60 | +| Workflow name | `Process-PSModule` | 60/60 | +| Run name | Not set | 60/60 | +| Reusable-workflow job | `Process-PSModule` | 60/60 | +| Reusable-workflow job condition | Not set | 60/60 | +| Events | `workflow_dispatch`, daily `schedule`, `pull_request` | 60/60 | +| Pull-request branch | `main` | 60/60 | +| Pull-request types | `closed`, `opened`, `reopened`, `synchronize`, `labeled` | 60/60 | +| Schedule | `0 0 * * *` | 60/60 | +| Concurrency group | `${{ github.workflow }}-${{ github.ref }}` | 60/60 | +| Cancel in progress | `true` | 60/60 | +| Permissions | `contents`, `pull-requests`, `statuses`, `pages`, and `id-token`: `write` | 60/60 | +| Additional jobs | None | 60/60 | + +The uniform wrapper is a strong starting point, but it predates the two latest breaking releases: + +- `v7.0.0` requires explicit PowerShell Gallery and GitHub App credentials. +- `v8.0.0` moves stable publication to a default-branch `push`, adds `unlabeled` routing, and requires non-cancelling + pull-request-or-ref concurrency. + +No current caller has the `v8.0.0` trigger and concurrency contract. The fleet spans nine older versions: + +| Version | Repositories | +| --- | ---: | +| `v5.4.6` | 35 | +| `v6.1.13` | 6 | +| `v6.1.4` | 5 | +| `v5.5.0` | 4 | +| `v6.1.15` | 4 | +| `v5.5.7` | 3 | +| `v5.4.3` | 1 | +| `v6.1.16` | 1 | +| `v6.1.19` | 1 | + +Secret forwarding is the only widespread caller variation: + +- 41 callers use `secrets: inherit`. +- 15 callers explicitly map an `APIKey` or `APIKEY` secret. +- `Confluence`, `GitHub`, `Jwt`, and `Yaml` map the old API key plus `TestData`. +- `Yaml` is the only caller with a `with:` override (`ImportantFilePatterns`). + +The case difference in the old API key name is historical drift, not a supported option in the current contract. + +## Proposed standard + +The standard caller should be: + +```yaml +name: Process-PSModule + +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + push: + branches: + - main + pull_request: + branches: + - main + types: + - closed + - opened + - reopened + - synchronize + - labeled + - unlabeled + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + statuses: write + pages: write + id-token: write + +jobs: + Process-PSModule: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + secrets: + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} + GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} + GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} +``` + +The full commit SHA is the machine-enforced pin. The version comment is required for humans and Dependabot. + +## Required elements + +| Element | Requirement | Reason | +| --- | --- | --- | +| Identity | Keep the standard file, workflow, and job names shown above. | Stable discovery, status checks, and fleet maintenance. | +| Pull requests | Target `main` and keep all six listed activity types. | CI, prerelease publication, label changes, and closed-PR cleanup depend on them. | +| Default-branch push | Keep `push.branches: [main]`. | `v8` authorizes stable releases from the tested default-branch push. | +| Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. | +| Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | +| Concurrency | Use the PR-number-or-ref key with `cancel-in-progress: false`. | Cleanup and stable release runs stay distinct; release mutations queue instead of being interrupted. | +| Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. | +| Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | +| Reference | Pin the latest approved release to its full commit SHA and retain the version comment. | Immutable supply-chain reference with readable update context. | +| Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | +| Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | + +## Supported optional elements + +Optional elements are supported contract variations, not permission to retain historical drift. + +| Option | When it is appropriate | Constraint | +| --- | --- | --- | +| `TestData` secret | Module-local tests need caller-defined secrets or variables. | Use the documented compact single-line JSON object and expose only required values. | +| `with.SettingsPath` | The settings file is not `.github/PSModule.yml`. | Prefer the standard path for normal module repositories. | +| `with.WorkingDirectory` | The module is intentionally rooted below the repository root. | Keep the default `.` for the standard layout. | +| `with.ImportantFilePatterns` | A caller must override change detection at the workflow boundary. | Prefer stable configuration in `.github/PSModule.yml`; the supplied list replaces all defaults. | +| `with.Debug`, `Verbose`, `Version`, or `Prerelease` | A deliberate diagnostic or dependency-selection scenario needs it. | Do not hard-code temporary diagnostics into the fleet baseline. | +| Schedule time | Health runs need staggering or a repository-specific maintenance window. | Keep at least one documented schedule unless the repository records why health runs are unnecessary. | +| `run-name` | A repository needs clearer run presentation. | Presentation must not change job names or routing behavior. | + +## Out-of-standard variations + +The following are migration defects or require a documented exception: + +- `secrets: inherit`; +- `APIKey` or `APIKEY` mappings from the pre-`v7` contract; +- a mutable tag instead of a full commit SHA; +- missing `push` or `unlabeled` triggers; +- `cancel-in-progress: true` or the old ref-only concurrency key; +- trigger-level path filters that bypass Process-PSModule important-file evaluation; +- unrelated additional jobs in the caller wrapper; +- omitted documented permissions without a verified settings-based least-privilege profile. + +Fork-originated pull requests are skipped by the standard caller because reusable-workflow caller jobs cannot select a +GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free, +read-only validation workflow; removing the guard is not a supported shortcut. + +Repository-specific automation should normally use a separate workflow file. That keeps the Process-PSModule wrapper +identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration +tasks. + +## Rollout boundary + +This research does not change consumer repositories. A fleet campaign should use one delivery issue and one early draft +pull request per repository, preserve the supported optional mappings discovered here, and replace every historical +credential mapping with the explicit `v8` contract. The inventory should be refreshed immediately before creating the +campaign leaves and again before declaring the campaign complete. diff --git a/docs/content/reference/repository-standard.md b/docs/content/reference/repository-standard.md index dbe7a6d0..b2690049 100644 --- a/docs/content/reference/repository-standard.md +++ b/docs/content/reference/repository-standard.md @@ -122,6 +122,7 @@ The caller workflow declares the triggers, concurrency, and permissions for the ```yaml jobs: Process-PSModule: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@ # secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} From d1d05d16f9e67accd32feea603448b666f639a16 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 13:13:12 +0200 Subject: [PATCH 04/26] Handle inventory API and shorthand edge cases Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 87 ++++++++++++++++--- ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 6 ++ 2 files changed, 80 insertions(+), 13 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 19f22e1a..5f799475 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -151,11 +151,20 @@ function Get-GitHubMatchingWorkflowFile { 'per_page=100' ) $pages = @($response | ConvertFrom-Json -Depth 100) + if ($pages.incomplete_results -contains $true) { + throw "GitHub code search returned incomplete results for [$query]." + } + $searchResults = @($pages | ForEach-Object { $_.items }) if (-not $searchResults) { throw "GitHub code search returned no matches for [$query]." } + $expectedResultCount = @($pages | Select-Object -ExpandProperty total_count -Unique) + if ($expectedResultCount.Count -ne 1 -or $searchResults.Count -ne $expectedResultCount[0]) { + throw "GitHub code search returned [$($searchResults.Count)] of [$($expectedResultCount -join ', ')] results for [$query]." + } + $repositoryByName = @{} foreach ($item in $RepositoryInfo) { $repositoryByName[$item.nameWithOwner] = $item @@ -419,6 +428,29 @@ function ConvertTo-StringArray { @($Value | ForEach-Object { "$_" }) } +function ConvertTo-PermissionValue { + [CmdletBinding()] + param( + [Parameter()] + [AllowNull()] + [object] $Value + ) + + if ($null -eq $Value) { + return [ordered]@{} + } + + if ($Value -is [string]) { + return $Value + } + + if ($Value -is [Collections.IDictionary]) { + return ConvertTo-StringMap -Map $Value + } + + throw "Unsupported workflow permissions type [$($Value.GetType().FullName)]." +} + function Get-WorkflowInventoryItem { [CmdletBinding()] param( @@ -494,10 +526,34 @@ function Get-WorkflowInventoryItem { Error = $_.Exception.Message } } - $pullRequest = Get-MapValue -Map $trigger -Name 'pull_request' - $push = Get-MapValue -Map $trigger -Name 'push' - $schedule = Get-MapValue -Map $trigger -Name 'schedule' - $concurrency = Get-MapValue -Map $workflow -Name 'concurrency' + try { + $pullRequest = Get-MapValue -Map $trigger -Name 'pull_request' + $push = Get-MapValue -Map $trigger -Name 'push' + $schedule = Get-MapValue -Map $trigger -Name 'schedule' + $concurrency = Get-MapValue -Map $workflow -Name 'concurrency' + if ($concurrency -is [string]) { + $concurrencyGroup = $concurrency + $cancelInProgress = $null + } elseif ($null -eq $concurrency -or $concurrency -is [Collections.IDictionary]) { + $concurrencyGroup = Get-MapValue -Map $concurrency -Name 'group' + $cancelInProgress = Get-MapValue -Map $concurrency -Name 'cancel-in-progress' + } else { + throw "Unsupported workflow concurrency type [$($concurrency.GetType().FullName)]." + } + $permissions = ConvertTo-PermissionValue -Value (Get-MapValue -Map $workflow -Name 'permissions') + } catch { + return [pscustomobject]@{ + Repository = $WorkflowFile.Repository + DefaultBranch = $WorkflowFile.DefaultBranch + Archived = $WorkflowFile.Archived + RepositoryUrl = $WorkflowFile.RepositoryUrl + WorkflowPath = $WorkflowFile.WorkflowPath + WorkflowUrl = $WorkflowFile.WorkflowUrl + SearchQuery = $WorkflowFile.SearchQuery + Status = 'ParseError' + Error = $_.Exception.Message + } + } $allJobNames = Get-MapKey -Map $jobs $processJobNames = @($processJobs.Name) @@ -533,9 +589,9 @@ function Get-WorkflowInventoryItem { PushPathsIgnore = ConvertTo-StringArray -Value (Get-MapValue -Map $push -Name 'paths-ignore') PullRequestBranches = ConvertTo-StringArray -Value (Get-MapValue -Map $pullRequest -Name 'branches') PullRequestTypes = ConvertTo-StringArray -Value (Get-MapValue -Map $pullRequest -Name 'types') - ConcurrencyGroup = Get-MapValue -Map $concurrency -Name 'group' - CancelInProgress = Get-MapValue -Map $concurrency -Name 'cancel-in-progress' - Permissions = ConvertTo-StringMap -Map (Get-MapValue -Map $workflow -Name 'permissions') + ConcurrencyGroup = $concurrencyGroup + CancelInProgress = $cancelInProgress + Permissions = $permissions ProcessJobs = @($processJobs) AdditionalJobs = @($allJobNames | Where-Object { $_ -notin $processJobNames }) VersionComments = $versionComments @@ -676,11 +732,15 @@ function ConvertTo-WorkflowInventoryMarkdown { Sort-Object -Unique ) $conditionSummary = @($item.ProcessJobs.Condition | Where-Object { $_ } | Sort-Object -Unique) - $permissionSummary = @( - $item.Permissions.GetEnumerator() | - Sort-Object Key | - ForEach-Object { "$($_.Key)=$($_.Value)" } - ) + $permissionSummary = if ($item.Permissions -is [string]) { + @($item.Permissions) + } else { + @( + $item.Permissions.GetEnumerator() | + Sort-Object Key | + ForEach-Object { "$($_.Key)=$($_.Value)" } + ) + } $lines.Add( "| $(ConvertTo-MarkdownCell $repositoryCell) " + @@ -758,7 +818,8 @@ if ($JsonPath) { if ($parent) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } - $inventory | ConvertTo-Json -Depth 100 | Set-Content -LiteralPath $JsonPath -Encoding utf8 + ConvertTo-Json -InputObject @($inventory) -Depth 100 | + Set-Content -LiteralPath $JsonPath -Encoding utf8 } if ($MarkdownPath) { diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index 90b932ae..f1ac9cde 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -117,6 +117,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { Test-Path -LiteralPath $jsonPath | Should -BeTrue Test-Path -LiteralPath $markdownPath | Should -BeTrue + (Get-Content -LiteralPath $jsonPath -Raw).TrimStart() | Should -Match '^\[' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Example' Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0123456789012345678901234567890123456789' } @@ -178,6 +179,8 @@ jobs: Set-Content -LiteralPath (Join-Path $shorthandWorkflowRoot 'Process.yml') -Value @' name: Shorthand on: [push, workflow_dispatch] +permissions: read-all +concurrency: process-${{ github.ref }} jobs: Process: uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 @@ -188,5 +191,8 @@ jobs: $result = @(& $scriptPath -Path $shorthandRoot) $result[0].Events | Should -Be @('push', 'workflow_dispatch') + $result[0].Permissions | Should -Be 'read-all' + $result[0].ConcurrencyGroup | Should -Be 'process-${{ github.ref }}' + $result[0].CancelInProgress | Should -BeNullOrEmpty } } From 3cb70c1b7e7aa46cb5ecd64df1b285058c1a78e4 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 13:13:26 +0200 Subject: [PATCH 05/26] Clarify workflow credential variants Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/reference/process-workflow-fleet-inventory.md | 2 +- docs/content/reference/process-workflow-fleet-standard.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-inventory.md b/docs/content/reference/process-workflow-fleet-inventory.md index 85eeb46b..0eb3b78e 100644 --- a/docs/content/reference/process-workflow-fleet-inventory.md +++ b/docs/content/reference/process-workflow-fleet-inventory.md @@ -5,7 +5,7 @@ description: Generated inventory of PSModule repositories that call the Process- # Process-PSModule workflow inventory -Generated: 2026-08-15T13:04:06+02:00 +Generated: 2026-08-15T13:13:06+02:00 - Source: GitHub - Workflow files: 60 diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 0a568b65..f7786e90 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -72,8 +72,9 @@ No current caller has the `v8.0.0` trigger and concurrency contract. The fleet s Secret forwarding is the only widespread caller variation: - 41 callers use `secrets: inherit`. -- 15 callers explicitly map an `APIKey` or `APIKEY` secret. -- `Confluence`, `GitHub`, `Jwt`, and `Yaml` map the old API key plus `TestData`. +- 15 callers explicitly map only an `APIKey` or `APIKEY` secret. +- Four more callers (`Confluence`, `GitHub`, `Jwt`, and `Yaml`) map the old API key plus `TestData`, for 19 explicit + API-key callers in total. - `Yaml` is the only caller with a `with:` override (`ImportantFilePatterns`). The case difference in the old API key name is historical drift, not a supported option in the current contract. From 26372a1c67a202feacb50eca43b62649fbf3471a Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:09:46 +0200 Subject: [PATCH 06/26] Track workflow target reference compliance Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 48 +++++++++++++++---- ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 16 ++++++- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 5f799475..6f3027d0 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -15,6 +15,7 @@ .EXAMPLE ./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` -Organization PSModule ` + -TargetReference v8 ` -JsonPath ./output/process-workflows.json ` -MarkdownPath ./output/process-workflows.md @@ -45,6 +46,10 @@ param( [ValidateNotNullOrEmpty()] [string] $WorkflowReference = 'PSModule/Process-PSModule/.github/workflows/workflow.yml', + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $TargetReference, + [Parameter()] [string] $JsonPath, @@ -458,7 +463,10 @@ function Get-WorkflowInventoryItem { [psobject] $WorkflowFile, [Parameter(Mandatory)] - [string] $ExpectedReference + [string] $ExpectedReference, + + [Parameter()] + [string] $ExpectedTargetReference ) if ($WorkflowFile.Content -notmatch [regex]::Escape($ExpectedReference)) { @@ -495,6 +503,11 @@ function Get-WorkflowInventoryItem { Name = $jobName Uses = "$uses" Reference = "$uses".Substring("$ExpectedReference@".Length) + MatchesTarget = if ($ExpectedTargetReference) { + "$uses".Substring("$ExpectedReference@".Length) -eq $ExpectedTargetReference + } else { + $null + } Inputs = ConvertTo-StringMap -Map (Get-MapValue -Map $job -Name 'with') SecretMode = $secretMode SecretMappings = if ($secretMode -eq 'explicit') { @@ -595,6 +608,12 @@ function Get-WorkflowInventoryItem { ProcessJobs = @($processJobs) AdditionalJobs = @($allJobNames | Where-Object { $_ -notin $processJobNames }) VersionComments = $versionComments + TargetReference = $ExpectedTargetReference + MatchesTarget = if ($ExpectedTargetReference) { + @($processJobs | Where-Object { -not $_.MatchesTarget }).Count -eq 0 + } else { + $null + } } } @@ -621,7 +640,10 @@ function ConvertTo-WorkflowInventoryMarkdown { [Parameter(Mandatory)] [ValidateSet('GitHub', 'Local')] - [string] $Source + [string] $Source, + + [Parameter()] + [string] $TargetReference ) $parsed = @($Inventory | Where-Object Status -eq 'Parsed') @@ -660,6 +682,11 @@ function ConvertTo-WorkflowInventoryMarkdown { $lines.Add("- Workflow files: $($Inventory.Count)") $lines.Add("- Parsed: $($parsed.Count)") $lines.Add("- Parse errors: $($parseErrors.Count)") + if ($TargetReference) { + $matchingTarget = @($parsed | Where-Object MatchesTarget).Count + $lines.Add("- Target reference: $TargetReference") + $lines.Add("- Matching target: $matchingTarget/$($parsed.Count)") + } $lines.Add('') $lines.Add('## Reference distribution') $lines.Add('') @@ -688,11 +715,11 @@ function ConvertTo-WorkflowInventoryMarkdown { $lines.Add('## Workflow files') $lines.Add('') $lines.Add( - '| Repository | File | Name | Run name | Events | Reference | Version | PR types | Push branches | Schedule |' + - ' Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs |' + '| Repository | File | Name | Run name | Events | Reference | Target | Version | PR types | Push branches |' + + ' Schedule | Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs |' ) $lines.Add( - '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' + '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' ) foreach ($item in $Inventory | Sort-Object Repository, WorkflowPath) { @@ -710,7 +737,7 @@ function ConvertTo-WorkflowInventoryMarkdown { if ($item.Status -eq 'ParseError') { $lines.Add( "| $(ConvertTo-MarkdownCell $repositoryCell) " + - "| $(ConvertTo-MarkdownCell $workflowCell) | parse error | | | | | | | | | | | | | | |" + "| $(ConvertTo-MarkdownCell $workflowCell) | parse error | | | | | | | | | | | | | | | |" ) continue } @@ -749,6 +776,7 @@ function ConvertTo-WorkflowInventoryMarkdown { "| $(ConvertTo-MarkdownCell $item.RunName) " + "| $(ConvertTo-MarkdownCell $item.Events) " + "| $(ConvertTo-MarkdownCell $referencesForItem) " + + "| $(ConvertTo-MarkdownCell $item.MatchesTarget) " + "| $(ConvertTo-MarkdownCell $versionsForItem) " + "| $(ConvertTo-MarkdownCell $item.PullRequestTypes) " + "| $(ConvertTo-MarkdownCell $item.PushBranches) " + @@ -805,7 +833,10 @@ if (-not $workflowFiles) { $inventory = @( $workflowFiles | ForEach-Object { - Get-WorkflowInventoryItem -WorkflowFile $_ -ExpectedReference $WorkflowReference + Get-WorkflowInventoryItem ` + -WorkflowFile $_ ` + -ExpectedReference $WorkflowReference ` + -ExpectedTargetReference $TargetReference } ) @@ -829,7 +860,8 @@ if ($MarkdownPath) { } ConvertTo-WorkflowInventoryMarkdown ` -Inventory $inventory ` - -Source $PSCmdlet.ParameterSetName | + -Source $PSCmdlet.ParameterSetName ` + -TargetReference $TargetReference | Set-Content -LiteralPath $MarkdownPath -Encoding utf8 } diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index f1ac9cde..a99fd49e 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -82,7 +82,11 @@ AfterAll { Describe 'Get-ProcessPSModuleWorkflowInventory' { It 'inventories matching local workflows and their compatibility dimensions' { - $result = @(& $scriptPath -Path $testRoot) + $result = @( + & $scriptPath ` + -Path $testRoot ` + -TargetReference '0123456789012345678901234567890123456789' + ) $result.Count | Should -Be 1 $result[0].Repository | Should -Be 'Example' @@ -93,6 +97,8 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { $result[0].ConcurrencyGroup | Should -Be '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' $result[0].CancelInProgress | Should -BeFalse $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + $result[0].ProcessJobs[0].MatchesTarget | Should -BeTrue + $result[0].MatchesTarget | Should -BeTrue $result[0].ProcessJobs[0].Condition | Should -Match 'head.repo.full_name' $result[0].ProcessJobs[0].Inputs.Keys | Should -Contain 'Debug' $result[0].ProcessJobs[0].SecretMappings.Keys | Should -Be @( @@ -113,13 +119,19 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { $jsonPath = Join-Path $testRoot 'inventory.json' $markdownPath = Join-Path $testRoot 'inventory.md' - & $scriptPath -Path $repositoryRoot -JsonPath $jsonPath -MarkdownPath $markdownPath | Out-Null + & $scriptPath ` + -Path $repositoryRoot ` + -TargetReference '0123456789012345678901234567890123456789' ` + -JsonPath $jsonPath ` + -MarkdownPath $markdownPath | + Out-Null Test-Path -LiteralPath $jsonPath | Should -BeTrue Test-Path -LiteralPath $markdownPath | Should -BeTrue (Get-Content -LiteralPath $jsonPath -Raw).TrimStart() | Should -Match '^\[' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Example' Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0123456789012345678901234567890123456789' + Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Matching target: 1/1' } It 'records a parse error for a matching malformed workflow' { From b3c95e664dfa78a148a8c056bf422d8c35fb6c23 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:13:05 +0200 Subject: [PATCH 07/26] Match workflow targets case-sensitively Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 2 +- ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 6f3027d0..940294be 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -504,7 +504,7 @@ function Get-WorkflowInventoryItem { Uses = "$uses" Reference = "$uses".Substring("$ExpectedReference@".Length) MatchesTarget = if ($ExpectedTargetReference) { - "$uses".Substring("$ExpectedReference@".Length) -eq $ExpectedTargetReference + "$uses".Substring("$ExpectedReference@".Length) -ceq $ExpectedTargetReference } else { $null } diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index a99fd49e..659c93f4 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -39,7 +39,7 @@ permissions: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@0123456789012345678901234567890123456789 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 with: Debug: true secrets: @@ -66,8 +66,8 @@ jobs: & git -C $repositoryRoot switch --quiet -c feature $featureContent = Get-Content -LiteralPath (Join-Path $workflowRoot 'Process-PSModule.yml') -Raw $featureContent.Replace( - '0123456789012345678901234567890123456789', - 'ffffffffffffffffffffffffffffffffffffffff' + 'workflow.yml@v8', + 'workflow.yml@v9' ) | Set-Content -LiteralPath (Join-Path $workflowRoot 'Process-PSModule.yml') } @@ -85,7 +85,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { $result = @( & $scriptPath ` -Path $testRoot ` - -TargetReference '0123456789012345678901234567890123456789' + -TargetReference 'v8' ) $result.Count | Should -Be 1 @@ -96,7 +96,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { $result[0].PullRequestTypes | Should -Be @('opened', 'synchronize') $result[0].ConcurrencyGroup | Should -Be '${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}' $result[0].CancelInProgress | Should -BeFalse - $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + $result[0].ProcessJobs[0].Reference | Should -Be 'v8' $result[0].ProcessJobs[0].MatchesTarget | Should -BeTrue $result[0].MatchesTarget | Should -BeTrue $result[0].ProcessJobs[0].Condition | Should -Match 'head.repo.full_name' @@ -108,11 +108,18 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { ) } + It 'compares target references case-sensitively' { + $result = @(& $scriptPath -Path $repositoryRoot -TargetReference 'V8') + + $result[0].ProcessJobs[0].MatchesTarget | Should -BeFalse + $result[0].MatchesTarget | Should -BeFalse + } + It 'reads the remote default branch instead of feature-worktree changes' { $result = @(& $scriptPath -Path $repositoryRoot) $result[0].DefaultBranch | Should -Be 'main' - $result[0].ProcessJobs[0].Reference | Should -Be '0123456789012345678901234567890123456789' + $result[0].ProcessJobs[0].Reference | Should -Be 'v8' } It 'writes JSON and Markdown refresh artifacts' { @@ -121,7 +128,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { & $scriptPath ` -Path $repositoryRoot ` - -TargetReference '0123456789012345678901234567890123456789' ` + -TargetReference 'v8' ` -JsonPath $jsonPath ` -MarkdownPath $markdownPath | Out-Null @@ -130,7 +137,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { Test-Path -LiteralPath $markdownPath | Should -BeTrue (Get-Content -LiteralPath $jsonPath -Raw).TrimStart() | Should -Match '^\[' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Example' - Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0123456789012345678901234567890123456789' + Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'v8' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Matching target: 1/1' } From 667dbc21524b129c6dabab69cc9674a80c87fbed Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:13:42 +0200 Subject: [PATCH 08/26] Document controlled major workflow tags Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/get-started/repository-setup.md | 2 +- docs/content/guides/calling-the-workflow.md | 15 ++++++++++----- .../guides/github-app-authentication.md | 2 +- docs/content/reference/repository-standard.md | 18 +++++++++++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/content/get-started/repository-setup.md b/docs/content/get-started/repository-setup.md index 8686efcc..d9a51adb 100644 --- a/docs/content/get-started/repository-setup.md +++ b/docs/content/get-started/repository-setup.md @@ -66,7 +66,7 @@ permissions: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/guides/calling-the-workflow.md b/docs/content/guides/calling-the-workflow.md index dd319005..1b9f6eb3 100644 --- a/docs/content/guides/calling-the-workflow.md +++ b/docs/content/guides/calling-the-workflow.md @@ -49,7 +49,7 @@ permissions: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -68,6 +68,11 @@ the PowerShell Gallery, GitHub Releases, and tags, so later runs must queue rath The reusable workflow uses its own prefixed concurrency group, so it cannot queue behind the caller while the caller waits for it to finish. +`Process-PSModule` is PSModule-owned automation, so callers use the controlled floating major tag (`@v8`). Compatible +patch and minor releases move that tag through the release workflow. A breaking release publishes a new major tag and +uses a deliberate fleet campaign rather than moving `v8` across the breaking boundary. External actions remain pinned +to full commit SHAs. + The job condition skips fork-originated pull requests because GitHub does not expose the required repository secrets to forks. Use a separate secret-free, read-only workflow if the repository accepts contributions from forks and requires fork CI. @@ -100,7 +105,7 @@ changes: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -130,7 +135,7 @@ content lines stay at the same indentation level: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -243,7 +248,7 @@ You can also pass patterns via the workflow input: jobs: Process: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 with: ImportantFilePatterns: | ^src/ @@ -257,7 +262,7 @@ To disable triggering via the workflow input, pass an explicit empty string: jobs: process: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 with: ImportantFilePatterns: '' ``` diff --git a/docs/content/guides/github-app-authentication.md b/docs/content/guides/github-app-authentication.md index 5f0dcb2b..7dda4776 100644 --- a/docs/content/guides/github-app-authentication.md +++ b/docs/content/guides/github-app-authentication.md @@ -24,7 +24,7 @@ names. Map the caller's secrets explicitly: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/reference/repository-standard.md b/docs/content/reference/repository-standard.md index b2690049..aad20549 100644 --- a/docs/content/reference/repository-standard.md +++ b/docs/content/reference/repository-standard.md @@ -123,14 +123,22 @@ The caller workflow declares the triggers, concurrency, and permissions for the jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@ # + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} ``` -Name the caller file `Process-PSModule.yml`, matching [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) and every existing module repository. `workflow.yml` is the reusable workflow's own filename inside `PSModule/Process-PSModule` and belongs only in the `uses:` reference. Pin the reference to a commit SHA with the version tag in a trailing comment so Dependabot can update it. +Name the caller file `Process-PSModule.yml`, matching [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) and every existing module repository. `workflow.yml` is the reusable workflow's own filename inside `PSModule/Process-PSModule` and belongs only in the `uses:` reference. + +`Process-PSModule` is PSModule-owned automation. Pin it to the approved floating major tag (`v8`) so compatible patch +and minor releases move across the fleet without one pull request per release. The release workflow owns movement of +the major tag; an incompatible release creates a new major tag and requires a deliberate fleet campaign. Do not use a +branch, `latest`, a floating minor tag, or an exact release/commit for the standard caller. + +This internal-major-tag policy does not apply to third-party actions. External actions remain pinned to their full +immutable commit SHA with the release version in a trailing comment. ## Required common files @@ -194,7 +202,11 @@ For PSModule module repositories, the requirements are: Every module repository must include `.github/dependabot.yml`. Dependabot is part of the repository supply-chain control, not an optional convenience. -Configure the `github-actions` ecosystem. It keeps the pinned actions current, including the pinned `PSModule/Process-PSModule` reference in the [caller workflow](#caller-workflow-and-reusable-workflow). This is what [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) ships, and it is the default for new repositories: +Configure the `github-actions` ecosystem. It keeps external SHA-pinned actions current and proposes intentional major +updates when supported. Compatible Process-PSModule patch and minor releases arrive through its controlled major tag +instead of a Dependabot pull request. This is what +[`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) ships, and it is the default for new +repositories: ```yaml version: 2 From bc5036919bbb03c7a89db096576dfa2a2fb0ff8c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:13:47 +0200 Subject: [PATCH 09/26] Prepare the v8 fleet campaign Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-inventory.md | 128 +++++++++--------- .../process-workflow-fleet-standard.md | 60 ++++++-- 2 files changed, 117 insertions(+), 71 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-inventory.md b/docs/content/reference/process-workflow-fleet-inventory.md index 0eb3b78e..463004b3 100644 --- a/docs/content/reference/process-workflow-fleet-inventory.md +++ b/docs/content/reference/process-workflow-fleet-inventory.md @@ -5,12 +5,14 @@ description: Generated inventory of PSModule repositories that call the Process- # Process-PSModule workflow inventory -Generated: 2026-08-15T13:13:06+02:00 +Generated: 2026-08-15T14:07:05+02:00 - Source: GitHub - Workflow files: 60 - Parsed: 60 - Parse errors: 0 +- Target reference: v8 +- Matching target: 0/60 ## Reference distribution @@ -48,65 +50,65 @@ Generated: 2026-08-15T13:13:06+02:00 ## Workflow files -| Repository | File | Name | Run name | Events | Reference | Version | PR types | Push branches | Schedule | Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| [PSModule/Admin](https://github.com/PSModule/Admin) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Admin/blob/c21efa2de875b25775cad332641b7509db2274b2/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Anthropic](https://github.com/PSModule/Anthropic) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Anthropic/blob/507a3fea65c13965fc550d1eec209db300436e49/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Ast](https://github.com/PSModule/Ast) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Ast/blob/769af9815cc948c21860f7392b0538df2065b20e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Base64](https://github.com/PSModule/Base64) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Base64/blob/f8a7942f4f857b26cdb63199670e480fba5e9d61/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Bluesky](https://github.com/PSModule/Bluesky) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Bluesky/blob/18503ebdf04e401434df2028d75899eb39cdc5db/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/CasingStyle](https://github.com/PSModule/CasingStyle) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CasingStyle/blob/fc26c170059b8012bdbde34031ffe47ac5cc53ec/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Claude](https://github.com/PSModule/Claude) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Claude/blob/081aae987ee37fc6d1f0142b376f92688dfcf2b0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Confluence](https://github.com/PSModule/Confluence) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Confluence/blob/3e5a057dca611ae1036bdd5731cc7031f2657144/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/Context](https://github.com/PSModule/Context) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context/blob/c80a0a0d97b88f6140ea351962ddf257a4f02b90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Context7](https://github.com/PSModule/Context7) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context7/blob/e56e0118107fae5e5cf1385711df8659d67dfde0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/CurseForge](https://github.com/PSModule/CurseForge) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CurseForge/blob/41373542ae348296a1ac5b74730946350afedfb0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | v5.4.3 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/DateTime](https://github.com/PSModule/DateTime) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DateTime/blob/17b99ed2aad7b63df512b61267a4f00436897e92/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/DeepSeek](https://github.com/PSModule/DeepSeek) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DeepSeek/blob/a12f3fe69db3c12cff2b22db017516df10db610e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Discord](https://github.com/PSModule/Discord) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Discord/blob/560a78d92a33ecdb080c33c1e28f6094da5c5d1e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Dns](https://github.com/PSModule/Dns) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Dns/blob/58558ff6c0bef552d087360bf5b0d6ad37eecf7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Domeneshop](https://github.com/PSModule/Domeneshop) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Domeneshop/blob/2ca6f788c4b68a72c63d6472ae19720bc90cc8b9/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/DynamicParams](https://github.com/PSModule/DynamicParams) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DynamicParams/blob/092726b82bf38bf8a49bf98cb1349dc6be691fde/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/ElvUI](https://github.com/PSModule/ElvUI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/ElvUI/blob/892a68211feb229698cfabc6e36abfee56727a30/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Fonts](https://github.com/PSModule/Fonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Fonts/blob/d665c51dc39cd4404ea2da2c9f4efb2cf932faa5/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Gemini](https://github.com/PSModule/Gemini) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Gemini/blob/eadf88ffc09f311d71ff398d36f27f07d188e64c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/GitHub](https://github.com/PSModule/GitHub) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GitHub/blob/3e1f9e7651797091830338ca4c36fb6814bef69f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/GoogleFonts](https://github.com/PSModule/GoogleFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GoogleFonts/blob/bb329c6912eaa9861d285a7f9915879d72a60567/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/GraphQL](https://github.com/PSModule/GraphQL) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GraphQL/blob/051ed470d8c81170c062a719a4d3a4343e3bd691/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Guid](https://github.com/PSModule/Guid) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Guid/blob/9ea7942021dc21307f774f6c5e63425529501233/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/GZip](https://github.com/PSModule/GZip) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GZip/blob/4422836a1cb68a8f06884c791ca22be62b808e79/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Hashtable](https://github.com/PSModule/Hashtable) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hashtable/blob/680c3e8291dfc1696dfad1658b50a0f28cf5a86f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Hcl](https://github.com/PSModule/Hcl) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hcl/blob/aa15ae16894757e8d2659cc1b0de42fc922178b3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/IPv4](https://github.com/PSModule/IPv4) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv4/blob/7c63729742f68e985d2216eac79d0ae4d097a756/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/IPv6](https://github.com/PSModule/IPv6) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv6/blob/f53d3e9b081f07557bb6542a916f41f42abbbb55/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Json](https://github.com/PSModule/Json) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Json/blob/4a996b7af4a354a90a2753fc1d00d31e9676fd11/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Jwt](https://github.com/PSModule/Jwt) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Jwt/blob/aa64677452cdd4cee62520439f48cdec1ec8621d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/LinkedIn](https://github.com/PSModule/LinkedIn) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/LinkedIn/blob/e129428630585da0c27d7c3466f4f3599a7be209/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Lovdata](https://github.com/PSModule/Lovdata) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lovdata/blob/dfe0f79562fecb8f99ed6e172ad3e4baa35cf821/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Lua](https://github.com/PSModule/Lua) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lua/blob/d532ebfca1c2d042b1a80846af3038ef2ff87386/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Markdown](https://github.com/PSModule/Markdown) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Markdown/blob/3377a6c9bd507a1ad25225fb8c2bb209114215eb/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/MemoryMappedFile](https://github.com/PSModule/MemoryMappedFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/MemoryMappedFile/blob/3f5eb7de7484558696ba9a633c7b39e084e2358c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/NerdFonts](https://github.com/PSModule/NerdFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/NerdFonts/blob/5a9abcb31663bb9d0e7ac58f904efa31a35e80b1/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | bf67cd90269ca5ce25cd76b203678907dc2984b4 | v6.1.19 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Net](https://github.com/PSModule/Net) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Net/blob/2facaf6bfa442a92f45b71094952e89999bf1024/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Object](https://github.com/PSModule/Object) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Object/blob/c385cfd09ec0ee9483a3123cbfd70cd1c26432cf/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/OpenAI](https://github.com/PSModule/OpenAI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/OpenAI/blob/d44c807117fda26311a1de0d14ef7fb071767597/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Path](https://github.com/PSModule/Path) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Path/blob/ddf6ba8a813819b6815411deffbe28fa678640a4/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PowerShellDataFile](https://github.com/PSModule/PowerShellDataFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellDataFile/blob/d256fea8410477e8b68e6bffa0b8085bb856ab0c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PowerShellGallery](https://github.com/PSModule/PowerShellGallery) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellGallery/blob/ea0734cf47e6b5957f61d6e9cb53f0ea3a9eb1ad/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSCredential](https://github.com/PSModule/PSCredential) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCredential/blob/87cdfd19eceef381faf37bb124d02dba7fa5f3c6/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSCustomObject](https://github.com/PSModule/PSCustomObject) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCustomObject/blob/25dd9dc1872f0cc7386e977d713b918738fcbb71/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSSemVer](https://github.com/PSModule/PSSemVer) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSSemVer/blob/1a621b14286331569f4d1ffd4643999c2a2a6ca8/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/PublicIP](https://github.com/PSModule/PublicIP) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PublicIP/blob/82f70c40a309c9b7390160035a6f836bd29da626/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Retry](https://github.com/PSModule/Retry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Retry/blob/2ecfcb46c3204a167a011202a663639996fa1895/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Sodium](https://github.com/PSModule/Sodium) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Sodium/blob/3d96d48c63758298616ab80215f59d4fed7cb7d3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Telemetry](https://github.com/PSModule/Telemetry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Telemetry/blob/e85cf2df6611ba056eafa46610b17c06674a71de/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Template-PSModule](https://github.com/PSModule/Template-PSModule) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Template-PSModule/blob/4f525ab008d2d616f2f4e4e20ee2d96d6f76ec67/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/TimeSpan](https://github.com/PSModule/TimeSpan) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/TimeSpan/blob/8700bcdc8340b52f7eab7b83414def47c36ed3a0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Tls](https://github.com/PSModule/Tls) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Tls/blob/11ea777e89668c1c4fd6280b312294db01a5ed4d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Toml](https://github.com/PSModule/Toml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Toml/blob/f8937f27af3c663c80fc3ca986a3aaa40a191a90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Twitch](https://github.com/PSModule/Twitch) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Twitch/blob/9d25274b23af02c45ecc6fb28f682883afa47027/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Uri](https://github.com/PSModule/Uri) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Uri/blob/fe821cf5c13498a092d919f9d3f8a207912afe96/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Utilities](https://github.com/PSModule/Utilities) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Utilities/blob/3583a87c377650bc4eacff70cef4fcb993c2117d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/WoW](https://github.com/PSModule/WoW) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/WoW/blob/fd432f8ea872d546eabb20488bc5adab15dbaa7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Yaml](https://github.com/PSModule/Yaml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yaml/blob/8e37203720719528495da3bef5273c674a4d2e0a/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | ImportantFilePatterns | | -| [PSModule/Yml](https://github.com/PSModule/Yml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yml/blob/93d2656563a719d99416b9e13a05e65f6f815498/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 1653be8d36607d9535f600278c44789979477813 | v6.1.16 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| Repository | File | Name | Run name | Events | Reference | Target | Version | PR types | Push branches | Schedule | Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| [PSModule/Admin](https://github.com/PSModule/Admin) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Admin/blob/c21efa2de875b25775cad332641b7509db2274b2/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Anthropic](https://github.com/PSModule/Anthropic) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Anthropic/blob/507a3fea65c13965fc550d1eec209db300436e49/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Ast](https://github.com/PSModule/Ast) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Ast/blob/769af9815cc948c21860f7392b0538df2065b20e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Base64](https://github.com/PSModule/Base64) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Base64/blob/f8a7942f4f857b26cdb63199670e480fba5e9d61/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Bluesky](https://github.com/PSModule/Bluesky) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Bluesky/blob/18503ebdf04e401434df2028d75899eb39cdc5db/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CasingStyle](https://github.com/PSModule/CasingStyle) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CasingStyle/blob/fc26c170059b8012bdbde34031ffe47ac5cc53ec/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Claude](https://github.com/PSModule/Claude) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Claude/blob/081aae987ee37fc6d1f0142b376f92688dfcf2b0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Confluence](https://github.com/PSModule/Confluence) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Confluence/blob/3e5a057dca611ae1036bdd5731cc7031f2657144/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/Context](https://github.com/PSModule/Context) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context/blob/c80a0a0d97b88f6140ea351962ddf257a4f02b90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Context7](https://github.com/PSModule/Context7) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context7/blob/e56e0118107fae5e5cf1385711df8659d67dfde0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CurseForge](https://github.com/PSModule/CurseForge) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CurseForge/blob/41373542ae348296a1ac5b74730946350afedfb0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | False | v5.4.3 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DateTime](https://github.com/PSModule/DateTime) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DateTime/blob/17b99ed2aad7b63df512b61267a4f00436897e92/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/DeepSeek](https://github.com/PSModule/DeepSeek) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DeepSeek/blob/a12f3fe69db3c12cff2b22db017516df10db610e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Discord](https://github.com/PSModule/Discord) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Discord/blob/560a78d92a33ecdb080c33c1e28f6094da5c5d1e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Dns](https://github.com/PSModule/Dns) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Dns/blob/58558ff6c0bef552d087360bf5b0d6ad37eecf7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Domeneshop](https://github.com/PSModule/Domeneshop) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Domeneshop/blob/2ca6f788c4b68a72c63d6472ae19720bc90cc8b9/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DynamicParams](https://github.com/PSModule/DynamicParams) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DynamicParams/blob/092726b82bf38bf8a49bf98cb1349dc6be691fde/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/ElvUI](https://github.com/PSModule/ElvUI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/ElvUI/blob/892a68211feb229698cfabc6e36abfee56727a30/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Fonts](https://github.com/PSModule/Fonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Fonts/blob/d665c51dc39cd4404ea2da2c9f4efb2cf932faa5/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Gemini](https://github.com/PSModule/Gemini) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Gemini/blob/eadf88ffc09f311d71ff398d36f27f07d188e64c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/GitHub](https://github.com/PSModule/GitHub) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GitHub/blob/3e1f9e7651797091830338ca4c36fb6814bef69f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/GoogleFonts](https://github.com/PSModule/GoogleFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GoogleFonts/blob/bb329c6912eaa9861d285a7f9915879d72a60567/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/GraphQL](https://github.com/PSModule/GraphQL) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GraphQL/blob/051ed470d8c81170c062a719a4d3a4343e3bd691/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Guid](https://github.com/PSModule/Guid) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Guid/blob/9ea7942021dc21307f774f6c5e63425529501233/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/GZip](https://github.com/PSModule/GZip) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GZip/blob/4422836a1cb68a8f06884c791ca22be62b808e79/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hashtable](https://github.com/PSModule/Hashtable) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hashtable/blob/680c3e8291dfc1696dfad1658b50a0f28cf5a86f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hcl](https://github.com/PSModule/Hcl) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hcl/blob/aa15ae16894757e8d2659cc1b0de42fc922178b3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/IPv4](https://github.com/PSModule/IPv4) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv4/blob/7c63729742f68e985d2216eac79d0ae4d097a756/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/IPv6](https://github.com/PSModule/IPv6) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv6/blob/f53d3e9b081f07557bb6542a916f41f42abbbb55/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Json](https://github.com/PSModule/Json) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Json/blob/4a996b7af4a354a90a2753fc1d00d31e9676fd11/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Jwt](https://github.com/PSModule/Jwt) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Jwt/blob/aa64677452cdd4cee62520439f48cdec1ec8621d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/LinkedIn](https://github.com/PSModule/LinkedIn) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/LinkedIn/blob/e129428630585da0c27d7c3466f4f3599a7be209/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Lovdata](https://github.com/PSModule/Lovdata) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lovdata/blob/dfe0f79562fecb8f99ed6e172ad3e4baa35cf821/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Lua](https://github.com/PSModule/Lua) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lua/blob/d532ebfca1c2d042b1a80846af3038ef2ff87386/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Markdown](https://github.com/PSModule/Markdown) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Markdown/blob/3377a6c9bd507a1ad25225fb8c2bb209114215eb/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/MemoryMappedFile](https://github.com/PSModule/MemoryMappedFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/MemoryMappedFile/blob/3f5eb7de7484558696ba9a633c7b39e084e2358c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/NerdFonts](https://github.com/PSModule/NerdFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/NerdFonts/blob/5a9abcb31663bb9d0e7ac58f904efa31a35e80b1/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | bf67cd90269ca5ce25cd76b203678907dc2984b4 | False | v6.1.19 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Net](https://github.com/PSModule/Net) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Net/blob/2facaf6bfa442a92f45b71094952e89999bf1024/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Object](https://github.com/PSModule/Object) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Object/blob/c385cfd09ec0ee9483a3123cbfd70cd1c26432cf/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/OpenAI](https://github.com/PSModule/OpenAI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/OpenAI/blob/d44c807117fda26311a1de0d14ef7fb071767597/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Path](https://github.com/PSModule/Path) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Path/blob/ddf6ba8a813819b6815411deffbe28fa678640a4/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellDataFile](https://github.com/PSModule/PowerShellDataFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellDataFile/blob/d256fea8410477e8b68e6bffa0b8085bb856ab0c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellGallery](https://github.com/PSModule/PowerShellGallery) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellGallery/blob/ea0734cf47e6b5957f61d6e9cb53f0ea3a9eb1ad/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCredential](https://github.com/PSModule/PSCredential) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCredential/blob/87cdfd19eceef381faf37bb124d02dba7fa5f3c6/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCustomObject](https://github.com/PSModule/PSCustomObject) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCustomObject/blob/25dd9dc1872f0cc7386e977d713b918738fcbb71/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSSemVer](https://github.com/PSModule/PSSemVer) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSSemVer/blob/1a621b14286331569f4d1ffd4643999c2a2a6ca8/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/PublicIP](https://github.com/PSModule/PublicIP) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PublicIP/blob/82f70c40a309c9b7390160035a6f836bd29da626/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Retry](https://github.com/PSModule/Retry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Retry/blob/2ecfcb46c3204a167a011202a663639996fa1895/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Sodium](https://github.com/PSModule/Sodium) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Sodium/blob/3d96d48c63758298616ab80215f59d4fed7cb7d3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Telemetry](https://github.com/PSModule/Telemetry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Telemetry/blob/e85cf2df6611ba056eafa46610b17c06674a71de/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Template-PSModule](https://github.com/PSModule/Template-PSModule) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Template-PSModule/blob/4f525ab008d2d616f2f4e4e20ee2d96d6f76ec67/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/TimeSpan](https://github.com/PSModule/TimeSpan) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/TimeSpan/blob/8700bcdc8340b52f7eab7b83414def47c36ed3a0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Tls](https://github.com/PSModule/Tls) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Tls/blob/11ea777e89668c1c4fd6280b312294db01a5ed4d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Toml](https://github.com/PSModule/Toml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Toml/blob/f8937f27af3c663c80fc3ca986a3aaa40a191a90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Twitch](https://github.com/PSModule/Twitch) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Twitch/blob/9d25274b23af02c45ecc6fb28f682883afa47027/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Uri](https://github.com/PSModule/Uri) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Uri/blob/fe821cf5c13498a092d919f9d3f8a207912afe96/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Utilities](https://github.com/PSModule/Utilities) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Utilities/blob/3583a87c377650bc4eacff70cef4fcb993c2117d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/WoW](https://github.com/PSModule/WoW) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/WoW/blob/fd432f8ea872d546eabb20488bc5adab15dbaa7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Yaml](https://github.com/PSModule/Yaml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yaml/blob/8e37203720719528495da3bef5273c674a4d2e0a/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | ImportantFilePatterns | | +| [PSModule/Yml](https://github.com/PSModule/Yml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yml/blob/93d2656563a719d99416b9e13a05e65f6f815498/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 1653be8d36607d9535f600278c44789979477813 | False | v6.1.16 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index f7786e90..d3a4d5d5 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -14,6 +14,7 @@ workflow. Refresh it with: ```powershell ./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` -Organization PSModule ` + -TargetReference v8 ` -JsonPath ./output/process-workflows.json ` -MarkdownPath ./docs/content/reference/process-workflow-fleet-inventory.md ``` @@ -24,6 +25,7 @@ GitHub discovery, use the local parameter set: ```powershell ./.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 ` -Path C:\Repos, C:\Users\me\.copilot\repos ` + -TargetReference v8 ` -JsonPath ./output/process-workflows.json ` -MarkdownPath ./output/process-workflows.md ``` @@ -118,14 +120,32 @@ permissions: jobs: Process-PSModule: if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@5a11e8e8b018faf97017e0416f136a751c026713 # v8.0.0 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} ``` -The full commit SHA is the machine-enforced pin. The version comment is required for humans and Dependabot. +The `v8` reference is the controlled moving major tag for this PSModule-owned workflow. On 2026-08-15, `v8`, `v8.0`, +and the immutable `v8.0.0` release tag all resolve to commit `5a11e8e8b018faf97017e0416f136a751c026713`. +`Release-GHRepository` creates and advances major and minor tags by default, while the organization tag ruleset prevents +deletion or non-fast-forward updates of exact `*.*.*` release tags. + +The effective repository rulesets currently protect exact release tags but do not restrict movement of `v8` itself. +Release automation is therefore the operational owner, but actors with sufficient contents access are not yet blocked +from moving the major tag manually. Enforce release-identity-only governance for moving tags before migrating the fleet +to `@v8`; until then, consumers must retain immutable SHA references. + +### Owned and external references + +| Automation source | Standard reference | Update model | +| --- | --- | --- | +| PSModule-owned action or reusable workflow | Floating major tag such as `@v8` | Compatible patch and minor releases advance the major tag through controlled release automation. | +| External action or reusable workflow | Full commit SHA with a trailing release-version comment | Dependabot proposes reviewed SHA updates; upstream cannot silently change the referenced code. | + +A major tag never crosses a breaking boundary. `v8` remains on the latest compatible `8.x` release; `v9` begins a new +fleet campaign. Branch names, `latest`, floating minor tags, and unqualified targets are not accepted pins. ## Required elements @@ -139,7 +159,7 @@ The full commit SHA is the machine-enforced pin. The version comment is required | Concurrency | Use the PR-number-or-ref key with `cancel-in-progress: false`. | Cleanup and stable release runs stay distinct; release mutations queue instead of being interrupted. | | Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. | | Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | -| Reference | Pin the latest approved release to its full commit SHA and retain the version comment. | Immutable supply-chain reference with readable update context. | +| Reference | Use the approved internal floating major tag (`v8`). | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | | Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | @@ -163,7 +183,8 @@ The following are migration defects or require a documented exception: - `secrets: inherit`; - `APIKey` or `APIKEY` mappings from the pre-`v7` contract; -- a mutable tag instead of a full commit SHA; +- any Process-PSModule reference other than the approved major tag (`v8`), including a branch, `latest`, minor tag, + exact patch tag, or full commit SHA; - missing `push` or `unlabeled` triggers; - `cancel-in-progress: true` or the old ref-only concurrency key; - trigger-level path filters that bypass Process-PSModule important-file evaluation; @@ -180,7 +201,30 @@ tasks. ## Rollout boundary -This research does not change consumer repositories. A fleet campaign should use one delivery issue and one early draft -pull request per repository, preserve the supported optional mappings discovered here, and replace every historical -credential mapping with the explicit `v8` contract. The inventory should be refreshed immediately before creating the -campaign leaves and again before declaring the campaign complete. +This research does not change consumer repositories. The campaign should use the stable slug +`process-v8-major-tag`, one delivery issue, branch, and early draft pull request per repository, and these waves: + +| Wave | Repositories | Change profile | +| --- | ---: | --- | +| Pilot | 1 | Update `Template-PSModule` first and use its final caller as the generated-repository reference. | +| Inherited secrets | 41 | Replace `secrets: inherit` with the three explicit `v8` credential mappings. | +| Old API key only | 14 | Replace `APIKey`/`APIKEY` with the three explicit mappings; excludes the template pilot. | +| Test data | 3 | Preserve each existing `TestData` payload while replacing the old API key contract. | +| Custom input | 1 | Update `Yaml` last while preserving `TestData` and `ImportantFilePatterns`. | + +Before opening leaves: + +1. Confirm `v8` and `v8.0.0` resolve to the same tested release commit. +2. Restrict moving major-tag updates to the controlled release identity. Do not start the consumer rollout while another + identity can move `v8`; retain immutable SHA references until this gate is enforced. +3. Have an organization administrator confirm `PSGALLERY_API_KEY`, `SHELLY_CLIENT_ID`, and `SHELLY_PRIVATE_KEY` coverage + in Actions and Dependabot scope. The inventory token can list repository-local secrets but receives `403` for + organization secret visibility, so inherited coverage is currently unresolved. +4. Refresh the inventory with `-TargetReference v8`; the starting target count should be `0/60`. +5. Confirm workflow-only changes are not important release changes. The fleet defaults match only `src/` and + `README.md`; `Yaml` explicitly matches `src/`, `tests/`, and `README.md`, so this campaign should not publish modules. + +Each leaf applies the common caller, retains only the supported optional mappings, and proves the PR path before merge. +Advance one wave only after the previous wave's push run completes without an unintended release. Completion requires a +fresh inventory showing `60/60` on `v8`, the complete trigger/concurrency contract, explicit credentials, no inherited +secrets or old API-key mappings, and no unresolved review or CI failures. From d85c21377bc5c60ea4cab93befc14238304ee188 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:18:55 +0200 Subject: [PATCH 10/26] Escape cron values in Markdown inventory Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 2 +- ...ProcessPSModuleWorkflowInventory.Tests.ps1 | 1 + .../process-workflow-fleet-inventory.md | 122 +++++++++--------- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 940294be..51902624 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -629,7 +629,7 @@ function ConvertTo-MarkdownCell { return '' } - (($Value -join ', ') -replace '\|', '\|' -replace '\r?\n', '
') + (($Value -join ', ') -replace '\|', '\|' -replace '\*', '\*' -replace '\r?\n', '
') } function ConvertTo-WorkflowInventoryMarkdown { diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index 659c93f4..900946b9 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -139,6 +139,7 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Example' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'v8' Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Matching target: 1/1' + Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0 0 \\\* \\\* \\\*' } It 'records a parse error for a matching malformed workflow' { diff --git a/docs/content/reference/process-workflow-fleet-inventory.md b/docs/content/reference/process-workflow-fleet-inventory.md index 463004b3..48b0131f 100644 --- a/docs/content/reference/process-workflow-fleet-inventory.md +++ b/docs/content/reference/process-workflow-fleet-inventory.md @@ -5,7 +5,7 @@ description: Generated inventory of PSModule repositories that call the Process- # Process-PSModule workflow inventory -Generated: 2026-08-15T14:07:05+02:00 +Generated: 2026-08-15T14:18:44+02:00 - Source: GitHub - Workflow files: 60 @@ -52,63 +52,63 @@ Generated: 2026-08-15T14:07:05+02:00 | Repository | File | Name | Run name | Events | Reference | Target | Version | PR types | Push branches | Schedule | Concurrency | Cancel | Permissions | Condition | Secrets | Inputs | Extra jobs | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| [PSModule/Admin](https://github.com/PSModule/Admin) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Admin/blob/c21efa2de875b25775cad332641b7509db2274b2/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Anthropic](https://github.com/PSModule/Anthropic) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Anthropic/blob/507a3fea65c13965fc550d1eec209db300436e49/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Ast](https://github.com/PSModule/Ast) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Ast/blob/769af9815cc948c21860f7392b0538df2065b20e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Base64](https://github.com/PSModule/Base64) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Base64/blob/f8a7942f4f857b26cdb63199670e480fba5e9d61/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Bluesky](https://github.com/PSModule/Bluesky) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Bluesky/blob/18503ebdf04e401434df2028d75899eb39cdc5db/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/CasingStyle](https://github.com/PSModule/CasingStyle) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CasingStyle/blob/fc26c170059b8012bdbde34031ffe47ac5cc53ec/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Claude](https://github.com/PSModule/Claude) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Claude/blob/081aae987ee37fc6d1f0142b376f92688dfcf2b0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Confluence](https://github.com/PSModule/Confluence) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Confluence/blob/3e5a057dca611ae1036bdd5731cc7031f2657144/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/Context](https://github.com/PSModule/Context) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context/blob/c80a0a0d97b88f6140ea351962ddf257a4f02b90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Context7](https://github.com/PSModule/Context7) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context7/blob/e56e0118107fae5e5cf1385711df8659d67dfde0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/CurseForge](https://github.com/PSModule/CurseForge) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CurseForge/blob/41373542ae348296a1ac5b74730946350afedfb0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | False | v5.4.3 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/DateTime](https://github.com/PSModule/DateTime) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DateTime/blob/17b99ed2aad7b63df512b61267a4f00436897e92/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/DeepSeek](https://github.com/PSModule/DeepSeek) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DeepSeek/blob/a12f3fe69db3c12cff2b22db017516df10db610e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Discord](https://github.com/PSModule/Discord) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Discord/blob/560a78d92a33ecdb080c33c1e28f6094da5c5d1e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Dns](https://github.com/PSModule/Dns) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Dns/blob/58558ff6c0bef552d087360bf5b0d6ad37eecf7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Domeneshop](https://github.com/PSModule/Domeneshop) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Domeneshop/blob/2ca6f788c4b68a72c63d6472ae19720bc90cc8b9/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/DynamicParams](https://github.com/PSModule/DynamicParams) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DynamicParams/blob/092726b82bf38bf8a49bf98cb1349dc6be691fde/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/ElvUI](https://github.com/PSModule/ElvUI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/ElvUI/blob/892a68211feb229698cfabc6e36abfee56727a30/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Fonts](https://github.com/PSModule/Fonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Fonts/blob/d665c51dc39cd4404ea2da2c9f4efb2cf932faa5/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Gemini](https://github.com/PSModule/Gemini) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Gemini/blob/eadf88ffc09f311d71ff398d36f27f07d188e64c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/GitHub](https://github.com/PSModule/GitHub) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GitHub/blob/3e1f9e7651797091830338ca4c36fb6814bef69f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/GoogleFonts](https://github.com/PSModule/GoogleFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GoogleFonts/blob/bb329c6912eaa9861d285a7f9915879d72a60567/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/GraphQL](https://github.com/PSModule/GraphQL) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GraphQL/blob/051ed470d8c81170c062a719a4d3a4343e3bd691/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Guid](https://github.com/PSModule/Guid) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Guid/blob/9ea7942021dc21307f774f6c5e63425529501233/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/GZip](https://github.com/PSModule/GZip) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GZip/blob/4422836a1cb68a8f06884c791ca22be62b808e79/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Hashtable](https://github.com/PSModule/Hashtable) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hashtable/blob/680c3e8291dfc1696dfad1658b50a0f28cf5a86f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Hcl](https://github.com/PSModule/Hcl) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hcl/blob/aa15ae16894757e8d2659cc1b0de42fc922178b3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/IPv4](https://github.com/PSModule/IPv4) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv4/blob/7c63729742f68e985d2216eac79d0ae4d097a756/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/IPv6](https://github.com/PSModule/IPv6) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv6/blob/f53d3e9b081f07557bb6542a916f41f42abbbb55/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Json](https://github.com/PSModule/Json) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Json/blob/4a996b7af4a354a90a2753fc1d00d31e9676fd11/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Jwt](https://github.com/PSModule/Jwt) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Jwt/blob/aa64677452cdd4cee62520439f48cdec1ec8621d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | -| [PSModule/LinkedIn](https://github.com/PSModule/LinkedIn) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/LinkedIn/blob/e129428630585da0c27d7c3466f4f3599a7be209/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Lovdata](https://github.com/PSModule/Lovdata) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lovdata/blob/dfe0f79562fecb8f99ed6e172ad3e4baa35cf821/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Lua](https://github.com/PSModule/Lua) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lua/blob/d532ebfca1c2d042b1a80846af3038ef2ff87386/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | -| [PSModule/Markdown](https://github.com/PSModule/Markdown) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Markdown/blob/3377a6c9bd507a1ad25225fb8c2bb209114215eb/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/MemoryMappedFile](https://github.com/PSModule/MemoryMappedFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/MemoryMappedFile/blob/3f5eb7de7484558696ba9a633c7b39e084e2358c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/NerdFonts](https://github.com/PSModule/NerdFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/NerdFonts/blob/5a9abcb31663bb9d0e7ac58f904efa31a35e80b1/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | bf67cd90269ca5ce25cd76b203678907dc2984b4 | False | v6.1.19 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Net](https://github.com/PSModule/Net) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Net/blob/2facaf6bfa442a92f45b71094952e89999bf1024/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Object](https://github.com/PSModule/Object) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Object/blob/c385cfd09ec0ee9483a3123cbfd70cd1c26432cf/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/OpenAI](https://github.com/PSModule/OpenAI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/OpenAI/blob/d44c807117fda26311a1de0d14ef7fb071767597/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Path](https://github.com/PSModule/Path) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Path/blob/ddf6ba8a813819b6815411deffbe28fa678640a4/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PowerShellDataFile](https://github.com/PSModule/PowerShellDataFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellDataFile/blob/d256fea8410477e8b68e6bffa0b8085bb856ab0c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PowerShellGallery](https://github.com/PSModule/PowerShellGallery) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellGallery/blob/ea0734cf47e6b5957f61d6e9cb53f0ea3a9eb1ad/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSCredential](https://github.com/PSModule/PSCredential) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCredential/blob/87cdfd19eceef381faf37bb124d02dba7fa5f3c6/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSCustomObject](https://github.com/PSModule/PSCustomObject) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCustomObject/blob/25dd9dc1872f0cc7386e977d713b918738fcbb71/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/PSSemVer](https://github.com/PSModule/PSSemVer) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSSemVer/blob/1a621b14286331569f4d1ffd4643999c2a2a6ca8/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/PublicIP](https://github.com/PSModule/PublicIP) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PublicIP/blob/82f70c40a309c9b7390160035a6f836bd29da626/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Retry](https://github.com/PSModule/Retry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Retry/blob/2ecfcb46c3204a167a011202a663639996fa1895/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Sodium](https://github.com/PSModule/Sodium) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Sodium/blob/3d96d48c63758298616ab80215f59d4fed7cb7d3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Telemetry](https://github.com/PSModule/Telemetry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Telemetry/blob/e85cf2df6611ba056eafa46610b17c06674a71de/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Template-PSModule](https://github.com/PSModule/Template-PSModule) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Template-PSModule/blob/4f525ab008d2d616f2f4e4e20ee2d96d6f76ec67/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/TimeSpan](https://github.com/PSModule/TimeSpan) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/TimeSpan/blob/8700bcdc8340b52f7eab7b83414def47c36ed3a0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Tls](https://github.com/PSModule/Tls) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Tls/blob/11ea777e89668c1c4fd6280b312294db01a5ed4d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Toml](https://github.com/PSModule/Toml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Toml/blob/f8937f27af3c663c80fc3ca986a3aaa40a191a90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | -| [PSModule/Twitch](https://github.com/PSModule/Twitch) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Twitch/blob/9d25274b23af02c45ecc6fb28f682883afa47027/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Uri](https://github.com/PSModule/Uri) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Uri/blob/fe821cf5c13498a092d919f9d3f8a207912afe96/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Utilities](https://github.com/PSModule/Utilities) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Utilities/blob/3583a87c377650bc4eacff70cef4fcb993c2117d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/WoW](https://github.com/PSModule/WoW) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/WoW/blob/fd432f8ea872d546eabb20488bc5adab15dbaa7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | -| [PSModule/Yaml](https://github.com/PSModule/Yaml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yaml/blob/8e37203720719528495da3bef5273c674a4d2e0a/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | ImportantFilePatterns | | -| [PSModule/Yml](https://github.com/PSModule/Yml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yml/blob/93d2656563a719d99416b9e13a05e65f6f815498/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 1653be8d36607d9535f600278c44789979477813 | False | v6.1.16 | closed, opened, reopened, synchronize, labeled | | 0 0 * * * | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Admin](https://github.com/PSModule/Admin) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Admin/blob/c21efa2de875b25775cad332641b7509db2274b2/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Anthropic](https://github.com/PSModule/Anthropic) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Anthropic/blob/507a3fea65c13965fc550d1eec209db300436e49/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Ast](https://github.com/PSModule/Ast) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Ast/blob/769af9815cc948c21860f7392b0538df2065b20e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Base64](https://github.com/PSModule/Base64) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Base64/blob/f8a7942f4f857b26cdb63199670e480fba5e9d61/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Bluesky](https://github.com/PSModule/Bluesky) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Bluesky/blob/18503ebdf04e401434df2028d75899eb39cdc5db/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CasingStyle](https://github.com/PSModule/CasingStyle) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CasingStyle/blob/fc26c170059b8012bdbde34031ffe47ac5cc53ec/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Claude](https://github.com/PSModule/Claude) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Claude/blob/081aae987ee37fc6d1f0142b376f92688dfcf2b0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Confluence](https://github.com/PSModule/Confluence) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Confluence/blob/3e5a057dca611ae1036bdd5731cc7031f2657144/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/Context](https://github.com/PSModule/Context) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context/blob/c80a0a0d97b88f6140ea351962ddf257a4f02b90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Context7](https://github.com/PSModule/Context7) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Context7/blob/e56e0118107fae5e5cf1385711df8659d67dfde0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/CurseForge](https://github.com/PSModule/CurseForge) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/CurseForge/blob/41373542ae348296a1ac5b74730946350afedfb0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 60bdf8a5a4c92c53fcf2a8d23f7d5f5c93e6864e | False | v5.4.3 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DateTime](https://github.com/PSModule/DateTime) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DateTime/blob/17b99ed2aad7b63df512b61267a4f00436897e92/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/DeepSeek](https://github.com/PSModule/DeepSeek) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DeepSeek/blob/a12f3fe69db3c12cff2b22db017516df10db610e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Discord](https://github.com/PSModule/Discord) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Discord/blob/560a78d92a33ecdb080c33c1e28f6094da5c5d1e/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Dns](https://github.com/PSModule/Dns) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Dns/blob/58558ff6c0bef552d087360bf5b0d6ad37eecf7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Domeneshop](https://github.com/PSModule/Domeneshop) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Domeneshop/blob/2ca6f788c4b68a72c63d6472ae19720bc90cc8b9/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/DynamicParams](https://github.com/PSModule/DynamicParams) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/DynamicParams/blob/092726b82bf38bf8a49bf98cb1349dc6be691fde/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/ElvUI](https://github.com/PSModule/ElvUI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/ElvUI/blob/892a68211feb229698cfabc6e36abfee56727a30/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Fonts](https://github.com/PSModule/Fonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Fonts/blob/d665c51dc39cd4404ea2da2c9f4efb2cf932faa5/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Gemini](https://github.com/PSModule/Gemini) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Gemini/blob/eadf88ffc09f311d71ff398d36f27f07d188e64c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/GitHub](https://github.com/PSModule/GitHub) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GitHub/blob/3e1f9e7651797091830338ca4c36fb6814bef69f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/GoogleFonts](https://github.com/PSModule/GoogleFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GoogleFonts/blob/bb329c6912eaa9861d285a7f9915879d72a60567/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/GraphQL](https://github.com/PSModule/GraphQL) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GraphQL/blob/051ed470d8c81170c062a719a4d3a4343e3bd691/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Guid](https://github.com/PSModule/Guid) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Guid/blob/9ea7942021dc21307f774f6c5e63425529501233/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/GZip](https://github.com/PSModule/GZip) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/GZip/blob/4422836a1cb68a8f06884c791ca22be62b808e79/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hashtable](https://github.com/PSModule/Hashtable) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hashtable/blob/680c3e8291dfc1696dfad1658b50a0f28cf5a86f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | da180bac16b13bfbcdf08b2e4e221b5b49e5ff28 | False | v6.1.4 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Hcl](https://github.com/PSModule/Hcl) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Hcl/blob/aa15ae16894757e8d2659cc1b0de42fc922178b3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/IPv4](https://github.com/PSModule/IPv4) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv4/blob/7c63729742f68e985d2216eac79d0ae4d097a756/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/IPv6](https://github.com/PSModule/IPv6) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/IPv6/blob/f53d3e9b081f07557bb6542a916f41f42abbbb55/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Json](https://github.com/PSModule/Json) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Json/blob/4a996b7af4a354a90a2753fc1d00d31e9676fd11/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Jwt](https://github.com/PSModule/Jwt) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Jwt/blob/aa64677452cdd4cee62520439f48cdec1ec8621d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | | | +| [PSModule/LinkedIn](https://github.com/PSModule/LinkedIn) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/LinkedIn/blob/e129428630585da0c27d7c3466f4f3599a7be209/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Lovdata](https://github.com/PSModule/Lovdata) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lovdata/blob/dfe0f79562fecb8f99ed6e172ad3e4baa35cf821/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Lua](https://github.com/PSModule/Lua) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Lua/blob/d532ebfca1c2d042b1a80846af3038ef2ff87386/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 11117919e65242d3388727819a751f74ad24ea9e | False | v5.5.0 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKEY | | | +| [PSModule/Markdown](https://github.com/PSModule/Markdown) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Markdown/blob/3377a6c9bd507a1ad25225fb8c2bb209114215eb/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/MemoryMappedFile](https://github.com/PSModule/MemoryMappedFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/MemoryMappedFile/blob/3f5eb7de7484558696ba9a633c7b39e084e2358c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/NerdFonts](https://github.com/PSModule/NerdFonts) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/NerdFonts/blob/5a9abcb31663bb9d0e7ac58f904efa31a35e80b1/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | bf67cd90269ca5ce25cd76b203678907dc2984b4 | False | v6.1.19 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Net](https://github.com/PSModule/Net) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Net/blob/2facaf6bfa442a92f45b71094952e89999bf1024/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Object](https://github.com/PSModule/Object) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Object/blob/c385cfd09ec0ee9483a3123cbfd70cd1c26432cf/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/OpenAI](https://github.com/PSModule/OpenAI) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/OpenAI/blob/d44c807117fda26311a1de0d14ef7fb071767597/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Path](https://github.com/PSModule/Path) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Path/blob/ddf6ba8a813819b6815411deffbe28fa678640a4/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellDataFile](https://github.com/PSModule/PowerShellDataFile) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellDataFile/blob/d256fea8410477e8b68e6bffa0b8085bb856ab0c/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PowerShellGallery](https://github.com/PSModule/PowerShellGallery) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PowerShellGallery/blob/ea0734cf47e6b5957f61d6e9cb53f0ea3a9eb1ad/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCredential](https://github.com/PSModule/PSCredential) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCredential/blob/87cdfd19eceef381faf37bb124d02dba7fa5f3c6/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSCustomObject](https://github.com/PSModule/PSCustomObject) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSCustomObject/blob/25dd9dc1872f0cc7386e977d713b918738fcbb71/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | ce64918acc96dda73eb78f827036b794bfa6fa1a | False | v5.5.7 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/PSSemVer](https://github.com/PSModule/PSSemVer) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PSSemVer/blob/1a621b14286331569f4d1ffd4643999c2a2a6ca8/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/PublicIP](https://github.com/PSModule/PublicIP) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/PublicIP/blob/82f70c40a309c9b7390160035a6f836bd29da626/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Retry](https://github.com/PSModule/Retry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Retry/blob/2ecfcb46c3204a167a011202a663639996fa1895/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Sodium](https://github.com/PSModule/Sodium) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Sodium/blob/3d96d48c63758298616ab80215f59d4fed7cb7d3/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Telemetry](https://github.com/PSModule/Telemetry) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Telemetry/blob/e85cf2df6611ba056eafa46610b17c06674a71de/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Template-PSModule](https://github.com/PSModule/Template-PSModule) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Template-PSModule/blob/4f525ab008d2d616f2f4e4e20ee2d96d6f76ec67/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | fb1bdb8fefd243292f779d2a856a38db6fe6daf4 | False | v6.1.13 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/TimeSpan](https://github.com/PSModule/TimeSpan) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/TimeSpan/blob/8700bcdc8340b52f7eab7b83414def47c36ed3a0/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Tls](https://github.com/PSModule/Tls) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Tls/blob/11ea777e89668c1c4fd6280b312294db01a5ed4d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Toml](https://github.com/PSModule/Toml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Toml/blob/f8937f27af3c663c80fc3ca986a3aaa40a191a90/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | +| [PSModule/Twitch](https://github.com/PSModule/Twitch) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Twitch/blob/9d25274b23af02c45ecc6fb28f682883afa47027/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Uri](https://github.com/PSModule/Uri) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Uri/blob/fe821cf5c13498a092d919f9d3f8a207912afe96/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Utilities](https://github.com/PSModule/Utilities) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Utilities/blob/3583a87c377650bc4eacff70cef4fcb993c2117d/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/WoW](https://github.com/PSModule/WoW) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/WoW/blob/fd432f8ea872d546eabb20488bc5adab15dbaa7f/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 205d193f34cbbaf9992955c21d842bcf98a1859f | False | v5.4.6 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | inherit | | | +| [PSModule/Yaml](https://github.com/PSModule/Yaml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yaml/blob/8e37203720719528495da3bef5273c674a4d2e0a/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 688896dc3ef70fb35bd74ae5328e76d5e57fe08a | False | v6.1.15 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey, TestData | ImportantFilePatterns | | +| [PSModule/Yml](https://github.com/PSModule/Yml) | [.github/workflows/Process-PSModule.yml](https://github.com/PSModule/Yml/blob/93d2656563a719d99416b9e13a05e65f6f815498/.github/workflows/Process-PSModule.yml) | Process-PSModule | | pull_request, schedule, workflow_dispatch | 1653be8d36607d9535f600278c44789979477813 | False | v6.1.16 | closed, opened, reopened, synchronize, labeled | | 0 0 \* \* \* | ${{ github.workflow }}-${{ github.ref }} | True | contents=write, id-token=write, pages=write, pull-requests=write, statuses=write | | explicit: APIKey | | | From 544e24064a20b538ada4c362c8fa75ef122405e7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:23:39 +0200 Subject: [PATCH 11/26] Validate inventory test fixture path Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index 900946b9..60d40da5 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -3,6 +3,7 @@ param() BeforeAll { $scriptPath = Join-Path $PSScriptRoot '../Get-ProcessPSModuleWorkflowInventory.ps1' + Test-Path -LiteralPath $scriptPath | Should -BeTrue $testRoot = Join-Path ([IO.Path]::GetTempPath()) "process-workflow-inventory-$([guid]::NewGuid())" $repositoryRoot = Join-Path $testRoot 'Example' $workflowRoot = Join-Path $repositoryRoot '.github/workflows' From 925b021204995bf7d066e4c920e7d06f887bcd5e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:29:08 +0200 Subject: [PATCH 12/26] Declare inventory helper contracts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 105 ++++++++++++++++-- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index 51902624..ac1573c3 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -64,7 +64,12 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Invoke-GhCommand { + <# + .SYNOPSIS + Invokes GitHub CLI arguments and returns their combined output. + #> [CmdletBinding()] + [OutputType([string])] param( [Parameter(Mandatory)] [string[]] $ArgumentList @@ -79,7 +84,12 @@ function Invoke-GhCommand { } function ConvertFrom-JsonResponse { + <# + .SYNOPSIS + Converts a possibly empty JSON response into a stable object array. + #> [CmdletBinding()] + [OutputType([object[]])] param( [Parameter(Mandatory)] [AllowEmptyString()] @@ -87,14 +97,19 @@ function ConvertFrom-JsonResponse { ) if ([string]::IsNullOrWhiteSpace($Content)) { - return @() + return [object[]] @() } - @($Content | ConvertFrom-Json -Depth 100) + [object[]] @($Content | ConvertFrom-Json -Depth 100) } function Get-GitHubRepository { + <# + .SYNOPSIS + Gets the repositories included in GitHub inventory discovery. + #> [CmdletBinding()] + [OutputType([object[]])] param( [Parameter(Mandatory)] [string] $Owner, @@ -124,13 +139,18 @@ function Get-GitHubRepository { $repositories = ConvertFrom-JsonResponse -Content $response } - @($repositories | + [object[]] @($repositories | Where-Object { $IncludeArchivedRepository -or -not $_.isArchived } | Sort-Object nameWithOwner) } function Get-GitHubMatchingWorkflowFile { + <# + .SYNOPSIS + Finds and reads default-branch workflow files matching the expected reference. + #> [CmdletBinding()] + [OutputType([object[]])] param( [Parameter(Mandatory)] [psobject[]] $RepositoryInfo, @@ -175,7 +195,7 @@ function Get-GitHubMatchingWorkflowFile { $repositoryByName[$item.nameWithOwner] = $item } - @($searchResults | + [object[]] @($searchResults | Where-Object { $repositoryByName.ContainsKey($_.repository.full_name) } | Sort-Object { $_.repository.full_name }, path -Unique | ForEach-Object { @@ -201,7 +221,12 @@ function Get-GitHubMatchingWorkflowFile { } function Get-LocalRepositoryRoot { + <# + .SYNOPSIS + Discovers unique Git repository roots below the supplied paths. + #> [CmdletBinding()] + [OutputType([string[]])] param( [Parameter(Mandatory)] [string[]] $InputPath @@ -224,11 +249,16 @@ function Get-LocalRepositoryRoot { } } - @($roots | Sort-Object -Unique) + [string[]] @($roots | Sort-Object -Unique) } function Get-LocalRepositoryName { + <# + .SYNOPSIS + Resolves a repository name from its origin URL or local directory. + #> [CmdletBinding()] + [OutputType([string])] param( [Parameter(Mandatory)] [string] $RepositoryRoot @@ -243,7 +273,12 @@ function Get-LocalRepositoryName { } function Get-LocalDefaultBranch { + <# + .SYNOPSIS + Resolves the Git ref used as the local repository's default branch. + #> [CmdletBinding()] + [OutputType([psobject])] param( [Parameter(Mandatory)] [string] $RepositoryRoot @@ -277,7 +312,12 @@ function Get-LocalDefaultBranch { } function Get-LocalWorkflowFile { + <# + .SYNOPSIS + Reads workflow files from each local repository's default-branch Git object. + #> [CmdletBinding()] + [OutputType([psobject[]])] param( [Parameter(Mandatory)] [string[]] $InputPath @@ -322,7 +362,12 @@ function Get-LocalWorkflowFile { } function Get-MapKey { + <# + .SYNOPSIS + Gets normalized string keys from dictionary-like YAML values. + #> [CmdletBinding()] + [OutputType([string[]])] param( [Parameter()] [AllowNull()] @@ -330,18 +375,23 @@ function Get-MapKey { ) if ($null -eq $Map) { - return @() + return [string[]] @() } if ($Map -is [Collections.IDictionary]) { - return @($Map.Keys | ForEach-Object { "$_" }) + return [string[]] @($Map.Keys | ForEach-Object { "$_" }) } - @($Map.PSObject.Properties.Name) + [string[]] @($Map.PSObject.Properties.Name) } function Get-MapValue { + <# + .SYNOPSIS + Gets a named value from dictionary-like YAML values. + #> [CmdletBinding()] + [OutputType([object])] param( [Parameter()] [AllowNull()] @@ -368,7 +418,12 @@ function Get-MapValue { } function ConvertTo-TriggerMap { + <# + .SYNOPSIS + Normalizes mapping, scalar, and list workflow trigger syntax. + #> [CmdletBinding()] + [OutputType([Collections.IDictionary], [Collections.Specialized.OrderedDictionary])] param( [Parameter()] [AllowNull()] @@ -403,7 +458,12 @@ function ConvertTo-TriggerMap { } function ConvertTo-StringMap { + <# + .SYNOPSIS + Converts dictionary-like values into an ordered string map. + #> [CmdletBinding()] + [OutputType([Collections.Specialized.OrderedDictionary])] param( [Parameter()] [AllowNull()] @@ -419,7 +479,12 @@ function ConvertTo-StringMap { } function ConvertTo-StringArray { + <# + .SYNOPSIS + Converts a possibly empty YAML value into a string array. + #> [CmdletBinding()] + [OutputType([string[]])] param( [Parameter()] [AllowNull()] @@ -427,14 +492,19 @@ function ConvertTo-StringArray { ) if ($null -eq $Value) { - return @() + return [string[]] @() } - @($Value | ForEach-Object { "$_" }) + [string[]] @($Value | ForEach-Object { "$_" }) } function ConvertTo-PermissionValue { + <# + .SYNOPSIS + Normalizes scalar and mapping workflow permission syntax. + #> [CmdletBinding()] + [OutputType([string], [Collections.Specialized.OrderedDictionary])] param( [Parameter()] [AllowNull()] @@ -457,7 +527,12 @@ function ConvertTo-PermissionValue { } function Get-WorkflowInventoryItem { + <# + .SYNOPSIS + Parses a workflow file into a normalized inventory record. + #> [CmdletBinding()] + [OutputType([psobject])] param( [Parameter(Mandatory)] [psobject] $WorkflowFile, @@ -618,7 +693,12 @@ function Get-WorkflowInventoryItem { } function ConvertTo-MarkdownCell { + <# + .SYNOPSIS + Escapes a value for safe rendering in a Markdown table cell. + #> [CmdletBinding()] + [OutputType([string])] param( [Parameter()] [AllowNull()] @@ -633,7 +713,12 @@ function ConvertTo-MarkdownCell { } function ConvertTo-WorkflowInventoryMarkdown { + <# + .SYNOPSIS + Renders workflow inventory records as a Markdown report. + #> [CmdletBinding()] + [OutputType([string])] param( [Parameter(Mandatory)] [psobject[]] $Inventory, From 73cd63d24e7d09214070b251667fd8e104c5a5b4 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 14:37:20 +0200 Subject: [PATCH 13/26] Keep caller layout as a candidate Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/get-started/repository-setup.md | 3 +- docs/content/guides/calling-the-workflow.md | 24 ++----- .../guides/github-app-authentication.md | 3 +- .../process-workflow-fleet-standard.md | 72 ++++++++++++------- docs/content/reference/repository-standard.md | 19 +---- 5 files changed, 55 insertions(+), 66 deletions(-) diff --git a/docs/content/get-started/repository-setup.md b/docs/content/get-started/repository-setup.md index d9a51adb..d8fbabaa 100644 --- a/docs/content/get-started/repository-setup.md +++ b/docs/content/get-started/repository-setup.md @@ -65,8 +65,7 @@ permissions: jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/guides/calling-the-workflow.md b/docs/content/guides/calling-the-workflow.md index 1b9f6eb3..6cd10c0d 100644 --- a/docs/content/guides/calling-the-workflow.md +++ b/docs/content/guides/calling-the-workflow.md @@ -48,8 +48,7 @@ permissions: jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -68,15 +67,6 @@ the PowerShell Gallery, GitHub Releases, and tags, so later runs must queue rath The reusable workflow uses its own prefixed concurrency group, so it cannot queue behind the caller while the caller waits for it to finish. -`Process-PSModule` is PSModule-owned automation, so callers use the controlled floating major tag (`@v8`). Compatible -patch and minor releases move that tag through the release workflow. A breaking release publishes a new major tag and -uses a deliberate fleet campaign rather than moving `v8` across the breaking boundary. External actions remain pinned -to full commit SHAs. - -The job condition skips fork-originated pull requests because GitHub does not expose the required repository secrets to -forks. Use a separate secret-free, read-only workflow if the repository accepts contributions from forks and requires -fork CI. - ## Passing test data The reusable workflow at `.github/workflows/workflow.yml` declares four workflow-call secrets, @@ -104,8 +94,7 @@ changes: ```yaml jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -134,8 +123,7 @@ content lines stay at the same indentation level: ```yaml jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} @@ -247,8 +235,7 @@ You can also pass patterns via the workflow input: ```yaml jobs: Process: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 with: ImportantFilePatterns: | ^src/ @@ -261,8 +248,7 @@ To disable triggering via the workflow input, pass an explicit empty string: ```yaml jobs: process: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 with: ImportantFilePatterns: '' ``` diff --git a/docs/content/guides/github-app-authentication.md b/docs/content/guides/github-app-authentication.md index 7dda4776..7be1620b 100644 --- a/docs/content/guides/github-app-authentication.md +++ b/docs/content/guides/github-app-authentication.md @@ -23,8 +23,7 @@ names. Map the caller's secrets explicitly: ```yaml jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v5 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index d3a4d5d5..4c356e7f 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -1,9 +1,9 @@ --- -title: Process-PSModule caller workflow fleet standard -description: Fleet research and proposed required and optional caller workflow elements for Process-PSModule consumers. +title: Process-PSModule caller workflow candidate +description: Fleet research and candidate required and optional caller workflow elements for Process-PSModule consumers. --- -# Process-PSModule caller workflow fleet standard +# Process-PSModule caller workflow candidate This page records the 2026-08-15 fleet research used to propose a common caller workflow for PowerShell module repositories. It is a proposal for review before the consumer repositories are changed. @@ -81,9 +81,9 @@ Secret forwarding is the only widespread caller variation: The case difference in the old API key name is historical drift, not a supported option in the current contract. -## Proposed standard +## Candidate for discussion -The standard caller should be: +The current candidate is: ```yaml name: Process-PSModule @@ -127,6 +127,21 @@ jobs: GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} ``` +This YAML is a recommendation derived from the v8 interface and fleet evidence. It is not an approved standard. +Issue [#514](https://github.com/PSModule/Process-PSModule/issues/514) must record agreement on the following structural +decisions before canonical guides, templates, or consumer workflows adopt it: + +| Decision | Candidate | Alternatives still open | +| --- | --- | --- | +| Wrapper scope | Exactly one reusable-workflow job. | Permit repository-specific jobs in the same file, or define pre/post extension jobs. | +| Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | +| Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | +| Concurrency | Use the PR-number-or-ref key and never cancel a release-capable run. | Use separate groups per event class or permit cancellation for non-mutating paths. | +| Permissions | Declare the five current scopes at workflow level. | Introduce settings-based least-privilege profiles or split read-only validation from release work. | +| Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. | +| Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | +| Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | + The `v8` reference is the controlled moving major tag for this PSModule-owned workflow. On 2026-08-15, `v8`, `v8.0`, and the immutable `v8.0.0` release tag all resolve to commit `5a11e8e8b018faf97017e0416f136a751c026713`. `Release-GHRepository` creates and advances major and minor tags by default, while the organization tag ruleset prevents @@ -147,11 +162,11 @@ to `@v8`; until then, consumers must retain immutable SHA references. A major tag never crosses a breaking boundary. `v8` remains on the latest compatible `8.x` release; `v9` begins a new fleet campaign. Branch names, `latest`, floating minor tags, and unqualified targets are not accepted pins. -## Required elements +## Candidate common elements -| Element | Requirement | Reason | +| Element | Candidate requirement | Reason | | --- | --- | --- | -| Identity | Keep the standard file, workflow, and job names shown above. | Stable discovery, status checks, and fleet maintenance. | +| Identity | Keep the candidate file, workflow, and job names shown above. | Stable discovery, status checks, and fleet maintenance. | | Pull requests | Target `main` and keep all six listed activity types. | CI, prerelease publication, label changes, and closed-PR cleanup depend on them. | | Default-branch push | Keep `push.branches: [main]`. | `v8` authorizes stable releases from the tested default-branch push. | | Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. | @@ -159,13 +174,13 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Concurrency | Use the PR-number-or-ref key with `cancel-in-progress: false`. | Cleanup and stable release runs stay distinct; release mutations queue instead of being interrupted. | | Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. | | Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | -| Reference | Use the approved internal floating major tag (`v8`). | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | +| Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | | Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | -## Supported optional elements +## Candidate optional elements -Optional elements are supported contract variations, not permission to retain historical drift. +These are evidence-based candidate variations, not approved policy. | Option | When it is appropriate | Constraint | | --- | --- | --- | @@ -177,13 +192,14 @@ Optional elements are supported contract variations, not permission to retain hi | Schedule time | Health runs need staggering or a repository-specific maintenance window. | Keep at least one documented schedule unless the repository records why health runs are unnecessary. | | `run-name` | A repository needs clearer run presentation. | Presentation must not change job names or routing behavior. | -## Out-of-standard variations +## Variations requiring a decision -The following are migration defects or require a documented exception: +The following differ from the candidate. They are inventory classifications, not policy violations, until #514 records +an approved structure: - `secrets: inherit`; - `APIKey` or `APIKEY` mappings from the pre-`v7` contract; -- any Process-PSModule reference other than the approved major tag (`v8`), including a branch, `latest`, minor tag, +- any Process-PSModule reference other than the intended major tag (`v8`), including a branch, `latest`, minor tag, exact patch tag, or full commit SHA; - missing `push` or `unlabeled` triggers; - `cancel-in-progress: true` or the old ref-only concurrency key; @@ -191,17 +207,18 @@ The following are migration defects or require a documented exception: - unrelated additional jobs in the caller wrapper; - omitted documented permissions without a verified settings-based least-privilege profile. -Fork-originated pull requests are skipped by the standard caller because reusable-workflow caller jobs cannot select a +Fork-originated pull requests are skipped by the candidate caller because reusable-workflow caller jobs cannot select a GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free, -read-only validation workflow; removing the guard is not a supported shortcut. +read-only validation workflow under this candidate; #514 must approve that boundary. -Repository-specific automation should normally use a separate workflow file. That keeps the Process-PSModule wrapper +The candidate keeps repository-specific automation in a separate workflow file. That keeps the Process-PSModule wrapper identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration tasks. ## Rollout boundary -This research does not change consumer repositories. The campaign should use the stable slug +This research does not approve or change consumer repositories. If #514 approves the candidate, the campaign would use +the stable slug `process-v8-major-tag`, one delivery issue, branch, and early draft pull request per repository, and these waves: | Wave | Repositories | Change profile | @@ -214,17 +231,18 @@ This research does not change consumer repositories. The campaign should use the Before opening leaves: -1. Confirm `v8` and `v8.0.0` resolve to the same tested release commit. -2. Restrict moving major-tag updates to the controlled release identity. Do not start the consumer rollout while another +1. Record approval of every structural decision above in #514 and update the canonical guides and template. +2. Confirm `v8` and `v8.0.0` resolve to the same tested release commit. +3. Restrict moving major-tag updates to the controlled release identity. Do not start the consumer rollout while another identity can move `v8`; retain immutable SHA references until this gate is enforced. -3. Have an organization administrator confirm `PSGALLERY_API_KEY`, `SHELLY_CLIENT_ID`, and `SHELLY_PRIVATE_KEY` coverage +4. Have an organization administrator confirm `PSGALLERY_API_KEY`, `SHELLY_CLIENT_ID`, and `SHELLY_PRIVATE_KEY` coverage in Actions and Dependabot scope. The inventory token can list repository-local secrets but receives `403` for organization secret visibility, so inherited coverage is currently unresolved. -4. Refresh the inventory with `-TargetReference v8`; the starting target count should be `0/60`. -5. Confirm workflow-only changes are not important release changes. The fleet defaults match only `src/` and +5. Refresh the inventory with `-TargetReference v8`; the starting target count should be `0/60`. +6. Confirm workflow-only changes are not important release changes. The fleet defaults match only `src/` and `README.md`; `Yaml` explicitly matches `src/`, `tests/`, and `README.md`, so this campaign should not publish modules. -Each leaf applies the common caller, retains only the supported optional mappings, and proves the PR path before merge. -Advance one wave only after the previous wave's push run completes without an unintended release. Completion requires a -fresh inventory showing `60/60` on `v8`, the complete trigger/concurrency contract, explicit credentials, no inherited -secrets or old API-key mappings, and no unresolved review or CI failures. +After approval, each leaf would apply the agreed caller, retain the agreed optional mappings, and prove the PR path +before merge. Advance one wave only after the previous wave's push run completes without an unintended release. +Completion would require a fresh inventory showing `60/60` on `v8`, the agreed trigger and concurrency contract, +the agreed credential mapping, and no unresolved review or CI failures. diff --git a/docs/content/reference/repository-standard.md b/docs/content/reference/repository-standard.md index aad20549..dbe7a6d0 100644 --- a/docs/content/reference/repository-standard.md +++ b/docs/content/reference/repository-standard.md @@ -122,23 +122,14 @@ The caller workflow declares the triggers, concurrency, and permissions for the ```yaml jobs: Process-PSModule: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 + uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@ # secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} ``` -Name the caller file `Process-PSModule.yml`, matching [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) and every existing module repository. `workflow.yml` is the reusable workflow's own filename inside `PSModule/Process-PSModule` and belongs only in the `uses:` reference. - -`Process-PSModule` is PSModule-owned automation. Pin it to the approved floating major tag (`v8`) so compatible patch -and minor releases move across the fleet without one pull request per release. The release workflow owns movement of -the major tag; an incompatible release creates a new major tag and requires a deliberate fleet campaign. Do not use a -branch, `latest`, a floating minor tag, or an exact release/commit for the standard caller. - -This internal-major-tag policy does not apply to third-party actions. External actions remain pinned to their full -immutable commit SHA with the release version in a trailing comment. +Name the caller file `Process-PSModule.yml`, matching [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) and every existing module repository. `workflow.yml` is the reusable workflow's own filename inside `PSModule/Process-PSModule` and belongs only in the `uses:` reference. Pin the reference to a commit SHA with the version tag in a trailing comment so Dependabot can update it. ## Required common files @@ -202,11 +193,7 @@ For PSModule module repositories, the requirements are: Every module repository must include `.github/dependabot.yml`. Dependabot is part of the repository supply-chain control, not an optional convenience. -Configure the `github-actions` ecosystem. It keeps external SHA-pinned actions current and proposes intentional major -updates when supported. Compatible Process-PSModule patch and minor releases arrive through its controlled major tag -instead of a Dependabot pull request. This is what -[`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) ships, and it is the default for new -repositories: +Configure the `github-actions` ecosystem. It keeps the pinned actions current, including the pinned `PSModule/Process-PSModule` reference in the [caller workflow](#caller-workflow-and-reusable-workflow). This is what [`PSModule/Template-PSModule`](https://github.com/PSModule/Template-PSModule) ships, and it is the default for new repositories: ```yaml version: 2 From 5a495cea0d81cf9aa6300c28d642d902ab466c0c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:18:27 +0200 Subject: [PATCH 14/26] Record the caller concurrency decision Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/reference/process-workflow-fleet-standard.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 4c356e7f..3cbe4f0c 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -108,7 +108,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: false + cancel-in-progress: ${{ github.event_name == 'pull_request' }} permissions: contents: write @@ -136,7 +136,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Wrapper scope | Exactly one reusable-workflow job. | Permit repository-specific jobs in the same file, or define pre/post extension jobs. | | Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | -| Concurrency | Use the PR-number-or-ref key and never cancel a release-capable run. | Use separate groups per event class or permit cancellation for non-mutating paths. | +| Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | | Permissions | Declare the five current scopes at workflow level. | Introduce settings-based least-privilege profiles or split read-only validation from release work. | | Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. | | Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | @@ -171,7 +171,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Default-branch push | Keep `push.branches: [main]`. | `v8` authorizes stable releases from the tested default-branch push. | | Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. | | Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | -| Concurrency | Use the PR-number-or-ref key with `cancel-in-progress: false`. | Cleanup and stable release runs stay distinct; release mutations queue instead of being interrupted. | +| Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | New PR events supersede older declarative reconciliation runs; same-ref push, dispatch, and schedule runs serialize without cancellation. | | Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. | | Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | @@ -202,7 +202,7 @@ an approved structure: - any Process-PSModule reference other than the intended major tag (`v8`), including a branch, `latest`, minor tag, exact patch tag, or full commit SHA; - missing `push` or `unlabeled` triggers; -- `cancel-in-progress: true` or the old ref-only concurrency key; +- a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - trigger-level path filters that bypass Process-PSModule important-file evaluation; - unrelated additional jobs in the caller wrapper; - omitted documented permissions without a verified settings-based least-privilege profile. From d7a63a5dd150dbce55277bf46e2bb10838d91185 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:25:47 +0200 Subject: [PATCH 15/26] Record the caller permission boundary Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-standard.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 3cbe4f0c..b4f5e726 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -110,12 +110,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} -permissions: - contents: write - pull-requests: write - statuses: write - pages: write - id-token: write +permissions: {} jobs: Process-PSModule: @@ -137,7 +132,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | -| Permissions | Declare the five current scopes at workflow level. | Introduce settings-based least-privilege profiles or split read-only validation from release work. | +| Permissions | Set caller permissions to `{}` and use scoped GitHub App tokens inside the reusable workflow. | Selected for the candidate; built-in `GITHUB_TOKEN` authority must not be required. | | Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. | | Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | | Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | @@ -172,7 +167,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. | | Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | | Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | New PR events supersede older declarative reconciliation runs; same-ref push, dispatch, and schedule runs serialize without cancellation. | -| Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. | +| Permissions | Set top-level `permissions: {}` and grant no caller-job permissions. | Repository access and mutations use narrowly scoped GitHub App installation tokens created inside the reusable workflow. | | Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | @@ -205,7 +200,13 @@ an approved structure: - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - trigger-level path filters that bypass Process-PSModule important-file evaluation; - unrelated additional jobs in the caller wrapper; -- omitted documented permissions without a verified settings-based least-privilege profile. +- any built-in `GITHUB_TOKEN` permission granted by the caller. + +The current v8 implementation still uses built-in token authority for checkout, linter status/reporting, and the standard +GitHub Pages deployment action. Before adopting the empty-permissions caller, each job must create a narrowly scoped +GitHub App installation token before checkout and pass it explicitly to checkout, GitHub CLI, and reporting actions. +Pages publication must either move to an App-authenticated deployment path or document the unavoidable `pages`/OIDC +exception if the standard Pages action remains. Fork-originated pull requests are skipped by the candidate caller because reusable-workflow caller jobs cannot select a GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free, From 52f34a80833b560955cc8bc5c3796b3ac153c2bd Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:28:12 +0200 Subject: [PATCH 16/26] Refine the workflow token boundary Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-standard.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index b4f5e726..aea57742 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -114,6 +114,10 @@ permissions: {} jobs: Process-PSModule: + permissions: + contents: read + pages: write + id-token: write if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: @@ -132,7 +136,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | -| Permissions | Set caller permissions to `{}` and use scoped GitHub App tokens inside the reusable workflow. | Selected for the candidate; built-in `GITHUB_TOKEN` authority must not be required. | +| Permissions | Default deny at workflow level, then grant the caller job `contents: read`, `pages: write`, and `id-token: write`. | Selected for the candidate: use `GITHUB_TOKEN` for repository-local, non-user-facing platform operations and App tokens for user-facing or otherwise unsupported operations. | | Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. | | Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | | Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | @@ -167,7 +171,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. | | Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | | Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | New PR events supersede older declarative reconciliation runs; same-ref push, dispatch, and schedule runs serialize without cancellation. | -| Permissions | Set top-level `permissions: {}` and grant no caller-job permissions. | Repository access and mutations use narrowly scoped GitHub App installation tokens created inside the reusable workflow. | +| Permissions | Set top-level `permissions: {}` and grant only `contents: read`, `pages: write`, and `id-token: write` to the caller job. | Checkout and Pages remain repository-local built-in capabilities; user-facing interactions and operations outside the built-in token boundary use scoped GitHub App tokens. | | Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | @@ -200,13 +204,13 @@ an approved structure: - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - trigger-level path filters that bypass Process-PSModule important-file evaluation; - unrelated additional jobs in the caller wrapper; -- any built-in `GITHUB_TOKEN` permission granted by the caller. +- caller permissions beyond `contents: read`, `pages: write`, and `id-token: write`. -The current v8 implementation still uses built-in token authority for checkout, linter status/reporting, and the standard -GitHub Pages deployment action. Before adopting the empty-permissions caller, each job must create a narrowly scoped -GitHub App installation token before checkout and pass it explicitly to checkout, GitHub CLI, and reporting actions. -Pages publication must either move to an App-authenticated deployment path or document the unavoidable `pages`/OIDC -exception if the standard Pages action remains. +Use the built-in `GITHUB_TOKEN` for non-user-facing operations confined to the calling repository, including checkout +and the standard GitHub Pages deployment. Create narrowly scoped GitHub App installation tokens for user-facing +interactions such as pull-request comments, labels, statuses, releases, and release cleanup, and whenever the built-in +token cannot provide the required repository or cross-repository access. Tokens remain step-scoped and must not fall +back silently from App authorization to broader built-in-token authority. Fork-originated pull requests are skipped by the candidate caller because reusable-workflow caller jobs cannot select a GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free, From 859d274ede94fc51eb04322b9f092e8dc5eecda4 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:31:13 +0200 Subject: [PATCH 17/26] Move event authorization into Plan Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../reference/process-workflow-fleet-standard.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index aea57742..1f96f034 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -118,7 +118,6 @@ jobs: contents: read pages: write id-token: write - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8 secrets: PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} @@ -137,7 +136,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | | Permissions | Default deny at workflow level, then grant the caller job `contents: read`, `pages: write`, and `id-token: write`. | Selected for the candidate: use `GITHUB_TOKEN` for repository-local, non-user-facing platform operations and App tokens for user-facing or otherwise unsupported operations. | -| Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. | +| Fork behavior | Keep the caller unconditional; gate unsupported fork events in the reusable workflow's Plan job. | Selected for the candidate; execution policy belongs to Process-PSModule rather than every consumer. | | Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | | Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | @@ -172,7 +171,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | | Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | New PR events supersede older declarative reconciliation runs; same-ref push, dispatch, and schedule runs serialize without cancellation. | | Permissions | Set top-level `permissions: {}` and grant only `contents: read`, `pages: write`, and `id-token: write` to the caller job. | Checkout and Pages remain repository-local built-in capabilities; user-facing interactions and operations outside the built-in token boundary use scoped GitHub App tokens. | -| Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. | +| Event gate | Keep the caller unconditional and gate unsupported events in `Plan`. | The reusable workflow owns execution policy; every downstream job must require a successful authorized plan. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | | Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | @@ -202,6 +201,7 @@ an approved structure: exact patch tag, or full commit SHA; - missing `push` or `unlabeled` triggers; - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; +- a caller-level fork or event-authorization condition; - trigger-level path filters that bypass Process-PSModule important-file evaluation; - unrelated additional jobs in the caller wrapper; - caller permissions beyond `contents: read`, `pages: write`, and `id-token: write`. @@ -212,9 +212,9 @@ interactions such as pull-request comments, labels, statuses, releases, and rele token cannot provide the required repository or cross-repository access. Tokens remain step-scoped and must not fall back silently from App authorization to broader built-in-token authority. -Fork-originated pull requests are skipped by the candidate caller because reusable-workflow caller jobs cannot select a -GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free, -read-only validation workflow under this candidate; #514 must approve that boundary. +The reusable workflow's Plan job rejects unsupported fork-originated pull requests before any credentialed or +repository-defined work runs. Every downstream job must depend on a successful authorized Plan result, including jobs +using `always()`. Supporting fork CI requires a separate, secret-free read-only workflow under this candidate. The candidate keeps repository-specific automation in a separate workflow file. That keeps the Process-PSModule wrapper identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration From 5ffec4053a85d2bf379d820cc9db437a3617b84d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:32:45 +0200 Subject: [PATCH 18/26] Allow restricted fork validation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../reference/process-workflow-fleet-standard.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 1f96f034..8f3b6d9e 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -136,7 +136,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | | Permissions | Default deny at workflow level, then grant the caller job `contents: read`, `pages: write`, and `id-token: write`. | Selected for the candidate: use `GITHUB_TOKEN` for repository-local, non-user-facing platform operations and App tokens for user-facing or otherwise unsupported operations. | -| Fork behavior | Keep the caller unconditional; gate unsupported fork events in the reusable workflow's Plan job. | Selected for the candidate; execution policy belongs to Process-PSModule rather than every consumer. | +| Fork behavior | Keep the caller unconditional; classify fork pull requests as restricted read-only validation in `Plan`. | Selected for the candidate; execution policy belongs to Process-PSModule rather than every consumer. | | Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | | Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | @@ -171,7 +171,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. | | Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | New PR events supersede older declarative reconciliation runs; same-ref push, dispatch, and schedule runs serialize without cancellation. | | Permissions | Set top-level `permissions: {}` and grant only `contents: read`, `pages: write`, and `id-token: write` to the caller job. | Checkout and Pages remain repository-local built-in capabilities; user-facing interactions and operations outside the built-in token boundary use scoped GitHub App tokens. | -| Event gate | Keep the caller unconditional and gate unsupported events in `Plan`. | The reusable workflow owns execution policy; every downstream job must require a successful authorized plan. | +| Event gate | Keep the caller unconditional and authorize capabilities in `Plan`. | The reusable workflow owns execution policy; fork pull requests may validate but cannot obtain App credentials, publish, deploy, clean up, or mutate repository state. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | | Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | @@ -212,9 +212,13 @@ interactions such as pull-request comments, labels, statuses, releases, and rele token cannot provide the required repository or cross-repository access. Tokens remain step-scoped and must not fall back silently from App authorization to broader built-in-token authority. -The reusable workflow's Plan job rejects unsupported fork-originated pull requests before any credentialed or -repository-defined work runs. Every downstream job must depend on a successful authorized Plan result, including jobs -using `always()`. Supporting fork CI requires a separate, secret-free read-only workflow under this candidate. +The reusable workflow's Plan job classifies fork-originated `pull_request` events as restricted read-only validation. +It may allow repository-local checkout, build, lint, and test with least-privilege built-in access, but must explicitly +deny App-token creation, Gallery access, publication, Pages deployment, cleanup, and repository or user-facing +mutations. Every downstream job, including jobs using `always()`, must require a successful valid Plan and the planned +capability for its operation before running or evaluating Settings. Privileged-context events such as +`pull_request_target` remain unsupported unless separately designed to prevent untrusted code from crossing the +credential boundary. The candidate keeps repository-specific automation in a separate workflow file. That keeps the Process-PSModule wrapper identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration From c18a40b91473b14e93e621bd639250f2ba25d7b9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:33:27 +0200 Subject: [PATCH 19/26] Define fork validation trust order Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/reference/process-workflow-fleet-standard.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 8f3b6d9e..20ccc763 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -215,8 +215,11 @@ back silently from App authorization to broader built-in-token authority. The reusable workflow's Plan job classifies fork-originated `pull_request` events as restricted read-only validation. It may allow repository-local checkout, build, lint, and test with least-privilege built-in access, but must explicitly deny App-token creation, Gallery access, publication, Pages deployment, cleanup, and repository or user-facing -mutations. Every downstream job, including jobs using `always()`, must require a successful valid Plan and the planned -capability for its operation before running or evaluating Settings. Privileged-context events such as +mutations. The controlled upstream Plan implementation derives this security envelope from GitHub event metadata before +it interprets repository settings or executes checked-out repository code. Fork-controlled files and settings remain +untrusted build inputs and cannot broaden the planned capabilities. Every downstream job, including jobs using +`always()`, must require a successful valid Plan and the planned capability for its operation before running or +evaluating Settings. Privileged-context events such as `pull_request_target` remain unsupported unless separately designed to prevent untrusted code from crossing the credential boundary. From 8346ad01c32a5449d5d68a8b5aec7f7b38b3133f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:44:47 +0200 Subject: [PATCH 20/26] Record reusable workflow path constraint Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/content/reference/process-workflow-fleet-standard.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 20ccc763..528271c2 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -150,6 +150,11 @@ Release automation is therefore the operational owner, but actors with sufficien from moving the major tag manually. Enforce release-identity-only governance for moving tags before migrating the fleet to `@v8`; until then, consumers must retain immutable SHA references. +The reusable workflow remains at `.github/workflows/workflow.yml`. A private cross-repository experiment on 2026-08-15 +confirmed that GitHub rejects a root-level reusable workflow reference with +`references to workflows must be rooted in '.github/workflows'`, even when the provider repository grants the caller +the required Actions access. + ### Owned and external references | Automation source | Standard reference | Update model | From cf7d21035d5ad2b7f43093ccbcdb650731122f94 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:48:10 +0200 Subject: [PATCH 21/26] Define optional test data mapping Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-standard.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 528271c2..bb6c9f83 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -125,6 +125,16 @@ jobs: GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} ``` +Modules whose local tests require repository-specific data may add the optional secret mapping: + +```yaml + TestData: >- + {"secrets":{"TOKEN":"${{ secrets.TEST_TOKEN }}"},"variables":{"ENDPOINT":"${{ vars.TEST_ENDPOINT }}"}} +``` + +The reusable workflow exports only the declared entries to module-local test setup, tests, and teardown. Callers omit +`TestData` when no module-local test data is required. + This YAML is a recommendation derived from the v8 interface and fleet evidence. It is not an approved standard. Issue [#514](https://github.com/PSModule/Process-PSModule/issues/514) must record agreement on the following structural decisions before canonical guides, templates, or consumer workflows adopt it: @@ -137,7 +147,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | | Permissions | Default deny at workflow level, then grant the caller job `contents: read`, `pages: write`, and `id-token: write`. | Selected for the candidate: use `GITHUB_TOKEN` for repository-local, non-user-facing platform operations and App tokens for user-facing or otherwise unsupported operations. | | Fork behavior | Keep the caller unconditional; classify fork pull requests as restricted read-only validation in `Plan`. | Selected for the candidate; execution policy belongs to Process-PSModule rather than every consumer. | -| Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. | +| Credentials | Explicitly map the three v8 credentials; optionally map `TestData` when module-local tests need it. | Define a narrower credential profile for repositories that cannot publish. | | Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | The `v8` reference is the controlled moving major tag for this PSModule-owned workflow. On 2026-08-15, `v8`, `v8.0`, @@ -187,14 +197,16 @@ These are evidence-based candidate variations, not approved policy. | Option | When it is appropriate | Constraint | | --- | --- | --- | -| `TestData` secret | Module-local tests need caller-defined secrets or variables. | Use the documented compact single-line JSON object and expose only required values. | +| `TestData` secret | Module-local tests need caller-defined secrets or variables. | Optionally map the documented JSON object with separate `secrets` and `variables` maps, exposing only required values. | | `with.SettingsPath` | The settings file is not `.github/PSModule.yml`. | Prefer the standard path for normal module repositories. | | `with.WorkingDirectory` | The module is intentionally rooted below the repository root. | Keep the default `.` for the standard layout. | | `with.ImportantFilePatterns` | A caller must override change detection at the workflow boundary. | Prefer stable configuration in `.github/PSModule.yml`; the supplied list replaces all defaults. | -| `with.Debug`, `Verbose`, `Version`, or `Prerelease` | A deliberate diagnostic or dependency-selection scenario needs it. | Do not hard-code temporary diagnostics into the fleet baseline. | +| `with.Verbose`, `Version`, or `Prerelease` | A deliberate diagnostic or dependency-selection scenario needs it. | Do not hard-code temporary diagnostics into the fleet baseline. | | Schedule time | Health runs need staggering or a repository-specific maintenance window. | Keep at least one documented schedule unless the repository records why health runs are unnecessary. | | `run-name` | A repository needs clearer run presentation. | Presentation must not change job names or routing behavior. | +Conforming callers do not set `with.Debug: true`; the reusable workflow default remains `false`. + ## Variations requiring a decision The following differ from the candidate. They are inventory classifications, not policy violations, until #514 records From 0d604a3214dbf96e5ff66b64e883545402d96232 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 15:50:11 +0200 Subject: [PATCH 22/26] Limit standard to caller contract Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../reference/process-workflow-fleet-standard.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index bb6c9f83..9732feab 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -141,7 +141,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Decision | Candidate | Alternatives still open | | --- | --- | --- | -| Wrapper scope | Exactly one reusable-workflow job. | Permit repository-specific jobs in the same file, or define pre/post extension jobs. | +| Contract scope | Standardize the `Process-PSModule` caller job and its shared workflow controls, not every job in the file. | Selected for the candidate; repository-owned jobs remain outside the caller contract. | | Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | @@ -189,7 +189,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Event gate | Keep the caller unconditional and authorize capabilities in `Plan`. | The reusable workflow owns execution policy; fork pull requests may validate but cannot obtain App credentials, publish, deploy, clean up, or mutate repository state. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | -| Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. | +| Scope | Require one conforming `Process-PSModule` delegation job. | Additional repository-owned jobs do not change caller conformance. | ## Candidate optional elements @@ -220,7 +220,6 @@ an approved structure: - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - a caller-level fork or event-authorization condition; - trigger-level path filters that bypass Process-PSModule important-file evaluation; -- unrelated additional jobs in the caller wrapper; - caller permissions beyond `contents: read`, `pages: write`, and `id-token: write`. Use the built-in `GITHUB_TOKEN` for non-user-facing operations confined to the calling repository, including checkout @@ -240,9 +239,10 @@ evaluating Settings. Privileged-context events such as `pull_request_target` remain unsupported unless separately designed to prevent untrusted code from crossing the credential boundary. -The candidate keeps repository-specific automation in a separate workflow file. That keeps the Process-PSModule wrapper -identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration -tasks. +The contract applies to the shared workflow controls and the `Process-PSModule` delegation job shown above. Repositories +may define additional jobs in the same file or separate workflows. The inventory reports those jobs for visibility, but +the contract does not prescribe their implementation. Additional jobs must not weaken or bypass the permissions, +authorization, trigger, or concurrency controls governing the Process-PSModule call. ## Rollout boundary From bd7c85fb00fa9909d19faab615ae2f4363602d0c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 16:27:06 +0200 Subject: [PATCH 23/26] =?UTF-8?q?=F0=9F=93=96=20[Docs]:=20Workflow=20lifec?= =?UTF-8?q?ycle=20behavior=20ready=20for=20review=20(#517)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Defines the Process-PSModule workflow lifecycle specification and architecture. The change documents behavior and implementation boundaries; it does not change functional workflow code. ## Scope - Adds behavior-driven requirements for dispatch recovery, scheduled published-artifact validation, pull-request routing, stable aggregation, concurrency recovery, Gallery disposition, and auditable outcomes. - Defines Plan as the sole lifecycle-policy authority, immutable Settings, and one release executor for stable, prerelease, recovery/resume, cleanup-only, and no-op actions. - Defines the caller boundary: explicit credentials, structured optional `TestData`, narrow built-in-token use, step-scoped App tokens, restricted fork validation, and capability-gated downstream jobs. - Adds both lifecycle pages to the Reference navigation. ## Validation - `npx --yes markdownlint-cli2 --config .github/linters/.markdown-lint.yml docs/content/reference/process-workflow-lifecycle-specification.md docs/content/reference/process-workflow-lifecycle-design.md` - `zensical build --clean` from `docs` --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-lifecycle-design.md | 200 ++++++++ ...rocess-workflow-lifecycle-specification.md | 445 ++++++++++++++++++ docs/zensical.toml | 2 + 3 files changed, 647 insertions(+) create mode 100644 docs/content/reference/process-workflow-lifecycle-design.md create mode 100644 docs/content/reference/process-workflow-lifecycle-specification.md diff --git a/docs/content/reference/process-workflow-lifecycle-design.md b/docs/content/reference/process-workflow-lifecycle-design.md new file mode 100644 index 00000000..72642640 --- /dev/null +++ b/docs/content/reference/process-workflow-lifecycle-design.md @@ -0,0 +1,200 @@ +--- +title: Process-PSModule workflow lifecycle design +description: Architecture for Process-PSModule event routing, stamped artifacts, recovery release notes, and concurrency isolation. +--- + +# Process-PSModule workflow lifecycle design + +This design defines the Process-PSModule lifecycle architecture. It implements the +[workflow lifecycle specification](process-workflow-lifecycle-specification.md) through a single policy authority, +immutable release records, and a general release executor. + +## Architecture + +Plan is the sole lifecycle-policy authority. It resolves each event before build and release work, emits enriched +Settings, and gates downstream execution. Build, validation, release, cleanup, and reporting consume Settings without +reinterpreting events, labels, or repository settings. + +One general module release action or reusable workflow consumes Settings and performs stable release, prerelease, +recovery or resume, cleanup-only, or no-op actions. It verifies artifacts when required and reconciles only the +requested state. + +## Event routing + +| Event | Plan classification | Release action | Concurrency | Result | +| --- | --- | --- | --- | --- | +| `workflow_dispatch` on the default branch | Recovery or resume | Stable release or no-op | Full-ref serialization | Rebuild and validate the selected commit; reconstruct unreleased release notes. | +| `schedule` | Published-artifact validation | No-op after validation | Full-ref serialization | Validate the latest published stable artifact and documentation. | +| Fork `pull_request` | Restricted read-only validation | No-op after validation | Pull-request cancellation | Perform repository-local checkout, build, lint, and test only. | +| Pull request `opened`, `reopened`, `synchronize` | Pull-request classification | Prerelease or no-op | Pull-request cancellation | Run validation and execute the planned release action. | +| Pull request `labeled`, `unlabeled` | Pull-request classification refresh | Prerelease, cleanup-only, or no-op | Pull-request cancellation | Resolve the complete current classification and execute its action. | +| Merged pull request `closed` | Post-merge close | No-op | Pull-request cancellation | Leave promotion cleanup to the stable release. | +| Abandoned pull request `closed` | Abandoned-close classification | Cleanup-only | Pull-request cancellation | Reconcile only prereleases owned by the abandoned pull request. | +| Push to the default branch | Stable release | Stable release | Full-ref serialization | Aggregate merged-pull-request intent, publish, and perform promotion cleanup. | + +## Caller boundary + +The [Process-PSModule caller contract](process-workflow-fleet-standard.md) contains exactly one reusable-workflow call +job and the shared top-level triggers, concurrency, permissions, Plan authorization, and credential boundary that govern +it. Repository-owned jobs MAY coexist in the same workflow file or in separate workflows. They are visible to +conformance reporting and MUST NOT weaken or bypass the Process-PSModule call boundary. + +## Event authorization + +The caller invokes the reusable workflow without a caller-level fork or event condition. The controlled upstream Plan +implementation derives its security and capability envelope from immutable GitHub event metadata before it interprets +repository settings or executes checked-out code. + +For a normal fork `pull_request`, Plan emits a restricted Settings record: + +```text +IsFork=true +AllowAppToken=false +AllowPublication=false +AllowMutation=false +``` + +The restricted route permits only repository-local checkout, build, lint, and test with the least-privilege built-in +token. It provides a green or red validation outcome without contributor secrets. Fork settings and checked-out files +are untrusted validation and build inputs and cannot alter the capability envelope. + +Restricted routes do not create App tokens; access PowerShell Gallery; mutate pull requests, statuses, releases, tags, +or assets; perform cleanup; deploy Pages; or run other privileged or user-facing operations. `pull_request_target` is +rejected before credentials or repository-defined code run. + +Every downstream job first requires successful Plan execution and valid Settings. Jobs using `always()` apply this gate +before their own failure-handling logic. Privileged jobs also require their relevant Settings capability and never parse +missing or invalid Settings. + +## Settings contract + +Settings contains one immutable release record: + +| Field | Purpose | +| --- | --- | +| Event and run type | Identifies the GitHub event and lifecycle classification. | +| Event action | Preserves the pull-request activity or non-pull-request action. | +| Pull-request identity, state, and merge status | Distinguishes active, merged, and abandoned outcomes. | +| Authorization capabilities and evidence | Records `IsFork`, immutable event metadata, and App-token, publication, and mutation capabilities. | +| Labels and repository settings result | Records the inputs resolved by Plan. | +| Version bump and base version | Defines the version transition. | +| Manifest version, prerelease identifier, and full version or tag | Defines the only version and tag permitted in an artifact and release. | +| Target commit | Binds validation, artifact, and release to one source revision. | +| Resolved release action and create or publish flags | Selects stable, prerelease, recovery or resume, cleanup-only, or no-op execution. | +| Cleanup intent and artifact identity | Defines the exact artifacts release execution may reconcile. | +| Release-note source and boundary | Identifies merged pull requests eligible for release notes. | + +## Artifact and version boundary + +Build stamps exactly the manifest version and prerelease identifier in Settings into the module artifact. Before any +package, tag, or release becomes visible, release execution verifies that the artifact equals the Settings record. A +mismatch stops execution; release execution does not recalculate versions or retag artifacts. + +## Repository authorization + +The caller uses the following job boundary: + +```yaml +permissions: {} +jobs: + Process-PSModule: + permissions: + contents: read + pages: write + id-token: write + secrets: + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} + GitHubAppClientId: ${{ secrets.SHELLY_CLIENT_ID }} + GitHubAppPrivateKey: ${{ secrets.SHELLY_PRIVATE_KEY }} +``` + +These explicit secret mappings are required. `secrets: inherit` is prohibited. The only optional secret mapping is +`TestData` for module-local tests: + +```yaml + TestData: ${{ secrets.TestData }} +``` + +When present, `TestData` contains a JSON object with separate `secrets` and `variables` maps. It is omitted when +unused. Callers do not set `with.Debug: true`; the reusable workflow default is `false`. + +Built-in `GITHUB_TOKEN` authorizes checkout, repository-local reads, and standard Pages/OIDC deployment within the job +boundary. Step-scoped GitHub App installation tokens authorize pull-request comments and labels, commit statuses and +check-facing reporting, releases, tags, assets, and cleanup. An App-required operation fails before its API request or +mutation when its App token is unavailable; it never falls back to the built-in token. + +Restricted fork Settings override the caller job boundary: no App token is created, no Pages deployment runs, and no +repository mutation or user-facing action runs. + +## Stable aggregation and recovery + +Every stable push and recovery target finds the last successfully published stable version and associated target commit, +then aggregates merged pull requests through the requested target commit. The aggregated release intent determines the +stable version and release-note range. + +Manual recovery accepts a selected default-branch commit and applies the same aggregation and validation path as a +default-branch push. It returns no-op when a stable publication already covers that commit. Release notes use the +ordered, de-duplicated merged-pull-request range rather than manual-dispatch payload data. + +## Scheduled validation and close behavior + +Scheduled validation resolves the latest published stable version as input and sets a validation-only mutation class. +Publication and cleanup execution do not run. + +A merged pull-request close performs no prerelease cleanup. The successful default-branch stable release owns promotion +cleanup. An abandoned pull-request close receives a pull-request-scoped artifact set and performs only prerelease +cleanup. Broad prerelease deletion requires its own exclusive scope and does not share the abandoned-close route. + +## Concurrency and recovery + +The caller uses: + +```yaml +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +``` + +All pull-request events for one pull request share a group and cancel superseded runs. Push, manual-dispatch, and +scheduled events use full `github.ref`, do not cancel a running run, and serialize by ref. GitHub retains one running +and one pending run per group; a later same-group event can replace an earlier pending run. Full `github.ref` prevents +branch and tag name collisions that `github.ref_name` cannot distinguish. + +Cancellation leaves only transient partial state. A subsequent `synchronize`, `labeled`, `unlabeled`, or `closed` +event resumes and reconciles the latest pull-request state: + +| External operation | Reconciliation | +| --- | --- | +| PowerShell Gallery publication | Resolve a deterministic pull-request-scoped prerelease identity, detect the existing version, and continue without duplicate publication. | +| GitHub Release creation | Resume or upsert the release and replace its asset set. | +| Prerelease cleanup | Repeat safely after partial deletion and converge to the latest pull-request state. | +| Production boundary | Do not create a stable or signable production artifact from a pull-request event. | + +## Gallery prerelease disposition + +PowerShell Gallery packages are immutable and cannot be overwritten. Each prerelease uses a deterministic +pull-request-scoped version. When a prerelease becomes obsolete, release execution unlists it through a supported +Gallery API when feasible. When unlisting is infeasible, release execution retains and records the immutable version. +GitHub Release and tag cleanup execute independently from Gallery disposition. + +## Verification + +The lifecycle is verified with event payload fixtures and publication fakes before credentials are used: + +| Behavior | Verification | +| --- | --- | +| Event routing | Fixtures for each supported event and pull-request activity, including merged and abandoned close outcomes. | +| Version boundary | A mismatched artifact fixture proves publication stops. | +| Recovery release notes | Merged-pull-request query fixtures cover empty, single, and multiple pull-request ranges. | +| Scheduled validation | A published-version fixture proves no release mutation is requested. | +| Pull-request convergence | Canceled prerelease publication and cleanup fixtures followed by synchronize, label, unlabel, and close events prove reconciliation. | +| Gallery disposition | Fixtures cover deterministic identity, existing-version detection, supported unlisting, and retained-version recording. | +| Stable aggregation | Push, manual-dispatch, and scheduled bursts replace a pending run and prove all unreleased merged pull requests are aggregated. | +| Caller authorization | Fixtures verify the explicit permissions and credential mappings, App-token failure, and no built-in-token fallback. | +| Fork authorization | Fixtures verify immutable-metadata-first restricted Settings, no privileged operations, and `pull_request_target` rejection. | +| Caller boundary | Fixtures verify repository-owned jobs remain visible without weakening or bypassing the reusable-workflow call boundary. | + +## Related + +- [Process-PSModule workflow lifecycle specification](process-workflow-lifecycle-specification.md) +- [Process-PSModule caller contract](process-workflow-fleet-standard.md) diff --git a/docs/content/reference/process-workflow-lifecycle-specification.md b/docs/content/reference/process-workflow-lifecycle-specification.md new file mode 100644 index 00000000..3024eef8 --- /dev/null +++ b/docs/content/reference/process-workflow-lifecycle-specification.md @@ -0,0 +1,445 @@ +--- +title: Process-PSModule workflow lifecycle specification +description: Behavior-driven requirements for Process-PSModule event routing, recovery releases, validation, and cleanup. +--- + +# Process-PSModule workflow lifecycle specification + +This specification defines the lifecycle requirements for the Process-PSModule reusable workflow. Requirements follow +[spec-driven development](https://msx.no/docs/Ways-of-Working/Spec-Driven-Development/) and use +[Given / When / Then scenarios](https://msx.no/docs/Ways-of-Working/Spec-Driven-Development/#behavioral-scenarios) as +the acceptance contract. + +## Scope + +The lifecycle covers dispatch recovery, scheduled published-artifact validation, pull-request validation and prerelease +evaluation, closed-pull-request cleanup, and stable publication after a default-branch push. + +The [Process-PSModule caller contract](process-workflow-fleet-standard.md) requires exactly one reusable-workflow call +job and the shared top-level controls that govern it. Repository-owned jobs MAY exist in the same workflow file or in +separate workflows, provided they do not weaken or bypass the call's trigger, concurrency, permissions, Plan +authorization, or credential boundary. + +## Functional requirements + +### FR1 — Manual dispatch MUST provide a safe recovery release {#fr1} + +A default-branch manual dispatch MUST either publish one stable release after all required validation succeeds or report +that the selected commit is already covered by a stable publication. It MUST NOT create a duplicate stable publication. +Release notes MUST identify the merged pull requests from the last successfully published stable version through the +selected commit. + +#### Behavioral scenarios {#fr1-scenarios} + +```gherkin +Scenario: Recover a missing stable publication + Given the default branch contains a validated commit without a stable publication + When a maintainer dispatches the workflow for that commit + Then the workflow publishes one stable artifact and release for the commit + And the release notes identify merged pull requests since the previous published version + +Scenario: Repeat a completed recovery dispatch + Given a stable publication already covers the selected default-branch commit + When a maintainer dispatches the workflow again + Then the workflow reports that no recovery release is required + And it does not create another artifact, tag, or release +``` + +### FR2 — Scheduled runs MUST validate published artifacts without publishing {#fr2} + +A scheduled run MUST validate the latest published stable artifact and its published documentation against configured +checks. It MUST NOT create, replace, or delete a package, tag, release, or prerelease. + +#### Behavioral scenarios {#fr2-scenarios} + +```gherkin +Scenario: Validate the latest published artifact + Given a stable module version and its documentation are published + When the scheduled workflow runs + Then the workflow validates that published version + And it reports the validated version and result + And it creates no release-related artifact +``` + +### FR3 — Pull-request delivery events MUST run validation only {#fr3} + +An `opened`, `reopened`, or `synchronize` pull-request event targeting the default branch MUST run configured +validation. It MUST NOT create a stable publication. + +#### Behavioral scenarios {#fr3-scenarios} + +```gherkin +Scenario: Validate a synchronized pull request + Given a pull request targets the default branch + When a new commit synchronizes the pull request + Then the workflow reports the configured validation result + And it does not publish a stable version +``` + +### FR4 — Label changes MUST refresh the release classification {#fr4} + +A `labeled` or `unlabeled` pull-request event targeting the default branch MUST resolve the complete classification +from the current label set and repository settings. A prerelease publication MUST occur only when the classification is +prerelease and every required validation succeeds. + +#### Behavioral scenarios {#fr4-scenarios} + +```gherkin +Scenario: Add prerelease eligibility + Given a validated pull request has no prerelease eligibility + When a prerelease label is added + Then Plan resolves the pull request as prerelease eligible + And it publishes at most one eligible prerelease version + +Scenario: Remove prerelease eligibility + Given a pull request has prerelease eligibility + When its prerelease label is removed + Then Plan resolves the pull request as prerelease ineligible + And it does not create a new prerelease version +``` + +### FR5 — Closed pull requests MUST route cleanup by close outcome {#fr5} + +A merged pull-request close MUST NOT perform prerelease cleanup. A successful default-branch stable release MUST own +promotion cleanup. An abandoned pull-request close MUST run cleanup-only behavior for prerelease artifacts associated +with that pull request. Neither close outcome MUST authorize or create a stable publication. + +#### Behavioral scenarios {#fr5-scenarios} + +```gherkin +Scenario: Merge a pull request with prereleases + Given a merged pull request owns prerelease artifacts + When its close event is processed + Then the close event does not clean up prerelease artifacts + And the successful default-branch release owns promotion cleanup + +Scenario: Abandon a pull request with prereleases + Given an unmerged closed pull request owns prerelease artifacts + When its close event is processed + Then the workflow runs cleanup only for that pull request's prerelease artifacts + And no stable artifact, tag, or release is created +``` + +### FR6 — Default-branch pushes MUST authorize stable publication after validation {#fr6} + +A push to the default branch MUST publish a stable version only after all required build, test, quality, and publication +gates succeed. A successful stable release MUST own promotion cleanup. + +#### Behavioral scenarios {#fr6-scenarios} + +```gherkin +Scenario: Publish a merged pull request + Given a merged pull request has an unambiguous release intent + And its merge commit is pushed to the default branch + When all required validation gates succeed + Then the workflow publishes the resulting stable version + And the publication is associated with the pushed commit + And the successful release performs promotion cleanup +``` + +### FR7 — Published artifacts MUST match the planned version {#fr7} + +Every prerelease or stable publication MUST contain the planned manifest version and prerelease identity. A mismatch +MUST fail publication before the release becomes visible. + +#### Behavioral scenarios {#fr7-scenarios} + +```gherkin +Scenario: Reject an incorrectly stamped artifact + Given Plan resolves a release version + And the built artifact reports a different version + When publication is attempted + Then publication fails + And no release is made visible for that artifact +``` + +### FR8 — Pull-request prereleases MUST have deterministic immutable identities {#fr8} + +A pull-request prerelease MUST use a deterministic identity scoped to its pull request. Reprocessing the same +pull-request state MUST resolve the same identity, and different pull requests MUST NOT resolve the same identity. +PowerShell Gallery versions MUST be treated as immutable and MUST NOT be overwritten. + +#### Behavioral scenarios {#fr8-scenarios} + +```gherkin +Scenario: Reprocess the same pull-request state + Given a pull request has resolved a prerelease identity + When the same pull-request state is processed again + Then the workflow resolves the same prerelease identity + And it detects an existing publication instead of attempting an overwrite + +Scenario: Publish prereleases for distinct pull requests + Given two pull requests are eligible for prerelease publication + When both pull requests are processed + Then each pull request resolves a distinct prerelease identity +``` + +### FR9 — Plan MUST resolve lifecycle policy before downstream work {#fr9} + +Plan MUST resolve lifecycle policy before build, test, or release execution. Its enriched Settings MUST contain the +event and run type, event action, pull-request identity, state and merge status, labels, version bump, base version, +manifest version, prerelease identifier, full version or tag, target commit, resolved release action, create and publish +flags, cleanup intent, artifact identity, release-note source and boundary, and authorization capabilities. Downstream +work MUST consume Settings and MUST NOT reinterpret event data, labels, or repository settings. + +#### Behavioral scenarios {#fr9-scenarios} + +```gherkin +Scenario: Execute a planned prerelease action + Given Plan resolves a pull request as an eligible prerelease publication + When downstream work executes + Then it consumes the planned release action and version + And it does not re-evaluate pull-request labels or event data + +Scenario: Execute a planned cleanup-only action + Given Plan resolves an abandoned pull-request close as cleanup only + When release execution runs + Then it reconciles only the planned cleanup state + And it does not create or publish an artifact +``` + +### FR10 — Stable targets MUST aggregate unreleased merged pull requests {#fr10} + +For every stable push or recovery target, Plan MUST aggregate merged pull requests and their release intent from the +last successfully published version through the target commit. The resulting stable action MUST converge when GitHub +replaces an intermediate pending run. + +#### Behavioral scenarios {#fr10-scenarios} + +```gherkin +Scenario: Publish after an intermediate pending push is replaced + Given merged pull requests exist after the last successfully published version + And an intermediate default-branch push is replaced while pending + When a later default-branch push is planned + Then Plan aggregates every merged pull request through the later target commit + And the stable release uses the aggregated release intent + +Scenario: Recover a range of unreleased merged pull requests + Given merged pull requests exist after the last successfully published version + When a maintainer dispatches recovery for a later default-branch commit + Then Plan aggregates every merged pull request through that target commit + And the release notes use that aggregated range +``` + +### FR11 — Repository operations MUST use scoped authorization {#fr11} + +The caller MUST declare top-level `permissions: {}`. Its Process-PSModule job MUST grant only `contents: read`, +`pages: write`, and `id-token: write`. It MUST explicitly map `PSGALLERY_API_KEY`, `GitHubAppClientId`, and +`GitHubAppPrivateKey`; `secrets: inherit` MUST NOT be used. It MAY map `TestData` only for module-local tests. +When present, `TestData` MUST contain a JSON object with separate `secrets` and `variables` maps; callers MUST omit it +when unused. A caller MUST NOT set `with.Debug: true`; the reusable workflow default is `false`. + +Built-in `GITHUB_TOKEN` MAY authorize repository-local, non-user-facing work when those permissions are sufficient, +including checkout, reads, and standard Pages/OIDC deployment. GitHub App installation tokens MUST authorize all +user-facing interactions and operations beyond the built-in token boundary, including pull-request comments and labels, +commit statuses and check-facing reporting, releases, tags, assets, and cleanup. Tokens MUST remain scoped to the steps +that require them. + +#### Behavioral scenarios {#fr11-scenarios} + +```gherkin +Scenario: Run with the caller's minimum permissions + Given the caller declares top-level permissions as an empty object + And its Process-PSModule job grants only contents read, Pages write, and ID-token write + When the reusable workflow performs checkout or standard Pages deployment + Then it may use the built-in workflow token within that granted boundary + +Scenario: Provide the required caller credentials explicitly + Given a conforming caller invokes the reusable workflow + When it maps credentials to the Process-PSModule job + Then it maps PSGALLERY_API_KEY, GitHubAppClientId, and GitHubAppPrivateKey explicitly + And it does not use secrets inherit + +Scenario: Provide optional module-local test data + Given module-local tests require caller-provided data + When the caller maps TestData + Then its secret value is a JSON object with separate secrets and variables maps + And the caller omits TestData when tests do not require it + +Scenario: Keep caller debug disabled + Given a conforming caller invokes the reusable workflow + When it sets workflow inputs + Then it does not set Debug to true + And the reusable workflow uses its false default + +Scenario: Perform a user-facing repository operation + Given the reusable workflow must create a pull-request comment or release + When the operation requires authority beyond the built-in token boundary + Then it creates a narrowly scoped GitHub App installation token + And it uses the App token only for the steps that require that authority +``` + +### FR12 — Plan MUST authorize events before downstream execution {#fr12} + +The caller MUST invoke the reusable workflow without a caller-level fork or event condition. For a normal fork +`pull_request`, the controlled upstream Plan implementation MUST derive a restricted capability envelope from immutable +GitHub event metadata, including fork, base, and head identities, before interpreting repository settings or executing +checked-out code. The Settings record MUST set `IsFork=true`, `AllowAppToken=false`, `AllowPublication=false`, and +`AllowMutation=false`. + +The restricted mode MAY perform repository-local checkout, build, lint, and test with the least-privilege built-in +token. It MUST NOT create an App token; access PowerShell Gallery; create pull-request comments, labels, or status +mutations; create releases, tags, or assets; perform cleanup; deploy Pages; or run another privileged or user-facing +operation. Fork settings and files MAY be consumed only as untrusted validation and build inputs and MUST NOT broaden +the capability envelope. The restricted mode MUST provide a green or red validation outcome without contributor secrets. + +`pull_request_target` MUST be rejected before credentials or repository-defined code run. Every downstream job, +including a job with `always()`, MUST require successful Plan execution and valid Settings. Every privileged downstream +job MUST also require its relevant planned capability. No downstream job MAY evaluate missing or invalid Settings or +bypass the Plan gate. + +#### Behavioral scenarios {#fr12-scenarios} + +```gherkin +Scenario: Validate a normal fork pull request in restricted mode + Given a pull request originates from a fork through the pull_request event + When Plan evaluates immutable fork, base, and head metadata + Then Plan emits valid restricted Settings + And downstream work may perform only repository-local checkout, build, lint, and test + And the contributor receives a green or red validation outcome without configured secrets + +Scenario: Prevent untrusted inputs from expanding fork capabilities + Given a pull request originates from a fork through the pull_request event + And its repository settings attempt to enable publication + When Plan reads the settings or checked-out files + Then App-token, publication, mutation, deployment, and cleanup capabilities remain false + +Scenario: Reject a pull_request_target event + Given a pull_request_target event is received + When Plan evaluates the event + Then Plan rejects the event before credentials or repository-defined code run + And no downstream job receives authorized Settings + +Scenario: Gate a privileged job for a restricted fork run + Given Plan emits valid restricted Settings for a fork pull request + And the planned capability for publication is false + When a publication job is evaluated + Then the job does not run + And it does not create an App token or parse an absent publication configuration +``` + +### FR13 — Caller conformance MUST be limited to the reusable-workflow boundary {#fr13} + +A conforming caller MUST contain exactly one Process-PSModule reusable-workflow call job and the shared top-level +triggers, concurrency, permissions, Plan authorization, and credential boundary that govern it. Repository-owned jobs +MAY coexist in the same workflow file or in separate workflows. They MUST NOT weaken or bypass any of those controls for +the Process-PSModule call. + +#### Behavioral scenarios {#fr13-scenarios} + +```gherkin +Scenario: Retain a repository-owned job beside the reusable-workflow call + Given a workflow contains one conforming Process-PSModule reusable-workflow call job + And a repository-owned documentation job exists in the same workflow file + When the workflow is evaluated for caller conformance + Then the documentation job is reported for visibility + And its existence does not make the Process-PSModule call nonconforming + +Scenario: Prevent a repository-owned job from bypassing the caller boundary + Given a repository-owned job exists beside or outside the caller workflow + When it weakens or bypasses the Process-PSModule call's trigger, concurrency, permissions, Plan authorization, or credential boundary + Then the caller arrangement is nonconforming +``` + +## Non-functional requirements + +### NFR1 — Lifecycle mutations MUST be idempotent {#nfr1} + +Retrying the same event for the same commit and resolved version MUST produce no more than one package, tag, and +release for that version. + +```gherkin +Scenario: Retry a publication after an interrupted run + Given a publication for a resolved version was interrupted + When the workflow retries the same event + Then it completes the missing work or reports the completed work + And it does not duplicate the package, tag, or release +``` + +### NFR2 — Pull-request cancellation MUST preserve non-pull-request serialization {#nfr2} + +The caller MUST use: + +```yaml +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +``` + +All pull-request events for one pull request share a cancellation scope. Push, manual dispatch, and scheduled runs +share a full-ref serialization scope and MUST NOT cancel an in-progress run. GitHub permits one running and one pending +run per group; a later non-pull-request run MAY replace an earlier pending run. Stable planning MUST therefore converge +from the last successfully published version. The group MUST use full `github.ref`; `github.ref_name` does not +distinguish colliding branch and tag names. + +```gherkin +Scenario: Serialize non-pull-request runs + Given a default-branch release is in progress + And an earlier default-branch run is pending + When a manual dispatch or scheduled validation starts for the same full ref + Then the later run does not cancel the running release + And it may replace the earlier pending run + And the next stable plan aggregates the unreleased merged pull requests +``` + +### NFR3 — Each lifecycle outcome MUST be auditable {#nfr3} + +Every run MUST report its event category, resolved or validated version, release decision, and terminal outcome. + +### NFR4 — Pull-request mutation paths MUST resume and converge {#nfr4} + +Every pull-request path, including prerelease publication and cleanup, MUST be idempotent and resumable after +cancellation. The next `synchronize`, `labeled`, `unlabeled`, or `closed` event MUST reconcile release-related state +to the latest pull-request state. Cancellation MAY leave transient partial state but MUST NOT leave a permanent +duplicate or obsolete artifact without its required disposition. + +### NFR5 — Pull-request events MUST NOT produce production artifacts {#nfr5} + +A pull-request event MUST NOT create a stable or signable production artifact. Pull-request events MAY create only +eligible prerelease artifacts and associated metadata. + +### NFR6 — Immutable Gallery prereleases MUST have a durable disposition {#nfr6} + +An obsolete PowerShell Gallery prerelease MUST be unlisted through a supported Gallery API when feasible. When unlisting +is infeasible, the workflow MUST retain and record the immutable version. GitHub Release and tag cleanup MUST execute +independently from Gallery disposition. + +```gherkin +Scenario: Dispose of an obsolete Gallery prerelease + Given a pull-request prerelease is obsolete + And a supported Gallery API can unlist that version + When the prerelease is reconciled + Then the workflow unlists the immutable Gallery package + And it performs GitHub Release and tag cleanup independently + +Scenario: Retain an immutable Gallery prerelease + Given a pull-request prerelease is obsolete + And unlisting that Gallery version is infeasible + When the prerelease is reconciled + Then the workflow records the retained immutable Gallery version + And it performs GitHub Release and tag cleanup independently +``` + +### NFR7 — App-required operations MUST fail closed {#nfr7} + +When an operation requires GitHub App authorization and the required App token is unavailable, the operation MUST fail +before an unauthorized API request, status update, comment, release, tag or asset mutation, or cleanup. It MUST NOT +fall back to built-in `GITHUB_TOKEN` authority. + +## Cross-cutting acceptance criteria + +```gherkin +Scenario: Lifecycle runs preserve release ownership after cancellation + Given a scheduled validation and an abandoned pull-request cleanup overlap a main-push release + And the cleanup supersedes a canceled prerelease publication + When all three runs complete + Then the scheduled run reports validation without a release mutation + And the cleanup reconciles only the abandoned pull request's prereleases + And none of the runs cancel the main-push release + And the main-push run is the only run that publishes the stable release and performs promotion cleanup +``` + +## Related + +- [Process-PSModule workflow lifecycle design](process-workflow-lifecycle-design.md) +- [Process-PSModule caller contract](process-workflow-fleet-standard.md) diff --git a/docs/zensical.toml b/docs/zensical.toml index cdd222bf..aacf6057 100644 --- a/docs/zensical.toml +++ b/docs/zensical.toml @@ -47,6 +47,8 @@ nav = [ {"Scenario matrix" = "reference/scenario-matrix.md"}, {"Framework test IDs" = "reference/framework-test-ids.md"}, {"Dependencies" = "reference/dependencies.md"}, + {"Workflow lifecycle specification" = "reference/process-workflow-lifecycle-specification.md"}, + {"Workflow lifecycle design" = "reference/process-workflow-lifecycle-design.md"}, ]}, {"Specification" = [ "specification/index.md", From 7c856a22f72ff1f84a02a677a0c31b06e2a02128 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 16:36:06 +0200 Subject: [PATCH 24/26] Fail closed on workflow parse errors Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../Get-ProcessPSModuleWorkflowInventory.ps1 | 10 +++++++++- ...-ProcessPSModuleWorkflowInventory.Tests.ps1 | 18 +++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 index ac1573c3..d34fd3b9 100644 --- a/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 +++ b/.github/scripts/Get-ProcessPSModuleWorkflowInventory.ps1 @@ -770,7 +770,7 @@ function ConvertTo-WorkflowInventoryMarkdown { if ($TargetReference) { $matchingTarget = @($parsed | Where-Object MatchesTarget).Count $lines.Add("- Target reference: $TargetReference") - $lines.Add("- Matching target: $matchingTarget/$($parsed.Count)") + $lines.Add("- Matching target: $matchingTarget/$($Inventory.Count)") } $lines.Add('') $lines.Add('## Reference distribution') @@ -929,6 +929,8 @@ if (-not $inventory) { throw "No reusable workflow jobs using [$WorkflowReference] were found in the discovered files." } +$parseErrors = @($inventory | Where-Object Status -eq 'ParseError') + if ($JsonPath) { $parent = Split-Path -Path $JsonPath -Parent if ($parent) { @@ -950,4 +952,10 @@ if ($MarkdownPath) { Set-Content -LiteralPath $MarkdownPath -Encoding utf8 } +if ($parseErrors) { + $parseErrorDetails = $parseErrors | + ForEach-Object { "[$($_.Repository)/$($_.WorkflowPath)]: $($_.Error)" } + throw "$($parseErrors.Count) matching workflow file(s) could not be parsed. $($parseErrorDetails -join '; ')" +} + $inventory diff --git a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 index 60d40da5..a834d138 100644 --- a/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 +++ b/.github/scripts/tests/Get-ProcessPSModuleWorkflowInventory.Tests.ps1 @@ -143,9 +143,11 @@ Describe 'Get-ProcessPSModuleWorkflowInventory' { Get-Content -LiteralPath $markdownPath -Raw | Should -Match '0 0 \\\* \\\* \\\*' } - It 'records a parse error for a matching malformed workflow' { + It 'reports and fails closed for a matching malformed workflow' { $malformedRoot = Join-Path $testRoot 'Malformed' $malformedWorkflowRoot = Join-Path $malformedRoot '.github/workflows' + $jsonPath = Join-Path $malformedRoot 'output/inventory.json' + $markdownPath = Join-Path $malformedRoot 'output/inventory.md' & git init --quiet --initial-branch=main $malformedRoot & git -C $malformedRoot config user.email 'inventory-tests@example.invalid' & git -C $malformedRoot config user.name 'Inventory Tests' @@ -160,11 +162,21 @@ jobs: & git -C $malformedRoot add . & git -C $malformedRoot commit --quiet -m 'Add malformed workflow' - $result = @(& $scriptPath -Path $malformedRoot) - + { + & $scriptPath ` + -Path $malformedRoot ` + -TargetReference 'v8' ` + -JsonPath $jsonPath ` + -MarkdownPath $markdownPath | + Out-Null + } | Should -Throw -ExpectedMessage '*1 matching workflow file(s) could not be parsed*' + + $result = @(Get-Content -LiteralPath $jsonPath -Raw | ConvertFrom-Json) $result.Count | Should -Be 1 $result[0].Status | Should -Be 'ParseError' $result[0].Error | Should -Not -BeNullOrEmpty + Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Parse errors: 1' + Get-Content -LiteralPath $markdownPath -Raw | Should -Match 'Matching target: 0/1' } It 'fails closed when no matching workflow is found' { From 946d4a69d01eba3e11dc058beac2acee4a38cb1b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 17:07:00 +0200 Subject: [PATCH 25/26] Limit caller variation to test data Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-standard.md | 30 +++++++++---------- .../process-workflow-lifecycle-design.md | 4 ++- ...rocess-workflow-lifecycle-specification.md | 11 +++---- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index 9732feab..e3137753 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -148,7 +148,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Permissions | Default deny at workflow level, then grant the caller job `contents: read`, `pages: write`, and `id-token: write`. | Selected for the candidate: use `GITHUB_TOKEN` for repository-local, non-user-facing platform operations and App tokens for user-facing or otherwise unsupported operations. | | Fork behavior | Keep the caller unconditional; classify fork pull requests as restricted read-only validation in `Plan`. | Selected for the candidate; execution policy belongs to Process-PSModule rather than every consumer. | | Credentials | Explicitly map the three v8 credentials; optionally map `TestData` when module-local tests need it. | Define a narrower credential profile for repositories that cannot publish. | -| Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. | +| Optional surface | Permit only the documented `TestData` secret mapping. | Selected for the candidate; every other caller-contract field matches the canonical template. | The `v8` reference is the controlled moving major tag for this PSModule-owned workflow. On 2026-08-15, `v8`, `v8.0`, and the immutable `v8.0.0` release tag all resolve to commit `5a11e8e8b018faf97017e0416f136a751c026713`. @@ -191,21 +191,15 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | | Scope | Require one conforming `Process-PSModule` delegation job. | Additional repository-owned jobs do not change caller conformance. | -## Candidate optional elements +## Allowed caller variation -These are evidence-based candidate variations, not approved policy. +The only conforming variation from the canonical template is the optional `TestData` secret mapping shown above. +Callers use it only when module-local tests need caller-defined secrets or variables, and expose only the required +values in the documented `secrets` and `variables` maps. -| Option | When it is appropriate | Constraint | -| --- | --- | --- | -| `TestData` secret | Module-local tests need caller-defined secrets or variables. | Optionally map the documented JSON object with separate `secrets` and `variables` maps, exposing only required values. | -| `with.SettingsPath` | The settings file is not `.github/PSModule.yml`. | Prefer the standard path for normal module repositories. | -| `with.WorkingDirectory` | The module is intentionally rooted below the repository root. | Keep the default `.` for the standard layout. | -| `with.ImportantFilePatterns` | A caller must override change detection at the workflow boundary. | Prefer stable configuration in `.github/PSModule.yml`; the supplied list replaces all defaults. | -| `with.Verbose`, `Version`, or `Prerelease` | A deliberate diagnostic or dependency-selection scenario needs it. | Do not hard-code temporary diagnostics into the fleet baseline. | -| Schedule time | Health runs need staggering or a repository-specific maintenance window. | Keep at least one documented schedule unless the repository records why health runs are unnecessary. | -| `run-name` | A repository needs clearer run presentation. | Presentation must not change job names or routing behavior. | - -Conforming callers do not set `with.Debug: true`; the reusable workflow default remains `false`. +Every other field in the Process-PSModule caller contract matches the template exactly. Callers do not add `with:` +inputs, change schedule timing, add `run-name`, add a caller condition, or broaden permissions. Repository-owned jobs +may coexist because they are outside the Process-PSModule caller contract; they do not modify the canonical call. ## Variations requiring a decision @@ -220,6 +214,10 @@ an approved structure: - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - a caller-level fork or event-authorization condition; - trigger-level path filters that bypass Process-PSModule important-file evaluation; +- any `with:` input, including `Debug`, `ImportantFilePatterns`, `Prerelease`, `SettingsPath`, `Verbose`, `Version`, or + `WorkingDirectory`; +- a schedule other than the canonical `0 0 * * *`; +- `run-name`; - caller permissions beyond `contents: read`, `pages: write`, and `id-token: write`. Use the built-in `GITHUB_TOKEN` for non-user-facing operations confined to the calling repository, including checkout @@ -256,7 +254,7 @@ the stable slug | Inherited secrets | 41 | Replace `secrets: inherit` with the three explicit `v8` credential mappings. | | Old API key only | 14 | Replace `APIKey`/`APIKEY` with the three explicit mappings; excludes the template pilot. | | Test data | 3 | Preserve each existing `TestData` payload while replacing the old API key contract. | -| Custom input | 1 | Update `Yaml` last while preserving `TestData` and `ImportantFilePatterns`. | +| Custom input | 1 | Update `Yaml` last, preserve `TestData`, and remove the caller-level `ImportantFilePatterns` override. | Before opening leaves: @@ -271,7 +269,7 @@ Before opening leaves: 6. Confirm workflow-only changes are not important release changes. The fleet defaults match only `src/` and `README.md`; `Yaml` explicitly matches `src/`, `tests/`, and `README.md`, so this campaign should not publish modules. -After approval, each leaf would apply the agreed caller, retain the agreed optional mappings, and prove the PR path +After approval, each leaf would apply the canonical caller, retain optional `TestData` only where required, and prove the PR path before merge. Advance one wave only after the previous wave's push run completes without an unintended release. Completion would require a fresh inventory showing `60/60` on `v8`, the agreed trigger and concurrency contract, the agreed credential mapping, and no unresolved review or CI failures. diff --git a/docs/content/reference/process-workflow-lifecycle-design.md b/docs/content/reference/process-workflow-lifecycle-design.md index 72642640..5940865f 100644 --- a/docs/content/reference/process-workflow-lifecycle-design.md +++ b/docs/content/reference/process-workflow-lifecycle-design.md @@ -116,7 +116,9 @@ These explicit secret mappings are required. `secrets: inherit` is prohibited. T ``` When present, `TestData` contains a JSON object with separate `secrets` and `variables` maps. It is omitted when -unused. Callers do not set `with.Debug: true`; the reusable workflow default is `false`. +unused. It is the only permitted variation from the canonical caller template. Callers do not declare `run-name`, +alter the canonical schedule, add a caller condition, or pass `with:` inputs. Repository-owned jobs may coexist +outside this caller contract. Built-in `GITHUB_TOKEN` authorizes checkout, repository-local reads, and standard Pages/OIDC deployment within the job boundary. Step-scoped GitHub App installation tokens authorize pull-request comments and labels, commit statuses and diff --git a/docs/content/reference/process-workflow-lifecycle-specification.md b/docs/content/reference/process-workflow-lifecycle-specification.md index 3024eef8..3637bad7 100644 --- a/docs/content/reference/process-workflow-lifecycle-specification.md +++ b/docs/content/reference/process-workflow-lifecycle-specification.md @@ -227,7 +227,8 @@ The caller MUST declare top-level `permissions: {}`. Its Process-PSModule job MU `pages: write`, and `id-token: write`. It MUST explicitly map `PSGALLERY_API_KEY`, `GitHubAppClientId`, and `GitHubAppPrivateKey`; `secrets: inherit` MUST NOT be used. It MAY map `TestData` only for module-local tests. When present, `TestData` MUST contain a JSON object with separate `secrets` and `variables` maps; callers MUST omit it -when unused. A caller MUST NOT set `with.Debug: true`; the reusable workflow default is `false`. +when unused. `TestData` MUST be the only variation from the canonical caller template. A caller MUST NOT declare +`run-name`, alter the canonical schedule, add a caller condition, or pass any `with:` input. Built-in `GITHUB_TOKEN` MAY authorize repository-local, non-user-facing work when those permissions are sufficient, including checkout, reads, and standard Pages/OIDC deployment. GitHub App installation tokens MUST authorize all @@ -256,11 +257,11 @@ Scenario: Provide optional module-local test data Then its secret value is a JSON object with separate secrets and variables maps And the caller omits TestData when tests do not require it -Scenario: Keep caller debug disabled +Scenario: Match the canonical caller template Given a conforming caller invokes the reusable workflow - When it sets workflow inputs - Then it does not set Debug to true - And the reusable workflow uses its false default + When its Process-PSModule caller contract is compared with the canonical template + Then every field matches the template + And TestData is the only permitted optional mapping Scenario: Perform a user-facing repository operation Given the reusable workflow must create a pull-request comment or release From 581bf0b20779aad27ace088ab764946fa9bca9c7 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 15 Aug 2026 17:07:35 +0200 Subject: [PATCH 26/26] Require the canonical caller template Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../process-workflow-fleet-standard.md | 17 +++++------ .../process-workflow-lifecycle-design.md | 9 +++--- ...rocess-workflow-lifecycle-specification.md | 30 ++++++++----------- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/docs/content/reference/process-workflow-fleet-standard.md b/docs/content/reference/process-workflow-fleet-standard.md index e3137753..ddde8547 100644 --- a/docs/content/reference/process-workflow-fleet-standard.md +++ b/docs/content/reference/process-workflow-fleet-standard.md @@ -141,7 +141,7 @@ decisions before canonical guides, templates, or consumer workflows adopt it: | Decision | Candidate | Alternatives still open | | --- | --- | --- | -| Contract scope | Standardize the `Process-PSModule` caller job and its shared workflow controls, not every job in the file. | Selected for the candidate; repository-owned jobs remain outside the caller contract. | +| Contract scope | Keep the file identical to the canonical template except for optional `TestData`. | Selected for the candidate; repository-owned jobs use separate workflow files. | | Trigger ownership | The caller owns manual, schedule, default-branch push, and pull-request triggers. | Move some trigger policy into separate workflows or omit selected event classes. | | Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. | | Concurrency | Use the workflow plus PR-number-or-full-ref key and cancel only pull-request runs. | Selected for the candidate: PR reconciliation must be resumable; non-PR runs serialize by full ref. | @@ -189,7 +189,7 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar | Event gate | Keep the caller unconditional and authorize capabilities in `Plan`. | The reusable workflow owns execution policy; fork pull requests may validate but cannot obtain App credentials, publish, deploy, clean up, or mutate repository state. | | Reference | Use the intended internal floating major tag (`v8`) after tag governance is enforced. | Compatible owned releases roll out centrally; breaking releases require a new major and campaign. | | Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. | -| Scope | Require one conforming `Process-PSModule` delegation job. | Additional repository-owned jobs do not change caller conformance. | +| Scope | Require exactly the canonical `Process-PSModule` workflow file. | Repository-owned jobs use separate workflow files. | ## Allowed caller variation @@ -197,9 +197,9 @@ The only conforming variation from the canonical template is the optional `TestD Callers use it only when module-local tests need caller-defined secrets or variables, and expose only the required values in the documented `secrets` and `variables` maps. -Every other field in the Process-PSModule caller contract matches the template exactly. Callers do not add `with:` -inputs, change schedule timing, add `run-name`, add a caller condition, or broaden permissions. Repository-owned jobs -may coexist because they are outside the Process-PSModule caller contract; they do not modify the canonical call. +Every other field in the Process-PSModule workflow file matches the template exactly. Callers do not add jobs, `with:` +inputs, change schedule timing, add `run-name`, add a caller condition, or broaden permissions. Repository-owned +automation uses separate workflow files. ## Variations requiring a decision @@ -214,6 +214,7 @@ an approved structure: - a concurrency key other than workflow plus PR number or full ref, or cancellation behavior other than pull-request-only; - a caller-level fork or event-authorization condition; - trigger-level path filters that bypass Process-PSModule important-file evaluation; +- any additional job in `.github/workflows/Process-PSModule.yml`; - any `with:` input, including `Debug`, `ImportantFilePatterns`, `Prerelease`, `SettingsPath`, `Verbose`, `Version`, or `WorkingDirectory`; - a schedule other than the canonical `0 0 * * *`; @@ -237,10 +238,8 @@ evaluating Settings. Privileged-context events such as `pull_request_target` remain unsupported unless separately designed to prevent untrusted code from crossing the credential boundary. -The contract applies to the shared workflow controls and the `Process-PSModule` delegation job shown above. Repositories -may define additional jobs in the same file or separate workflows. The inventory reports those jobs for visibility, but -the contract does not prescribe their implementation. Additional jobs must not weaken or bypass the permissions, -authorization, trigger, or concurrency controls governing the Process-PSModule call. +The contract applies to the entire `.github/workflows/Process-PSModule.yml` file shown above. Repository-owned +automation uses separate workflow files so the canonical caller remains directly comparable across the fleet. ## Rollout boundary diff --git a/docs/content/reference/process-workflow-lifecycle-design.md b/docs/content/reference/process-workflow-lifecycle-design.md index 5940865f..476b7de6 100644 --- a/docs/content/reference/process-workflow-lifecycle-design.md +++ b/docs/content/reference/process-workflow-lifecycle-design.md @@ -36,8 +36,8 @@ requested state. The [Process-PSModule caller contract](process-workflow-fleet-standard.md) contains exactly one reusable-workflow call job and the shared top-level triggers, concurrency, permissions, Plan authorization, and credential boundary that govern -it. Repository-owned jobs MAY coexist in the same workflow file or in separate workflows. They are visible to -conformance reporting and MUST NOT weaken or bypass the Process-PSModule call boundary. +it. The file matches the canonical template except for optional `TestData`. Repository-owned automation uses separate +workflow files. ## Event authorization @@ -117,8 +117,7 @@ These explicit secret mappings are required. `secrets: inherit` is prohibited. T When present, `TestData` contains a JSON object with separate `secrets` and `variables` maps. It is omitted when unused. It is the only permitted variation from the canonical caller template. Callers do not declare `run-name`, -alter the canonical schedule, add a caller condition, or pass `with:` inputs. Repository-owned jobs may coexist -outside this caller contract. +alter the canonical schedule, add jobs or caller conditions, or pass `with:` inputs. Built-in `GITHUB_TOKEN` authorizes checkout, repository-local reads, and standard Pages/OIDC deployment within the job boundary. Step-scoped GitHub App installation tokens authorize pull-request comments and labels, commit statuses and @@ -194,7 +193,7 @@ The lifecycle is verified with event payload fixtures and publication fakes befo | Stable aggregation | Push, manual-dispatch, and scheduled bursts replace a pending run and prove all unreleased merged pull requests are aggregated. | | Caller authorization | Fixtures verify the explicit permissions and credential mappings, App-token failure, and no built-in-token fallback. | | Fork authorization | Fixtures verify immutable-metadata-first restricted Settings, no privileged operations, and `pull_request_target` rejection. | -| Caller boundary | Fixtures verify repository-owned jobs remain visible without weakening or bypassing the reusable-workflow call boundary. | +| Caller boundary | Fixtures verify exact canonical-template conformance with optional `TestData` as the only variation. | ## Related diff --git a/docs/content/reference/process-workflow-lifecycle-specification.md b/docs/content/reference/process-workflow-lifecycle-specification.md index 3637bad7..80b17937 100644 --- a/docs/content/reference/process-workflow-lifecycle-specification.md +++ b/docs/content/reference/process-workflow-lifecycle-specification.md @@ -16,9 +16,8 @@ The lifecycle covers dispatch recovery, scheduled published-artifact validation, evaluation, closed-pull-request cleanup, and stable publication after a default-branch push. The [Process-PSModule caller contract](process-workflow-fleet-standard.md) requires exactly one reusable-workflow call -job and the shared top-level controls that govern it. Repository-owned jobs MAY exist in the same workflow file or in -separate workflows, provided they do not weaken or bypass the call's trigger, concurrency, permissions, Plan -authorization, or credential boundary. +job and the shared top-level controls that govern it. The workflow file MUST match the canonical template except for +optional `TestData`. Repository-owned automation MUST use separate workflow files. ## Functional requirements @@ -319,27 +318,24 @@ Scenario: Gate a privileged job for a restricted fork run And it does not create an App token or parse an absent publication configuration ``` -### FR13 — Caller conformance MUST be limited to the reusable-workflow boundary {#fr13} +### FR13 — The caller workflow file MUST match the canonical template {#fr13} -A conforming caller MUST contain exactly one Process-PSModule reusable-workflow call job and the shared top-level -triggers, concurrency, permissions, Plan authorization, and credential boundary that govern it. Repository-owned jobs -MAY coexist in the same workflow file or in separate workflows. They MUST NOT weaken or bypass any of those controls for -the Process-PSModule call. +A conforming `.github/workflows/Process-PSModule.yml` MUST match the canonical template exactly except for optional +`TestData`. Repository-owned automation MUST use separate workflow files. #### Behavioral scenarios {#fr13-scenarios} ```gherkin -Scenario: Retain a repository-owned job beside the reusable-workflow call - Given a workflow contains one conforming Process-PSModule reusable-workflow call job - And a repository-owned documentation job exists in the same workflow file +Scenario: Keep repository-owned automation separate + Given a repository needs a documentation job When the workflow is evaluated for caller conformance - Then the documentation job is reported for visibility - And its existence does not make the Process-PSModule call nonconforming + Then the documentation job exists in a separate workflow file + And Process-PSModule.yml still matches the canonical template -Scenario: Prevent a repository-owned job from bypassing the caller boundary - Given a repository-owned job exists beside or outside the caller workflow - When it weakens or bypasses the Process-PSModule call's trigger, concurrency, permissions, Plan authorization, or credential boundary - Then the caller arrangement is nonconforming +Scenario: Reject any other caller variation + Given Process-PSModule.yml differs from the canonical template + When the difference is not the optional TestData mapping + Then the caller is nonconforming ``` ## Non-functional requirements