Example #1
0
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,
            ))
Example #2
0
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",
        )
    ]
Example #3
0
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),
    ))
Example #4
0
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),
    ))
Example #5
0
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),
    ))
Example #6
0
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),
    ))
Example #7
0
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),
        ))
Example #8
0
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,
    )
Example #9
0
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)
Example #10
0
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),
            )
        )
Example #11
0
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),
        )
Example #12
0
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),
            ))