From 5b157166665b253733862e5d653ef06f11828c02 Mon Sep 17 00:00:00 2001 From: "Darryl L. Miles" Date: Thu, 1 Jun 2023 17:56:35 +0100 Subject: [PATCH] git_utils.py: headers_try_to_add_authorization_from_environment(headers: dict) This should make the project easier to setup and use and less likely to break (through expired user tokens). After this commit: * Pickup implicit GHA os.environ $GITHUB_ACTOR and $GITHUB_TOKEN by default. This will provide a good enough token for read-only access to public repositories. * Allow override with custom secret GH_USERNAME and/or GH_TOKEN just as before in those use cases where a token with additional scopes maybe needed to perform administrative actions. The values must be defined and non-blank to be considered. A non-blank expires token will cause failure (as before). * Continue on a best-effort basis (of not using any token to access public repositories) instead of failing the workflow immediately. * Emit a warning to stderr for diagnostic purpose from GHA and local shell in scenarios where no token is setup. This means it is still correct to keep the *.yaml config as-is: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_USERNAME: ${{ secrets.GH_USERNAME }} GH_TOKEN: ${{ secrets.GH_TOKEN }} Because a normal consumer of this project inside a GHA will utilise their $GITHUB_ACTOR and $GITHUB_TOKEN values instead of acting upon blank values for $GH_USERNAME and $GH_TOKEN. --- git_utils.py | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/git_utils.py b/git_utils.py index 495e4ad..1bbb5e9 100644 --- a/git_utils.py +++ b/git_utils.py @@ -6,6 +6,7 @@ import os import zipfile import io import errno +import sys def unique(duplist): @@ -35,19 +36,43 @@ def check_status(r): logging.error("unauthorised, check INFO.md for information about GitHub API keys") exit(1) +def headers_try_to_add_authorization_from_environment(headers: dict) -> bool: + gh_token = os.getenv('GH_TOKEN', '') # override like gh CLI + if not gh_token: + gh_token = os.getenv('GITHUB_TOKEN', '') # GHA inherited + + if len(gh_token) > 0: + # As per https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api + headers['authorization'] = 'Bearer ' + gh_token + return True + + # Use a token instead which is designed to limit exposure of passwords + # I can't find any GH docs explaining use cases for Basic auth and confirming a token + # can be used instead of PASSWORD in the password field of authorization header. + gh_username = os.getenv('GH_USERNAME', '') # override like gh CLI + if not gh_username: + gh_username = os.getenv('GITHUB_ACTOR', '') # GHA inherited + + gh_password = os.getenv('GH_PASSWORD', '') + + if len(gh_username) > 0 and len(gh_password) > 0: + auth_string = gh_username + ':' + gh_password + encoded = base64.b64encode(auth_string.encode('ascii')) + headers['authorization'] = 'Basic ' + encoded.decode('ascii') + return True + + print("WARNING: No github token found from environment, trying public API requests without, see docs/INFO.md#instructions-to-build-gds", file=sys.stderr) + return False def fetch_file_from_git(git_url, path): # get the basics user_name, repo = split_git_url(git_url) - # authenticate for rate limiting - auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN'] - encoded = base64.b64encode(auth_string.encode('ascii')) headers = { - "authorization" : 'Basic ' + encoded.decode('ascii'), "Accept" : "application/vnd.github+json", } - encoded = base64.b64encode(auth_string.encode('ascii')) + # authenticate for rate limiting + headers_try_to_add_authorization_from_environment(headers) api_url = 'https://api.github.com/repos/%s/%s/contents/%s' % (user_name, repo, path) @@ -107,13 +132,11 @@ def install_artifacts(url, directory): logging.debug(url) user_name, repo = split_git_url(url) - # authenticate for rate limiting - auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN'] - encoded = base64.b64encode(auth_string.encode('ascii')) headers = { - "authorization" : 'Basic ' + encoded.decode('ascii'), "Accept" : "application/vnd.github+json", } + # authenticate for rate limiting + headers_try_to_add_authorization_from_environment(headers) # first fetch the git commit history api_url = f'https://api.github.com/repos/{user_name}/{repo}/commits' @@ -172,13 +195,11 @@ def get_latest_action_url(url, directory): logging.debug(url) user_name, repo = split_git_url(url) - # authenticate for rate limiting - auth_string = os.environ['GH_USERNAME'] + ':' + os.environ['GH_TOKEN'] - encoded = base64.b64encode(auth_string.encode('ascii')) headers = { - "authorization" : 'Basic ' + encoded.decode('ascii'), "Accept" : "application/vnd.github+json", } + # authenticate for rate limiting + headers_try_to_add_authorization_from_environment(headers) # first fetch the git commit history api_url = f'https://api.github.com/repos/{user_name}/{repo}/commits'