Skip to content

Commit efa135e

Browse files
albertotbclaude
andauthored
fix: skip no-op security PRs and drop phantom rows (#13)
* fix: skip no-op security PRs and drop phantom rows When an advisory flags a package that is already at its fix version, uv lock only moves the per-package exclude-newer timestamp without changing any resolved version. The bot previously committed that metadata churn and opened a daily no-op PR. - upgrade_packages: restore the lockfile when no package version actually moves, so an all-no-op run leaves no diff and opens no PR. - build_pr_body: drop rows for packages already at/above the fix that did not change and caused no collateral, so PRs with genuine upgrades no longer list phantom 'X -> X' entries. Packages still below their fix remain in 'Not upgraded'. Fixes #12 * chore: ignore Python bytecode artifacts --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8f37788 commit efa135e

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
*.pyc

security-updates/update.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ def upgrade_packages(packages: list[VulnerablePackage], lockfile: Path) -> dict[
272272

273273
if upgrade_ok:
274274
versions_after = read_all_versions(lockfile.read_bytes())
275+
if versions_before == versions_after:
276+
# No package version actually moved — the only change is
277+
# metadata (e.g. the per-package exclude-newer timestamp).
278+
# This happens when the package is already at the fix version
279+
# but the scanner still flags it. Discard the metadata-only
280+
# churn so an all-no-op run leaves no diff and opens no PR.
281+
print(
282+
f" No version change for {pkg.name} — only metadata moved, "
283+
f"restoring lockfile"
284+
)
285+
lockfile.write_bytes(snapshot)
286+
results[pkg.name] = UpgradeResult(before=before, after=after)
287+
continue
275288
collateral = _diff_versions(versions_before, versions_after, target_names)
276289
results[pkg.name] = UpgradeResult(
277290
before=before, after=after, collateral=collateral,
@@ -352,6 +365,23 @@ def build_pr_body(
352365
needs = pkg.fixed or "unknown"
353366
was_upgraded = after != "unknown" and before != after
354367

368+
# Drop phantom rows: the package is already at (or above) the fix
369+
# version, nothing actually changed, and no other package moved as a
370+
# result. These come from stale/false-positive advisories that flag a
371+
# version that is in fact already patched (e.g. urllib3 2.7.0 → 2.7.0).
372+
# Listing them confuses reviewers, so omit them from the PR body
373+
# entirely (they don't belong in "Not upgraded" — they're not stuck).
374+
already_satisfied = (
375+
pkg.fixed is not None
376+
and after != "unknown"
377+
and not was_upgraded
378+
and not r.collateral
379+
and parse_version(after) >= parse_version(pkg.fixed)
380+
)
381+
if already_satisfied:
382+
print(f" ⏭️ {pkg.name} ({label}) already at {after} (needs {needs}) — skipping")
383+
continue
384+
355385
# Fixed if: version meets the known fix, OR fix is unknown but we upgraded to latest
356386
is_fixed = after != "unknown" and (
357387
(pkg.fixed is not None and parse_version(after) >= parse_version(pkg.fixed))
@@ -403,7 +433,10 @@ def create_or_update_pr(pr_body: str, branch_name: str, pr_title: str) -> None:
403433
"""Commit changes to uv.lock and open or update a PR."""
404434
diff = git("diff", "uv.lock")
405435
if not diff.strip():
406-
print("No lockfile changes — all packages may be constrained.")
436+
print(
437+
"No real package changes (only stale advisories or constrained "
438+
"packages) — skipping PR."
439+
)
407440
return
408441

409442
git("config", "user.name", "github-actions[bot]")

0 commit comments

Comments
 (0)