diff --git a/src/pyarcanist/__init__.py b/src/pyarcanist/__init__.py index 514ec32..688b303 100644 --- a/src/pyarcanist/__init__.py +++ b/src/pyarcanist/__init__.py @@ -1,10 +1,10 @@ from .cache import cache from . import cli # make sure these are imported after cli, since some # cache config may occur in there from . import whoami from . import diff from . import harbormaster -__all__ = (cli, whoami, diff, cache) +__all__ = (cli, whoami, diff, cache, harbormaster) diff --git a/src/pyarcanist/cli.py b/src/pyarcanist/cli.py index 048e6f4..fbbaafd 100644 --- a/src/pyarcanist/cli.py +++ b/src/pyarcanist/cli.py @@ -1,51 +1,49 @@ -import sys import click import hashlib from phabricator import Phabricator from . import cache # we use a global variable to store the Phabricator instance so we do not # have to add the cnx argument to several utility functions which can then # be cached by beaker. Not very elegent but it works. cnx = None class options(dict): def __getattr__(self, key): return self[key] def __setattr__(self, key, value): self[key] = value @click.group() @click.option('-v', '--verbose/--no-verbose', default=False, envvar='VERBOSE') @click.option('-h', '--host', default=None, envvar='PHAB_CONDUIT_URL') @click.option('-t', '--token', default=None, envvar='PHAB_CONDUIT_TOKEN') - @click.pass_context def pyarc(ctx, verbose, host, token): """Entry point""" global cnx ctx.ensure_object(dict) kwargs = {} if host: kwargs['host'] = host if token: kwargs['token'] = token if host or token: hkey = hashlib.sha256('{}:{}'.format( host or '', token or '').encode()).hexdigest() cache.kwargs['data_dir'] += '/{}'.format(hkey) ctx.obj['cnx'] = cnx = Phabricator(**kwargs) ctx.obj['options'] = options(verbose=verbose, host=host, token=token) if verbose: click.echo('Connecting to:') click.echo(' API endpoint: {}'.format(cnx.host)) click.echo(' Token: {}'.format(cnx.token)) if __name__ == '__main__': pyarc(obj={}) diff --git a/src/pyarcanist/diff.py b/src/pyarcanist/diff.py index cbbbc2e..8d49eb5 100644 --- a/src/pyarcanist/diff.py +++ b/src/pyarcanist/diff.py @@ -1,108 +1,108 @@ from itertools import chain from datetime import datetime import humanize import click import git from . import cli from .whoami import get_user -from .tools import wrap, object_from_phid +from .tools import wrap from . import cache @cache.cache() def get_repositories(uris): if isinstance(uris, str): uris = [uris] return cli.cnx.diffusion.repository.search( constraints={'uris': uris}).data @cache.cache() def repo_from_phid(phid): repo = cli.cnx.diffusion.repository.search( constraints={'phids': [phid]}).data return repo and repo[0] or None def format_diff(kw): # warning: this function modifies the given dict 'kw' kw['id'] = click.style(str(kw['id']), bold=True) kw['fields']['status']['name'] = click.style( kw['fields']['status']['name'], fg=kw['fields']['status']['color.ansi']) for k in kw['fields']: if k.startswith('date'): kw['fields'][k] = datetime.fromtimestamp(kw['fields'][k]) return kw @cli.pyarc.command() @click.option('-u', '--mine/--all-users', default=False) @click.option('-A', '--all-repos/--current-repo', default=False) @click.option('-s', '--summary/--default', default=False) @click.pass_context def diff(ctx, mine, all_repos, summary): '''List Diffs''' cnx = cli.cnx user = get_user() # options = ctx.obj['options'] query = {'statuses': ['open()']} gitrepo = None repos = None if not all_repos: try: gitrepo = git.Repo() remotes = list(chain(*(r.urls for r in gitrepo.remotes))) repos = get_repositories(remotes) except git.InvalidGitRepositoryError: pass if repos: query['repositoryPHIDs'] = [r['phid'] for r in repos] if mine: query['authorPHIDs'] = [user['phid']] # print('query=', query) diffs = cnx.differential.revision.search(constraints=query).data for diff in sorted(diffs, key=lambda x: int(x['id'])): fdiff = format_diff(diff) if summary: click.echo( '{fields[status][name]:25} D{id}: {fields[title]}'.format( **fdiff)) else: click.echo( wrap('{fields[status][name]:25} D{id}'.format( **fdiff))) # give a bit more informations fields = fdiff['fields'] phrepo = repo_from_phid(fields['repositoryPHID'])['fields'] author = get_user(fields['authorPHID']) click.echo('{key}: {shortName} ({callsign})'.format( key=click.style('Repo', fg='yellow'), **phrepo)) click.echo('{key}: {value}'.format( key=click.style('Author', fg='yellow'), value=click.style( author['name'], fg='red' if author['name'] == user['userName'] else ''))) n = datetime.now() click.echo('{key}: {value} ago'.format( key=click.style('Created', fg='yellow'), value=humanize.naturaldelta(n - fields['dateCreated']))) click.echo('{key}: {value} ago'.format( key=click.style('Modified', fg='yellow'), value=humanize.naturaldelta(n - fields['dateModified']))) click.secho('Summary:', fg='yellow') click.secho(' ' + fields['title'], bold=True) click.echo() click.echo('\n'.join(' ' + x for x in fields['summary'].splitlines())) click.echo() diff --git a/src/pyarcanist/harbormaster.py b/src/pyarcanist/harbormaster.py index fd8af41..bf5fdb2 100644 --- a/src/pyarcanist/harbormaster.py +++ b/src/pyarcanist/harbormaster.py @@ -1,84 +1,84 @@ import click from . import cli validators = { 'unit': [ ('name', True, None), ('result', True, ('pass', 'fail', 'skip', 'broken', 'unsound')), ('namespace', False, None), ('engine', False, None), ('duration', False, float), ('path', False, None), ('coverage', False, None), ('details', False, None), ('format', False, None), ], 'lint': [ ('name', True, None), ('code', True, None), ('severity', True, ('advice', 'autofix', 'warning', 'error', 'disabled')), ('path', True, None), ('line', False, int), ('char', False, int), ('description', False, None), ], } def check_validator(validator, value): if not validator: return True if isinstance(validator, tuple): return value in validator if callable(validator): try: validator(value) except Exception: return False return True def validate(params, report_type): if isinstance(params, str): params = dict(x.strip().split('=', 1) for x in params.split(',')) for k, mandatory, validator in validators[report_type]: - if mandatory and not k in params: + if mandatory and k not in params: raise ValueError( 'Parameter {} is mandatory for a {} report'.format( k, report_type)) if k in params and not check_validator(validator, params[k]): raise ValueError( 'Parameter {} has an invalid value "{}"'.format(k, params[k])) return params @cli.pyarc.command() @click.argument('message-type', type=click.Choice(['pass', 'fail', 'work'])) @click.argument('phid') @click.option('-u', '--unit', multiple=True) @click.option('-l', '--lint', multiple=True) @click.pass_context def send_message(ctx, message_type, phid, unit, lint): '''Send a harbormaster message''' options = ctx.obj['options'] kw = {'type': message_type, 'buildTargetPHID': phid} try: if unit: kw['unit'] = [validate(u, 'unit') for u in unit] if lint: kw['lint'] = [validate(l, 'lint') for l in lint] except ValueError as e: ctx.fail('Invalid parameter: {}'. format(e)) if options.verbose: click.echo('Sending message to harbormaster:') for k, v in kw.items(): click.echo(" {}: {}".format(k, v)) cli.cnx.harbormaster.sendmessage(**kw)