Skip to content

ci: run firmwareci for fork pull requests - #372

Open
RiSKeD wants to merge 1 commit into
mainfrom
feat/firmwareci-fork-handoff
Open

ci: run firmwareci for fork pull requests#372
RiSKeD wants to merge 1 commit into
mainfrom
feat/firmwareci-fork-handoff

Conversation

@RiSKeD

@RiSKeD RiSKeD commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Problem

Pull requests from forks run without repository secrets, so secrets.FWCI_TOKEN is empty and the Upload to FirmwareCI step in fwci-test fails. #334 (9elements/dutctl-9eBlindspotSoftware/dutctl) is the current example. Everything else in that workflow — the goreleaser build, get-matrix, validate-config — needs no secrets and already passes on fork PRs.

There is no setting that fixes this. GitHub's "send secrets to fork pull request workflows" toggle is private-repository-only; the API rejects it here with Fork PR workflow settings is not allowed for public repositories.

Change

Split the submission out of the run that has no secrets.

  • fwci-test — the FirmwareCI job is now skipped on fork pull requests. Same-repo pull requests, pushes and merge groups are unaffected and still submit inline.
  • fwci-fork (new) — triggers on fwci-test completing, downloads the binaries artifact that run already produced, and submits it from this repository, where the secrets live.

Fork-authored code never runs in the privileged job. It checks out this repository, not the contributor's, and does nothing but hand the build artifact to FirmwareCI. That closes the credential exposure; it does not close the hardware question, which is what the environment gate is for.

Required before this does anything

Create an environment named firmwareci-fork (Settings → Environments) with required reviewers. A fork pull request then parks at Waiting for approval until a maintainer approves, and approving means letting a contributor's build run on a real DUT.

The environment is the whole gate. Without required reviewers configured, this submits every fork pull request to hardware automatically.

Hardening

The artifact is the output of a run of the contributor's code, so the job treats it as untrusted input:

  • the filename lookup rejects anything but a single plainly named path before it reaches GITHUB_OUTPUT — a filename containing a newline would otherwise let the artifact append arbitrary step outputs;
  • event payload values reach the Debug script through env: rather than being interpolated into it.

Details worth knowing

  • BRANCH: main is deliberate. FirmwareCI resolves test-dutctl per branch, and the fork's branch does not exist in this repository, so resolving against it would miss. COMMIT_HASH still points at the fork's head commit, so the job reports against the commit actually under test.
  • workflow_run.pull_requests is empty for cross-repository runs, which is why the job keys off head_sha and head_repository instead of the PR number.
  • CONFIG comes from this repository's tree, not the fork's. A fork PR that edits contrib/dutagent-cfg-example.yaml would be tested against the base version. Not the case for Gude PDU support #334. If that matters later, add the config as a separate artifact — appending it to binaries would shift the tree and break the existing ls globs in fwci-test.
  • The action stays pinned at v6.0. It is never reached on a fork pull request now, so this does not depend on BlindspotSoftware/actions#24 landing. Bumping to v6.2 afterwards adds a backstop: the action itself then reports a skip instead of failing if a fork run ever reaches it.

Testing

actionlint is clean on both files. The path guard was exercised locally: a normal goreleaser path is accepted, a two-match/newline result exits 1.

The first real fork pull request is what verifies the rest — specifically that the FirmwareCI check run posts back onto the pull request. The GitHub App is installed on this repository and the fork's head commit exists here via refs/pull/N/head, so it should; worth watching on the first run.

Pull requests from forks run without repository secrets, so FWCI_TOKEN
is empty and the FirmwareCI step in fwci-test cannot submit a job. Skip
that job for fork pull requests and add fwci-fork, which triggers on the
completion of fwci-test, downloads the binaries it already built, and
submits them from the base repository where the secrets live.

Fork-authored code never runs in the new workflow. It checks out this
repository, not the contributor's, and only hands the build artifact to
FirmwareCI. What still needs a human is the hardware itself, so the job
sits behind the firmwareci-fork environment: approving it means letting
a contributor's build run on a real DUT.

The artifact comes from a run of the contributor's code, so the filename
lookup rejects anything but a single plainly named path before it
reaches GITHUB_OUTPUT, and event payload values are passed through the
environment instead of being interpolated into the script.

BRANCH is pinned to main because the fork's branch does not exist here
and the FirmwareCI config has to resolve against a branch of this
repository. COMMIT_HASH comes from the workflow_run payload, which
carries the pull request head commit rather than the base ref this
workflow runs on.

The firmwareci-fork environment must exist with required reviewers
before this has any effect.

Signed-off-by: Fabian Wienand <fabian.wienand@blindspot.software>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant