Пример #1
0
    async def get_pull_request_rule(self,
                                    ctxt: context.Context) -> RulesEvaluator:
        runtime_rules = []
        for rule in self.rules:
            if not rule.actions:
                runtime_rules.append(
                    self._gen_rule_from(rule, rule.actions, []))
                continue

            actions_without_special_rules = {}
            for name, action in rule.actions.items():
                conditions = await action.get_conditions_requirements(ctxt)
                if conditions:
                    runtime_rules.append(
                        self._gen_rule_from(rule, {name: action}, conditions))
                else:
                    actions_without_special_rules[name] = action

            if actions_without_special_rules:
                runtime_rules.append(
                    self._gen_rule_from(rule, actions_without_special_rules,
                                        []))

        return await RulesEvaluator.create(
            runtime_rules,
            ctxt.repository,
            [ctxt.pull_request],
            True,
            ctxt.log,
            ctxt.has_been_refreshed_by_timer(),
        )
Пример #2
0
    async def _update_merge_queue_summary(
        self,
        ctxt: context.Context,
        rule: "rules.EvaluatedRule",
        q: merge_train.Train,
        car: typing.Optional[merge_train.TrainCar],
    ) -> None:

        if car and car.creation_state == "updated" and not ctxt.closed:
            # NOTE(sileht): This car doesn't have tmp pull, so we have the
            # MERGE_QUEUE_SUMMARY and train reset here
            queue_rule_evaluated = await self.queue_rule.get_evaluated_queue_rule(
                ctxt.repository,
                ctxt.pull["base"]["ref"],
                [ctxt.pull_request],
                ctxt.log,
                ctxt.has_been_refreshed_by_timer(),
            )
            await delayed_refresh.plan_next_refresh(ctxt,
                                                    [queue_rule_evaluated],
                                                    ctxt.pull_request)

            unexpected_changes: typing.Optional[merge_train.UnexpectedChange]
            status: checks_status.ChecksCombinedStatus
            if await ctxt.has_been_synchronized_by_user(
            ) or await ctxt.is_behind:
                unexpected_changes = merge_train.UnexpectedUpdatedPullRequestChange(
                    ctxt.pull["number"])
                status = check_api.Conclusion.PENDING
                ctxt.log.info("train will be reset",
                              unexpected_changes=unexpected_changes)
                await q.reset(unexpected_changes)
            else:
                unexpected_changes = None
                status = await checks_status.get_rule_checks_status(
                    ctxt.log,
                    ctxt.repository,
                    [ctxt.pull_request],
                    queue_rule_evaluated,
                    unmatched_conditions_return_failure=False,
                )
            await car.update_state(status, queue_rule_evaluated)
            await car.update_summaries(status,
                                       unexpected_change=unexpected_changes)
            await q.save()
Пример #3
0
async def handle(queue_rules: rules.QueueRules, ctxt: context.Context) -> None:
    # FIXME: Maybe create a command to force the retesting to put back the PR in the queue?

    train = await merge_train.Train.from_context(ctxt)

    car = train.get_car_by_tmp_pull(ctxt)
    if not car:
        if ctxt.closed:
            ctxt.log.info(
                "train car temporary pull request has been closed", sources=ctxt.sources
            )
        else:
            ctxt.log.warning(
                "train car not found for an opened merge queue pull request",
                sources=ctxt.sources,
            )

        return

    if car.checks_conclusion != check_api.Conclusion.PENDING and ctxt.closed:
        ctxt.log.info(
            "train car temporary pull request has been closed", sources=ctxt.sources
        )
        return

    if car.queue_pull_request_number is None:
        raise RuntimeError(
            "Got draft pull request event on car without queue_pull_request_number"
        )

    ctxt.log.info(
        "handling train car temporary pull request event",
        sources=ctxt.sources,
        gh_pulls_queued=[
            ep.user_pull_request_number for ep in car.still_queued_embarked_pulls
        ],
    )

    queue_name = car.still_queued_embarked_pulls[0].config["name"]
    try:
        queue_rule = queue_rules[queue_name]
    except KeyError:
        ctxt.log.warning(
            "queue_rule not found for this train car",
            gh_pulls_queued=[
                ep.user_pull_request_number for ep in car.still_queued_embarked_pulls
            ],
            queue_rules=queue_rules,
            queue_name=queue_name,
        )
        return

    pull_requests = await car.get_pull_requests_to_evaluate()
    evaluated_queue_rule = await queue_rule.get_evaluated_queue_rule(
        ctxt.repository,
        ctxt.pull["base"]["ref"],
        pull_requests,
        ctxt.log,
        ctxt.has_been_refreshed_by_timer(),
    )

    for pull_request in pull_requests:
        await delayed_refresh.plan_next_refresh(
            ctxt, [evaluated_queue_rule], pull_request
        )

    if not ctxt.sources:
        # NOTE(sileht): Only comment/command, don't need to go further
        return None

    unexpected_changes: typing.Optional[merge_train.UnexpectedChange] = None
    if await have_unexpected_draft_pull_request_changes(ctxt, car):
        unexpected_changes = merge_train.UnexpectedDraftPullRequestChange(
            car.queue_pull_request_number
        )
    else:
        current_base_sha = await train.get_base_sha()
        if not await train.is_synced_with_the_base_branch(current_base_sha):
            unexpected_changes = merge_train.UnexpectedBaseBranchChange(
                current_base_sha
            )

    if unexpected_changes is None:
        real_status = status = await checks_status.get_rule_checks_status(
            ctxt.log,
            ctxt.repository,
            pull_requests,
            evaluated_queue_rule,
            unmatched_conditions_return_failure=False,
        )
        if real_status == check_api.Conclusion.FAILURE and (
            not car.has_previous_car_status_succeeded()
            or len(car.initial_embarked_pulls) != 1
        ):
            # NOTE(sileht): we can't set it as failed as we don't known
            # yet which pull request is responsible for the failure.
            # * one of the batch ?
            # * one of the parent car ?
            status = check_api.Conclusion.PENDING
    else:
        real_status = status = check_api.Conclusion.PENDING

    ctxt.log.info(
        "train car temporary pull request evaluation",
        gh_pull_queued=[
            ep.user_pull_request_number for ep in car.still_queued_embarked_pulls
        ],
        evaluated_queue_rule=evaluated_queue_rule.conditions.get_summary(),
        unexpected_changes=unexpected_changes,
        temporary_status=status,
        real_status=real_status,
        event_types=[se["event_type"] for se in ctxt.sources],
    )

    await car.update_state(real_status, evaluated_queue_rule)
    await car.update_summaries(status, unexpected_change=unexpected_changes)
    await train.save()

    if unexpected_changes:
        ctxt.log.info(
            "train will be reset",
            gh_pull_queued=[
                ep.user_pull_request_number for ep in car.still_queued_embarked_pulls
            ],
            unexpected_changes=unexpected_changes,
        )
        await train.reset(unexpected_changes)

        await ctxt.client.post(
            f"{ctxt.base_url}/issues/{ctxt.pull['number']}/comments",
            json={
                "body": f"This pull request has unexpected changes: {unexpected_changes}. The whole train will be reset."
            },
        )