Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0a321c7
ci: dispatch external CI to the external repositories (ext-ci orchest…
thomas-bc Oct 20, 2026
b1f8570
ci: lazy DISPATCH_TOKEN check so the fork preflight runs without the …
thomas-bc Oct 20, 2026
01b8673
ci: ext-ci nightly schedule; cancel-in-progress only for PR fan-outs
thomas-bc Oct 20, 2026
76bb733
ci: create the per-repo Check Run before touching the external repo s…
thomas-bc Oct 20, 2026
ab930cf
ci: ext-ci manifest lists led-blinker only for the first dispatch pilot
thomas-bc Nov 4, 2026
a18ed2c
ci: ext-ci orchestrator in Python (stdlib only) instead of Node
thomas-bc Nov 4, 2026
77c7917
ci: ext-ci review fixes (carry-forward KeyError, manual fprime_ref ma…
thomas-bc Nov 4, 2026
6052882
ci: simplify the ext-ci orchestrator for the pilot
thomas-bc Nov 5, 2026
4f6b54c
ci: docstrings and type hints for the ext-ci orchestrator
thomas-bc Nov 5, 2026
19cbe94
ci: ext-ci review follow-ups: step-scoped dispatch token, on_pr on ma…
thomas-bc Nov 5, 2026
646ce8f
ci: ext-ci runs for PRs, merge queue and manual dispatch only; orches…
thomas-bc Nov 5, 2026
219317e
ci: ext-ci re-runs dispatch only the repos whose check is not green
thomas-bc Nov 5, 2026
d2045d3
ci: ext-ci header documents the manifest and external ci.yml contract…
thomas-bc Nov 5, 2026
e447fd1
ci: black-format the ext-ci orchestrator
thomas-bc Nov 5, 2026
ca346a3
ci: ext-ci orchestrator formatting compatible with black 21.6b0 (CI pin)
thomas-bc Nov 5, 2026
c5982f7
ci: ext-ci files under .github/actions/ext-ci with a README; ubuntu-l…
thomas-bc Nov 5, 2026
a363fa3
ci: ext-ci queue allowance = timeout_minutes (20 min), so queue + run…
thomas-bc Nov 5, 2026
ba78521
ci: ext-ci job display name "Orchestrate"
thomas-bc Nov 5, 2026
a7c7020
ci: ext-ci display names: "External CI / Approval Gate", "External CI…
thomas-bc Nov 5, 2026
cf2f06e
ci: trigger workflow named "External CI Trigger" so workflow_run cann…
thomas-bc Nov 5, 2026
5dbbb94
ci: ext-ci uses GitHub App installation tokens (dispatch + Check Runs…
thomas-bc Nov 5, 2026
c9951b1
ci: ext-ci scopes each App token to the permissions its client uses
thomas-bc Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/actions/ext-ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# External CI

F´ does not build the `fprime-community` projects itself any more: `.github/workflows/ext-ci.yml`
dispatches each project's own `ci.yml` with the F´ revision under test and reports the result as a
Check Run named `External CI / <id>` on the F´ commit. `ext-ci-trigger.yml` (`External CI Trigger / Approval Gate`) is the trivial `pull_request`
workflow whose completion (after GitHub's fork-approval gate) starts the orchestrator; the orchestrator
also runs for merge groups and on manual dispatch (`repos`, `pr_number` or `fprime_ref` inputs).

## manifest.json

| key | meaning |
|---|---|
| `upstream_repository` | only this repository dispatches automatically; forks run only on manual dispatch |
| `check_prefix` | Check Run name is `<check_prefix> / <id>` |
| `paths_ignore` | globs; a PR touching only these gets `skipped` checks and no dispatch |
| `repos[].id` | Check Run suffix and value for the manual `repos` input |
| `repos[].repository` | external repository (`owner/name`) |
| `repos[].workflow` | workflow file in that repository; must accept the inputs below |
| `repos[].default_ref` | branch to dispatch when neither `pr-<N>` nor a branch named like the F´ base branch exists there |
| `repos[].timeout_minutes` | remote running time allowed, and separately the time it may wait in queue; the remote run is cancelled on timeout. Keep `2 * timeout_minutes` under the job's `timeout-minutes` in ext-ci.yml |
| `repos[].failure_policy` | `block`: a failure is a red check and fails the orchestrator job; `notify`: neutral (non-blocking) check |
| `repos[].on_pr` | `false`: never dispatched for a PR head (merge queue and manual `fprime_ref` runs only) |

Orchestrator-side faults (dispatch error, run never started, polling lost the run) fail the job
whatever the policy. A re-run of the orchestrator re-dispatches only the repos whose check is not green.

## Contract for an external `ci.yml`

- `on: workflow_dispatch` with inputs `fprime_repository`, `fprime_ref` (exact SHA to use as the F´
dependency), `fprime_pr_number`, `dispatch_id`.
- `permissions: contents: read` (or `{}`), no `${{ secrets.* }}`, no Actions cache writes on dispatched
runs: the dispatched run executes untrusted F´ PR code in the external repository's own context.
- Repositories with self-hosted or hardware runners: `on_pr: false` in the manifest, ephemeral runners,
SHA-pinned actions, and a job-level guard on `inputs.fprime_repository` in the workflow.

## Credentials

One GitHub App ("External CI", registered under fprime-community; permissions Actions read/write,
Checks read/write, Contents read, Pull requests read; no webhook) installed twice: on the external
repositories listed in the manifest and on this repository. The workflow mints two 1-hour installation
tokens from the repository secrets `EXT_CI_APP_ID` and `EXT_CI_APP_PRIVATE_KEY`, exposed only to the two
orchestrator steps:

- `DISPATCH_TOKEN` (fprime-community installation) starts and cancels the remote runs. Adding an
external repository = adding it to that installation and to the manifest.
- `CHECKS_TOKEN` (this repository's installation) creates the Check Runs, so they appear under their own
"External CI" group in the PR checks list. If the App is not installed here the orchestrator falls
back to `GITHUB_TOKEN` (`checks: write`) and GitHub files the checks under another workflow's group.
22 changes: 22 additions & 0 deletions .github/actions/ext-ci/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"upstream_repository": "nasa/fprime",
"check_prefix": "External CI",
"paths_ignore": [
"docs/**",
"**.md",
".github/actions/spelling/**",
".github/ISSUE_TEMPLATE/**",
".github/agents/**"
],
"repos": [
{
"id": "led-blinker",
"repository": "fprime-community/fprime-workshop-led-blinker",
"workflow": "ci.yml",
"default_ref": "devel",
"timeout_minutes": 20,
"failure_policy": "notify",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Documentation] could fix CONTRIBUTING.md § "Automated Checks on Reference Repositories" does not describe the new ext-ci / <id> check contributors will now see on their PRs.

(Anchored here; the finding is on CONTRIBUTING.md, which this PR does not touch.) That section still only describes the in-repo ext-*.yml builds. The new check runs the external repository's own ci.yml against the PR head, appears only after a maintainer approves the fork workflow run, and with failure_policy: notify a failure shows as a neutral (non-blocking) check. Branch selection (pr-<N>, then the base branch, then devel) is unchanged, so the existing sentence stays true; the section is incomplete rather than wrong, hence not must fix. Proposed addition after the pr-<PR_NUMBER> paragraph:

Some reference repositories are checked through their own CI instead: F´ dispatches the repository's `ci.yml` against your PR head and reports the result as a check named `ext-ci / <repository id>` (see `.github/ext-ci-manifest.json`). For pull requests from forks this check appears once a maintainer has approved the workflow run. Repositories marked `failure_policy: notify` in the manifest report failures as a neutral, non-blocking check.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferred: CONTRIBUTING.md will be updated when ext-build-led-blinker.yml is retired in favour of this check, so contributors read one description, not two.

"on_pr": true
}
]
}
Loading
Loading