예제 #1
0
async def have_unexpected_changes(
    ctxt: context.Context, car: merge_train.TrainCar
) -> bool:
    if ctxt.pull["base"]["sha"] != car.initial_current_base_sha:
        ctxt.log.info(
            "train car has an unexpected base sha change",
            base_sha=ctxt.pull["base"]["sha"],
            initial_current_base_sha=car.initial_current_base_sha,
        )
        return True

    if ctxt.have_been_synchronized():
        ctxt.log.info(
            "train car has unexpectedly been synchronized",
        )
        return True

    unexpected_event = first(
        (source for source in ctxt.sources),
        key=lambda s: s["event_type"] == "pull_request"
        and typing.cast(github_types.GitHubEventPullRequest, s["data"])["action"]
        in ["closed", "reopened"],
    )
    if unexpected_event:
        ctxt.log.debug(
            "train car received an unexpected event",
            unexpected_event=unexpected_event,
        )
        return True

    return False
예제 #2
0
    async def _should_be_cancel(self, ctxt: context.Context,
                                rule: "rules.EvaluatedRule") -> bool:
        # It's closed, it's not going to change
        if ctxt.pull["state"] == "closed":
            return True

        if ctxt.have_been_synchronized():
            return True

        q = await merge_train.Train.from_context(ctxt)
        car = q.get_car(ctxt)
        if car and car.state == "updated":
            # NOTE(sileht) check first if PR should be removed from the queue
            pull_rule_checks_status = await self.get_pull_rule_checks_status(
                ctxt, rule)
            if pull_rule_checks_status == check_api.Conclusion.FAILURE:
                return True

            # NOTE(sileht): This car have been updated/rebased, so we should not cancel
            # the merge until we have a check that doesn't pass
            queue_rule_evaluated = await self.queue_rule.get_pull_request_rule(
                ctxt)
            queue_rule_checks_status = await merge_train.get_queue_rule_checks_status(
                ctxt, queue_rule_evaluated)
            return queue_rule_checks_status == check_api.Conclusion.FAILURE

        return True
예제 #3
0
    async def run(self, ctxt: context.Context,
                  rule: rules.EvaluatedRule) -> check_api.Result:
        if ctxt.have_been_synchronized():
            # FIXME(sileht): Currently sender id is not the bot by the admin
            # user that enroll the repo in Mergify, because branch_updater uses
            # his access_token instead of the Mergify installation token.
            # As workaround we track in redis merge commit id
            # This is only true for method="rebase"
            if await ctxt.redis.get(f"branch-update-{ctxt.pull['head']['sha']}"
                                    ):
                return check_api.Result(
                    check_api.Conclusion.SUCCESS,
                    "Updated by Mergify, ignoring",
                    "",
                )

            try:
                message = await ctxt.pull_request.render_template(
                    self.config["message"])
            except context.RenderTemplateFailure as rmf:
                return check_api.Result(
                    check_api.Conclusion.FAILURE,
                    "Invalid dismiss reviews message",
                    str(rmf),
                )

            errors = set()
            for review in (await ctxt.consolidated_reviews())[1]:
                conf = self.config.get(review["state"].lower(), False)
                if conf and (conf is True or review["user"]["login"] in conf):
                    try:
                        await ctxt.client.put(
                            f"{ctxt.base_url}/pulls/{ctxt.pull['number']}/reviews/{review['id']}/dismissals",
                            json={"message": message},
                        )
                    except http.HTTPClientSideError as e:  # pragma: no cover
                        errors.add(
                            f"GitHub error: [{e.status_code}] `{e.message}`")

            if errors:
                return check_api.Result(
                    check_api.Conclusion.PENDING,
                    "Unable to dismiss review",
                    "\n".join(errors),
                )
            else:
                await signals.send(ctxt, "action.dismiss_reviews")
                return check_api.Result(check_api.Conclusion.SUCCESS,
                                        "Review dismissed", "")
        else:
            return check_api.Result(
                check_api.Conclusion.SUCCESS,
                "Nothing to do, pull request have not been synchronized",
                "",
            )
예제 #4
0
    async def _should_be_cancel(
        self, ctxt: context.Context, rule: "rules.EvaluatedRule"
    ) -> bool:
        # It's closed, it's not going to change
        if ctxt.pull["state"] == "closed":
            return True

        if ctxt.have_been_synchronized():
            return True

        pull_rule_checks_status = await self.get_pull_rule_checks_status(ctxt, rule)
        return pull_rule_checks_status == check_api.Conclusion.FAILURE
예제 #5
0
    async def run(self, ctxt: context.Context,
                  rule: "rules.EvaluatedRule") -> check_api.Result:
        if not ctxt.subscription.has_feature(
                subscription.Features.QUEUE_ACTION):
            return check_api.Result(
                check_api.Conclusion.ACTION_REQUIRED,
                "Queue action is disabled",
                ctxt.subscription.missing_feature_reason(
                    ctxt.pull["base"]["repo"]["owner"]["login"]),
            )

        q = await merge_train.Train.from_context(ctxt)
        car = q.get_car(ctxt)
        if car and car.state == "updated":
            # NOTE(sileht): This car doesn't have tmp pull, so we have the
            # MERGE_QUEUE_SUMMARY and train reset here
            need_reset = ctxt.have_been_synchronized() or await ctxt.is_behind
            if need_reset:
                status = check_api.Conclusion.PENDING
                ctxt.log.info("train will be reset")
                await q.reset()
            else:
                queue_rule_evaluated = await self.queue_rule.get_pull_request_rule(
                    ctxt)
                status = await merge_train.get_queue_rule_checks_status(
                    ctxt, queue_rule_evaluated)
            await car.update_summaries(status, will_be_reset=need_reset)

        if ctxt.user_refresh_requested() or ctxt.admin_refresh_requested():
            # NOTE(sileht): user ask a refresh, we just remove the previous state of this
            # check and the method _should_be_queue will become true again :)
            check = await ctxt.get_engine_check_run(
                constants.MERGE_QUEUE_SUMMARY_NAME)
            if check and check_api.Conclusion(check["conclusion"]) not in [
                    check_api.Conclusion.SUCCESS,
                    check_api.Conclusion.PENDING,
            ]:
                await check_api.set_check_run(
                    ctxt,
                    constants.MERGE_QUEUE_SUMMARY_NAME,
                    check_api.Result(
                        check_api.Conclusion.PENDING,
                        "The pull request has been refreshed and is going to be re-embarked soon",
                        "",
                    ),
                )

        return await super().run(ctxt, rule)
예제 #6
0
파일: merge.py 프로젝트: jd/mergify-engine
    def _should_be_cancel(
        self, ctxt: context.Context, rule: "rules.EvaluatedRule"
    ) -> bool:
        # It's closed, it's not going to change
        if ctxt.pull["state"] == "closed":
            return True

        if ctxt.have_been_synchronized():
            return True

        need_look_at_checks = []
        for condition in rule.missing_conditions:
            if condition.attribute_name.startswith(
                "check-"
            ) or condition.attribute_name.startswith("status-"):
                # TODO(sileht): Just return True here, no need to checks checks anymore,
                # this method is no more used by teh merge queue
                need_look_at_checks.append(condition)
            else:
                # something else does not match anymore
                return True

        if need_look_at_checks:
            if not ctxt.checks:
                return False

            states = [
                state
                for name, state in ctxt.checks.items()
                for cond in need_look_at_checks
                if cond(FakePR(cond.attribute_name, name))
            ]
            if not states:
                return False

            for state in states:
                if state in ("pending", None):
                    return False

        return True