From 8aaea6d200c190f7f28566616aaaf3b56c182e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Bol=C3=ADvar?= Date: Wed, 11 May 2022 00:32:20 -0700 Subject: [PATCH] scripts: add github_helpers.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is meant to be a place where we can store generic zephyr wrappers around the de-facto standard github API for python. The first 'customer' will be a script that snapshots our open bugs at a particular point in time, which will be added in a later patch. I think it's useful to factor this file out of there from the beginning just to keep things clean, even though I don't have a second customer in mind at the moment. Signed-off-by: Martí Bolívar Signed-off-by: Stephanos Ioannidis --- scripts/github_helpers.py | 81 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 scripts/github_helpers.py diff --git a/scripts/github_helpers.py b/scripts/github_helpers.py new file mode 100644 index 00000000000000..9ef77eac5c12a0 --- /dev/null +++ b/scripts/github_helpers.py @@ -0,0 +1,81 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 + +''' +Generic GitHub helper routines which may be useful to other scripts. + +This file is not meant to be run directly, but rather to be imported +as a module from other scripts. +''' + +# Note that the type annotations are not currently checked by mypy. +# Unless that changes, they serve as documentation, rather than +# guarantees from a type checker. + +# stdlib +import getpass +import os +import netrc +import sys +from typing import Dict + +# third party +import github + +def get_github_credentials(ask: bool = True) -> Dict[str, str]: + '''Get credentials for constructing a github.Github object. + + This function tries to get github.com credentials from these + places, in order: + + 1. a ~/.netrc file, if one exists + 2. a GITHUB_TOKEN environment variable + 3. if the 'ask' kwarg is truthy, from the user on the + at the command line. + + On failure, RuntimeError is raised. + + Scripts often need credentials because anonym access to + api.github.com is rate limited more aggressively than + authenticated access. Scripts which use anonymous access are + therefore more likely to fail due to rate limiting. + + The return value is a dict which can be passed to the + github.Github constructor as **kwargs. + + :param ask: if truthy, the user will be prompted for credentials + if none are found from other sources + ''' + + try: + nrc = netrc.netrc() + except (FileNotFoundError, netrc.NetrcParseError): + nrc = None + + if nrc is not None: + auth = nrc.authenticators('github.com') + if auth is not None: + return {'login_or_token': auth[0], 'password': auth[2]} + + token = os.environ.get('GITHUB_TOKEN') + if token: + return {'login_or_token': token} + + if ask: + print('Missing GitHub credentials:\n' + '~/.netrc file not found or has no github.com credentials, ' + 'and GITHUB_TOKEN is not set in the environment. ' + 'Please give your GitHub token.', + file=sys.stderr) + token = getpass.getpass('token: ') + return {'login_or_token': token} + + raise RuntimeError('no credentials found') + +def get_github_object(ask: bool = True) -> github.Github: + '''Get a github.Github object, created with credentials. + + :param ask: passed to get_github_credentials() + ''' + return github.Github(**get_github_credentials())