Skip to content

Docs-only PRs are unmergeable: required checks skipped at trigger never report #333

@lllamnyp

Description

Summary

Docs-only PRs (and any PR that doesn't touch code/charts/workflows) are unmergeable because three required status checks never report.

Root cause

Branch protection on main requires six contexts:

DCO  verify  image-multiarch  kamaji-datastore  smoke (helm)  smoke (manifest)

Three of them are produced by workflows that are filtered out at the trigger level:

  • kamaji-datastore.github/workflows/e2e.yml has pull_request: paths-ignore: ['**.md', 'docs/**'], so the workflow never starts on a docs-only change.
  • smoke (helm) / smoke (manifest).github/workflows/release-smoke.yml has a pull_request: paths: allowlist (workflows, charts/**, api/**, Makefile, Dockerfile, hack/release-smoke.sh), so it never starts otherwise.

When a workflow is skipped at the on: trigger via a path filter, GitHub never creates the check context. Branch protection then holds the required context in the Expected state indefinitely, and the PR stays BLOCKED. enforce_admins is enabled, so there is no admin-merge bypass either.

This is the canonical GitHub "skipped but required" deadlock. See: https://docs.github.com/actions/how-tos/manage-workflow-runs/skip-workflow-runs and GitHub's guidance on handling skipped-but-required checks.

Why the path filters exist

The filters are deliberate and worth keeping: the kamaji-datastore e2e run provisions kind + cert-manager + Kamaji and costs ~30–45 minutes; the smoke matrix spins up kind twice. We don't want to pay that on a typo fix to a .md file. The bug is where the filtering happens, not that it happens.

Fix

Move the path filtering off the on: trigger and into the jobs:

  1. Drop paths-ignore / paths from the pull_request: triggers in e2e.yml and release-smoke.yml.
  2. Add a cheap changes job (e.g. dorny/paths-filter) that detects whether relevant paths changed — runs in seconds, no Go/Docker setup.
  3. Guard the expensive jobs with if: needs.changes.outputs.<flag> == 'true'.

The key distinction: a job skipped via a job-level if: still reports its check context with conclusion skipped, and branch protection treats a skipped required check as passing. So on a docs-only PR the workflows start, the cheap detector runs, the expensive jobs are skipped (zero compute cost), and all three contexts report skipped → pass. The deadlock is gone and the cost-saving intent is preserved.

Caveat to verify

Confirm that the smoke matrix job, when skipped via a job-level if:, still emits the per-leg contexts smoke (helm) and smoke (manifest) as skipped (rather than a single collapsed smoke). If it collapses, split the matrix into two named jobs or add a sentinel job.

Immediate unblock for the PR that surfaced this

#331 was unblocked by manually dispatching both workflows (workflow_dispatch) against the PR's head ref, which produces the three contexts on the head SHA. That runs the real suites and must be repeated per docs PR — the fix above removes the need for that ritual.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions