Skip to content

Commit 8825edc

Browse files
docs: require App-authorized workflow access
Co-authored-by: Copilot App <[email protected]>
1 parent fcb5856 commit 8825edc

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

docs/content/reference/process-workflow-lifecycle-design.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The confirmed reusable workflow runs a `Plan` job, enriches one settings object
1515

1616
The version resolver treats non-pull-request events, including `workflow_dispatch` and `schedule`, as events without a release decision. The workflow's concurrency identity contains either the pull request number or the Git ref, and its runs are not canceled. The documented scenario matrix also identifies closed-pull-request cleanup as non-stable behavior.
1717

18-
The current code detects an existing published version, resumes GitHub Release creation, and repeats GitHub Release and tag cleanup. Full reconciliation of immutable PowerShell Gallery prereleases is a design gap: it requires implementation and cancellation-boundary tests. These facts establish the starting point. They do not implement the scheduled validation or manual recovery behavior proposed below.
18+
The current code detects an existing published version, resumes GitHub Release creation, and repeats GitHub Release and tag cleanup. Full reconciliation of immutable PowerShell Gallery prereleases is a design gap: it requires implementation and cancellation-boundary tests. Current reusable jobs create App tokens after checkout, and the Pages workflow declares `pages: write` and `id-token: write`; the permissionless-caller candidate is therefore not confirmed behavior. These facts establish the starting point. They do not implement the scheduled validation or manual recovery behavior proposed below.
1919

2020
## Candidate event routing
2121

@@ -56,6 +56,22 @@ The build stage stamps exactly the planned manifest version and prerelease ident
5656

5757
One general module release action or reusable workflow consumes enriched Settings after validation. It handles stable release, prerelease, recovery or resume, cleanup-only, and no-op actions according to the planned desired release action and flags. It verifies the artifact when an artifact is required and reconciles only the requested state. It does not recompute versioning, labels, event routing, or cleanup policy.
5858

59+
## Candidate repository authorization
60+
61+
The caller remains permissionless:
62+
63+
```yaml
64+
permissions: {}
65+
```
66+
67+
It grants no job permissions. Every reusable-workflow job creates a narrowly scoped GitHub App installation token before checkout and passes that token explicitly to checkout. The same explicit App token authorizes `gh` and API requests, linter statuses and comments, releases, and cleanup. Downstream jobs do not rely on built-in `GITHUB_TOKEN` authority.
68+
69+
Each job requests only the App installation permissions required for its operation. A missing App token is an authorization failure: the job stops before checkout or its repository operation, without falling back to the built-in workflow token.
70+
71+
## Candidate Pages deployment boundary
72+
73+
The current Pages deployment uses `actions/deploy-pages`, which requires `pages: write` and `id-token: write`. This is a design gap under the permissionless-caller candidate. The implementation must either replace it with an App-authenticated supported deployment path or document an explicit, unavoidable Pages/OIDC exception. It MUST NOT silently grant caller or job `GITHUB_TOKEN` permissions.
74+
5975
## Candidate stable Plan aggregation
6076

6177
Every stable push and recovery target uses the same aggregation rule. Plan finds the last successfully published stable version and its associated target commit, then queries merged pull requests through the requested target commit. It aggregates their release intent and uses that range for the stable version decision and release notes.
@@ -130,6 +146,9 @@ The lifecycle contract is exercised with event payload fixtures and publication
130146
| Pull-request convergence | Canceled prerelease-publication and cleanup fixtures followed by synchronize, label, unlabel, and close events that prove the latest pull-request state is reconciled. |
131147
| Gallery immutability | Deterministic pull-request identity, existing-version detection, supported-unlist, and retained-version fixtures across the cancellation boundary. |
132148
| Stable aggregation | Bursts of main-push, manual-dispatch, and scheduled fixtures that replace an intermediate pending run and prove the later stable target aggregates all unreleased merged pull requests. |
149+
| Permissionless caller | Empty caller permissions and no job grants with explicit App-token checkout, API, status, comment, release, and cleanup verification. |
150+
| Authorization failure | Missing-App-token fixtures that prove each repository job fails closed without built-in token fallback. |
151+
| Pages boundary | A supported App-authenticated deployment-path test or an explicit Pages/OIDC exception test. |
133152

134153
## Decisions requiring approval
135154

