~hydrazine-core/hydrazine/trunk

106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
1
#! /usr/bin/python3
62.1.15 by Robert Collins
Show the most recent comment on merge proposals.
2
# vi: expandtab:sts=4
47 by Martin Pool
Start a script to feed pqm from bmps
3
4
# Copyright (C) 2010 Martin Pool
5
6
"""Take approved merge proposals and ask pqm to merge them.
7
"""
8
9
10
# TODO: comment on an mp
11
#
12
# TODO: open in browser
13
#
14
# TODO: show all comments
15
#
48 by Martin Pool
Can now set message and send to pqm
16
# TODO: some way to persistently say "not for me" and not be asked again
47 by Martin Pool
Start a script to feed pqm from bmps
17
18
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
19
import optparse
47 by Martin Pool
Start a script to feed pqm from bmps
20
import sys
21
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
22
from breezy import initialize as breezy_initialize
23
import atexit
24
def breezy_setup():
25
    """Call breezy.initialize and register cleanup to be run on exit"""
26
    context = breezy_initialize()
27
    if context is not None:
28
        atexit.register(context.__enter__().__exit__, None, None, None)
66.1.1 by Martin
Initialize the bzrlib ui so it can prompt for an email password
29
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
30
from breezy.email_message import EmailMessage
31
from breezy import (
101.1.1 by Jelmer Vernooij
Fix compatibility with bzr 2.5.
32
    config as _mod_config,
33
    gpg,
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
34
    version_info as breezy_version,
101.1.1 by Jelmer Vernooij
Fix compatibility with bzr 2.5.
35
    )
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
36
from breezy.smtp_connection import SMTPConnection
47 by Martin Pool
Start a script to feed pqm from bmps
37
38
import hydrazine
39
40
48 by Martin Pool
Can now set message and send to pqm
41
PQM_ADDRESS = 'pqm@bazaar-vcs.org'
42
# PQM_ADDRESS = 'mbp@sourcefrog.net'
43
44
47 by Martin Pool
Start a script to feed pqm from bmps
45
def read_choice():
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
46
    print('What now?', end='')
47 by Martin Pool
Start a script to feed pqm from bmps
47
    return raw_input()
48
49
50
def show_help():
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
51
    print("""\
47 by Martin Pool
Start a script to feed pqm from bmps
52
feed-pqm looks for approved merge proposals and sends them to pqm.
53
54
commands:
55
    h       help
48 by Martin Pool
Can now set message and send to pqm
56
    m       set the proposed commit message for this proposal
47 by Martin Pool
Start a script to feed pqm from bmps
57
    n       skip this, go to the next
48 by Martin Pool
Can now set message and send to pqm
58
    p       previous
59
    q       quit
89 by Jonathan Riddell
remove unused submit option
60
    e       send in an approved merge via email. Requires GPG and email setup
61
            appropriately in bazaar.conf, and your key in the PQM keyring.
62
63
Please send feedback to mbp@canonical.com
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
64
""")
89 by Jonathan Riddell
remove unused submit option
65
66
    #does not currently do anything useful
67
    """\
62.1.11 by Robert Collins
Remove --cron mode.
68
    s       submit approved merges to the queue (PQM reads from the launchpad
69
            queue) for already queued merges this toggles in and out of queued,
70
            setting a new queuer and putting it at the back of the queue.
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
71
            Requires membership in the review team for the target branch.
47 by Martin Pool
Start a script to feed pqm from bmps
72
"""
73
74
def show_datetime(label, dt):
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
75
    print("  %12s: %s"  % (label, dt) )
47 by Martin Pool
Start a script to feed pqm from bmps
76
        # TODO: show relative time
77
        #  (datetime.datetime.utcnow() - dt).days)
78
79
80
def show_mp(mp):
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
81
    print(mp.web_link)
48 by Martin Pool
Can now set message and send to pqm
82
    # nb: .url is empty (except on mirrored branches?)
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
83
    print('  %12s: %s' % ('message', mp.commit_message))
84
    print('  %12s: %s' % ('source', mp.source_branch.bzr_identity))
85
    print('  %12s: %s' % ('target', mp.target_branch.bzr_identity))
91.3.1 by Martin
Display prerequisite branches when showing merge proposals
86
    prereq_branch = mp.prerequisite_branch
87
    if prereq_branch is not None:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
88
        print('  %12s: %s' % ('prereq', prereq_branch.bzr_identity))
89
    print('  %12s: %s' % ('status', mp.queue_status))
47 by Martin Pool
Start a script to feed pqm from bmps
90
    show_datetime('created', mp.date_created)
91
    show_datetime('reviewed', mp.date_reviewed)
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
92
    print('  %12s: %s' % ('registrant', mp.registrant.name))
47 by Martin Pool
Start a script to feed pqm from bmps
93
    for vote in mp.votes_collection:
