async def push(push_event: PushEvent) -> None: """ Trigger evaluation of PRs that depend on the pushed branch. """ owner = push_event.repository.owner.login repo = push_event.repository.name installation_id = str(push_event.installation.id) branch_name = get_branch_name(push_event.ref) log = logger.bind(ref=push_event.ref, branch_name=branch_name) if branch_name is None: log.info("could not extract branch name from ref") return async with Client(owner=owner, repo=repo, installation_id=installation_id) as api_client: # find all the PRs that depend on the branch affected by this push and # queue them for evaluation. # Any PR that has a base ref matching our event ref is dependent. prs = await api_client.get_open_pull_requests(base=branch_name) if prs is None: log.info("api call to find pull requests failed") return None for pr in prs: await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=owner, repo_name=repo, pull_request_number=pr.number, installation_id=installation_id, ))
def test_ignore_external_pull_requests() -> None: """ We should filter out pull requests that don't belong to our repository. """ internal_repository_id = 554453 internal_pull_request = PullRequest( number=123, base=Ref(ref="main", repo=PullRequestRepository(id=internal_repository_id)), ) external_pull_request = PullRequest(number=534, base=Ref( ref="main", repo=PullRequestRepository(id=22))) event = CheckRunEvent( installation=Installation(id=69039045), check_run=CheckRun( name="circleci: build", pull_requests=[external_pull_request, internal_pull_request], ), repository=Repository(id=internal_repository_id, name="cake-api", owner=Owner(login="******")), ) assert list(check_run(event)) == [ WebhookEvent( repo_owner="acme-corp", repo_name="cake-api", pull_request_number=internal_pull_request.number, installation_id=str(event.installation.id), target_name="main", ) ]
async def pr_review(review: events.PullRequestReviewEvent) -> None: assert review.installation await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=review.repository.owner.login, repo_name=review.repository.name, pull_request_number=review.pull_request.number, installation_id=str(review.installation.id), ))
async def pr_event(pr: events.PullRequestEvent) -> None: assert pr.installation is not None await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=pr.repository.owner.login, repo_name=pr.repository.name, pull_request_number=pr.number, installation_id=str(pr.installation.id), ))
async def pr_event(pr: PullRequestEvent) -> None: """ Trigger evaluation of modified PR. """ await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=pr.repository.owner.login, repo_name=pr.repository.name, pull_request_number=pr.number, installation_id=str(pr.installation.id), ))
async def pr_review(review: PullRequestReviewEvent) -> None: """ Trigger evaluation of the modified PR. """ await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=review.repository.owner.login, repo_name=review.repository.name, pull_request_number=review.pull_request.number, installation_id=str(review.installation.id), ))
async def check_run(check_run_event: events.CheckRunEvent) -> None: assert check_run_event.installation # Prevent an infinite loop when we update our check run if check_run_event.check_run.name == queries.CHECK_RUN_NAME: return for pr in check_run_event.check_run.pull_requests: await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=check_run_event.repository.owner.login, repo_name=check_run_event.repository.name, pull_request_number=pr.number, installation_id=str(check_run_event.installation.id), ))
async def refresh_pull_requests_for_installation( *, installation_id: str, redis: RedisConnection) -> None: async with AsyncClient() as http: login = await get_login_for_install(http=http, installation_id=installation_id) token = await get_token_for_install(session=http, installation_id=installation_id) res = await http.post( conf.GITHUB_V4_API_URL, json=dict(query=QUERY, variables=dict(login=login)), headers=dict(Authorization=f"Bearer {token}"), ) res.raise_for_status() organizationdata = res.json()["data"]["organizationdata"] userdata = res.json()["data"]["userdata"] user_kind = None if organizationdata is not None: user_kind = "organization" data = organizationdata elif userdata is not None: user_kind = "user" data = userdata else: raise ValueError("missing data for user/organization") events = [] for repository in data["repositories"]["nodes"]: repo_name = repository["name"] for pull_request in repository["pullRequests"]["nodes"]: events.append( WebhookEvent( repo_owner=login, repo_name=repo_name, target_name=pull_request["baseRef"]["name"], pull_request_number=pull_request["number"], installation_id=installation_id, )) for event in events: await redis.zadd( event.get_webhook_queue_name(), {event.json(): time.time()}, only_if_not_exists=True, ) logger.info( "pull_requests_refreshed", installation_id=installation_id, events_queued=len(events), user_kind=user_kind, )
async def status_event(status_event: StatusEvent) -> None: """ Trigger evaluation of all PRs associated with the status event commit SHA. """ owner = status_event.repository.owner.login repo = status_event.repository.name installation_id = str(status_event.installation.id) log = logger.bind(owner=owner, repo=repo, install=installation_id) refs = find_branch_names_latest(sha=status_event.sha, branches=status_event.branches) async with Client(owner=owner, repo=repo, installation_id=installation_id) as api_client: if len(refs) == 0: # when a pull request is from a fork the status event will not have # any `branches`, so to be able to trigger evaluation of the PR, we # fetch all pull requests. # # I think we could optimize this by selecting only the fork PRs, but # I worry that we might miss some events where `branches` is empty, # but not because of a fork. pr_results = [await api_client.get_open_pull_requests()] log.warning("could not find refs for status_event") else: pr_requests = [ api_client.get_open_pull_requests(head=f"{owner}:{ref}") for ref in refs ] pr_results = cast( List[Optional[List[GetOpenPullRequestsResponse]]], await asyncio.gather(*pr_requests), ) all_events: Set[WebhookEvent] = set() for prs in pr_results: if prs is None: continue for pr in prs: all_events.add( WebhookEvent( repo_owner=owner, repo_name=repo, pull_request_number=pr.number, installation_id=str(installation_id), )) for event in all_events: await redis_webhook_queue.enqueue(event=event)
async def check_run(check_run_event: CheckRunEvent) -> None: """ Trigger evaluation of all PRs included in check run. """ # Prevent an infinite loop when we update our check run if check_run_event.check_run.name == queries.CHECK_RUN_NAME: return for pr in check_run_event.check_run.pull_requests: await redis_webhook_queue.enqueue( event=WebhookEvent( repo_owner=check_run_event.repository.owner.login, repo_name=check_run_event.repository.name, pull_request_number=pr.number, target_name=pr.base.ref, installation_id=str(check_run_event.installation.id), ) )
def check_run(check_run_event: CheckRunEvent) -> Iterator[WebhookEvent]: """ Trigger evaluation of all PRs included in check run. """ # Prevent an infinite loop when we update our check run if check_run_event.check_run.name == queries.CHECK_RUN_NAME: return for pr in check_run_event.check_run.pull_requests: # filter out pull requests for other repositories if pr.base.repo.id != check_run_event.repository.id: continue yield WebhookEvent( repo_owner=check_run_event.repository.owner.login, repo_name=check_run_event.repository.name, pull_request_number=pr.number, target_name=pr.base.ref, installation_id=str(check_run_event.installation.id), )
async def status_event(status_event: events.StatusEvent) -> None: assert status_event.installation sha = status_event.commit.sha owner = status_event.repository.owner.login repo = status_event.repository.name installation_id = str(status_event.installation.id) async with Client(owner=owner, repo=repo, installation_id=installation_id) as api_client: prs = await api_client.get_pull_requests_for_sha(sha=sha) if prs is None: logger.warning("problem finding prs for sha") return None for pr in prs: await redis_webhook_queue.enqueue(event=WebhookEvent( repo_owner=owner, repo_name=repo, pull_request_number=pr.number, installation_id=str(installation_id), ))