apb_extra_utils.github
1import functools 2import json 3import os 4import shutil 5from tempfile import mkdtemp 6from urllib.error import HTTPError 7from urllib.request import Request, urlopen 8 9from apb_extra_utils.misc import download_and_unzip, remove_content_dir, zip_dir, create_dir 10 11PREFIX_FILE_LAST_TAG_REPO = 'last_tag_repo_github_' 12 13 14def get_api_github(owner, repo, api_request, token=None): 15 """ 16 GET Request for repository GITHUB via API GITHUB. 17 18 See the REST API DOCS here https://docs.github.com/en/rest 19 20 Args: 21 owner (str): 22 repo (str): 23 api_request (str): 24 token (str=None): 25 26 Returns: 27 info_response (dict) 28 """ 29 request_headers = { 30 'Accept': 'application/vnd.github.v3+json' 31 } 32 if token: 33 request_headers['Authorization'] = f'token {token}' 34 35 url_github = f'https://api.github.com/repos/{owner}/{repo}/{api_request}' 36 req = Request(url_github, headers=request_headers, method='GET') 37 info_response = {} 38 try: 39 with urlopen(req) as resp_request: 40 if resp_request: 41 info_response = json.load(resp_request) 42 except HTTPError as exc: 43 info_response[HTTPError.__name__] = str(exc) 44 45 return info_response 46 47 48def post_api_github(owner, repo, api_request, post_data, token=None): 49 """ 50 POST Request on repository GITHUB via API GITHUB 51 52 See the REST API DOCS here https://docs.github.com/en/rest 53 54 Args: 55 owner (str): 56 repo (str): 57 api_request (str): 58 post_data (dict): 59 token (str=None): 60 61 Returns: 62 info_response (dict) 63 """ 64 request_headers = { 65 'Accept': 'application/vnd.github.v3+json', 66 'Content-Type': 'application/json; charset=utf-8', 67 } 68 if token: 69 request_headers['Authorization'] = f'token {token}' 70 71 url_github = f'https://api.github.com/repos/{owner}/{repo}/{api_request}' 72 73 post_data_enc = json.dumps(post_data).encode('utf-8') 74 75 req = Request(url_github, headers=request_headers, method='POST') 76 info_response = {} 77 try: 78 with urlopen(req, post_data_enc) as resp_request: 79 if resp_request: 80 info_response = resp_request.__dict__ 81 except HTTPError as exc: 82 info_response[HTTPError.__name__] = str(exc) 83 84 return info_response 85 86 87@functools.cache 88def has_changes_in_github(owner, repo, branch, download_to, token=None): 89 """ 90 Check if the GitHub repository branch has changes. 91 92 Args: 93 owner (str): Owner repository Github 94 repo (str): Name repository Github 95 branch (str): Branch repository to check 96 download_to (str): Path to the local repository 97 token (str=None): Github token for private access 98 99 Returns: 100 bool: True if there are changes, False otherwise 101 str: sha_commit of the current branch state 102 """ 103 branch = branch.lower() 104 info_branches = get_api_github(owner, repo, 'branches', token) 105 info_branch = next(filter(lambda el: el.get('name', '').lower() == branch, 106 info_branches), None) 107 108 if not info_branch: 109 return False, None 110 111 sha_commit = info_branch.get('commit').get('sha') 112 expected_name_zip_repo = f'{repo}-{branch}' 113 log_last_tag = os.path.join(download_to, f'.{PREFIX_FILE_LAST_TAG_REPO}{expected_name_zip_repo}') 114 115 if os.path.exists(log_last_tag): 116 with open(log_last_tag) as fr: 117 last_tag = fr.read() 118 if last_tag and last_tag.strip() == sha_commit.strip(): 119 return False, sha_commit 120 121 return True, sha_commit 122 123 124def get_resources_from_repo_github(html_repo, tag, expected_name_zip_repo, path_repo, header=None, 125 force_update=False, remove_prev=False, as_zip=False): 126 """ 127 128 Args: 129 html_repo (str): 130 tag (str): 131 expected_name_zip_repo (str): 132 path_repo (str): 133 header (dict=None): 134 force_update (bool=False): 135 remove_prev (bool=False): 136 as_zip (bool=False): 137 138 Returns: 139 updated (bool) 140 """ 141 updated = False 142 log_last_tag = os.path.join(path_repo, f'.{PREFIX_FILE_LAST_TAG_REPO}{expected_name_zip_repo}') 143 144 if not force_update: 145 last_tag = None 146 if os.path.exists(log_last_tag): 147 with open(log_last_tag) as fr: 148 last_tag = fr.read() 149 150 if last_tag and last_tag.strip() == tag.strip(): 151 return updated 152 153 if not header: 154 header = {} 155 header['Accept'] = 'application/octet-stream' 156 157 dir_temp = mkdtemp() 158 download_and_unzip(html_repo, extract_to=dir_temp, headers=[(k, v) for k, v in header.items()]) 159 path_res = os.path.join(dir_temp, expected_name_zip_repo) 160 161 if os.path.exists(path_res): 162 create_dir(path_repo) 163 164 if as_zip: 165 zip_dir(path_res, os.path.join(path_repo, f'{expected_name_zip_repo}.zip')) 166 else: 167 if remove_prev and os.path.exists(path_repo): 168 remove_content_dir(path_repo) 169 shutil.copytree(path_res, path_repo, dirs_exist_ok=True) 170 171 shutil.rmtree(path_res, ignore_errors=True) 172 173 with open(log_last_tag, "w+") as fw: 174 fw.write(tag) 175 176 updated = True 177 178 return updated 179 180 181def download_release_repo_github(owner, repo, download_to, tag_release=None, token=None, force=False, as_zip=False, 182 remove_prev=False): 183 """ 184 Download release Github repository on the path selected. 185 186 Args: 187 owner (str): Owner repository Github 188 repo (str): Name repository Github 189 download_to (str): Path to download 190 tag_release (str=None): if not informed get 'latest' release 191 token (str=None): Github token for private access 192 force (bool=False): Force update if exists previous sources 193 remove_prev (bool=False): Remove all previous resources 194 as_zip (bool=False): Retorna como ZIP 195 196 Returns: 197 tag_name (str) 198 """ 199 if not tag_release: 200 info_release = get_api_github(owner, repo, 'releases/latest', token) 201 else: 202 info_release = get_api_github(owner, repo, f'releases/tags/{tag_release}', token) 203 204 tag_name = info_release.get('tag_name') 205 if tag_name: 206 html_release = f'https://github.com/{owner}/{repo}/archive/refs/tags/{tag_name}.zip' 207 header = {} 208 if token: 209 header['Authorization'] = f'token {token}' 210 211 get_resources_from_repo_github(html_release, tag_name, f'{repo}-{tag_name}', download_to, 212 header=header, force_update=force, remove_prev=remove_prev, as_zip=as_zip) 213 214 return tag_name 215 216 217def download_branch_repo_github(owner, repo, branch, download_to, token=None, force=False, as_zip=False, 218 remove_prev=False): 219 """ 220 Download the branch selected for the Github repo on the path selected 221 222 Args: 223 owner (str): Owner repository Github 224 repo (str): Name repository Github 225 branch (str): Branch repository to download 226 download_to (str): Path to download 227 token (str=None): Github token for private access 228 force (bool=False): Force update if exists previous sources 229 remove_prev (bool=False): Remove all previous resources 230 as_zip (bool=False): Retorna como ZIP 231 232 Returns: 233 sha_commit (str), updated (boolean) 234 """ 235 has_changes, sha_commit = has_changes_in_github(owner, repo, branch, download_to, token) 236 if not has_changes and not force: 237 return sha_commit, False 238 html_branch = f'https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip' 239 header = {} 240 if token: 241 header['Authorization'] = f'token {token}' 242 243 name_zip = f'{repo}-{branch}' 244 updated = get_resources_from_repo_github(html_branch, sha_commit, name_zip, download_to, 245 header=header, force_update=force, remove_prev=remove_prev, as_zip=as_zip) 246 247 if as_zip: 248 path_zip = os.path.join(download_to, f'{name_zip}.zip') 249 if os.path.exists(path_zip): 250 new_path_zip = os.path.join(download_to, f'{name_zip}-{sha_commit}.zip') 251 if os.path.exists(new_path_zip): 252 os.remove(new_path_zip) 253 os.rename(path_zip, new_path_zip) 254 255 return sha_commit, updated 256 257 258if __name__ == '__main__': 259 import fire, sys 260 261 sys.exit(fire.Fire( 262 { 263 get_api_github.__name__: get_api_github, 264 post_api_github.__name__: post_api_github, 265 download_release_repo_github.__name__: download_release_repo_github, 266 download_branch_repo_github.__name__: download_branch_repo_github 267 } 268 ))
PREFIX_FILE_LAST_TAG_REPO =
'last_tag_repo_github_'
def
get_api_github(owner, repo, api_request, token=None):
15def get_api_github(owner, repo, api_request, token=None): 16 """ 17 GET Request for repository GITHUB via API GITHUB. 18 19 See the REST API DOCS here https://docs.github.com/en/rest 20 21 Args: 22 owner (str): 23 repo (str): 24 api_request (str): 25 token (str=None): 26 27 Returns: 28 info_response (dict) 29 """ 30 request_headers = { 31 'Accept': 'application/vnd.github.v3+json' 32 } 33 if token: 34 request_headers['Authorization'] = f'token {token}' 35 36 url_github = f'https://api.github.com/repos/{owner}/{repo}/{api_request}' 37 req = Request(url_github, headers=request_headers, method='GET') 38 info_response = {} 39 try: 40 with urlopen(req) as resp_request: 41 if resp_request: 42 info_response = json.load(resp_request) 43 except HTTPError as exc: 44 info_response[HTTPError.__name__] = str(exc) 45 46 return info_response
GET Request for repository GITHUB via API GITHUB.
See the REST API DOCS here apb_extra_utils.github.com/en/rest">https://docsapb_extra_utils.github.com/en/rest
Arguments:
- owner (str):
- repo (str):
- api_request (str):
- token (str=None):
Returns:
info_response (dict)
def
post_api_github(owner, repo, api_request, post_data, token=None):
49def post_api_github(owner, repo, api_request, post_data, token=None): 50 """ 51 POST Request on repository GITHUB via API GITHUB 52 53 See the REST API DOCS here https://docs.github.com/en/rest 54 55 Args: 56 owner (str): 57 repo (str): 58 api_request (str): 59 post_data (dict): 60 token (str=None): 61 62 Returns: 63 info_response (dict) 64 """ 65 request_headers = { 66 'Accept': 'application/vnd.github.v3+json', 67 'Content-Type': 'application/json; charset=utf-8', 68 } 69 if token: 70 request_headers['Authorization'] = f'token {token}' 71 72 url_github = f'https://api.github.com/repos/{owner}/{repo}/{api_request}' 73 74 post_data_enc = json.dumps(post_data).encode('utf-8') 75 76 req = Request(url_github, headers=request_headers, method='POST') 77 info_response = {} 78 try: 79 with urlopen(req, post_data_enc) as resp_request: 80 if resp_request: 81 info_response = resp_request.__dict__ 82 except HTTPError as exc: 83 info_response[HTTPError.__name__] = str(exc) 84 85 return info_response
POST Request on repository GITHUB via API GITHUB
See the REST API DOCS here apb_extra_utils.github.com/en/rest">https://docsapb_extra_utils.github.com/en/rest
Arguments:
- owner (str):
- repo (str):
- api_request (str):
- post_data (dict):
- token (str=None):
Returns:
info_response (dict)
@functools.cache
def
has_changes_in_github(owner, repo, branch, download_to, token=None):
88@functools.cache 89def has_changes_in_github(owner, repo, branch, download_to, token=None): 90 """ 91 Check if the GitHub repository branch has changes. 92 93 Args: 94 owner (str): Owner repository Github 95 repo (str): Name repository Github 96 branch (str): Branch repository to check 97 download_to (str): Path to the local repository 98 token (str=None): Github token for private access 99 100 Returns: 101 bool: True if there are changes, False otherwise 102 str: sha_commit of the current branch state 103 """ 104 branch = branch.lower() 105 info_branches = get_api_github(owner, repo, 'branches', token) 106 info_branch = next(filter(lambda el: el.get('name', '').lower() == branch, 107 info_branches), None) 108 109 if not info_branch: 110 return False, None 111 112 sha_commit = info_branch.get('commit').get('sha') 113 expected_name_zip_repo = f'{repo}-{branch}' 114 log_last_tag = os.path.join(download_to, f'.{PREFIX_FILE_LAST_TAG_REPO}{expected_name_zip_repo}') 115 116 if os.path.exists(log_last_tag): 117 with open(log_last_tag) as fr: 118 last_tag = fr.read() 119 if last_tag and last_tag.strip() == sha_commit.strip(): 120 return False, sha_commit 121 122 return True, sha_commit
Check if the GitHub repository branch has changes.
Arguments:
- owner (str): Owner repository Github
- repo (str): Name repository Github
- branch (str): Branch repository to check
- download_to (str): Path to the local repository
- token (str=None): Github token for private access
Returns:
bool: True if there are changes, False otherwise str: sha_commit of the current branch state
def
get_resources_from_repo_github( html_repo, tag, expected_name_zip_repo, path_repo, header=None, force_update=False, remove_prev=False, as_zip=False):
125def get_resources_from_repo_github(html_repo, tag, expected_name_zip_repo, path_repo, header=None, 126 force_update=False, remove_prev=False, as_zip=False): 127 """ 128 129 Args: 130 html_repo (str): 131 tag (str): 132 expected_name_zip_repo (str): 133 path_repo (str): 134 header (dict=None): 135 force_update (bool=False): 136 remove_prev (bool=False): 137 as_zip (bool=False): 138 139 Returns: 140 updated (bool) 141 """ 142 updated = False 143 log_last_tag = os.path.join(path_repo, f'.{PREFIX_FILE_LAST_TAG_REPO}{expected_name_zip_repo}') 144 145 if not force_update: 146 last_tag = None 147 if os.path.exists(log_last_tag): 148 with open(log_last_tag) as fr: 149 last_tag = fr.read() 150 151 if last_tag and last_tag.strip() == tag.strip(): 152 return updated 153 154 if not header: 155 header = {} 156 header['Accept'] = 'application/octet-stream' 157 158 dir_temp = mkdtemp() 159 download_and_unzip(html_repo, extract_to=dir_temp, headers=[(k, v) for k, v in header.items()]) 160 path_res = os.path.join(dir_temp, expected_name_zip_repo) 161 162 if os.path.exists(path_res): 163 create_dir(path_repo) 164 165 if as_zip: 166 zip_dir(path_res, os.path.join(path_repo, f'{expected_name_zip_repo}.zip')) 167 else: 168 if remove_prev and os.path.exists(path_repo): 169 remove_content_dir(path_repo) 170 shutil.copytree(path_res, path_repo, dirs_exist_ok=True) 171 172 shutil.rmtree(path_res, ignore_errors=True) 173 174 with open(log_last_tag, "w+") as fw: 175 fw.write(tag) 176 177 updated = True 178 179 return updated
Arguments:
- html_repo (str):
- tag (str):
- expected_name_zip_repo (str):
- path_repo (str):
- header (dict=None):
- force_update (bool=False):
- remove_prev (bool=False):
- as_zip (bool=False):
Returns:
updated (bool)
def
download_release_repo_github( owner, repo, download_to, tag_release=None, token=None, force=False, as_zip=False, remove_prev=False):
182def download_release_repo_github(owner, repo, download_to, tag_release=None, token=None, force=False, as_zip=False, 183 remove_prev=False): 184 """ 185 Download release Github repository on the path selected. 186 187 Args: 188 owner (str): Owner repository Github 189 repo (str): Name repository Github 190 download_to (str): Path to download 191 tag_release (str=None): if not informed get 'latest' release 192 token (str=None): Github token for private access 193 force (bool=False): Force update if exists previous sources 194 remove_prev (bool=False): Remove all previous resources 195 as_zip (bool=False): Retorna como ZIP 196 197 Returns: 198 tag_name (str) 199 """ 200 if not tag_release: 201 info_release = get_api_github(owner, repo, 'releases/latest', token) 202 else: 203 info_release = get_api_github(owner, repo, f'releases/tags/{tag_release}', token) 204 205 tag_name = info_release.get('tag_name') 206 if tag_name: 207 html_release = f'https://github.com/{owner}/{repo}/archive/refs/tags/{tag_name}.zip' 208 header = {} 209 if token: 210 header['Authorization'] = f'token {token}' 211 212 get_resources_from_repo_github(html_release, tag_name, f'{repo}-{tag_name}', download_to, 213 header=header, force_update=force, remove_prev=remove_prev, as_zip=as_zip) 214 215 return tag_name
Download release Github repository on the path selected.
Arguments:
- owner (str): Owner repository Github
- repo (str): Name repository Github
- download_to (str): Path to download
- tag_release (str=None): if not informed get 'latest' release
- token (str=None): Github token for private access
- force (bool=False): Force update if exists previous sources
- remove_prev (bool=False): Remove all previous resources
- as_zip (bool=False): Retorna como ZIP
Returns:
tag_name (str)
def
download_branch_repo_github( owner, repo, branch, download_to, token=None, force=False, as_zip=False, remove_prev=False):
218def download_branch_repo_github(owner, repo, branch, download_to, token=None, force=False, as_zip=False, 219 remove_prev=False): 220 """ 221 Download the branch selected for the Github repo on the path selected 222 223 Args: 224 owner (str): Owner repository Github 225 repo (str): Name repository Github 226 branch (str): Branch repository to download 227 download_to (str): Path to download 228 token (str=None): Github token for private access 229 force (bool=False): Force update if exists previous sources 230 remove_prev (bool=False): Remove all previous resources 231 as_zip (bool=False): Retorna como ZIP 232 233 Returns: 234 sha_commit (str), updated (boolean) 235 """ 236 has_changes, sha_commit = has_changes_in_github(owner, repo, branch, download_to, token) 237 if not has_changes and not force: 238 return sha_commit, False 239 html_branch = f'https://github.com/{owner}/{repo}/archive/refs/heads/{branch}.zip' 240 header = {} 241 if token: 242 header['Authorization'] = f'token {token}' 243 244 name_zip = f'{repo}-{branch}' 245 updated = get_resources_from_repo_github(html_branch, sha_commit, name_zip, download_to, 246 header=header, force_update=force, remove_prev=remove_prev, as_zip=as_zip) 247 248 if as_zip: 249 path_zip = os.path.join(download_to, f'{name_zip}.zip') 250 if os.path.exists(path_zip): 251 new_path_zip = os.path.join(download_to, f'{name_zip}-{sha_commit}.zip') 252 if os.path.exists(new_path_zip): 253 os.remove(new_path_zip) 254 os.rename(path_zip, new_path_zip) 255 256 return sha_commit, updated
Download the branch selected for the Github repo on the path selected
Arguments:
- owner (str): Owner repository Github
- repo (str): Name repository Github
- branch (str): Branch repository to download
- download_to (str): Path to download
- token (str=None): Github token for private access
- force (bool=False): Force update if exists previous sources
- remove_prev (bool=False): Remove all previous resources
- as_zip (bool=False): Retorna como ZIP
Returns:
sha_commit (str), updated (boolean)