#!/usr/bin/env python3
#
# InspIRCd -- Internet Relay Chat Daemon
#
# Copyright (C) 2026 Sadie Powell <sadie@witchery.services>
#
# 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 <http://www.gnu.org/licenses/>.
#
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 <noreply@inspircd.org>":
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 <noreply@inspircd.org>",
"--message", "Update the author list.",
"--",
authors_txt,
info_h,
]
)