Example #1
0
    def create_check_run_and_push_event(self,
                                        pr,
                                        name,
                                        conclusion=None,
                                        created=True,
                                        check_suite=False):
        if conclusion is None:
            status = "in_progress"
        else:
            status = "completed"

        check_api.set_check_run(pr, name, status, conclusion)

        expected_events = []
        if created:
            expected_events.append(
                # Created
                ("check_run", {
                    "check_run": {
                        "conclusion": conclusion
                    }
                }), )
        if conclusion:
            expected_events.append(
                # Completed
                ("check_run", {
                    "check_run": {
                        "conclusion": conclusion
                    }
                }), )
            if check_suite:
                expected_events.append(("check_suite", {
                    'action': 'completed'
                }), )
        self.push_events(expected_events, ordered=False)
Example #2
0
    def create_check_run_and_push_event(self,
                                        pr,
                                        name,
                                        conclusion=None,
                                        ignore_check_run_event=False,
                                        ignore_check_suite_event=False):
        if conclusion is None:
            status = "in_progress"
        else:
            status = "completed"

        pr_as_app = self.repo_as_app.get_pull(pr.number)
        check_api.set_check_run(pr_as_app, name, status, conclusion)

        expected_events = []
        if not ignore_check_run_event:
            expected_events.append(("check_run", {
                "action": "created",
                "check_run": {
                    "conclusion": conclusion
                }
            }))
        if not ignore_check_suite_event:
            expected_events.append(("check_suite", {'action': 'completed'}))

        self.push_events(expected_events, ordered=False)
Example #3
0
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})
Example #4
0
def post_summary(event_type, data, pull, match, checks):
    summary_title, summary = gen_summary(event_type, data, pull, match)

    summary_name = "Summary"
    summary_check = checks.get(summary_name)

    summary_changed = (not summary_check
                       or summary_check.output["title"] != summary_title
                       or summary_check.output["summary"] != summary)

    if summary_changed:
        LOG.debug("summary changed",
                  summary={
                      "title": summary_title,
                      "name": summary_name,
                      "summary": summary
                  })
        check_api.set_check_run(pull.g_pull,
                                summary_name,
                                "completed",
                                "success",
                                output={
                                    "title": summary_title,
                                    "summary": summary
                                })
    else:
        LOG.debug("summary unchanged",
                  summary={
                      "title": summary_title,
                      "name": summary_name,
                      "summary": summary
                  })
def post_summary(event_type, data, pull, match, summary_check, conclusions):
    summary_title, summary = gen_summary(event_type, data, pull, match)

    summary += doc.MERGIFY_PULL_REQUEST_DOC
    summary += serialize_conclusions(conclusions)

    summary_changed = (
        not summary_check
        or summary_check.output["title"] != summary_title
        or summary_check.output["summary"] != summary
    )

    if summary_changed:
        LOG.debug(
            "summary changed",
            summary={"title": summary_title, "name": SUMMARY_NAME, "summary": summary},
        )
        check_api.set_check_run(
            pull.g_pull,
            SUMMARY_NAME,
            "completed",
            "success",
            output={"title": summary_title, "summary": summary},
        )
    else:
        LOG.debug(
            "summary unchanged",
            summary={"title": summary_title, "name": SUMMARY_NAME, "summary": summary},
        )
Example #6
0
def check_configuration_changes(event_pull):
    if event_pull.base.repo.default_branch == event_pull.base.ref:
        ref = None
        for f in event_pull.get_files():
            if f.filename == ".mergify.yml":
                ref = f.contents_url.split("?ref=")[1]

        if ref is not None:
            try:
                rules.get_mergify_config(event_pull.base.repo, ref=ref)
            except rules.InvalidRules as e:  # pragma: no cover
                # Not configured, post status check with the error message
                # TODO(sileht): we can annotate the .mergify.yml file in Github
                # UI with that API
                check_api.set_check_run(
                    event_pull, "Summary", "completed",
                    "failure", output={
                        "title": "The new Mergify configuration is invalid",
                        "summary": str(e)
                    })
            else:
                check_api.set_check_run(
                    event_pull, "Summary", "completed",
                    "success", output={
                        "title": "The new Mergify configuration is valid",
                        "summary": "This pull request have to be merged "
                        "manually because it modifies Mergify configuration",
                    })

            return True
    return False
Example #7
0
def update_next_pull(installation_id, installation_token, subscription, owner,
                     reponame, branch, key, cur_key):
    redis = utils.get_redis_for_cache()
    pull_number = redis.srandmember(key)
    if not pull_number:
        LOG.debug("no more pull request to update",
                  installation_id=installation_id,
                  pull_number=pull_number,
                  repo=owner + "/" + reponame,
                  branch=branch)
        return

    LOG.debug("next pull to rebase",
              installation_id=installation_id,
              pull_number=pull_number,
              repo=owner + "/" + reponame,
              branch=branch)

    pull = mergify_pull.MergifyPull.from_number(installation_id,
                                                installation_token, owner,
                                                reponame, int(pull_number))

    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)
    ]

    output = output_for_mergeable_state(pull, True)
    if output:
        redis.srem(_get_cache_key(pull), pull.g_pull.number)
        conclusion, title, summary = output
    elif pull.g_pull.state == "closed":
        redis.srem(_get_cache_key(pull), pull.g_pull.number)
        if pull.g_pull.merged:
            conclusion = "success"
            title = "The pull request has been merged manually"
            summary = ("The pull request has been merged manually "
                       "at *%s*" % pull.g_pull.merge_commit_sha)
        else:
            conclusion = "cancelled"
            title = "The pull request has been closed manually"
            summary = ""
    else:
        conclusion, title, summary = update_pull_base_branch(
            pull, subscription)
        redis.set(cur_key, pull_number)

    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
                                })
Example #8
0
def run_actions(installation_id, installation_token, subscription, event_type,
                data, pull, match, checks):

    # Run actions
    for rule, missing_conditions in match.matching_rules:
        for action in rule['actions']:
            check_name = "Mergify — Rule: %s (%s)" % (rule['name'], action)
            prev_check = checks.get(check_name)

            if missing_conditions:
                # NOTE(sileht): The rule was matching before, but it doesn't
                # anymore, since we can't remove checks, put them in cancelled
                # state
                cancel_in_progress = rule["actions"][action].cancel_in_progress
                if (cancel_in_progress and prev_check
                        and prev_check.status == "in_progress"):
                    title = ("The rule doesn't match anymore, this action "
                             "has been cancelled")
                    check_api.set_check_run(pull.g_pull,
                                            check_name,
                                            "completed",
                                            "cancelled",
                                            output={
                                                "title": title,
                                                "summary": " "
                                            })
                continue

            # NOTE(sileht): actions already done
            if prev_check:
                if prev_check.conclusion == "success":
                    continue
                elif (prev_check.conclusion and event_type != "refresh"):
                    continue

            report = run_action(rule, action, check_name, prev_check,
                                installation_id, installation_token,
                                subscription, event_type, data, pull)

            if not report:
                continue

            conclusion, title, summary = report

            if conclusion:
                status = "completed"
            else:
                status = "in_progress"

            check_api.set_check_run(pull.g_pull,
                                    check_name,
                                    status,
                                    conclusion,
                                    output={
                                        "title": title,
                                        "summary": summary
                                    })
Example #9
0
def update_next_pull(installation_id, installation_token, owner, reponame,
                     branch, key, cur_key):
    redis = utils.get_redis_for_cache()
    pull_number = redis.srandmember(key)
    if not pull_number:
        LOG.debug("no more pull request to update",
                  installation_id=installation_id,
                  pull_number=pull_number,
                  repo=owner + "/" + reponame,
                  branch=branch)
        return

    LOG.debug("next pull to rebase",
              installation_id=installation_id,
              pull_number=pull_number,
              repo=owner + "/" + reponame,
              branch=branch)

    pull = mergify_pull.MergifyPull.from_number(installation_id,
                                                installation_token, owner,
                                                reponame, int(pull_number))

    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 = merge_report(pull)
    mergeable_state_output = output_for_mergeable_state(pull, True)
    if merge_output:
        redis.srem(_get_queue_cache_key(pull), pull.g_pull.number)
        conclusion, title, summary = merge_output
    elif mergeable_state_output:
        redis.srem(_get_queue_cache_key(pull), pull.g_pull.number)
        conclusion, title, summary = mergeable_state_output
    else:
        method = redis.get(_get_update_method_cache_key(pull)) or "merge"
        conclusion, title, summary = update_pull_base_branch(
            pull, installation_id, method)
        if pull.g_pull.state == "closed":
            redis.srem(_get_queue_cache_key(pull), pull.g_pull.number)
        else:
            redis.set(cur_key, pull_number)

    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
                                })
Example #10
0
def run_actions(installation_id, installation_token, subscription, event_type,
                data, pull, match, checks):

    # Run actions
    for rule, missing_conditions in match.matching_rules:
        for action in rule['actions']:
            check_name = "Mergify — Rule: %s (%s)" % (rule['name'], action)
            prev_check = checks.get(check_name)

            if missing_conditions:
                if not prev_check:
                    LOG.info("action evaluation: nothing to cancel",
                             check_name=check_name,
                             pull_request=pull,
                             missing_conditions=missing_conditions)
                    continue
                method_name = "cancel"
                expected_conclusion = ["cancelled"]
            else:
                method_name = "run"
                expected_conclusion = ["success", "failure"]

            already_run = (prev_check
                           and prev_check.conclusion in expected_conclusion
                           and event_type != "refresh")
            if already_run:
                LOG.info("action evaluation: already in expected state",
                         conclusion=(prev_check.conclusion
                                     if prev_check else "no-previous-check"),
                         check_name=check_name,
                         pull_request=pull,
                         missing_conditions=missing_conditions)
                continue

            report = exec_action(method_name, rule, action, installation_id,
                                 installation_token, subscription, event_type,
                                 data, pull, missing_conditions)

            if report:
                conclusion, title, summary = report
                status = "completed" if conclusion else "in_progress"
                check_api.set_check_run(pull.g_pull,
                                        check_name,
                                        status,
                                        conclusion,
                                        output={
                                            "title": title,
                                            "summary": summary
                                        })

            LOG.info("action evaluation: done",
                     report=report,
                     check_name=check_name,
                     pull_request=pull,
                     missing_conditions=missing_conditions)
Example #11
0
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
            },
        )
Example #12
0
def post_summary(event_type, data, pull, match, checks):
    # Set the summary
    summary_name = "Summary"
    deprecated_summary_name = "Mergify — " + summary_name
    summary = ""

    summary += get_already_merged_summary(event_type, data, pull, match)

    completed_rules = 0
    for rule, missing_conditions in match.matching_rules:
        summary += "#### Rule: %s" % rule['name']
        summary += " (%s)" % ", ".join(rule['actions'])
        for cond in rule['conditions']:
            checked = " " if cond in missing_conditions else "X"
            summary += "\n- [%s] `%s`" % (checked, cond)
        if not missing_conditions:
            completed_rules += 1
        summary += "\n\n"

    potential_rules = len(match.matching_rules) - completed_rules

    summary_title = []
    if completed_rules == 1:
        summary_title.append("%d rule matches" % completed_rules)
    elif completed_rules > 1:
        summary_title.append("%d rules match" % completed_rules)

    if potential_rules == 1:
        summary_title.append("%s potential rule" % potential_rules)
    elif potential_rules > 1:
        summary_title.append("%s potential rules" % potential_rules)

    if completed_rules == 0 and potential_rules == 0:
        summary_title.append("no rules match, no planned actions")

    summary_title = " and ".join(summary_title)

    summary_check = (checks.get(deprecated_summary_name)
                     or checks.get(summary_name))

    summary_changed = (not summary_check
                       or summary_check.output["title"] != summary_title
                       or summary_check.output["summary"] != summary)

    if summary_changed:
        check_api.set_check_run(pull.g_pull,
                                summary_name,
                                "completed",
                                "success",
                                output={
                                    "title": summary_title,
                                    "summary": summary
                                })
Example #13
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)")
        ]

        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
                },
            )
