#!/usr/bin/env python3 # # InspIRCd -- Internet Relay Chat Daemon # # Copyright (C) 2026 Sadie Powell # # This file is part of InspIRCd. InspIRCd is free software: you can # redistribute it and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation, version 2. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # import os import pathlib import re import shutil import subprocess import sys import textwrap import columnize import yaml ROOT = pathlib.Path(__file__).resolve().parents[1] sys.path.append(str(ROOT)) from src.python import console GIT = os.getenv("GIT", "git") if not shutil.which(GIT): console.error(f"Git ({GIT}) must be installed to build the author list!") commit_log = subprocess.check_output( [GIT, "log", "--pretty=%an <%ae>%n%(trailers:key=Co-Authored-By,valueonly)", "HEAD"] ).splitlines() committers = {} for committer in [c for c in commit_log if c]: committers[committer] = committers.get(committer, 0) + 1 authors = {} for committer, commits in committers.items(): author = ( subprocess.check_output([GIT, "check-mailmap", committer]) .strip() .decode("utf-8") ) # Remove email addresses that aren"t real. author = re.sub( r"^(.+) <(?:unknown@email.invalid|\S+@users.noreply.github.com)>$", r"\1", author, ) # Skip GitHub bots like dependabot. if author.endswith("[bot]"): continue # Skip commits made by our own scripts. if author == "InspIRCd Robot ": continue authors[author] = authors.get(author, 0) + commits authors_txt = ROOT / "docs" / "AUTHORS.txt" with open(authors_txt, "w") as fh: print( textwrap.dedent( f""" Since the first commit in January 2003 {len(authors)} people have submitted patches, commits, and other useful contributions to InspIRCd. These people, ordered by the number of contributions they have made, are: """ ).lstrip(), file=fh, ) # We show the names in the authors file ordered by number of commits and # then name case insensitively. sorted_authors = list(authors.items()) sorted_authors.sort(key=lambda a: a[0].lower()) sorted_authors.sort(key=lambda a: a[1], reverse=True) for author, commits in sorted_authors: print(f" * {author}", file=fh) info_lines = [] with open(ROOT / "docs" / "BANNER.txt") as fh: for line in fh: escaped_line = line.rstrip().replace("\\", r"\\").replace('"', r"\"") info_lines.append(escaped_line) with open(ROOT / "modules" / "core" / "core_info" / "info.yml", "r") as fh: seen = [] for team, members in yaml.safe_load(fh).items(): info_lines.append(" ") info_lines.append(f"\\002{team}\\002:") names = [] if isinstance(members, list): for member in members: if "name" in member: names.append(f"{member['name']} ({member['nick']})") seen.append(member["name"]) else: names.append(member["nick"]) else: for author in authors: name = re.sub(r"^(.+) <.+>$", r"\1", author) if name not in seen: names.append(name) names.sort(key=lambda n: n.lower()) columnized_names = columnize.columnize( names, arrange_vertical=False, colsep=" " * 4, displaywidth=78, ).splitlines() info_lines.extend([f" {line.strip()}" for line in columnized_names]) info_h = ROOT / "modules" / "core" / "core_info" / "info.h" with open(info_h, "w") as fh: print( textwrap.dedent( """ // This file was generated by mkauthors. Any changes will be overwritten. #pragma once static constexpr const char* const lines[] = { """ ).strip("\n"), file=fh, ) for info_line in info_lines: print(f'\t"{info_line}",', file=fh) print( textwrap.dedent( """ \t" ", \t"For more information visit https://www.inspircd.org/ or our IRC channel at", \t"ircs://irc.teranova.net/inspircd.", \tnullptr, }; """ ).strip("\n"), file=fh, ) if int(os.getenv("MKAUTHORS_COMMIT", "1")) > 0: subprocess.check_call( [ GIT, "commit", "--author", "InspIRCd Robot ", "--message", "Update the author list.", "--", authors_txt, info_h, ] )