Merge lp:~broder/ubuntu-dev-tools/backportpackage into lp:~ubuntu-dev/ubuntu-dev-tools/trunk

Proposed by Evan Broder
Status: Merged
Merged at revision: 830
Proposed branch: lp:~broder/ubuntu-dev-tools/backportpackage
Merge into: lp:~ubuntu-dev/ubuntu-dev-tools/trunk
Diff against target: 1120 lines (+611/-146) (has conflicts)
8 files modified
backportpackage (+293/-0)
debian/changelog (+7/-0)
doc/backportpackage.1 (+105/-0)
doc/sponsor-patch.1 (+8/-3)
setup.py (+1/-0)
sponsor-patch (+61/-143)
ubuntutools/builder.py (+81/-0)
ubuntutools/logger.py (+55/-0)
Text conflict in debian/changelog
To merge this branch: bzr merge lp:~broder/ubuntu-dev-tools/backportpackage
Reviewer Review Type Date Requested Status
Scott Kitterman interface Pending
Benjamin Drung Pending
Evan Broder Pending
Stefano Rivera Pending
Review via email: mp+43491@code.launchpad.net

This proposal supersedes a proposal from 2010-12-12.

Description of the change

At the Ubuntu Backports BOF at UDS-N (https://blueprints.edge.launchpad.net/ubuntu/+spec/ubuntutheproject-backports-n-bof), we discussed creating tools to make it easier to test backports. ubuntu-dev-tools seems like the logical place for it to live.

backportpackage is sort of vaguely analogous to syncpackage for backports. Note that while it should have all of the correct semantics for actually doing a backport to the Ubuntu archive, those should continue to be handled by the Archive Admins. backportpackage is really intended for uploading backports to a PPA.

To post a comment you must log in.
Revision history for this message
Benjamin Drung (bdrung) wrote : Posted in a previous version of this proposal

This script belongs to this package.

You should set DEB_VENDOR=Ubuntu for dpkg-source.

Revision history for this message
Stefano Rivera (stefanor) wrote : Posted in a previous version of this proposal

Should this be installed by setup.py? (it isn't, yet it appears in the changelog).

Note that on Debian users can't use ppa:X syntax with dput, due to http://bugs.debian.org/505173. I don't know if there's anything we can do about that here except defaulting to the release pocket unless uploading to 'ubuntu'.

{syncpackage, sponsor-patch} uses the current directory, ack-sync (although it isn't installed) uses /tmp/ack-sync, backportpackage uses a temporary directory. sponsor-patch prints a file:/// URI, others print local paths. I wish there were some consistency.

Revision history for this message
Evan Broder (broder) wrote : Posted in a previous version of this proposal

Heh. I...guess I sort of expected the script to get magically installed. Clever plan, that one.

In terms of the directory to use, I'm happy to change backportpackage to use the current directory, but I honestly kind of hate it when scripts go and drop stuff all over my cwd. Let me know what your preferences are as package maintainers.

Revision history for this message
Stefano Rivera (stefanor) wrote : Posted in a previous version of this proposal

I'm with you on the current directory thing. But I'm also hesitant about adding a 3rd behavior.

I guess that as this generates something the user isn't expecting to work with, /tmp is correct. But then I think we should give mkdtemp a 'backportpackage' prefix (so it can be easily tab-completed) and document the fact that it'll build in /tmp in the manpage.

Revision history for this message
Stefano Rivera (stefanor) wrote : Posted in a previous version of this proposal

LGTM, although still with reservations about defaulting to the -backports pocket (which makes the tool hard to use on Debian, you have to say you are going to upload to Debian, then dput to your ppa in another terminal, while it's asking for confirmation)

review: Approve
Revision history for this message
Evan Broder (broder) wrote : Posted in a previous version of this proposal

I think you're reading the logic backwards. backportpackage defaults to the release pocket *unless* the upload target is "ubuntu" (i.e. if you were doing a real backport).

Last time I checked, PPAs don't support uploading to anything but the release pocket, so the intent is that if you upload to a PPA, it goes to the release pocket, and if you upload to the archive proper, it goes to the -backports pocket.

Revision history for this message
Benjamin Drung (bdrung) wrote : Posted in a previous version of this proposal

sponsor-patch prints a file:/// URI to make them click-able in gnome-terminal.

The default workdir of sponsor-patch can be changed to point to temporary directory instead of cwd.

Revision history for this message
Benjamin Drung (bdrung) wrote : Posted in a previous version of this proposal

Use subprocess.call instead of subprocess.check_call and print a proper error message if the command fails.

How do you ensure that the orig source tarball is available in the upload destination?

review: Needs Fixing
Revision history for this message
Evan Broder (broder) wrote : Posted in a previous version of this proposal

Ok, file:/// justification makes sense. I'll updated backportpackage to use that.

I'm also not currently checking to see if the orig tarball is available. Fixing by passing -sa to debuild -S.

Revision history for this message
Stefano Rivera (stefanor) wrote : Posted in a previous version of this proposal

Sorry yes, I was commenting from memory, always a bad idea. The only check based on "ppa:" is to add ~ppa1. That's fine.

New issue: Your use subprocess.call and check_call. You use it correctly for pull-lp-source.
For dch, you should probably check "== 0", instead of "not" because returncode can be negative if dch was killed.
For debuild, remember that check_call will throw, not return, on error.

review: Needs Fixing
Revision history for this message
Stefano Rivera (stefanor) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Evan Broder (broder) wrote : Posted in a previous version of this proposal

Actually, please hold off on merging this for the moment. I'd like to adjust the interface to accept multiple destination releases and do a backport to each one.

review: Needs Fixing
Revision history for this message
Benjamin Drung (bdrung) wrote : Posted in a previous version of this proposal

"Something went wrong updating the package changelog" is not very precise. You could tell that the "dch" command fail and tell the exit code.

847. By Evan Broder

backportpackage: Unquote URLs we get back from LP

848. By Evan Broder

backportpackage: Abort if build fails

849. By Evan Broder

backportpackage: Error if neither -b nor -u is specified

850. By Evan Broder

backportpackage: --source/--destination instead of --from/--to

851. By Evan Broder

backportpackage: Eliminate global variables

852. By Evan Broder

backportpackage: Pass around options instead of options container

853. By Evan Broder

backportpackage: If no dest release is specified, default to the current release

854. By Evan Broder

doc/backportpackage.1: Add an EXAMPLES section

855. By Evan Broder

backportpackage: Allow specifying a working directory

856. By Evan Broder

backportpackage: Accept a URL or path to a .dsc file as an alternative to a source package name

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'backportpackage'
2--- backportpackage 1970-01-01 00:00:00 +0000
3+++ backportpackage 2010-12-16 09:14:08 +0000
4@@ -0,0 +1,293 @@
5+#!/usr/bin/python
6+# -*- coding: utf-8 -*-
7+# ##################################################################
8+#
9+# This program is free software; you can redistribute it and/or
10+# modify it under the terms of the GNU General Public License
11+# as published by the Free Software Foundation; version 2.
12+#
13+# This program is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU General Public License for more details.
17+#
18+# See file /usr/share/common-licenses/GPL-2 for more details.
19+#
20+# ##################################################################
21+
22+import optparse
23+import os
24+import shutil
25+import subprocess
26+import sys
27+import tempfile
28+import urllib
29+
30+from debian.deb822 import Dsc
31+import launchpadlib.launchpad
32+import lsb_release
33+
34+from ubuntutools.builder import getBuilder
35+from ubuntutools.logger import Logger
36+
37+def error(msg):
38+ Logger.error(msg)
39+ sys.exit(1)
40+
41+def check_call(cmd, *args, **kwargs):
42+ Logger.command(cmd)
43+ ret = subprocess.call(cmd, *args, **kwargs)
44+ if ret != 0:
45+ error('%s returned %d' % (cmd, ret))
46+
47+def parse(args):
48+ usage = 'Usage: %prog [options] <source package name or .dsc URL/file>'
49+ p = optparse.OptionParser(usage)
50+ p.add_option('-d', '--destination',
51+ dest='dest_releases',
52+ default=[],
53+ action='append',
54+ help='Backport to DEST release (default: current release)',
55+ metavar='DEST')
56+ p.add_option('-s', '--source',
57+ dest='source_release',
58+ default=None,
59+ help='Backport from SOURCE release (default: devel release)',
60+ metavar='SOURCE')
61+ p.add_option('-b', '--build',
62+ dest='build',
63+ default=False,
64+ action='store_true',
65+ help='Build the package before uploading (default: %default)')
66+ p.add_option('-B', '--builder',
67+ dest='builder',
68+ default=None,
69+ help='Specify the package builder (default: pbuilder)',
70+ metavar='BUILDER')
71+ p.add_option('-u', '--upload',
72+ dest='upload',
73+ help='Specify an upload destination',
74+ metavar='UPLOAD')
75+ p.add_option('-v', '--version',
76+ dest='version',
77+ default=None,
78+ help='Package version to backport (or verify)',
79+ metavar='VERSION')
80+ p.add_option('-w', '--workdir',
81+ dest='workdir',
82+ default=None,
83+ help='Specify a working directory (default: temporary dir)',
84+ metavar='WORKDIR')
85+ p.add_option('-l', '--launchpad',
86+ dest='launchpad',
87+ default='production',
88+ help='Launchpad instance to connect to (default: %default)',
89+ metavar='INSTANCE')
90+
91+ opts, args = p.parse_args(args)
92+ if len(args) != 1:
93+ p.error('You must specify a single source package or a .dsc URL/path')
94+ if not opts.upload and not opts.build:
95+ p.error('Nothing to do')
96+
97+ return opts, args
98+
99+def find_release_package(lp, package, version, source_release):
100+ ubuntu = lp.distributions['ubuntu']
101+ archive = ubuntu.main_archive
102+ series = ubuntu.getSeries(name_or_version=source_release)
103+ status = 'Published'
104+ for pocket in ('Updates', 'Security', 'Release'):
105+ try:
106+ srcpkg = archive.getPublishedSources(source_name=package,
107+ distro_series=series,
108+ pocket=pocket,
109+ status=status,
110+ exact_match=True)[0]
111+ break
112+ except IndexError:
113+ continue
114+ else:
115+ error('Unable to find package %s in release %s' %
116+ (package, source_release))
117+
118+ if version and version != srcpkg.source_package_version:
119+ error('Requested backport of version %s but %s is at version %s' %
120+ (version, package, srcpkg.source_package_version))
121+
122+ return srcpkg
123+
124+def find_version_package(lp, package, version):
125+ ubuntu = lp.distributions['ubuntu']
126+ archive = ubuntu.main_archive
127+ try:
128+ # Might get more than one (i.e. same version in multiple
129+ # releases), but they should all be identical
130+ return archive.getPublishedSources(source_name=package,
131+ version=version)[0]
132+ except IndexError:
133+ error('Version %s of package %s was never published in Ubuntu' %
134+ (version, package))
135+
136+def dscurl_from_package(lp, workdir, package, version, source_release):
137+ if not source_release and not version:
138+ source_release = lp.distributions['ubuntu'].current_series.name
139+
140+ # If source_release is specified, then version is just for
141+ # verification
142+ if source_release:
143+ srcpkg = find_release_package(lp, package, version, source_release)
144+ else:
145+ srcpkg = find_version_package(lp, package, version)
146+
147+ for f in srcpkg.sourceFileUrls():
148+ if f.endswith('.dsc'):
149+ return urllib.unquote(f)
150+ else:
151+ error('Package %s contains no .dsc file' % package)
152+
153+def dscurl_from_dsc(package):
154+ path = os.path.abspath(os.path.expanduser(package))
155+ if os.path.exists(path):
156+ return 'file://%s' % path
157+ else:
158+ # Can't resolve it as a local path? Let's just hope it's good
159+ # as-is
160+ return package
161+
162+def fetch_package(lp, workdir, package, version, source_release):
163+ # Returns the path to the .dsc file that was fetched
164+
165+ if package.endswith('.dsc'):
166+ dsc = dscurl_from_dsc(package)
167+ else:
168+ dsc = dscurl_from_package(lp, workdir, package, version, source_release)
169+
170+ check_call(['dget',
171+ '--download-only',
172+ '--allow-unauthenticated',
173+ dsc],
174+ cwd=workdir)
175+ return os.path.join(workdir, os.path.basename(dsc))
176+
177+def get_backport_version(version, upload, release):
178+ v = version + ('~%s1' % release)
179+ if upload and upload.startswith('ppa:'):
180+ v += '~ppa1'
181+ return v
182+
183+def get_backport_dist(upload, release):
184+ if not upload or upload == 'ubuntu':
185+ return '%s-backports' % release
186+ else:
187+ return release
188+
189+def do_build(workdir, package, release, bp_version, builder):
190+ builder = getBuilder(builder)
191+ if not builder:
192+ return
193+
194+ return builder.build(os.path.join(workdir,
195+ '%s_%s.dsc' % (package, bp_version)),
196+ release,
197+ workdir)
198+
199+def do_upload(workdir, package, bp_version, upload):
200+ prompt = 'Do you want to upload this to %s? [Y/n]' % upload
201+ while True:
202+ answer = raw_input(prompt).strip().lower()
203+ if answer in ('', 'y', 'yes'):
204+ break
205+ elif answer in ('n', 'no'):
206+ return
207+
208+ check_call(['dput',
209+ upload,
210+ '%s_%s_source.changes' %
211+ (package, bp_version)],
212+ cwd=workdir)
213+
214+
215+def do_backport(workdir, package, dscfile, version, release, build, builder, upload):
216+ check_call(['dpkg-source',
217+ '-x',
218+ dscfile,
219+ package],
220+ cwd=workdir)
221+ srcdir = os.path.join(workdir, package)
222+
223+ bp_version = get_backport_version(version, upload, release)
224+ bp_dist = get_backport_dist(upload, release)
225+
226+ check_call(['dch',
227+ '--force-bad-version',
228+ '--preserve',
229+ '--newversion', bp_version,
230+ '--distribution', bp_dist,
231+ 'No-change backport to %s' % release],
232+ cwd=srcdir)
233+ check_call(['debuild', '-S', '-sa'],
234+ cwd=srcdir)
235+
236+ if ':' in bp_version:
237+ bp_version = bp_version[bp_version.find(':')+1:]
238+
239+ print 'Please check the package in file://%s carefully' % workdir
240+ if build:
241+ if 0 != do_build(workdir, package, release, bp_version, builder):
242+ error('Package failed to build; aborting')
243+ if upload:
244+ do_upload(workdir, package, bp_version, upload)
245+
246+ shutil.rmtree(srcdir)
247+
248+def main(args):
249+ os.environ['DEB_VENDOR'] = 'Ubuntu'
250+
251+ opts, (package_or_dsc,) = parse(args[1:])
252+
253+ script_name = os.path.basename(sys.argv[0])
254+ lp = launchpadlib.launchpad.Launchpad.login_anonymously(script_name,
255+ opts.launchpad)
256+
257+ if not opts.dest_releases:
258+ try:
259+ distinfo = lsb_release.get_distro_information()
260+ opts.dest_releases = [distinfo['CODENAME']]
261+ except:
262+ error('No destination release specified and unable to guess yours')
263+
264+ if opts.workdir:
265+ workdir = os.path.expanduser(opts.workdir)
266+ else:
267+ workdir = tempfile.mkdtemp(prefix='backportpackage-')
268+
269+ if not os.path.exists(workdir):
270+ os.makedirs(workdir)
271+
272+ try:
273+ dscfile = fetch_package(lp,
274+ workdir,
275+ package_or_dsc,
276+ opts.version,
277+ opts.source_release)
278+
279+ dsc = Dsc(open(os.path.join(workdir, dscfile)))
280+ package = dsc['Source']
281+ version = dsc['Version']
282+
283+ for release in opts.dest_releases:
284+ do_backport(workdir,
285+ package,
286+ dscfile,
287+ version,
288+ release,
289+ opts.build,
290+ opts.builder,
291+ opts.upload)
292+ finally:
293+ if not opts.workdir:
294+ shutil.rmtree(workdir)
295+
296+if __name__ == '__main__':
297+ sys.exit(main(sys.argv))
298
299=== modified file 'debian/changelog'
300--- debian/changelog 2010-12-15 14:50:54 +0000
301+++ debian/changelog 2010-12-16 09:14:08 +0000
302@@ -5,6 +5,7 @@
303 Use the 'production' LP instance instead of 'edge' (which is going away).
304 * pbuilder-dist: Fix typo in local archive support, introduced in 0.107.
305
306+<<<<<<< TREE
307 [ Benjamin Drung ]
308 * pull-lp-source: Unquote URI to get "+" instead of "%2B" in the file name
309 (LP: #681114).
310@@ -13,6 +14,12 @@
311 * grep-merges: New tool.
312
313 -- Benjamin Drung <bdrung@ubuntu.com> Tue, 14 Dec 2010 18:21:37 +0100
314+=======
315+ [ Evan Broder ]
316+ * backportpackage: new script for testing backport requests in a PPA.
317+
318+ -- Evan Broder <evan@ebroder.net> Sat, 11 Dec 2010 14:11:54 -0800
319+>>>>>>> MERGE-SOURCE
320
321 ubuntu-dev-tools (0.107) experimental; urgency=low
322
323
324=== added file 'doc/backportpackage.1'
325--- doc/backportpackage.1 1970-01-01 00:00:00 +0000
326+++ doc/backportpackage.1 2010-12-16 09:14:08 +0000
327@@ -0,0 +1,105 @@
328+.TH BACKPORTPACKAGE "1" "December 2010" "ubuntu-dev-tools"
329+.SH NAME
330+backportpackage \- helper to test package backports
331+.SH SYNOPSIS
332+.TP
333+.B backportpackage \fR[\fIadditional options\fR]
334+\-\-upload <\fIupload target\fR>
335+.br
336+<\fIsource package name or .dsc URL/file\fR>
337+.PP
338+.B backportpackage \-h
339+.SH OPTIONS
340+.TP
341+.B \-d \fIDEST\fR, \-\-destination=\fIDEST\fR
342+\fBRequired\fR. Backport the package to the specified Ubuntu
343+release. If this option is unspecified, then \fBbackportpackage\fR
344+defaults to the release on which it is currently running.
345+.TP
346+.B \-s \fISOURCE\fR, \-\-source=\fISOURCE\fR
347+Backport the package from the specified Ubuntu release. If neither
348+this option nor \fB\-\-version\fR are specified, then
349+\fBbackportpackage\fR defaults to the current Ubuntu development
350+release.
351+.TP
352+.B \-b, \-\-build
353+Build the package with the specified builder before uploading. Note
354+for \fBpbuilder\fR(8) users: This assumes the common configuration,
355+where the \fBDIST\fR environment is read by \fBpbuilderrc\fR(5) to
356+select the correct base image.
357+.TP
358+.B \-B \fIBUILDER\fR, \fB\-\-builder\fR=\fIBUILDER
359+Use the specified builder to build the package. Supported are
360+\fBpbuilder\fR(8) and \fBsbuild\fR(1). This overrides
361+\fBUBUNTUTOOLS_BUILDER\fR. The default is \fBpbuilder\fR(8).
362+.TP
363+.B \-u \fIUPLOAD\fR, \-\-upload=\fIUPLOAD\fR
364+Upload to \fIUPLOAD\fR with \fBdput\fR(1) (after confirmation).
365+.TP
366+.B \-v \fIVERSION\fR, \-\-version=\fIVERSION\fR
367+If the \fB\-\-source\fR option is specified, then
368+\fBbackportpackage\fR verifies that the current version of \fIsource
369+package\fR in \fISOURCE\fR is the same as \fIVERSION\fR. Otherwise,
370+\fBbackportpackage\fR finds version \fIVERSION\fR of \fIsource
371+package\fR, regardless of the release in which it was published (or if
372+that version is still current). This option is ignored if a .dsc URL
373+or path is passed in instead of a source package name.
374+.TP
375+.B \-w \fIWORKDIR\fR, \-\-workdir=\fIWORKDIR\fR
376+If \fIWORKDIR\fR is specified, then all files are downloaded,
377+unpacked, built into, and otherwise manipulated in
378+\fIWORKDIR\fR. Otherwise, a temporary directory is created, which is
379+deleted before \fIbackportpackage\fR exits.
380+.TP
381+.B \-l \fIINSTANCE\fR, \-\-launchpad=\fIINSTANCE\fR
382+Use the specified instance of Launchpad (e.g. "staging"), instead of
383+the default of "production".
384+.SH DESCRIPTION
385+\fBbackportpackage\fR fetches a package from one Ubuntu release or
386+from a specified .dsc path or URL and creates a no-change backport of
387+that package to a previous release, optionally doing a test build of
388+the package and/or uploading the resulting backport for testing.
389+.PP
390+Unless a working directory is specified, the backported package is
391+fetched and built in a temporary directory in \fB/tmp\fR, which is
392+removed once the script finishes running.
393+.PP
394+\fBbackportpackage\fR is only recommended for testing backports in a
395+PPA, not uploading backports to the Ubuntu archive.
396+.SH ENVIRONMENT
397+.TP
398+.B UBUNTUTOOLS_BUILDER
399+The default builder for Ubuntu development tools that support it
400+(including \fBbackportpackage\fR). Supported are \fBpbuilder\fR(8) and
401+\fBsbuild\fR(1). If unset and not provided on the command line,
402+\fBpbuilder\fR(8) is used.
403+.SH EXAMPLES
404+Test-build in your PPA a backport of znc from the current development
405+release to your workstation's release, deleting the build products
406+afterwards:
407+.IP
408+.nf
409+.B backportpackage -u ppa:\fIuser\fR/\fIppa\fB znc
410+.fi
411+.PP
412+Backport squashfs-tools from Maverick to both Karmic and Lucid and
413+test-build both locally, leaving all build products in the current
414+working directory:
415+.IP
416+.nf
417+.B backportpackage -b -s maverick -d karmic -d lucid -w . \\\\
418+.B " "squashfs-tools
419+.fi
420+.PP
421+Fetch a package from a PPA, backport it to Hardy, then upload it back
422+to the same PPA:
423+.IP
424+.nf
425+.B backportpackage -d hardy -u ppa:\fIuser\fR/\fIppa\fR \\\\
426+.B " "https://launchpad.net/\fIsome/file.dsc\fR
427+.fi
428+.SH AUTHOR
429+\fBbackportpackage\fR and this manpage were written by Evan Broder
430+<evan@ebroder.net>
431+.PP
432+Both are released under GNU General Public License, version 2.
433
434=== modified file 'doc/sponsor-patch.1'
435--- doc/sponsor-patch.1 2010-11-26 18:34:31 +0000
436+++ doc/sponsor-patch.1 2010-12-16 09:14:08 +0000
437@@ -61,7 +61,7 @@
438 .B \-B \fIBUILDER\fR, \fB\-\-builder\fR=\fIBUILDER
439 Use the specify builder to build the package.
440 Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
441-This overrides \fBSPONSOR_PATCH_BUILDER\fR.
442+This overrides \fBUBUNTUTOOLS_BUILDER\fR and \fBSPONSOR_PATCH_BUILDER\fR.
443 The default is \fBpbuilder\fR(8).
444 .TP
445 .BR \-e ", " \-\-edit
446@@ -90,10 +90,15 @@
447 .SH ENVIRONMENT
448
449 .TP
450+.B UBUNTUTOOLS_BUILDER
451+The default builder for Ubuntu development tools that support it (including \fBsponsor\-patch\fR.
452+Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
453+If unset and not provided on the command line, \fBpbuilder\fR(8) is used.
454+
455+.TP
456 .B SPONSOR_PATCH_BUILDER
457 The default builder for \fBsponsor\-patch\fR.
458-Supported are \fBpbuilder\fR(8) and \fBsbuild\fR(1).
459-If unset and not provided on the command line, \fBpbuilder\fR(8) is used.
460+If specified, this overrides \fBUBUNTUTOOLS_BUILDER\fR.
461
462 .TP
463 .B SPONSOR_PATCH_WORKDIR
464
465=== modified file 'setup.py'
466--- setup.py 2010-12-15 20:50:30 +0000
467+++ setup.py 2010-12-16 09:14:08 +0000
468@@ -16,6 +16,7 @@
469 setup(name='ubuntu-dev-tools',
470 version=version,
471 scripts=['404main',
472+ 'backportpackage',
473 'check-symbols',
474 'dch-repeat',
475 'dgetlp',
476
477=== modified file 'sponsor-patch'
478--- sponsor-patch 2010-11-26 18:34:31 +0000
479+++ sponsor-patch 2010-12-16 09:14:08 +0000
480@@ -28,7 +28,9 @@
481 import debian.debian_support
482 import launchpadlib.launchpad
483
484+from ubuntutools.builder import getBuilder
485 import ubuntutools.update_maintainer
486+from ubuntutools.logger import Logger
487
488 USER_ABORT = 2
489
490@@ -57,7 +59,7 @@
491 dsc_file = None
492 for url in source_files:
493 filename = urllib.unquote(os.path.basename(url))
494- Print.info("Downloading %s..." % (filename))
495+ Logger.info("Downloading %s..." % (filename))
496 urllib.urlretrieve(url, filename)
497 if url.endswith(".dsc"):
498 dsc_file = filename
499@@ -148,50 +150,6 @@
500 return self.project == "ubuntu"
501
502
503-class Builder(object):
504- def __init__(self, name):
505- self.name = name
506- cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"]
507- process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
508- self.architecture = process.communicate()[0].strip()
509-
510- def get_architecture(self):
511- return self.architecture
512-
513- def get_name(self):
514- return self.name
515-
516-
517-class Pbuilder(Builder):
518- def __init__(self):
519- Builder.__init__(self, "pbuilder")
520-
521- def build(self, dsc_file, dist, result_directory):
522- # TODO: Do not rely on a specific pbuilder configuration.
523- cmd = ["sudo", "-E", "DIST=" + dist, "pbuilder", "--build",
524- "--distribution", dist, "--architecture", self.architecture,
525- "--buildresult", result_directory, dsc_file]
526- Print.command(cmd)
527- return subprocess.call(cmd)
528-
529-
530-class Sbuild(Builder):
531- def __init__(self):
532- Builder.__init__(self, "sbuild")
533-
534- def build(self, dsc_file, dist, result_directory):
535- workdir = os.getcwd()
536- Print.command(["cd", result_directory])
537- os.chdir(result_directory)
538- cmd = ["sbuild", "--arch-all", "--dist=" + dist,
539- "--arch=" + self.architecture, dsc_file]
540- Print.command(cmd)
541- result = subprocess.call(cmd)
542- Print.command(["cd", workdir])
543- os.chdir(workdir)
544- return result
545-
546-
547 class Patch(object):
548 def __init__(self, patch_file):
549 self.patch_file = patch_file
550@@ -220,41 +178,6 @@
551 self.changed_files)) > 0
552
553
554-class Print(object):
555- script_name = os.path.basename(sys.argv[0])
556- verbose = False
557-
558- @classmethod
559- def command(cls, cmd):
560- if cls.verbose:
561- for i in xrange(len(cmd)):
562- if cmd[i].find(" ") >= 0:
563- cmd[i] = '"' + cmd[i] + '"'
564- print "%s: I: %s" % (script_name, " ".join(cmd))
565-
566- @classmethod
567- def debug(cls, message):
568- if cls.verbose:
569- print "%s: D: %s" % (script_name, message)
570-
571- @classmethod
572- def error(cls, message):
573- print >> sys.stderr, "%s: Error: %s" % (script_name, message)
574-
575- @classmethod
576- def info(cls, message):
577- if cls.verbose:
578- print "%s: I: %s" % (script_name, message)
579-
580- @classmethod
581- def normal(cls, message):
582- print "%s: %s" % (script_name, message)
583-
584- @classmethod
585- def set_verbosity(cls, verbose):
586- cls.verbose = verbose
587-
588-
589 def get_source_package_name(bug_task):
590 package = None
591 if bug_task.bug_target_name != "ubuntu":
592@@ -334,7 +257,7 @@
593 def edit_source():
594 # Spawn shell to allow modifications
595 cmd = [get_user_shell()]
596- Print.command(cmd)
597+ Logger.command(cmd)
598 print """An interactive shell was launched in
599 file://%s
600 Edit your files. When you are done, exit the shell. If you wish to abort the
601@@ -342,7 +265,7 @@
602 """ % (os.getcwd()),
603 returncode = subprocess.call(cmd)
604 if returncode != 0:
605- Print.error("Shell exited with exit value %i." % (returncode))
606+ Logger.error("Shell exited with exit value %i." % (returncode))
607 sys.exit(1)
608
609 def get_fixed_lauchpad_bugs(changes_file):
610@@ -380,10 +303,10 @@
611 linked_branches = map(lambda b: b.branch, bug.linked_branches)
612 if len(attached_patches) == 0 and len(linked_branches) == 0:
613 if len(bug.attachments) == 0:
614- Print.error("No attachment and no linked branch found on bug #%i." \
615+ Logger.error("No attachment and no linked branch found on bug #%i." \
616 % (bug.id))
617 else:
618- Print.error(("No attached patch and no linked branch found. Go " \
619+ Logger.error(("No attached patch and no linked branch found. Go " \
620 "to https://launchpad.net/bugs/%i and mark an " \
621 "attachment as patch.") % (bug.id))
622 sys.exit(1)
623@@ -393,13 +316,13 @@
624 branch = linked_branches[0].bzr_identity
625 else:
626 if len(attached_patches) == 0:
627- Print.normal("https://launchpad.net/bugs/%i has %i branches " \
628+ Logger.normal("https://launchpad.net/bugs/%i has %i branches " \
629 "linked:" % (bug.id, len(linked_branches)))
630 elif len(linked_branches) == 0:
631- Print.normal("https://launchpad.net/bugs/%i has %i patches" \
632+ Logger.normal("https://launchpad.net/bugs/%i has %i patches" \
633 " attached:" % (bug.id, len(attached_patches)))
634 else:
635- Print.normal("https://launchpad.net/bugs/%i has %i branch(es)" \
636+ Logger.normal("https://launchpad.net/bugs/%i has %i branch(es)" \
637 " linked and %i patch(es) attached:" % \
638 (bug.id, len(linked_branches), len(attached_patches)))
639 i = 0
640@@ -421,11 +344,11 @@
641 patch_filename = re.sub(" ", "_", patch.title)
642 if not reduce(lambda r, x: r or patch.title.endswith(x),
643 (".debdiff", ".diff", ".patch"), False):
644- Print.info("Patch %s does not have a proper file extension." % \
645+ Logger.info("Patch %s does not have a proper file extension." % \
646 (patch.title))
647 patch_filename += ".patch"
648
649- Print.info("Downloading %s." % (patch_filename))
650+ Logger.info("Downloading %s." % (patch_filename))
651 patch_file = open(patch_filename, "w")
652 patch_file.write(patch.data.open().read())
653 patch_file.close()
654@@ -436,18 +359,18 @@
655 if os.path.isdir(dir_name):
656 shutil.rmtree(dir_name)
657 cmd = ["bzr", "branch", branch]
658- Print.command(cmd)
659+ Logger.command(cmd)
660 if subprocess.call(cmd) != 0:
661- Print.error("Failed to download branch %s." % (branch))
662+ Logger.error("Failed to download branch %s." % (branch))
663 sys.exit(1)
664 return dir_name
665
666 def merge_branch(branch):
667 edit = False
668 cmd = ["bzr", "merge", branch]
669- Print.command(cmd)
670+ Logger.command(cmd)
671 if subprocess.call(cmd) != 0:
672- Print.error("Failed to merge branch %s." % (branch))
673+ Logger.error("Failed to merge branch %s." % (branch))
674 ask_for_manual_fixing()
675 edit = True
676 return edit
677@@ -456,9 +379,9 @@
678 cmd = ["dpkg-source", "--no-preparation", "-x", dsc_file]
679 if not verbose:
680 cmd.insert(1, "-q")
681- Print.command(cmd)
682+ Logger.command(cmd)
683 if subprocess.call(cmd) != 0:
684- Print.error("Extraction of %s failed." % (os.path.basename(dsc_file)))
685+ Logger.error("Extraction of %s failed." % (os.path.basename(dsc_file)))
686 sys.exit(1)
687
688 def apply_patch(task, patch):
689@@ -466,9 +389,9 @@
690 if patch.is_debdiff():
691 cmd = ["patch", "--merge", "--force", "-p",
692 str(patch.get_strip_level()), "-i", patch.full_path]
693- Print.command(cmd)
694+ Logger.command(cmd)
695 if subprocess.call(cmd) != 0:
696- Print.error("Failed to apply debdiff %s to %s %s." % \
697+ Logger.error("Failed to apply debdiff %s to %s %s." % \
698 (patch.get_name(), task.package, task.get_version()))
699 if not edit:
700 ask_for_manual_fixing()
701@@ -477,9 +400,9 @@
702 # FIXME: edit-patch needs a non-interactive mode
703 # https://launchpad.net/bugs/612566
704 cmd = ["edit-patch", patch.full_path]
705- Print.command(cmd)
706+ Logger.command(cmd)
707 if subprocess.call(cmd) != 0:
708- Print.error("Failed to apply diff %s to %s %s." % \
709+ Logger.error("Failed to apply diff %s to %s %s." % \
710 (patch.get_name(), task.package, task.get_version()))
711 if not edit:
712 ask_for_manual_fixing()
713@@ -493,11 +416,11 @@
714 try:
715 os.makedirs(workdir)
716 except os.error, error:
717- Print.error("Failed to create the working directory %s [Errno " \
718+ Logger.error("Failed to create the working directory %s [Errno " \
719 "%i]: %s." % (workdir, error.errno, error.strerror))
720 sys.exit(1)
721 if workdir != os.getcwd():
722- Print.command(["cd", workdir])
723+ Logger.command(["cd", workdir])
724 os.chdir(workdir)
725
726 script_name = os.path.basename(sys.argv[0])
727@@ -510,13 +433,13 @@
728 bug_tasks = map(lambda x: BugTask(x, launchpad), bug.bug_tasks)
729 ubuntu_tasks = filter(lambda x: x.is_ubuntu_task(), bug_tasks)
730 if len(ubuntu_tasks) == 0:
731- Print.error("No Ubuntu bug task found on bug #%i." % (bug_number))
732+ Logger.error("No Ubuntu bug task found on bug #%i." % (bug_number))
733 sys.exit(1)
734 elif len(ubuntu_tasks) == 1:
735 task = ubuntu_tasks[0]
736 if len(ubuntu_tasks) > 1:
737 if verbose:
738- Print.info("%i Ubuntu tasks exist for bug #%i." % \
739+ Logger.info("%i Ubuntu tasks exist for bug #%i." % \
740 (len(ubuntu_tasks), bug_number))
741 for task in ubuntu_tasks:
742 print task.get_short_info()
743@@ -524,7 +447,7 @@
744 if len(open_ubuntu_tasks) == 1:
745 task = open_ubuntu_tasks[0]
746 else:
747- Print.normal("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" \
748+ Logger.normal("https://launchpad.net/bugs/%i has %i Ubuntu tasks:" \
749 % (bug_number, len(ubuntu_tasks)))
750 for i in xrange(len(ubuntu_tasks)):
751 print "%i) %s" % (i + 1,
752@@ -532,7 +455,7 @@
753 selected = input_number("To which Ubuntu tasks do the patch belong",
754 1, len(ubuntu_tasks))
755 task = ubuntu_tasks[selected - 1]
756- Print.info("Selected Ubuntu task: %s" % (task.get_short_info()))
757+ Logger.info("Selected Ubuntu task: %s" % (task.get_short_info()))
758
759 dsc_file = task.download_source()
760 assert os.path.isfile(dsc_file), "%s does not exist." % (dsc_file)
761@@ -540,15 +463,15 @@
762 if patch:
763 patch = download_patch(patch)
764
765- Print.info("Ubuntu package: %s" % (task.package))
766+ Logger.info("Ubuntu package: %s" % (task.package))
767 if task.is_merge():
768- Print.info("The task is a merge request.")
769+ Logger.info("The task is a merge request.")
770
771 extract_source(dsc_file, verbose)
772
773 # change directory
774 directory = task.package + '-' + task.get_version().upstream_version
775- Print.command(["cd", directory])
776+ Logger.command(["cd", directory])
777 os.chdir(directory)
778
779 edit |= apply_patch(task, patch)
780@@ -556,7 +479,7 @@
781 branch_dir = download_branch(task.get_branch_link())
782
783 # change directory
784- Print.command(["cd", branch_dir])
785+ Logger.command(["cd", branch_dir])
786 os.chdir(branch_dir)
787
788 edit |= merge_branch(branch)
789@@ -568,9 +491,9 @@
790 edit = True
791
792 # update the Maintainer field
793- Print.command(["update-maintainer"])
794+ Logger.command(["update-maintainer"])
795 if ubuntutools.update_maintainer.update_maintainer(verbose) != 0:
796- Print.error("update-maintainer script failed.")
797+ Logger.error("update-maintainer script failed.")
798 sys.exit(1)
799
800 # Get new version of package
801@@ -578,7 +501,7 @@
802 try:
803 new_version = changelog.get_version()
804 except IndexError:
805- Print.error("Debian package version could not be determined. " \
806+ Logger.error("Debian package version could not be determined. " \
807 "debian/changelog is probably malformed.")
808 ask_for_manual_fixing()
809 continue
810@@ -586,15 +509,15 @@
811 # Check if version of the new package is greater than the version in
812 # the archive.
813 if new_version <= task.get_version():
814- Print.error("The version %s is not greater than the already " \
815+ Logger.error("The version %s is not greater than the already " \
816 "available %s." % (new_version, task.get_version()))
817 ask_for_manual_fixing()
818 continue
819
820 cmd = ["dch", "--maintmaint", "--edit", ""]
821- Print.command(cmd)
822+ Logger.command(cmd)
823 if subprocess.call(cmd) != 0:
824- Print.info("Failed to update timestamp in debian/changelog.")
825+ Logger.info("Failed to update timestamp in debian/changelog.")
826
827 # Build source package
828 if patch:
829@@ -615,9 +538,9 @@
830 env = os.environ
831 if upload == 'ubuntu':
832 env['DEB_VENDOR'] = 'Ubuntu'
833- Print.command(cmd)
834+ Logger.command(cmd)
835 if subprocess.call(cmd, env=env) != 0:
836- Print.error("Failed to build source tarball.")
837+ Logger.error("Failed to build source tarball.")
838 # TODO: Add a "retry" option
839 ask_for_manual_fixing()
840 continue
841@@ -634,7 +557,7 @@
842 debdiff_filename = os.path.join(workdir, debdiff_name)
843 if not verbose:
844 cmd.insert(1, "-q")
845- Print.command(cmd + [">", debdiff_filename])
846+ Logger.command(cmd + [">", debdiff_filename])
847 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
848 debdiff = process.communicate()[0]
849
850@@ -646,7 +569,7 @@
851 # Make sure that the Launchpad bug will be closed
852 changes_file = new_dsc_file[:-4] + "_source.changes"
853 if bug_number not in get_fixed_lauchpad_bugs(changes_file):
854- Print.error("Launchpad bug #%i is not closed by new version." % \
855+ Logger.error("Launchpad bug #%i is not closed by new version." % \
856 (bug_number))
857 ask_for_manual_fixing()
858 continue
859@@ -660,7 +583,7 @@
860 allowed = map(lambda s: s + "-proposed", supported_series) + \
861 [devel_series]
862 if changelog.distributions not in allowed:
863- Print.error("%s is not an allowed series. It needs to be one " \
864+ Logger.error("%s is not an allowed series. It needs to be one " \
865 "of %s." % (changelog.distributions,
866 ", ".join(allowed)))
867 ask_for_manual_fixing()
868@@ -668,7 +591,7 @@
869 elif upload and upload.startwith("ppa/"):
870 allowed = supported_series + [devel_series]
871 if changelog.distributions not in allowed:
872- Print.error("%s is not an allowed series. It needs to be one " \
873+ Logger.error("%s is not an allowed series. It needs to be one " \
874 "of %s." % (changelog.distributions,
875 ", ".join(allowed)))
876 ask_for_manual_fixing()
877@@ -683,7 +606,7 @@
878 dist = re.sub("-.*$", "", changelog.distributions)
879 result = builder.build(new_dsc_file, dist, buildresult)
880 if result != 0:
881- Print.error("Failed to build %s from source with %s." % \
882+ Logger.error("Failed to build %s from source with %s." % \
883 (os.path.basename(new_dsc_file),
884 builder.get_name()))
885 # TODO: Add "retry" and "update" option
886@@ -699,7 +622,7 @@
887 cmd = ["lintian", "-IE", "--pedantic", "-q", build_changes]
888 lintian_filename = os.path.join(workdir,
889 task.package + "_" + strip_epoch(new_version) + ".lintian")
890- Print.command(cmd + [">", lintian_filename])
891+ Logger.command(cmd + [">", lintian_filename])
892 process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
893 report = process.communicate()[0]
894
895@@ -727,26 +650,26 @@
896 print "Abort."
897 sys.exit(USER_ABORT)
898 cmd = ["dput", "--force", upload, changes_file]
899- Print.command(cmd)
900+ Logger.command(cmd)
901 if subprocess.call(cmd) != 0:
902- Print.error("Upload of %s to %s failed." % \
903+ Logger.error("Upload of %s to %s failed." % \
904 (os.path.basename(changes_file), upload))
905 sys.exit(1)
906 if branch:
907 cmd = ['debcommit']
908- Print.command(cmd)
909+ Logger.command(cmd)
910 if subprocess.call(cmd) != 0:
911- Print.error('Bzr commit failed.')
912+ Logger.error('Bzr commit failed.')
913 sys.exit(1)
914 cmd = ['bzr', 'mark-uploaded']
915- Print.command(cmd)
916+ Logger.command(cmd)
917 if subprocess.call(cmd) != 0:
918- Print.error('Bzr tagging failed.')
919+ Logger.error('Bzr tagging failed.')
920 sys.exit(1)
921 cmd = ['bzr', 'push', ':parent']
922- Print.command(cmd)
923+ Logger.command(cmd)
924 if subprocess.call(cmd) != 0:
925- Print.error('Bzr push failed.')
926+ Logger.error('Bzr push failed.')
927 sys.exit(1)
928
929 # Leave while loop if everything worked
930@@ -766,7 +689,7 @@
931 if "SPONSOR_PATCH_BUILDER" in os.environ:
932 default_builder = os.environ["SPONSOR_PATCH_BUILDER"]
933 else:
934- default_builder = "pbuilder"
935+ default_builder = None
936
937 parser.add_option("-b", "--build", dest="build",
938 help="Build the package with the specified builder.",
939@@ -790,29 +713,24 @@
940 help="Specify a working directory.")
941
942 (options, args) = parser.parse_args()
943- Print.set_verbosity(options.verbose)
944+ Logger.set_verbosity(options.verbose)
945
946 if len(args) == 0:
947- Print.error("No bug number specified.")
948+ Logger.error("No bug number specified.")
949 sys.exit(1)
950 elif len(args) > 1:
951- Print.error("Multiple bug numbers specified: %s" % (", ".join(args)))
952+ Logger.error("Multiple bug numbers specified: %s" % (", ".join(args)))
953 sys.exit(1)
954
955 bug_number = args[0]
956 if bug_number.isdigit():
957 bug_number = int(bug_number)
958 else:
959- Print.error("Invalid bug number specified: %s" % (bug_number))
960+ Logger.error("Invalid bug number specified: %s" % (bug_number))
961 sys.exit(1)
962
963- if options.builder == "pbuilder":
964- builder = Pbuilder()
965- elif options.builder == "sbuild":
966- builder = Sbuild()
967- else:
968- Print.error("Unsupported builder specified: %s. Only pbuilder and "
969- "sbuild are supported." % (options.builder))
970+ builder = getBuilder(options.builder)
971+ if not builder:
972 sys.exit(1)
973
974 if options.sponsoring:
975
976=== added file 'ubuntutools/builder.py'
977--- ubuntutools/builder.py 1970-01-01 00:00:00 +0000
978+++ ubuntutools/builder.py 2010-12-16 09:14:08 +0000
979@@ -0,0 +1,81 @@
980+#
981+# builder.py - Helper classes for building packages
982+#
983+# Copyright (C) 2010, Benjamin Drung <bdrung@ubuntu.com>
984+# Copyright (C) 2010, Evan Broder <evan@ebroder.net>
985+#
986+# Permission to use, copy, modify, and/or distribute this software
987+# for any purpose with or without fee is hereby granted, provided
988+# that the above copyright notice and this permission notice appear
989+# in all copies.
990+#
991+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
992+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
993+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
994+# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
995+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
996+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
997+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
998+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
999+#
1000+
1001+import os
1002+import subprocess
1003+
1004+from ubuntutools.logger import Logger
1005+
1006+class Builder(object):
1007+ def __init__(self, name):
1008+ self.name = name
1009+ cmd = ["dpkg-architecture", "-qDEB_BUILD_ARCH_CPU"]
1010+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
1011+ self.architecture = process.communicate()[0].strip()
1012+
1013+ def get_architecture(self):
1014+ return self.architecture
1015+
1016+ def get_name(self):
1017+ return self.name
1018+
1019+
1020+class Pbuilder(Builder):
1021+ def __init__(self):
1022+ Builder.__init__(self, "pbuilder")
1023+
1024+ def build(self, dsc_file, dist, result_directory):
1025+ # TODO: Do not rely on a specific pbuilder configuration.
1026+ cmd = ["sudo", "-E", "DIST=" + dist, "pbuilder", "--build",
1027+ "--distribution", dist, "--architecture", self.architecture,
1028+ "--buildresult", result_directory, dsc_file]
1029+ Logger.command(cmd)
1030+ return subprocess.call(cmd)
1031+
1032+
1033+class Sbuild(Builder):
1034+ def __init__(self):
1035+ Builder.__init__(self, "sbuild")
1036+
1037+ def build(self, dsc_file, dist, result_directory):
1038+ workdir = os.getcwd()
1039+ Logger.command(["cd", result_directory])
1040+ os.chdir(result_directory)
1041+ cmd = ["sbuild", "--arch-all", "--dist=" + dist,
1042+ "--arch=" + self.architecture, dsc_file]
1043+ Logger.command(cmd)
1044+ result = subprocess.call(cmd)
1045+ Logger.command(["cd", workdir])
1046+ os.chdir(workdir)
1047+ return result
1048+
1049+
1050+def getBuilder(builder=None):
1051+ if not builder:
1052+ builder = os.environ.get('UBUNTUTOOLS_BUILDER', 'pbuilder')
1053+
1054+ if builder == 'pbuilder':
1055+ return Pbuilder()
1056+ elif builder == 'sbuild':
1057+ return Sbuild()
1058+
1059+ Logger.error("Unsupported builder specified: %s. Only pbuilder and "
1060+ "sbuild are supported." % builder)
1061
1062=== added file 'ubuntutools/logger.py'
1063--- ubuntutools/logger.py 1970-01-01 00:00:00 +0000
1064+++ ubuntutools/logger.py 2010-12-16 09:14:08 +0000
1065@@ -0,0 +1,55 @@
1066+#
1067+# logger.py - A simple logging helper class
1068+#
1069+# Copyright (C) 2010, Benjamin Drung <bdrung@ubuntu.com>
1070+#
1071+# Permission to use, copy, modify, and/or distribute this software
1072+# for any purpose with or without fee is hereby granted, provided
1073+# that the above copyright notice and this permission notice appear
1074+# in all copies.
1075+#
1076+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
1077+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
1078+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
1079+# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
1080+# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
1081+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
1082+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1083+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1084+
1085+import os
1086+import sys
1087+
1088+class Logger(object):
1089+ script_name = os.path.basename(sys.argv[0])
1090+ verbose = False
1091+
1092+ @classmethod
1093+ def command(cls, cmd):
1094+ if cls.verbose:
1095+ for i in xrange(len(cmd)):
1096+ if cmd[i].find(" ") >= 0:
1097+ cmd[i] = '"' + cmd[i] + '"'
1098+ print "%s: I: %s" % (cls.script_name, " ".join(cmd))
1099+
1100+ @classmethod
1101+ def debug(cls, message):
1102+ if cls.verbose:
1103+ print "%s: D: %s" % (cls.script_name, message)
1104+
1105+ @classmethod
1106+ def error(cls, message):
1107+ print >> sys.stderr, "%s: Error: %s" % (cls.script_name, message)
1108+
1109+ @classmethod
1110+ def info(cls, message):
1111+ if cls.verbose:
1112+ print "%s: I: %s" % (cls.script_name, message)
1113+
1114+ @classmethod
1115+ def normal(cls, message):
1116+ print "%s: %s" % (cls.script_name, message)
1117+
1118+ @classmethod
1119+ def set_verbosity(cls, verbose):
1120+ cls.verbose = verbose

Subscribers

People subscribed via source and target branches

to status/vote changes: