midnightntwrk / midnightntwrk/midnight-node

CI cannot pass on fork pull requests — every meaningful job dies at GHCR login or private-repo fetch

Open
#1,998 3 comments 0 reactions 1 assignee View on GitHub

@gilescope is already working on this.

Since Aug 10, 2026.

  • #612 by @gilescope — open
ci triaged
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
  1. Fork midnightntwrk/midnight-node.
  2. Open a PR against main from the fork.
  3. Approve the workflow run.
  4. 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 ... }} in continuous-integration.yml:38,532,1097 and continuous-integration-test.yml:22 keep 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_target anywhere in .github/ — the security posture is sound. Do not "fix" this by widening secret access to fork PRs.
  • The /bot commands (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:

  1. Make the public path credential-free. Use the fork PR's own read-only GITHUB_TOKEN for GHCR (it can read public packages) and publish season releases somewhere fetchable without a PAT. Highest effort, but the only option that gives forks real CI.
  2. 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.
  3. Document the maintainer workflow. Accept the gap, but make it explicit in CONTRIBUTING and 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.