f22ef9571d1d7773d63efa5e6382818bf30da5a0
[debtags/alioth-scripts.git] / debtags-submit-patch
1 #!/usr/bin/python
2
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
20
21 import pwd
22 import os
23 import sys
24 import hashlib
25 import urllib
26 import urllib2
27 import json
28 import logging
29
30 log = logging.getLogger(sys.argv[0])
31
32 SUBMIT_URL = "http://debtags.debian.net/api/patch"
33
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]
39
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)
51
52         for pkg, tags in sorted(res.get("pkgs", {}).iteritems()):
53             log.info("%s: %s: %s", fname, pkg, ", ".join(tags))
54
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())
65
66
67 if __name__ == "__main__":
68     from optparse import OptionParser
69     import sys
70
71     VERSION="0.1"
72
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'.
78
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.
83
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)
91
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()
110
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)
119
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)