diff options
Diffstat (limited to 'apps/patchwork/bin')
-rw-r--r-- | apps/patchwork/bin/__init__.py | 0 | ||||
-rw-r--r-- | apps/patchwork/bin/bash_completion | 29 | ||||
-rwxr-xr-x | apps/patchwork/bin/parsemail-batch.sh | 45 | ||||
-rwxr-xr-x | apps/patchwork/bin/parsemail.py | 455 | ||||
-rwxr-xr-x | apps/patchwork/bin/parsemail.sh | 29 | ||||
-rwxr-xr-x | apps/patchwork/bin/patchwork-cron.py | 15 | ||||
-rwxr-xr-x | apps/patchwork/bin/pwclient | 744 | ||||
-rwxr-xr-x | apps/patchwork/bin/rehash.py | 34 | ||||
-rwxr-xr-x | apps/patchwork/bin/update-patchwork-status.py | 70 |
9 files changed, 0 insertions, 1421 deletions
diff --git a/apps/patchwork/bin/__init__.py b/apps/patchwork/bin/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/apps/patchwork/bin/__init__.py +++ /dev/null diff --git a/apps/patchwork/bin/bash_completion b/apps/patchwork/bin/bash_completion deleted file mode 100644 index a120a76..0000000 --- a/apps/patchwork/bin/bash_completion +++ /dev/null @@ -1,29 +0,0 @@ -# Autocompletion for bash. - -_pwclient() { - local cur prev words cword split - - if declare -f _init_completion >/dev/null; then - _init_completion -s || return - else - cur=$(_get_cword) - prev=${COMP_WORDS[COMP_CWORD-1]} - fi - - case "${COMP_CWORD}" in - 0|1) return 0;; - esac - - projects="$(sed -r -e '/\[options\]/d;' \ - -e '/^\[(.+)\]$/!d;' \ - -e 's//\1/;' ~/.pwclientrc 2>/dev/null)" - - case "${prev}" in - -p) COMPREPLY=( $(compgen -W "${projects}" -- "${cur}" ) );; - esac - - return 0 -} -complete -F _pwclient pwclient - -# vim: ft=sh diff --git a/apps/patchwork/bin/parsemail-batch.sh b/apps/patchwork/bin/parsemail-batch.sh deleted file mode 100755 index 31ef4f0..0000000 --- a/apps/patchwork/bin/parsemail-batch.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh -# -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -PATCHWORK_BINDIR=`dirname $0` - -if [ $# -ne 1 ] -then - echo "usage: $0 <dir>" >&2 - exit 1 -fi - -mail_dir="$1" - -echo "dir: $mail_dir" - -if [ ! -d "$mail_dir" ] -then - echo "$mail_dir should be a directory"? >&2 - exit 1 -fi - -ls -1rt "$mail_dir" | -while read line; -do - echo $line - $PATCHWORK_BINDIR/parsemail.sh < "$mail_dir/$line" -done diff --git a/apps/patchwork/bin/parsemail.py b/apps/patchwork/bin/parsemail.py deleted file mode 100755 index 19e6e57..0000000 --- a/apps/patchwork/bin/parsemail.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env python -# -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import sys -import re -import datetime -import time -import operator -import codecs -from email import message_from_file -try: - from email.header import Header, decode_header - from email.utils import parsedate_tz, mktime_tz -except ImportError: - # Python 2.4 compatibility - from email.Header import Header, decode_header - from email.Utils import parsedate_tz, mktime_tz - -from patchwork.parser import parse_patch -from patchwork.models import Patch, Project, Person, Comment, State, \ - get_default_initial_patch_state -from django.contrib.auth.models import User - -list_id_headers = ['List-ID', 'X-Mailing-List', 'X-list'] - -whitespace_re = re.compile('\s+') -def normalise_space(str): - return whitespace_re.sub(' ', str).strip() - -def clean_header(header): - """ Decode (possibly non-ascii) headers """ - - def decode(fragment): - (frag_str, frag_encoding) = fragment - if frag_encoding: - return frag_str.decode(frag_encoding) - return frag_str.decode() - - fragments = map(decode, decode_header(header)) - - return normalise_space(u' '.join(fragments)) - -def find_project(mail): - project = None - listid_res = [re.compile('.*<([^>]+)>.*', re.S), - re.compile('^([\S]+)$', re.S)] - - for header in list_id_headers: - if header in mail: - - for listid_re in listid_res: - match = listid_re.match(mail.get(header)) - if match: - break - - if not match: - continue - - listid = match.group(1) - - try: - project = Project.objects.get(listid = listid) - break - except: - pass - - return project - -def find_author(mail): - - from_header = clean_header(mail.get('From')) - (name, email) = (None, None) - - # tuple of (regex, fn) - # - where fn returns a (name, email) tuple from the match groups resulting - # from re.match().groups() - from_res = [ - # for "Firstname Lastname" <example@example.com> style addresses - (re.compile('"?(.*?)"?\s*<([^>]+)>'), (lambda g: (g[0], g[1]))), - - # for example@example.com (Firstname Lastname) style addresses - (re.compile('"?(.*?)"?\s*\(([^\)]+)\)'), (lambda g: (g[1], g[0]))), - - # everything else - (re.compile('(.*)'), (lambda g: (None, g[0]))), - ] - - for regex, fn in from_res: - match = regex.match(from_header) - if match: - (name, email) = fn(match.groups()) - break - - if email is None: - raise Exception("Could not parse From: header") - - email = email.strip() - if name is not None: - name = name.strip() - - new_person = False - - try: - person = Person.objects.get(email__iexact = email) - except Person.DoesNotExist: - person = Person(name = name, email = email) - new_person = True - - return (person, new_person) - -def mail_date(mail): - t = parsedate_tz(mail.get('Date', '')) - if not t: - return datetime.datetime.utcnow() - return datetime.datetime.utcfromtimestamp(mktime_tz(t)) - -def mail_headers(mail): - return reduce(operator.__concat__, - ['%s: %s\n' % (k, Header(v, header_name = k, \ - continuation_ws = '\t').encode()) \ - for (k, v) in mail.items()]) - -def find_pull_request(content): - git_re = re.compile('^The following changes since commit.*' + - '^are available in the git repository at:\n' - '^\s*([\S]+://[^\n]+)$', - re.DOTALL | re.MULTILINE) - match = git_re.search(content) - if match: - return match.group(1) - return None - -def try_decode(payload, charset): - try: - payload = unicode(payload, charset) - except UnicodeDecodeError: - return None - return payload - -def find_content(project, mail): - patchbuf = None - commentbuf = '' - pullurl = None - - for part in mail.walk(): - if part.get_content_maintype() != 'text': - continue - - payload = part.get_payload(decode=True) - subtype = part.get_content_subtype() - - if not isinstance(payload, unicode): - charset = part.get_content_charset() - - # Check that we have a charset that we understand. Otherwise, - # ignore it and fallback to our standard set. - if charset is not None: - try: - codec = codecs.lookup(charset) - except LookupError: - charset = None - - # If there is no charset or if it is unknown, then try some common - # charsets before we fail. - if charset is None: - try_charsets = ['utf-8', 'windows-1252', 'iso-8859-1'] - else: - try_charsets = [charset] - - for cset in try_charsets: - decoded_payload = try_decode(payload, cset) - if decoded_payload is not None: - break - payload = decoded_payload - - # Could not find a valid decoded payload. Fail. - if payload is None: - return (None, None) - - if subtype in ['x-patch', 'x-diff']: - patchbuf = payload - - elif subtype == 'plain': - c = payload - - if not patchbuf: - (patchbuf, c) = parse_patch(payload) - - if not pullurl: - pullurl = find_pull_request(payload) - - if c is not None: - commentbuf += c.strip() + '\n' - - patch = None - comment = None - - if pullurl or patchbuf: - name = clean_subject(mail.get('Subject'), [project.linkname]) - patch = Patch(name = name, pull_url = pullurl, content = patchbuf, - date = mail_date(mail), headers = mail_headers(mail)) - - if commentbuf: - if patch: - cpatch = patch - else: - cpatch = find_patch_for_comment(project, mail) - if not cpatch: - return (None, None) - comment = Comment(patch = cpatch, date = mail_date(mail), - content = clean_content(commentbuf), - headers = mail_headers(mail)) - - return (patch, comment) - -def find_patch_for_comment(project, mail): - # construct a list of possible reply message ids - refs = [] - if 'In-Reply-To' in mail: - refs.append(mail.get('In-Reply-To')) - - if 'References' in mail: - rs = mail.get('References').split() - rs.reverse() - for r in rs: - if r not in refs: - refs.append(r) - - for ref in refs: - patch = None - - # first, check for a direct reply - try: - patch = Patch.objects.get(project = project, msgid = ref) - return patch - except Patch.DoesNotExist: - pass - - # see if we have comments that refer to a patch - try: - comment = Comment.objects.get(patch__project = project, msgid = ref) - return comment.patch - except Comment.DoesNotExist: - pass - - - return None - -split_re = re.compile('[,\s]+') - -def split_prefixes(prefix): - """ Turn a prefix string into a list of prefix tokens - - >>> split_prefixes('PATCH') - ['PATCH'] - >>> split_prefixes('PATCH,RFC') - ['PATCH', 'RFC'] - >>> split_prefixes('') - [] - >>> split_prefixes('PATCH,') - ['PATCH'] - >>> split_prefixes('PATCH ') - ['PATCH'] - >>> split_prefixes('PATCH,RFC') - ['PATCH', 'RFC'] - >>> split_prefixes('PATCH 1/2') - ['PATCH', '1/2'] - """ - matches = split_re.split(prefix) - return [ s for s in matches if s != '' ] - -re_re = re.compile('^(re|fwd?)[:\s]\s*', re.I) -prefix_re = re.compile('^\[([^\]]*)\]\s*(.*)$') - -def clean_subject(subject, drop_prefixes = None): - """ Clean a Subject: header from an incoming patch. - - Removes Re: and Fwd: strings, as well as [PATCH]-style prefixes. By - default, only [PATCH] is removed, and we keep any other bracketed data - in the subject. If drop_prefixes is provided, remove those too, - comparing case-insensitively. - - >>> clean_subject('meep') - 'meep' - >>> clean_subject('Re: meep') - 'meep' - >>> clean_subject('[PATCH] meep') - 'meep' - >>> clean_subject('[PATCH] meep \\n meep') - 'meep meep' - >>> clean_subject('[PATCH RFC] meep') - '[RFC] meep' - >>> clean_subject('[PATCH,RFC] meep') - '[RFC] meep' - >>> clean_subject('[PATCH,1/2] meep') - '[1/2] meep' - >>> clean_subject('[PATCH RFC 1/2] meep') - '[RFC,1/2] meep' - >>> clean_subject('[PATCH] [RFC] meep') - '[RFC] meep' - >>> clean_subject('[PATCH] [RFC,1/2] meep') - '[RFC,1/2] meep' - >>> clean_subject('[PATCH] [RFC] [1/2] meep') - '[RFC,1/2] meep' - >>> clean_subject('[PATCH] rewrite [a-z] regexes') - 'rewrite [a-z] regexes' - >>> clean_subject('[PATCH] [RFC] rewrite [a-z] regexes') - '[RFC] rewrite [a-z] regexes' - >>> clean_subject('[foo] [bar] meep', ['foo']) - '[bar] meep' - >>> clean_subject('[FOO] [bar] meep', ['foo']) - '[bar] meep' - """ - - subject = clean_header(subject) - - if drop_prefixes is None: - drop_prefixes = [] - else: - drop_prefixes = [ s.lower() for s in drop_prefixes ] - - drop_prefixes.append('patch') - - # remove Re:, Fwd:, etc - subject = re_re.sub(' ', subject) - - subject = normalise_space(subject) - - prefixes = [] - - match = prefix_re.match(subject) - - while match: - prefix_str = match.group(1) - prefixes += [ p for p in split_prefixes(prefix_str) \ - if p.lower() not in drop_prefixes] - - subject = match.group(2) - match = prefix_re.match(subject) - - subject = normalise_space(subject) - - subject = subject.strip() - if prefixes: - subject = '[%s] %s' % (','.join(prefixes), subject) - - return subject - -sig_re = re.compile('^(-- |_+)\n.*', re.S | re.M) -def clean_content(str): - """ Try to remove signature (-- ) and list footer (_____) cruft """ - str = sig_re.sub('', str) - return str.strip() - -def get_state(state_name): - """ Return the state with the given name or the default State """ - if state_name: - try: - return State.objects.get(name__iexact=state_name) - except State.DoesNotExist: - pass - return get_default_initial_patch_state() - -def get_delegate(delegate_email): - """ Return the delegate with the given email or None """ - if delegate_email: - try: - return User.objects.get(email__iexact=delegate_email) - except User.DoesNotExist: - pass - return None - -def parse_mail(mail): - - # some basic sanity checks - if 'From' not in mail: - return 0 - - if 'Subject' not in mail: - return 0 - - if 'Message-Id' not in mail: - return 0 - - hint = mail.get('X-Patchwork-Hint', '').lower() - if hint == 'ignore': - return 0; - - project = find_project(mail) - if project is None: - print "no project found" - return 0 - - msgid = mail.get('Message-Id').strip() - - (author, save_required) = find_author(mail) - - (patch, comment) = find_content(project, mail) - - if patch: - # we delay the saving until we know we have a patch. - if save_required: - author.save() - save_required = False - patch.submitter = author - patch.msgid = msgid - patch.project = project - patch.state = get_state(mail.get('X-Patchwork-State', '').strip()) - patch.delegate = get_delegate( - mail.get('X-Patchwork-Delegate', '').strip()) - try: - patch.save() - except Exception, ex: - print str(ex) - - if comment: - if save_required: - author.save() - # looks like the original constructor for Comment takes the pk - # when the Comment is created. reset it here. - if patch: - comment.patch = patch - comment.submitter = author - comment.msgid = msgid - try: - comment.save() - except Exception, ex: - print str(ex) - - return 0 - -def main(args): - mail = message_from_file(sys.stdin) - return parse_mail(mail) - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/apps/patchwork/bin/parsemail.sh b/apps/patchwork/bin/parsemail.sh deleted file mode 100755 index 246c2a1..0000000 --- a/apps/patchwork/bin/parsemail.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -BIN_DIR=`dirname $0` -PATCHWORK_BASE=`readlink -e $BIN_DIR/../../..` - -PYTHONPATH="$PATCHWORK_BASE/apps":"$PATCHWORK_BASE/lib/python:$PYTHONPATH" \ - DJANGO_SETTINGS_MODULE=settings \ - "$PATCHWORK_BASE/apps/patchwork/bin/parsemail.py" - -exit 0 diff --git a/apps/patchwork/bin/patchwork-cron.py b/apps/patchwork/bin/patchwork-cron.py deleted file mode 100755 index 148e97c..0000000 --- a/apps/patchwork/bin/patchwork-cron.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python - -import sys -from patchwork.utils import send_notifications, do_expiry - -def main(args): - errors = send_notifications() - for (recipient, error) in errors: - print "Failed sending to %s: %s" % (recipient.email, ex) - - do_expiry() - -if __name__ == '__main__': - sys.exit(main(sys.argv)) - diff --git a/apps/patchwork/bin/pwclient b/apps/patchwork/bin/pwclient deleted file mode 100755 index 8d1f476..0000000 --- a/apps/patchwork/bin/pwclient +++ /dev/null @@ -1,744 +0,0 @@ -#!/usr/bin/env python -# -# Patchwork command line client -# Copyright (C) 2008 Nate Case <ncase@xes-inc.com> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import os -import sys -import xmlrpclib -import argparse -import string -import tempfile -import subprocess -import base64 -import ConfigParser -import shutil -import re - -# Default Patchwork remote XML-RPC server URL -# This script will check the PW_XMLRPC_URL environment variable -# for the URL to access. If that is unspecified, it will fallback to -# the hardcoded default value specified here. -DEFAULT_URL = "http://patchwork/xmlrpc/" -CONFIG_FILE = os.path.expanduser('~/.pwclientrc') - -class Filter: - """Filter for selecting patches.""" - def __init__(self): - # These fields refer to specific objects, so they are special - # because we have to resolve them to IDs before passing the - # filter to the server - self.state = "" - self.project = "" - - # The dictionary that gets passed to via XML-RPC - self.d = {} - - def add(self, field, value): - if field == 'state': - self.state = value - elif field == 'project': - self.project = value - else: - # OK to add directly - self.d[field] = value - - def resolve_ids(self, rpc): - """Resolve State, Project, and Person IDs based on filter strings.""" - if self.state != "": - id = state_id_by_name(rpc, self.state) - if id == 0: - sys.stderr.write("Note: No State found matching %s*, " \ - "ignoring filter\n" % self.state) - else: - self.d['state_id'] = id - - if self.project != None: - id = project_id_by_name(rpc, self.project) - if id == 0: - sys.stderr.write("Note: No Project found matching %s, " \ - "ignoring filter\n" % self.project) - else: - self.d['project_id'] = id - - def __str__(self): - """Return human-readable description of the filter.""" - return str(self.d) - -class BasicHTTPAuthTransport(xmlrpclib.SafeTransport): - - def __init__(self, username = None, password = None, use_https = False): - self.username = username - self.password = password - self.use_https = use_https - xmlrpclib.SafeTransport.__init__(self) - - def authenticated(self): - return self.username != None and self.password != None - - def send_host(self, connection, host): - xmlrpclib.Transport.send_host(self, connection, host) - if not self.authenticated(): - return - credentials = '%s:%s' % (self.username, self.password) - auth = 'Basic ' + base64.encodestring(credentials).strip() - connection.putheader('Authorization', auth) - - def make_connection(self, host): - if self.use_https: - fn = xmlrpclib.SafeTransport.make_connection - else: - fn = xmlrpclib.Transport.make_connection - return fn(self, host) - -def project_id_by_name(rpc, linkname): - """Given a project short name, look up the Project ID.""" - if len(linkname) == 0: - return 0 - projects = rpc.project_list(linkname, 0) - for project in projects: - if project['linkname'] == linkname: - return project['id'] - return 0 - -def state_id_by_name(rpc, name): - """Given a partial state name, look up the state ID.""" - if len(name) == 0: - return 0 - states = rpc.state_list(name, 0) - for state in states: - if state['name'].lower().startswith(name.lower()): - return state['id'] - return 0 - -def person_ids_by_name(rpc, name): - """Given a partial name or email address, return a list of the - person IDs that match.""" - if len(name) == 0: - return [] - people = rpc.person_list(name, 0) - return map(lambda x: x['id'], people) - -def list_patches(patches, format_str=None): - """Dump a list of patches to stdout.""" - if format_str: - format_field_re = re.compile("%{([a-z0-9_]+)}") - - def patch_field(matchobj): - fieldname = matchobj.group(1) - - if fieldname == "_msgid_": - # naive way to strip < and > from message-id - val = string.strip(str(patch["msgid"]), "<>") - else: - val = str(patch[fieldname]) - - return val - - for patch in patches: - print(format_field_re.sub(patch_field, format_str)) - else: - print("%-7s %-12s %s" % ("ID", "State", "Name")) - print("%-7s %-12s %s" % ("--", "-----", "----")) - for patch in patches: - print("%-7d %-12s %s" % (patch['id'], patch['state'], patch['name'])) - -def action_list(rpc, filter, submitter_str, delegate_str, format_str=None): - filter.resolve_ids(rpc) - - if submitter_str != None: - ids = person_ids_by_name(rpc, submitter_str) - if len(ids) == 0: - sys.stderr.write("Note: Nobody found matching *%s*\n" % \ - submitter_str) - else: - for id in ids: - person = rpc.person_get(id) - print "Patches submitted by %s <%s>:" % \ - (unicode(person['name']).encode("utf-8"), \ - unicode(person['email']).encode("utf-8")) - f = filter - f.add("submitter_id", id) - patches = rpc.patch_list(f.d) - list_patches(patches, format_str) - return - - if delegate_str != None: - ids = person_ids_by_name(rpc, delegate_str) - if len(ids) == 0: - sys.stderr.write("Note: Nobody found matching *%s*\n" % \ - delegate_str) - else: - for id in ids: - person = rpc.person_get(id) - print "Patches delegated to %s <%s>:" % \ - (person['name'], person['email']) - f = filter - f.add("delegate_id", id) - patches = rpc.patch_list(f.d) - list_patches(patches, format_str) - return - - patches = rpc.patch_list(filter.d) - list_patches(patches, format_str) - -def action_projects(rpc): - projects = rpc.project_list("", 0) - print("%-5s %-24s %s" % ("ID", "Name", "Description")) - print("%-5s %-24s %s" % ("--", "----", "-----------")) - for project in projects: - print("%-5d %-24s %s" % (project['id'], \ - project['linkname'], \ - project['name'])) - -def action_states(rpc): - states = rpc.state_list("", 0) - print("%-5s %s" % ("ID", "Name")) - print("%-5s %s" % ("--", "----")) - for state in states: - print("%-5d %s" % (state['id'], state['name'])) - -def action_info(rpc, patch_id): - patch = rpc.patch_get(patch_id) - s = "Information for patch id %d" % (patch_id) - print(s) - print('-' * len(s)) - for key, value in sorted(patch.iteritems()): - print("- %- 14s: %s" % (key, unicode(value).encode("utf-8"))) - -def action_get(rpc, patch_id): - patch = rpc.patch_get(patch_id) - s = rpc.patch_get_mbox(patch_id) - - if patch == {} or len(s) == 0: - sys.stderr.write("Unable to get patch %d\n" % patch_id) - sys.exit(1) - - base_fname = fname = os.path.basename(patch['filename']) - i = 0 - while os.path.exists(fname): - fname = "%s.%d" % (base_fname, i) - i += 1 - - try: - f = open(fname, "w") - except: - sys.stderr.write("Unable to open %s for writing\n" % fname) - sys.exit(1) - - try: - f.write(unicode(s).encode("utf-8")) - f.close() - print "Saved patch to %s" % fname - except: - sys.stderr.write("Failed to write to %s\n" % fname) - sys.exit(1) - -def action_apply(rpc, patch_id, apply_cmd=None): - patch = rpc.patch_get(patch_id) - if patch == {}: - sys.stderr.write("Error getting information on patch ID %d\n" % \ - patch_id) - sys.exit(1) - - if apply_cmd is None: - print "Applying patch #%d to current directory" % patch_id - apply_cmd = ['patch', '-p1'] - else: - print "Applying patch #%d using %s" % ( - patch_id, repr(' '.join(apply_cmd))) - - print "Description: %s" % patch['name'] - s = rpc.patch_get_mbox(patch_id) - if len(s) > 0: - proc = subprocess.Popen(apply_cmd, stdin = subprocess.PIPE) - proc.communicate(unicode(s).encode('utf-8')) - return proc.returncode - else: - sys.stderr.write("Error: No patch content found\n") - sys.exit(1) - -def action_update_patch(rpc, patch_id, state = None, archived = None, commit = None): - patch = rpc.patch_get(patch_id) - if patch == {}: - sys.stderr.write("Error getting information on patch ID %d\n" % \ - patch_id) - sys.exit(1) - - params = {} - - if state: - state_id = state_id_by_name(rpc, state) - if state_id == 0: - sys.stderr.write("Error: No State found matching %s*\n" % state) - sys.exit(1) - params['state'] = state_id - - if commit: - params['commit_ref'] = commit - - if archived: - params['archived'] = archived == 'yes' - - success = False - try: - success = rpc.patch_set(patch_id, params) - except xmlrpclib.Fault, f: - sys.stderr.write("Error updating patch: %s\n" % f.faultString) - - if not success: - sys.stderr.write("Patch not updated\n") - -def patch_id_from_hash(rpc, project, hash): - try: - patch = rpc.patch_get_by_project_hash(project, hash) - except xmlrpclib.Fault: - # the server may not have the newer patch_get_by_project_hash function, - # so fall back to hash-only. - patch = rpc.patch_get_by_hash(hash) - - if patch == {}: - sys.stderr.write("No patch has the hash provided\n") - sys.exit(1) - - patch_id = patch['id'] - # be super paranoid - try: - patch_id = int(patch_id) - except: - sys.stderr.write("Invalid patch ID obtained from server\n") - sys.exit(1) - return patch_id - -auth_actions = ['update'] - -# unfortunately we currently have to revert to this ugly hack.. -class _RecursiveHelpAction(argparse._HelpAction): - - def __call__(self, parser, namespace, values, option_string=None): - parser.print_help() - print - - subparsers_actions = [ - action for action in parser._actions - if isinstance(action, argparse._SubParsersAction) - ] - hash_n_id_actions = set(['hash', 'id', 'help']) - for subparsers_action in subparsers_actions: - for choice, subparser in subparsers_action.choices.items(): - # gross but the whole thing is.. - if (len(subparser._actions) == 3 \ - and set([a.dest for a in subparser._actions]) \ - == hash_n_id_actions) \ - or len(subparser._actions) == 0: - continue - print("command '{}'".format(choice)) - print(subparser.format_help()) - - parser.exit() - -def main(): - hash_parser = argparse.ArgumentParser(add_help=False, version=False) - hash_parser.add_argument( - '-h', metavar='HASH', dest='hash', action='store', - help='''Lookup by patch hash''' - ) - hash_parser.add_argument( - 'id', metavar='ID', nargs='*', action='store', type=int, - help='Patch ID', - ) - hash_parser.add_argument( - '-p', metavar='PROJECT', - help='''Lookup patch in project''' - ) - - filter_parser = argparse.ArgumentParser(add_help=False, version=False) - filter_parser.add_argument( - '-s', metavar='STATE', - help='''Filter by patch state (e.g., 'New', 'Accepted', etc.)''' - ) - filter_parser.add_argument( - '-a', choices=['yes','no'], - help='''Filter by patch archived state''' - ) - filter_parser.add_argument( - '-p', metavar='PROJECT', - help='''Filter by project name (see 'projects' for list)''' - ) - filter_parser.add_argument( - '-w', metavar='WHO', - help='''Filter by submitter (name, e-mail substring search)''' - ) - filter_parser.add_argument( - '-d', metavar='WHO', - help='''Filter by delegate (name, e-mail substring search)''' - ) - filter_parser.add_argument( - '-n', metavar='MAX#', - type=int, - help='''Restrict number of results''' - ) - filter_parser.add_argument( - '-m', metavar='MESSAGEID', - help='''Filter by Message-Id''' - ) - filter_parser.add_argument( - '-f', metavar='FORMAT', - help='''Print output in the given format. You can use tags matching ''' - '''fields, e.g. %%{id}, %%{state}, or %%{msgid}.''' - ) - filter_parser.add_argument( - 'patch_name', metavar='STR', nargs='?', - help='substring to search for patches by name', - ) - help_parser = argparse.ArgumentParser(add_help=False, version=False) - help_parser.add_argument( - '--help', action='help', help=argparse.SUPPRESS, - #help='''show this help message and exit''' - ) - - action_parser = argparse.ArgumentParser( - prog='pwclient', - add_help=False, - version=False, - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog='''(apply | get | info | view | update) (-h HASH | ID [ID ...])''', - ) - action_parser.add_argument( - '--help', - #action='help', - action=_RecursiveHelpAction, - help='''Print this help text''' - ) - - subparsers = action_parser.add_subparsers( - title='Commands', - metavar='' - ) - apply_parser = subparsers.add_parser( - 'apply', parents=[hash_parser, help_parser], - add_help=False, - help='''Apply a patch (in the current dir, using -p1)''' - ) - apply_parser.set_defaults(subcmd='apply') - git_am_parser = subparsers.add_parser( - 'git-am', parents=[hash_parser, help_parser], - add_help=False, - help='''Apply a patch to current git branch using "git am".''' - ) - git_am_parser.set_defaults(subcmd='git_am') - git_am_parser.add_argument( - '-s', '--signoff', - action='store_true', - help='''pass --signoff to git-am''' - ) - get_parser = subparsers.add_parser( - 'get', parents=[hash_parser, help_parser], - add_help=False, - help='''Download a patch and save it locally''' - ) - get_parser.set_defaults(subcmd='get') - info_parser = subparsers.add_parser( - 'info', parents=[hash_parser, help_parser], - add_help=False, - help='''Display patchwork info about a given patch ID''' - ) - info_parser.set_defaults(subcmd='info') - projects_parser = subparsers.add_parser( - 'projects', - add_help=False, - help='''List all projects''' - ) - projects_parser.set_defaults(subcmd='projects') - states_parser = subparsers.add_parser( - 'states', - add_help=False, - help='''Show list of potential patch states''' - ) - states_parser.set_defaults(subcmd='states') - view_parser = subparsers.add_parser( - 'view', parents=[hash_parser, help_parser], - add_help=False, - help='''View a patch''' - ) - view_parser.set_defaults(subcmd='view') - update_parser = subparsers.add_parser( - 'update', parents=[hash_parser, help_parser], - add_help=False, - help='''Update patch''', - epilog='''Using a COMMIT-REF allows for only one ID to be specified''', - ) - update_parser.add_argument( - '-c', metavar='COMMIT-REF', - help='''commit reference hash''' - ) - update_parser.add_argument( - '-s', metavar='STATE', - required=True, - help='''Set patch state (e.g., 'Accepted', 'Superseded' etc.)''' - ) - update_parser.add_argument( - '-a', choices=['yes', 'no'], - help='''Set patch archived state''' - ) - update_parser.set_defaults(subcmd='update') - list_parser = subparsers.add_parser("list", - add_help=False, - #aliases=['search'], - parents=[filter_parser, help_parser], - help='''List patches, using the optional filters specified - below and an optional substring to search for patches - by name''' - ) - list_parser.set_defaults(subcmd='list') - search_parser = subparsers.add_parser("search", - add_help=False, - parents=[filter_parser, help_parser], - help='''Alias for "list"''' - ) - # Poor man's argparse aliases: - # We register the "search" parser but effectively use "list" for the - # help-text. - search_parser.set_defaults(subcmd='list') - if len(sys.argv) < 2: - action_parser.print_help() - sys.exit(0) - - args = action_parser.parse_args() - args = dict(vars(args)) - action = args.get('subcmd') - - if args.get('hash') and len(args.get('id')): - # mimic mutual exclusive group - sys.stderr.write("Error: [-h HASH] and [ID [ID ...]] " + - "are mutually exlusive\n") - locals()[action + '_parser'].print_help() - sys.exit(1) - - # set defaults - filt = Filter() - commit_str = None - url = DEFAULT_URL - - archived_str = args.get('a') - state_str = args.get('s') - project_str = args.get('p') - submitter_str = args.get('w') - delegate_str = args.get('d') - format_str = args.get('f') - hash_str = args.get('hash') - patch_ids = args.get('id') - msgid_str = args.get('m') - if args.get('c'): - # update multiple IDs with a single commit-hash does not make sense - if action == 'update' and patch_ids and len(patch_ids) > 1: - sys.stderr.write( - "Declining update with COMMIT-REF on multiple IDs\n" - ) - update_parser.print_help() - sys.exit(1) - commit_str = args.get('c') - - if args.get('n') != None: - try: - filt.add("max_count", args.get('n')) - except: - sys.stderr.write("Invalid maximum count '%s'\n" % args.get('n')) - action_parser.print_help() - sys.exit(1) - - do_signoff = args.get('signoff') - - # grab settings from config files - config = ConfigParser.ConfigParser() - config.read([CONFIG_FILE]) - - if not config.has_section('options'): - sys.stderr.write('~/.pwclientrc is in the old format. Migrating it...') - - old_project = config.get('base','project') - - new_config = ConfigParser.ConfigParser() - new_config.add_section('options') - - new_config.set('options','default',old_project) - new_config.add_section(old_project) - - new_config.set(old_project,'url',config.get('base','url')) - if config.has_option('auth', 'username'): - new_config.set(old_project,'username',config.get('auth','username')) - if config.has_option('auth', 'password'): - new_config.set(old_project,'password',config.get('auth','password')) - - old_config_file = CONFIG_FILE + '.orig' - shutil.copy2(CONFIG_FILE,old_config_file) - - with open(CONFIG_FILE, 'wb') as fd: - new_config.write(fd) - - sys.stderr.write(' Done.\n') - sys.stderr.write('Your old ~/.pwclientrc was saved to %s\n' % old_config_file) - sys.stderr.write('and was converted to the new format. You may want to\n') - sys.stderr.write('inspect it before continuing.\n') - sys.exit(1) - - if not project_str: - try: - project_str = config.get('options', 'default') - except: - sys.stderr.write("No default project configured in ~/.pwclientrc\n") - action_parser.print_help() - sys.exit(1) - - if not config.has_section(project_str): - sys.stderr.write("No section for project %s\n" % project_str) - sys.exit(1) - if not config.has_option(project_str, 'url'): - sys.stderr.write("No URL for project %s\n" % project_str) - sys.exit(1) - if not do_signoff and config.has_option('options', 'signoff'): - do_signoff = config.getboolean('options', 'signoff') - if not do_signoff and config.has_option(project_str, 'signoff'): - do_signoff = config.getboolean(project_str, 'signoff') - - url = config.get(project_str, 'url') - - transport = None - if action in auth_actions: - if config.has_option(project_str, 'username') and \ - config.has_option(project_str, 'password'): - - use_https = url.startswith('https') - - transport = BasicHTTPAuthTransport( \ - config.get(project_str, 'username'), - config.get(project_str, 'password'), - use_https) - - else: - sys.stderr.write(("The %s action requires authentication, " - "but no username or password\nis configured\n") % action) - sys.exit(1) - - if project_str: - filt.add("project", project_str) - - if state_str: - filt.add("state", state_str) - - if archived_str: - filt.add("archived", archived_str == 'yes') - - if msgid_str: - filt.add("msgid", msgid_str) - - try: - rpc = xmlrpclib.Server(url, transport = transport) - except: - sys.stderr.write("Unable to connect to %s\n" % url) - sys.exit(1) - - # It should be safe to assume hash_str is not zero, but who knows.. - if hash_str != None: - patch_ids = [patch_id_from_hash(rpc, project_str, hash_str)] - - # helper for non_empty() to print correct helptext - h = locals()[action + '_parser'] - - # Require either hash_str or IDs for - def non_empty(h, patch_ids): - """Error out if no patch IDs were specified""" - if patch_ids == None or len(patch_ids) < 1: - sys.stderr.write("Error: Missing Argument! " + - "Either [-h HASH] or [ID [ID ...]] are required\n") - if h: - h.print_help() - sys.exit(1) - return patch_ids - - if action == 'list' or action == 'search': - if args.get('patch_name') != None: - filt.add("name__icontains", args.get('patch_name')) - action_list(rpc, filt, submitter_str, delegate_str, format_str) - - elif action.startswith('project'): - action_projects(rpc) - - elif action.startswith('state'): - action_states(rpc) - - elif action == 'view': - pager = os.environ.get('PAGER') - if pager: - pager = subprocess.Popen( - pager.split(), stdin=subprocess.PIPE - ) - if pager: - i = list() - for patch_id in non_empty(h, patch_ids): - s = rpc.patch_get_mbox(patch_id) - if len(s) > 0: - i.append(unicode(s).encode("utf-8")) - if len(i) > 0: - pager.communicate(input="\n".join(i)) - pager.stdin.close() - else: - for patch_id in non_empty(h, patch_ids): - s = rpc.patch_get_mbox(patch_id) - if len(s) > 0: - print unicode(s).encode("utf-8") - - elif action == 'info': - for patch_id in non_empty(h, patch_ids): - action_info(rpc, patch_id) - - elif action == 'get': - for patch_id in non_empty(h, patch_ids): - action_get(rpc, patch_id) - - elif action == 'apply': - for patch_id in non_empty(h, patch_ids): - ret = action_apply(rpc, patch_id) - if ret: - sys.stderr.write("Apply failed with exit status %d\n" % ret) - sys.exit(1) - - elif action == 'git_am': - cmd = ['git', 'am'] - if do_signoff: - cmd.append('-s') - for patch_id in non_empty(h, patch_ids): - ret = action_apply(rpc, patch_id, cmd) - if ret: - sys.stderr.write("'git am' failed with exit status %d\n" % ret) - sys.exit(1) - - elif action == 'update': - for patch_id in non_empty(h, patch_ids): - action_update_patch(rpc, patch_id, state = state_str, - archived = archived_str, commit = commit_str - ) - - else: - sys.stderr.write("Unknown action '%s'\n" % action) - action_parser.print_help() - sys.exit(1) - -if __name__ == "__main__": - main() diff --git a/apps/patchwork/bin/rehash.py b/apps/patchwork/bin/rehash.py deleted file mode 100755 index c44e49b..0000000 --- a/apps/patchwork/bin/rehash.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -from patchwork.models import Patch -import sys - -if __name__ == '__main__': - if len(sys.argv) > 1: - patches = Patch.objects.filter(id__in = sys.argv[1:]) - else: - patches = Patch.objects.all() - - for patch in patches: - print patch.id, patch.name - patch.hash = None - patch.save() diff --git a/apps/patchwork/bin/update-patchwork-status.py b/apps/patchwork/bin/update-patchwork-status.py deleted file mode 100755 index 2da5d23..0000000 --- a/apps/patchwork/bin/update-patchwork-status.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -# -# Patchwork - automated patch tracking system -# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org> -# -# This file is part of the Patchwork package. -# -# Patchwork is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# Patchwork is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Patchwork; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -import sys -import subprocess -from optparse import OptionParser - -def commits(options, revlist): - cmd = ['git', 'rev-list', revlist] - proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, cwd = options.repodir) - - revs = [] - - for line in proc.stdout.readlines(): - revs.append(line.strip()) - - return revs - -def commit(options, rev): - cmd = ['git', 'diff', '%(rev)s^..%(rev)s' % {'rev': rev}] - proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, cwd = options.repodir) - - buf = proc.communicate()[0] - - return buf - - -def main(args): - parser = OptionParser(usage = '%prog [options] revspec') - parser.add_option("-p", "--project", dest = "project", action = 'store', - help="use project PROJECT", metavar="PROJECT") - parser.add_option("-d", "--dir", dest = "repodir", action = 'store', - help="use git repo in DIR", metavar="DIR") - - (options, args) = parser.parse_args(args[1:]) - - if len(args) != 1: - parser.error("incorrect number of arguments") - - revspec = args[0] - revs = commits(options, revspec) - - for rev in revs: - print rev - print commit(options, rev) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) - - |