Пример #1
0
    def test_push_branch_with_retry(self, mock_sleep, mock_git):
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'womp'
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_ret_f = mock.Mock()
        mock_ret_f.flags = 1
        mock_ret_f.ERROR = 0
        mock_ret_f.summary = "mock failure summary"
        mock_ret_ok = mock.Mock()
        mock_ret_ok.flags = 0
        mock_ret_ok.ERROR = 1
        mock_repo.remotes = mock.Mock()
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock(
            side_effect=[[mock_ret_f], [mock_ret_f], [mock_ret_ok]])

        ghc = GitHubContext('REPO')

        ghc.push_branch_with_retry('womp', attempts=3, cooloff=2)
        self.assertEqual(mock_repo.remotes.origin.push.call_count, 3)

        mock_repo.remotes.origin.push = mock.Mock(
            side_effect=GitCommandError('B', 128))
        self.assertRaises(RuntimeError,
                          ghc.push_branch_with_retry,
                          'womp',
                          attempts=3,
                          cooloff=2)
Пример #2
0
    def test_push_branch_with_retry(self, mock_sleep, mock_git):
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'womp'
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_ret_f = mock.Mock()
        mock_ret_f.flags = 1
        mock_ret_f.ERROR = 0
        mock_ret_f.summary = "mock failure summary"
        mock_ret_ok = mock.Mock()
        mock_ret_ok.flags = 0
        mock_ret_ok.ERROR = 1
        mock_repo.remotes = mock.Mock()
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock(
            side_effect=[[mock_ret_f], [mock_ret_f], [mock_ret_ok]]
        )

        ghc = GitHubContext('REPO')

        ghc.push_branch_with_retry('womp', attempts=3, cooloff=2)
        self.assertEqual(mock_repo.remotes.origin.push.call_count, 3)

        mock_repo.remotes.origin.push = mock.Mock(side_effect=GitCommandError('B', 128))
        self.assertRaises(RuntimeError, ghc.push_branch_with_retry, 'womp', attempts=3, cooloff=2)
Пример #3
0
    def test_tag_release(self, mock_git):
        """test tag_release call"""
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'master'
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_tag1 = mock.Mock()
        mock_tag1.name = '0.0.0'
        mock_tag2 = mock.Mock()
        mock_tag2.name = '0.0.1'
        mock_repo.tags = [
            mock_tag1, mock_tag2
        ]
        mock_repo.create_tag = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_repo.remotes = mock.Mock()
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock()

        ghc = GitHubContext('REPO')
        ghc.tag_release('0.0.2', 'master', push=True, attempts=2, cooloff=0)

        self.failUnless(mock_repo.create_tag.called)
        self.failUnless(mock_repo.remotes.origin.push.called)
Пример #4
0
 def test_merge_branch(self, mock_git):
     """test merge branch"""
     mock_repo = mock.Mock()
     mock_repo.git = mock.Mock()
     mock_repo.git.merge = mock.Mock()
     mock_git.Repo = mock.Mock(return_value=mock_repo)
     ghc = GitHubContext('REPO')
     ghc.merge_branch('develop')
     self.assertTrue(mock_repo.git.merge.called)
Пример #5
0
 def test_merge_branch(self, mock_git):
     """test merge branch"""
     mock_repo = mock.Mock()
     mock_repo.git = mock.Mock()
     mock_repo.git.merge = mock.Mock()
     mock_git.Repo = mock.Mock(return_value=mock_repo)
     ghc = GitHubContext('REPO')
     ghc.merge_branch('develop')
     self.assertTrue(mock_repo.git.merge.called)
