Skip to content

CI never runs on a PR stacked on a feature branch, and retargeting does not backfill it #62

Description

@fsecada01

Found on PR #60, which sat MERGEABLE / CLEAN with the test matrix having never run against it once.

The problem

.github/workflows/ci.yml triggers on:

on:
  pull_request:
    branches: [master, main]

branches: on a pull_request trigger filters on the PR's base, not its head. So a PR opened against any branch other than master/main never runs CI at all.

That is normally fine, except for two things that combine badly:

  1. Stacked PRs are the repo's own working pattern. docs(primitives): close Tier 3 — layout is out of scope, no grid (#55) #60 targeted feat(primitives): box and prose across five themes (#54) #59's branch because its only deliverable rewrote a section Primitives Tier 2: box/surface and prose/content across all five themes #54 owned. That is the correct way to stack, and it silently opts the PR out of CI.
  2. Retargeting does not backfill the run. When feat(primitives): box and prose across five themes (#54) #59 merged, GitHub auto-retargeted docs(primitives): close Tier 3 — layout is out of scope, no grid (#55) #60's base to master — which fires a pull_request event of type edited. The default types for pull_request are opened, synchronize and reopened; edited is not among them, so no workflow run is queued.

The result is a PR that reports mergeStateStatus: CLEAN and shows a green checklist, because the checks that did run (docs.yml's build, GitGuardian) passed — while the three test jobs and tailwind-build are not pending, not failed, but absent. A green-looking PR whose tests never ran is worse than a red one.

Worked around on #60 by closing and reopening it (reopened is a default type, so that queues the full matrix). That is a manual step nobody will remember next time.

Why it matters here specifically

A docs-only diff is exactly the case where this feels safe and isn't. tests/unit/test_docs_samples.py resolves every Cf: and <c-cf.*> tag in the docs against the real catalogue and fails the build on a component that does not exist — it caught a bad tag during #54 and again during #55. That guard lives in the test matrix that a stacked docs PR skips.

Options

  1. Drop the branches: filter from ci.yml's pull_request trigger. docs.yml already uses a bare pull_request: and is the reason docs(primitives): close Tier 3 — layout is out of scope, no grid (#55) #60 had any checks at all. Simplest fix, and it makes every PR run CI regardless of base. Cost: PRs stacked on a feature branch run the matrix twice (once on the stack, once after retarget) — cheap here, the matrix is ~2 minutes.
  2. Add types: [opened, synchronize, reopened, edited]. Narrower, and directly targets the retarget case, but leaves the initial no-CI window open and edited also fires on title/body edits.
  3. Both.

Option 1 alone is probably right. The branches: filter is not buying anything — there is no branch this repo wants to skip CI on.

Acceptance criteria

  • A PR whose base is a feature branch runs the full test matrix.
  • Proven non-vacuous: open a throwaway PR against a non-master base and confirm gh pr checks lists the test (3.11/3.12/3.13) jobs. The current failure mode is checks being absent, which no assertion about passing checks would catch.
  • If the repo ever adds required status checks in branch protection, confirm the absent-checks case blocks the merge rather than being treated as satisfied.

Metadata

Metadata

Assignees

Labels

wontfixThis will not be worked on

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions