X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;f=apps%2Fpatchwork%2Fbin%2Fpwclient;h=dfbea30862756950e46b4a845e4e426726402284;hb=429ef9ba9dcc8f92b8362c1b0fca0916db05e84b;hp=b4804080921b0c90ebd4a4140d8751c64529b463;hpb=d5edf740c9c2c31fa60c757aa8d66e1dedbab74a;p=patchwork diff --git a/apps/patchwork/bin/pwclient b/apps/patchwork/bin/pwclient index b480408..dfbea30 100755 --- a/apps/patchwork/bin/pwclient +++ b/apps/patchwork/bin/pwclient @@ -28,13 +28,14 @@ import tempfile import subprocess import base64 import ConfigParser +import shutil # 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_FILES = [os.path.expanduser('~/.pwclientrc')] +CONFIG_FILE = os.path.expanduser('~/.pwclientrc') class Filter: """Filter for selecting patches.""" @@ -79,12 +80,13 @@ class Filter: """Return human-readable description of the filter.""" return str(self.d) -class BasicHTTPAuthTransport(xmlrpclib.Transport): +class BasicHTTPAuthTransport(xmlrpclib.SafeTransport): - def __init__(self, username = None, password = None): + def __init__(self, username = None, password = None, use_https = False): self.username = username self.password = password - xmlrpclib.Transport.__init__(self) + self.use_https = use_https + xmlrpclib.SafeTransport.__init__(self) def authenticated(self): return self.username != None and self.password != None @@ -97,13 +99,22 @@ class BasicHTTPAuthTransport(xmlrpclib.Transport): 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 usage(): sys.stderr.write("Usage: %s [options]\n\n" % \ (os.path.basename(sys.argv[0]))) sys.stderr.write("Where is one of:\n") sys.stderr.write( """ apply : Apply a patch (in the current dir, using -p1) + git-am : Apply a patch to current git branch using "git am" get : Download a patch and save it locally + info : Display patchwork info about a given patch ID projects : List all projects states : Show list of potential patch states list [str] : List patches, using the optional filters specified @@ -118,7 +129,8 @@ def usage(): -p : Filter by project name (see 'projects' for list) -w : Filter by submitter (name, e-mail substring search) -d : Filter by delegate (name, e-mail substring search) - -n : Restrict number of results\n""") + -n : Restrict number of results + -m : Filter by Message-Id\n""") sys.stderr.write("""\nActions that take an ID argument can also be \ invoked with: -h : Lookup by patch hash\n""") @@ -154,10 +166,10 @@ def person_ids_by_name(rpc, name): def list_patches(patches): """Dump a list of patches to stdout.""" - print("%-5s %-12s %s" % ("ID", "State", "Name")) - print("%-5s %-12s %s" % ("--", "-----", "----")) + print("%-7s %-12s %s" % ("ID", "State", "Name")) + print("%-7s %-12s %s" % ("--", "-----", "----")) for patch in patches: - print("%-5d %-12s %s" % (patch['id'], patch['state'], patch['name'])) + print("%-7d %-12s %s" % (patch['id'], patch['state'], patch['name'])) def action_list(rpc, filter, submitter_str, delegate_str): filter.resolve_ids(rpc) @@ -165,13 +177,14 @@ def action_list(rpc, filter, submitter_str, delegate_str): if submitter_str != "": ids = person_ids_by_name(rpc, submitter_str) if len(ids) == 0: - sys.stderr.write("Note: Nobody found matching *%s*\n", \ + 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>:" % \ - (person['name'], person['email']) + (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) @@ -181,7 +194,7 @@ def action_list(rpc, filter, submitter_str, delegate_str): if delegate_str != "": ids = person_ids_by_name(rpc, delegate_str) if len(ids) == 0: - sys.stderr.write("Note: Nobody found matching *%s*\n", \ + sys.stderr.write("Note: Nobody found matching *%s*\n" % \ delegate_str) else: for id in ids: @@ -213,6 +226,14 @@ def action_states(rpc): 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) @@ -241,18 +262,25 @@ def action_get(rpc, patch_id): sys.stderr.write("Failed to write to %s\n" % fname) sys.exit(1) -def action_apply(rpc, patch_id): +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) - print "Applying patch #%d to current directory" % patch_id + + 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(['patch', '-p1'], stdin = subprocess.PIPE) - proc.communicate(s) + proc = subprocess.Popen(apply_cmd, stdin = subprocess.PIPE) + proc.communicate(unicode(s).encode('utf-8')) else: sys.stderr.write("Error: No patch content found\n") sys.exit(1) @@ -302,7 +330,7 @@ auth_actions = ['update'] def main(): try: - opts, args = getopt.getopt(sys.argv[2:], 's:p:w:d:n:c:h:') + opts, args = getopt.getopt(sys.argv[2:], 's:p:w:d:n:c:h:m:') except getopt.GetoptError, err: print str(err) usage() @@ -320,18 +348,9 @@ def main(): commit_str = "" state_str = "" hash_str = "" + msgid_str = "" url = DEFAULT_URL - config = ConfigParser.ConfigParser() - config.read(CONFIG_FILES) - - # grab settings from config files - if config.has_option('base', 'url'): - url = config.get('base', 'url') - - if config.has_option('base', 'project'): - project_str = config.get('base', 'project') - for name, value in opts: if name == '-s': state_str = value @@ -345,6 +364,8 @@ def main(): commit_str = value elif name == '-h': hash_str = value + elif name == '-m': + msgid_str = value elif name == '-n': try: filt.add("max_count", int(value)) @@ -359,15 +380,68 @@ def main(): sys.stderr.write("Too many arguments specified\n") usage() + # 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") + usage() + + 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) + + url = config.get(project_str, 'url') + (username, password) = (None, None) transport = None if action in auth_actions: - if config.has_option('auth', 'username') and \ - config.has_option('auth', 'password'): + if config.has_option(project_str, 'username') and \ + config.has_option(project_str, 'password'): + + use_https = url.startswith('https') transport = BasicHTTPAuthTransport( \ - config.get('auth', 'username'), - config.get('auth', 'password')) + config.get(project_str, 'username'), + config.get(project_str, 'password'), + use_https) else: sys.stderr.write(("The %s action requires authentication, " @@ -380,6 +454,9 @@ def main(): if state_str: filt.add("state", state_str) + if msgid_str: + filt.add("msgid", msgid_str) + try: rpc = xmlrpclib.Server(url, transport = transport) except: @@ -416,14 +493,17 @@ def main(): if len(s) > 0: print unicode(s).encode("utf-8") - elif action == 'get' or action == 'save': + elif action in ('get', 'save', 'info'): try: patch_id = patch_id or int(args[0]) except: sys.stderr.write("Invalid patch ID given\n") sys.exit(1) - action_get(rpc, patch_id) + if action == 'info': + action_info(rpc, patch_id) + else: + action_get(rpc, patch_id) elif action == 'apply': try: @@ -434,6 +514,15 @@ def main(): action_apply(rpc, patch_id) + elif action == 'git-am': + try: + patch_id = patch_id or int(args[0]) + except: + sys.stderr.write("Invalid patch ID given\n") + sys.exit(1) + + action_apply(rpc, patch_id, ['git', 'am']) + elif action == 'update': try: patch_id = patch_id or int(args[0])