diff options
author | github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> | 2023-08-14 00:11:47 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-14 00:11:47 +0000 |
commit | 658c5f1dcff19a09aa38ee43c4822797c607afde (patch) | |
tree | 35bdd8ba978a56130e3b813b72b1e7c32604b32f /maintainers/scripts | |
parent | 163a5a5675d7e95d3856cf6ae2a26227f25a96f8 (diff) | |
parent | 890ef80b26adb50b4c9ffef99eb70e1631cf231a (diff) |
Merge master into haskell-updates
Diffstat (limited to 'maintainers/scripts')
-rw-r--r-- | maintainers/scripts/pluginupdate.py | 183 |
1 files changed, 106 insertions, 77 deletions
diff --git a/maintainers/scripts/pluginupdate.py b/maintainers/scripts/pluginupdate.py index 7c6cfd4fed7f0..6a607eb624803 100644 --- a/maintainers/scripts/pluginupdate.py +++ b/maintainers/scripts/pluginupdate.py @@ -4,20 +4,20 @@ # - maintainers/scripts/update-luarocks-packages # format: -# $ nix run nixpkgs.python3Packages.black -c black update.py +# $ nix run nixpkgs#black maintainers/scripts/pluginupdate.py # type-check: -# $ nix run nixpkgs.python3Packages.mypy -c mypy update.py +# $ nix run nixpkgs#python3.pkgs.mypy maintainers/scripts/pluginupdate.py # linted: -# $ nix run nixpkgs.python3Packages.flake8 -c flake8 --ignore E501,E265 update.py +# $ nix run nixpkgs#python3.pkgs.flake8 -- --ignore E501,E265 maintainers/scripts/pluginupdate.py import argparse import csv import functools import http import json +import logging import os import subprocess -import logging import sys import time import traceback @@ -25,14 +25,14 @@ import urllib.error import urllib.parse import urllib.request import xml.etree.ElementTree as ET +from dataclasses import asdict, dataclass from datetime import datetime from functools import wraps from multiprocessing.dummy import Pool from pathlib import Path -from typing import Dict, List, Optional, Tuple, Union, Any, Callable -from urllib.parse import urljoin, urlparse from tempfile import NamedTemporaryFile -from dataclasses import dataclass, asdict +from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from urllib.parse import urljoin, urlparse import git @@ -41,12 +41,13 @@ ATOM_LINK = "{http://www.w3.org/2005/Atom}link" # " ATOM_UPDATED = "{http://www.w3.org/2005/Atom}updated" # " LOG_LEVELS = { - logging.getLevelName(level): level for level in [ - logging.DEBUG, logging.INFO, logging.WARN, logging.ERROR ] + logging.getLevelName(level): level + for level in [logging.DEBUG, logging.INFO, logging.WARN, logging.ERROR] } log = logging.getLogger() + def retry(ExceptionToCheck: Any, tries: int = 4, delay: float = 3, backoff: float = 2): """Retry calling the decorated function using an exponential backoff. http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ @@ -77,6 +78,7 @@ def retry(ExceptionToCheck: Any, tries: int = 4, delay: float = 3, backoff: floa return deco_retry + @dataclass class FetchConfig: proc: int @@ -91,22 +93,21 @@ def make_request(url: str, token=None) -> urllib.request.Request: # a dictionary of plugins and their new repositories -Redirects = Dict['PluginDesc', 'Repo'] +Redirects = Dict["PluginDesc", "Repo"] + class Repo: - def __init__( - self, uri: str, branch: str - ) -> None: + def __init__(self, uri: str, branch: str) -> None: self.uri = uri - '''Url to the repo''' + """Url to the repo""" self._branch = branch # Redirect is the new Repo to use - self.redirect: Optional['Repo'] = None + self.redirect: Optional["Repo"] = None self.token = "dummy_token" @property def name(self): - return self.uri.split('/')[-1] + return self.uri.split("/")[-1] @property def branch(self): @@ -114,6 +115,7 @@ class Repo: def __str__(self) -> str: return f"{self.uri}" + def __repr__(self) -> str: return f"Repo({self.name}, {self.uri})" @@ -125,9 +127,9 @@ class Repo: def latest_commit(self) -> Tuple[str, datetime]: log.debug("Latest commit") loaded = self._prefetch(None) - updated = datetime.strptime(loaded['date'], "%Y-%m-%dT%H:%M:%S%z") + updated = datetime.strptime(loaded["date"], "%Y-%m-%dT%H:%M:%S%z") - return loaded['rev'], updated + return loaded["rev"], updated def _prefetch(self, ref: Optional[str]): cmd = ["nix-prefetch-git", "--quiet", "--fetch-submodules", self.uri] @@ -144,23 +146,23 @@ class Repo: return loaded["sha256"] def as_nix(self, plugin: "Plugin") -> str: - return f'''fetchgit {{ + return f"""fetchgit {{ url = "{self.uri}"; rev = "{plugin.commit}"; sha256 = "{plugin.sha256}"; - }}''' + }}""" class RepoGitHub(Repo): - def __init__( - self, owner: str, repo: str, branch: str - ) -> None: + def __init__(self, owner: str, repo: str, branch: str) -> None: self.owner = owner self.repo = repo self.token = None - '''Url to the repo''' + """Url to the repo""" super().__init__(self.url(""), branch) - log.debug("Instantiating github repo owner=%s and repo=%s", self.owner, self.repo) + log.debug( + "Instantiating github repo owner=%s and repo=%s", self.owner, self.repo + ) @property def name(self): @@ -213,7 +215,6 @@ class RepoGitHub(Repo): new_repo = RepoGitHub(owner=new_owner, repo=new_name, branch=self.branch) self.redirect = new_repo - def prefetch(self, commit: str) -> str: if self.has_submodules(): sha256 = super().prefetch(commit) @@ -233,12 +234,12 @@ class RepoGitHub(Repo): else: submodule_attr = "" - return f'''fetchFromGitHub {{ + return f"""fetchFromGitHub {{ owner = "{self.owner}"; repo = "{self.repo}"; rev = "{plugin.commit}"; sha256 = "{plugin.sha256}";{submodule_attr} - }}''' + }}""" @dataclass(frozen=True) @@ -258,15 +259,14 @@ class PluginDesc: return self.repo.name < other.repo.name @staticmethod - def load_from_csv(config: FetchConfig, row: Dict[str, str]) -> 'PluginDesc': + def load_from_csv(config: FetchConfig, row: Dict[str, str]) -> "PluginDesc": branch = row["branch"] - repo = make_repo(row['repo'], branch.strip()) + repo = make_repo(row["repo"], branch.strip()) repo.token = config.github_token return PluginDesc(repo, branch.strip(), row["alias"]) - @staticmethod - def load_from_string(config: FetchConfig, line: str) -> 'PluginDesc': + def load_from_string(config: FetchConfig, line: str) -> "PluginDesc": branch = "HEAD" alias = None uri = line @@ -279,6 +279,7 @@ class PluginDesc: repo.token = config.github_token return PluginDesc(repo, branch.strip(), alias) + @dataclass class Plugin: name: str @@ -302,22 +303,38 @@ class Plugin: return copy -def load_plugins_from_csv(config: FetchConfig, input_file: Path,) -> List[PluginDesc]: +def load_plugins_from_csv( + config: FetchConfig, + input_file: Path, +) -> List[PluginDesc]: log.debug("Load plugins from csv %s", input_file) plugins = [] - with open(input_file, newline='') as csvfile: + with open(input_file, newline="") as csvfile: log.debug("Writing into %s", input_file) - reader = csv.DictReader(csvfile,) + reader = csv.DictReader( + csvfile, + ) for line in reader: plugin = PluginDesc.load_from_csv(config, line) plugins.append(plugin) return plugins + def run_nix_expr(expr): - with CleanEnvironment(): - cmd = ["nix", "eval", "--extra-experimental-features", - "nix-command", "--impure", "--json", "--expr", expr] + with CleanEnvironment() as nix_path: + cmd = [ + "nix", + "eval", + "--extra-experimental-features", + "nix-command", + "--impure", + "--json", + "--expr", + expr, + "--nix-path", + nix_path, + ] log.debug("Running command %s", " ".join(cmd)) out = subprocess.check_output(cmd) data = json.loads(out) @@ -348,7 +365,7 @@ class Editor: self.nixpkgs_repo = None def add(self, args): - '''CSV spec''' + """CSV spec""" log.debug("called the 'add' command") fetch_config = FetchConfig(args.proc, args.github_token) editor = self @@ -356,23 +373,27 @@ class Editor: log.debug("using plugin_line", plugin_line) pdesc = PluginDesc.load_from_string(fetch_config, plugin_line) log.debug("loaded as pdesc", pdesc) - append = [ pdesc ] - editor.rewrite_input(fetch_config, args.input_file, editor.deprecated, append=append) - plugin, _ = prefetch_plugin(pdesc, ) + append = [pdesc] + editor.rewrite_input( + fetch_config, args.input_file, editor.deprecated, append=append + ) + plugin, _ = prefetch_plugin( + pdesc, + ) autocommit = not args.no_commit if autocommit: commit( editor.nixpkgs_repo, "{drv_name}: init at {version}".format( drv_name=editor.get_drv_name(plugin.normalized_name), - version=plugin.version + version=plugin.version, ), [args.outfile, args.input_file], ) # Expects arguments generated by 'update' subparser - def update(self, args ): - '''CSV spec''' + def update(self, args): + """CSV spec""" print("the update member function should be overriden in subclasses") def get_current_plugins(self) -> List[Plugin]: @@ -385,11 +406,11 @@ class Editor: return plugins def load_plugin_spec(self, config: FetchConfig, plugin_file) -> List[PluginDesc]: - '''CSV spec''' + """CSV spec""" return load_plugins_from_csv(config, plugin_file) def generate_nix(self, _plugins, _outfile: str): - '''Returns nothing for now, writes directly to outfile''' + """Returns nothing for now, writes directly to outfile""" raise NotImplementedError() def get_update(self, input_file: str, outfile: str, config: FetchConfig): @@ -413,7 +434,6 @@ class Editor: return update - @property def attr_path(self): return self.name + "Plugins" @@ -427,10 +447,11 @@ class Editor: def create_parser(self): common = argparse.ArgumentParser( add_help=False, - description=(f""" + description=( + f""" Updates nix derivations for {self.name} plugins.\n By default from {self.default_in} to {self.default_out}""" - ) + ), ) common.add_argument( "--input-names", @@ -463,26 +484,33 @@ class Editor: Uses GITHUB_API_TOKEN environment variables as the default value.""", ) common.add_argument( - "--no-commit", "-n", action="store_true", default=False, - help="Whether to autocommit changes" + "--no-commit", + "-n", + action="store_true", + default=False, + help="Whether to autocommit changes", ) common.add_argument( - "--debug", "-d", choices=LOG_LEVELS.keys(), + "--debug", + "-d", + choices=LOG_LEVELS.keys(), default=logging.getLevelName(logging.WARN), - help="Adjust log level" + help="Adjust log level", ) main = argparse.ArgumentParser( parents=[common], - description=(f""" + description=( + f""" Updates nix derivations for {self.name} plugins.\n By default from {self.default_in} to {self.default_out}""" - ) + ), ) subparsers = main.add_subparsers(dest="command", required=False) padd = subparsers.add_parser( - "add", parents=[], + "add", + parents=[], description="Add new plugin", add_help=False, ) @@ -502,10 +530,12 @@ class Editor: pupdate.set_defaults(func=self.update) return main - def run(self,): - ''' + def run( + self, + ): + """ Convenience function - ''' + """ parser = self.create_parser() args = parser.parse_args() command = args.command or "update" @@ -518,17 +548,15 @@ class Editor: getattr(self, command)(args) - - class CleanEnvironment(object): - def __enter__(self) -> None: + def __enter__(self) -> str: self.old_environ = os.environ.copy() local_pkgs = str(Path(__file__).parent.parent.parent) - os.environ["NIX_PATH"] = f"localpkgs={local_pkgs}" self.empty_config = NamedTemporaryFile() self.empty_config.write(b"{}") self.empty_config.flush() os.environ["NIXPKGS_CONFIG"] = self.empty_config.name + return f"localpkgs={local_pkgs}" def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: os.environ.update(self.old_environ) @@ -570,14 +598,15 @@ def print_download_error(plugin: PluginDesc, ex: Exception): ] print("\n".join(tb_lines)) + def check_results( results: List[Tuple[PluginDesc, Union[Exception, Plugin], Optional[Repo]]] ) -> Tuple[List[Tuple[PluginDesc, Plugin]], Redirects]: - ''' ''' + """ """ failures: List[Tuple[PluginDesc, Exception]] = [] plugins = [] redirects: Redirects = {} - for (pdesc, result, redirect) in results: + for pdesc, result, redirect in results: if isinstance(result, Exception): failures.append((pdesc, result)) else: @@ -594,17 +623,18 @@ def check_results( else: print(f", {len(failures)} plugin(s) could not be downloaded:\n") - for (plugin, exception) in failures: + for plugin, exception in failures: print_download_error(plugin, exception) sys.exit(1) + def make_repo(uri: str, branch) -> Repo: - '''Instantiate a Repo with the correct specialization depending on server (gitub spec)''' + """Instantiate a Repo with the correct specialization depending on server (gitub spec)""" # dumb check to see if it's of the form owner/repo (=> github) or https://... res = urlparse(uri) - if res.netloc in [ "github.com", ""]: - res = res.path.strip('/').split('/') + if res.netloc in ["github.com", ""]: + res = res.path.strip("/").split("/") repo = RepoGitHub(res[0], res[1], branch) else: repo = Repo(uri.strip(), branch) @@ -675,7 +705,6 @@ def prefetch( return (pluginDesc, e, None) - def rewrite_input( config: FetchConfig, input_file: Path, @@ -684,12 +713,14 @@ def rewrite_input( redirects: Redirects = {}, append: List[PluginDesc] = [], ): - plugins = load_plugins_from_csv(config, input_file,) + plugins = load_plugins_from_csv( + config, + input_file, + ) plugins.extend(append) if redirects: - cur_date_iso = datetime.now().strftime("%Y-%m-%d") with open(deprecated, "r") as f: deprecations = json.load(f) @@ -709,8 +740,8 @@ def rewrite_input( with open(input_file, "w") as f: log.debug("Writing into %s", input_file) # fields = dataclasses.fields(PluginDesc) - fieldnames = ['repo', 'branch', 'alias'] - writer = csv.DictWriter(f, fieldnames, dialect='unix', quoting=csv.QUOTE_NONE) + fieldnames = ["repo", "branch", "alias"] + writer = csv.DictWriter(f, fieldnames, dialect="unix", quoting=csv.QUOTE_NONE) writer.writeheader() for plugin in sorted(plugins): writer.writerow(asdict(plugin)) @@ -726,7 +757,6 @@ def commit(repo: git.Repo, message: str, files: List[Path]) -> None: print("no changes in working tree to commit") - def update_plugins(editor: Editor, args): """The main entry function of this module. All input arguments are grouped in the `Editor`.""" @@ -751,4 +781,3 @@ def update_plugins(editor: Editor, args): f"{editor.attr_path}: resolve github repository redirects", [args.outfile, args.input_file, editor.deprecated], ) - |