From 76a5e49d861c653e139d60946023933a4efb7ebc Mon Sep 17 00:00:00 2001 From: Kelley Spoon Date: Wed, 13 Jun 2018 23:41:18 +0000 Subject: phabtools: initial commit with linaro customizations --- .gitignore | 1 + TODO | 4 ++ bugzilla_create.py | 49 +++++++++++++----- bugzilla_fetch.py | 1 + bugzilla_tag_update.py | 5 +- linaro/group_sync.py | 117 +++++++++++++++++++++++++++++++++++++++++++ linaro/import_ldap_user.php | 73 +++++++++++++++++++++++++++ linaro/phab_list_projects.py | 43 ++++++++++++++++ linaro_migrate_prod_comp.py | 43 ++++++++++++++++ wmfphablib/bzlib.py | 70 +++++++++++++++++--------- wmfphablib/config.py | 3 +- wmfphablib/phabapi.py | 50 +++++++++++------- wmfphablib/rtlib.py | 11 ++-- wmfphablib/util.py | 9 ++-- 14 files changed, 414 insertions(+), 65 deletions(-) create mode 100644 TODO create mode 100755 linaro/group_sync.py create mode 100755 linaro/import_ldap_user.php create mode 100755 linaro/phab_list_projects.py create mode 100755 linaro_migrate_prod_comp.py diff --git a/.gitignore b/.gitignore index 0d20b64..406faa4 100755 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +phabtools.conf diff --git a/TODO b/TODO new file mode 100644 index 0000000..9882429 --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +- Bug 3453 missing from Kernel Funcional Testing/General +- assignee not being set to bugzilla user...defaulting to phabbot +- double check attachments being copied +- debug why multiple tags aren't applied to the task diff --git a/bugzilla_create.py b/bugzilla_create.py index 07817d0..fe20434 100755 --- a/bugzilla_create.py +++ b/bugzilla_create.py @@ -30,9 +30,8 @@ from wmfphablib import epoch_to_datetime from wmfphablib import ipriority def create(bugid): - phab = Phabricator(config.phab_user, - config.phab_cert, - config.phab_host) + phab = Phabricator(token=config.phab_cert, + host=config.phab_host) phabm = phabmacros('', '', '') phabm.con = phab @@ -153,10 +152,12 @@ def create(bugid): if buginfo['status'] == 'verified': ptags.append(('verified', 'tags')) - if buginfo['cf_browser'] not in ['---', "Other"]: - btag = "Browser_Support_%s" % (buginfo['cf_browser'].replace(' ', '-'),) - log('Adding browser tag: %s' % (btag,)) - ptags.append((btag, 'tags')) + #kspoon: I think this is a custom WM thing... might need to lift it + # for any bugzilla customizations linaro has added + #if buginfo['cf_browser'] not in ['---', "Other"]: + # btag = "Browser_Support_%s" % (buginfo['cf_browser'].replace(' ', '-'),) + # log('Adding browser tag: %s' % (btag,)) + # ptags.append((btag, 'tags')) if buginfo['target_milestone'] != '---': log('Creating milestone: %s' % (buginfo['target_milestone'],)) @@ -165,10 +166,25 @@ def create(bugid): #set defaults to be overridden by sec if needed buginfo['viewPolicy'] = 'public' buginfo['editPolicy'] = 'users' - buginfo['project'] = bzlib.sanitize_project_name(buginfo["product"], - buginfo["component"]) + #buginfo['project'] = bzlib.sanitize_project_name(buginfo["product"], + # buginfo["component"]) + + #kspoon: for linaro, we want to create the product as a container/parent + # project, and the components as subprojects of the parent whereas wm just + # flattened it. + buginfo['parent_project'] = bzlib.sanitize_project_name(buginfo["product"]) + parent_phid = phabm.get_project_phid(buginfo['parent_project']) + if not parent_phid: + parent_phid = phabm.ensure_project(buginfo['parent_project'], parent_phid=None, edit='public', view='public') + + buginfo['bugzilla_id'] = "bugzilla-%s" % buginfo['id'] + + print "parent_phid set to %s" % parent_phid + + buginfo['project'] = bzlib.sanitize_project_name(buginfo["component"]) vlog(buginfo['project']) ptags.append((buginfo['project'], None)) + ptags.append((buginfo['parent_project'], None)) title = buginfo['summary'] @@ -195,7 +211,7 @@ def create(bugid): 'creation_time': buginfo['creation_time']} created = epoch_to_datetime(description['creation_time']) - desc_block = "**Author:** `%s`\n\n**Description:**\n%s\n" % (description['author'], + desc_block = "**Creator:** `%s`\n\n**Description:**\n%s\n" % (description['creator'], description['text']) # https://phabricator.wikimedia.org/T694 @@ -254,7 +270,7 @@ def create(bugid): phids = [] for p in ptags: edit, view = project_security_settings(p[0]) - phid = phabm.ensure_project(p[0], edit=edit, view=view) + phid = phabm.ensure_project(p[0], parent_phid=parent_phid, edit=edit, view=view) phids.append(phid) if p[1] is not None: vlog("setting project %s icon to %s" % (p[0], p[1])) @@ -273,6 +289,15 @@ def create(bugid): # viewPolicy = buginfo['viewPolicy'], # editPolicy = buginfo['editPolicy'], vlog("Ticket Info: %s" % (desc_block,)) + #kspoon: see if ticket already exists by checking title... not a great + # idea. Should search for bugzilla- tag instead + print "looking up tickets..." + tickets = phab.maniphest.search(constraints={'query':'"%s"' % buginfo['summary']}, queryKey='all', order='newest') + + # if + if len( tickets['data'] ) > 0: + log("Task exists: T%s (%s)" % (tickets['data'][0]['id'], tickets['data'][0]['phid'])) + return True ticket = phab.maniphest.createtask(title=buginfo['summary'], description=full_description, projectPHIDs=phids, @@ -295,7 +320,7 @@ def create(bugid): for c in clean_com: fmt_comment = {} created = epoch_to_datetime(c['creation_time']) - comment_header = "**%s** wrote:\n\n" % (c['author'],) + comment_header = "**%s** wrote:\n\n" % (c['creator'],) comment_body = c['text'] attachments = '' if 'attachment' in c: diff --git a/bugzilla_fetch.py b/bugzilla_fetch.py index 2ba2d9d..501bb5b 100755 --- a/bugzilla_fetch.py +++ b/bugzilla_fetch.py @@ -119,6 +119,7 @@ def run_fetch(bugid, tries=1): def main(): bugs = return_bug_list() + from multiprocessing import Pool pool = Pool(processes=int(config.bz_fetchmulti)) _ = pool.map(run_fetch, bugs) diff --git a/bugzilla_tag_update.py b/bugzilla_tag_update.py index b49af18..15f1c36 100755 --- a/bugzilla_tag_update.py +++ b/bugzilla_tag_update.py @@ -92,6 +92,9 @@ def create(bugid): #list of projects to add to ticket ptags = [] + parent_phid = phabm.get_parent_phid(buginfo['product']) + parent_phid = phabm.ensure_project(p[0], parent_phid=None, edit="public", view="public") + if buginfo['status'] == 'VERIFIED': vlog("Adding 'verified' to %s" % (ticket,)) ptags.append(('verified', 'tags')) @@ -105,7 +108,7 @@ def create(bugid): phids = [] for p in ptags: edit, view = project_security_settings(p[0]) - phid = phabm.ensure_project(p[0], edit=edit, view=view) + phid = phabm.ensure_project(p[0], parent_phid=parent_phid, edit=edit, view=view) phids.append(phid) if p[1] is not None: vlog("setting project %s icon to %s" % (p[0], p[1])) diff --git a/linaro/group_sync.py b/linaro/group_sync.py new file mode 100755 index 0000000..3a7fb19 --- /dev/null +++ b/linaro/group_sync.py @@ -0,0 +1,117 @@ +#!/usr/bin/python + +import linaro_ldap +from phabricator import Phabricator +import json + +PHAB_TOKEN = 'api-gpwwrvgqz4qdrzhkbhcazdltwcyp' +PHAB_GROUPS_PARENT = "Groups" + +PHAB_USERS = {} + +PHAB_LOCAL_USERS = ["systemsadmin","phabbot"] + +phab_groups_tbl = {} + +def get_phab_top_level_group_id(phab, name): + phab_query = phab.project.search(constraints={'name': name}) + return phab_query["data"][0]["phid"] + +def get_user(phab,phid): + user_query = phab.user.search(constraints={'phids': [phid]}) + return user_query["data"][0]["fields"]["username"] + +def get_user_phid(phab,username): + if not PHAB_USERS.has_key(username): + user_query = phab.user.search(constraints={'usernames':[username]}) + #print user_query["data"][0] + PHAB_USERS[username] = user_query["data"][0]["phid"] + return PHAB_USERS[username] + +def get_groups_and_users(phab,top_group): + phab_query = phab.project.search(constraints={'parents': [top_group]}, attachments={"members":"true"}) + return phab_query["data"] + +def get_group_phid(phab, top_group, name): + phab_query = phab.project.search(constraints={'parents': [top_group], 'name': name}) + return phab_query["data"][0]["fields"]["phid"] + +def create_group(phab, parent, group_name): + print "phabbot phid: %s" % PHAB_USERS["phabbot"] + tlist = [ + { "type": "name", "value": group_name }, + { "type": "parent", "value": parent }, + { "type": "join", "value": "no-one" }, + { "type": "edit", "value": "users" }, + { "type": "members.set", "value": [PHAB_USERS["systemsadmin"], PHAB_USERS["phabbot"]]}, + { "type": "view", "value": "users" }, + { "type": "icon", "value": "group" }, + ] + return( phab.project.edit( transactions = tlist ) ) + +ldap_groups = linaro_ldap.get_groups_and_users() +#for g in groups: +# print g +# print "Members: %s" % groups[g] + +phab = Phabricator(host="https://staging-bugs.linaro.org/api/", token=PHAB_TOKEN) + +#preload the PHAB_USER table with system accounts +for x in PHAB_LOCAL_USERS: + print get_user_phid(phab,x) + +groups_project_id = get_phab_top_level_group_id(phab, PHAB_GROUPS_PARENT) +groups = get_groups_and_users(phab, groups_project_id) + +for pgroup in groups: + group_name = pgroup["fields"]["name"] + + if not phab_groups_tbl.has_key( group_name ): + phab_groups_tbl[group_name] = [] + + for phid_entry in pgroup["attachments"]["members"]["members"]: + phid = phid_entry["phid"] + username = get_user(phab, phid) + + # create a lookup table for ldap lookups + if not PHAB_USERS.has_key( phid ): + PHAB_USERS[phid] = username + if username not in phab_groups_tbl[group_name] and username not in PHAB_LOCAL_USERS: + phab_groups_tbl[group_name].append( username ) + +#for g in phab_groups_tbl.keys(): +# print "%s: " % g, +# for u in phab_groups_tbl[g]: +# print "%s " % u, +# print + +groups_add_to_phab = [] +groups_remove_from_phab = [] + +for x in ldap_groups: + # check for new group + if not phab_groups_tbl.has_key(x): + print "add group to phab: %s" % x + try: + rv = create_group(phab, groups_project_id, x) + print rv + except Exception e: + print e + + for y in ldap_groups[x]: + print " - add user %s to %s" % (y,x) + else: + # check for new users in ldap group + for y in ldap_groups[x]: + if y not in phab_groups_tbl[x]: + print "- useradd %s to %s" % (y,x) + + # check for removed users in ldap group + for y in phab_groups_tbl[x]: + if y not in PHAB_LOCAL_USERS and y not in ldap_groups[x]: + print "- userdel %s from %s" % (y,x) + +# lastly check for groups removed from ldap +for x in phab_groups_tbl.keys(): + if x not in ldap_groups: + print "del group from phab: %s" % x diff --git a/linaro/import_ldap_user.php b/linaro/import_ldap_user.php new file mode 100755 index 0000000..9f9e411 --- /dev/null +++ b/linaro/import_ldap_user.php @@ -0,0 +1,73 @@ +#!/usr/bin/env php + '); + exit(1); +} + +$username = $argv[1]; +$email = $argv[2]; +$realname = $argv[3]; +$admin = $argv[4]; + +$admin = id(new PhabricatorUser())->loadOneWhere( + 'username = %s', + $argv[4]); +if (!$admin) { + throw new Exception( + pht( + 'Admin user must be the username of a valid Phabricator account')); +} + +$existing_user = id(new PhabricatorUser())->loadOneWhere( + 'username = %s', + $username); +if ($existing_user) { + throw new Exception( + pht( + "There is already a user with the username '%s'!", + $username)); +} + +$existing_email = id(new PhabricatorUserEmail())->loadOneWhere( + 'address = %s', + $email); +if ($existing_email) { + throw new Exception( + pht( + "There is already a user with the email '%s'!", + $email)); +} + +$user = new PhabricatorUser(); +$user->setUsername($username); +$user->setRealname($realname); +$user->setIsApproved(1); + +$email_object = id(new PhabricatorUserEmail()) + ->setAddress($email) + ->setIsVerified(1); + +id(new PhabricatorUserEditor()) + ->setActor($admin) + ->createNewUser($user, $email_object); + +id(new PhabricatorExternalAccount()) + ->setUserPHID($user->getPHID()) + ->setAccountType('ldap') + ->setAccountDomain('self') + ->setAccountID($username) + ->save(); + + +echo pht( + "Created ldap user '%s' (realname='%s', email='%s').\n", + $username, + $realname, + $email); diff --git a/linaro/phab_list_projects.py b/linaro/phab_list_projects.py new file mode 100755 index 0000000..74cc553 --- /dev/null +++ b/linaro/phab_list_projects.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import multiprocessing +import time +import yaml +import ast +import json +import sys +import xmlrpclib +import os +import re +from phabricator import Phabricator +from wmfphablib import Phab as phabmacros +from wmfphablib import return_bug_list +from wmfphablib import phdb +from wmfphablib import now +from wmfphablib import mailinglist_phid +from wmfphablib import set_project_icon +from wmfphablib import phabdb +from wmfphablib import Phab +from wmfphablib import log +from wmfphablib import notice +from wmfphablib import vlog +from wmfphablib import errorlog as elog +from wmfphablib import bzlib +from wmfphablib import config +from wmfphablib import bzlib +from wmfphablib import util +from wmfphablib import datetime_to_epoch +from wmfphablib import epoch_to_datetime +from wmfphablib import ipriority + +def list_projects(): + phab = Phabricator(token=config.phab_cert, + host=config.phab_host) + + phab_query = phab.project.search() + + return phab_query['data'] + +for x in list_projects(): + print '%s - %s' % (x['phid'],x['fields']['name']) + if x['fields']['parent'] is not None: + print ' parent: %s' % x['fields']['parent']['phid'] diff --git a/linaro_migrate_prod_comp.py b/linaro_migrate_prod_comp.py new file mode 100755 index 0000000..fc30c78 --- /dev/null +++ b/linaro_migrate_prod_comp.py @@ -0,0 +1,43 @@ +#!/usr/bin/python + +import xmlrpclib,sys,datetime +from wmfphablib import config +import argparse +import subprocess + +server = xmlrpclib.ServerProxy(config.Bugzilla_url, use_datetime=True) +token_data = server.User.login({'login':config.Bugzilla_login,'password':config.Bugzilla_password}) +token = token_data['token'] + +parser = argparse.ArgumentParser(description="Generate list of bug ids by specifying product and/or component") +parser.add_argument('-c', help='limit to a component', action='store', dest='COMPONENT') +parser.add_argument('-p', help='limit to a product', action='store', dest='PRODUCT') +parser.add_argument('-F', help='no fetch', action='store_true', dest='NO_FETCH', default=False) +parser.add_argument('-C', help='no create', action='store_true', dest='NO_CREATE', default=False) +args = parser.parse_args() + +kwargs = {'Bugzilla_token': token} + +if args.COMPONENT is not None: + kwargs['component'] = args.COMPONENT + +if args.PRODUCT is not None: + kwargs['product'] = args.PRODUCT + +#print kwargs +#print "no fetch: %s" % args.NO_FETCH +#print "no create: %s" % args.NO_CREATE +#sys.exit(2) + +bug_list = server.Bug.search(kwargs) + +for b in bug_list['bugs']: + print 'Processing: [%s] %s/%s - % s' % (b['id'], b['product'], b['component'], b['summary']) + + if not args.NO_FETCH: + output = subprocess.check_output(['./bugzilla_fetch.py', str(b['id'])]) + print str(output) + + if not args.NO_CREATE: + output = subprocess.check_output(['./bugzilla_create.py', str(b['id'])]) + print str(output) diff --git a/wmfphablib/bzlib.py b/wmfphablib/bzlib.py index f0c66ab..3b63ce6 100755 --- a/wmfphablib/bzlib.py +++ b/wmfphablib/bzlib.py @@ -8,25 +8,34 @@ dupe_literals = ['This bug has been marked as a duplicate of bug', # Some issues are just missing instead of constant failures we skip -missing = [15368, 15369, 15370, 15371, 15372, 15373, 15374] +#missing = [15368, 15369, 15370, 15371, 15372, 15373, 15374] +missing = [] def is_sensitive(name): return name.strip().lower().startswith('security') -def sanitize_project_name(product, component): - """ translate bz product/component into valid project - :param product: str - :param component: str +#def sanitize_project_name(product, component): +# """ translate bz product/component into valid project +# :param product: str +# :param component: str +# """ +# component_separator = '-' +# product = re.sub('\s', '-', product) +# product = product.replace('_', '-') +# component = re.sub('\s', '-', component) +# component = component.replace('_', '-') +# component = component.replace('/', '-or-') +# return "%s%s%s" % (product, +# component_separator, +# component) + +def sanitize_project_name(name): + """ translate bz product/component name into valid project """ - component_separator = '-' - product = re.sub('\s', '-', product) - product = product.replace('_', '-') - component = re.sub('\s', '-', component) - component = component.replace('_', '-') - component = component.replace('/', '-or-') - return "%s%s%s" % (product, - component_separator, - component) + name = re.sub('\s', '-', name) + name = name.replace('_', '-') + name = name.replace('/', '-or-') + return name def build_comment(c, secstate): """ takes a native bz comment dict and outputs @@ -36,11 +45,14 @@ def build_comment(c, secstate): # these indicate textual metadata history and should be # preserved as literal clean_c = {} - clean_c['author'] = c['author'].split('@')[0] + #kspoon: another WM customization? We call it 'creator' for comment + #clean_c['author'] = c['author'].split('@')[0] + clean_c['creator'] = c['creator'].split('@')[0] clean_c['creation_time'] = str(c['creation_time']) clean_c['creation_time'] = int(float(c['creation_time'])) - if c['author'] != c['creator']: - clean_c['creator'] = c['creator'].split('@')[0] + #kspoon: see above + #if c['author'] != c['creator']: + # clean_c['creator'] = c['creator'].split('@')[0] clean_c['count'] = c['count'] if c['count'] == 0: @@ -113,14 +125,19 @@ def status_convert(bz_status, bz_resolution): declined we have decided not too -- even though we could """ - statuses = {'new': 'open', + statuses = {'unconfirmed': 'open', + 'confirmed': 'open', + 'in_progress': 'open', 'resolved': 'resolved', - 'reopened': 'open', - 'closed': 'resolved', - 'verified': 'resolved', - 'assigned': 'open', - 'unconfirmed': 'open', - 'patch_to_review': 'open'} + 'verified': 'resolved'} + #statuses = {'new': 'open', + # 'resolved': 'resolved', + # 'reopened': 'open', + # 'closed': 'resolved', + # 'verified': 'resolved', + # 'assigned': 'open', + # 'unconfirmed': 'open', + # 'patch_to_review': 'open'} if bz_resolution.lower() in ['wontfix', 'later', 'worksforme']: return 'declined' @@ -147,7 +164,10 @@ def priority_convert(bz_priority): 'normal': 50, 'low': 25, 'lowest': 10} - return priorities[bz_priority.lower()] + if bz_priority.lower() in priorities.keys(): + return priorities[bz_priority.lower()] + else: + return priorities['unprioritized'] def see_also_transform(): """convert see also listing to T123 refs in phab diff --git a/wmfphablib/config.py b/wmfphablib/config.py index 74c74e6..bbfee21 100755 --- a/wmfphablib/config.py +++ b/wmfphablib/config.py @@ -10,7 +10,8 @@ import ConfigParser parser = ConfigParser.SafeConfigParser() parser.read(cfile) dbhost = parser.get('general', 'dbhost') -dbslave = parser.get('general', 'dbslave') +#dbslave = parser.get('general', 'dbslave') +dbslave = None file_upload_timeout = int(parser.get('general', 'file_upload_timeout')) parser_mode = 'phmanifest' phmanifest_user = parser.get(parser_mode, 'user') diff --git a/wmfphablib/phabapi.py b/wmfphablib/phabapi.py index 9f2dbe7..e1d753d 100755 --- a/wmfphablib/phabapi.py +++ b/wmfphablib/phabapi.py @@ -84,7 +84,16 @@ class phabapi: auxiliary={"std:maniphest:external_reference":"%s" % (reference,), "std:maniphest:security_topic": security}) + def get_project_phid(self, name): + proj = self.con.project.query(names=[name]) + if len(proj['data']) > 0: + phid = proj['data'][proj['data'].keys()[0]]['phid'] + return phid + else: + return None + def ensure_project(self, project_name, + parent_phid=None, pmembers=[], view='public', edit='public'): @@ -93,28 +102,35 @@ class phabapi: :param pmembers: list :param view: str :param edit str""" - existing_proj = phabdb.get_project_phid(project_name) + existing_proj = self.con.project.query(names=[project_name]) + log(str(existing_proj)) + #existing_proj = phabdb.get_project_phid(project_name) #print "EXISTING PROJ: ", existing_proj #print "EXISTING PROJ TYPE: ", type(existing_proj) #existing_proj = self.con.project.query(names=[project_name]) - if not existing_proj: + if len(existing_proj['data']) < 1: log('need to create project(s) ' + project_name) - try: - new_proj = self.con.project.create(name=project_name, members=pmembers) - #XXX: Bug where we have to specify a members array! - except phabricator.APIError: - pass - phid = phabdb.get_project_phid(project_name) - if not phid: - raise Exception("Project %s does not exist still." % (project_name,)) - #existing_proj = self.con.project.query(names=[project_name]) - #log(str(existing_proj)) - #phid = existing_proj['data'][existing_proj['data'].keys()[0]]['phid'] - phabdb.set_project_policy(phid, view, edit) + #try: + print "name = %s" % project_name + print "members = %s" % pmembers + # obsoleted API call + #new_proj = self.con.project.create(name=project_name, members=pmembers) + tlist = [ + {"type":"name", "value":project_name}, + {"type":"view", "value":view}, + {"type":"edit", "value":edit}, + ] + + if len(pmembers) > 0: + tlist.append( {"type":"members.set", "value":pmembers} ) + if parent_phid != None: + tlist.append( {"type":"parent", "value":parent_phid} ) + + existing_proj = self.con.project.edit( transactions = tlist ) + phid = existing_proj['object']['phid'] else: - phid = existing_proj - #phid = existing_proj['data'][existing_proj['data'].keys()[0]]['phid'] - log(project_name + ' exists') + print existing_proj + phid = existing_proj['data'].keys()[0] return phid def upload_file(self, name, data, viewPolicy, dump=False): diff --git a/wmfphablib/rtlib.py b/wmfphablib/rtlib.py index 53fdb32..67ffab0 100755 --- a/wmfphablib/rtlib.py +++ b/wmfphablib/rtlib.py @@ -1,11 +1,12 @@ enabled = ['codfw', 'ulsfo', 'pmtpa', 'ops-requests', 'network', 'esams', 'eqiad', 'core-ops',] import util -try: - from rtppl import ppl as users -except: - util.notice("rtppl not found!") - users = {} +#kspoon: linaro doesn't use RT +#try: +# from rtppl import ppl as users +#except: +# util.notice("rtppl not found!") +users = {} import re from datetime import datetime diff --git a/wmfphablib/util.py b/wmfphablib/util.py index 0100650..cf5daba 100644 --- a/wmfphablib/util.py +++ b/wmfphablib/util.py @@ -11,6 +11,7 @@ import syslog import phabdb import bzlib +PHAB_PATH="/srv/phabricator" def tflatten(t_of_tuples): return [element for tupl in t_of_tuples for element in tupl] @@ -78,7 +79,7 @@ def log(msg): def notice(msg): msg = unicode(msg) - print "NOTICE: ", msg + #print "NOTICE: ", msg log(msg) def vlog(msg): @@ -126,10 +127,10 @@ def get_index(seq, attr, value): return next(index for (index, d) in enumerate(seq) if d[attr] == value) def purge_cache(): - return runBash('/srv/phab/phabricator/bin/cache purge --purge-remarkup') + return runBash('%s/bin/cache purge --purge-remarkup' % PHAB_PATH) def destroy_issue(id): - return runBash('/srv/phab/phabricator/bin/remove destroy T%s --no-ansi --force' % (id,)) + return runBash('%s/bin/remove destroy T%s --no-ansi --force' % (PHAB_PATH,id,)) def remove_issue_by_bugid(bugid, ref): log("Removing issue by reference %s%s" % (ref, bugid)) @@ -159,7 +160,7 @@ def return_bug_list(dbcon=None, priority=None, table='bugzilla_meta'): bugs = phabdb.get_issues_by_priority(dbcon, priority, table=table) - elif '-' in bugs[0]: + elif len(bugs) > 0 and '-' in bugs[0]: start, stop = bugs[0].split('-') bugrange = range(int(start), int(stop) + 1) -- cgit v1.2.3