Example #14
0
def post_summary(ctxt, match, summary_check, conclusions,
                 previous_conclusions):
    summary_title, summary = gen_summary(ctxt, match)

    summary += doc.MERGIFY_PULL_REQUEST_DOC
    summary += serialize_conclusions(conclusions)

    summary_changed = (not summary_check
                       or summary_check["output"]["title"] != summary_title
                       or summary_check["output"]["summary"] != summary)

    if summary_changed:
        ctxt.log.info(
            "summary changed",
            summary={
                "title": summary_title,
                "name": SUMMARY_NAME,
                "summary": summary
            },
            sources=_filterred_sources_for_logging(ctxt.sources),
            conclusions=conclusions,
            previous_conclusions=previous_conclusions,
        )
        check_api.set_check_run(
            ctxt,
            SUMMARY_NAME,
            "completed",
            "success",
            output={
                "title": summary_title,
                "summary": summary
            },
        )
    else:
        ctxt.log.info(
            "summary unchanged",
            summary={
                "title": summary_title,
                "name": SUMMARY_NAME,
                "summary": summary
            },
            sources=_filterred_sources_for_logging(ctxt.sources),
            conclusions=conclusions,
            previous_conclusions=previous_conclusions,
        )
Example #15
0
def check_configuration_changes(ctxt):
    if ctxt.pull["base"]["repo"]["default_branch"] == ctxt.pull["base"]["ref"]:
        ref = None
        for f in ctxt.files:
            if f["filename"] in rules.MERGIFY_CONFIG_FILENAMES:
                ref = f["contents_url"].split("?ref=")[1]

        if ref is not None:
            try:
                rules.get_mergify_config(ctxt.client,
                                         ctxt.pull["base"]["repo"]["name"],
                                         ref=ref)
            except rules.InvalidRules as e:
                # Not configured, post status check with the error message
                check_api.set_check_run(
                    ctxt,
                    actions_runner.SUMMARY_NAME,
                    "completed",
                    "failure",
                    output={
                        "title": "The new Mergify configuration is invalid",
                        "summary": str(e),
                        "annotations": e.get_annotations(e.filename),
                    },
                )
            else:
                check_api.set_check_run(
                    ctxt,
                    actions_runner.SUMMARY_NAME,
                    "completed",
                    "success",
                    output={
                        "title":
                        "The new Mergify configuration is valid",
                        "summary":
                        "This pull request must be merged "
                        "manually because it modifies Mergify configuration",
                    },
                )

            return True
    return False
    def test_command_refresh(self):
        rules = {
            "pull_request_rules": [{
                "name": "nothing",
                "conditions": ["base!=master"],
                "actions": {
                    "merge": {}
                },
            }]
        }
        self.setup_repo(yaml.dump(rules))
        p, commits = self.create_pr()

        check_api.set_check_run(
            p,
            "Summary",
            "completed",
            "success",
            output={
                "title": "whatever",
                "summary": "erased"
            },
        )

        checks = list(check_api.get_checks(p))
        assert len(checks) == 1
        assert checks[0].name == "Summary"
        completed_at = checks[0].completed_at

        p.create_issue_comment("@mergifyio refresh")

        self.wait_for("issue_comment", {"action": "created"})

        checks = list(check_api.get_checks(p))
        assert len(checks) == 1
        assert checks[0].name == "Summary"
        assert completed_at != checks[0].completed_at

        p.update()
        comments = list(p.get_issue_comments())
        self.assertEqual("**Command `refresh`: success**", comments[-1].body)
Example #17
0
def check_configuration_changes(ctxt):
    if ctxt.pull["base"]["repo"]["default_branch"] == ctxt.pull["base"]["ref"]:
        ref = None
        for f in ctxt.files:
            if f["filename"] in rules.MERGIFY_CONFIG_FILENAMES:
                ref = f["contents_url"].split("?ref=")[1]

        if ref is not None:
            try:
                rules.get_mergify_config(ctxt, ref=ref)
            except rules.InvalidRules as e:  # pragma: no cover
                # Not configured, post status check with the error message
                # TODO(sileht): we can annotate the .mergify.yml file in Github
                # UI with that API
                check_api.set_check_run(
                    ctxt,
                    "Summary",
                    "completed",
                    "failure",
                    output={
                        "title": "The new Mergify configuration is invalid",
                        "summary": str(e),
                    },
                )
            else:
                check_api.set_check_run(
                    ctxt,
                    "Summary",
                    "completed",
                    "success",
                    output={
                        "title":
                        "The new Mergify configuration is valid",
                        "summary":
                        "This pull request must be merged "
                        "manually because it modifies Mergify configuration",
                    },
                )

            return True
    return False
Example #18
0
def copy_summary_from_previous_head_sha(ctxt, sha):
    checks = check_api.get_checks_for_ref(
        ctxt, sha, check_name=actions_runner.SUMMARY_NAME,
    )
    checks = [c for c in checks if c["app"]["id"] == config.INTEGRATION_ID]

    if not checks:
        ctxt.log.warning(
            "Got synchronize event but didn't find Summary on previous head sha",
        )
        return
    check_api.set_check_run(
        ctxt,
        actions_runner.SUMMARY_NAME,
        "completed",
        "success",
        output={
            "title": checks[0]["output"]["title"],
            "summary": checks[0]["output"]["summary"],
        },
    )
Example #19
0
def copy_summary_from_previous_head_sha(ctxt, sha):
    checks = check_api.get_checks_for_ref(
        ctxt,
        sha,
        check_name=actions_runner.SUMMARY_NAME,
    )
    checks = [c for c in checks if c["app"]["id"] == config.INTEGRATION_ID]
    if checks:
        check_api.set_check_run(
            ctxt,
            actions_runner.SUMMARY_NAME,
            "completed",
            "success",
            output={
                "title": checks[0]["output"]["title"],
                "summary": checks[0]["output"]["summary"],
            },
        )
        return True
    else:
        return False
    def test_comment_backwardcompat(self):
        rules = {
            "pull_request_rules": [{
                "name": "comment",
                "conditions": ["base=master"],
                "actions": {
                    "comment": {
                        "message": "WTF?"
                    }
                },
            }]
        }

        self.setup_repo(yaml.dump(rules))

        p, _ = self.create_pr()

        p.update()
        comments = list(p.get_issue_comments())
        self.assertEqual("WTF?", comments[-1].body)

        # Override Summary with the old format
        check_api.set_check_run(
            p,
            "Summary",
            "completed",
            "success",
            output={
                "title": "whatever",
                "summary": "erased"
            },
        )

        # Add a label to trigger mergify
        self.add_label(p, "stable")

        # Ensure nothing changed
        new_comments = list(p.get_issue_comments())
        self.assertEqual(len(comments), len(new_comments))
        self.assertEqual("WTF?", new_comments[-1].body)
Example #21
0
def post_summary(pull, sources, match, summary_check, conclusions):
    summary_title, summary = gen_summary(pull, sources, match)

    summary += doc.MERGIFY_PULL_REQUEST_DOC
    summary += serialize_conclusions(conclusions)

    summary_changed = (not summary_check
                       or summary_check.output["title"] != summary_title
                       or summary_check.output["summary"] != summary)

    if summary_changed:
        pull.log.debug(
            "summary changed",
            summary={
                "title": summary_title,
                "name": SUMMARY_NAME,
                "summary": summary
            },
            sources=_filterred_sources_for_logging(sources),
        )
        check_api.set_check_run(
            pull.g_pull,
            SUMMARY_NAME,
            "completed",
            "success",
            output={
                "title": summary_title,
                "summary": summary
            },
        )
    else:
        pull.log.debug(
            "summary unchanged",
            summary={
                "title": summary_title,
                "name": SUMMARY_NAME,
                "summary": summary
            },
            sources=_filterred_sources_for_logging(sources),
        )
Example #22
0
def check_configuration_changes(event_type, data, event_pull):
    if (event_type == "pull_request"
            and data["action"] in ["opened", "synchronize"]
            and event_pull.base.repo.default_branch == event_pull.base.ref):
        ref = None
        for f in event_pull.get_files():
            if f.filename == ".mergify.yml":
                ref = f.contents_url.split("?ref=")[1]

        if ref is not None:
            try:
                mergify_config = rules.get_mergify_config(event_pull.base.repo,
                                                          ref=ref)
                if "rules" in mergify_config:
                    rules.get_branch_rule(mergify_config['rules'],
                                          event_pull.base.ref)
            except rules.InvalidRules as e:  # pragma: no cover
                # Not configured, post status check with the error message
                # TODO(sileht): we can annotate the .mergify.yml file in Github
                # UI with that API
                check_api.set_check_run(
                    event_pull,
                    "future-config-checker",
                    "completed",
                    "failure",
                    output={
                        "title": "The new Mergify configuration is invalid",
                        "summary": str(e)
                    })
            else:
                check_api.set_check_run(
                    event_pull,
                    "future-config-checker",
                    "completed",
                    "success",
                    output={
                        "title": "The new Mergify configuration is valid",
                        "summary": "No action required",
                    })
