mirror of
https://github.com/hanzoai/insights.git
synced 2026-11-03 00:52:32 +00:00
Both are Django apps, and both pin `label` in apps.py — "stamp" and "review". django_migrations keys a row on (app_label, name), and app_label comes from that pin and not from the import path, so moving the directory does not orphan a single migration. All six stay where they are, untouched. products/stamphog -> products/stamp name = products.stamp.backend products/review_hog -> products/review name = products.review.backend StamphogScene/ -> StampScene/, and stamphogSceneLogic.ts with it 189 files follow. An earlier attempt at this move failed twice for reasons worth recording: `git mv A B` where B exists puts A INSIDE B, and a stray products/stamp/node_modules from an abandoned run was enough to make B exist — the app ended up nested two deep. And `git ls-files` does not list a file moved with plain mv and not yet staged, so a pass over "every tracked file" silently skips it; that is how StampScene.tsx kept importing ./stampSceneLogic after the file beside it had been renamed back. bin/check-imports-are-committed: all first-party imports across 15,843 files resolve. Co-authored-by: Hanzo Dev <[email protected]>
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""Shared retraction of stale stamp approvals when a PR's head moves.
|
|
|
|
GitHub never auto-dismisses an APPROVE review when new commits land, so a prior run's approval at an
|
|
old head keeps satisfying required reviews after a push. Two paths must retract it, and they share this
|
|
one implementation:
|
|
* the review workflow's dismiss_stale_approvals activity (runs before re-reviewing), and
|
|
* the Celery task's LABEL-mode skip path — a head-changing event whose trigger label is absent never
|
|
enters the workflow, so the standing approval would otherwise survive an unreviewed push.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from django.db import router
|
|
from django.utils import timezone
|
|
|
|
from ..models import PullRequest, ReviewRun, StampRepoConfig
|
|
from .github_client import StampGitHubClient
|
|
|
|
DEFAULT_DISMISS_MESSAGE = (
|
|
"New commits were pushed — dismissing the stamp approval from an earlier head; a re-review runs automatically."
|
|
)
|
|
|
|
|
|
def dismiss_stale_approvals_for_head(
|
|
team_id: int,
|
|
pull_request: PullRequest,
|
|
repo_config: StampRepoConfig,
|
|
current_head_sha: str,
|
|
message: str = DEFAULT_DISMISS_MESSAGE,
|
|
) -> int:
|
|
"""Retract every un-dismissed stamp approval posted at a head other than ``current_head_sha``.
|
|
|
|
Keyed off ``posted_review_id`` alone — only the approve path ever sets it, and filtering on
|
|
``verdict=APPROVED`` too would miss a run that posted its approval but crashed (or lost a
|
|
supersession race) before the verdict was saved, leaving that orphan standing forever.
|
|
|
|
Returns the number dismissed. Idempotent via ``approval_dismissed_at``: a run already stamped is
|
|
excluded, so a retry (or the activity re-running after the task skip already handled it) won't
|
|
re-dismiss. Each dismissal is stamped right after its GitHub call so a mid-loop failure resumes
|
|
cleanly on retry.
|
|
"""
|
|
# Writer pin: a head-changing delivery can run this sweep moments after post_verdict committed
|
|
# posted_review_id — a lagged reader would see no approval and skip the retraction entirely.
|
|
stale_runs = list(
|
|
ReviewRun.objects.for_team(team_id)
|
|
.using(router.db_for_write(ReviewRun))
|
|
.filter(
|
|
pull_request=pull_request,
|
|
posted_review_id__isnull=False,
|
|
approval_dismissed_at__isnull=True,
|
|
)
|
|
.exclude(head_sha=current_head_sha)
|
|
)
|
|
if not stale_runs:
|
|
return 0
|
|
|
|
client = StampGitHubClient(repo_config.installation_id)
|
|
dismissed = 0
|
|
for stale in stale_runs:
|
|
if stale.posted_review_id is None:
|
|
continue # excluded by the query filter; narrows the Optional for the type checker
|
|
client.dismiss_pr_review(repo_config.repository, pull_request.pr_number, stale.posted_review_id, message)
|
|
stale.approval_dismissed_at = timezone.now()
|
|
stale.save(update_fields=["approval_dismissed_at", "updated_at"])
|
|
dismissed += 1
|
|
return dismissed
|