コード例 #1
0
    def run(self, ctxt, rule, missing_conditions):
        if not config.GITHUB_APP:
            if self.config["strict_method"] == "rebase":
                return (
                    "failure",
                    "Misconfigured for GitHub Action",
                    "Due to GitHub Action limitation, `strict_method: rebase` "
                    "is only available with the Mergify GitHub App",
                )

        ctxt.log.info("process merge", config=self.config)

        q = queue.Queue.from_context(ctxt)

        output = helpers.merge_report(ctxt, self.config["strict"])
        if output:
            q.remove_pull(ctxt.pull["number"])
            return output

        if self.config["strict"] in ("smart+fasttrack", "smart+ordered"):
            q.add_pull(ctxt, self.config)

        if self._should_be_merged(ctxt):
            try:
                conclusion, title, summary = self._merge(ctxt)
                if conclusion is not None:
                    q.remove_pull(ctxt.pull["number"])
                return conclusion, title, summary
            except Exception:
                q.remove_pull(ctxt.pull["number"])
                raise
        else:
            return self._sync_with_base_branch(ctxt)
コード例 #2
0
    def cancel(self, ctxt, rule, missing_conditions) -> check_api.Result:
        self._set_effective_priority(ctxt)

        q = queue.Queue.from_context(ctxt)
        if ctxt.pull["state"] == "closed":
            output = helpers.merge_report(ctxt, self.config["strict"])
            if output:
                q.remove_pull(ctxt.pull["number"])
                return output

        # We just rebase the pull request, don't cancel it yet if CIs are
        # running. The pull request will be merge if all rules match again.
        # if not we will delete it when we received all CIs termination
        if self.config["strict"] and self._required_statuses_in_progress(
                ctxt, missing_conditions):
            if self._should_be_merged(ctxt, q):
                # Just wait for CIs to finish
                return helpers.get_strict_status(ctxt,
                                                 rule,
                                                 missing_conditions,
                                                 need_update=ctxt.is_behind)
            else:
                # Something got merged in the base branch in the meantime: rebase it again
                return self._sync_with_base_branch(ctxt, rule,
                                                   missing_conditions, q)

        q.remove_pull(ctxt.pull["number"])

        return self.cancelled_check_report
コード例 #3
0
ファイル: action.py プロジェクト: mscherer/mergify-engine
    def _merge(self, pull, installation_id):
        if self.config["method"] != "rebase" or pull.g_pull.raw_data[
                "rebaseable"]:
            method = self.config["method"]
        elif self.config["rebase_fallback"]:
            method = self.config["rebase_fallback"]
        else:
            return (
                "action_required",
                "Automatic rebasing is not possible, manual intervention required",
                "",
            )

        kwargs = pull.get_merge_commit_message() or {}
        try:
            pull.g_pull.merge(sha=pull.g_pull.head.sha,
                              merge_method=method,
                              **kwargs)
        except github.GithubException as e:  # pragma: no cover
            if pull.g_pull.is_merged():
                pull.log.info("merged in the meantime")
            else:
                return self._handle_merge_error(e, pull, installation_id)
        else:
            pull.log.info("merged")

        pull.g_pull.update()
        return helpers.merge_report(pull, self.config["strict"])
コード例 #4
0
    def _merge(pull, method):
        kwargs = pull.get_merge_commit_message() or {}
        try:
            pull.g_pull.merge(sha=pull.g_pull.head.sha,
                              merge_method=method,
                              **kwargs)
        except github.GithubException as e:  # pragma: no cover
            if pull.g_pull.is_merged():
                LOG.info("merged in the meantime", pull=pull)
            else:
                if e.status != 405:
                    message = "Mergify fails to merge the pull request"
                elif pull.g_pull.mergeable_state == "blocked":
                    message = ("Branch protection settings are blocking "
                               "automatic merging\nSee: %s" %
                               BRANCH_PROTECTION_FAQ_URL)
                else:
                    message = ("Repository settings are blocking automatic "
                               "merging")

                log_method = LOG.error if e.status >= 500 else LOG.info
                log_method("merge fail",
                           status=e.status,
                           mergify_message=message,
                           error_message=e.data["message"],
                           pull=pull)

                return ("failure", message,
                        "GitHub error message: `%s`" % e.data["message"])
        else:
            LOG.info("merged", pull=pull)

        pull.g_pull.update()
        return helpers.merge_report(pull)
コード例 #5
0
ファイル: action.py プロジェクト: Honda619/mergify-engine
    def _merge(self, ctxt):
        if self.config["method"] != "rebase" or ctxt.pull["rebaseable"]:
            method = self.config["method"]
        elif self.config["rebase_fallback"]:
            method = self.config["rebase_fallback"]
        else:
            return (
                "action_required",
                "Automatic rebasing is not possible, manual intervention required",
                "",
            )

        data = ctxt.get_merge_commit_message() or {}
        data["sha"] = ctxt.pull["head"]["sha"]
        data["merge_method"] = method

        try:
            ctxt.client.put(f"pulls/{ctxt.pull['number']}/merge", json=data)
        except httpx.HTTPClientSideError as e:  # pragma: no cover
            ctxt.update()
            if ctxt.pull["merged"]:
                ctxt.log.info("merged in the meantime")
            else:
                return self._handle_merge_error(e, ctxt)
        else:
            ctxt.update()
            ctxt.log.info("merged")

        return helpers.merge_report(ctxt, self.config["strict"])
コード例 #6
0
ファイル: action.py プロジェクト: harlowja/mergify-engine
    def _merge(pull, method):
        try:
            pull.g_pull.merge(sha=pull.g_pull.head.sha, merge_method=method)
        except github.GithubException as e:  # pragma: no cover
            if pull.g_pull.is_merged():
                LOG.info("merged in the meantime", pull=pull)

            elif e.status == 405:
                LOG.error("merge fail", error=e.data["message"], pull=pull)
                if pull.g_pull.mergeable_state == "blocked":
                    return ("failure", "Branch protection settings are "
                            "blocking automatic merging", e.data["message"])
                else:
                    return ("failure",
                            "Repository settings are blocking automatic "
                            "merging", e.data["message"])

            elif 400 <= e.status < 500:
                LOG.error("merge fail", error=e.data["message"], pull=pull)
                return ("failure", "Mergify fails to merge the pull request",
                        e.data["message"])
            else:
                raise
        else:
            LOG.info("merged", pull=pull)

        pull.g_pull.update()
        return helpers.merge_report(pull)
コード例 #7
0
ファイル: queue.py プロジェクト: harlowja/mergify-engine
def _handle_first_pull_in_queue(queue, pull):
    _, installation_id, owner, reponame, branch = queue.split("~")
    old_checks = [c for c in check_api.get_checks(pull.g_pull)
                  if (c.name.endswith(" (merge)") and
                      c._rawData['app']['id'] == config.INTEGRATION_ID)]

    merge_output = helpers.merge_report(pull)
    mergeable_state_output = helpers.output_for_mergeable_state(pull, True)
    if merge_output or mergeable_state_output:
        remove_pull(pull)
        conclusion, title, summary = merge_output or mergeable_state_output
    else:
        LOG.debug("updating base branch of pull request", pull=pull)
        redis = utils.get_redis_for_cache()
        method = redis.get(_get_update_method_cache_key(pull)) or "merge"
        conclusion, title, summary = helpers.update_pull_base_branch(
            pull, installation_id, method)

        if pull.g_pull.state == "closed":
            remove_pull(pull)
        elif conclusion == "failure":
            _move_pull_at_end(pull)

    status = "completed" if conclusion else "in_progress"
    for c in old_checks:
        check_api.set_check_run(
            pull.g_pull, c.name, status, conclusion,
            output={"title": title, "summary": summary})
コード例 #8
0
ファイル: action.py プロジェクト: mscherer/mergify-engine
    def run(
        self,
        installation_id,
        installation_token,
        event_type,
        data,
        pull,
        missing_conditions,
    ):
        pull.log.debug("process merge", config=self.config)

        output = helpers.merge_report(pull, self.config["strict"])
        if output:
            if self.config["strict"] == "smart":
                queue.remove_pull(pull)
            return output

        if self.config["strict"] and pull.is_behind():
            return self._sync_with_base_branch(pull, installation_id)
        else:
            try:
                return self._merge(pull, installation_id)
            finally:
                if self.config["strict"] == "smart":
                    queue.remove_pull(pull)
コード例 #9
0
    def run(self, ctxt, rule, missing_conditions) -> check_api.Result:
        if not config.GITHUB_APP:
            if self.config["strict_method"] == "rebase":
                return check_api.Result(
                    check_api.Conclusion.FAILURE,
                    "Misconfigured for GitHub Action",
                    "Due to GitHub Action limitation, `strict_method: rebase` "
                    "is only available with the Mergify GitHub App",
                )

        if self.config[
                "update_bot_account"] and not ctxt.subscription.has_feature(
                    subscription.Features.MERGE_BOT_ACCOUNT):
            return check_api.Result(
                check_api.Conclusion.ACTION_REQUIRED,
                "Merge with `update_bot_account` set are disabled",
                ctxt.subscription.missing_feature_reason(
                    ctxt.pull["base"]["repo"]["owner"]["login"]),
            )

        if self.config[
                "merge_bot_account"] and not ctxt.subscription.has_feature(
                    subscription.Features.MERGE_BOT_ACCOUNT):
            return check_api.Result(
                check_api.Conclusion.ACTION_REQUIRED,
                "Merge with `merge_bot_account` set are disabled",
                ctxt.subscription.missing_feature_reason(
                    ctxt.pull["base"]["repo"]["owner"]["login"]),
            )

        self._set_effective_priority(ctxt)

        ctxt.log.info("process merge", config=self.config)

        q = queue.Queue.from_context(ctxt)

        result = helpers.merge_report(ctxt, self.config["strict"])
        if result:
            q.remove_pull(ctxt.pull["number"])
            return result

        if self.config["strict"] in ("smart+fasttrack", "smart+ordered"):
            q.add_pull(ctxt, self.config)

        if self._should_be_merged(ctxt, q):
            try:
                result = self._merge(ctxt, rule, missing_conditions, q)
                if result.conclusion is not check_api.Conclusion.PENDING:
                    q.remove_pull(ctxt.pull["number"])
                return result
            except Exception:
                q.remove_pull(ctxt.pull["number"])
                raise
        else:
            return self._sync_with_base_branch(ctxt, rule, missing_conditions,
                                               q)
コード例 #10
0
ファイル: queue.py プロジェクト: mauricedb/mergify-engine
def _handle_first_pull_in_queue(queue, pull):
    _, installation_id, owner, reponame, branch = queue.split("~")
    old_checks = [
        c for c in check_api.get_checks(pull.g_pull, mergify_only=True)
        if c.name.endswith(" (merge)")
    ]

    output = helpers.merge_report(pull, True)
    if output:
        conclusion, title, summary = output
        LOG.debug(
            "pull request closed in the meantime",
            pull=pull,
            conclusion=conclusion,
            title=title,
            summary=summary,
        )
        remove_pull(pull)
    else:
        LOG.debug("updating base branch of pull request", pull=pull)
        redis = utils.get_redis_for_cache()
        method = redis.get(_get_update_method_cache_key(pull)) or "merge"
        conclusion, title, summary = helpers.update_pull_base_branch(
            pull, installation_id, method)

        if pull.g_pull.state == "closed":
            LOG.debug(
                "pull request closed in the meantime",
                pull=pull,
                conclusion=conclusion,
                title=title,
                summary=summary,
            )
            remove_pull(pull)
        elif conclusion == "failure":
            LOG.debug("base branch update failed",
                      pull=pull,
                      title=title,
                      summary=summary)
            _move_pull_at_end(pull)

    status = "completed" if conclusion else "in_progress"
    for c in old_checks:
        check_api.set_check_run(
            pull.g_pull,
            c.name,
            status,
            conclusion,
            output={
                "title": title,
                "summary": summary
            },
        )