48 by Martin Pool
Can now set message and send to pqm
94
        # XXX: would like to show the date but see bug 530475
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
95
        print("  %12s: %30s %-12s" % ('vote', vote.reviewer.name, 
51 by Martin Pool
Cope nicely with mps that still have some merge requests
96
            (vote.comment and vote.comment.vote or 'Requested'),
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
97
            ))
62.1.15 by Robert Collins
Show the most recent comment on merge proposals.
98
    # The last comment is usually enough to get a sense for things. Change if
99
    # desired.
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
100
    print("Recent comments:")
62.1.16 by Robert Collins
Some comments about the code.
101
    comments = list(mp.all_comments) #XXX: See bug lp:583761
62.1.15 by Robert Collins
Show the most recent comment on merge proposals.
102
    for comment in comments[-1:]:
77.2.1 by John Arbash Meinel
Work around bug #612641 by encoding to ascii if necessary.
103
        val = "%s: %s" % (comment.author.name, comment.message_body)
104
        try:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
105
            print(val)
77.2.1 by John Arbash Meinel
Work around bug #612641 by encoding to ascii if necessary.
106
        except UnicodeError:
107
            val = val.encode('ascii', 'replace')
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
108
            print(val)
47 by Martin Pool
Start a script to feed pqm from bmps
109
110
48 by Martin Pool
Can now set message and send to pqm
111
def set_message(mp):
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
112
    print("old commit message: %s" % (mp.commit_message,))
113
    print("new message> ", end='')
88 by Martin
Require commit messages to be at least two characters long
114
    message = raw_input()
115
    if len(message) < 2:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
116
        print("Too short to be a commit message?")
88 by Martin
Require commit messages to be at least two characters long
117
        return
118
    mp.commit_message = message
48 by Martin Pool
Can now set message and send to pqm
119
    mp.lp_save()
120
121
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
122
def queue_mp(launchpad, mp):
48 by Martin Pool
Can now set message and send to pqm
123
    if not mp.commit_message:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
124
        print("No message set?  Use 'm'.")
48 by Martin Pool
Can now set message and send to pqm
125
        return
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
126
    if mp.queue_status == 'Queued':
127
        mp.setStatus(status='Approved')
62.1.8 by Robert Collins
Set queued revision id when queuing.
128
    mp.setStatus(status='Queued', revid=mp.reviewed_revid)
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
129
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
130
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
131
def send_mp(launchpad, mp, send_mail=True):
132
    if not mp.commit_message:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
133
        print("No message set - %s queued badly? Set a message first." % mp)
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
134
        return False
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
135
    print("merge command to be signed:")
59 by Martin Pool
Try to make mail subject match the one used by Launchpad
136
    raw_message = (
137
        "star-merge %s %s\n"
138
        % (mp.source_branch.composePublicURL(scheme='http'),
139
            mp.target_branch.composePublicURL(scheme='http')))
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
140
    print(raw_message)
59 by Martin Pool
Try to make mail subject match the one used by Launchpad
141
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
142
    config = _mod_config.GlobalStack()
143
    my_email = config.get('email')
144
    print("Will send email from address <%s>" % (my_email,))
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
145
    signer = gpg.GPGStrategy(config)
146
    signed_message = signer.sign(raw_message.encode('utf8'))
62.1.6 by Robert Collins
Include the queuer as a prefix and the branch owner as a suffix on commit messages, to automate current practice.
147
    # TODO: put in bug numbers as well.
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
148
    commit_message = u"(%s) %s (%s)" % (launchpad.me.name, mp.commit_message,
62.1.7 by Robert Collins
Save the tabs, save the world.
149
        mp.source_branch.owner.display_name)
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
150
    message = EmailMessage(my_email, PQM_ADDRESS, commit_message,
52.2.1 by Jelmer Vernooij
Use bzrlib's email message API to send messages off to PQM.
151
                           signed_message)
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
152
    if send_mail:
153
        SMTPConnection(config).send_email(message)
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
154
        print("Sent!")
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
155
    else:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
156
        print("Not sending email - disabled.")
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
157
158
    # suggested subject should match the last comment on the mp so that gmail
159
    # doesn't see it as a separate thread;
160
    # <https://bugs.edge.launchpad.net/hydrazine/+bug/541586>
161
    subject = "Re: [Merge] %s into %s" % (mp.source_branch.bzr_identity,
62.1.7 by Robert Collins
Save the tabs, save the world.
162
        mp.target_branch.bzr_identity)
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
163
    print("Recording that the proposal is submitted to PQM.")
64 by Martin Pool
Add back email support
164
    fed_pqm = "sent to pqm by email\n"
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
165
    mp.createComment(content=fed_pqm, subject=subject)
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
166
    return True
50 by Martin Pool
Post a comment when fed to pqm
167
47 by Martin Pool
Start a script to feed pqm from bmps
168
91.3.2 by Martin
Add iter_merge_proposals and include logic to hide approved mps with unmerged prerequisites
169
def iter_mergable_proposals(project, include_queued=False):
170
    """Iterate over merge proposals for `project` that are ready to be landed
171
172
    If `show_queued` is set, proposals using launchpad queue tracking which
173
    are already due to be landed are also shown. This doesn't matter for merge
174
    commands sent by email instead.
175
176
    Proposals that have unmerged prerequiste branches will not be offered for
177
    merging to prevent accidentally landing unreviewed code.
178
    """
179
    find_status = ['Approved']
180
    if include_queued:
181
        find_status.append('Queued')
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
182
    print('Looking for %r mps in %s' % (find_status, project))
91.3.2 by Martin
Add iter_merge_proposals and include logic to hide approved mps with unmerged prerequisites
183
    for mp in project.getMergeProposals(status=find_status):
184
        prereq = mp.prerequisite_branch
185
        if not prereq or prereq.lifecycle_status == 'Merged':
186
            yield mp
187
188
47 by Martin Pool
Start a script to feed pqm from bmps
189
def main(argv):
70.1.1 by John Arbash Meinel
Properly handle argument parsing.
190
    parser = optparse.OptionParser('%prog [options] PROJECT\n\n'
191
        '  PROJECT is the launchpad project to inspect (eg bzr)')
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
192
    parser.add_option('--nomail', help="Do not send email commands", action="store_true", default=False)
193
    parser.add_option('--queued', help="Examine already queued proposals", action="store_true", default=False)
194
    opts, args = parser.parse_args()
70.1.1 by John Arbash Meinel
Properly handle argument parsing.
195
    if len(args) != 1:
196
        parser.print_usage()
197
        return 1
198
    project = args[0]
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
199
    breezy_setup()
47 by Martin Pool
Start a script to feed pqm from bmps
200
    launchpad = hydrazine.create_session()
70.1.1 by John Arbash Meinel
Properly handle argument parsing.
201
    project = launchpad.projects[project]
48 by Martin Pool
Can now set message and send to pqm
202
    i = 0
91.3.2 by Martin
Add iter_merge_proposals and include logic to hide approved mps with unmerged prerequisites
203
    all_mps = list(iter_mergable_proposals(project,
204
        include_queued=opts.queued))
62.1.9 by Robert Collins
After sending merge proposals, remove them from the queue if they would not have been found originally.
205
    while len(all_mps):
48 by Martin Pool
Can now set message and send to pqm
206
        mp = all_mps[i]
207
        show_mp(mp)
208
209
        while True:
210
            whatnow = read_choice()
211
            if whatnow == 'q':
62.1.10 by Robert Collins
Handle submitting the last pending proposal.
212
                return 0
48 by Martin Pool
Can now set message and send to pqm
213
            elif whatnow in ('h', 'help', '?'):
214
                show_help()
215
            elif whatnow == 'n':
216
                i = (i + 1) % len(all_mps)
217
                break
218
            elif whatnow == 'p':
219
                i -= 1
220
                if i < 0:
221
                    i = len(all_mps) - 1
222
                break
223
            elif whatnow == 'm':
224
                set_message(mp)
225
            elif whatnow == 's':
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
226
                print("Sorry, not currently implemented %r" % whatnow)
89 by Jonathan Riddell
remove unused submit option
227
                """
62.1.5 by Robert Collins
Allow anyone with LP API access to send to PQM without needing to futz with GPG by adding a cron mode that can run in batch to trigger submission emails.
228
                queue_mp(launchpad, mp)
62.1.9 by Robert Collins
After sending merge proposals, remove them from the queue if they would not have been found originally.
229
                if mp.queue_status not in find_status:
230
                    del all_mps[i]
231
                    if i == len(all_mps):
232
                        i -= 1
62.1.10 by Robert Collins
Handle submitting the last pending proposal.
233
                    break
89 by Jonathan Riddell
remove unused submit option
234
                """
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
235
            elif whatnow == 'e':
64 by Martin Pool
Add back email support
236
                if send_mp(launchpad, mp, send_mail=not opts.nomail):
62.1.12 by Robert Collins
Add back manual email submission via 'e'.
237
                    del all_mps[i]
238
                    if i == len(all_mps):
239
                        i -= 1
240
                    break
48 by Martin Pool
Can now set message and send to pqm
241
            elif whatnow == '':
242
                continue
243
            else:
106.1.1 by Jelmer Vernooij
Initial work on port to Python 3.
244
                print("Sorry, don't understand %r" % whatnow)
245
    print("No remaining merge proposals ready to be merged.")
99.1.1 by Martin Packman
Don't refer to find_status when there are no pending mps
246
    return 0
47 by Martin Pool
Start a script to feed pqm from bmps
247
248
if __name__ == '__main__':
249
    sys.exit(main(sys.argv))