~pqm-dev/pqm/trunk

31 by Colin Walters
fix python reexecution bits (now pass make check)
1
#!/usr/bin/env python
1 by Colin Walters
initial import
2
# -*- mode: python; coding: utf-8 -*-
64 by Robert Collins
merge in Canonical pqm improvements
3
# vim:smartindent cinwords=if,elif,else,for,while,try,except,finally,def,class:ts=4:sts=4:sta:et:ai:shiftwidth=4
4
#
1 by Colin Walters
initial import
5
# arch-tag: Simple patch queue manager for tla
47 by Colin Walters
great tla-pqm => arch-pqm conversion (me, wlandry)
6
# Copyright © 2003,2004 Colin Walters <walters@verbum.org>
174.2.2 by Tim Penhey
Updates following review.
7
# Copyright ©  2004 Canonical Ltd.
142 by Robert Collins
Add project awareness to branch specs.
8
#   Author: Robert Collins <robertc@robertcollins.net>
72 by Robert Collins
merge in arx support improvements from walter landry
9
# Copyright © 2003, 2005 Walter Landry
1 by Colin Walters
initial import
10
11
# This program is free software; you can redistribute it and/or modify
12
# it under the terms of the GNU General Public License as published by
13
# the Free Software Foundation; either version 2 of the License, or
14
# (at your option) any later version.
15
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
# GNU General Public License for more details.
20
21
# You should have received a copy of the GNU General Public License
22
# along with this program; if not, write to the Free Software
23
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
30 by Colin Walters
some junk to try finding Python 2.3 if it isn't /usr/bin/python
25
import os,sys
174.5.2 by Daniel Watkins
Copied bzr's Python version checking.
26
27
try:
28
    version_info = sys.version_info
29
except AttributeError:
30
    version_info = 1, 5 # 1.5 or older
31
32
REINVOKE = "__PQM_REINVOKE"
33
NEED_VERS = (2, 4)
34
KNOWN_PYTHONS = ('python2.4', 'python2.5')
35
36
if version_info < NEED_VERS:
37
    if not os.environ.has_key(REINVOKE):
38
        # mutating os.environ doesn't work in old Pythons
39
        os.putenv(REINVOKE, "1")
40
        for python in KNOWN_PYTHONS:
41
            try:
42
                os.execvp(python, [python] + sys.argv)
43
            except OSError:
44
                pass
45
    sys.stderr.write("error: cannot find a suitable python interpreter")
46
    sys.stderr.write("  (need %d.%d or later)" % NEED_VERS)
47
    sys.exit(1)
48
49
if hasattr(os, "unsetenv"):
50
    os.unsetenv(REINVOKE)
51
194 by Robert Collins
Remove usage of popen2.
52
import string, stat, re, glob, getopt, time, traceback, gzip, getpass
239.1.6 by Robert Collins
Move EmailScript into queue/email.py.
53
import smtplib
34 by Colin Walters
major refactoring
54
import logging, logging.handlers
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
55
import pqm
56
from pqm import *
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
57
from pqm.PQMConfigParser import ConfigParser
58
from pqm.commandline import parse_command_line
174.8.1 by Tim Penhey
Alright! Tests all pass again.
59
from pqm.core import get_script_queue, PatchQueueManager
174.7.4 by Tim Penhey
Move some code around in preparation.
60
from pqm.errors import PQMCmdFailure, PQMException
130 by Robert Collins
Extract LockFile to a module.
61
from pqm.lockfile import LockFile
239.1.6 by Robert Collins
Move EmailScript into queue/email.py.
62
from pqm.script import Command
63
from pqm.queue.email import read_email
1 by Colin Walters
initial import
64
47 by Colin Walters
great tla-pqm => arch-pqm conversion (me, wlandry)
65
32 by Colin Walters
clean up execution of tla
66
def runtla_internal(sender, cmd, *args):
47 by Colin Walters
great tla-pqm => arch-pqm conversion (me, wlandry)
67
    return apply(popen_noshell, [arch_path, cmd] + list(args))
32 by Colin Walters
clean up execution of tla
68
174.8.1 by Tim Penhey
Alright! Tests all pass again.
69
32 by Colin Walters
clean up execution of tla
70
def runtla(sender, cmd, *args):
71
    (status, msg, output) = apply(runtla_internal, [sender, cmd] + list(args))
