def _install_from_github(package_id: str) -> str: try: path, version = package_id.split("@") org, repo = path.split("/") except ValueError: raise ValueError( "Invalid package ID. Must be given as [ORG]/[REPO]@[VERSION]" "\ne.g. 'OpenZeppelin/[email protected]'") from None base_install_path = _get_data_folder().joinpath("packages") install_path = base_install_path.joinpath(f"{org}") install_path.mkdir(exist_ok=True) install_path = install_path.joinpath(f"{repo}@{version}") if install_path.exists(): raise FileExistsError("Package is aleady installed") headers = REQUEST_HEADERS.copy() headers.update(_maybe_retrieve_github_auth()) if re.match(r"^[0-9a-f]+$", version): download_url = f"https://api.github.com/repos/{org}/{repo}/zipball/{version}" else: download_url = _get_download_url_from_tag(org, repo, version, headers) existing = list(install_path.parent.iterdir()) _stream_download(download_url, str(install_path.parent), headers) installed = next(i for i in install_path.parent.iterdir() if i not in existing) shutil.move(installed, install_path) try: if not install_path.joinpath("brownie-config.yaml").exists(): brownie_config: Dict = {"project_structure": {}} contract_paths = set( i.relative_to(install_path).parts[0] for i in install_path.glob("**/*.sol")) contract_paths.update( i.relative_to(install_path).parts[0] for i in install_path.glob("**/*.vy")) if not contract_paths: raise InvalidPackage( f"{package_id} does not contain any .sol or .vy files") if install_path.joinpath("contracts").is_dir(): brownie_config["project_structure"]["contracts"] = "contracts" elif len(contract_paths) == 1: brownie_config["project_structure"][ "contracts"] = contract_paths.pop() else: raise InvalidPackage( f"{package_id} has no `contracts/` subdirectory, and " "multiple directories containing source files") with install_path.joinpath("brownie-config.yaml").open("w") as fp: yaml.dump(brownie_config, fp) project = load(install_path) project.close() except InvalidPackage: shutil.rmtree(install_path) raise except Exception as e: notify( "WARNING", f"Unable to compile {package_id} due to a {type(e).__name__} - you may still be able to" " import sources from the package, but will be unable to load the package directly.\n", ) return f"{org}/{repo}@{version}"
def _install_from_github(package_id: str) -> str: try: path, version = package_id.split("@") org, repo = path.split("/") except ValueError: raise ValueError( "Invalid package ID. Must be given as [ORG]/[REPO]@[VERSION]" "\ne.g. 'OpenZeppelin/[email protected]'") from None base_install_path = _get_data_folder().joinpath("packages") install_path = base_install_path.joinpath(f"{org}") install_path.mkdir(exist_ok=True) install_path = install_path.joinpath(f"{repo}@{version}") if install_path.exists(): raise FileExistsError("Package is aleady installed") headers = REQUEST_HEADERS.copy() if os.getenv("GITHUB_TOKEN"): auth = b64encode(os.environ["GITHUB_TOKEN"].encode()).decode() headers.update({"Authorization": "Basic {}".format(auth)}) response = requests.get( f"https://api.github.com/repos/{org}/{repo}/tags?per_page=100", headers=headers) if response.status_code != 200: msg = "Status {} when getting package versions from Github: '{}'".format( response.status_code, response.json()["message"]) if response.status_code == 403: msg += ( "\n\nIf this issue persists, generate a Github API token and store" " it as the environment variable `GITHUB_TOKEN`:\n" "https://github.blog/2013-05-16-personal-api-tokens/") raise ConnectionError(msg) data = response.json() if not data: raise ValueError("Github repository has no tags set") org, repo = data[0]["zipball_url"].split("/")[3:5] tags = [i["name"].lstrip("v") for i in data] if version not in tags: raise ValueError( "Invalid version for this package. Available versions are:\n" + ", ".join(tags)) from None download_url = next(i["zipball_url"] for i in data if i["name"].lstrip("v") == version) existing = list(install_path.parent.iterdir()) _stream_download(download_url, str(install_path.parent)) installed = next(i for i in install_path.parent.iterdir() if i not in existing) shutil.move(installed, install_path) try: if not install_path.joinpath("brownie-config.yaml").exists(): brownie_config: Dict = {"project_structure": {}} contract_paths = set( i.relative_to(install_path).parts[0] for i in install_path.glob("**/*.sol")) contract_paths.update( i.relative_to(install_path).parts[0] for i in install_path.glob("**/*.vy")) if not contract_paths: raise InvalidPackage( f"{package_id} does not contain any .sol or .vy files") if install_path.joinpath("contracts").is_dir(): brownie_config["project_structure"]["contracts"] = "contracts" elif len(contract_paths) == 1: brownie_config["project_structure"][ "contracts"] = contract_paths.pop() else: raise InvalidPackage( f"{package_id} has no `contracts/` subdirectory, and " "multiple directories containing source files") with install_path.joinpath("brownie-config.yaml").open("w") as fp: yaml.dump(brownie_config, fp) project = load(install_path) project.close() except InvalidPackage: shutil.rmtree(install_path) raise except Exception as e: notify( "WARNING", f"Unable to compile {package_id} due to a {type(e).__name__} - you may still be able to" " import sources from the package, but will be unable to load the package directly.\n", ) return f"{org}/{repo}@{version}"
def _install_from_github(package_id: str) -> str: try: path, version = package_id.split("@") org, repo = path.split("/") except ValueError: raise ValueError( "Invalid package ID. Must be given as [ORG]/[REPO]@[VERSION]" "\ne.g. 'OpenZeppelin/[email protected]'") from None install_path = _get_data_folder().joinpath(f"packages/{org}") install_path.mkdir(exist_ok=True) install_path = install_path.joinpath(f"{repo}@{version}") if install_path.exists(): raise FileExistsError("Package is aleady installed") headers: Dict = {} if os.getenv("GITHUB_TOKEN"): auth = b64encode(os.environ["GITHUB_TOKEN"].encode()).decode() headers = {"Authorization": "Basic {}".format(auth)} response = requests.get( f"https://api.github.com/repos/{org}/{repo}/tags?per_page=100", headers=headers) if response.status_code != 200: msg = "Status {} when getting package versions from Github: '{}'".format( response.status_code, response.json()["message"]) if response.status_code == 403: msg += ( "\n\nIf this issue persists, generate a Github API token and store" " it as the environment variable `GITHUB_TOKEN`:\n" "https://github.blog/2013-05-16-personal-api-tokens/") raise ConnectionError(msg) data = response.json() if not data: raise ValueError("Github repository has no tags set") org, repo = data[0]["zipball_url"].split("/")[3:5] tags = [i["name"].lstrip("v") for i in data] if version not in tags: raise ValueError( "Invalid version for this package. Available versions are:\n" + ", ".join(tags)) from None download_url = next(i["zipball_url"] for i in data if i["name"].lstrip("v") == version) existing = list(install_path.parent.iterdir()) _stream_download(download_url, str(install_path.parent)) installed = next(i for i in install_path.parent.iterdir() if i not in existing) shutil.move(installed, install_path) try: if not install_path.joinpath("contracts").exists(): raise Exception new(str(install_path), ignore_existing=True) project = load(install_path) project.close() except Exception: shutil.rmtree(install_path) raise InvalidPackage( f"{package_id} cannot be interpreted as a Brownie project") return f"{org}/{repo}@{version}"