f22ef9571d1d7773d63efa5e6382818bf30da5a0
1 #!/usr/bin/python
3 # debtags-submit-patch: submit a tag patch to the debtags website
4 #
5 # Copyright (C) 2007--2012 Enrico Zini <enrico@debian.org>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 import pwd
22 import os
23 import sys
24 import hashlib
25 import urllib
26 import urllib2
27 import json
28 import logging
30 log = logging.getLogger(sys.argv[0])
32 SUBMIT_URL = "http://debtags.debian.net/api/patch"
34 def make_default_tag():
35 "Autogenerate a mostly persistant, non-trackable tag"
36 m = hashlib.md5()
37 m.update(":".join(str(x) for x in pwd.getpwuid(os.getuid())))
38 return m.hexdigest()[:20]
40 def submit(opts, patch, fname="[stdin]"):
41 log.info("Submitting patch %s to %s...", fname, opts.url)
42 try:
43 conn = urllib2.urlopen(opts.url,
44 urllib.urlencode((
45 ("tag", opts.tag),
46 ("patch", patch),
47 )))
48 res = json.load(conn)
49 for note in res.get("notes", ()):
50 log.warn("%s: %s", fname, note)
52 for pkg, tags in sorted(res.get("pkgs", {}).iteritems()):
53 log.info("%s: %s: %s", fname, pkg, ", ".join(tags))
55 if opts.dump_http_error:
56 try:
57 os.unlink(opts.dump_http_error)
58 except OSError:
59 pass
60 except urllib2.HTTPError, e:
61 log.error("%s", str(e))
62 if opts.dump_http_error:
63 with open(opts.dump_http_error, "w") as fd:
64 fd.write(e.read())
67 if __name__ == "__main__":
68 from optparse import OptionParser
69 import sys
71 VERSION="0.1"
73 class Parser(OptionParser):
74 def print_help(self, out=None):
75 if out is None: out = sys.stdout
76 OptionParser.print_help(self, out)
77 print >>out, """Patch files can be generated with 'debtags diff' or 'tagcoll diff'.
79 Patch submissions are marked with a tag of your choice. It does not need to
80 identify yourself (but feel free to use your email address), but reusing your
81 tag allows to handle all your edits as if they were a single one. This helps
82 greatly when tags are reviewed.
84 By default, a mostly persistent but anonymous tag is generated by hashing your
85 passwd entry.
86 """
87 def error(self, msg):
88 sys.stderr.write("%s: error: %s\n\n" % (self.get_prog_name(), msg))
89 self.print_help(sys.stderr)
90 sys.exit(2)
92 parser = Parser(usage="usage: %prog [-t TAG] [options] [patchfile [patchfile...]]",
93 version="%prog "+ VERSION,
94 description="Submits a tag patch to the Debtags website."
95 " Each patch file is submitted in a different query. If no"
96 " patch file is given, patch data is read from stdin.")
97 parser.add_option("-t", "--tag", action="store", metavar="tag", default=make_default_tag(),
98 help="tag the patch with the given string (default: %default)")
99 parser.add_option("-q", "--quiet", action="store_true",
100 help="quiet mode: only output errors")
101 parser.add_option("-v", "--verbose", action="store_true",
102 help="verbose mode: output progress and non-essential information")
103 parser.add_option("--url", action="store", metavar="url", default=SUBMIT_URL,
104 help="URL to submit to (default: %default)")
105 parser.add_option("--dump-http-error", action="store", metavar="file",
106 help="if the server returns an error, dump the contents"
107 "of the error page to the given file (default:"
108 "discard the error page)")
109 (opts, args) = parser.parse_args()
111 #FORMAT = "%(asctime)-15s %(levelname)s %(message)s"
112 FORMAT = "%(message)s"
113 if opts.quiet:
114 logging.basicConfig(level=logging.ERROR, stream=sys.stderr, format=FORMAT)
115 elif not opts.verbose:
116 logging.basicConfig(level=logging.WARNING, stream=sys.stderr, format=FORMAT)
117 else:
118 logging.basicConfig(level=logging.INFO, stream=sys.stderr, format=FORMAT)
120 if not args:
121 patch = sys.stdin.read()
122 submit(opts, patch)
123 else:
124 for fname in args:
125 with open(fname, "r") as fd:
126 patch = fd.read()
127 submit(opts, patch, fname)
