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."""
""" apply <ID> : Apply a patch (in the current dir, using -p1)
git-am <ID> : Apply a patch to current git branch using "git am"
get <ID> : Download a patch and save it locally
+ info <ID> : 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
-p <project> : Filter by project name (see 'projects' for list)
-w <who> : Filter by submitter (name, e-mail substring search)
-d <who> : Filter by delegate (name, e-mail substring search)
- -n <max #> : Restrict number of results\n""")
+ -n <max #> : Restrict number of results
+ -m <messageid>: Filter by Message-Id\n""")
sys.stderr.write("""\nActions that take an ID argument can also be \
invoked with:
-h <hash> : Lookup by patch hash\n""")
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)
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)
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()
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
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))
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:
if state_str:
filt.add("state", state_str)
+ if msgid_str:
+ filt.add("msgid", msgid_str)
+
try:
rpc = xmlrpclib.Server(url, transport = transport)
except:
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: