예제 #1
0
def update(pull, token, method="merge"):
    # NOTE(sileht):
    # $ curl https://api.github.com/repos/sileht/repotest/pulls/2 | jq .commits
    # 2
    # $ git clone https://[email protected]/sileht-tester/repotest \
    #           --depth=$((2 + 1)) -b sileht/testpr
    # $ cd repotest
    # $ git remote add upstream https://[email protected]/sileht/repotest.git
    # $ git log | grep Date | tail -1
    # Date:   Fri Mar 30 21:30:26 2018 (10 days ago)
    # $ git fetch upstream master --shallow-since="Fri Mar 30 21:30:26 2018"
    # $ git rebase upstream/master
    # $ git push origin sileht/testpr:sileht/testpr

    head_repo = pull.g_pull.head.repo.full_name
    base_repo = pull.g_pull.base.repo.full_name

    head_branch = pull.g_pull.head.ref
    base_branch = pull.g_pull.base.ref

    git = utils.Gitter()
    try:
        git("init")
        git.configure()
        git.add_cred(token, "", head_repo)
        git.add_cred(token, "", base_repo)
        git("remote", "add", "origin",
            "https://%s/%s" % (config.GITHUB_DOMAIN, head_repo))
        git("remote", "add", "upstream",
            "https://%s/%s" % (config.GITHUB_DOMAIN, base_repo))

        depth = int(pull.g_pull.commits) + 1
        git("fetch", "--quiet", "--depth=%d" % depth, "origin", head_branch)
        git("checkout", "-q", "-b", head_branch, "origin/%s" % head_branch)

        out = git("log", "--format=%cI")
        last_commit_date = [
            d for d in out.decode("utf8").split("\n") if d.strip()
        ][-1]

        git("fetch", "--quiet", "upstream", pull.g_pull.base.ref,
            "--shallow-since='%s'" % last_commit_date)

        if method == "merge":
            git("merge", "--quiet", "upstream/%s" % base_branch, "-m",
                "Merge branch '%s' into '%s'" % (base_branch, head_branch))
            git("push", "--quiet", "origin", head_branch)
        elif method == "rebase":
            git("rebase", "upstream/%s" % base_branch)
            git("push", "--quiet", "origin", head_branch, "-f")
        else:
            raise RuntimeError("Invalid branch update method")

        return git("log", "-1", "--format=%H").decode().strip()
    except Exception:  # pragma: no cover
        LOG.error("update branch fail", pull_request=pull, exc_info=True)
    finally:
        git.cleanup()
예제 #2
0
def update_branch(self, token, merge=True):
    # NOTE(sileht):
    # $ curl https://api.github.com/repos/sileht/repotest/pulls/2 | jq .commits
    # 2
    # $ git clone https://[email protected]/sileht-tester/repotest \
    #           --depth=$((2 + 1)) -b sileht/testpr
    # $ cd repotest
    # $ git remote add upstream https://[email protected]/sileht/repotest.git
    # $ git log | grep Date | tail -1
    # Date:   Fri Mar 30 21:30:26 2018 (10 days ago)
    # $ git fetch upstream master --shallow-since="Fri Mar 30 21:30:26 2018"
    # $ git rebase upstream/master
    # $ git push origin sileht/testpr:sileht/testpr

    git = utils.Gitter()
    try:
        git("clone", "--depth=%d" % (int(self.commits) + 1),
            "-b", self.head.ref,
            "https://%[email protected]/%s/" % (token, self.head.repo.full_name),
            ".")
        git("remote", "add", "upstream",
            "https://%[email protected]/%s.git" % (token, self.base.repo.full_name))
        git("config", "user.name", "%s-bot" % config.CONTEXT)
        git("config", "user.email", "*****@*****.**")

        out = git("log", "--pretty='format:%cI'")
        last_commit_date = out.decode("utf8").split("\n")[-1]

        git("fetch", "upstream", self.base.ref,
            "--shallow-since='%s'" % last_commit_date)
        if merge:
            git("merge", "upstream/%s" % self.base.ref, "-m",
                "Merge branch '%s' into '%s'" % (self.base.ref, self.head.ref))
        else:
            # TODO(sileht): This will removes approvals, we need to add them
            # back
            git("rebase", "upstream/%s" % self.base.ref)
        git("push", "origin", self.head.ref)
    except Exception:
        LOG.exception("git rebase fail")
        return False
    finally:
        git.cleanup()
    return True
