/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
1
# Copyright (C) 2018 Breezy Developers
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
from __future__ import absolute_import
18
19
from .propose import (
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
20
    Hoster,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
21
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
22
    MergeProposalBuilder,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
23
    MergeProposalExists,
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
24
    UnsupportedHoster,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
25
    )
26
27
from ... import (
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
28
    branch as _mod_branch,
0.432.3 by Jelmer Vernooij
Publish command works for github.
29
    controldir,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
30
    errors,
31
    hooks,
32
    urlutils,
0.432.3 by Jelmer Vernooij
Publish command works for github.
33
    version_string as breezy_version,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
34
    )
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
35
from ...config import AuthenticationConfig, GlobalStack
0.431.32 by Jelmer Vernooij
Properly resolve git+ssh URLs.
36
from ...git.urls import git_url_to_bzr_url
0.432.3 by Jelmer Vernooij
Publish command works for github.
37
from ...i18n import gettext
38
from ...trace import note
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
39
from ...lazy_import import lazy_import
40
lazy_import(globals(), """
41
from github import Github
42
""")
43
44
45
class NotGitHubUrl(errors.BzrError):
46
47
    _fmt = "Not a GitHub URL: %(url)s"
48
49
    def __init__(self, url):
50
        errors.BzrError.__init__(self)
51
        self.url = url
52
53
54
def connect_github():
55
    user_agent = user_agent="Breezy/%s" % breezy_version
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
56
57
    auth = AuthenticationConfig()
58
59
    credentials = auth.get_credentials('https', 'github.com')
60
    if credentials is not None:
0.432.3 by Jelmer Vernooij
Publish command works for github.
61
        return Github(credentials['user'], credentials['password'],
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
62
                      user_agent=user_agent)
63
64
    # TODO(jelmer): Support using an access token
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
65
    #return Github("token", user_agent=user_agent)
66
    return Github(user_agent=user_agent)
67
68
69
def parse_github_url(branch):
70
    url = urlutils.split_segment_parameters(branch.user_url)[0]
71
    (scheme, user, password, host, port, path) = urlutils.parse_url(
72
        url)
73
    if host != 'github.com':
74
        raise NotGitHubUrl(url)
75
    (owner, repo_name) = path.strip('/').split('/')
0.432.12 by Jelmer Vernooij
Fix .git ends.
76
    if repo_name.endswith('.git'):
77
        repo_name = repo_name[:-4]
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
78
    return owner, repo_name, branch.name
79
80
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
81
class GitHub(Hoster):
82
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
83
    supports_merge_proposal_labels = True
84
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
85
    def __init__(self):
86
        self.gh = connect_github()
87
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
88
    def publish_derived(self, local_branch, base_branch, name, project=None,
89
                        owner=None, revision_id=None, overwrite=False):
0.432.12 by Jelmer Vernooij
Fix .git ends.
90
        import github
