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", )
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)
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)), )