예제 #3
0
def _do_update(pull, token, method="merge"):
    # NOTE(sileht):
    # $ curl https://api.github.com/repos/sileht/repotest/pulls/2 | jq .commits
    # 2
    # $ git clone https://[email protected]/sileht-tester/repotest \
    #           --depth=$((2 + 1)) -b sileht/testpr
    # $ cd repotest
    # $ git remote add upstream https://[email protected]/sileht/repotest.git
    # $ git log | grep Date | tail -1
    # Date:   Fri Mar 30 21:30:26 2018 (10 days ago)
    # $ git fetch upstream master --shallow-since="Fri Mar 30 21:30:26 2018"
    # $ git rebase upstream/master
    # $ git push origin sileht/testpr:sileht/testpr

    head_repo = pull.g_pull.head.repo.full_name
    base_repo = pull.g_pull.base.repo.full_name

    head_branch = pull.g_pull.head.ref
    base_branch = pull.g_pull.base.ref
    git = utils.Gitter()
    try:
        git("init")
        git.configure()
        git.add_cred(token, "", head_repo)
        git.add_cred(token, "", base_repo)
        git("remote", "add", "origin",
            "https://%s/%s" % (config.GITHUB_DOMAIN, head_repo))
        git("remote", "add", "upstream",
            "https://%s/%s" % (config.GITHUB_DOMAIN, base_repo))

        depth = int(pull.g_pull.commits) + 1
        git("fetch", "--quiet", "--depth=%d" % depth, "origin", head_branch)
        git("checkout", "-q", "-b", head_branch, "origin/%s" % head_branch)

        out = git("log", "--format=%cI")
        last_commit_date = [
            d for d in out.decode("utf8").split("\n") if d.strip()
        ][-1]

        git("fetch", "--quiet", "upstream", base_branch,
            "--shallow-since='%s'" % last_commit_date)

        try:
            _do_update_branch(git, method, base_branch, head_branch)
        except subprocess.CalledProcessError as e:  # pragma: no cover
            if b"unrelated histories" in e.output:
                LOG.debug("Complete history cloned", pull_request=pull)
                # NOTE(sileht): We currently assume we have only one parent
                # commit in common. Since Git is a graph, in some case this
                # graph can be more complicated.
                # So, retrying with the whole git history for now
                git("fetch", "--quiet", "origin", head_branch)
                git("fetch", "--quiet", "upstream", base_branch)
                _do_update_branch(git, method, base_branch, head_branch)
            else:
                raise

        return git("log", "-1", "--format=%H").decode().strip()
    except subprocess.CalledProcessError as e:  # pragma: no cover
        for message in AUTHENTICATION_FAILURE_MESSAGES:
            if message in e.output:
                raise AuthentificationFailure(e.output)
        else:
            LOG.error("update branch failed: %s",
                      e.output,
                      pull_request=pull,
                      exc_info=True)
    except Exception:  # pragma: no cover
        LOG.error("update branch failed", pull_request=pull, exc_info=True)
    finally:
        git.cleanup()
