Skip to content

Commit 7a1975e

Browse files
docs: authorize workflow events in plan
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 89d3d9b commit 7a1975e

3 files changed

Lines changed: 46 additions & 19 deletions

File tree

docs/content/reference/process-workflow-fleet-standard.md

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,16 @@ on:
108108

109109
concurrency:
110110
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
111-
cancel-in-progress: false
111+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
112112

113-
permissions:
114-
contents: write
115-
pull-requests: write
116-
statuses: write
117-
pages: write
118-
id-token: write
113+
permissions: {}
119114

120115
jobs:
121116
Process-PSModule:
122-
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
117+
permissions:
118+
contents: read
119+
pages: write
120+
id-token: write
123121
uses: PSModule/Process-PSModule/.github/workflows/workflow.yml@v8
124122
secrets:
125123
PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }}
@@ -136,9 +134,9 @@ decisions before canonical guides, templates, or consumer workflows adopt it:
136134
| Wrapper scope | Exactly one reusable-workflow job. | Permit repository-specific jobs in the same file, or define pre/post extension jobs. |
137135
| 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. |
138136
| Pull-request activities | Keep all six listed activity types. | Reduce the activity list if a v8 behavior is intentionally unsupported. |
139-
| 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. |
140-
| Permissions | Declare the five current scopes at workflow level. | Introduce settings-based least-privilege profiles or split read-only validation from release work. |
141-
| Fork behavior | Skip fork-originated pull requests in this credentialed wrapper. | Add a separate secret-free workflow or define another supported fork-validation design. |
137+
| Concurrency | Use the PR-number-or-ref key and cancel only superseded pull-request runs. | Use separate groups per event class or disable cancellation for all runs. |
138+
| Permissions | Set top-level permissions to empty and grant only `contents: read`, `pages: write`, and `id-token: write` to the caller job. | Define a narrower profile for repositories that do not publish Pages. |
139+
| Fork behavior | Invoke the reusable workflow unconditionally; Plan rejects unsupported fork events before credentialed or repository-defined code. | Add an independent secret-free fork-CI workflow. |
142140
| Credentials | Explicitly map the three v8 credentials. | Define a narrower credential profile for repositories that cannot publish. |
143141
| Optional surface | Permit only documented `TestData`, workflow inputs, schedule timing, and presentation metadata. | Allow additional extension points after naming and compatibility rules are agreed. |
144142

@@ -171,9 +169,9 @@ fleet campaign. Branch names, `latest`, floating minor tags, and unqualified tar
171169
| Default-branch push | Keep `push.branches: [main]`. | `v8` authorizes stable releases from the tested default-branch push. |
172170
| Manual dispatch | Keep `workflow_dispatch`. | Provides the documented default-branch manual release and recovery path. |
173171
| Schedule | Keep a scheduled health run. | Exercises current dependencies even when repository code is unchanged. |
174-
| 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. |
175-
| Permissions | Declare the five documented permissions explicitly. | The called workflow cannot elevate caller permissions. |
176-
| Fork guard | Skip pull requests whose head repository differs from `github.repository`. | GitHub withholds the required repository secrets from fork pull requests. |
172+
| Concurrency | Use the PR-number-or-ref key and cancel only pull-request runs. | Pull-request changes converge promptly while non-pull-request runs serialize by ref. |
173+
| Permissions | Use empty top-level permissions and the three caller-job permissions shown above. | Repository-local reads and Pages/OIDC stay narrow; App tokens provide broader authority. |
174+
| Fork authorization | Leave the caller job unconditional. | Plan rejects unsupported forks before credentials or repository-defined code run. |
177175
| 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. |
178176
| Credentials | Explicitly map the three required secrets. | Satisfies the `v7+` contract and prevents unrelated secret inheritance. |
179177
| Scope | Keep the caller as a single delegation job. | Repository-specific automation remains independently understandable and maintainable. |
@@ -202,14 +200,14 @@ an approved structure:
202200
- any Process-PSModule reference other than the intended major tag (`v8`), including a branch, `latest`, minor tag,
203201
exact patch tag, or full commit SHA;
204202
- missing `push` or `unlabeled` triggers;
205-
- `cancel-in-progress: true` or the old ref-only concurrency key;
203+
- a `cancel-in-progress` expression other than `github.event_name == 'pull_request'` or the old ref-only concurrency key;
206204
- trigger-level path filters that bypass Process-PSModule important-file evaluation;
207205
- unrelated additional jobs in the caller wrapper;
208206
- omitted documented permissions without a verified settings-based least-privilege profile.
209207

