/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
7408.3.1 by Jelmer Vernooij
Move propose module into core.
1
# Copyright (C) 2018-2019 Breezy Developers
0.431.1 by Jelmer Vernooij
Start work on propose command.
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
"""Helper functions for proposing merges."""
18
7408.3.1 by Jelmer Vernooij
Move propose module into core.
19
from . import (
0.431.1 by Jelmer Vernooij
Start work on propose command.
20
    errors,
21
    hooks,
22
    registry,
7268.12.3 by Jelmer Vernooij
Add missing import.
23
    urlutils,
0.431.1 by Jelmer Vernooij
Start work on propose command.
24
    )
25
26
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
27
class NoSuchProject(errors.BzrError):
28
29
    _fmt = "Project does not exist: %(project)s."
30
31
    def __init__(self, project):
32
        errors.BzrError.__init__(self)
33
        self.project = project
34
35
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
36
class MergeProposalExists(errors.BzrError):
37
38
    _fmt = "A merge proposal already exists: %(url)s."
39
40
    def __init__(self, url):
41
        errors.BzrError.__init__(self)
42
        self.url = url
43
44
0.432.2 by Jelmer Vernooij
Publish command sort of works.
45
class UnsupportedHoster(errors.BzrError):
46
47
    _fmt = "No supported hoster for %(branch)s."
48
49
    def __init__(self, branch):
50
        errors.BzrError.__init__(self)
51
        self.branch = branch
52
53
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
54
class ReopenFailed(errors.BzrError):
55
56
    _fmt = "Reopening the merge proposal failed: %(error)s."
57
58
0.431.1 by Jelmer Vernooij
Start work on propose command.
59
class ProposeMergeHooks(hooks.Hooks):
60
    """Hooks for proposing a merge on Launchpad."""
61
62
    def __init__(self):
63
        hooks.Hooks.__init__(self, __name__, "Proposer.hooks")
0.431.57 by Jelmer Vernooij
Cleanups.
64
        self.add_hook(
65
            'get_prerequisite',
0.431.1 by Jelmer Vernooij
Start work on propose command.
66
            "Return the prerequisite branch for proposing as merge.", (3, 0))
0.431.57 by Jelmer Vernooij
Cleanups.
67
        self.add_hook(
68
            'merge_proposal_body',
0.431.1 by Jelmer Vernooij
Start work on propose command.
69
            "Return an initial body for the merge proposal message.", (3, 0))
70
71
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
72
class LabelsUnsupported(errors.BzrError):
73
    """Labels not supported by this hoster."""
74
75
    _fmt = "Labels are not supported by %(hoster)r."
76
77
    def __init__(self, hoster):
78
        errors.BzrError.__init__(self)
79
        self.hoster = hoster
80
81
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
82
class PrerequisiteBranchUnsupported(errors.BzrError):
83
    """Prerequisite branch not supported by this hoster."""
84
85
    def __init__(self, hoster):
86
        errors.BzrError.__init__(self)
87
        self.hoster = hoster
88
89
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
90
class HosterLoginRequired(errors.BzrError):
91
    """Action requires hoster login credentials."""
92
7268.8.2 by Jelmer Vernooij
Handle GitHub errors.
93
    _fmt = "Action requires credentials for hosting site %(hoster)r."""
94
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
95
    def __init__(self, hoster):
96
        errors.BzrError.__init__(self)
97
        self.hoster = hoster
98
99
7490.117.3 by Jelmer Vernooij
Raise detailed error when source branch is not derived from target branch.
100
class SourceNotDerivedFromTarget(errors.BzrError):
101
    """Source branch is not derived from target branch."""
102
103
    _fmt = ("Source %(source_branch)r not derived from "
104
            "target %(target_branch)r.")
105
106
    def __init__(self, source_branch, target_branch):
107
        errors.BzrError.__init__(
108
            self, source_branch=source_branch,
109
            target_branch=target_branch)
110
111
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
112
class MergeProposal(object):
113
    """A merge proposal.
114
115
    :ivar url: URL for the merge proposal
116
    """
117
118
    def __init__(self, url=None):
119
        self.url = url