72
    if not ((status is None) or (status == 0)):
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
73
        raise PQMTlaFailure(sender, ["VCS command %s %s failed (%s): %s" % (cmd, args, status, msg)] + output)
34 by Colin Walters
major refactoring
74
    return output
32 by Colin Walters
clean up execution of tla
75
174.8.1 by Tim Penhey
Alright! Tests all pass again.
76
def do_read_mode(logger, options, mail_reply, mail_server, from_address, fromaddr):
3 by Colin Walters
tons of fixes
77
    sender = None
78
    try:
65 by Robert Collins
pickup new logging, and gnupg hash: filtering
79
        (sender, subject, msg, sig) = read_email(logger)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
80
        if options.verify_sigs:
81
            sigid, siguid = verify_sig(
82
                sender, msg, sig, 1, logger, options.keyring)
34 by Colin Walters
major refactoring
83
            open(transaction_file, 'a').write(sigid + '\n')
5 by Colin Walters
add autotools infrastructure, lots of other stuff
84
        fname = 'patch.%d' % (time.time())
85
        logger.info('new patch ' + fname)
86
        f = open('tmp.' + fname, 'w')
87
        f.write('From: ' + sender + '\n')
15 by Colin Walters
merge test passes, woot!
88
        f.write('Subject: ' + subject + '\n')
5 by Colin Walters
add autotools infrastructure, lots of other stuff
89
        f.write(string.join(re.split('\r?\n', msg), '\n')) # canonicalize line endings
90
        f.close()
12 by Colin Walters
make first test pass, but it only sorta works
91
        os.rename('tmp.' + fname, fname)
3 by Colin Walters
tons of fixes
92
    except:
9 by Colin Walters
much more hackery
93
        if sender and mail_reply:
3 by Colin Walters
tons of fixes
94
            server = smtplib.SMTP(mail_server)
95
            tb=string.join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback), '')
5 by Colin Walters
add autotools infrastructure, lots of other stuff
96
            server.sendmail(from_address, [sender], 'From: %s\r\nTo: %s\r\nSubject: error processing requests\r\n\r\n' % (fromaddr, sender) + 'An error was encountered:\n' + tb)
3 by Colin Walters
tons of fixes
97
            server.quit()
98
        logger.exception("Caught exception")
5 by Colin Walters
add autotools infrastructure, lots of other stuff
99
        sys.exit(1)
19 by Colin Walters
only acquire lockfile for running
100
    sys.exit(0)
101
67 by Robert Collins
New release, supports branch permissions, replay, debug output and baz 1.0
102
arch_path = 'baz'
64 by Robert Collins
merge in Canonical pqm improvements
103
arch_impl = None
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
104
logfile_name = 'pqm.log'
64 by Robert Collins
merge in Canonical pqm improvements
105
default_mail_log_level = logging.ERROR
106
precommit_hook = []
107
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
108
(options, args) = parse_command_line(sys.argv[1:])
109
110
if options.show_version:
111
    print "pqm %s" % pqm.__version__
112
    sys.exit(0)
64 by Robert Collins
merge in Canonical pqm improvements
113
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
114
logger = logging.getLogger("pqm")
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
115
# TODO: move the logging confuration somewhere else.
64 by Robert Collins
merge in Canonical pqm improvements
116
logger.setLevel(logging.DEBUG)
239.1.18 by Robert Collins
Adjust StreamHandler construction to be compatible with 2.6 and 2.7.
117
stderr_handler = logging.StreamHandler(sys.stderr)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
118
stderr_handler.setLevel(options.loglevel)
119
stderr_handler.setFormatter(logging.Formatter(fmt="%(name)s [%(thread)d] %(levelname)s: %(message)s"))
64 by Robert Collins
merge in Canonical pqm improvements
120
logger.addHandler(stderr_handler)
121
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
122
if not (options.read_mode or options.run_mode):
64 by Robert Collins
merge in Canonical pqm improvements
123
    logger.error("Either --read or --run must be specified")
124
    sys.exit(1)