210-
Fork-originated pull requests are skipped by the candidate caller because reusable-workflow caller jobs cannot select a
211-
GitHub Environment and repository secrets are unavailable to forks. Supporting fork CI requires a separate, secret-free,
212-
read-only validation workflow under this candidate; #514 must approve that boundary.
208+
The candidate caller invokes the reusable workflow for fork-originated pull requests. Plan rejects unsupported fork events
209+
before credentials or repository-defined code run. Supporting fork CI requires a separate, secret-free, read-only workflow;
210+
issue [#514](https://github.com/PSModule/Process-PSModule/issues/514) must approve that boundary.
213211

214212
The candidate keeps repository-specific automation in a separate workflow file. That keeps the Process-PSModule wrapper
215213
identical enough for automated comparison while allowing modules to own unrelated schedules, generation, or integration

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ Plan resolves the caller event into one release classification before build and
3333

3434
Plan records its classification and release decision in enriched Settings. Downstream jobs consume that Settings object and do not infer policy from events, labels, or repository settings again.
3535

36+
## Candidate event authorization
37+
38+
The caller invokes the reusable workflow without a caller-level fork or event condition. Plan is the event-authorization boundary: it rejects unsupported fork pull requests before credentialed or repository-defined code runs. An authorization rejection produces no usable Settings and no credentialed follow-on work.
39+
40+
Every downstream job depends on a successful authorized Plan and valid Settings. This requirement applies equally to jobs that use `always()`: their conditions first require the Plan result and Settings validity, then apply their own failure-handling logic. A downstream job never parses missing or invalid Settings and cannot bypass the Plan gate.
41+
42+
Secret-free fork CI, if needed, is a separate workflow with its own trigger, authorization, and read-only contract. It is not a mode of the credentialed Process-PSModule reusable workflow.
43+
3644
## Candidate artifact and version boundary
3745

3846
Version resolution is the boundary between planning and release-capable work. The candidate carries one immutable release record in enriched Settings through build, test, and release execution:
@@ -155,6 +163,7 @@ The lifecycle contract is exercised with event payload fixtures and publication
155163
| Scoped caller permissions | Empty caller top-level permissions, the three job grants, built-in-token checkout/read, and standard Pages/OIDC verification. |
156164
| App authorization failure | Missing-App-token fixtures that prove user-facing operations fail closed without built-in token fallback. |
157165
| Token boundary | Fixtures that prove App tokens are step-scoped and built-in-token operations remain within the caller job's boundary. |
166+
| Event authorization | Unsupported-fork fixtures that prove Plan rejects before credentialed or repository-defined code, including for downstream `always()` jobs. |
158167

159168
## Decisions requiring approval
160169

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,26 @@ Scenario: Perform a user-facing repository operation
229229
And it uses the App token only for the steps that require that authority
230230
```
231231

232+
### FR12 — Plan MUST authorize events before downstream execution {#fr12}
233+
234+
The caller MUST invoke the reusable workflow without a caller-level fork or event condition. Plan MUST reject unsupported fork pull-request execution before any credentialed or repository-defined code runs. Every downstream job, including a job with `always()`, MUST require a successful authorized Plan and valid Settings. No downstream job MAY evaluate missing or invalid Settings or bypass the Plan gate.
235+
236+
#### Behavioral scenarios {#fr12-scenarios}
237+
238+
```gherkin
239+
Scenario: Reject an unsupported fork pull request
240+
Given a pull request originates from an unsupported fork
241+
When Plan evaluates the event
242+
Then Plan rejects the event before credentialed or repository-defined code runs
243+
And no downstream job receives authorized Settings
244+
245+
Scenario: Gate an always-running downstream job
246+
Given Plan rejects an event or produces invalid Settings
247+
When a downstream job with an always condition is evaluated
248+
Then the job does not run
249+
And it does not evaluate the missing or invalid Settings
250+
```
251+
232252
## Non-functional requirements
233253

234254
### NFR1 — Lifecycle mutations MUST be idempotent {#nfr1}
@@ -365,7 +385,7 @@ Scenario: Recover release notes after a missed main-push publication
365385
And a retry creates no duplicate publication
366386
```
367387

368-
### 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)
388+
### AC2 — Verifies: [FR2](#fr2), [FR4](#fr4), [FR5](#fr5), [FR6](#fr6), [FR8](#fr8), [FR9](#fr9), [FR10](#fr10), [FR11](#fr11), [FR12](#fr12), [NFR2](#nfr2), [NFR3](#nfr3), [NFR4](#nfr4), [NFR5](#nfr5), [NFR6](#nfr6), [NFR7](#nfr7)
369389

370390
```gherkin
371391
Scenario: Lifecycle runs preserve release ownership after cancellation

0 commit comments

Comments
 (0)