예제 #4
0
def duplicate(pull, branch, installation_token, kind=BACKPORT):
    """Duplicate a pull request.

    :param repo: The repository.
    :param pull: The pull request.
    :type pull: py:class:mergify_engine.mergify_pull.MergifyPull
    :param branch_name: The branch name to copy to.
    :param installation_token: The installation token.
    :param kind: is a backport or a copy
    """
    repo = pull.g_pull.base.repo

    bp_branch = get_destination_branch_name(pull, branch, kind)

    cherry_pick_fail = False
    body = "This is an automated %s of pull request #%d done " "by Mergify.io" % (
        kind,
        pull.g_pull.number,
    )

    git = utils.Gitter()

    # TODO(sileht): This can be done with the Github API only I think:
    # An example:
    # https://github.com/shiqiyang-okta/ghpick/blob/master/ghpick/cherry.py
    try:
        git("init")
        git.configure()
        git.add_cred("x-access-token", installation_token, repo.full_name)
        git(
            "remote",
            "add",
            "origin",
            "https://%s/%s" % (config.GITHUB_DOMAIN, repo.full_name),
        )

        git("fetch", "--quiet", "origin", "pull/%s/head" % pull.g_pull.number)
        git("fetch", "--quiet", "origin", pull.g_pull.base.ref)
        git("fetch", "--quiet", "origin", branch.name)
        git("checkout", "--quiet", "-b", bp_branch, "origin/%s" % branch.name)

        merge_commit = repo.get_commit(pull.g_pull.merge_commit_sha)
        for commit in _get_commits_to_cherrypick(pull, merge_commit):
            # FIXME(sileht): Github does not allow to fetch only one commit
            # So we have to fetch the branch since the commit date ...
            # git("fetch", "origin", "%s:refs/remotes/origin/%s-commit" %
            #    (commit.sha, commit.sha)
            #    )
            # last_commit_date = commit.commit.committer.date
            # git("fetch", "origin", pull.base.ref,
            #    "--shallow-since='%s'" % last_commit_date)
            try:
                git("cherry-pick", "-x", commit.sha)
            except subprocess.CalledProcessError as e:  # pragma: no cover
                pull.log.debug("fail to cherry-pick %s: %s", commit.sha,
                               e.output)
                cherry_pick_fail = True
                status = git("status").decode("utf8")
                git("add", "*")
                git("commit", "-a", "--no-edit", "--allow-empty")

                body += "\n\nCherry-pick of %s has failed:\n```\n%s```\n\n" % (
                    commit.sha,
                    status,
                )

        git("push", "origin", bp_branch)
    except subprocess.CalledProcessError as in_exception:  # pragma: no cover
        for message, out_exception in GIT_MESSAGE_TO_EXCEPTION.items():
            if message in in_exception.output:
                if out_exception is None:
                    return
                else:
                    raise out_exception(in_exception.output.decode())
        else:
            pull.log.error(
                "duplicate failed: %s",
                in_exception.output.decode(),
                branch=branch.name,
                kind=kind,
                exc_info=True,
            )
            return
    except Exception:  # pragma: no cover
        pull.log.error(
            "duplicate failed",
            pull_request=pull,
            branch=branch.name,
            kind=kind,
            exc_info=True,
        )
        return
    finally:
        git.cleanup()

    if cherry_pick_fail:
        body += ("To fixup this pull request, you can check out it locally. "
                 "See documentation: "
                 "https://help.github.com/articles/"
                 "checking-out-pull-requests-locally/")

    try:
        return repo.create_pull(
            title="{} ({} #{})".format(pull.g_pull.title,
                                       BRANCH_PREFIX_MAP[kind],
                                       pull.g_pull.number),
            body=body + "\n---\n\n" + doc.MERGIFY_PULL_REQUEST_DOC,
            base=branch.name,
            head=bp_branch,
        )
    except github.GithubException as e:
        if e.status == 422 and "No commits between" in e.data["message"]:
            return
        raise