Example #23
0
def copy_summary_from_previous_head_sha(ctxt, sha):
    checks = check_api.get_checks_for_ref(
        ctxt,
        sha,
        mergify_only=True,
        check_name=actions_runner.SUMMARY_NAME,
    )
    if not checks:
        ctxt.log.warning(
            "Got synchronize event but didn't find Summary on previous head sha",
        )
        return
    check_api.set_check_run(
        ctxt,
        actions_runner.SUMMARY_NAME,
        "completed",
        "success",
        output={
            "title": checks[0]["output"]["title"],
            "summary": checks[0]["output"]["summary"],
        },
    )
Example #24
0
def copy_summary_from_previous_head_sha(g_pull, sha):
    checks = check_api.get_checks_for_ref(
        g_pull.base.repo,
        sha,
        {"check_name": actions_runner.SUMMARY_NAME},
        mergify_only=True,
    )
    if not checks:
        g_pull.log.warning(
            "Got synchronize event but didn't find Summary on previous head sha",
        )
        return
    check_api.set_check_run(
        g_pull,
        actions_runner.SUMMARY_NAME,
        "completed",
        "success",
        output={
            "title": checks[0].output["title"],
            "summary": checks[0].output["summary"],
        },
    )
Example #25
0
def ensure_summary_on_head_sha(ctxt):
    for check in ctxt.pull_engine_check_runs:
        if check["name"] == actions_runner.SUMMARY_NAME:
            return

    sha = actions_runner.get_last_summary_head_sha(ctxt)
    if sha:
        previous_summary = get_summary_from_sha(ctxt, sha)
    else:
        previous_summary = get_summary_from_synchronize_event(ctxt)

    if previous_summary:
        check_api.set_check_run(
            ctxt,
            actions_runner.SUMMARY_NAME,
            "completed",
            "success",
            output={
                "title": previous_summary["output"]["title"],
                "summary": previous_summary["output"]["summary"],
            },
        )
        actions_runner.save_last_summary_head_sha(ctxt)
Example #26
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)
Example #27
0
    async def set_summary_check(
            self, result: check_api.Result) -> github_types.GitHubCheckRun:
        """Set the Mergify Summary check result."""

        previous_sha = await self.get_cached_last_summary_head_sha()
        # NOTE(sileht): we first commit in redis the future sha,
        # so engine.create_initial_summary() cannot creates a second SUMMARY
        self._save_cached_last_summary_head_sha(self.pull["head"]["sha"])

        try:
            return check_api.set_check_run(self, self.SUMMARY_NAME, result)
        except Exception:
            if previous_sha:
                # Restore previous sha in redis
                self._save_cached_last_summary_head_sha(previous_sha)
            raise
 def test_truncated_check_output(self):
     # not used anyhow
     rules = {
         "pull_request_rules": [{
             "name": "noop",
             "conditions": [],
             "actions": {}
         }]
     }
     self.setup_repo(yaml.dump(rules))
     pr, commits = self.create_pr()
     check = check_api.set_check_run(pr, "Test", "completed", "success", {
         "summary": "a" * 70000,
         "title": "bla"
     })
     assert check.output["summary"] == ("a" * 65532 + "…")
Example #29
0
def check_configuration_changes(event_type, data, event_pull):
    if event_pull.base.repo.default_branch == event_pull.base.ref:
        ref = None
        for f in event_pull.get_files():
            if f.filename == ".mergify.yml":
                ref = f.contents_url.split("?ref=")[1]

        if ref is not None:
            try:
                mergify_config = rules.get_mergify_config(event_pull.base.repo,
                                                          ref=ref)
                if "rules" in mergify_config:
                    rules.get_branch_rule(mergify_config['rules'],
                                          event_pull.base.ref)
            except rules.InvalidRules as e:  # pragma: no cover
                # Not configured, post status check with the error message
                # TODO(sileht): we can annotate the .mergify.yml file in Github
                # UI with that API
                check_api.set_check_run(
                    event_pull,
                    "Mergify — future config checker",
                    "completed",
                    "failure",
                    output={
                        "title": "The new Mergify configuration is invalid",
                        "summary": str(e)
                    })
            else:
                check_api.set_check_run(
                    event_pull,
                    "Mergify — future config checker",
                    "completed",
                    "success",
                    output={
                        "title": "The new Mergify configuration is valid",
                        "summary": "No action required",
                    })

            check_api.set_check_run(
                event_pull,
                "Mergify — disabled due to configuration change",
                "completed",
                "success",
                output={
                    "title": "Mergify configuration has been modified",
                    "summary": "The pull request needs to be merged manually",
                })

            return True
    return False
Example #30
0
def run(event_type, data):
    """Everything starts here."""
    installation_id = data["installation"]["id"]
    installation_token = utils.get_installation_token(installation_id)
    if not installation_token:
        return

    g = github.Github(installation_token,
                      base_url="https://api.%s" % config.GITHUB_DOMAIN)

    if config.LOG_RATELIMIT:  # pragma: no cover
        rate = g.get_rate_limit().rate
        LOG.info("ratelimit: %s/%s, reset at %s",
                 rate.remaining, rate.limit, rate.reset,
                 repository=data["repository"]["name"])

    repo = g.get_repo(data["repository"]["owner"]["login"] + "/" +
                      data["repository"]["name"])

    event_pull = get_github_pull_from_event(repo, event_type, data)

    if not event_pull:  # pragma: no cover
        LOG.info("No pull request found in the event %s, "
                 "ignoring", event_type)
        return

    LOG.info("Pull request found in the event %s", event_type,
             repo=repo.full_name,
             pull_request=event_pull)

    subscription = sub_utils.get_subscription(utils.get_redis_for_cache(),
                                              installation_id)

    if repo.private and not subscription["subscription_active"]:
        check_api.set_check_run(
            event_pull, "Summary",
            "completed", "failure", output={
                "title": "Mergify is disabled",
                "summary": subscription["subscription_reason"],
            })
        return

    if ("base" not in event_pull.raw_data or
            "repo" not in event_pull.raw_data["base"] or
            len(list(event_pull.raw_data["base"]["repo"].keys())) < 70):
        LOG.warning("the pull request payload looks suspicious",
                    event_type=event_type,
                    data=data,
                    pull_request=event_pull.raw_data,
                    repo=repo.fullname)

    if (event_type == "status" and
            event_pull.head.sha != data["sha"]):  # pragma: no cover
        LOG.info("No need to proceed queue (got status of an old commit)",
                 repo=repo.full_name,
                 pull_request=event_pull)
        return

    elif (event_type in ["status", "check_suite", "check_run"] and
          event_pull.merged):  # pragma: no cover
        LOG.info("No need to proceed queue (got status of a merged "
                 "pull request)",
                 repo=repo.full_name,
                 pull_request=event_pull)
        return
    elif (event_type in ["check_suite", "check_run"] and
          event_pull.head.sha != data[event_type]["head_sha"]
          ):  # pragma: no cover
        LOG.info("No need to proceed queue (got %s of an old "
                 "commit)", event_type,
                 repo=repo.full_name,
                 pull_request=event_pull)
        return

    if check_configuration_changes(event_pull):
        LOG.info("Configuration changed, ignoring",
                 repo=repo.full_name,
                 pull_request=event_pull)
        return

    # BRANCH CONFIGURATION CHECKING
    try:
        mergify_config = rules.get_mergify_config(repo)
    except rules.NoRules:  # pragma: no cover
        LOG.info("No need to proceed queue (.mergify.yml is missing)",
                 repo=repo.full_name,
                 pull_request=event_pull)
        return
    except rules.InvalidRules as e:  # pragma: no cover
        # Not configured, post status check with the error message
        if (event_type == "pull_request" and
                data["action"] in ["opened", "synchronize"]):
            check_api.set_check_run(
                event_pull, "Summary", "completed",
                "failure", output={
                    "title": "The Mergify configuration is invalid",
                    "summary": str(e)
                })
        return

    create_metrics(event_type, data)

    v2.handle.s(
        installation_id,
        mergify_config["pull_request_rules"].as_dict(),
        event_type, data, event_pull.raw_data
    ).apply_async()