예제 #1
0
async def pre_rebase_check(ctxt: context.Context) -> None:
    pre_update_check(ctxt)

    # If PR from a private fork but cannot be edited:
    # NOTE(jd): GitHub removed the ability to configure `maintainer_can_modify` on private
    # fork we which make rebase impossible
    if (
        ctxt.pull_from_fork
        and ctxt.pull["base"]["repo"]["private"]
        and not ctxt.pull["maintainer_can_modify"]
    ):
        raise BranchUpdateFailure(
            "Mergify needs the permission to update the base branch of the pull request.\n"
            "GitHub does not allow a GitHub App to modify base branch for a private fork.\n"
            "You cannot `rebase` a pull request from a private fork.",
            title="Pull request can't be updated with latest base branch changes",
        )
    elif not ctxt.can_change_github_workflow() and await ctxt.github_workflow_changed():
        raise BranchUpdateFailure(
            "The new Mergify permissions must be accepted to rebase pull request with `.github/workflows` changes.\n"
            "You can accept them at https://dashboard.mergify.com/.\n"
            "In the meantime, this pull request must be rebased manually.",
            title="Pull request can't be updated with latest base branch changes",
        )
예제 #2
0
    async def merge_report(
        self,
        ctxt: context.Context,
    ) -> typing.Optional[check_api.Result]:
        if ctxt.pull["draft"]:
            conclusion = check_api.Conclusion.PENDING
            title = "Draft flag needs to be removed"
            summary = ""
        elif ctxt.pull["merged"]:
            if ctxt.pull["merged_by"] is None:
                mode = "somehow"
            elif ctxt.pull["merged_by"]["login"] == config.BOT_USER_LOGIN:
                mode = "automatically"
            else:
                mode = "manually"
            conclusion = check_api.Conclusion.SUCCESS
            title = f"The pull request has been merged {mode}"
            summary = f"The pull request has been merged {mode} at *{ctxt.pull['merge_commit_sha']}*"
        elif ctxt.closed:
            conclusion = check_api.Conclusion.CANCELLED
            title = "The pull request has been closed manually"
            summary = ""

        # NOTE(sileht): Take care of all branch protection state
        elif ctxt.pull["mergeable_state"] == "dirty":
            conclusion = check_api.Conclusion.CANCELLED
            title = "Merge conflict needs to be solved"
            summary = ""

        elif ctxt.pull["mergeable_state"] == "unknown":
            conclusion = check_api.Conclusion.FAILURE
            title = "Pull request state reported as `unknown` by GitHub"
            summary = ""
        # FIXME(sileht): We disable this check as github wrongly report
        # mergeable_state == blocked sometimes. The workaround is to try to merge
        # it and if that fail we checks for blocking state.
        # elif ctxt.pull["mergeable_state"] == "blocked":
        #     conclusion = "failure"
        #     title = "Branch protection settings are blocking automatic merging"
        #     summary = ""

        elif (await self._is_branch_protection_linear_history_enabled(ctxt)
              and self.config["method"] == "merge"):
            conclusion = check_api.Conclusion.FAILURE
            title = "Branch protection setting 'linear history' conflicts with Mergify configuration"
            summary = "Branch protection setting 'linear history' works only if `method: squash` or `method: rebase`."

        elif (not ctxt.can_change_github_workflow()
              and await ctxt.github_workflow_changed()):
            conclusion = check_api.Conclusion.ACTION_REQUIRED
            title = "Pull request must be merged manually"
            summary = """The new Mergify permissions must be accepted to merge pull request with `.github/workflows` changes.\n
You can accept them at https://dashboard.mergify.com/\n
\n
In the meantime, the pull request must be merged manually."
"""

        # NOTE(sileht): remaining state "behind, clean, unstable, has_hooks
        # are OK for us
        else:
            return None

        return check_api.Result(conclusion, title, summary)
예제 #3
0
    async def run(
        self, ctxt: context.Context, rule: rules.EvaluatedRule
    ) -> check_api.Result:
        if (
            not ctxt.can_change_github_workflow()
            and await ctxt.github_workflow_changed()
        ):
            return check_api.Result(
                check_api.Conclusion.FAILURE,
                self.FAILURE_MESSAGE,
                "The new Mergify permissions must be accepted to create pull request with `.github/workflows` changes.\n"
                "You can accept them at https://dashboard.mergify.com/",
            )

        template_result = await self._verify_template(ctxt)
        if template_result is not None:
            return template_result

        try:
            bot_account = await action_utils.render_bot_account(
                ctxt,
                self.config["bot_account"],
                required_feature=subscription.Features.BOT_ACCOUNT,
                missing_feature_message=f"{self.KIND.capitalize()} with `bot_account` set is unavailable",
            )
        except action_utils.RenderBotAccountFailure as e:
            return check_api.Result(e.status, e.title, e.reason)

        branches: typing.List[github_types.GitHubRefType] = self.config["branches"]
        if self.config["regexes"]:
            branches.extend(
                [
                    branch["name"]
                    async for branch in typing.cast(
                        typing.AsyncGenerator[github_types.GitHubBranch, None],
                        ctxt.client.items(
                            f"{ctxt.base_url}/branches",
                            resource_name="branches",
                            page_limit=10,
                        ),
                    )
                    if any(
                        map(
                            lambda regex: regex.match(branch["name"]),
                            self.config["regexes"],
                        )
                    )
                ]
            )

        if len(branches) == 0:
            return check_api.Result(
                check_api.Conclusion.FAILURE,
                self.FAILURE_MESSAGE,
                "No destination branches found",
            )

        results = [
            await self._copy(ctxt, branch_name, bot_account) for branch_name in branches
        ]

        # Pick the first status as the final_status
        conclusion = results[0][0]
        for r in results[1:]:
            if r[0] == check_api.Conclusion.FAILURE:
                conclusion = check_api.Conclusion.FAILURE
                # If we have a failure, everything is set to fail
                break
            elif r[0] == check_api.Conclusion.SUCCESS:
                # If it was None, replace with success
                # Keep checking for a failure just in case
                conclusion = check_api.Conclusion.SUCCESS

        if conclusion == check_api.Conclusion.SUCCESS:
            message = self.SUCCESS_MESSAGE
        elif conclusion == check_api.Conclusion.FAILURE:
            message = self.FAILURE_MESSAGE
        else:
            message = "Pending"

        return check_api.Result(
            conclusion,
            message,
            "\n".join(f"* {detail}" for detail in (r[1] for r in results)),
        )