0.432.3 by Jelmer Vernooij
Publish command works for github.
91
        base_owner, base_project, base_branch_name = parse_github_url(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
92
        base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
0.432.3 by Jelmer Vernooij
Publish command works for github.
93
        if owner is None:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
94
            owner = self.gh.get_user().login
0.432.3 by Jelmer Vernooij
Publish command works for github.
95
        if project is None:
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
96
            project = base_repo.name
0.432.3 by Jelmer Vernooij
Publish command works for github.
97
        try:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
98
            remote_repo = self.gh.get_repo('%s/%s' % (owner, project))
0.432.12 by Jelmer Vernooij
Fix .git ends.
99
            remote_repo.id
100
        except github.UnknownObjectException:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
101
            base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
102
            if owner == self.gh.get_user().login:
103
                owner_obj = self.gh.get_user()
0.432.3 by Jelmer Vernooij
Publish command works for github.
104
            else:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
105
                owner_obj = self.gh.get_organization(owner)
0.432.12 by Jelmer Vernooij
Fix .git ends.
106
            remote_repo = owner_obj.create_fork(base_repo)
0.432.3 by Jelmer Vernooij
Publish command works for github.
107
            note(gettext('Forking new repository %s from %s') %
108
                    (remote_repo.html_url, base_repo.html_url))
109
        else:
110
            note(gettext('Reusing existing repository %s') % remote_repo.html_url)
0.431.32 by Jelmer Vernooij
Properly resolve git+ssh URLs.
111
        remote_dir = controldir.ControlDir.open(git_url_to_bzr_url(remote_repo.ssh_url))
0.432.3 by Jelmer Vernooij
Publish command works for github.
112
        push_result = remote_dir.push_branch(local_branch, revision_id=revision_id,
113
            overwrite=overwrite, name=name)
114
        return push_result.target_branch, urlutils.join_segment_parameters(
115
                remote_repo.html_url, {"branch": name.encode('utf-8')})
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
116
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
117
    def get_push_url(self, branch):
118
        owner, project, branch_name = parse_github_url(branch)
119
        repo = self.gh.get_repo('%s/%s' % (owner, project))
120
        return urlutils.join_segment_parameters(
0.431.32 by Jelmer Vernooij
Properly resolve git+ssh URLs.
121
            git_url_to_bzr_url(repo.ssh_url), {"branch": branch_name.encode('utf-8')})
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
122
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
123
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
124
        import github
125
        base_owner, base_project, base_branch_name = parse_github_url(base_branch)
126
        base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
127
        if owner is None:
128
            owner = self.gh.get_user().login
129
        if project is None:
130
            project = base_repo.name
131
        try:
132
            remote_repo = self.gh.get_repo('%s/%s' % (owner, project))
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
133
            full_url = urlutils.join_segment_parameters(
134
                git_url_to_bzr_url(remote_repo.ssh_url), {"branch": name.encode('utf-8')})
135
            return _mod_branch.Branch.open(full_url)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
136
        except github.UnknownObjectException:
137
            raise errors.NotBranchError('https://github.com/%s/%s' % (owner, project))
138
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
139
    def get_proposer(self, source_branch, target_branch):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
140
        return GitHubMergeProposalBuilder(self.gh, source_branch, target_branch)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
141
142
    @classmethod
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
143
    def probe(cls, branch):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
144
        try:
145
            parse_github_url(branch)
146
        except NotGitHubUrl:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
147
            raise UnsupportedHoster(branch)
148
        return cls()
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
149
150
0.432.2 by Jelmer Vernooij
Publish command sort of works.
151
class GitHubMergeProposalBuilder(MergeProposalBuilder):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
152
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
153
    def __init__(self, gh, source_branch, target_branch):
154
        self.gh = gh
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
155
        self.source_branch = source_branch
156
        self.target_branch = target_branch
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
157
        (self.target_owner, self.target_repo_name, self.target_branch_name) = (
158
                parse_github_url(self.target_branch))
159
        (self.source_owner, self.source_repo_name, self.source_branch_name) = (
160
                parse_github_url(self.source_branch))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
161
162
    def get_infotext(self):
163
        """Determine the initial comment for the merge proposal."""
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
164
        info = []
165
        info.append("Merge %s into %s:%s\n" % (
166
            self.source_branch_name, self.target_owner,
167
            self.target_branch_name))
168
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
169
        info.append("Target: %s\n" % self.target_branch.user_url)
170
        return ''.join(info)
171
172
    def get_initial_body(self):
173
        """Get a body for the proposal for the user to modify.
174
175
        :return: a str or None.
176
        """
177
        return None
178
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
179
    def create_proposal(self, description, reviewers=None, labels=None):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
180
        """Perform the submission."""
0.432.10 by Jelmer Vernooij
More test fixes.
181
        import github
0.432.7 by Jelmer Vernooij
propose works \o/
182
        # TODO(jelmer): Probe for right repo name
0.432.12 by Jelmer Vernooij
Fix .git ends.
183
        if self.target_repo_name.endswith('.git'):
184
            self.target_repo_name = self.target_repo_name[:-4]
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
185
        target_repo = self.gh.get_repo("%s/%s" % (self.target_owner, self.target_repo_name))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
186
        # TODO(jelmer): Allow setting title explicitly?
187
        title = description.splitlines()[0]
188
        # TOOD(jelmer): Set maintainers_can_modify?
0.432.10 by Jelmer Vernooij
More test fixes.
189
        try:
190
            pull_request = target_repo.create_pull(
191
                title=title, body=description,
192
                head="%s:%s" % (self.source_owner, self.source_branch_name),
193
                base=self.target_branch_name)
194
        except github.GithubException as e:
195
            if e.status == 422:
196
                raise MergeProposalExists(self.source_branch.user_url)
197
            raise
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
198
        if reviewers:
199
            for reviewer in reviewers:
200
                pull_request.assignees.append(
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
201
                    self.gh.get_user(reviewer))
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
202
        if labels:
203
            for label in labels:
204
                pull_request.issue.labels.append(label)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
205
        return MergeProposal(pull_request.html_url)