Skip to content

Master CI gatekeeper: a green PR does not predict a green master #281

Description

@pftg

Highest priority. Master CI was red for six consecutive commits while every PR showed green, and a release was nearly cut on it.

Why PR-green cannot predict master-green

.github/workflows/test.yml:104 — the full Test Ruby & Rails matrix is deliberately kept off pull requests:

# Cost-intentional: full matrix stays off PRs by default (free-tier
# Actions minutes). Runs on master pushes, manual dispatch, the weekly
# scheduled drift check, and PRs opted in via the 'full-ci' label.
if: >
  github.ref == 'refs/heads/master' ||
  github.event_name == 'workflow_dispatch' ||
  github.event_name == 'schedule' ||
  contains(github.event.pull_request.labels.*.name, 'full-ci')

That trade-off is reasonable. The consequence is not obvious: the job that breaks master is a job that never ran on the PR, so PR checks are green by construction and every merge is a blind push.

What actually broke (tracked separately)

Subprocess probes chdir into a temp directory — correct, and the documented rule — while inheriting the workflow's relative BUNDLE_GEMFILE: gemfiles/rails81_gems.rb (test.yml:30), which then fails to resolve:

/tmp/d20260824-2715-vtghif/gemfiles/rails81_gems.rb not found (Bundler::GemfileNotFound)

Invisible locally, because bundle exec normalises BUNDLE_GEMFILE to an absolute path before spawning. The documented rule is chdir out and scrub RUBYOPT/BUNDLE_GEMFILE — the chdir landed, the scrub did not. Fix in flight on fix/probe-env-under-ci.

The gatekeeper

  • Process rule recorded in the loop protocol: after every merge, check gh run list --branch master --limit 1. A red master blocks the next pull. cancelled is not a pass — it means a later push superseded the run.
  • Live monitor watching master's latest run, reporting every terminal conclusion (success, failure, cancelled) rather than only failures — silence must not look like success.
  • Label rule: any PR touching subprocess probes, env handling, gemfiles, or CI itself gets the full-ci label before merge, so the matrix runs where it can still block. Document this in CONTRIBUTING.md next to the label.
  • Decide whether full-ci should be automatic for paths that have historically broken master — test/**, gemfiles/**, .github/**. A paths-based label bot, or simply making the matrix run on PRs that touch those paths. Costs minutes; buys the guarantee that PR-green means something.
  • Branch protection should require the master-push matrix once its check names settle (they change with 2.1: delete the legacy namespace trees and the driver abstraction #249, 2 dimensions → 1). Until then the rule above is the only gate.

The general lesson, for the record

A CI configuration that runs different jobs on PRs than on master is a configuration where green means two different things depending on where you read it. That is defensible for cost, but it must be stated where people merge, not only in a comment inside the workflow.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions