PKª³yGê\sjgithub_maintainer/__init__.py__version__ = '0.1' PK ´yGÎ_÷(NNgithub_maintainer/__main__.pyfrom github_maintainer.cli import main if __name__ == '__main__': main() PKq¶yG$wG;;github_maintainer/cli.pyimport click import codecs import datetime import os import requests import stups_cli.config import time import yaml from clickclick import print_table, Action, AliasedGroup CONFIG_DIR = click.get_app_dir('github-maintainer-cli') adapter = requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10) session = requests.Session() session.mount('http://', adapter) session.mount('https://', adapter) def parse_time(s: str) -> float: ''' >>> parse_time('2015-04-14T19:09:01Z') > 0 True ''' try: utc = datetime.datetime.strptime(s, '%Y-%m-%dT%H:%M:%SZ') ts = time.time() utc_offset = datetime.datetime.fromtimestamp(ts) - datetime.datetime.utcfromtimestamp(ts) local = utc + utc_offset return local.timestamp() except: return None def get_my_issues(token): headers = {'Authorization': 'Bearer {}'.format(token)} page = 1 while True: response = session.get('https://api.github.com/issues', params={'per_page': 100, 'page': page, 'filter': 'all'}, headers=headers) response.raise_for_status() for issue in response.json(): yield issue page += 1 if 'next' not in response.headers.get('Link', ''): break def get_my_repos(my_emails, token): headers = {'Authorization': 'Bearer {}'.format(token)} page = 1 while True: response = session.get('https://api.github.com/user/repos', params={'per_page': 100, 'page': page}, headers=headers) response.raise_for_status() for gh_repo in response.json(): contents_url = gh_repo['contents_url'] r = session.get(contents_url.replace('{+path}', 'MAINTAINERS'), headers=headers) if r.status_code == 200: b64 = r.json()['content'] maintainers = codecs.decode(b64.encode('utf-8'), 'base64').decode('utf-8') maintainers = list(filter(None, maintainers.split('\n'))) for maintainer in maintainers: name, _, email = maintainer.strip().partition('<') email = email.strip().rstrip('>') if email in my_emails: repo = {} for key in ['url', 'name', 'full_name', 'description', 'private', 'language', 'stargazers_count', 'subscribers_count', 'forks_count', 'fork']: repo[key] = gh_repo.get(key) repo['maintainers'] = maintainers yield repo page += 1 if 'next' not in response.headers.get('Link'): break def get_repositories(): path = os.path.join(CONFIG_DIR, 'repositories.yaml') try: with open(path) as fd: repositories = yaml.safe_load(fd) except: repositories = {} return repositories @click.group(cls=AliasedGroup) @click.pass_context def cli(ctx): config = stups_cli.config.load_config('github-maintainer-cli') emails = config.get('emails') token = config.get('github_access_token') if not 'configure'.startswith(ctx.invoked_subcommand or 'x'): if not emails: raise click.UsageError('No emails configured. Please run "configure".') if not token: raise click.UsageError('No GitHub access token configured. Please run "configure".') ctx.obj = config @cli.command() @click.pass_obj def configure(config): '''Configure GitHub access''' emails = click.prompt('Your email addresses', default=','.join(config.get('emails'))) token = click.prompt('Your personal GitHub access token', hide_input=True, default=config.get('github_access_token')) emails = emails.split(',') config = {'emails': emails, 'github_access_token': token} repositories = {} with Action('Scanning repositories..') as act: for repo in get_my_repos(emails, token): repositories[repo['url']] = repo act.progress() path = os.path.join(CONFIG_DIR, 'repositories.yaml') os.makedirs(CONFIG_DIR, exist_ok=True) with open(path, 'w') as fd: yaml.safe_dump(repositories, fd) with Action('Storing configuration..'): stups_cli.config.store_config(config, 'github-maintainer-cli') @cli.command() @click.pass_obj def repositories(config): '''List repositories''' token = config.get('github_access_token') repositories = get_repositories() for issue in get_my_issues(token): repo = repositories.get(issue['repository']['url']) if repo: repo['open_issues'] = repo.get('open_issues', 0) + 1 if issue.get('pull_request'): repo['open_pull_requests'] = repo.get('open_pull_requests', 0) + 1 rows = [] for url, repo in sorted(repositories.items()): rows.append(repo) print_table(['full_name', 'stargazers_count', 'forks_count', 'open_issues', 'open_pull_requests'], rows) @cli.command() @click.pass_obj def issues(config): '''List open issues''' token = config.get('github_access_token') repositories = get_repositories() rows = [] for issue in get_my_issues(token): if not issue.get('pull_request'): repo = repositories.get(issue['repository']['url']) if repo: issue['repository'] = repo['full_name'] issue['created_time'] = parse_time(issue['created_at']) issue['created_by'] = issue['user']['login'] issue['labels'] = ', '.join([l['name'] for l in issue['labels']]) rows.append(issue) rows.sort(key=lambda x: (x['repository'], x['number'])) print_table(['repository', 'number', 'title', 'labels', 'created_time', 'created_by'], rows) @cli.command('pull-requests') @click.pass_obj def pull_requests(config): '''List pull requests''' token = config.get('github_access_token') repositories = get_repositories() rows = [] for issue in get_my_issues(token): pr = issue.get('pull_request') if pr: repo = repositories.get(issue['repository']['url']) if repo: r = session.get(pr['url'], headers={'Authorization': 'Bearer {}'.format(token)}) pr = r.json() issue.update(**pr) issue['repository'] = repo['full_name'] issue['created_time'] = parse_time(issue['created_at']) issue['created_by'] = issue['user']['login'] issue['labels'] = ', '.join([l['name'] for l in issue['labels']]) rows.append(issue) rows.sort(key=lambda x: (x['repository'], x['number'])) print_table(['repository', 'number', 'title', 'labels', 'mergeable', 'mergeable_state', 'created_time', 'created_by'], rows) def main(): cli() PKÀ¶yGü¶€ee/github_maintainer-0.1.dist-info/DESCRIPTION.rst===================== GitHub Maintainer CLI ===================== .. image:: https://img.shields.io/pypi/dw/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: License Command line tool to help you in the role of an Open Source project maintainer on GitHub. * Reads ``MAINTAINERS`` file to find repositories you are responsible for * Allows listing open issues and pull requests Why? ==== One could argue that the GitHub "watch" feature (+ notifications) should be enough to get along, but what if I want to watch many repositories but only maintain a few? GitHub has no notion of a "project maintainer", therefore we use the convention of putting a ``MAINTAINERS`` file in the root of each git repository. Each person listed in the ``MAINTAINERS`` file is responsible for managing issues, pull requests and keeping code quality. This tool should support you as a maintainer in doing so easily from the command line. Usage ===== .. code-block:: bash $ sudo pip3 install -U github-maintainer $ github-maintainer repositories $ github-maintainer issues $ github-maintainer pull-requests Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKÀ¶yG\ BB0github_maintainer-0.1.dist-info/entry_points.txt[console_scripts] github-maintainer = github_maintainer.cli:main PKÀ¶yGµáœ<¹¹-github_maintainer-0.1.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython"], "extensions": {"python.commands": {"wrap_console": {"github-maintainer": "github_maintainer.cli:main"}}, "python.details": {"contacts": [{"email": "henning.jacobs@zalando.de", "name": "Henning Jacobs", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zalando-stups/github-maintainer-cli"}}, "python.exports": {"console_scripts": {"github-maintainer": "github_maintainer.cli:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["github", "git", "project", "maintainer"], "license": "Apache License 2.0", "metadata_version": "2.0", "name": "github-maintainer", "run_requires": [{"requires": ["clickclick", "requests", "stups-cli-support"]}], "summary": "CLI support tool for GitHub repo maintainers", "test_requires": [{"requires": ["pytest", "pytest-cov"]}], "version": "0.1"}PKÀ¶yG}¨‘ -github_maintainer-0.1.dist-info/top_level.txtgithub_maintainer PKÀ¶yG}À‚¼\\%github_maintainer-0.1.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKÀ¶yG§—9±Ž Ž (github_maintainer-0.1.dist-info/METADATAMetadata-Version: 2.0 Name: github-maintainer Version: 0.1 Summary: CLI support tool for GitHub repo maintainers Home-page: https://github.com/zalando-stups/github-maintainer-cli Author: Henning Jacobs Author-email: henning.jacobs@zalando.de License: Apache License 2.0 Keywords: github git project maintainer Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Dist: clickclick Requires-Dist: requests Requires-Dist: stups-cli-support ===================== GitHub Maintainer CLI ===================== .. image:: https://img.shields.io/pypi/dw/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: PyPI Downloads .. image:: https://img.shields.io/pypi/v/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/l/github-maintainer.svg :target: https://pypi.python.org/pypi/github-maintainer/ :alt: License Command line tool to help you in the role of an Open Source project maintainer on GitHub. * Reads ``MAINTAINERS`` file to find repositories you are responsible for * Allows listing open issues and pull requests Why? ==== One could argue that the GitHub "watch" feature (+ notifications) should be enough to get along, but what if I want to watch many repositories but only maintain a few? GitHub has no notion of a "project maintainer", therefore we use the convention of putting a ``MAINTAINERS`` file in the root of each git repository. Each person listed in the ``MAINTAINERS`` file is responsible for managing issues, pull requests and keeping code quality. This tool should support you as a maintainer in doing so easily from the command line. Usage ===== .. code-block:: bash $ sudo pip3 install -U github-maintainer $ github-maintainer repositories $ github-maintainer issues $ github-maintainer pull-requests Running Unit Tests ================== .. code-block:: bash $ python3 setup.py test --cov-html=true Releasing ========= .. code-block:: bash $ ./release.sh PKÀ¶yGÞéÊ‚‚&github_maintainer-0.1.dist-info/RECORDgithub_maintainer/__init__.py,sha256=B5gaXa_iv3KKAmR9gppWSb7F1Iaba-sVO-73URL8e70,20 github_maintainer/__main__.py,sha256=el-0anHtJsZeYOzzFqjyuu07FkH-gwQdbCRLSpqMSHs,78 github_maintainer/cli.py,sha256=QOPAHBTPAFHRVSi2ycNod_E6DUOBgcu3YfRL36euVr8,6971 github_maintainer-0.1.dist-info/DESCRIPTION.rst,sha256=7dB3CmGvDWSDkkCW9S2xFfdcm5c9Hy6NdmtQ1opG4MM,1637 github_maintainer-0.1.dist-info/METADATA,sha256=pc0EyG9RV0bqs5ujDaXTQRTqhuYRYHyKz12sUhbkWJo,2446 github_maintainer-0.1.dist-info/RECORD,, github_maintainer-0.1.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 github_maintainer-0.1.dist-info/entry_points.txt,sha256=wHtNgtGF8B4YKdlIEvUPWJ09YfAqEdsmuATSoHDzQFY,66 github_maintainer-0.1.dist-info/metadata.json,sha256=m75XKkWcsE7arHsizoClSnvzOymnfV9bk7r3FFmDSQM,1209 github_maintainer-0.1.dist-info/top_level.txt,sha256=ww1bkzU7QJmyAd3ClFNgNgYNblg8hQQSquYCnqLa1tA,18 PKª³yGê\sjgithub_maintainer/__init__.pyPK ´yGÎ_÷(NNOgithub_maintainer/__main__.pyPKq¶yG$wG;;Øgithub_maintainer/cli.pyPKÀ¶yGü¶€ee/Igithub_maintainer-0.1.dist-info/DESCRIPTION.rstPKÀ¶yG\ BB0û"github_maintainer-0.1.dist-info/entry_points.txtPKÀ¶yGµáœ<¹¹-‹#github_maintainer-0.1.dist-info/metadata.jsonPKÀ¶yG}¨‘ -(github_maintainer-0.1.dist-info/top_level.txtPKÀ¶yG}À‚¼\\%ì(github_maintainer-0.1.dist-info/WHEELPKÀ¶yG§—9±Ž Ž (‹)github_maintainer-0.1.dist-info/METADATAPKÀ¶yGÞéÊ‚‚&_3github_maintainer-0.1.dist-info/RECORDPK J%7