from typing import Any import logging import requests from majordome.errors import NoReleaseFound, AssetNotFound, DownloadFailure logger = logging.getLogger(__name__) class GithubConnector: def __init__(self, token: str): self.base_url = "https://api.github.com" self.token = token @property def headers(self) -> dict[str, str]: return { "Accept": "application/vnd.github.v3+json", "Authorization": f"Bearer {self.token}", "X-GitHub-Api-Version": "2022-11-28", } def get_latest_release(self, owner: str, repo: str) -> dict[str, Any]: logger.info("Getting latest release from Github") logger.debug( "GET %s with headers %s", self.latest_release_url(owner, repo), self.headers ) response = requests.get( self.latest_release_url(owner, repo), headers=self.headers ) if response.ok: logger.debug("Latest release found") return response.json()[0] logger.error( "Failed to get latest release from Github. Response code was: %s", response.status_code, ) raise NoReleaseFound( f"No release found on Github for the repo {repo} from owner {owner}" ) @staticmethod def latest_release_url(owner: str, repo: str) -> str: return f"https://api.github.com/repos/{owner}/{repo}/releases?per_page=1" def download_asset(self, owner: str, repo: str, asset_id: int) -> bytes: logger.info( "Downloading asset with id %s from Github owner %s and repo %s", asset_id, owner, repo, ) asset_response = requests.get( self.asset_url(owner, repo, asset_id), headers=self.headers ) if asset_response.ok: download_url = asset_response.json().get("browser_download_url") downloaded_response = requests.get(download_url, headers=self.headers) if downloaded_response.ok: return downloaded_response.content logger.error( "Failed to download asset from Github repo %s of owner %s. Response code was: %s", repo, owner, downloaded_response.status_code, ) raise DownloadFailure( f"The asset {asset_id} was not found on Github for the repo {repo} from owner {owner}" ) logger.error( "Failed to find asset with id %s from Github repo %s of owner %s. Response code was: %s", asset_id, repo, owner, asset_response.status_code, ) raise AssetNotFound( f"The asset {asset_id} was not found on Github for the repo {repo} from owner {owner}" ) @staticmethod def asset_url(owner: str, repo: str, asset_id: int) -> str: return f"https://api.github.com/repos/{owner}/{repo}/releases/assets/{asset_id}"