120
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
121
    def get_description(self):
122
        """Get the description of the merge proposal."""
123
        raise NotImplementedError(self.get_description)
124
125
    def set_description(self, description):
126
        """Set the description of the merge proposal."""
127
        raise NotImplementedError(self.set_description)
128
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
129
    def get_commit_message(self):
130
        """Get the proposed commit message."""
131
        raise NotImplementedError(self.get_commit_message)
132
133
    def set_commit_message(self, commit_message):
134
        """Set the propose commit message."""
135
        raise NotImplementedError(self.set_commit_message)
136
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
137
    def get_source_branch_url(self):
138
        """Return the source branch."""
139
        raise NotImplementedError(self.get_source_branch_url)
140
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
141
    def get_source_revision(self):
142
        """Return the latest revision for the source branch."""
143
        raise NotImplementedError(self.get_source_revision)
144
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
145
    def get_target_branch_url(self):
146
        """Return the target branch."""
147
        raise NotImplementedError(self.get_target_branch_url)
148
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
149
    def get_source_project(self):
150
        raise NotImplementedError(self.get_source_project)
151
152
    def get_target_project(self):
153
        raise NotImplementedError(self.get_target_project)
154
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
155
    def close(self):
156
        """Close the merge proposal (without merging it)."""
157
        raise NotImplementedError(self.close)
158
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
159
    def is_merged(self):
160
        """Check whether this merge proposal has been merged."""
161
        raise NotImplementedError(self.is_merged)
162
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
163
    def is_closed(self):
164
        """Check whether this merge proposal is closed
165
166
        This can either mean that it is merged or rejected.
167
        """
168
        raise NotImplementedError(self.is_closed)
169
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
170
    def merge(self, commit_message=None):
171
        """Merge this merge proposal."""
172
        raise NotImplementedError(self.merge)
173
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
174
    def can_be_merged(self):
175
        """Can this merge proposal be merged?
176
177
        The answer to this can be no if e.g. it has conflics.
178
        """
179
        raise NotImplementedError(self.can_be_merged)
180
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
181
    def get_merged_by(self):
182
        """If this proposal was merged, who merged it.
183
        """
184
        raise NotImplementedError(self.get_merged_by)
185
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
186
    def get_merged_at(self):
187
        """If this proposal was merged, when it was merged.
188
        """
189
        raise NotImplementedError(self.get_merged_at)
190
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
191
    def post_comment(self, body):
192
        """Post a comment on the merge proposal.
193
194
        Args:
195
          body: Body of the comment
196
        """
197
        raise NotImplementedError(self.post_comment)
198
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
199
0.432.2 by Jelmer Vernooij
Publish command sort of works.
200
class MergeProposalBuilder(object):
0.431.1 by Jelmer Vernooij
Start work on propose command.
201
    """Merge proposal creator.
202
203
    :param source_branch: Branch to propose for merging
204
    :param target_branch: Target branch
205
    """
206
207
    hooks = ProposeMergeHooks()
208
209
    def __init__(self, source_branch, target_branch):
210
        self.source_branch = source_branch
211
        self.target_branch = target_branch
212
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
213
    def get_initial_body(self):
214
        """Get a body for the proposal for the user to modify.
215
216
        :return: a str or None.
217
        """
218
        raise NotImplementedError(self.get_initial_body)
219
220
    def get_infotext(self):
221
        """Determine the initial comment for the merge proposal.
222
        """
223
        raise NotImplementedError(self.get_infotext)
224
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
225
    def create_proposal(self, description, reviewers=None, labels=None,
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
226
                        prerequisite_branch=None, commit_message=None,
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
227
                        work_in_progress=False, allow_collaboration=False):
0.431.1 by Jelmer Vernooij
Start work on propose command.
228
        """Create a proposal to merge a branch for merging.
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
229
230
        :param description: Description for the merge proposal
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
231
        :param reviewers: Optional list of people to ask reviews from
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
232
        :param labels: Labels to attach to the proposal
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
233
        :param prerequisite_branch: Optional prerequisite branch
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
234
        :param commit_message: Optional commit message
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
235
        :param work_in_progress:
236
            Whether this merge proposal is still a work-in-progress
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
237
        :param allow_collaboration:
238
            Whether to allow changes to the branch from the target branch
239
            maintainer(s)
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
240
        :return: A `MergeProposal` object
0.431.1 by Jelmer Vernooij
Start work on propose command.
241
        """
242
        raise NotImplementedError(self.create_proposal)
243
244
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
245
class Hoster(object):
246
    """A hosting site manager.
247
    """
248
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
249
    # Does this hoster support arbitrary labels being attached to merge
250
    # proposals?
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
251
    supports_merge_proposal_labels = None
252
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
253
    @property
254
    def name(self):
255
        """Name of this instance."""
256
        return "%s at %s" % (type(self).__name__, self.base_url)
257
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
258
    # Does this hoster support suggesting a commit message in the
259
    # merge proposal?
260
    supports_merge_proposal_commit_message = None
261
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
262
    # The base_url that would be visible to users. I.e. https://github.com/
263
    # rather than https://api.github.com/
264
    base_url = None
265
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
266
    # The syntax to use for formatting merge proposal descriptions.
267
    # Common values: 'plain', 'markdown'
268
    merge_proposal_description_format = None
269
7490.3.9 by Jelmer Vernooij
Add supports_allow_collaboration flag.
270
    # Does this hoster support the allow_collaboration flag?
271
    supports_allow_collaboration = False
272
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
273
    def publish_derived(self, new_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
274
                        owner=None, revision_id=None, overwrite=False,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
275
                        allow_lossy=True, tag_selector=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
276
        """Publish a branch to the site, derived from base_branch.
277
278
        :param base_branch: branch to derive the new branch from
279
        :param new_branch: branch to publish
0.432.3 by Jelmer Vernooij
Publish command works for github.
280
        :return: resulting branch, public URL
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
281
        :raise HosterLoginRequired: Action requires a hoster login, but none is
282
            known.
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
283
        """
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
284
        raise NotImplementedError(self.publish_derived)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
285
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
286
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
0.431.31 by Jelmer Vernooij
Drop autopropose command.
287
        """Get a derived branch ('a fork').
288
        """
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
289
        raise NotImplementedError(self.get_derived_branch)
290
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
291
    def get_push_url(self, branch):
292
        """Get the push URL for a branch."""
293
        raise NotImplementedError(self.get_push_url)
294
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
295
    def get_proposer(self, source_branch, target_branch):
296
        """Get a merge proposal creator.
297
0.431.31 by Jelmer Vernooij
Drop autopropose command.
298
        :note: source_branch does not have to be hosted by the hoster.
299
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
300
        :param source_branch: Source branch
301
        :param target_branch: Target branch
0.432.2 by Jelmer Vernooij
Publish command sort of works.
302
        :return: A MergeProposalBuilder object
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
303
        """
304
        raise NotImplementedError(self.get_proposer)
305
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
306
    def iter_proposals(self, source_branch, target_branch, status='open'):
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
307
        """Get the merge proposals for a specified branch tuple.
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
308
309
        :param source_branch: Source branch
310
        :param target_branch: Target branch
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
311
        :param status: Status of proposals to iterate over
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
312
        :return: Iterate over MergeProposal object
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
313
        """
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
314
        raise NotImplementedError(self.iter_proposals)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
315
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
316
    def get_proposal_by_url(self, url):
317
        """Retrieve a branch proposal by URL.
318
319
        :param url: Merge proposal URL.
320
        :return: MergeProposal object
321
        :raise UnsupportedHoster: Hoster does not support this URL
322
        """
323
        raise NotImplementedError(self.get_proposal_by_url)
324
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
325
    def hosts(self, branch):
326
        """Return true if this hoster hosts given branch."""
327
        raise NotImplementedError(self.hosts)
328
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
329
    @classmethod
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
330
    def probe_from_branch(cls, branch):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
331
        """Create a Hoster object if this hoster knows about a branch."""
7441.1.1 by Jelmer Vernooij
Add strip_segment_parameters function.
332
        url = urlutils.strip_segment_parameters(branch.user_url)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
333
        return cls.probe_from_url(
7296.10.2 by Jelmer Vernooij
More fixes.
334
            url, possible_transports=[branch.control_transport])
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
335
336
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
337
    def probe_from_url(cls, url, possible_hosters=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
338
        """Create a Hoster object if this hoster knows about a URL."""
339
        raise NotImplementedError(cls.probe_from_url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
340
0.431.66 by Jelmer Vernooij
Add support for status argument.
341
    def iter_my_proposals(self, status='open'):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
342
        """Iterate over the proposals created by the currently logged in user.
343
0.431.66 by Jelmer Vernooij
Add support for status argument.
344
        :param status: Only yield proposals with this status
345
            (one of: 'open', 'closed', 'merged', 'all')
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
346
        :return: Iterator over MergeProposal objects
7268.8.1 by Jelmer Vernooij
Add HosterLoginRequired exception.
347
        :raise HosterLoginRequired: Action requires a hoster login, but none is
348
            known.
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
349
        """
350
        raise NotImplementedError(self.iter_my_proposals)
351
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
352
    def iter_my_forks(self):
353
        """Iterate over the currently logged in users' forks.
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
354
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
355
        :return: Iterator over project_name
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
356
        """
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
357
        raise NotImplementedError(self.iter_my_forks)
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
358
359
    def delete_project(self, name):
360
        """Delete a project.
361
        """
362
        raise NotImplementedError(self.delete_project)
363
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
364
    @classmethod
365
    def iter_instances(cls):
366
        """Iterate instances.
367
368
        :return: Hoster instances
369
        """
370
        raise NotImplementedError(cls.iter_instances)
371
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
372
    def get_current_user(self):
373
        """Retrieve the name of the currently logged in user.
374
375
        :return: Username or None if not logged in
376
        """
377
        raise NotImplementedError(self.get_current_user)
378
379
    def get_user_url(self, user):
380
        """Rerieve the web URL for a user."""
381
        raise NotImplementedError(self.get_user_url)
382
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
383
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
384
def determine_title(description):
385
    """Determine the title for a merge proposal based on full description."""
386
    return description.splitlines()[0].split('.')[0]
387
388
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
389
def get_hoster(branch, possible_hosters=None):
7408.3.2 by Jelmer Vernooij
Add some tests.
390
    """Find the hoster for a branch.
391
392
    :param branch: Branch to find hoster for
393
    :param possible_hosters: Optional list of hosters to reuse
394
    :raise UnsupportedHoster: if there is no hoster that supports `branch`
395
    :return: A `Hoster` object
396
    """
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
397
    if possible_hosters:
398
        for hoster in possible_hosters:
399
            if hoster.hosts(branch):
400
                return hoster
0.432.2 by Jelmer Vernooij
Publish command sort of works.
401
    for name, hoster_cls in hosters.items():
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
402
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
403
            hoster = hoster_cls.probe_from_branch(branch)
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
404
        except UnsupportedHoster:
405
            pass
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
406
        else:
407
            if possible_hosters is not None:
408
                possible_hosters.append(hoster)
409
            return hoster
0.432.2 by Jelmer Vernooij
Publish command sort of works.
410
    raise UnsupportedHoster(branch)
411
412
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
413
def iter_hoster_instances():
414
    """Iterate over all known hoster instances.
415
416
    :return: Iterator over Hoster instances
417
    """
418
    for name, hoster_cls in hosters.items():
419
        for instance in hoster_cls.iter_instances():
420
            yield instance
421
422
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
423
def get_proposal_by_url(url):
7408.3.2 by Jelmer Vernooij
Add some tests.
424
    """Get the proposal object associated with a URL.
425
426
    :param url: URL of the proposal
427
    :raise UnsupportedHoster: if there is no hoster that supports the URL
428
    :return: A `MergeProposal` object
429
    """
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
430
    for instance in iter_hoster_instances():
431
        try:
432
            return instance.get_proposal_by_url(url)
433
        except UnsupportedHoster:
434
            pass
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
435
    raise UnsupportedHoster(url)
436
437
0.432.2 by Jelmer Vernooij
Publish command sort of works.
438
hosters = registry.Registry()