@@ -140,6 +159,7 @@ The candidate does not decide the following:
140159
- Which consumer-facing checks comprise scheduled published-artifact validation.
141160
- Whether removing prerelease eligibility cleans up existing prereleases immediately or leaves them until the abandoned-close cleanup route.
142161
- Whether the supported Gallery API can unlist obsolete prereleases; otherwise, how retained immutable versions are recorded.
162+
- Whether Pages can use a supported App-authenticated deployment path or requires an explicit Pages/OIDC exception.
143163
- Approval of the selected caller concurrency expression in [PSModule/Process-PSModule#514](https://github.com/PSModule/Process-PSModule/issues/514).
144164

145165
## Related

docs/content/reference/process-workflow-lifecycle-specification.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,27 @@ Scenario: Recover a range of unreleased merged pull requests
209209
And the release notes use that aggregated range
210210
```
211211

212+
### FR11 — Repository operations MUST use explicit GitHub App authorization {#fr11}
213+
214+
The caller MUST declare top-level `permissions: {}` and MUST NOT grant job permissions. The reusable workflow MUST create narrowly scoped GitHub App installation tokens before repository access and pass them explicitly to every repository operation. Built-in `GITHUB_TOKEN` authority MUST NOT authorize checkout, API access, status reporting, comments, releases, or cleanup.
215+
216+
#### Behavioral scenarios {#fr11-scenarios}
217+
218+
```gherkin
219+
Scenario: Run from a permissionless caller
220+
Given the caller declares top-level permissions as an empty object
221+
And the caller grants no job permissions
222+
When the reusable workflow runs with authorized GitHub App installation tokens
223+
Then checkout and repository operations use the explicit App tokens
224+
And the built-in workflow token does not authorize those operations
225+
226+
Scenario: Start a repository operation
227+
Given the reusable workflow requires repository access
228+
When the workflow begins
229+
Then it creates the required GitHub App token before checkout
230+
And it passes that token explicitly to checkout and subsequent repository operations
231+
```
232+
212233
## Non-functional requirements
213234

214235
### NFR1 — Lifecycle mutations MUST be idempotent {#nfr1}
@@ -317,6 +338,21 @@ Scenario: Retain an immutable Gallery prerelease
317338
And it performs GitHub Release and tag cleanup independently
318339
```
319340

341+
### NFR7 — Missing App authorization MUST fail closed {#nfr7}
342+
343+
When the required GitHub App authorization is unavailable, every repository-mutating or reporting job MUST fail before performing an unauthorized checkout, API request, status update, comment, release, or cleanup. It MUST NOT fall back to built-in `GITHUB_TOKEN` authority.
344+
345+
#### Behavioral scenarios {#nfr7-scenarios}
346+
347+
```gherkin
348+
Scenario: Reject a job without GitHub App authorization
349+
Given a caller with no granted token permissions
350+
And a reusable-workflow job cannot create its required GitHub App token
351+
When the job attempts repository access
352+
Then the job fails before the repository operation
353+
And it does not use the built-in workflow token as a fallback
354+
```
355+
320356
## Cross-cutting acceptance criteria
321357

322358
### AC1 — Verifies: [FR1](#fr1), [FR6](#fr6), [FR7](#fr7), [NFR1](#nfr1)
@@ -331,7 +367,7 @@ Scenario: Recover release notes after a missed main-push publication
331367
And a retry creates no duplicate publication
332368
```
333369

334-
### AC2 — Verifies: [FR2](#fr2), [FR4](#fr4), [FR5](#fr5), [FR6](#fr6), [FR8](#fr8), [FR9](#fr9), [FR10](#fr10), [NFR2](#nfr2), [NFR3](#nfr3), [NFR4](#nfr4), [NFR5](#nfr5), [NFR6](#nfr6)
370+
### AC2 — Verifies: [FR2](#fr2), [FR4](#fr4), [FR5](#fr5), [FR6](#fr6), [FR8](#fr8), [FR9](#fr9), [FR10](#fr10), [FR11](#fr11), [NFR2](#nfr2), [NFR3](#nfr3), [NFR4](#nfr4), [NFR5](#nfr5), [NFR6](#nfr6), [NFR7](#nfr7)
335371

336372
```gherkin
337373
Scenario: Lifecycle runs preserve release ownership after cancellation

0 commit comments

Comments
 (0)