From 054718640f5e03a22d5fab176ce00116760fd38a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 08:14:56 -0600 Subject: [PATCH 01/23] add module to track repository contributors --- landlab/cmd/authors.py | 247 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 landlab/cmd/authors.py diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py new file mode 100644 index 0000000000..133c7a5aa4 --- /dev/null +++ b/landlab/cmd/authors.py @@ -0,0 +1,247 @@ +import itertools +import os +import subprocess +from collections import ChainMap + +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib + + +def load_config(): + config = load_first_of(["authors.toml", "pyproject.toml"], "rb") + return config + + +def load_first_of(files, mode="rb"): + files = [files] if isinstance(files, str) else files + + config = {} + for name in files: + try: + with open(name, mode=mode) as fp: + config = tomllib.load(fp)["tool"]["rollcall"] + except FileNotFoundError: + pass + except KeyError: + pass + else: + break + try: + config.pop("author") + except KeyError: + pass + return config + + +class AuthorsConfig: + _files = ["authors.toml", "pyproject.toml"] + _defaults = { + "authors_file": "AUTHORS.rst", + "ignore": (), + "author_format": "{name}", + } + + def __init__(self, **kwds): + + self._config = ChainMap( + {k: v for k, v in kwds.items() if k in self._defaults}, + load_first_of(self._files, "rb"), + self._defaults, + ) + + def __getitem__(self, key): + return self._config[key] + + def __str__(self): + lines = ["[tool.rollcall]"] + for key, value in sorted(self._config.items(), key=lambda item: item[0]): + lines.append(f"{key} = {value!r}") + return os.linesep.join(lines) + + +class GitLog: + def __init__(self, format): + self._format = format + self._args = ["git", "log", f"--format={self._format}"] + + def __call__(self): + process = subprocess.run( + self._args, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + return process + + def __str__(self): + return " ".join(self._args) + + def __repr__(self): + return f"GitLog({self._format!r})" + + +class Author: + def __init__(self, name, email, aliases=None, alternate_emails=None): + self._name = name + self._email = email + self._aliases = set() if not aliases else set(aliases) + self._alternate_emails = ( + set() if not alternate_emails else set(alternate_emails) + ) + self._aliases.discard(self.name) + self._alternate_emails.discard(self.email) + + @classmethod + def from_dict(cls, attrs): + return cls( + attrs["name"], + attrs["email"], + aliases=attrs.get("aliases", None), + alternate_emails=attrs.get("alternate_emails", None), + ) + + @property + def name(self): + return self._name + + @property + def names(self): + return (self.name,) + self.aliases + + @property + def email(self): + return self._email + + @property + def emails(self): + return (self.email,) + self.alternate_emails + + @property + def aliases(self): + return tuple(self._aliases) + + @property + def alternate_emails(self): + return tuple(self._alternate_emails) + + def add_alias(self, alias): + if alias != self.name: + self._aliases.add(alias) + + def to_toml(self): + lines = [ + "[[tool.rollcall.author]]", + f"name = {self.name!r}", + f"email = {self.email!r}", + ] + lines += ( + ["aliases = ["] + + [f" {alias!r}," for alias in sorted(self.aliases)] + + ["]"] + ) + lines += ( + ["alternate_emails = ["] + + [f" {email!r}," for email in sorted(self.alternate_emails)] + + ["]"] + ) + + return os.linesep.join(lines) + + def update(self, other): + if other.name != self.name: + self._aliases.add(other.name) + if other.email != self.email: + self._alternate_emails.add(other.email) + self._aliases |= set(other.aliases) + self._alternate_emails |= set(other.alternate_emails) + + def __repr__(self): + aliases = None if not self.aliases else self.aliases + alternate_emails = None if not self.alternate_emails else self.alternate_emails + return f"Author({self.name!r}, {self.email!r}, aliases={aliases!r}, alternate_emails={alternate_emails!r})" + + +class AuthorList: + def __init__(self, authors=None): + authors = [] if authors is None else authors + self._name = {} + self._email = {} + + for author in authors: + for name in author.names: + self._name[name] = author + for email in author.emails: + self._email[email] = author + + def __iter__(self): + names = set([author.name for author in self._name.values()]) + for name in sorted(names): + yield self._name[name] + + def __len__(self): + names = set([author.name for author in self._name.values()]) + return len(names) + + def find_author(self, name_or_email): + if name_or_email in self._name: + author = self._name[name_or_email] + elif name_or_email in self._email: + author = self._email[name_or_email] + else: + raise KeyError(f"unknown author: {name_or_email}") + return author + + @classmethod + def from_toml(cls, toml_file): + with open(toml_file, "rb") as fp: + authors = [ + Author.from_dict(author) + for author in tomllib.load(fp)["tool"]["rollcall"]["author"] + ] + + return cls(authors=authors) + + @classmethod + def from_csv(cls, csv): + authors = cls() + for line in csv.splitlines(): + name, email = (item.strip() for item in line.rsplit(",", maxsplit=1)) + authors.add(name, email) + return authors + + def update(self, other): + for author in other: + for name, email in itertools.product(author.names, author.emails): + self.add(name, email) + + def add(self, name, email): + have_name = name in self._name + have_email = email in self._email + + new_author = Author(name, email) + + if not (have_name or have_email): + author = new_author + self._name[name] = new_author + self._email[email] = new_author + elif have_name and not have_email: + author = self._name[name] + author.update(new_author) + self._email[email] = author + elif have_email and not have_name: + author = self._email[email] + author.update(new_author) + self._name[name] = author + elif self._name[name].name != self._email[email].name: + other = self._email[email] + author = self._name[name] + author.update(other) + for name in other.names: + self._name[name] = author + for email in other.emails: + self._email[email] = author + else: + author = self._name[name] From 8c2c56c771d14b0e6d9b9c1264d9e525bb9ebcdc Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 08:17:26 -0600 Subject: [PATCH 02/23] add authors subcommand to landlab cli --- landlab/cmd/landlab.py | 189 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 8af18bdf7b..7ad0a72fb1 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -1,5 +1,7 @@ import inspect +import itertools import os +import pathlib import sys import textwrap from collections import defaultdict @@ -8,6 +10,8 @@ import numpy as np import rich_click as click +from .authors import AuthorsConfig, AuthorList, GitLog + from landlab import ( ModelGrid, RasterModelGrid, @@ -139,6 +143,191 @@ def validate(component): out("💥 All good! 💥") +@landlab.group() +@click.option("--ignore", help="users to ignore", multiple=True, type=str) +@click.option( + "--authors-file", + type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), + help="existing authors file", +) +@click.pass_context +def authors(ctx, ignore, authors_file): + verbose = ctx.parent.params["verbose"] + silent = ctx.parent.params["silent"] + + config = AuthorsConfig(**{k: v for k, v in ctx.params.items() if v}) + + ctx.params["authors_file"] = config["authors_file"] + ctx.params["ignore"] = config["ignore"] + ctx.params["author_format"] = config["author_format"] + + if verbose and not silent: + out(f"{str(config)}") + + +@authors.command() +@click.option("--update-existing/--no-update-existing", default=True) +@click.option( + "--file", + default="authors.toml", + type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), + help="existing authors file", +) +@click.pass_context +def create(ctx, update_existing, file): + verbose = ctx.parent.parent.params["verbose"] + silent = ctx.parent.parent.params["silent"] + + git_log = GitLog("%an, %ae") + if verbose and not silent: + out(f"{str(git_log)}") + + process = git_log() + + if update_existing and not pathlib.Path(file).is_file(): + err(f"not a file: {file}") + raise click.Abort() + + if update_existing and pathlib.Path(file).is_file(): + if verbose and not silent: + out(f"updating existing authors file: {file}") + authors = AuthorList.from_toml(file) + else: + if verbose and not silent: + out("ignoring any existing authors files") + authors = AuthorList() + + if process.returncode == 0: + authors.update(AuthorList.from_csv(process.stdout)) + for author in sorted(authors, key=lambda item: item.name): + print(author.to_toml()) + print("") + else: + err("unable to retrieve commit authors") + err(process.stderr) + + +@authors.command() +@click.pass_context +def build(ctx): + ignore = set(ctx.parent.params["ignore"]) + authors_file = ctx.parent.params["authors_file"] + author_format = ctx.parent.params["author_format"] + + git_log = GitLog("%aN") + + out(f"{str(git_log)}") + process = git_log() + + with open(authors_file, "r") as fp: + out(f"building authors file: {authors_file}") + for line in fp.readlines(): + if line.startswith(".. rollcall start-author-list"): + print(line) + break + print(line, end="") + authors = AuthorList.from_toml("authors.toml") + + commits = defaultdict(int) + for author in process.stdout.splitlines(): + canonical_name = authors.find_author(author.strip()).name + commits[canonical_name] += 1 + + for author in sorted(authors, key=lambda a: commits[a], reverse=True): + github = _guess_github_user(author) + if github is None: + github = "landlab" + author.github = github + if ignore.isdisjoint(author.names): + print( + author_format.format( + name=author.name, github=author.github, email=author.email + ) + ) + # print(f"* `{author.name} `_") + + +def _guess_github_user(author): + github = None + + try: + github = getattr(author, "github") + except AttributeError: + pass + + if github is None: + for email in author.emails: + if email.endswith("github.com"): + github, _ = email.split("@") + if "+" in github: + _, github = github.split("+") + break + + if github is None: + for name in author.names: + if name.isalnum(): + github = name + break + + return github + + +@authors.command() +@click.option( + "--file", + default="authors.toml", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + help="existing authors file", +) +@click.pass_context +def mailmap(ctx, file): + verbose = ctx.parent.parent.params["verbose"] + silent = ctx.parent.parent.params["silent"] + + if verbose and not silent: + out(f"reading author list: {file}") + authors = AuthorList.from_toml(file) + for author in authors: + good_name, good_email = author.name, author.email + for bad_name, bad_email in itertools.product( + sorted(author.names), sorted(author.emails) + ): + print(f"{good_name} <{good_email}> {bad_name} <{bad_email}>") + + +@authors.command() +@click.option( + "--file", + default="authors.toml", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + help="existing authors file", +) +@click.pass_context +def list(ctx, file): + ignore = set(ctx.parent.params["ignore"]) + verbose = ctx.parent.parent.params["verbose"] + silent = ctx.parent.parent.params["silent"] + + git_log = GitLog("%aN") + if verbose and not silent: + out(f"{str(git_log)}") + process = git_log() + + if verbose and not silent: + out(f"reading authors from {file}") + authors = AuthorList.from_toml(file) + + commits = defaultdict(int) + for author in process.stdout.splitlines(): + canonical_name = authors.find_author(author.strip()).name + commits[canonical_name] += 1 + + for name, n_commits in sorted(commits.items(), key=lambda x: x[1], reverse=True): + author = authors.find_author(name) + if ignore.isdisjoint(author.names): + print(f"{author.name} <{author.email}> ({n_commits})") + + @landlab.group(chain=True) @click.pass_context def index(ctx): From e8a73048cf0594e0de6684f0af548d96424d0209 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 10:25:24 -0600 Subject: [PATCH 03/23] change AuthorsConfig to inherit from UserDict --- landlab/cmd/authors.py | 86 ++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index 133c7a5aa4..cbc8548ebd 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -1,7 +1,7 @@ import itertools import os import subprocess -from collections import ChainMap +from collections import ChainMap, UserDict try: import tomllib @@ -9,57 +9,61 @@ import tomli as tomllib -def load_config(): - config = load_first_of(["authors.toml", "pyproject.toml"], "rb") - return config - - -def load_first_of(files, mode="rb"): - files = [files] if isinstance(files, str) else files - - config = {} - for name in files: - try: - with open(name, mode=mode) as fp: - config = tomllib.load(fp)["tool"]["rollcall"] - except FileNotFoundError: - pass - except KeyError: - pass - else: - break - try: - config.pop("author") - except KeyError: - pass - return config - - -class AuthorsConfig: - _files = ["authors.toml", "pyproject.toml"] - _defaults = { +class AuthorsConfig(UserDict): + _FILES = [".rollcall.toml", "rollcall.toml", "pyproject.toml"] + _DEFAULTS = { "authors_file": "AUTHORS.rst", "ignore": (), "author_format": "{name}", + "roll_file": ".roll.toml", } - def __init__(self, **kwds): + def __init__(self, *args, **kwds): - self._config = ChainMap( - {k: v for k, v in kwds.items() if k in self._defaults}, - load_first_of(self._files, "rb"), - self._defaults, - ) + user_data = { + k: v for k, v in dict(*args, **kwds).items() if k in self._DEFAULTS + } - def __getitem__(self, key): - return self._config[key] + self.data = ChainMap( + user_data, AuthorsConfig._load_first_of(self._FILES), self._DEFAULTS + ) def __str__(self): lines = ["[tool.rollcall]"] - for key, value in sorted(self._config.items(), key=lambda item: item[0]): + for key, value in sorted(self.data.items(), key=lambda item: item[0]): lines.append(f"{key} = {value!r}") return os.linesep.join(lines) + @classmethod + def from_toml(cls, toml_file): + with open(toml_file, mode="rb") as fp: + config = tomllib.load(fp)["tool"]["rollcall"] + return cls(config) + + @staticmethod + def _load_toml(name): + with open(name, mode="rb") as fp: + config = tomllib.load(fp)["tool"]["rollcall"] + return config + + @staticmethod + def _load_first_of(files): + for name in files: + try: + config = AuthorsConfig._load_toml(name) + except (FileNotFoundError, KeyError): + pass + else: + break + else: + config = {} + + try: + config.pop("author") + except KeyError: + pass + return config + class GitLog: def __init__(self, format): @@ -74,7 +78,9 @@ def __call__(self): stderr=subprocess.PIPE, stdin=subprocess.PIPE, ) - return process + if process.returncode != 0: + raise RuntimeError(process.stderr) + return process.stdout def __str__(self): return " ".join(self._args) From 4fe5d8e5e1649df6ecec2c18df2ca2da358913f6 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 10:25:58 -0600 Subject: [PATCH 04/23] change git_log to return stdout, not process --- landlab/cmd/landlab.py | 142 +++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 56 deletions(-) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 7ad0a72fb1..ea8f7fbf7b 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -150,89 +150,106 @@ def validate(component): type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), help="existing authors file", ) +@click.option( + "--roll-file", + default=".roll.toml", + type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), + help="The file that contains a list of authors", +) @click.pass_context -def authors(ctx, ignore, authors_file): +def authors(ctx, ignore, authors_file, roll_file): verbose = ctx.parent.params["verbose"] silent = ctx.parent.params["silent"] config = AuthorsConfig(**{k: v for k, v in ctx.params.items() if v}) - ctx.params["authors_file"] = config["authors_file"] - ctx.params["ignore"] = config["ignore"] - ctx.params["author_format"] = config["author_format"] + for k, v in config.items(): + ctx.params[k] = v if verbose and not silent: - out(f"{str(config)}") + config_str = textwrap.indent(str(config), prefix=" ") + out("using the following configuration:") + out(f"{config_str}") @authors.command() @click.option("--update-existing/--no-update-existing", default=True) -@click.option( - "--file", - default="authors.toml", - type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), - help="existing authors file", -) @click.pass_context -def create(ctx, update_existing, file): +def create(ctx, update_existing): verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] + roll_file = pathlib.Path(ctx.parent.params["roll_file"]) git_log = GitLog("%an, %ae") if verbose and not silent: out(f"{str(git_log)}") - process = git_log() - - if update_existing and not pathlib.Path(file).is_file(): - err(f"not a file: {file}") + try: + names_and_emails = git_log() + except RuntimeError as error: + err("error running `git log`") + err(error) raise click.Abort() - if update_existing and pathlib.Path(file).is_file(): - if verbose and not silent: - out(f"updating existing authors file: {file}") - authors = AuthorList.from_toml(file) - else: - if verbose and not silent: - out("ignoring any existing authors files") - authors = AuthorList() + if not silent and update_existing: + if not roll_file.is_file(): + err(f"nothing to update ({roll_file})") + else: + out(f"updating existing author roll ({roll_file})") - if process.returncode == 0: - authors.update(AuthorList.from_csv(process.stdout)) - for author in sorted(authors, key=lambda item: item.name): - print(author.to_toml()) - print("") - else: - err("unable to retrieve commit authors") - err(process.stderr) + authors = ( + AuthorList.from_toml(roll_file) + if update_existing and roll_file.is_file() + else AuthorList() + ) + + authors.update(AuthorList.from_csv(names_and_emails)) + for author in sorted(authors, key=lambda item: item.name): + print(author.to_toml()) + print("") @authors.command() @click.pass_context def build(ctx): + verbose = ctx.parent.parent.params["verbose"] + silent = ctx.parent.parent.params["silent"] ignore = set(ctx.parent.params["ignore"]) - authors_file = ctx.parent.params["authors_file"] + authors_file = pathlib.Path(ctx.parent.params["authors_file"]) author_format = ctx.parent.params["author_format"] + roll_file = pathlib.Path(ctx.parent.params["roll_file"]) git_log = GitLog("%aN") - out(f"{str(git_log)}") - process = git_log() + if verbose and not silent: + out(f"{str(git_log)}") + try: + commit_authors = git_log() + except RuntimeError as error: + err("unable to get commit authors") + err(error) + raise click.Abort() - with open(authors_file, "r") as fp: - out(f"building authors file: {authors_file}") - for line in fp.readlines(): - if line.startswith(".. rollcall start-author-list"): - print(line) - break - print(line, end="") - authors = AuthorList.from_toml("authors.toml") + # out(f"building authors file: {authors_file}") + intro = ( + _read_until(authors_file, until=".. rollcall start-author-list") + if authors_file.is_file() + else "" + ) + if len(intro) == 0: + err(f"empty or missing authors file ({authors_file})") + + authors = AuthorList.from_toml(roll_file) if roll_file.is_file() else AuthorList() + + if len(authors) == 0: + err(f"missing or empty roll ({roll_file})") commits = defaultdict(int) - for author in process.stdout.splitlines(): + for author in commit_authors.splitlines(): canonical_name = authors.find_author(author.strip()).name commits[canonical_name] += 1 + print(intro) for author in sorted(authors, key=lambda a: commits[a], reverse=True): github = _guess_github_user(author) if github is None: @@ -244,7 +261,20 @@ def build(ctx): name=author.name, github=author.github, email=author.email ) ) - # print(f"* `{author.name} `_") + + +def _read_until(path_to_file, until=None): + with open(path_to_file, "r") as fp: + if until is None: + return fp.read() + + lines = [] + for line in fp.readlines(): + if line.startswith(until): + lines.append(line) + break + lines.append(line) + return "".join(lines) def _guess_github_user(author): @@ -273,20 +303,15 @@ def _guess_github_user(author): @authors.command() -@click.option( - "--file", - default="authors.toml", - type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), - help="existing authors file", -) @click.pass_context -def mailmap(ctx, file): +def mailmap(ctx): verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] + roll_file = ctx.parent.params["roll_file"] if verbose and not silent: - out(f"reading author list: {file}") - authors = AuthorList.from_toml(file) + out(f"reading author list: {roll_file}") + authors = AuthorList.from_toml(roll_file) for author in authors: good_name, good_email = author.name, author.email for bad_name, bad_email in itertools.product( @@ -311,14 +336,19 @@ def list(ctx, file): git_log = GitLog("%aN") if verbose and not silent: out(f"{str(git_log)}") - process = git_log() + try: + commit_authors = git_log() + except RuntimeError as error: + err("unable to get commit authors") + err(error) + raise click.Abort() if verbose and not silent: out(f"reading authors from {file}") authors = AuthorList.from_toml(file) commits = defaultdict(int) - for author in process.stdout.splitlines(): + for author in commit_authors.splitlines(): canonical_name = authors.find_author(author.strip()).name commits[canonical_name] += 1 From 543aa506dee353658dc0e6606aa81170d3c10a10 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 11:42:30 -0600 Subject: [PATCH 05/23] add AuthorsSubprocessError --- landlab/cmd/authors.py | 13 +++++++++++-- landlab/cmd/landlab.py | 29 +++++++++++++---------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index cbc8548ebd..b9d5b7e833 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -1,6 +1,7 @@ import itertools import os import subprocess +import textwrap from collections import ChainMap, UserDict try: @@ -9,6 +10,10 @@ import tomli as tomllib +class AuthorsSubprocessError(Exception): + pass + + class AuthorsConfig(UserDict): _FILES = [".rollcall.toml", "rollcall.toml", "pyproject.toml"] _DEFAULTS = { @@ -67,7 +72,7 @@ def _load_first_of(files): class GitLog: def __init__(self, format): - self._format = format + self._format = f'"{format}"' self._args = ["git", "log", f"--format={self._format}"] def __call__(self): @@ -79,7 +84,11 @@ def __call__(self): stdin=subprocess.PIPE, ) if process.returncode != 0: - raise RuntimeError(process.stderr) + raise AuthorsSubprocessError( + f"`{self} did not run successfully` (exit code was {process.returncode})\n" + + textwrap.indent(process.stderr, prefix=" ") + + "This error originates from a subprocess." + ) return process.stdout def __str__(self): diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index ea8f7fbf7b..55a59a413b 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -10,7 +10,7 @@ import numpy as np import rich_click as click -from .authors import AuthorsConfig, AuthorList, GitLog +from .authors import AuthorsConfig, AuthorsSubprocessError, AuthorList, GitLog from landlab import ( ModelGrid, @@ -181,15 +181,14 @@ def create(ctx, update_existing): roll_file = pathlib.Path(ctx.parent.params["roll_file"]) git_log = GitLog("%an, %ae") - if verbose and not silent: - out(f"{str(git_log)}") - try: names_and_emails = git_log() - except RuntimeError as error: - err("error running `git log`") + except AuthorsSubprocessError as error: err(error) raise click.Abort() + else: + if verbose and not silent: + out(f"{git_log}") if not silent and update_existing: if not roll_file.is_file(): @@ -220,17 +219,15 @@ def build(ctx): roll_file = pathlib.Path(ctx.parent.params["roll_file"]) git_log = GitLog("%aN") - - if verbose and not silent: - out(f"{str(git_log)}") try: commit_authors = git_log() - except RuntimeError as error: - err("unable to get commit authors") + except AuthorsSubprocessError as error: err(error) raise click.Abort() + else: + if verbose and not silent: + out(f"{git_log}") - # out(f"building authors file: {authors_file}") intro = ( _read_until(authors_file, until=".. rollcall start-author-list") if authors_file.is_file() @@ -334,14 +331,14 @@ def list(ctx, file): silent = ctx.parent.parent.params["silent"] git_log = GitLog("%aN") - if verbose and not silent: - out(f"{str(git_log)}") try: commit_authors = git_log() - except RuntimeError as error: - err("unable to get commit authors") + except AuthorsSubprocessError as error: err(error) raise click.Abort() + else: + if verbose and not silent: + out(f"{git_log}") if verbose and not silent: out(f"reading authors from {file}") From b0182ee54439cc4fb1894b393c0b9e81a157474f Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 11:43:11 -0600 Subject: [PATCH 06/23] add config for rollcall tool to pyproject.toml --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 93cbc827a3..705872276c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -194,3 +194,7 @@ directory = "misc" name = "Other Changes and Additions" showcontent = true +[tool.rollcall] +ignore = ['(no author)', 'root'] +authors_file = 'AUTHORS.rst' +author_format = "* `{name} `_" From caff7ad44af1d42141c44b261fd379053727e458 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 13:36:53 -0600 Subject: [PATCH 07/23] add optional additional properties to Author --- landlab/cmd/authors.py | 22 +++++++++++++++------- landlab/cmd/landlab.py | 5 +++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index b9d5b7e833..fb77882cca 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -72,7 +72,7 @@ def _load_first_of(files): class GitLog: def __init__(self, format): - self._format = f'"{format}"' + self._format = f"{format}" self._args = ["git", "log", f"--format={self._format}"] def __call__(self): @@ -108,15 +108,20 @@ def __init__(self, name, email, aliases=None, alternate_emails=None): ) self._aliases.discard(self.name) self._alternate_emails.discard(self.email) + self._extras = {} @classmethod def from_dict(cls, attrs): - return cls( - attrs["name"], - attrs["email"], - aliases=attrs.get("aliases", None), - alternate_emails=attrs.get("alternate_emails", None), + attrs = dict(attrs) + author = cls( + attrs.pop("name"), + attrs.pop("email"), + aliases=attrs.pop("aliases", None), + alternate_emails=attrs.pop("alternate_emails", None), ) + for k, v in attrs.items(): + author._extras[k] = v + return author @property def name(self): @@ -162,6 +167,8 @@ def to_toml(self): + [f" {email!r}," for email in sorted(self.alternate_emails)] + ["]"] ) + for k, v in sorted(self._extras.items(), key=lambda x: x[0]): + lines += [f"{k} = {v!r}"] return os.linesep.join(lines) @@ -172,6 +179,7 @@ def update(self, other): self._alternate_emails.add(other.email) self._aliases |= set(other.aliases) self._alternate_emails |= set(other.alternate_emails) + self._extras.update(other._extras) def __repr__(self): aliases = None if not self.aliases else self.aliases @@ -206,7 +214,7 @@ def find_author(self, name_or_email): elif name_or_email in self._email: author = self._email[name_or_email] else: - raise KeyError(f"unknown author: {name_or_email}") + raise KeyError(f"unknown author: {name_or_email!r}") return author @classmethod diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 55a59a413b..b5ef53ecae 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -282,6 +282,11 @@ def _guess_github_user(author): except AttributeError: pass + try: + github = author._extras["github"] + except KeyError: + pass + if github is None: for email in author.emails: if email.endswith("github.com"): From f427f10b06775f57cfdcbde17c92fd086e00dee5 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 15:59:45 -0600 Subject: [PATCH 08/23] add an initial author roll file --- .roll.toml | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 .roll.toml diff --git a/.roll.toml b/.roll.toml new file mode 100644 index 0000000000..36e74091e1 --- /dev/null +++ b/.roll.toml @@ -0,0 +1,334 @@ +[[tool.rollcall.author]] +name = '(no author)' +email = '(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a' +aliases = [ +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Allison Pfeiffer' +email = 'pfeif@uw.edu' +aliases = [ + 'pfeiffea', +] +alternate_emails = [ + '35848814+pfeiffea@users.noreply.github.com', +] + +[[tool.rollcall.author]] +name = 'Amanda Manaster' +email = 'amanaste@uw.edu' +aliases = [ +] +alternate_emails = [ +] +github = 'amanaster2' + +[[tool.rollcall.author]] +name = 'Benjamin Campforts' +email = 'benjamincampforts@gmail.com' +aliases = [ + 'Benjamin', +] +alternate_emails = [ +] +github = 'BCampforts' + +[[tool.rollcall.author]] +name = 'Charlie Shobe' +email = 'cmshobe@email.wm.edu' +aliases = [ + 'cmshobe', +] +alternate_emails = [ + 'cmshobe@users.noreply.github.com', +] + +[[tool.rollcall.author]] +name = 'Christopher Sheehan' +email = '56881748+Sheehace@users.noreply.github.com' +aliases = [ +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Dan Hobley' +email = 'dan.hobley@gmail.com' +aliases = [ + 'DanielHobley', + 'SiccarPoint', + 'danhobley', +] +alternate_emails = [ + 'danhobley@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'daniel.hobley@adas.co.uk', + 'daniel@dhmac.geol.cf.ac.uk', + 'daniel@v3061-0a9084c0.wifi.cf.ac.uk', +] +github = 'SiccarPoint' + +[[tool.rollcall.author]] +name = 'David Litwin' +email = '45630470+DavidLitwin@users.noreply.github.com' +aliases = [ + 'DavidLitwin', +] +alternate_emails = [ + 'davidglitwin@posteo.net', +] + +[[tool.rollcall.author]] +name = 'Dylan Ward' +email = 'dylan.ward@uc.edu' +aliases = [ + 'Dylan', +] +alternate_emails = [ +] +github = 'ddoubleprime' + +[[tool.rollcall.author]] +name = 'Eric Hutton' +email = 'mcflugen@gmail.com' +aliases = [ + 'huttone', + 'mcflugen', +] +alternate_emails = [ + 'huttone@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'huttone@colorado.edu', + 'mcflugen@users.noreply.github.com', +] + +[[tool.rollcall.author]] +name = 'Francis Rengers' +email = 'francis.rengers@colorado.edu' +aliases = [ +] +alternate_emails = [ +] +github = 'frengers' + +[[tool.rollcall.author]] +name = 'Giuseppecipolla95' +email = '36881480+Giuseppecipolla95@users.noreply.github.com' +aliases = [ +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Greg Tucker' +email = 'gtucker@colorado.edu' +aliases = [ + 'gregtucker', + 'gtucker', +] +alternate_emails = [ + 'gregteucro@yahoo.com', + 'gtucker@093548ea-dc74-4ebb-8037-4be9f23db00a', +] + +[[tool.rollcall.author]] +name = 'Jenny Knuth' +email = 'inkboco@gmail.com' +aliases = [ +] +alternate_emails = [ +] +github = 'jennyknuth' + +[[tool.rollcall.author]] +name = 'Jordan Adams' +email = 'Jordan@e0-f8-47-11-a-8c.cg-guest.ucar.edu' +aliases = [ + 'Jordan', + 'jadams15', + 'jordan', +] +alternate_emails = [ + 'Jordan@Jordan-Adamss-MacBook-Pro.local', + 'jadams15@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'jadams15@tulane.edu', + 'jadams15@users.noreply.github.com', +] +github = 'jadams15' + +[[tool.rollcall.author]] +name = 'Katy Barnhart' +email = 'krbarnhart@usgs.gov' +aliases = [ + 'Barnhart, Katherine (Katy) Ruth', + 'Katherine Barnhart', + 'Katy', + 'barnhark', + 'kbarnhart', +] +alternate_emails = [ + 'barnhark@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'katy.barnhart@gmail.com', + 'katybarnhart@Katys-MacBook-Pro.local', + 'katybarnhart@engr2-2-94-28-dhcp.int.colorado.edu', +] +github = 'kbarnhart' + +[[tool.rollcall.author]] +name = 'Kristen Thyng' +email = 'kthyng@gmail.com' +aliases = [ +] +alternate_emails = [ +] +github = 'kthyng' + +[[tool.rollcall.author]] +name = 'Mark Piper' +email = 'mark.piper@colorado.edu' +aliases = [ +] +alternate_emails = [ +] +github = 'mdpiper' + +[[tool.rollcall.author]] +name = 'Nathan Lyons' +email = 'nathan.j.lyons@gmail.com' +aliases = [ + 'NathanLyons', + 'nathanlyons', +] +alternate_emails = [ + 'njlyons@Icacos.local', +] +github = 'nathanlyons' + +[[tool.rollcall.author]] +name = 'Nicole M Gasparini' +email = 'nicgaspar@gmail.com' +aliases = [ + 'nicgaspar', +] +alternate_emails = [ + 'nicgaspar@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'nicolegasparini@Nicoles-Mac-Pro.local', + 'nicolegasparini@ngaspari.tulane.edu', +] + +[[tool.rollcall.author]] +name = 'Rachel Glade' +email = 'rcglade@gmail.com' +aliases = [ + 'Glader011235', +] +alternate_emails = [ + 'rachel.glade@colorado.edu', +] + +[[tool.rollcall.author]] +name = 'Ronda Strauch' +email = 'rstrauch@uw.edu' +aliases = [ + 'RondaStrauch', +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Sai Nudurupati' +email = 'saisiddu@uw.edu' +aliases = [ + 'Sai Siddhartha Nudurupati', + 'saisiddu', +] +alternate_emails = [ + 'saisiddu@093548ea-dc74-4ebb-8037-4be9f23db00a', + 'saisiddu@e-istanbul-3.ce.washington.edu', +] + +[[tool.rollcall.author]] +name = 'Sebastien Lenard' +email = 'sebastien.lenard@gmail.com' +aliases = [ + 'sebastien-lenard', +] +alternate_emails = [ + '91671955+sebastien-lenard@users.noreply.github.com', +] + +[[tool.rollcall.author]] +name = 'Shelby Ahrendt' +email = '36043125+shelbyahrendt@users.noreply.github.com' +aliases = [ +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Abby Langston' +email = 'alangston@ksu.edu' +aliases = [ + 'alangston', +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Andy Wickert' +email = 'awickert@093548ea-dc74-4ebb-8037-4be9f23db00a' +aliases = [ + 'awickert', +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Jay Hariharan' +email = 'jayaram.hariharan@utexas.edu' +aliases = [ + 'jay', +] +alternate_emails = [ +] +github = 'elbeejay' + +[[tool.rollcall.author]] +name = 'Josh Wolpert' +email = '66105693+josh-wolpert@users.noreply.github.com' +aliases = [ + 'josh-wolpert' +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Margaux Mouchene' +email = 'margaux.mouchene@laposte.net' +aliases = [ + 'margauxmouchene', +] +alternate_emails = [ + 'margauxmouchene@Margauxs-iMac.local', + 'margauxmouchene@dhcp-216-222.tulane.edu', + 'margauxmouchene@dhcp-216-249.tulane.edu', + 'margauxmouchene@dhcp-216-251.tulane.edu', +] + +[[tool.rollcall.author]] +name = 'root' +email = 'root@093548ea-dc74-4ebb-8037-4be9f23db00a' +aliases = [ +] +alternate_emails = [ +] + +[[tool.rollcall.author]] +name = 'Sarah Lundell' +email = 's_lundell@att.net' +aliases = [ + 'slundell123', +] +alternate_emails = [ +] + From 68c85f98f9325e036da1b8d8bc5cd57e9c8c958f Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 21:40:45 -0600 Subject: [PATCH 09/23] update author roll --- .roll.toml | 117 +++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/.roll.toml b/.roll.toml index 36e74091e1..0a3e5860ea 100644 --- a/.roll.toml +++ b/.roll.toml @@ -6,6 +6,15 @@ aliases = [ alternate_emails = [ ] +[[tool.rollcall.author]] +name = 'Abby Langston' +email = 'alangston@ksu.edu' +aliases = [ + 'alangston', +] +alternate_emails = [ +] + [[tool.rollcall.author]] name = 'Allison Pfeiffer' email = 'pfeif@uw.edu' @@ -25,6 +34,15 @@ alternate_emails = [ ] github = 'amanaster2' +[[tool.rollcall.author]] +name = 'Andy Wickert' +email = 'awickert@093548ea-dc74-4ebb-8037-4be9f23db00a' +aliases = [ + 'awickert', +] +alternate_emails = [ +] + [[tool.rollcall.author]] name = 'Benjamin Campforts' email = 'benjamincampforts@gmail.com' @@ -131,6 +149,16 @@ alternate_emails = [ 'gtucker@093548ea-dc74-4ebb-8037-4be9f23db00a', ] +[[tool.rollcall.author]] +name = 'Jay Hariharan' +email = 'jayaram.hariharan@utexas.edu' +aliases = [ + 'jay', +] +alternate_emails = [ +] +github = 'elbeejay' + [[tool.rollcall.author]] name = 'Jenny Knuth' email = 'inkboco@gmail.com' @@ -156,6 +184,15 @@ alternate_emails = [ ] github = 'jadams15' +[[tool.rollcall.author]] +name = 'Josh Wolpert' +email = '66105693+josh-wolpert@users.noreply.github.com' +aliases = [ + 'josh-wolpert', +] +alternate_emails = [ +] + [[tool.rollcall.author]] name = 'Katy Barnhart' email = 'krbarnhart@usgs.gov' @@ -183,6 +220,19 @@ alternate_emails = [ ] github = 'kthyng' +[[tool.rollcall.author]] +name = 'Margaux Mouchene' +email = 'margaux.mouchene@laposte.net' +aliases = [ + 'margauxmouchene', +] +alternate_emails = [ + 'margauxmouchene@Margauxs-iMac.local', + 'margauxmouchene@dhcp-216-222.tulane.edu', + 'margauxmouchene@dhcp-216-249.tulane.edu', + 'margauxmouchene@dhcp-216-251.tulane.edu', +] + [[tool.rollcall.author]] name = 'Mark Piper' email = 'mark.piper@colorado.edu' @@ -248,71 +298,30 @@ alternate_emails = [ ] [[tool.rollcall.author]] -name = 'Sebastien Lenard' -email = 'sebastien.lenard@gmail.com' -aliases = [ - 'sebastien-lenard', -] -alternate_emails = [ - '91671955+sebastien-lenard@users.noreply.github.com', -] - -[[tool.rollcall.author]] -name = 'Shelby Ahrendt' -email = '36043125+shelbyahrendt@users.noreply.github.com' -aliases = [ -] -alternate_emails = [ -] - -[[tool.rollcall.author]] -name = 'Abby Langston' -email = 'alangston@ksu.edu' -aliases = [ - 'alangston', -] -alternate_emails = [ -] - -[[tool.rollcall.author]] -name = 'Andy Wickert' -email = 'awickert@093548ea-dc74-4ebb-8037-4be9f23db00a' -aliases = [ - 'awickert', -] -alternate_emails = [ -] - -[[tool.rollcall.author]] -name = 'Jay Hariharan' -email = 'jayaram.hariharan@utexas.edu' +name = 'Sarah Lundell' +email = 's_lundell@att.net' aliases = [ - 'jay', + 'slundell123', ] alternate_emails = [ ] -github = 'elbeejay' [[tool.rollcall.author]] -name = 'Josh Wolpert' -email = '66105693+josh-wolpert@users.noreply.github.com' +name = 'Sebastien Lenard' +email = 'sebastien.lenard@gmail.com' aliases = [ - 'josh-wolpert' + 'sebastien-lenard', ] alternate_emails = [ + '91671955+sebastien-lenard@users.noreply.github.com', ] [[tool.rollcall.author]] -name = 'Margaux Mouchene' -email = 'margaux.mouchene@laposte.net' +name = 'Shelby Ahrendt' +email = '36043125+shelbyahrendt@users.noreply.github.com' aliases = [ - 'margauxmouchene', ] alternate_emails = [ - 'margauxmouchene@Margauxs-iMac.local', - 'margauxmouchene@dhcp-216-222.tulane.edu', - 'margauxmouchene@dhcp-216-249.tulane.edu', - 'margauxmouchene@dhcp-216-251.tulane.edu', ] [[tool.rollcall.author]] @@ -323,12 +332,4 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] -name = 'Sarah Lundell' -email = 's_lundell@att.net' -aliases = [ - 'slundell123', -] -alternate_emails = [ -] From 5c88b4569216c0dee48f8a90a9b06413c6df035a Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 21:42:58 -0600 Subject: [PATCH 10/23] add rollcall session to the noxfile --- noxfile.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/noxfile.py b/noxfile.py index 64ec116c0f..fdf1122661 100644 --- a/noxfile.py +++ b/noxfile.py @@ -127,3 +127,34 @@ def clean(session): p.rmdir() else: p.unlink() + + +@nox.session +def rollcall(session): + """Update the various authors files.""" + from landlab.cmd.authors import AuthorsConfig + + config = AuthorsConfig() + + with open(".mailmap", "wb") as fp: + session.run( + "landlab", "--silent", "authors", "mailmap", stdout=fp, external=True + ) + + contents = session.run( + "landlab", + "--silent", + "authors", + "create", + "--update-existing", + external=True, + silent=True, + ) + with open(config["roll_file"], "w") as fp: + print(contents, file=fp) + + contents = session.run( + "landlab", "--silent", "authors", "build", silent=True, external=True + ) + with open(config["authors_file"], "w") as fp: + print(contents, file=fp) From aadef0caa72e40cf25fe8df65db3737f2dd05610 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 21:51:45 -0600 Subject: [PATCH 11/23] sort authors by number of commits --- landlab/cmd/landlab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index b5ef53ecae..7c0b7c1bf4 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -247,7 +247,7 @@ def build(ctx): commits[canonical_name] += 1 print(intro) - for author in sorted(authors, key=lambda a: commits[a], reverse=True): + for author in sorted(authors, key=lambda a: commits[a.name], reverse=True): github = _guess_github_user(author) if github is None: github = "landlab" From e28654bd7aed209fb8233c958072da59b1e45fdd Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 21:53:51 -0600 Subject: [PATCH 12/23] update authors file --- AUTHORS.rst | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 1d54b9b8c8..b0ec7c17f8 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -21,20 +21,36 @@ Development Leads Contributors ------------ -- `Charlie Shobe `_ -- `Ronda Strauch `_ -- `David Litwin `_ -- `Rachel Glade `_ -- `Giuseppecipolla95 `_ -- `Amanda Manaster `_ -- `elbeejay `_ -- `Allison Pfeiffer `_ -- `alangston `_ -- `Kristen Thyng `_ -- `Dylan Ward `_ -- `Benjamin Campforts `_ -- `Sebastien Lenard `_ -- `Shelby Ahrendt `_ -- `Josh Wolpert `_ -- `Christopher Sheehan `_ -- `slundell123 `_ \ No newline at end of file +.. rollcall start-author-list + +* `Eric Hutton `_ +* `Katy Barnhart `_ +* `Dan Hobley `_ +* `Greg Tucker `_ +* `Jordan Adams `_ +* `Sai Nudurupati `_ +* `Nicole M Gasparini `_ +* `Charlie Shobe `_ +* `David Litwin `_ +* `Sebastien Lenard `_ +* `Ronda Strauch `_ +* `Margaux Mouchene `_ +* `Jenny Knuth `_ +* `Nathan Lyons `_ +* `Rachel Glade `_ +* `Giuseppecipolla95 `_ +* `Dylan Ward `_ +* `Amanda Manaster `_ +* `Benjamin Campforts `_ +* `Allison Pfeiffer `_ +* `Mark Piper `_ +* `Jay Hariharan `_ +* `Shelby Ahrendt `_ +* `Abby Langston `_ +* `Andy Wickert `_ +* `Christopher Sheehan `_ +* `Francis Rengers `_ +* `Josh Wolpert `_ +* `Kristen Thyng `_ +* `Sarah Lundell `_ + From d08ee9ea619138ce1a1edafb1232321181c47643 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 22:12:16 -0600 Subject: [PATCH 13/23] sort names/emails when guessing github name --- landlab/cmd/landlab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 7c0b7c1bf4..e14d0efb52 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -288,7 +288,7 @@ def _guess_github_user(author): pass if github is None: - for email in author.emails: + for email in sorted(author.emails): if email.endswith("github.com"): github, _ = email.split("@") if "+" in github: @@ -296,7 +296,7 @@ def _guess_github_user(author): break if github is None: - for name in author.names: + for name in sorted(author.names): if name.isalnum(): github = name break From 091bfe261c9a584660f7e82e26e776fa6b95ec24 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 22:12:44 -0600 Subject: [PATCH 14/23] update some emails --- .roll.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.roll.toml b/.roll.toml index 0a3e5860ea..f5bc9a112b 100644 --- a/.roll.toml +++ b/.roll.toml @@ -89,12 +89,12 @@ github = 'SiccarPoint' [[tool.rollcall.author]] name = 'David Litwin' -email = '45630470+DavidLitwin@users.noreply.github.com' +email = 'davidglitwin@posteo.net' aliases = [ 'DavidLitwin', ] alternate_emails = [ - 'davidglitwin@posteo.net', + '45630470+DavidLitwin@users.noreply.github.com', ] [[tool.rollcall.author]] @@ -170,7 +170,7 @@ github = 'jennyknuth' [[tool.rollcall.author]] name = 'Jordan Adams' -email = 'Jordan@e0-f8-47-11-a-8c.cg-guest.ucar.edu' +email = 'jadams15@tulane.edu' aliases = [ 'Jordan', 'jadams15', @@ -178,8 +178,8 @@ aliases = [ ] alternate_emails = [ 'Jordan@Jordan-Adamss-MacBook-Pro.local', + 'Jordan@e0-f8-47-11-a-8c.cg-guest.ucar.edu', 'jadams15@093548ea-dc74-4ebb-8037-4be9f23db00a', - 'jadams15@tulane.edu', 'jadams15@users.noreply.github.com', ] github = 'jadams15' From 8f638dbfbaa5ffd679bde5d9b5e54a4a4ef59c36 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Wed, 5 Oct 2022 22:15:36 -0600 Subject: [PATCH 15/23] add a mailmap file --- .mailmap | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..ce76158acc --- /dev/null +++ b/.mailmap @@ -0,0 +1,170 @@ +(no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> (no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> +Abby Langston Abby Langston +Abby Langston alangston +Allison Pfeiffer Allison Pfeiffer <35848814+pfeiffea@users.noreply.github.com> +Allison Pfeiffer Allison Pfeiffer +Allison Pfeiffer pfeiffea <35848814+pfeiffea@users.noreply.github.com> +Allison Pfeiffer pfeiffea +Amanda Manaster Amanda Manaster +Andy Wickert Andy Wickert +Andy Wickert awickert +Benjamin Campforts Benjamin +Benjamin Campforts Benjamin Campforts +Charlie Shobe Charlie Shobe +Charlie Shobe Charlie Shobe +Charlie Shobe cmshobe +Charlie Shobe cmshobe +Christopher Sheehan <56881748+Sheehace@users.noreply.github.com> Christopher Sheehan <56881748+Sheehace@users.noreply.github.com> +Dan Hobley Dan Hobley +Dan Hobley Dan Hobley +Dan Hobley Dan Hobley +Dan Hobley Dan Hobley +Dan Hobley Dan Hobley +Dan Hobley DanielHobley +Dan Hobley DanielHobley +Dan Hobley DanielHobley +Dan Hobley DanielHobley +Dan Hobley DanielHobley +Dan Hobley SiccarPoint +Dan Hobley SiccarPoint +Dan Hobley SiccarPoint +Dan Hobley SiccarPoint +Dan Hobley SiccarPoint +Dan Hobley danhobley +Dan Hobley danhobley +Dan Hobley danhobley +Dan Hobley danhobley +Dan Hobley danhobley +David Litwin David Litwin <45630470+DavidLitwin@users.noreply.github.com> +David Litwin David Litwin +David Litwin DavidLitwin <45630470+DavidLitwin@users.noreply.github.com> +David Litwin DavidLitwin +Dylan Ward Dylan +Dylan Ward Dylan Ward +Eric Hutton Eric Hutton +Eric Hutton Eric Hutton +Eric Hutton Eric Hutton +Eric Hutton Eric Hutton +Eric Hutton huttone +Eric Hutton huttone +Eric Hutton huttone +Eric Hutton huttone +Eric Hutton mcflugen +Eric Hutton mcflugen +Eric Hutton mcflugen +Eric Hutton mcflugen +Francis Rengers Francis Rengers +Giuseppecipolla95 <36881480+Giuseppecipolla95@users.noreply.github.com> Giuseppecipolla95 <36881480+Giuseppecipolla95@users.noreply.github.com> +Greg Tucker Greg Tucker +Greg Tucker Greg Tucker +Greg Tucker Greg Tucker +Greg Tucker gregtucker +Greg Tucker gregtucker +Greg Tucker gregtucker +Greg Tucker gtucker +Greg Tucker gtucker +Greg Tucker gtucker +Jay Hariharan Jay Hariharan +Jay Hariharan jay +Jenny Knuth Jenny Knuth +Jordan Adams Jordan +Jordan Adams Jordan +Jordan Adams Jordan +Jordan Adams Jordan +Jordan Adams Jordan +Jordan Adams Jordan Adams +Jordan Adams Jordan Adams +Jordan Adams Jordan Adams +Jordan Adams Jordan Adams +Jordan Adams Jordan Adams +Jordan Adams jadams15 +Jordan Adams jadams15 +Jordan Adams jadams15 +Jordan Adams jadams15 +Jordan Adams jadams15 +Jordan Adams jordan +Jordan Adams jordan +Jordan Adams jordan +Jordan Adams jordan +Jordan Adams jordan +Josh Wolpert <66105693+josh-wolpert@users.noreply.github.com> Josh Wolpert <66105693+josh-wolpert@users.noreply.github.com> +Josh Wolpert <66105693+josh-wolpert@users.noreply.github.com> josh-wolpert <66105693+josh-wolpert@users.noreply.github.com> +Katy Barnhart Barnhart, Katherine (Katy) Ruth +Katy Barnhart Barnhart, Katherine (Katy) Ruth +Katy Barnhart Barnhart, Katherine (Katy) Ruth +Katy Barnhart Barnhart, Katherine (Katy) Ruth +Katy Barnhart Barnhart, Katherine (Katy) Ruth +Katy Barnhart Katherine Barnhart +Katy Barnhart Katherine Barnhart +Katy Barnhart Katherine Barnhart +Katy Barnhart Katherine Barnhart +Katy Barnhart Katherine Barnhart +Katy Barnhart Katy +Katy Barnhart Katy +Katy Barnhart Katy +Katy Barnhart Katy +Katy Barnhart Katy +Katy Barnhart Katy Barnhart +Katy Barnhart Katy Barnhart +Katy Barnhart Katy Barnhart +Katy Barnhart Katy Barnhart +Katy Barnhart Katy Barnhart +Katy Barnhart barnhark +Katy Barnhart barnhark +Katy Barnhart barnhark +Katy Barnhart barnhark +Katy Barnhart barnhark +Katy Barnhart kbarnhart +Katy Barnhart kbarnhart +Katy Barnhart kbarnhart +Katy Barnhart kbarnhart +Katy Barnhart kbarnhart +Kristen Thyng Kristen Thyng +Margaux Mouchene Margaux Mouchene +Margaux Mouchene Margaux Mouchene +Margaux Mouchene Margaux Mouchene +Margaux Mouchene Margaux Mouchene +Margaux Mouchene Margaux Mouchene +Margaux Mouchene margauxmouchene +Margaux Mouchene margauxmouchene +Margaux Mouchene margauxmouchene +Margaux Mouchene margauxmouchene +Margaux Mouchene margauxmouchene +Mark Piper Mark Piper +Nathan Lyons Nathan Lyons +Nathan Lyons Nathan Lyons +Nathan Lyons NathanLyons +Nathan Lyons NathanLyons +Nathan Lyons nathanlyons +Nathan Lyons nathanlyons +Nicole M Gasparini Nicole M Gasparini +Nicole M Gasparini Nicole M Gasparini +Nicole M Gasparini Nicole M Gasparini +Nicole M Gasparini Nicole M Gasparini +Nicole M Gasparini nicgaspar +Nicole M Gasparini nicgaspar +Nicole M Gasparini nicgaspar +Nicole M Gasparini nicgaspar +Rachel Glade Glader011235 +Rachel Glade Glader011235 +Rachel Glade Rachel Glade +Rachel Glade Rachel Glade +Ronda Strauch Ronda Strauch +Ronda Strauch RondaStrauch +Sai Nudurupati Sai Nudurupati +Sai Nudurupati Sai Nudurupati +Sai Nudurupati Sai Nudurupati +Sai Nudurupati Sai Siddhartha Nudurupati +Sai Nudurupati Sai Siddhartha Nudurupati +Sai Nudurupati Sai Siddhartha Nudurupati +Sai Nudurupati saisiddu +Sai Nudurupati saisiddu +Sai Nudurupati saisiddu +Sarah Lundell Sarah Lundell +Sarah Lundell slundell123 +Sebastien Lenard Sebastien Lenard <91671955+sebastien-lenard@users.noreply.github.com> +Sebastien Lenard Sebastien Lenard +Sebastien Lenard sebastien-lenard <91671955+sebastien-lenard@users.noreply.github.com> +Sebastien Lenard sebastien-lenard +Shelby Ahrendt <36043125+shelbyahrendt@users.noreply.github.com> Shelby Ahrendt <36043125+shelbyahrendt@users.noreply.github.com> +root root From 4bf0957c6687711436387bdd19929c079c199095 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 6 Oct 2022 08:24:41 -0600 Subject: [PATCH 16/23] upgrade to python 3.8+ syntax --- landlab/cmd/authors.py | 7 +++---- landlab/cmd/landlab.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index fb77882cca..c38577f8e0 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -79,8 +79,7 @@ def __call__(self): process = subprocess.run( self._args, text=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + capture_output=True, stdin=subprocess.PIPE, ) if process.returncode != 0: @@ -200,12 +199,12 @@ def __init__(self, authors=None): self._email[email] = author def __iter__(self): - names = set([author.name for author in self._name.values()]) + names = {author.name for author in self._name.values()} for name in sorted(names): yield self._name[name] def __len__(self): - names = set([author.name for author in self._name.values()]) + names = {author.name for author in self._name.values()} return len(names) def find_author(self, name_or_email): diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index e14d0efb52..f7bae2101f 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -261,7 +261,7 @@ def build(ctx): def _read_until(path_to_file, until=None): - with open(path_to_file, "r") as fp: + with open(path_to_file) as fp: if until is None: return fp.read() From 0c6b8dbd731616dbe92ab589e5a15bfe95fb1aec Mon Sep 17 00:00:00 2001 From: mcflugen Date: Thu, 6 Oct 2022 08:43:14 -0600 Subject: [PATCH 17/23] check for OSError when looking for config files --- landlab/cmd/authors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index c38577f8e0..ab1f76dd64 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -56,7 +56,7 @@ def _load_first_of(files): for name in files: try: config = AuthorsConfig._load_toml(name) - except (FileNotFoundError, KeyError): + except (OSError, KeyError): pass else: break From 4b1dd85ef37fa37ef2816169024d4f3b691c5be6 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Tue, 11 Oct 2022 12:52:25 -0600 Subject: [PATCH 18/23] add a few docstrings --- landlab/cmd/landlab.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index f7bae2101f..93b3d55039 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -158,6 +158,7 @@ def validate(component): ) @click.pass_context def authors(ctx, ignore, authors_file, roll_file): + """Commands for working with lists of authors.""" verbose = ctx.parent.params["verbose"] silent = ctx.parent.params["silent"] @@ -176,6 +177,7 @@ def authors(ctx, ignore, authors_file, roll_file): @click.option("--update-existing/--no-update-existing", default=True) @click.pass_context def create(ctx, update_existing): + """Create a database of contributors.""" verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] roll_file = pathlib.Path(ctx.parent.params["roll_file"]) @@ -211,6 +213,7 @@ def create(ctx, update_existing): @authors.command() @click.pass_context def build(ctx): + """Build an authors file.""" verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] ignore = set(ctx.parent.params["ignore"]) @@ -261,6 +264,22 @@ def build(ctx): def _read_until(path_to_file, until=None): + """Read a file until a line starting with a given string. + + Parameters + ---------- + path_to_file : str or path-like + The file to read. + until : str, optional + Read lines until reaching a line that starts with ``until``. + If not provided, read the entire file. + + Returns + ------- + str + The contents of the file up to, and including, the search + string. + """ with open(path_to_file) as fp: if until is None: return fp.read() @@ -275,6 +294,7 @@ def _read_until(path_to_file, until=None): def _guess_github_user(author): + """Guess an author's github username.""" github = None try: @@ -307,6 +327,7 @@ def _guess_github_user(author): @authors.command() @click.pass_context def mailmap(ctx): + """Create a mailmap file from an author list.""" verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] roll_file = ctx.parent.params["roll_file"] From 16645dcb01f8123bc3552cc39b5ac06a908b172c Mon Sep 17 00:00:00 2001 From: mcflugen Date: Fri, 3 Nov 2023 17:19:27 -0600 Subject: [PATCH 19/23] update the author list --- .mailmap | 22 ++++++++++++++++++++++ .roll.toml | 37 +++++++++++++++++++++++++++++++++++++ AUTHORS.rst | 6 +++++- landlab/cmd/landlab.py | 15 +++++++++++++++ pyproject.toml | 4 ++-- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/.mailmap b/.mailmap index ce76158acc..270b693ea9 100644 --- a/.mailmap +++ b/.mailmap @@ -1,3 +1,13 @@ +# Prevent git from showing duplicate names with commands like "git shortlog" +# See the manpage of git-shortlog for details. +# The syntax is: +# Name that should be used Bad name +# +# You can skip Bad name if it is the same as the one that should be used, and is unique. +# +# This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u +# gives no duplicates. + (no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> (no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> Abby Langston Abby Langston Abby Langston alangston @@ -8,7 +18,9 @@ Allison Pfeiffer pfeiffea Amanda Manaster Amanda Manaster Andy Wickert Andy Wickert Andy Wickert awickert +Benjamin Campforts Benjamin Benjamin Campforts Benjamin +Benjamin Campforts Benjamin Campforts Benjamin Campforts Benjamin Campforts Charlie Shobe Charlie Shobe Charlie Shobe Charlie Shobe @@ -66,6 +78,10 @@ Greg Tucker gtucker gtucker Jay Hariharan Jay Hariharan Jay Hariharan jay +Jeffrey Keck Jeffrey Keck <31672064+keckje@users.noreply.github.com> +Jeffrey Keck Jeffrey Keck +Jeffrey Keck keckje <31672064+keckje@users.noreply.github.com> +Jeffrey Keck keckje Jenny Knuth Jenny Knuth Jordan Adams Jordan Jordan Adams Jordan @@ -120,6 +136,10 @@ Katy Barnhart kbarnhart kbarnhart Katy Barnhart kbarnhart Kristen Thyng Kristen Thyng +Laurent Roberge Laurent Roberge <59940112+loroberge@users.noreply.github.com> +Laurent Roberge Laurent Roberge +Laurent Roberge loroberge <59940112+loroberge@users.noreply.github.com> +Laurent Roberge loroberge Margaux Mouchene Margaux Mouchene Margaux Mouchene Margaux Mouchene Margaux Mouchene Margaux Mouchene @@ -131,6 +151,7 @@ Margaux Mouchene margauxmouchene margauxmouchene Margaux Mouchene margauxmouchene Mark Piper Mark Piper +Muneer Ahammad <65404451+muneerVT@users.noreply.github.com> Muneer Ahammad <65404451+muneerVT@users.noreply.github.com> Nathan Lyons Nathan Lyons Nathan Lyons Nathan Lyons Nathan Lyons NathanLyons @@ -167,4 +188,5 @@ Sebastien Lenard Sebastien Lenard sebastien-lenard <91671955+sebastien-lenard@users.noreply.github.com> Sebastien Lenard sebastien-lenard Shelby Ahrendt <36043125+shelbyahrendt@users.noreply.github.com> Shelby Ahrendt <36043125+shelbyahrendt@users.noreply.github.com> +dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> root root diff --git a/.roll.toml b/.roll.toml index f5bc9a112b..27b2961cf6 100644 --- a/.roll.toml +++ b/.roll.toml @@ -50,6 +50,7 @@ aliases = [ 'Benjamin', ] alternate_emails = [ + 'bca@Benjamins-MacBook-Pro.local', ] github = 'BCampforts' @@ -159,6 +160,16 @@ alternate_emails = [ ] github = 'elbeejay' +[[tool.rollcall.author]] +name = 'Jeffrey Keck' +email = 'keckje@gmail.com' +aliases = [ + 'keckje', +] +alternate_emails = [ + '31672064+keckje@users.noreply.github.com', +] + [[tool.rollcall.author]] name = 'Jenny Knuth' email = 'inkboco@gmail.com' @@ -220,6 +231,16 @@ alternate_emails = [ ] github = 'kthyng' +[[tool.rollcall.author]] +name = 'Laurent Roberge' +email = 'lroberge@tulane.edu' +aliases = [ + 'loroberge', +] +alternate_emails = [ + '59940112+loroberge@users.noreply.github.com', +] + [[tool.rollcall.author]] name = 'Margaux Mouchene' email = 'margaux.mouchene@laposte.net' @@ -242,6 +263,14 @@ alternate_emails = [ ] github = 'mdpiper' +[[tool.rollcall.author]] +name = 'Muneer Ahammad' +email = '65404451+muneerVT@users.noreply.github.com' +aliases = [ +] +alternate_emails = [ +] + [[tool.rollcall.author]] name = 'Nathan Lyons' email = 'nathan.j.lyons@gmail.com' @@ -324,6 +353,14 @@ aliases = [ alternate_emails = [ ] +[[tool.rollcall.author]] +name = 'dependabot[bot]' +email = '49699333+dependabot[bot]@users.noreply.github.com' +aliases = [ +] +alternate_emails = [ +] + [[tool.rollcall.author]] name = 'root' email = 'root@093548ea-dc74-4ebb-8037-4be9f23db00a' diff --git a/AUTHORS.rst b/AUTHORS.rst index 7a52402c2a..7df270d432 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -36,12 +36,14 @@ Contributors * `Ronda Strauch `_ * `Margaux Mouchene `_ * `Jenny Knuth `_ +* `Benjamin Campforts `_ * `Nathan Lyons `_ +* `Jeffrey Keck `_ * `Rachel Glade `_ +* `Laurent Roberge `_ * `Giuseppecipolla95 `_ * `Dylan Ward `_ * `Amanda Manaster `_ -* `Benjamin Campforts `_ * `Allison Pfeiffer `_ * `Mark Piper `_ * `Jay Hariharan `_ @@ -52,4 +54,6 @@ Contributors * `Francis Rengers `_ * `Josh Wolpert `_ * `Kristen Thyng `_ +* `Muneer Ahammad `_ * `Sarah Lundell `_ + diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 73c3acf74c..14fbb7ee20 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -334,6 +334,21 @@ def mailmap(ctx): if verbose and not silent: out(f"reading author list: {roll_file}") + print( + textwrap.dedent( + """ + # Prevent git from showing duplicate names with commands like "git shortlog" + # See the manpage of git-shortlog for details. + # The syntax is: + # Name that should be used Bad name + # + # You can skip Bad name if it is the same as the one that should be used, and is unique. + # + # This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u + # gives no duplicates. + """ + ).lstrip() + ) authors = AuthorList.from_toml(roll_file) for author in authors: good_name, good_email = author.name, author.email diff --git a/pyproject.toml b/pyproject.toml index 59c866ebb0..f8f207da26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,6 +158,6 @@ type = [ ] [tool.rollcall] -ignore = ['(no author)', 'root'] -authors_file = 'AUTHORS.rst' +ignore = ["(no author)", "root", "dependabot[bot]"] +authors_file = "AUTHORS.rst" author_format = "* `{name} `_" From 8551d1eba9b68db74bfe10b68c5352dab34524b2 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 4 Nov 2023 14:49:58 -0600 Subject: [PATCH 20/23] rename landlab credits tool --- .roll.toml => .credits.toml | 74 ++++++++++++++++++------------------- AUTHORS.rst | 2 +- landlab/cmd/authors.py | 20 +++++----- landlab/cmd/landlab.py | 55 ++++++++++++++------------- noxfile.py | 4 +- pyproject.toml | 2 +- 6 files changed, 81 insertions(+), 76 deletions(-) rename .roll.toml => .credits.toml (83%) diff --git a/.roll.toml b/.credits.toml similarity index 83% rename from .roll.toml rename to .credits.toml index 27b2961cf6..8796ea800b 100644 --- a/.roll.toml +++ b/.credits.toml @@ -1,4 +1,4 @@ -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = '(no author)' email = '(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a' aliases = [ @@ -6,7 +6,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Abby Langston' email = 'alangston@ksu.edu' aliases = [ @@ -15,7 +15,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Allison Pfeiffer' email = 'pfeif@uw.edu' aliases = [ @@ -25,7 +25,7 @@ alternate_emails = [ '35848814+pfeiffea@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Amanda Manaster' email = 'amanaste@uw.edu' aliases = [ @@ -34,7 +34,7 @@ alternate_emails = [ ] github = 'amanaster2' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Andy Wickert' email = 'awickert@093548ea-dc74-4ebb-8037-4be9f23db00a' aliases = [ @@ -43,7 +43,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Benjamin Campforts' email = 'benjamincampforts@gmail.com' aliases = [ @@ -54,7 +54,7 @@ alternate_emails = [ ] github = 'BCampforts' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Charlie Shobe' email = 'cmshobe@email.wm.edu' aliases = [ @@ -64,7 +64,7 @@ alternate_emails = [ 'cmshobe@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Christopher Sheehan' email = '56881748+Sheehace@users.noreply.github.com' aliases = [ @@ -72,7 +72,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Dan Hobley' email = 'dan.hobley@gmail.com' aliases = [ @@ -88,7 +88,7 @@ alternate_emails = [ ] github = 'SiccarPoint' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'David Litwin' email = 'davidglitwin@posteo.net' aliases = [ @@ -98,7 +98,7 @@ alternate_emails = [ '45630470+DavidLitwin@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Dylan Ward' email = 'dylan.ward@uc.edu' aliases = [ @@ -108,7 +108,7 @@ alternate_emails = [ ] github = 'ddoubleprime' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Eric Hutton' email = 'mcflugen@gmail.com' aliases = [ @@ -121,7 +121,7 @@ alternate_emails = [ 'mcflugen@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Francis Rengers' email = 'francis.rengers@colorado.edu' aliases = [ @@ -130,7 +130,7 @@ alternate_emails = [ ] github = 'frengers' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Giuseppecipolla95' email = '36881480+Giuseppecipolla95@users.noreply.github.com' aliases = [ @@ -138,7 +138,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Greg Tucker' email = 'gtucker@colorado.edu' aliases = [ @@ -150,7 +150,7 @@ alternate_emails = [ 'gtucker@093548ea-dc74-4ebb-8037-4be9f23db00a', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Jay Hariharan' email = 'jayaram.hariharan@utexas.edu' aliases = [ @@ -160,7 +160,7 @@ alternate_emails = [ ] github = 'elbeejay' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Jeffrey Keck' email = 'keckje@gmail.com' aliases = [ @@ -170,7 +170,7 @@ alternate_emails = [ '31672064+keckje@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Jenny Knuth' email = 'inkboco@gmail.com' aliases = [ @@ -179,7 +179,7 @@ alternate_emails = [ ] github = 'jennyknuth' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Jordan Adams' email = 'jadams15@tulane.edu' aliases = [ @@ -195,7 +195,7 @@ alternate_emails = [ ] github = 'jadams15' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Josh Wolpert' email = '66105693+josh-wolpert@users.noreply.github.com' aliases = [ @@ -204,7 +204,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Katy Barnhart' email = 'krbarnhart@usgs.gov' aliases = [ @@ -222,7 +222,7 @@ alternate_emails = [ ] github = 'kbarnhart' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Kristen Thyng' email = 'kthyng@gmail.com' aliases = [ @@ -231,7 +231,7 @@ alternate_emails = [ ] github = 'kthyng' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Laurent Roberge' email = 'lroberge@tulane.edu' aliases = [ @@ -241,7 +241,7 @@ alternate_emails = [ '59940112+loroberge@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Margaux Mouchene' email = 'margaux.mouchene@laposte.net' aliases = [ @@ -254,7 +254,7 @@ alternate_emails = [ 'margauxmouchene@dhcp-216-251.tulane.edu', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Mark Piper' email = 'mark.piper@colorado.edu' aliases = [ @@ -263,7 +263,7 @@ alternate_emails = [ ] github = 'mdpiper' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Muneer Ahammad' email = '65404451+muneerVT@users.noreply.github.com' aliases = [ @@ -271,7 +271,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Nathan Lyons' email = 'nathan.j.lyons@gmail.com' aliases = [ @@ -283,7 +283,7 @@ alternate_emails = [ ] github = 'nathanlyons' -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Nicole M Gasparini' email = 'nicgaspar@gmail.com' aliases = [ @@ -295,7 +295,7 @@ alternate_emails = [ 'nicolegasparini@ngaspari.tulane.edu', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Rachel Glade' email = 'rcglade@gmail.com' aliases = [ @@ -305,7 +305,7 @@ alternate_emails = [ 'rachel.glade@colorado.edu', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Ronda Strauch' email = 'rstrauch@uw.edu' aliases = [ @@ -314,7 +314,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Sai Nudurupati' email = 'saisiddu@uw.edu' aliases = [ @@ -326,7 +326,7 @@ alternate_emails = [ 'saisiddu@e-istanbul-3.ce.washington.edu', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Sarah Lundell' email = 's_lundell@att.net' aliases = [ @@ -335,7 +335,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Sebastien Lenard' email = 'sebastien.lenard@gmail.com' aliases = [ @@ -345,7 +345,7 @@ alternate_emails = [ '91671955+sebastien-lenard@users.noreply.github.com', ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'Shelby Ahrendt' email = '36043125+shelbyahrendt@users.noreply.github.com' aliases = [ @@ -353,7 +353,7 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'dependabot[bot]' email = '49699333+dependabot[bot]@users.noreply.github.com' aliases = [ @@ -361,12 +361,10 @@ aliases = [ alternate_emails = [ ] -[[tool.rollcall.author]] +[[tool.landlab.credits.author]] name = 'root' email = 'root@093548ea-dc74-4ebb-8037-4be9f23db00a' aliases = [ ] alternate_emails = [ ] - - diff --git a/AUTHORS.rst b/AUTHORS.rst index 7df270d432..d7b0fefdb5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -21,7 +21,7 @@ Development Leads Contributors ------------ -.. rollcall start-author-list +.. credits-roll start-author-list * `Eric Hutton `_ * `Katy Barnhart `_ diff --git a/landlab/cmd/authors.py b/landlab/cmd/authors.py index ab1f76dd64..537184492d 100644 --- a/landlab/cmd/authors.py +++ b/landlab/cmd/authors.py @@ -15,16 +15,15 @@ class AuthorsSubprocessError(Exception): class AuthorsConfig(UserDict): - _FILES = [".rollcall.toml", "rollcall.toml", "pyproject.toml"] + _FILES = [".credit-roll.toml", "credit-roll.toml", "pyproject.toml"] _DEFAULTS = { "authors_file": "AUTHORS.rst", "ignore": (), "author_format": "{name}", - "roll_file": ".roll.toml", + "credits_file": ".credits.toml", } def __init__(self, *args, **kwds): - user_data = { k: v for k, v in dict(*args, **kwds).items() if k in self._DEFAULTS } @@ -34,7 +33,7 @@ def __init__(self, *args, **kwds): ) def __str__(self): - lines = ["[tool.rollcall]"] + lines = ["[tool.landlab.credits]"] for key, value in sorted(self.data.items(), key=lambda item: item[0]): lines.append(f"{key} = {value!r}") return os.linesep.join(lines) @@ -42,13 +41,13 @@ def __str__(self): @classmethod def from_toml(cls, toml_file): with open(toml_file, mode="rb") as fp: - config = tomllib.load(fp)["tool"]["rollcall"] + config = tomllib.load(fp)["tool"]["landlab"]["credits"] return cls(config) @staticmethod def _load_toml(name): with open(name, mode="rb") as fp: - config = tomllib.load(fp)["tool"]["rollcall"] + config = tomllib.load(fp)["tool"]["landlab"]["credits"] return config @staticmethod @@ -152,7 +151,7 @@ def add_alias(self, alias): def to_toml(self): lines = [ - "[[tool.rollcall.author]]", + "[[tool.landlab.credits.author]]", f"name = {self.name!r}", f"email = {self.email!r}", ] @@ -183,7 +182,10 @@ def update(self, other): def __repr__(self): aliases = None if not self.aliases else self.aliases alternate_emails = None if not self.alternate_emails else self.alternate_emails - return f"Author({self.name!r}, {self.email!r}, aliases={aliases!r}, alternate_emails={alternate_emails!r})" + return ( + f"Author({self.name!r}, {self.email!r}," + f" aliases={aliases!r}, alternate_emails={alternate_emails!r})" + ) class AuthorList: @@ -221,7 +223,7 @@ def from_toml(cls, toml_file): with open(toml_file, "rb") as fp: authors = [ Author.from_dict(author) - for author in tomllib.load(fp)["tool"]["rollcall"]["author"] + for author in tomllib.load(fp)["tool"]["landlab"]["credits"]["author"] ] return cls(authors=authors) diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index 14fbb7ee20..aa1da878e1 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -11,8 +11,6 @@ import numpy as np import rich_click as click -from .authors import AuthorsConfig, AuthorsSubprocessError, AuthorList, GitLog - from landlab import ( FramedVoronoiGrid, HexModelGrid, @@ -22,6 +20,8 @@ VoronoiDelaunayGrid, ) +from .authors import AuthorList, AuthorsConfig, AuthorsSubprocessError, GitLog + GRIDS = [ ModelGrid, RasterModelGrid, @@ -151,13 +151,13 @@ def validate(component): help="existing authors file", ) @click.option( - "--roll-file", - default=".roll.toml", + "--credits-file", + default=".credits.toml", type=click.Path(exists=False, file_okay=True, dir_okay=False, readable=True), help="The file that contains a list of authors", ) @click.pass_context -def authors(ctx, ignore, authors_file, roll_file): +def authors(ctx, ignore, authors_file, credits_file): """Commands for working with lists of authors.""" verbose = ctx.parent.params["verbose"] silent = ctx.parent.params["silent"] @@ -180,27 +180,27 @@ def create(ctx, update_existing): """Create a database of contributors.""" verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] - roll_file = pathlib.Path(ctx.parent.params["roll_file"]) + credits_file = pathlib.Path(ctx.parent.params["credits_file"]) git_log = GitLog("%an, %ae") try: names_and_emails = git_log() except AuthorsSubprocessError as error: err(error) - raise click.Abort() + raise click.Abort() from error else: if verbose and not silent: out(f"{git_log}") if not silent and update_existing: - if not roll_file.is_file(): - err(f"nothing to update ({roll_file})") + if not credits_file.is_file(): + err(f"nothing to update ({credits_file})") else: - out(f"updating existing author roll ({roll_file})") + out(f"updating existing author credits ({credits_file})") authors = ( - AuthorList.from_toml(roll_file) - if update_existing and roll_file.is_file() + AuthorList.from_toml(credits_file) + if update_existing and credits_file.is_file() else AuthorList() ) @@ -219,30 +219,30 @@ def build(ctx): ignore = set(ctx.parent.params["ignore"]) authors_file = pathlib.Path(ctx.parent.params["authors_file"]) author_format = ctx.parent.params["author_format"] - roll_file = pathlib.Path(ctx.parent.params["roll_file"]) + credits_file = pathlib.Path(ctx.parent.params["credits_file"]) git_log = GitLog("%aN") try: commit_authors = git_log() except AuthorsSubprocessError as error: err(error) - raise click.Abort() + raise click.Abort() from error else: if verbose and not silent: out(f"{git_log}") intro = ( - _read_until(authors_file, until=".. rollcall start-author-list") + _read_until(authors_file, until=".. credits-roll start-author-list") if authors_file.is_file() else "" ) if len(intro) == 0: err(f"empty or missing authors file ({authors_file})") - authors = AuthorList.from_toml(roll_file) if roll_file.is_file() else AuthorList() + authors = AuthorList.from_toml(credits_file) if credits_file.is_file() else AuthorList() if len(authors) == 0: - err(f"missing or empty roll ({roll_file})") + err(f"missing or empty credits file ({credits_file})") commits = defaultdict(int) for author in commit_authors.splitlines(): @@ -298,7 +298,7 @@ def _guess_github_user(author): github = None try: - github = getattr(author, "github") + github = author.github except AttributeError: pass @@ -330,26 +330,31 @@ def mailmap(ctx): """Create a mailmap file from an author list.""" verbose = ctx.parent.parent.params["verbose"] silent = ctx.parent.parent.params["silent"] - roll_file = ctx.parent.params["roll_file"] + credits_file = ctx.parent.params["credits_file"] if verbose and not silent: - out(f"reading author list: {roll_file}") + out(f"reading author list: {credits_file}") print( textwrap.dedent( """ # Prevent git from showing duplicate names with commands like "git shortlog" # See the manpage of git-shortlog for details. # The syntax is: - # Name that should be used Bad name # - # You can skip Bad name if it is the same as the one that should be used, and is unique. + # Name that should be used Bad name + # + # You can skip Bad name if it is the same as the one that should be used, + # and is unique. + # + # This file is up-to-date if the command, + # + # git log --format="%aN <%aE>" | sort -u # - # This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u # gives no duplicates. """ ).lstrip() ) - authors = AuthorList.from_toml(roll_file) + authors = AuthorList.from_toml(credits_file) for author in authors: good_name, good_email = author.name, author.email for bad_name, bad_email in itertools.product( @@ -376,7 +381,7 @@ def list(ctx, file): commit_authors = git_log() except AuthorsSubprocessError as error: err(error) - raise click.Abort() + raise click.Abort() from error else: if verbose and not silent: out(f"{git_log}") diff --git a/noxfile.py b/noxfile.py index 912af0be86..4d1bd477b9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -390,7 +390,7 @@ def _clean_rglob(pattern): @nox.session -def rollcall(session): +def credits(session): """Update the various authors files.""" from landlab.cmd.authors import AuthorsConfig @@ -410,7 +410,7 @@ def rollcall(session): external=True, silent=True, ) - with open(config["roll_file"], "w") as fp: + with open(config["credits_file"], "w") as fp: print(contents, file=fp) contents = session.run( diff --git a/pyproject.toml b/pyproject.toml index f8f207da26..2b10c4023e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -157,7 +157,7 @@ type = [ {name="Other Changes and Additions", directory="misc", showcontent=true}, ] -[tool.rollcall] +[tool.landlab.credits] ignore = ["(no author)", "root", "dependabot[bot]"] authors_file = "AUTHORS.rst" author_format = "* `{name} `_" From 5ae53e0caeeaa60a8d66f5e8ab7facc8692f1635 Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 4 Nov 2023 15:29:56 -0600 Subject: [PATCH 21/23] fix end-of-file line ending --- .mailmap | 11 ++++++++--- AUTHORS.rst | 1 - landlab/cmd/landlab.py | 16 ++++++++++------ noxfile.py | 4 ++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.mailmap b/.mailmap index 270b693ea9..dea5c66eff 100644 --- a/.mailmap +++ b/.mailmap @@ -1,11 +1,16 @@ # Prevent git from showing duplicate names with commands like "git shortlog" # See the manpage of git-shortlog for details. # The syntax is: -# Name that should be used Bad name # -# You can skip Bad name if it is the same as the one that should be used, and is unique. +# Name that should be used Bad name +# +# You can skip Bad name if it is the same as the one that should be used, +# and is unique. +# +# This file is up-to-date if the command, +# +# git log --format="%aN <%aE>" | sort -u # -# This file is up-to-date if the command git log --format="%aN <%aE>" | sort -u # gives no duplicates. (no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> (no author) <(no author)@093548ea-dc74-4ebb-8037-4be9f23db00a> diff --git a/AUTHORS.rst b/AUTHORS.rst index d7b0fefdb5..cbb27f8953 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -56,4 +56,3 @@ Contributors * `Kristen Thyng `_ * `Muneer Ahammad `_ * `Sarah Lundell `_ - diff --git a/landlab/cmd/landlab.py b/landlab/cmd/landlab.py index aa1da878e1..963c60bdf1 100644 --- a/landlab/cmd/landlab.py +++ b/landlab/cmd/landlab.py @@ -205,9 +205,9 @@ def create(ctx, update_existing): ) authors.update(AuthorList.from_csv(names_and_emails)) - for author in sorted(authors, key=lambda item: item.name): - print(author.to_toml()) - print("") + lines = [author.to_toml() for author in sorted(authors, key=lambda item: item.name)] + + print((2 * os.linesep).join(lines)) @authors.command() @@ -239,7 +239,9 @@ def build(ctx): if len(intro) == 0: err(f"empty or missing authors file ({authors_file})") - authors = AuthorList.from_toml(credits_file) if credits_file.is_file() else AuthorList() + authors = ( + AuthorList.from_toml(credits_file) if credits_file.is_file() else AuthorList() + ) if len(authors) == 0: err(f"missing or empty credits file ({credits_file})") @@ -249,19 +251,21 @@ def build(ctx): canonical_name = authors.find_author(author.strip()).name commits[canonical_name] += 1 - print(intro) + lines = [intro] for author in sorted(authors, key=lambda a: commits[a.name], reverse=True): github = _guess_github_user(author) if github is None: github = "landlab" author.github = github if ignore.isdisjoint(author.names): - print( + lines.append( author_format.format( name=author.name, github=author.github, email=author.email ) ) + print(os.linesep.join(lines)) + def _read_until(path_to_file, until=None): """Read a file until a line starting with a given string. diff --git a/noxfile.py b/noxfile.py index 4d1bd477b9..1dd37a46ae 100644 --- a/noxfile.py +++ b/noxfile.py @@ -411,10 +411,10 @@ def credits(session): silent=True, ) with open(config["credits_file"], "w") as fp: - print(contents, file=fp) + print(contents, file=fp, end="") contents = session.run( "landlab", "--silent", "authors", "build", silent=True, external=True ) with open(config["authors_file"], "w") as fp: - print(contents, file=fp) + print(contents, file=fp, end="") From 9036aa3bf351c07ae55beee8300f3dff0efba8bb Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 4 Nov 2023 15:38:17 -0600 Subject: [PATCH 22/23] update the manifest --- MANIFEST.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index c3400c3e4d..ea4caf1d88 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ +include .credits.toml +include .mailmap include AUTHORS.rst include CHANGES.rst include FUNDING.rst From b13dbfa479cfac09b5bd06b865a90c7a03bbe06d Mon Sep 17 00:00:00 2001 From: mcflugen Date: Sat, 4 Nov 2023 17:31:19 -0600 Subject: [PATCH 23/23] add news fragment [skip ci] --- news/1795.misc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 news/1795.misc diff --git a/news/1795.misc b/news/1795.misc new file mode 100644 index 0000000000..dea2d53db8 --- /dev/null +++ b/news/1795.misc @@ -0,0 +1,3 @@ + +Added a tool that builds a list of *Landlab* contributors and updates the +``AUTHORS.rst`` and ``.mailmap`` files.