Skip to content

ci: Dependabot auto-merge merge job can never merge, and its CI gate reads a surface with no CI in it #1476

Description

@groupthinking

Summary

The merge job in .github/workflows/dependabot-auto-merge.yml has two independent defects. The first makes it a no-op; the second means that if the first were fixed naively, it would merge Dependabot PRs whose CI had not passed.

MERGE_POLICY.md adoption step 6 is "Enable Class A auto-merge", and dependency patch/minor is Class A. This job is the mechanism that step depends on, so both defects are load-bearing for that plan.

Defect 1 — update type is read from a field that does not exist

const metadata = await github.request('GET /repos/{owner}/{repo}/pulls/{pull_number}', {
  ..., mediaType: { previews: ['dorian'] },
});
const updateType = metadata.data?.dependency?.update_type ?? ...;
if (!updateType) { core.info('Could not determine update type...'); continue; }

GET /repos/{owner}/{repo}/pulls/{pull_number} returns no dependency, update_type, or dependency_update_type field, and no media-type preview adds one (REST docs). updateType is therefore always undefined, the guard always fires, and every pull request is skipped before any merge is attempted.

The approve job in the same file gets this right — it uses dependabot/fetch-metadata@v3, which exists precisely because there is no such API field.

Defect 2 — the readiness gate reads commit statuses, which contain no CI

const { data: combined } = await github.rest.repos.getCombinedStatusForRef({...});
if (combined.state !== 'success') { continue; }

Every check named in MERGE_POLICY.md gate 2 — validate, guards, lint-python, lint-frontend, build, test, CodeQL, gitleaks (working tree), dependency-review, PR Governance, Canonical issue and evidence — is a check run. getCombinedStatusForRef returns only commit statuses, which on this repo are just Vercel's two and CodeRabbit's.

Observed live on #1433 today: combined status was success at 20:48 UTC (two Vercel statuses) while build, test, lint-python, lint-frontend and guards were all still queued. A job gated this way would merge with CI unfinished.

Why this was not caught

tests/unit/test_dependabot_automation_workflow.py asserts the merge script contains the substrings "dependabot[bot]", "pulls.merge" and "semver-major". All three are present in the broken script, so the tests pass on a job that cannot merge anything. The assertions pin vocabulary, not behaviour.

Note on current state

vars.DEPENDABOT_AUTO_MERGE_ENABLED is not set to 'true' — on #1433, a Dependabot PR meeting every other condition in the approve job's if, the job was skipped. So neither job has ever executed and both defects are latent rather than active. Flipping that variable without fixing the job would produce either silence (defect 1) or unsafe merges (defect 2). Setting the variable is a repository-settings decision and is deliberately out of scope here.

Acceptance criteria

  • Update type is derived from a first-party source that actually carries it, and grouped updates that cannot be classified are skipped rather than guessed.
  • Major updates are still blocked, including when Dependabot omits update-type (it does for some indirect bumps, e.g. build(deps-dev): bump js-yaml from 4.3.0 to 4.3.1 #1433).
  • Merge readiness gates on check runs; unfinished checks block, and skipped/neutral count as satisfied per gate 2's conditional list.
  • The workflow's own approve/merge check runs are excluded from the readiness scan, so the gate cannot deadlock against itself.
  • checks: read permission is granted, without which the readiness call 403s.
  • Tests fail against the current workflow and pass against the fixed one.

Activity

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions