示例#1
0
    def test_empty_octopus_merge(self):
        mock_repo = MagicMock(spec=Repo)
        api = LocalGitAPI(mock_repo)
        sha = api.octopus_merge('public/release-candidate', [])

        mock_repo.git.merge.assert_not_called()
        self.assertEqual(sha, mock_repo.head.commit.hexsha)
示例#2
0
    def test_octopus_merge(self):
        mock_repo = MagicMock(spec=Repo)
        api = LocalGitAPI(mock_repo)
        sha = api.octopus_merge('public/release-candidate',
                                ['12345abcdef', 'deadbeef'])

        mock_repo.git.merge.assert_called_once_with('12345abcdef', 'deadbeef')
        self.assertEqual(sha, mock_repo.head.commit.hexsha)
示例#3
0
    def test_clone_failure(self, mock_repo, mock_rmtree):
        """
        Tests failing to merge a branch.
        """
        mock_repo.clone_from.side_effect = GitCommandError('cmd', 1)

        with self.assertRaises(GitCommandError):
            LocalGitAPI.clone('[email protected]:edx/tubular.git', 'bar')
        self.assertEqual(mock_rmtree.call_count, 0)
示例#4
0
    def test_cleanup(self, failing_mock, mock_repo, mock_rmtree):
        """
        Tests failing to merge a branch.
        """
        mock_repo.configure_mock(autospec=True,
                                 **{
                                     '{}.side_effect'.format(failing_mock):
                                     GitCommandError('cmd', 1)
                                 })

        with self.assertRaises(GitCommandError):
            LocalGitAPI.clone('[email protected]:edx/tubular.git',
                              'bar').merge_branch('foo', 'bar')
            mock_rmtree.assert_called_once_with('tubular')
示例#5
0
def merge_branch(org, repo, source_branch, target_branch, fast_forward_only,
                 output_file, reference_repo):
    u"""
    Merges the source branch into the target branch without creating a pull request for the merge.
    Clones the repo in order to perform the proper git commands locally.

    Args:
        org (str):
        repo (str):
        source_branch (str):
        target_branch (str):
        fast_forward_only (bool): If True, the branch merge will be performed as a fast-forward merge.
          If the merge cannot be performed as a fast-forward merge, the merge will fail.
    """
    github_url = u'[email protected]:{}/{}.git'.format(org, repo)
    with LocalGitAPI.clone(github_url, target_branch,
                           reference_repo).cleanup() as local_repo:
        merge_sha = local_repo.merge_branch(source_branch, target_branch,
                                            fast_forward_only)
        local_repo.push_branch(target_branch)

    with io.open(output_file, u'w') as stream:
        yaml.safe_dump(
            {
                u'org_name': org,
                u'repo_name': repo,
                u'source_branch_name': source_branch,
                u'target_branch_name': target_branch,
                u'fast_forward_only': fast_forward_only,
                u'sha': merge_sha
            },
            stream,
            default_flow_style=False,
            explicit_start=True)
示例#6
0
def push_public_to_private(private_org, private_repo, private_target_branch,
                           public_org, public_repo, public_source_branch,
                           output_file, reference_repo):
    u"""
    Push the results of a merge of private changes to public back over to the private
    repo to keep the repo branches in-sync.
    """
    public_github_url = u'[email protected]:{}/{}.git'.format(
        public_org, public_repo)
    private_github_url = u'[email protected]:{}/{}.git'.format(
        private_org, private_repo)
    output_yaml = {
        u'private_github_url': private_github_url,
        u'private_target_branch_name': private_target_branch,
        u'public_github_url': public_github_url,
        u'public_target_branch_name': public_source_branch
    }

    # Clone the public repo, checking out the proper public branch.
    LOG.info('Cloning public repo %s with branch %s.', public_github_url,
             public_source_branch)
    with LocalGitAPI.clone(public_github_url, public_source_branch,
                           reference_repo).cleanup() as local_repo:
        # Add the private repo as a remote for the public git working tree.
        local_repo.add_remote('private', private_github_url)
        try:
            # Push the public branch back to the private branch - without forcing.
            is_pushed = local_repo.push_branch(public_source_branch,
                                               'private',
                                               private_target_branch,
                                               force=False,
                                               log_info=True)
            if is_pushed:
                output_yaml.update({u'branch_pushed': is_pushed})
                LOG.info('public branch successfully pushed to repo.')
            else:
                LOG.warning(
                    "Failed to push public branch %s to private branch %s",
                    public_source_branch, private_target_branch)
                output_yaml.update({u'branch_pushed': False})
        except Exception as exc:  # pylint: disable=broad-except
            # On any failure besides auth, simply log and ignore.
            # The problem will work itself out in the next private->public cycle.
            LOG.warning(
                "Failed to push public branch %s to private branch %s without fast-forward: %s",
                public_source_branch, private_target_branch, exc)
            output_yaml.update({u'branch_pushed': False})

    if output_file:
        with io.open(output_file, u'w') as stream:
            yaml.safe_dump(output_yaml,
                           stream,
                           default_flow_style=False,
                           explicit_start=True)
    else:
        yaml.safe_dump(
            output_yaml,
            sys.stdout,
        )
示例#7
0
 def create_version_file(self):
     """ Creates a version.json file to be deployed with frontend """
     # Add version.json file to build.
     version = {
         'repo': self.app_name,
         'commit': LocalGitAPI(Repo(self.app_name)).get_head_sha(),
         'created': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
     }
     try:
         with io.open(self.version_file, 'w') as output_file:
             json.dump(version, output_file)
     except IOError:
         self.FAIL(1, 'Could not write to version file for app {}.'.format(self.app_name))
示例#8
0
    def test_merge_branch_success(self, mock_repo, mock_rmtree):
        """
        Tests merging a branch successfully.
        """
        with LocalGitAPI.clone('[email protected]:edx/tubular.git',
                               'bar').cleanup() as repo:
            merge_sha = repo.merge_branch('foo', 'bar')

        mock_repo.clone_from.assert_called_once_with(
            '[email protected]:edx/tubular.git',
            to_path='tubular',
            branch='bar',
        )
        git_wrapper = mock_repo.clone_from.return_value.git
        git_wrapper.merge.assert_called_once_with('foo', ff_only=True)
        git_wrapper.rev_parse.assert_called_once_with('HEAD')
        self.assertEqual(git_wrapper.rev_parse.return_value, merge_sha)
        mock_rmtree.assert_called_once_with(
            mock_repo.clone_from.return_value.working_dir)
def create_private_to_public_pr(private_org, private_repo,
                                private_source_branch, public_org, public_repo,
                                public_target_branch, token, output_file,
                                reference_repo):
    u"""
    Creates a PR to merge the private source branch into the public target branch.
    Clones the repo in order to perform the proper git commands locally.
    """
    public_github_url = u'[email protected]:{}/{}.git'.format(
        public_org, public_repo)
    private_github_url = u'[email protected]:{}/{}.git'.format(
        private_org, private_repo)
    output_yaml = {
        u'private_github_url': private_github_url,
        u'private_source_branch_name': private_source_branch,
        u'public_github_url': public_github_url,
        u'public_target_branch_name': public_target_branch
    }

    LOG.info('Cloning private repo %s with branch %s.', private_github_url,
             private_source_branch)
    with LocalGitAPI.clone(private_github_url, private_source_branch,
                           reference_repo).cleanup() as local_repo:
        # Add the public repo as a remote for the private git working tree.
        local_repo.add_remote('public', public_github_url)
        # Create a new public branch with unique name.
        new_branch_name = 'private_to_public_{}'.format(
            local_repo.get_head_sha()[:7])
        # Push the private branch into the public repo.
        LOG.info('Pushing private branch %s to public repo %s as branch %s.',
                 private_source_branch, public_github_url, new_branch_name)
        local_repo.push_branch(private_source_branch, 'public',
                               new_branch_name)
        github_api = GitHubAPI(public_org, public_repo, token)
        # Create a PR from new public branch to public master.
        try:
            pull_request = github_api.create_pull_request(
                title='Mergeback PR from private to public.',
                body=
                'Merge private changes back to the public repo post-PR-merge.\n\n'
                'Please review and tag appropriate parties.',
                head=new_branch_name,
                base=public_target_branch)
        except PullRequestCreationError as exc:
            LOG.info(
                "No pull request created for merging %s into %s in '%s' repo - nothing to merge: %s",
                new_branch_name, public_target_branch, public_github_url, exc)
            output_yaml.update({
                'pr_created': False,
            })
            # Cleanup - delete the pushed branch.
            github_api.delete_branch(new_branch_name)
        else:
            LOG.info('Created PR #%s for repo %s: %s', pull_request.number,
                     public_github_url, pull_request.html_url)
            output_yaml.update({
                'pr_created':
                True,
                'pr_id':
                pull_request.id,
                'pr_number':
                pull_request.number,
                'pr_url':
                pull_request.url,
                'pr_repo_url':
                github_api.github_repo.url,
                'pr_head':
                pull_request.head.sha,
                'pr_base':
                pull_request.base.sha,
                'pr_html_url':
                pull_request.html_url,
                'pr_diff_url':
                pull_request.diff_url,
                'pr_mergable':
                pull_request.mergeable,
                'pr_state':
                pull_request.state,
                'pr_mergable_state':
                pull_request.mergeable_state,
            })

    if output_file:
        with io.open(output_file, u'w') as stream:
            yaml.safe_dump(output_yaml,
                           stream,
                           default_flow_style=False,
                           explicit_start=True)
    else:
        yaml.safe_dump(
            output_yaml,
            sys.stdout,
        )