midnightntwrk / midnightntwrk/midnight-node
CI cannot pass on fork pull requests — every meaningful job dies at GHCR login or private-repo fetch
- Dominant language
- Rust
- Stars
- 68
- Forks
- 45
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 64
Description
Context & versions
midnight-node is a public repository, but its CI cannot pass on a pull request opened from a fork. Every job that does meaningful work is gated behind a credential that GitHub deliberately withholds from fork PR runs, so the jobs start, fail within ~20s, and take the whole downstream graph with them.
This is not a misconfiguration of a single workflow — it is a structural gap. For public repos GitHub never passes repo/org/environment secrets to pull_request runs from forks, and GITHUB_TOKEN is forced read-only. Any CI design that requires a PAT or a private-repo fetch is therefore unrunnable for external contributors.
Observed on PR #1974 (wbaxterh:wes-1956-indexer-network-id), a two-file change to local-environment/.
Steps to reproduce
- Fork
midnightntwrk/midnight-node. - Open a PR against
mainfrom the fork. - Approve the workflow run.
- Observe that 6 jobs fail and ~16 more are skipped.
Expected behavior
An external contributor gets actionable CI feedback on their PR — at minimum formatting/lint/test signal, and ideally the job that actually exercises their change. Jobs that genuinely cannot run without credentials should report as skipped/neutral rather than failed.
Actual behavior
Two distinct root causes.
1. GHCR login fails — ##[error]Password required
docker/login-action is passed password: ${{ secrets.MIDNIGHTCI_PACKAGES_READ }} (e.g. .github/workflows/continuous-integration-checks.yml:44 and :89, and the equivalents in continuous-integration.yml). On a fork PR that expands to an empty string and the action errors immediately.
Failing jobs:
- Feature Unification Check
- Fomatting and Linting
- Build node and images (arm64)
- Build node and images (linux/amd64, self-hosted-tier:large, amd64)
- build-indexer-images
In every case Login to GHCR is one of the first steps and all subsequent steps report skipped — nothing is ever built, linted, or tested.
2. check-changes fails — release not found
.github/actions/install-season runs:
gh release download "$SEASON_TAG" -R "$SEASON_REPO" -p "season-x86_64-unknown-linux-musl.tar.gz"
with GH_TOKEN: empty (visible verbatim in the job log). midnightntwrk/season is a private repository, so the unauthenticated lookup returns release not found rather than a permission error.
Cascade
Because the build jobs failed, everything with a needs: on them never runs — roughly 16 checks report skipping, including Local Environment Tests, Toolkit E2E, Metadata Check, Chainspec Validation, Test Toolkit, and the SBOM/Scan jobs.
Net effect on #1974: the only job that would have exercised the contributor's docker-compose change (Local Environment Tests) never ran. CI produced a wall of red that says nothing about the PR's correctness.
Notes
- The fork-runner guards already exist and work correctly —
runs-on: ${{ ... head.repo.full_name != github.repository ... }}incontinuous-integration.yml:38,532,1097andcontinuous-integration-test.yml:22keep untrusted code off the self-hosted pool. The intent to support forks is there; the credential side was never adapted, so the guards currently just produce jobs that start and immediately die. - No secrets are actually leaked to forks today, and there is no
pull_request_targetanywhere in.github/— the security posture is sound. Do not "fix" this by widening secret access to fork PRs. - The
/botcommands (rebuild-metadata,rebuild-chainspec,cargo-fmt) also hard-refuse fork PRs by design, so a maintainer cannot use them to unblock a fork contributor either.
Possible directions
Not prescriptive — whoever picks this up should choose:
- Make the public path credential-free. Use the fork PR's own read-only
GITHUB_TOKENfor GHCR (it can read public packages) and publishseasonreleases somewhere fetchable without a PAT. Highest effort, but the only option that gives forks real CI. - Degrade gracefully. Detect fork PRs and skip the credential-dependent jobs so they report neutral instead of failing, leaving whatever can run (actionlint, frozen-paths, DCO, scanners — these already pass) as the fork signal.
- Document the maintainer workflow. Accept the gap, but make it explicit in
CONTRIBUTINGand have a maintainer re-run fork PRs from an in-repo branch before merge, so contributors aren't left staring at unexplained red checks.
Option 2 is the cheap stopgap; option 1 is what "we support forks" actually means.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.