Skip to content

Commit d28d0b4

Browse files
zkoppertCopilotCopilot
authored
fix(triage-dependabot): five bugs - SimpleCov, archived repos, branch-protection loop, stale-removal guard, prerelease close (#37)
* fix(triage-dependabot): detect SimpleCov coverage thresholds on Ruby repos detect_repo_coverage() only inspected Python config files (pyproject.toml, setup.cfg, Makefile, tox.ini, .coveragerc) so Ruby repos using SimpleCov returned None. github/markup pins line and branch coverage at 100 in test/test_helper.rb but every patch-bump Dependabot PR there got flagged for review instead of auto-merging. Adds a SimpleCov regex pass over test/test_helper.rb, spec/spec_helper.rb, .simplecov, and Rakefile. Handles three forms: SimpleCov.minimum_coverage 100 SimpleCov.minimum_coverage 80.5 SimpleCov.minimum_coverage line: 100, branch: 100 When line: and branch: are both present, takes the lower value because the lowest gate is the one that fails the build first. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com> * fix(triage-dependabot): skip archived repos before flagging to Q1 There was no archived-repo check in the main loop. PRs against archived repos can never merge, but the tool was happily flagging them to Q1 every hour. zkoppert/advanced-security-enforcer (archived) PR #73 has been re-flagged repeatedly. Adds is_archived_repo() with a process-lifetime cache, called right after the dependabot-author check. When archived: log + mark thread done + clean stale entries + record cooldown + continue. The archived PR never enters the decision tree, so it never becomes a flag-for-review candidate. API failures fall back to False so a flaky GitHub does not suppress real Dependabot work. New skipped_archived counter shows up in the end-of-run summary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com> * fix(triage-dependabot): idempotent approve + branch-protection backoff jmeridth/gh-health-files#59 logged 52 redundant approve events in one day. The pathology: 1. Tool decides outcome=merge. 2. Auto-merge fails (repo has no repo-level auto-merge). 3. Fallback path approves the PR and runs a sync merge. 4. Sync merge fails with 'the base branch policy prohibits the merge' because the repo requires a CODEOWNERS review. 5. Outer except records an error but does NOT extend the cooldown. 6. Next hour: cron picks the same PR up, re-approves, re-fails. Three changes, all required to break the loop: a. has_existing_approval() consults gh pr view headRefOid + reviews + latestReviews and returns True when my login already has APPROVED at the current head SHA. do_merge() now skips the approve fallback in that case. b. New _BRANCH_PROTECTION_MARKERS tuple (matched case-insensitively) captures the rejection strings: base-branch-policy, required status check, changes requested, review-required, at-least-1-approving- review. Kept separate from _AUTO_MERGE_DISABLED_MARKERS so the existing auto-merge -> approve fallback semantics are unchanged. c. BranchProtectionBlocked is raised from do_merge when either the auto-merge attempt or the sync merge attempt trips a branch- protection marker. The run loop converts it into a flag-for-review with a new 24h cooldown (BRANCH_PROTECTION_COOLDOWN_SECONDS), marks the notification done, and never retries the same PR until the cooldown lapses. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com> * fix(triage-dependabot): defensive guard + diagnostic logging in stale-removal A 2026-06-11 triage session reported the cron deleting a hand-curated Q1 entry (core-ux-2746-author-me-copilot-coauthored-prs) that had no notification field. Investigation of that cron log showed both stale_removed=2 events were legitimate notification matches (jmeridth/gh-health-files#58 and #59 - both PRs closed that hour with matching thread_ids), and remove_stale_entries() already returns False for items whose notification field is missing or not a dict. So the dependabot cron is not the proximate cause - but the symptom is severe enough that defense in depth plus diagnostic instrumentation is worth shipping anyway. Changes: * remove_stale_entries() docs the invariant: never removes an item without a dict-typed notification field, never removes anything when both thread_id and pr_url are empty. * Every removal now logs at INFO with the matched key (thread_id=... or url=...), the bucket name (inbox / prioritized.q1_do_first / ...), the item id, and the item title. Future stomps are now diagnosable from the log file alone without reconstructing the input todo.yml. * Regression test covers four entry shapes in the same bucket: no notification field, notification: null, notification: {}, and one legitimately-matched dependabot entry. Asserts only the matched entry is removed and the diagnostic log line fires. * Round-trip test verifies ruamel preserves an entry containing IssueQuery#maybe_expand_author_for_agents (the Ruby method syntax in the original lost item) plus backtick-quoted code, so YAML quoting is ruled out as a future loss vector. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com> * fix(triage-dependabot): read commit.oid from gh review json shape has_existing_approval was reading review.get("commit_id") / review.get("commitId"), but `gh pr view --json reviews,latestReviews` emits the commit as a nested object: {"commit": {"oid": "<sha>"}}. The flat keys are always absent in real gh output, so the head-SHA guard always returned False and the function always returned False - the consistent-approve skip never fired in production. The bug-3 recovery still worked because MergeBlockedByBranchProtectionError + 24h cooldown breaks the retry loop, but the first line of defense was silently dead. I now read commit.oid first, then fall back to commit.sha and the flat commit_id/commitId fields so older or alternate gh shapes still work. Updated the three existing tests to use the production-shape nested commit object, added a new test that asserts the flat commit_id fallback still works, and added a latestReviews coverage test (the same nested shape applies there). Verified against live gh output on PR #37: gh pr view 37 --json reviews | jq '.reviews[0].commit' -> {"oid": "d5eaf3a585..."} Tests: 204 passed (was 202). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Zack Koppert <zkoppert@github.com> --------- Signed-off-by: Zack Koppert <zkoppert@github.com> Signed-off-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
1 parent 07ccbbc commit d28d0b4

3 files changed

Lines changed: 841 additions & 517 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""Pytest configuration for triage-dependabot tests."""
2+
3+
4+
def pytest_configure(config):
5+
config.addinivalue_line(
6+
"markers",
7+
"no_archive_stub: opt out of the autouse is_archived_repo stub.",
8+
)

0 commit comments

Comments
 (0)