예제 #5
0
def duplicate(ctxt,
              branch_name,
              label_conflicts=None,
              ignore_conflicts=False,
              kind=BACKPORT):
    """Duplicate a pull request.

    :param pull: The pull request.
    :type pull: py:class:mergify_engine.context.Context
    :param branch: The branch to copy to.
    :param label_conflicts: The label to add to the created PR when cherry-pick failed.
    :param ignore_conflicts: Whether to commit the result if the cherry-pick fails.
    :param kind: is a backport or a copy
    """
    repo_full_name = ctxt.pull["base"]["repo"]["full_name"]
    bp_branch = get_destination_branch_name(ctxt.pull["number"], branch_name,
                                            kind)

    cherry_pick_fail = False
    body = ""

    git = utils.Gitter(ctxt.log)

    repo_info = ctxt.client.item(f"/repos/{repo_full_name}")
    if repo_info["size"] > config.NOSUB_MAX_REPO_SIZE_KB:
        if not ctxt.subscription.has_feature(
                subscription.Features.LARGE_REPOSITORY):
            ctxt.log.warning(
                "repository too big and no subscription active, refusing to %s",
                kind,
                size=repo_info["size"],
            )
            raise DuplicateFailed(
                f"{kind} fail: repository is too big and no subscription is active"
            )
        ctxt.log.info("running %s on large repository", kind)

    # TODO(sileht): This can be done with the Github API only I think:
    # An example:
    # https://github.com/shiqiyang-okta/ghpick/blob/master/ghpick/cherry.py
    try:
        token = ctxt.client.auth.get_access_token()
        git("init")
        git.configure()
        git.add_cred("x-access-token", token, repo_full_name)
        git("remote", "add", "origin", f"{config.GITHUB_URL}/{repo_full_name}")
        git("fetch", "--quiet", "origin", "pull/%s/head" % ctxt.pull["number"])
        git("fetch", "--quiet", "origin", ctxt.pull["base"]["ref"])
        git("fetch", "--quiet", "origin", branch_name)
        git("checkout", "--quiet", "-b", bp_branch, "origin/%s" % branch_name)

        merge_commit = ctxt.client.item(
            f"{ctxt.base_url}/commits/{ctxt.pull['merge_commit_sha']}")
        for commit in _get_commits_to_cherrypick(ctxt, merge_commit):
            # FIXME(sileht): Github does not allow to fetch only one commit
            # So we have to fetch the branch since the commit date ...
            # git("fetch", "origin", "%s:refs/remotes/origin/%s-commit" %
            #    (commit["sha"], commit["sha"])
            #    )
            # last_commit_date = commit["commit"]["committer"]["date"]
            # git("fetch", "origin", ctxt.pull["base"]["ref"],
            #    "--shallow-since='%s'" % last_commit_date)
            try:
                git("cherry-pick", "-x", commit["sha"])
            except subprocess.CalledProcessError as e:  # pragma: no cover
                ctxt.log.info("fail to cherry-pick %s: %s", commit["sha"],
                              e.output)
                git_status = git("status").decode("utf8")
                body += f"\n\nCherry-pick of {commit['sha']} has failed:\n```\n{git_status}```\n\n"
                if not ignore_conflicts:
                    raise DuplicateFailed(body)
                cherry_pick_fail = True
                git("add", "*")
                git("commit", "-a", "--no-edit", "--allow-empty")

        git("push", "origin", bp_branch)
    except subprocess.CalledProcessError as in_exception:  # pragma: no cover
        for message, out_exception in GIT_MESSAGE_TO_EXCEPTION.items():
            if message in in_exception.output:
                if out_exception is None:
                    return
                else:
                    raise out_exception(in_exception.output.decode())
        else:
            ctxt.log.error(
                "duplicate failed: %s",
                in_exception.output.decode(),
                branch=branch_name,
                kind=kind,
                exc_info=True,
            )
            return
    finally:
        git.cleanup()

    body = (
        f"This is an automated {kind} of pull request #{ctxt.pull['number']} done by Mergify"
        + body)

    if cherry_pick_fail:
        body += ("To fixup this pull request, you can check out it locally. "
                 "See documentation: "
                 "https://help.github.com/articles/"
                 "checking-out-pull-requests-locally/")

    try:
        duplicate_pr = ctxt.client.post(
            f"{ctxt.base_url}/pulls",
            json={
                "title":
                "{} ({} #{})".format(ctxt.pull["title"],
                                     BRANCH_PREFIX_MAP[kind],
                                     ctxt.pull["number"]),
                "body":
                body + "\n---\n\n" + doc.MERGIFY_PULL_REQUEST_DOC,
                "base":
                branch_name,
                "head":
                bp_branch,
            },
        ).json()
    except http.HTTPClientSideError as e:
        if e.status_code == 422 and "No commits between" in e.message:
            return
        raise

    if cherry_pick_fail and label_conflicts is not None:
        ctxt.client.post(
            f"{ctxt.base_url}/issues/{duplicate_pr['number']}/labels",
            json={"labels": [label_conflicts]},
        )

    return duplicate_pr