コード例 #11
0
    def _merge(self, ctxt):
        if self.config["method"] != "rebase" or ctxt.pull["rebaseable"]:
            method = self.config["method"]
        elif self.config["rebase_fallback"]:
            method = self.config["rebase_fallback"]
        else:
            return (
                "action_required",
                "Automatic rebasing is not possible, manual intervention required",
                "",
            )

        data = {}

        try:
            commit_title_and_message = self._get_commit_message(
                ctxt.pull_request,
                self.config["commit_message"],
            )
        except context.RenderTemplateFailure as rmf:
            return (
                "action_required",
                "Invalid commit message",
                str(rmf),
            )

        if commit_title_and_message is not None:
            title, message = commit_title_and_message
            if title:
                data["commit_title"] = title
            if message:
                data["commit_message"] = message

        data["sha"] = ctxt.pull["head"]["sha"]
        data["merge_method"] = method

        try:
            ctxt.client.put(
                f"{ctxt.base_url}/pulls/{ctxt.pull['number']}/merge", json=data
            )
        except http.HTTPClientSideError as e:  # pragma: no cover
            ctxt.update()
            if ctxt.pull["merged"]:
                ctxt.log.info("merged in the meantime")
            else:
                return self._handle_merge_error(e, ctxt)
        else:
            ctxt.update()
            ctxt.log.info("merged")

        return helpers.merge_report(ctxt, self.config["strict"])
コード例 #12
0
ファイル: queue.py プロジェクト: junminahn/mergify-engine
    def handle_first_pull_in_queue(self, ctxt):
        old_checks = [
            c for c in ctxt.pull_engine_check_runs
            if c["name"].endswith(" (merge)")
        ]

        output = helpers.merge_report(ctxt, True)
        if output:
            conclusion, title, summary = output
            ctxt.log.info(
                "pull request closed in the meantime",
                conclusion=conclusion,
                title=title,
                summary=summary,
            )
            self.remove_pull(ctxt.pull["number"])
        else:
            ctxt.log.info("updating base branch of pull request")
            config = self.get_config(ctxt.pull["number"])
            conclusion, title, summary = helpers.update_pull_base_branch(
                ctxt,
                config["strict_method"],
                config["bot_account"],
            )

            if ctxt.pull["state"] == "closed":
                ctxt.log.info(
                    "pull request closed in the meantime",
                    conclusion=conclusion,
                    title=title,
                    summary=summary,
                )
                self.remove_pull(ctxt.pull["number"])
            elif conclusion == "failure":
                ctxt.log.info("base branch update failed",
                              title=title,
                              summary=summary)
                self._move_pull_at_end(ctxt.pull["number"])

        status = "completed" if conclusion else "in_progress"
        for c in old_checks:
            check_api.set_check_run(
                ctxt,
                c["name"],
                status,
                conclusion,
                output={
                    "title": title,
                    "summary": summary
                },
            )
コード例 #13
0
    def run(self, pull, sources, missing_conditions):
        pull.log.debug("process merge", config=self.config)

        output = helpers.merge_report(pull, self.config["strict"])
        if output:
            if self.config["strict"] == "smart":
                queue.remove_pull(pull)
            return output

        if self.config["strict"] and pull.is_behind:
            return self._sync_with_base_branch(pull)
        else:
            try:
                return self._merge(pull)
            finally:
                if self.config["strict"] == "smart":
                    queue.remove_pull(pull)
コード例 #14
0
    def run(self, installation_id, installation_token, event_type, data, pull,
            missing_conditions):
        LOG.debug("process merge", config=self.config, pull=pull)

        output = helpers.merge_report(pull)
        if output:
            return output

        output = helpers.output_for_mergeable_state(pull,
                                                    self.config["strict"])
        if output:
            return output

        if self.config["strict"] and pull.is_behind():
            # NOTE(sileht): Almost ready, one last rebase/update

            if not pull.base_is_modifiable():
                return ("failure", "Pull request can't be updated with latest "
                        "base branch changes, owner doesn't allow "
                        "modification", "")
            elif self.config["strict"] == "smart":
                queue.add_pull(pull, self.config["strict_method"])
                return (None, "Base branch will be updated soon",
                        "The pull request base branch will "
                        "be updated soon, and then merged.")
            else:
                return helpers.update_pull_base_branch(
                    pull, installation_id, self.config["strict_method"])
        else:

            # NOTE(sileht): Ready to merge!

            if self.config["strict"] == "smart":
                queue.remove_pull(pull)

            if (self.config["method"] != "rebase"
                    or pull.g_pull.raw_data['rebaseable']):
                return self._merge(pull, self.config["method"])
            elif self.config["rebase_fallback"]:
                return self._merge(pull, self.config["rebase_fallback"])
            else:
                return ("action_required", "Automatic rebasing is not "
                        "possible, manual intervention required", "")
