scripts/ci/util.py (118 lines of code) (raw):

# -------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import logging import os import re import shlex import json import zipfile from subprocess import check_output logger = logging.getLogger(__name__) # copy from wheel==0.30.0 WHEEL_INFO_RE = re.compile( r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>\d.+?))?) ((-(?P<build>\d.*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) \.whl|\.dist-info)$""", re.VERBOSE).match def get_repo_root(): current_dir = os.path.dirname(os.path.abspath(__file__)) while not os.path.exists(os.path.join(current_dir, 'CONTRIBUTING.rst')): current_dir = os.path.dirname(current_dir) return current_dir def _get_extension_modname(ext_dir): # Modification of https://github.com/Azure/azure-cli/blob/dev/src/azure-cli-core/azure/cli/core/extension.py#L153 EXTENSIONS_MOD_PREFIX = 'azext_' pos_mods = [n for n in os.listdir(ext_dir) if n.startswith(EXTENSIONS_MOD_PREFIX) and os.path.isdir(os.path.join(ext_dir, n))] if len(pos_mods) != 1: raise AssertionError("Expected 1 module to load starting with " "'{}': got {}".format(EXTENSIONS_MOD_PREFIX, pos_mods)) return pos_mods[0] def _get_azext_metadata(ext_dir): # Modification of https://github.com/Azure/azure-cli/blob/dev/src/azure-cli-core/azure/cli/core/extension.py#L109 AZEXT_METADATA_FILENAME = 'azext_metadata.json' azext_metadata = None ext_modname = _get_extension_modname(ext_dir=ext_dir) azext_metadata_filepath = os.path.join(ext_dir, ext_modname, AZEXT_METADATA_FILENAME) if os.path.isfile(azext_metadata_filepath): with open(azext_metadata_filepath) as f: azext_metadata = json.load(f) return azext_metadata def get_ext_metadata(ext_dir, ext_file, ext_name): # Modification of https://github.com/Azure/azure-cli/blob/dev/src/azure-cli-core/azure/cli/core/extension.py#L89 WHL_METADATA_FILENAME = 'metadata.json' zip_ref = zipfile.ZipFile(ext_file, 'r') zip_ref.extractall(ext_dir) zip_ref.close() metadata = {} dist_info_dirs = [f for f in os.listdir(ext_dir) if f.endswith('.dist-info')] azext_metadata = _get_azext_metadata(ext_dir) if not azext_metadata: raise ValueError('azext_metadata.json for Extension "{}" Metadata is missing'.format(ext_name)) metadata.update(azext_metadata) for dist_info_dirname in dist_info_dirs: parsed_dist_info_dir = WHEEL_INFO_RE(dist_info_dirname) if parsed_dist_info_dir and parsed_dist_info_dir.groupdict().get('name') == ext_name.replace('-', '_'): whl_metadata_filepath = os.path.join(ext_dir, dist_info_dirname, WHL_METADATA_FILENAME) if os.path.isfile(whl_metadata_filepath): with open(whl_metadata_filepath) as f: metadata.update(json.load(f)) return metadata def get_whl_from_url(url, filename, tmp_dir, whl_cache=None): if not whl_cache: whl_cache = {} if url in whl_cache: return whl_cache[url] import requests TRIES = 3 for try_number in range(TRIES): try: r = requests.get(url, stream=True) assert r.status_code == 200, "Request to {} failed with {}".format(url, r.status_code) break except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError) as err: import time time.sleep(0.5) continue ext_file = os.path.join(tmp_dir, filename) with open(ext_file, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # ignore keep-alive new chunks f.write(chunk) whl_cache[url] = ext_file return ext_file SRC_PATH = os.path.join(get_repo_root(), 'src') INDEX_PATH = os.path.join(SRC_PATH, 'index.json') def _catch_dup_keys(pairs): seen = {} for k, v in pairs: if k in seen: raise ValueError("duplicate key {}".format(k)) seen[k] = v return seen def get_index_data(): try: with open(INDEX_PATH) as f: return json.load(f, object_pairs_hook=_catch_dup_keys) except ValueError as err: raise AssertionError("Invalid JSON in {}: {}".format(INDEX_PATH, err)) def diff_code(start, end): diff_ref = [] for src_d in os.listdir(SRC_PATH): src_d_full = os.path.join(SRC_PATH, src_d) if not os.path.isdir(src_d_full): continue pkg_name = next((d for d in os.listdir(src_d_full) if d.startswith('azext_')), None) # If running in Travis CI, only run tests for edited extensions commit_range = os.environ.get('TRAVIS_COMMIT_RANGE') if commit_range and not check_output( ['git', '--no-pager', 'diff', '--name-only', commit_range, '--', src_d_full]): continue # Running in Azure DevOps cmd_tpl = 'git --no-pager diff --name-only origin/{start} {end} -- {code_dir}' # ado_branch_last_commit = os.environ.get('ADO_PULL_REQUEST_LATEST_COMMIT') # ado_target_branch = os.environ.get('ADO_PULL_REQUEST_TARGET_BRANCH') if start and end: if end == '$(System.PullRequest.SourceCommitId)': # default value if ADO_PULL_REQUEST_LATEST_COMMIT not set in ADO continue elif start == '$(System.PullRequest.TargetBranch)': # default value if ADO_PULL_REQUEST_TARGET_BRANCH not set in ADO continue else: cmd = cmd_tpl.format(start=start, end=end, code_dir=src_d_full) if not check_output(shlex.split(cmd)): continue diff_ref.append((pkg_name, src_d_full)) logger.warning(f'start: {start}, ' f'end: {end}, ' f'diff_ref: {diff_ref}.') return diff_ref