125
126
configp = ConfigParser()
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
127
logger.debug("Reading config files: %s" % (options.configfile_names,))
128
configp.read(options.configfile_names)
64 by Robert Collins
merge in Canonical pqm improvements
129
130
if configp.has_option('DEFAULT', 'arch_path'):
131
    arch_path = configp.get('DEFAULT', 'arch_path')
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
132
elif configp.has_option('DEFAULT', 'tlapath'):
64 by Robert Collins
merge in Canonical pqm improvements
133
    logger.warn("Option 'tlapath' is deprecated")
134
    arch_path = configp.get('DEFAULT', 'tlapath')
135
136
if os.access(arch_path, os.X_OK):
137
    logger.error("Can't execute \"%s\", please fix arch_path" % (arch_path,))
138
    sys.exit(1)
139
74 by Robert Collins
move CommandRunner code through to the library, and start testing star-merge command inputs
140
# ugly transitional code
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
141
pqm.logger = logger
64 by Robert Collins
merge in Canonical pqm improvements
142
if configp.has_option('DEFAULT', 'arch_impl'):
143
    impl = configp.get('DEFAULT', 'arch_impl')
144
    if impl == 'tla':
145
        arch_impl = TlaHandler()
146
    elif impl == 'arx':
147
        arch_impl = ArXHandler()
67 by Robert Collins
New release, supports branch permissions, replay, debug output and baz 1.0
148
    elif impl == 'baz':
68 by Robert Collins
merge in Pascal Hakims work, adjusting to retain baz 1.0 support
149
        arch_impl = Baz1_1Handler()
150
    elif impl == 'baz1.0':
151
        arch_impl = Baz1_0Handler()
64 by Robert Collins
merge in Canonical pqm improvements
152
    else:
153
        logger.error("Unknown arch_impl \"%s\"" % (impl,))
154
        sys.exit(1)
143 by Robert Collins
Overhaul of command running logic to start clear Command separation: move Merge to MergeCommand.
155
# FIXME: move this into a nicer place.
174.7.4 by Tim Penhey
Move some code around in preparation.
156
Command.arch_impl = arch_impl
157
Command.arch_path = arch_path
64 by Robert Collins
merge in Canonical pqm improvements
158
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
159
pqm.gpgv_path = configp.get_option('DEFAULT', 'gpgv_path', 'gpgv')
174.8.1 by Tim Penhey
Alright! Tests all pass again.
160
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
161
# The command line paramter overrides the setting in the config file.
162
if options.verify_sigs:
163
    options.verify_sigs = configp.get_boolean_option(
164
        'DEFAULT', 'verify_sigs', True)
64 by Robert Collins
merge in Canonical pqm improvements
165
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
166
if options.queuedir:
167
    queuedir = options.queuedir
168
else:
169
    queuedir = get_queuedir(configp, logger, args)
64 by Robert Collins
merge in Canonical pqm improvements
170
queuedir=os.path.abspath(queuedir)
174.8.1 by Tim Penhey
Alright! Tests all pass again.
171
172
manager = PatchQueueManager(queuedir, configp, options, logger)
173
174
if manager.from_address is None:
175
    logger.error("No from_address specified")
176
    sys.exit(1)
177
178
# Still temporary hack.  Tim Penhey 2008-05-29
179
pqm.pqm_subdir = manager.control_dir
180
# ugly transitional code
181
pqm.logger = logger
182
pqm.workdir = manager.work_dir
183
pqm.runtla = runtla
184
pqm.precommit_hook = precommit_hook
64 by Robert Collins
merge in Canonical pqm improvements
185
186
if not configp.has_option('DEFAULT', 'dont_set_home'):
142 by Robert Collins
Add project awareness to branch specs.
187
    os.environ['HOME'] = queuedir
64 by Robert Collins
merge in Canonical pqm improvements
188
198 by Robert Collins
Results from manual qa: import cycles, and wrong object being called for start/stop test run.
189
if not options.keyring and options.verify_sigs:
64 by Robert Collins
merge in Canonical pqm improvements
190
    if configp.has_option('DEFAULT', 'keyring'):
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
191
        options.keyring = configp.get('DEFAULT', 'keyring')
64 by Robert Collins
merge in Canonical pqm improvements
192
    else:
193
        logger.error("No keyring specified on command line or in config files.")
194
        sys.exit(1)
198 by Robert Collins
Results from manual qa: import cycles, and wrong object being called for start/stop test run.
195
if options.verify_sigs and not os.access(options.keyring, os.R_OK):
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
196
    logger.error("Couldn't access keyring %s" % (options.keyring,))
64 by Robert Collins
merge in Canonical pqm improvements
197
    sys.exit(1)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
198
174.8.1 by Tim Penhey
Alright! Tests all pass again.
199
manager.make_directories()
153 by Robert Collins
Generate incremental status output from precommit hooks.
200
os.chdir(queuedir)
201
154 by Robert Collins
Change StatusFile creation to not depend upon pqm_subdir being set.
202
rev_optionhandler = pqm.BranchSpecOptionHandler(configp, queuedir=queuedir)
144 by Robert Collins
Implement per-project status pages.
203
if len(rev_optionhandler._specs) == 0:
141 by Robert Collins
Rename allowed_revisioons to branch_specs
204
    logger.error("No branches to manage!")
64 by Robert Collins
merge in Canonical pqm improvements
205
    sys.exit(1)
144 by Robert Collins
Implement per-project status pages.
206
for spec in rev_optionhandler._specs:
207
    logger.info("managing branch(s): " + spec)
64 by Robert Collins
merge in Canonical pqm improvements
208
209
210
if configp.has_option('DEFAULT', 'logfile'):
211
    logfile_name = configp.get('DEFAULT', 'logfile')
212
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
213
if not options.no_log:
64 by Robert Collins
merge in Canonical pqm improvements
214
    if not os.path.isabs(logfile_name):
174.8.1 by Tim Penhey
Alright! Tests all pass again.
215
        logfile_name = os.path.join(manager.control_dir, logfile_name)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
216
    logger.debug("Adding log file: %s" % logfile_name)
64 by Robert Collins
merge in Canonical pqm improvements
217
    filehandler = logging.FileHandler(logfile_name)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
218
    if options.loglevel >= logging.WARN:
64 by Robert Collins
merge in Canonical pqm improvements
219
        filehandler.setLevel(logging.INFO)
220
    else:
221
        filehandler.setLevel(logging.DEBUG)
222
    logger.addHandler(filehandler)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
223
    filehandler.setFormatter(logging.Formatter(
224
            fmt="%(asctime)s %(name)s [%(thread)d] %(levelname)s: %(message)s",
225
            datefmt="%b %d %H:%M:%S"))
64 by Robert Collins
merge in Canonical pqm improvements
226
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
227
if not options.debug_mode:
64 by Robert Collins
merge in Canonical pqm improvements
228
    # Don't log to stderr past this point
229
    logger.removeHandler(stderr_handler)
230
231
transaction_file = os.path.join(queuedir, 'transactions-completed')
232
if os.access(transaction_file, os.R_OK):
233
    lines = open(transaction_file).readlines()
234
    for line in lines:
115 by Robert Collins
Patch from Walter Landry to rename arch-pqm to pqm throughout.
235
        pqm.used_transactions[line[0:-1]] = 1
64 by Robert Collins
merge in Canonical pqm improvements
236
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
237
if options.read_mode:
174.8.1 by Tim Penhey
Alright! Tests all pass again.
238
    do_read_mode(logger, options, manager.mail_reply, manager.mail_server,
239
                 manager.from_address, manager.nice_from_address)
174.2.1 by Tim Penhey
Changed from getopt to optparse to parse command line arguments.
240
241
assert(options.run_mode)
242
174.8.1 by Tim Penhey
Alright! Tests all pass again.
243
script_queue = get_script_queue(
217 by Robert Collins
Use a PQM instance rather than the global workdir variable to obtain the queuedir for launchpadlib support.
244
    queuedir, logger, rev_optionhandler, configp, options, manager)
174.8.1 by Tim Penhey
Alright! Tests all pass again.
245
246
manager.run(script_queue)
247
1 by Colin Walters
initial import
248
logger.info("main thread exiting...")
249
sys.exit(0)
64 by Robert Collins
merge in Canonical pqm improvements
250