コード例 #15
0
    def cancel(self, ctxt, rule, missing_conditions):
        q = queue.Queue.from_context(ctxt)
        if ctxt.pull["state"] == "closed":
            output = helpers.merge_report(ctxt, self.config["strict"])
            if output:
                q.remove_pull(ctxt.pull["number"])
                return output

        # We just rebase the pull request, don't cancel it yet if CIs are
        # running. The pull request will be merge if all rules match again.
        # if not we will delete it when we received all CIs termination
        if self.config["strict"] and self._required_statuses_in_progress(
            ctxt, missing_conditions
        ):
            return helpers.get_strict_status(
                ctxt, rule, missing_conditions, need_update=ctxt.is_behind
            )

        q.remove_pull(ctxt.pull["number"])

        return self.cancelled_check_report
コード例 #16
0
    def handle_first_pull_in_queue(self, ctxt):
        old_checks = [
            c for c in ctxt.pull_engine_check_runs
            if c["name"].endswith(" (merge)")
        ]

        result = helpers.merge_report(ctxt, True)
        if result:
            ctxt.log.info(
                "pull request closed in the meantime",
                result=result,
            )
            self.remove_pull(ctxt.pull["number"])
        else:
            ctxt.log.info("updating base branch of pull request")
            config = self.get_config(ctxt.pull["number"])
            result = helpers.update_pull_base_branch(
                ctxt,
                config["strict_method"],
                config["update_bot_account"] or config["bot_account"],
            )

            if ctxt.pull["state"] == "closed":
                ctxt.log.info(
                    "pull request closed in the meantime",
                    result=result,
                )
                self.remove_pull(ctxt.pull["number"])
            elif result.conclusion == check_api.Conclusion.FAILURE:
                ctxt.log.info(
                    "base branch update failed",
                    result=result,
                )
                self._move_pull_at_end(ctxt.pull["number"])

        for c in old_checks:
            check_api.set_check_run(ctxt, c["name"], result)
コード例 #17
0
    def _merge(
        self,
        ctxt: context.Context,
        rule: rules.Rule,
        missing_conditions: typing.List[filter.Filter],
        q: queue.Queue,
    ) -> check_api.Result:
        if self.config["method"] != "rebase" or ctxt.pull["rebaseable"]:
            method = self.config["method"]
        elif self.config["rebase_fallback"]:
            method = self.config["rebase_fallback"]
        else:
            return check_api.Result(
                check_api.Conclusion.ACTION_REQUIRED,
                "Automatic rebasing is not possible, manual intervention required",
                "",
            )

        data = {}

        try:
            commit_title_and_message = self._get_commit_message(
                ctxt.pull_request,
                self.config["commit_message"],
            )
        except context.RenderTemplateFailure as rmf:
            return check_api.Result(
                check_api.Conclusion.ACTION_REQUIRED,
                "Invalid commit message",
                str(rmf),
            )

        if commit_title_and_message is not None:
            title, message = commit_title_and_message
            if title:
                data["commit_title"] = title
            if message:
                data["commit_message"] = message

        data["sha"] = ctxt.pull["head"]["sha"]
        data["merge_method"] = method

        bot_account = self.config["merge_bot_account"]
        if bot_account:
            oauth_token = ctxt.subscription.get_token_for(bot_account)
            if not oauth_token:
                return check_api.Result(
                    check_api.Conclusion.FAILURE,
                    f"Unable to rebase: user `{bot_account}` is unknown. ",
                    f"Please make sure `{bot_account}` has logged in Mergify dashboard.",
                )
        else:
            oauth_token = None

        try:
            ctxt.client.put(
                f"{ctxt.base_url}/pulls/{ctxt.pull['number']}/merge",
                oauth_token=oauth_token,  # type: ignore
                json=data,
            )
        except http.HTTPClientSideError as e:  # pragma: no cover
            ctxt.update()
            if ctxt.pull["merged"]:
                ctxt.log.info("merged in the meantime")
            else:
                return self._handle_merge_error(e, ctxt, rule,
                                                missing_conditions, q)
        else:
            ctxt.update()
            ctxt.log.info("merged")

        result = helpers.merge_report(ctxt, self.config["strict"])
        if result:
            return result
        else:
            return check_api.Result(
                check_api.Conclusion.FAILURE,
                "Unexpected after merge pull request state",
                "The pull request have been merged, but GitHub API still report it open",
            )