예제 #6
0
def _do_update(ctxt, token, method="merge"):
    # NOTE(sileht):
    # $ curl https://api.github.com/repos/sileht/repotest/pulls/2 | jq .commits
    # 2
    # $ git clone https://[email protected]/sileht-tester/repotest \
    #           --depth=$((2 + 1)) -b sileht/testpr
    # $ cd repotest
    # $ git remote add upstream https://[email protected]/sileht/repotest.git
    # $ git log | grep Date | tail -1
    # Date:   Fri Mar 30 21:30:26 2018 (10 days ago)
    # $ git fetch upstream master --shallow-since="Fri Mar 30 21:30:26 2018"
    # $ git rebase upstream/master
    # $ git push origin sileht/testpr:sileht/testpr

    head_repo = (ctxt.pull["head"]["repo"]["owner"]["login"] + "/" +
                 ctxt.pull["head"]["repo"]["name"])
    base_repo = (ctxt.pull["base"]["repo"]["owner"]["login"] + "/" +
                 ctxt.pull["base"]["repo"]["name"])

    head_branch = ctxt.pull["head"]["ref"]
    base_branch = ctxt.pull["base"]["ref"]
    git = utils.Gitter(ctxt.log)
    try:
        git("init")
        git.configure()
        git.add_cred(token, "", head_repo)
        git.add_cred(token, "", base_repo)
        git("remote", "add", "origin", f"{config.GITHUB_URL}/{head_repo}")
        git("remote", "add", "upstream", f"{config.GITHUB_URL}/{base_repo}")

        depth = len(ctxt.commits) + 1
        git("fetch", "--quiet", "--depth=%d" % depth, "origin", head_branch)
        git("checkout", "-q", "-b", head_branch, "origin/%s" % head_branch)

        out = git("log", "--format=%cI")
        last_commit_date = [
            d for d in out.decode("utf8").split("\n") if d.strip()
        ][-1]

        git(
            "fetch",
            "--quiet",
            "upstream",
            base_branch,
            "--shallow-since='%s'" % last_commit_date,
        )

        # Try to find the merge base, but don't fetch more that 1000 commits.
        for _ in range(20):
            git("repack", "-d")
            if git("merge-base", f"upstream/{base_branch}",
                   f"origin/{head_branch}"):
                break
            git("fetch", "-q", "--deepen=50", "upsteam", base_branch)

        try:
            _do_update_branch(git, method, base_branch, head_branch)
        except subprocess.CalledProcessError as e:  # pragma: no cover
            for message in GIT_MESSAGE_TO_UNSHALLOW:
                if message in e.output:
                    ctxt.log.info("Complete history cloned")
                    # NOTE(sileht): We currently assume we have only one parent
                    # commit in common. Since Git is a graph, in some case this
                    # graph can be more complicated.
                    # So, retrying with the whole git history for now
                    git("fetch", "--unshallow")
                    git("fetch", "--quiet", "origin", head_branch)
                    git("fetch", "--quiet", "upstream", base_branch)
                    _do_update_branch(git, method, base_branch, head_branch)
                    break
            else:
                raise

        expected_sha = git("log", "-1", "--format=%H").decode().strip()
        # NOTE(sileht): We store this for dismissal action
        with utils.get_redis_for_cache() as redis:
            redis.setex("branch-update-%s" % expected_sha, 60 * 60,
                        expected_sha)
    except subprocess.CalledProcessError as in_exception:  # pragma: no cover
        for message, out_exception in GIT_MESSAGE_TO_EXCEPTION.items():
            if message in in_exception.output:
                raise out_exception(
                    "Git reported the following error:\n"
                    f"```\n{in_exception.output.decode()}\n```\n")
        else:
            ctxt.log.error(
                "update branch failed: %s",
                in_exception.output.decode(),
                exc_info=True,
            )
            raise BranchUpdateFailure()

    except Exception:  # pragma: no cover
        ctxt.log.error("update branch failed", exc_info=True)
        raise BranchUpdateFailure()
    finally:
        git.cleanup()
예제 #7
0
def _do_update(pull, token, method="merge"):
    # NOTE(sileht):
    # $ curl https://api.github.com/repos/sileht/repotest/pulls/2 | jq .commits
    # 2
    # $ git clone https://[email protected]/sileht-tester/repotest \
    #           --depth=$((2 + 1)) -b sileht/testpr
    # $ cd repotest
    # $ git remote add upstream https://[email protected]/sileht/repotest.git
    # $ git log | grep Date | tail -1
    # Date:   Fri Mar 30 21:30:26 2018 (10 days ago)
    # $ git fetch upstream master --shallow-since="Fri Mar 30 21:30:26 2018"
    # $ git rebase upstream/master
    # $ git push origin sileht/testpr:sileht/testpr

    head_repo = pull.head_repo_owner_login + "/" + pull.head_repo_name
    base_repo = pull.base_repo_owner_login + "/" + pull.base_repo_name

    head_branch = pull.head_ref
    base_branch = pull.base_ref
    git = utils.Gitter()
    try:
        git("init")
        git.configure()
        git.add_cred(token, "", head_repo)
        git.add_cred(token, "", base_repo)
        git(
            "remote",
            "add",
            "origin",
            "https://%s/%s" % (config.GITHUB_DOMAIN, head_repo),
        )
        git(
            "remote",
            "add",
            "upstream",
            "https://%s/%s" % (config.GITHUB_DOMAIN, base_repo),
        )

        depth = len(pull.commits) + 1
        git("fetch", "--quiet", "--depth=%d" % depth, "origin", head_branch)
        git("checkout", "-q", "-b", head_branch, "origin/%s" % head_branch)

        out = git("log", "--format=%cI")
        last_commit_date = [
            d for d in out.decode("utf8").split("\n") if d.strip()
        ][-1]

        git(
            "fetch",
            "--quiet",
            "upstream",
            base_branch,
            "--shallow-since='%s'" % last_commit_date,
        )

        try:
            _do_update_branch(git, method, base_branch, head_branch)
        except subprocess.CalledProcessError as e:  # pragma: no cover
            for message in GIT_MESSAGE_TO_UNSHALLOW:
                if message in e.output:
                    pull.log.debug("Complete history cloned")
                    # NOTE(sileht): We currently assume we have only one parent
                    # commit in common. Since Git is a graph, in some case this
                    # graph can be more complicated.
                    # So, retrying with the whole git history for now
                    git("fetch", "--unshallow")
                    git("fetch", "--quiet", "origin", head_branch)
                    git("fetch", "--quiet", "upstream", base_branch)
                    _do_update_branch(git, method, base_branch, head_branch)
                    break
            else:
                raise

        expected_sha = git("log", "-1", "--format=%H").decode().strip()
        # NOTE(sileht): We store this for dismissal action
        redis = utils.get_redis_for_cache()
        redis.setex("branch-update-%s" % expected_sha, 60 * 60, expected_sha)
    except subprocess.CalledProcessError as in_exception:  # pragma: no cover
        for message, out_exception in GIT_MESSAGE_TO_EXCEPTION.items():
            if message in in_exception.output:
                raise out_exception(in_exception.output.decode())
        else:
            pull.log.error(
                "update branch failed: %s",
                in_exception.output.decode(),
                exc_info=True,
            )
            raise BranchUpdateFailure()

    except Exception:  # pragma: no cover
        pull.log.error("update branch failed",
                       pull_request=pull,
                       exc_info=True)
        raise BranchUpdateFailure()
    finally:
        git.cleanup()
예제 #8
0
def backport(pull, branch, installation_token):
    """Backport a pull request.

    :param repo: The repository.
    :param pull: The pull request.
    :type pull: py:class:mergify_engine.mergify_pull.MergifyPull
    :param branch_name: The branch name to backport to.
    :param installation_token: The installation token.
    """
    repo = pull.g_pull.base.repo

    bp_branch = "mergify/bp/%s/pr-%s" % (branch.name, pull.g_pull.number)

    cherry_pick_fail = False
    body = ("This is an automated backport of pull request #%d done "
            "by Mergify.io" % pull.g_pull.number)

    git = utils.Gitter()

    # TODO(sileht): This can be done with the Github API only I think:
    # An example:
    # https://github.com/shiqiyang-okta/ghpick/blob/master/ghpick/cherry.py
    try:
        git("init")
        git.configure()
        git.add_cred("x-access-token", installation_token, repo.full_name)
        git("remote", "add", "origin", "https://%s/%s" % (config.GITHUB_DOMAIN,
                                                          repo.full_name))

        git("fetch", "--quiet", "origin", "pull/%s/head" % pull.g_pull.number)
        git("fetch", "--quiet", "origin", pull.g_pull.base.ref)
        git("fetch", "--quiet", "origin", branch.name)
        git("checkout", "--quiet", "-b", bp_branch, "origin/%s" % branch.name)

        merge_commit = repo.get_commit(pull.g_pull.merge_commit_sha)
        for commit in _get_commits_to_cherrypick(pull, merge_commit):
            # FIXME(sileht): Github does not allow to fetch only one commit
            # So we have to fetch the branch since the commit date ...
            # git("fetch", "origin", "%s:refs/remotes/origin/%s-commit" %
            #    (commit.sha, commit.sha)
            #    )
            # last_commit_date = commit.commit.committer.date
            # git("fetch", "origin", pull.base.ref,
            #    "--shallow-since='%s'" % last_commit_date)
            try:
                git("cherry-pick", "-x", commit.sha)
            except subprocess.CalledProcessError:
                cherry_pick_fail = True
                status = git("status").decode("utf8")
                git("add", "*")
                git("commit", "-a", "--no-edit", "--allow-empty")

                body += ("\n\nCherry-pick of %s have failed:\n```\n%s```\n\n"
                         % (commit.sha, status))

        git("push", "origin", bp_branch)
    except Exception:  # pragma: no cover
        LOG.error("backport failed", pull_request=pull, branch=branch.name,
                  exc_info=True)
        return
    finally:
        git.cleanup()

    if cherry_pick_fail:
        body += (
            "To fixup this pull request, you can check out it locally. "
            "See documentation: "
            "https://help.github.com/articles/"
            "checking-out-pull-requests-locally/")

    return repo.create_pull(
        title="Automatic backport of pull request #%d" % pull.g_pull.number,
        body=body,
        base=branch.name,
        head=bp_branch,
    )
예제 #9
0
def _backport(repo, pull, branch_name, installation_token):
    try:
        branch = repo.get_branch(branch_name)
    except github.GithubException as e:
        # NOTE(sileht): PyGitHub is buggy here it should
        # UnknownObjectException. but because the message is "Branch not
        # found", instead of "Not found", we got the generic exception.
        if e.status != 404:  # pragma: no cover
            raise

        LOG.info("%s doesn't exist for repo %s", branch_name, repo.full_name)
        return

    bp_branch = "mergify/bp/%s/pr-%s" % (branch_name, pull.number)

    cherry_pick_fail = False
    body = ("This is an automated backport of pull request #%d done "
            "by Mergify.io" % pull.number)

    git = utils.Gitter()

    # TODO(sileht): This can be done with the Github API only I think:
    # An example:
    # https://github.com/shiqiyang-okta/ghpick/blob/master/ghpick/cherry.py
    try:
        git(
            "clone", "-b", branch_name,
            "https://*****:*****@github.com/%s/" %
            (installation_token, repo.full_name), ".")
        git("branch", "-M", bp_branch)
        git("config", "user.name", "%s-bot" % config.CONTEXT)
        git("config", "user.email", config.GIT_EMAIL)
        merge_commit = repo.get_commit(pull.merge_commit_sha)
        for commit in _get_commits_to_cherrypick(pull, merge_commit):
            # FIXME(sileht): Github does not allow to fetch only one commit
            # So we have to fetch the branch since the commit date ...
            # git("fetch", "origin", "%s:refs/remotes/origin/%s-commit" %
            #    (commit.sha, commit.sha)
            #    )
            # last_commit_date = commit.commit.committer.date
            # git("fetch", "origin", pull.base.ref,
            #    "--shallow-since='%s'" % last_commit_date)
            try:
                git("cherry-pick", "-x", commit.sha)
            except subprocess.CalledProcessError:
                cherry_pick_fail = True
                status = git("status").decode("utf8")
                git("add", "*")
                git("commit", "-a", "--no-edit")

                body += ("\n\nCherry-pick of %s have failed:\n```\n%s```\n\n" %
                         (commit.sha, status))

        git("push", "origin", bp_branch)
    except Exception:
        LOG.error("%s: backport to branch %s fail",
                  pull.pretty(),
                  branch.name,
                  exc_info=True)
        return
    finally:
        git.cleanup()

    if cherry_pick_fail:
        body += ("To fixup this pull request, you can check out it locally. "
                 "See documentation: "
                 "https://help.github.com/articles/"
                 "checking-out-pull-requests-locally/")

    repo.create_pull(
        title="Automatic backport of pull request #%d" % pull.number,
        body=body,
        base=branch.name,
        head=bp_branch,
    )