Пример #6
0
    def test_tag_release_retry(self, mock_time, mock_git):
        """test repeated tries to push tags"""
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'master'
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_tag1 = mock.Mock()
        mock_tag1.name = '0.0.0'
        mock_tag2 = mock.Mock()
        mock_tag2.name = '0.0.1'
        mock_repo.tags = [mock_tag1, mock_tag2]
        mock_repo.create_tag = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_repo.remotes = mock.Mock()
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock(
            side_effect=RuntimeError("push it real good"))
        mock_time.sleep = mock.Mock()

        ghc = GitHubContext('REPO')
        self.assertRaises(RuntimeError,
                          ghc.tag_release,
                          '0.0.2',
                          'master',
                          push=True,
                          attempts=5,
                          cooloff=0)
        self.assertEqual(mock_repo.remotes.origin.push.call_count, 5)
        self.assertEqual(mock_time.sleep.call_count, 5)
        self.failUnless(mock_repo.create_tag.called)
Пример #7
0
def plusone_pr(opts):
    """
    _plusone_pr_

    Set the +1 context status for the PR
    """
    repo_dir = os.getcwd()
    with GitHubContext(repo_dir) as ghc:
        ghc.plus_one_pull_request(pr_id=opts.id, context=opts.plus_one_context)
Пример #8
0
def list_feature_branches(opts):
    """
    list unmerged feature branches
    """
    repo_dir = os.getcwd()
    print "unmerged feature branches:"
    with GitHubContext(repo_dir) as ghc:
        for x in ghc.iter_git_feature_branches(merged=False):
            print x
Пример #9
0
def list_feature_branches(opts):
    """
    list unmerged feature branches
    """
    repo_dir = repo_directory()
    print("unmerged feature branches:")
    with GitHubContext(repo_dir) as ghc:
        for x in ghc.iter_git_feature_branches(merged=False):
            print(x)
Пример #10
0
def review_pr(opts):
    """
    _review_pr_

    Add a review comment to a PR, and optionally set the
    plus one context status
    """
    repo_dir = os.getcwd()
    with GitHubContext(repo_dir) as ghc:
        ghc.review_pull_request(opts.id, opts.comment, opts.plus_one,
                                opts.plus_one_context)
Пример #11
0
def get_pr(opts):
    """
    _get_pr_

    Print the details of the pr specified by the CLI opts

    """
    repo_dir = os.getcwd()
    with GitHubContext(repo_dir) as ghc:
        pr_data = ghc.pull_request_details(opts.id)
        pprint.pprint(pr_data, indent=2)
Пример #12
0
def list_prs(opts):
    """
    _list_prs_

    Print a summary of all open PRs for this repo
    optionally filtered by user

    """
    repo_dir = os.getcwd()
    with GitHubContext(repo_dir) as ghc:
        print "  ID,  User, Title"
        for pr in ghc.pull_requests(user=opts.user):
            print pr['number'], pr['user']['login'], pr['title']
Пример #13
0
    def test_push_branch(self, mock_git):
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'womp'
        mock_repo.head = "HEAD"
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_repo.remotes = mock.Mock()
        mock_ret = mock.Mock()
        mock_ret.flags = 1000
        mock_ret.ERROR = 10000
        mock_ret.summary = "SUMMARY"
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock(return_value=[mock_ret])
        ghc = GitHubContext('REPO')

        ghc.push_branch('womp')
        self.failUnless(mock_repo.remotes.origin.push.called)
        mock_repo.remotes.origin.push.assert_has_calls([mock.call('HEAD')])

        mock_repo.remotes.origin.push = mock.Mock(side_effect=GitCommandError('A', 128))
        self.assertRaises(RuntimeError, ghc.push_branch, 'womp2')
Пример #14
0
    def test_tag_release(self, mock_git):
        """test tag_release call"""
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'master'
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_tag1 = mock.Mock()
        mock_tag1.name = '0.0.0'
        mock_tag2 = mock.Mock()
        mock_tag2.name = '0.0.1'
        mock_repo.tags = [mock_tag1, mock_tag2]
        mock_repo.create_tag = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_repo.remotes = mock.Mock()
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock()

        ghc = GitHubContext('REPO')
        ghc.tag_release('0.0.2', 'master', push=True, attempts=2, cooloff=0)

        self.failUnless(mock_repo.create_tag.called)
        self.failUnless(mock_repo.remotes.origin.push.called)
Пример #15
0
    def test_push_branch(self, mock_git):
        mock_repo = mock.Mock()
        mock_repo.active_branch = mock.Mock()
        mock_repo.active_branch.name = 'womp'
        mock_repo.head = "HEAD"
        mock_repo.git = mock.Mock()
        mock_repo.git.checkout = mock.Mock()
        mock_git.Repo = mock.Mock(return_value=mock_repo)
        mock_repo.remotes = mock.Mock()
        mock_ret = mock.Mock()
        mock_ret.flags = 1000
        mock_repo.remotes.origin = mock.Mock()
        mock_repo.remotes.origin.push = mock.Mock(return_value=[mock_ret])

        ghc = GitHubContext('REPO')

        ghc.push_branch('womp')
        self.failUnless(mock_repo.remotes.origin.push.called)
        mock_repo.remotes.origin.push.assert_has_calls([mock.call('HEAD')])

        mock_repo.remotes.origin.push = mock.Mock(
            side_effect=GitCommandError('A', 128))
        self.assertRaises(RuntimeError, ghc.push_branch, 'womp2')
Пример #16
0
 def test_merge_branch_conflict(self, mock_git):
     """test merge branch"""
     mock_repo = mock.Mock()
     mock_repo.git = mock.Mock()
     mock_repo.git.merge = mock.Mock(
         side_effect=GitCommandError(mock.Mock(), mock.Mock(), mock.Mock()))
     mock_repo.active_branch = mock.Mock()
     mock_repo.active_branch.name = "ACTIVE"
     mock_repo.index = mock.Mock()
     mock_repo.index.unmerged_blobs = mock.Mock(return_value={
         'file1': [(1, "BLOB1")],
         'file2': [(2, "BLOB2")]
     })
     mock_git.Repo = mock.Mock(return_value=mock_repo)
     ghc = GitHubContext('REPO')
     self.assertRaises(GitCommandError, ghc.merge_branch, 'develop')
Пример #17
0
def cleanup_release(opts):
    """
    _cleanup_release_

    Remove local and remote release branches if they exist

    """
    config = load_configuration()
    repo_dir = os.getcwd()
    pfix = config.gitflow_release_prefix()
    branch_name = release_branch_name(config)

    if opts.version is not None:
        if not opts.version.startswith(pfix):
            branch_name = "{0}{1}".format(pfix, opts.version)
        else:
            branch_name = opts.version
    LOGGER.info("Cleaning release branches for {}".format(branch_name))
    with GitHubContext(repo_dir) as ghc:
        ghc.delete_branch(branch_name, not opts.no_remote)
Пример #18
0
def merge_feature_branch(opts):
    """
    merge current feature branch into develop
    """
    config = load_configuration()
    main_branch = config.gitflow_branch_name()
    repo_dir = repo_directory()
    curr_branch = current_branch(repo_dir)
    LOGGER.info("Merging {} into {}".format(curr_branch, main_branch))
    # make sure repo is clean
    if has_unstaged_changes(repo_dir):
        msg = ("Error: Unstaged changes are present on the feature branch {}"
               "Please commit them or clean up before proceeding"
               ).format(curr_branch)
        LOGGER.error(msg)
        raise RuntimeError(msg)

    checkout_and_pull(repo_dir, main_branch, pull=not opts.no_remote)
    with GitHubContext(repo_dir) as ghc:
        ghc.merge_branch(curr_branch)
        if not opts.no_remote:
            ghc.push_branch(main_branch)
            LOGGER.info("Branch {0} pushed to remote".format(main_branch))
Пример #19
0
def merge_release(opts):
    """
    _merge_release_

    Merge a release branch git flow style into master and develop
    branches (or those configured for this package) and tag
    master.

    """
    config = load_configuration()
    rel_conf = release_config(config, opts)
    repo_dir = os.getcwd()
    tag = config.package_version()
    master = config.gitflow_master_name()
    develop = config.gitflow_branch_name()

    with GitHubContext(repo_dir) as ghc:

        release_branch = ghc.active_branch_name
        expected_branch = release_branch_name(config)
        if release_branch != expected_branch:
            msg = (u"Not on the expected release branch according "
                   u"to cirrus.conf\n Expected:{0} but on {1}").format(
                       expected_branch, release_branch)
            LOGGER.error(msg)
            raise RuntimeError(msg)

        # merge release branch into master
        LOGGER.info(u"Tagging and pushing {0}".format(tag))
        if opts.skip_master:
            LOGGER.info(u'Skipping merging to {}'.format(master))
        if opts.skip_develop:
            LOGGER.info(u'Skipping merging to {}'.format(develop))

        if opts.log_status:
            ghc.log_branch_status(master)
        if not opts.skip_master:
            sha = ghc.repo.head.ref.commit.hexsha

            if rel_conf['wait_on_ci']:
                #
                # wait on release branch CI success
                #
                LOGGER.info(
                    u"Waiting on CI build for {0}".format(release_branch))
                ghc.wait_on_gh_status(sha,
                                      timeout=rel_conf['wait_on_ci_timeout'],
                                      interval=rel_conf['wait_on_ci_interval'])

            LOGGER.info(u"Merging {} into {}".format(release_branch, master))
            ghc.pull_branch(master, remote=not opts.no_remote)
            ghc.merge_branch(release_branch)
            sha = ghc.repo.head.ref.commit.hexsha

            if rel_conf['wait_on_ci_master']:
                #
                # wait on release branch CI success
                #
                LOGGER.info(u"Waiting on CI build for {0}".format(master))
                ghc.wait_on_gh_status(sha,
                                      timeout=rel_conf['wait_on_ci_timeout'],
                                      interval=rel_conf['wait_on_ci_interval'])
            if rel_conf['update_github_context']:
                for ctx in rel_conf['github_context_string']:
                    LOGGER.info(u"Setting {} for {}".format(ctx, sha))
                    ghc.set_branch_state('success', ctx, branch=sha)

            if rel_conf['update_master_github_context']:
                for ctx in rel_conf['github_master_context_string']:
                    LOGGER.info(u"Setting {} for {}".format(ctx, sha))
                    ghc.set_branch_state('success', ctx, branch=sha)
            if not opts.no_remote:
                ghc.push_branch_with_retry(
                    attempts=rel_conf['push_retry_attempts'],
                    cooloff=rel_conf['push_retry_cooloff'])
                LOGGER.info(u"Tagging {} as {}".format(master, tag))
            ghc.tag_release(tag,
                            master,
                            push=not opts.no_remote,
                            attempts=rel_conf['push_retry_attempts'],
                            cooloff=rel_conf['push_retry_cooloff'])

        LOGGER.info(u"Merging {} into {}".format(release_branch, develop))
        if opts.log_status:
            ghc.log_branch_status(develop)
        if not opts.skip_develop:
            ghc.pull_branch(develop, remote=not opts.no_remote)
            ghc.merge_branch(release_branch)
            sha = ghc.repo.head.ref.commit.hexsha

            if rel_conf['wait_on_ci_develop']:
                #
                # wait on release branch CI success
                #
                LOGGER.info(u"Waiting on CI build for {0}".format(develop))
                ghc.wait_on_gh_status(sha,
                                      timeout=rel_conf['wait_on_ci_timeout'],
                                      interval=rel_conf['wait_on_ci_interval'])
            if rel_conf['update_github_context']:
                for ctx in rel_conf['github_context_string']:
                    LOGGER.info(u"Setting {} for {}".format(ctx, sha))
                    ghc.set_branch_state('success', ctx, branch=sha)

            if rel_conf['update_develop_github_context']:
                for ctx in rel_conf['github_develop_context_string']:
                    LOGGER.info(u"Setting {} for {}".format(ctx, sha))
                    ghc.set_branch_state('success', ctx, branch=sha)
            if not opts.no_remote:
                ghc.push_branch_with_retry(
                    attempts=rel_conf['push_retry_attempts'],
                    cooloff=rel_conf['push_retry_cooloff'])
        if opts.cleanup:
            ghc.delete_branch(release_branch, remote=not opts.no_remote)
Пример #20
0
def release_status(release):
    """
    given a release branch name or tag, look at the status
    of that release based on git history and attempt to
    check wether it has been fully merged or tagged.

    returns True if the release looks to be successfully merged and tagged

    """
    result = False
    with GitHubContext(repo_directory()) as ghc:
        LOGGER.info("Checking release status for {}".format(release))
        # work out the branch and tag for the release

        rel_pfix = ghc.config.gitflow_release_prefix()
        develop_branch = ghc.config.gitflow_branch_name()
        master_branch = ghc.config.gitflow_master_name()
        origin_name = ghc.config.gitflow_origin_name()
        if rel_pfix in release:
            release_tag = release.split(rel_pfix)[1]
            release_branch = release
        else:
            release_branch = "{}{}".format(rel_pfix, release)
            release_tag = release

        LOGGER.info("Checking: branch={} tag={}".format(
            release_branch, release_tag))
        branch_commit = ghc.find_release_commit(release_branch)
        tag_commit = ghc.find_release_commit(release_tag)
        LOGGER.info("Resolved Commits: branch={} tag={}".format(
            branch_commit, tag_commit))

        # handles caser where tag or release is not present, eg typo
        if (not tag_commit) and (not branch_commit):
            msg = ("Unable to find any branch or tag commits "
                   "for release \'{}\'\n"
                   "Are you sure this is a valid release?").format(release)
            LOGGER.error(msg)
            return False

        # check the tag commit is present on master and remote master
        tag_present = False
        if tag_commit:
            branches = ghc.commit_on_branches(tag_commit)
            remote_master = "remotes/{}/{}".format(origin_name, master_branch)
            on_master = master_branch in branches
            on_origin = remote_master in branches
            if on_master:
                LOGGER.info("Tag is present on local master {}...".format(
                    master_branch))
                tag_present = True
            if on_origin:
                LOGGER.info("Tag is present on remote master {}...".format(
                    remote_master))
                tag_present = True

        # look for common merge base containing the release name for master
        # and develop
        develop_merge = ghc.merge_base(release_tag, develop_branch)
        master_merge = ghc.merge_base(release_tag, master_branch)
        merge_on_develop = False
        merge_on_master = False
        if develop_merge:
            merge_on_develop = release_branch in ghc.git_show_commit(
                develop_merge)
        if master_merge:
            merge_on_master = release_branch in ghc.git_show_commit(
                master_merge)

        if merge_on_develop:
            LOGGER.info("Merge of {} is on {}".format(release_branch,
                                                      develop_branch))
        else:
            LOGGER.info("Merge of {} not found on {}".format(
                release_branch, develop_branch))
        if merge_on_master:
            LOGGER.info("Merge of {} is on {}".format(release_branch,
                                                      master_branch))
        else:
            LOGGER.info("Merge of {} not found on {}".format(
                release_branch, master_branch))

        if not all([tag_present, merge_on_develop, merge_on_master]):
            msg = (
                "\nRelease branch {} was not found either as a tag or merge "
                "commit on develop branch: {} or master branch: {} branches\n"
                "This may mean that the release is still running on a CI platform or "
                "has errored out. \n"
                " => Tag Present: {}\n"
                " => Merged to develop: {}\n"
                " => Merged to master: {}\n"
                "\nFor troubleshooting please see: "
                "https://github.com/evansde77/cirrus/wiki/Troubleshooting#release\n"
            ).format(release_branch, develop_branch, master_branch,
                     tag_present, merge_on_develop, merge_on_master)
            LOGGER.error(msg)
            result = False
        else:
            msg = "Release {} looks to be successfully merged and tagged\n".format(
                release_branch)
            LOGGER.info(msg)
            result = True
    return result