mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 00:02:25 +00:00
Credit all contributors in release notes
This commit is contained in:
parent
84243027ec
commit
f380becffa
51
maintainers/data/release-credits-email-to-handle.json
Normal file
51
maintainers/data/release-credits-email-to-handle.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"bogus": "bogus",
|
||||||
|
"edolstra@gmail.com": "edolstra",
|
||||||
|
"roberth@users.noreply.github.com": "roberth",
|
||||||
|
"toscano.pino@tiscali.it": "pinotree",
|
||||||
|
"valentin@gagarin.work": "fricklerhandwerk",
|
||||||
|
"mr.trubach@icloud.com": "tie",
|
||||||
|
"robert@roberthensing.nl": "roberth",
|
||||||
|
"lix@jade.fyi": "lf-",
|
||||||
|
"cole.e.helbling@outlook.com": "cole-h",
|
||||||
|
"joerg@thalheim.io": "Mic92",
|
||||||
|
"John.Ericson@Obsidian.Systems": "Ericson2314",
|
||||||
|
"ryan.hendrickson@alum.mit.edu": "rhendric",
|
||||||
|
"67135060+poweredbypie@users.noreply.github.com": "poweredbypie",
|
||||||
|
"detroyejr@outlook.com": "detroyejr",
|
||||||
|
"silvan.mosberger@tweag.io": "infinisil",
|
||||||
|
"vcs@emily.moe": "emilazy",
|
||||||
|
"farid.m.zakaria@gmail.com": "fzakaria",
|
||||||
|
"22859658+RTUnreal@users.noreply.github.com": "RTUnreal",
|
||||||
|
"me@las.rs": "L-as",
|
||||||
|
"philip.taron@gmail.com": "philiptaron",
|
||||||
|
"root@goldstein.rs": "GoldsteinE",
|
||||||
|
"tomberek@users.noreply.github.com": "tomberek",
|
||||||
|
"lexi.mattick@neuralink.com": "kognise",
|
||||||
|
"andrew@johnandrewmarshall.com": "amarshall",
|
||||||
|
"contact@romain-neil.fr": "romain-neil",
|
||||||
|
"Mic92@users.noreply.github.com": "Mic92",
|
||||||
|
"valentin.gagarin@tweag.io": "fricklerhandwerk",
|
||||||
|
"siddhantk232@gmail.com": "siddhantk232",
|
||||||
|
"kn@openbsd.org": "klemensn",
|
||||||
|
"slyich@gmail.com": "trofi",
|
||||||
|
"theophane.hufschmitt@tweag.io": "thufschmitt",
|
||||||
|
"alicebob@lijzij.de": "alicebob",
|
||||||
|
"winter@winter.cafe": "winterqt",
|
||||||
|
"brian@brianmckenna.org": "puffnfresh",
|
||||||
|
"git@haenoe.party": "haenoe",
|
||||||
|
"peshogo@gmail.com": "pineapplehunter",
|
||||||
|
"poweredbypie@users.noreply.github.com": "poweredbypie",
|
||||||
|
"arthur200126@gmail.com": "Artoria2e5",
|
||||||
|
"tomberek@gmail.com": "tomberek",
|
||||||
|
"jaredbaur@fastmail.com": "jmbaur",
|
||||||
|
"andreas@rammhold.de": "andir",
|
||||||
|
"hamirmahal@gmail.com": "hamirmahal",
|
||||||
|
"git@JohnEricson.me": "Ericson2314",
|
||||||
|
"8763518+SkamDart@users.noreply.github.com": "SkamDart",
|
||||||
|
"kirillrdy@gmail.com": "kirillrdy",
|
||||||
|
"pennae@lix.systems": "pennae",
|
||||||
|
"delroth@gmail.com": "delroth",
|
||||||
|
"enno@nerdworks.de": "elohmeier",
|
||||||
|
"mjbauer95@gmail.com": "matthewbauer"
|
||||||
|
}
|
44
maintainers/data/release-credits-handle-to-name.json
Normal file
44
maintainers/data/release-credits-handle-to-name.json
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"fzakaria": "Farid Zakaria",
|
||||||
|
"kognise": "Lexi Mattick",
|
||||||
|
"L-as": "Las Safin",
|
||||||
|
"haenoe": "HaeNoe",
|
||||||
|
"andir": "Andreas Rammhold",
|
||||||
|
"matthewbauer": "Matthew Bauer",
|
||||||
|
"emilazy": "Emily",
|
||||||
|
"pineapplehunter": "Shogo Takata",
|
||||||
|
"RTUnreal": null,
|
||||||
|
"jmbaur": "Jared Baur",
|
||||||
|
"Ericson2314": "John Ericson",
|
||||||
|
"pinotree": "Pino Toscano",
|
||||||
|
"tie": "Ivan Trubach",
|
||||||
|
"poweredbypie": null,
|
||||||
|
"fricklerhandwerk": "Valentin Gagarin",
|
||||||
|
"Mic92": "J\u00f6rg Thalheim",
|
||||||
|
"alicebob": "Harmen",
|
||||||
|
"elohmeier": "Enno Richter",
|
||||||
|
"delroth": "Pierre Bourdon",
|
||||||
|
"kirillrdy": null,
|
||||||
|
"thufschmitt": "Th\u00e9ophane Hufschmitt",
|
||||||
|
"detroyejr": "Jonathan De Troye",
|
||||||
|
"klemensn": "Klemens Nanni",
|
||||||
|
"tomberek": null,
|
||||||
|
"rhendric": "Ryan Hendrickson",
|
||||||
|
"philiptaron": "Philip Taron",
|
||||||
|
"puffnfresh": "Brian McKenna",
|
||||||
|
"lf-": "jade",
|
||||||
|
"romain-neil": "Romain Neil",
|
||||||
|
"hamirmahal": "Hamir Mahal",
|
||||||
|
"edolstra": "Eelco Dolstra",
|
||||||
|
"Artoria2e5": "Mingye Wang",
|
||||||
|
"SkamDart": "Cameron",
|
||||||
|
"roberth": "Robert Hensing",
|
||||||
|
"amarshall": "Andrew Marshall",
|
||||||
|
"trofi": "Sergei Trofimovich",
|
||||||
|
"cole-h": "Cole Helbling",
|
||||||
|
"infinisil": "Silvan Mosberger",
|
||||||
|
"siddhantk232": "Siddhant Kumar",
|
||||||
|
"winterqt": "Winter",
|
||||||
|
"GoldsteinE": "Max \u201cGoldstein\u201d Siling",
|
||||||
|
"pennae": null
|
||||||
|
}
|
176
maintainers/release-credits
Executable file
176
maintainers/release-credits
Executable file
@ -0,0 +1,176 @@
|
|||||||
|
#!/usr/bin/env nix
|
||||||
|
#!nix develop --impure --expr
|
||||||
|
#!nix ``
|
||||||
|
#!nix let flake = builtins.getFlake ("git+file://" + toString ../.);
|
||||||
|
#!nix pkgs = flake.inputs.nixpkgs.legacyPackages.${builtins.currentSystem};
|
||||||
|
#!nix in pkgs.mkShell { nativeBuildInputs = [
|
||||||
|
#!nix (pkgs.python3.withPackages (ps: with ps; [ requests ]))
|
||||||
|
#!nix ]; }
|
||||||
|
#!nix `` --command python3
|
||||||
|
|
||||||
|
# This script lists out the contributors for a given release.
|
||||||
|
# It must be run from the root of the Nix repository.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
github_token = os.environ.get("GITHUB_TOKEN")
|
||||||
|
if not github_token:
|
||||||
|
print("GITHUB_TOKEN is not set. If you hit the rate limit, set it", file=sys.stderr)
|
||||||
|
# Might be ok, as we have a cache.
|
||||||
|
# raise ValueError("GITHUB_TOKEN must be set")
|
||||||
|
|
||||||
|
# 1. Read the current version in .version
|
||||||
|
version = os.environ.get("VERSION")
|
||||||
|
if not version:
|
||||||
|
version = open(".version").read().strip()
|
||||||
|
|
||||||
|
print(f"Generating release credits for Nix {version}", file=sys.stderr)
|
||||||
|
|
||||||
|
# 2. Compute previous version
|
||||||
|
vcomponents = version.split(".")
|
||||||
|
if len(vcomponents) >= 2:
|
||||||
|
prev_version = f"{vcomponents[0]}.{int(vcomponents[1])-1}.0"
|
||||||
|
else:
|
||||||
|
raise ValueError(".version must have at least two components")
|
||||||
|
|
||||||
|
# For unreleased versions
|
||||||
|
endref = "HEAD"
|
||||||
|
# For older releases
|
||||||
|
# endref = version
|
||||||
|
|
||||||
|
# 2. Find the merge base between the current version and the previous version
|
||||||
|
mergeBase = os.popen(f"git merge-base {prev_version} {endref}").read().strip()
|
||||||
|
print(f"Merge base between {prev_version} and {endref} is {mergeBase}", file=sys.stderr)
|
||||||
|
|
||||||
|
# 3. Find the date of the merge base
|
||||||
|
mergeBaseDate = os.popen(f"git show -s --format=%ci {mergeBase}").read().strip()[0:10]
|
||||||
|
print(f"Merge base date is {mergeBaseDate}", file=sys.stderr)
|
||||||
|
|
||||||
|
# 4. Get the commits between the merge base and the current version
|
||||||
|
|
||||||
|
def get_commits():
|
||||||
|
raw = os.popen(f"git log --pretty=format:'%H\t%an\t%ae' {mergeBase}..{endref}").read().strip()
|
||||||
|
lines = raw.split("\n")
|
||||||
|
return [ { "hash": items[0], "author": items[1], "email": items[2] }
|
||||||
|
for line in lines
|
||||||
|
for items in (line.split("\t"),)
|
||||||
|
]
|
||||||
|
|
||||||
|
def commits_to_first_commit_by_email(commits):
|
||||||
|
by_email = dict()
|
||||||
|
for commit in commits:
|
||||||
|
email = commit["email"]
|
||||||
|
if email not in by_email:
|
||||||
|
by_email[email] = commit
|
||||||
|
return by_email
|
||||||
|
|
||||||
|
|
||||||
|
samples = commits_to_first_commit_by_email(get_commits())
|
||||||
|
|
||||||
|
# For quick testing, only pick two samples from the dict
|
||||||
|
# samples = dict(list(samples.items())[:2])
|
||||||
|
|
||||||
|
# Query the GitHub API to get handle
|
||||||
|
def get_github_commit(commit):
|
||||||
|
url = f"https://api.github.com/repos/NixOS/nix/commits/{commit['hash']}"
|
||||||
|
headers = {'Authorization': f'token {github_token}'}
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
class Cache:
|
||||||
|
def __init__(self, filename, require = True):
|
||||||
|
self.filename = filename
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
self.values = json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
if require:
|
||||||
|
raise
|
||||||
|
self.values = dict()
|
||||||
|
def save(self):
|
||||||
|
with open(self.filename, "w") as f:
|
||||||
|
json.dump(self.values, f, indent=4)
|
||||||
|
print(f"Saved cache to {self.filename}", file=sys.stderr)
|
||||||
|
|
||||||
|
# The email to handle cache maps email addresses to either
|
||||||
|
# - a handle (string)
|
||||||
|
# - None (if no handle was found)
|
||||||
|
email_to_handle_cache = Cache("maintainers/data/release-credits-email-to-handle.json")
|
||||||
|
|
||||||
|
handles = set()
|
||||||
|
emails = dict()
|
||||||
|
|
||||||
|
for sample in samples:
|
||||||
|
s = samples[sample]
|
||||||
|
email = s["email"]
|
||||||
|
if not email in email_to_handle_cache.values:
|
||||||
|
print(f"Querying GitHub API for {s['hash']}, to get handle for {s['email']}")
|
||||||
|
ghc = get_github_commit(samples[sample])
|
||||||
|
gha = ghc["author"]
|
||||||
|
if gha and gha["login"]:
|
||||||
|
handle = gha["login"]
|
||||||
|
print(f"Handle: {handle}")
|
||||||
|
email_to_handle_cache.values[email] = handle
|
||||||
|
else:
|
||||||
|
print(f"Found no handle for {s['email']}")
|
||||||
|
email_to_handle_cache.values[email] = None
|
||||||
|
handle = email_to_handle_cache.values[email]
|
||||||
|
if handle is not None:
|
||||||
|
handles.add(handle)
|
||||||
|
else:
|
||||||
|
emails[email] = s["author"]
|
||||||
|
|
||||||
|
# print(email_to_handle_cache.values)
|
||||||
|
|
||||||
|
email_to_handle_cache.save()
|
||||||
|
|
||||||
|
handle_to_name_cache = Cache("maintainers/data/release-credits-handle-to-name.json")
|
||||||
|
|
||||||
|
print(f"Found {len(handles)} handles", file=sys.stderr)
|
||||||
|
|
||||||
|
for handle in handles:
|
||||||
|
if not handle in handle_to_name_cache.values:
|
||||||
|
print(f"Querying GitHub API for {handle}, to get name", file=sys.stderr)
|
||||||
|
url = f"https://api.github.com/users/{handle}"
|
||||||
|
headers = {'Authorization': f'token {github_token}'}
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
response.raise_for_status()
|
||||||
|
user = response.json()
|
||||||
|
name = user["name"]
|
||||||
|
print(f"Name: {name}", file=sys.stderr)
|
||||||
|
handle_to_name_cache.values[handle] = name
|
||||||
|
|
||||||
|
handle_to_name_cache.save()
|
||||||
|
|
||||||
|
entries = list()
|
||||||
|
|
||||||
|
for handle in handles:
|
||||||
|
name = handle_to_name_cache.values[handle]
|
||||||
|
if name is None:
|
||||||
|
# This way it looks more regular
|
||||||
|
name = handle
|
||||||
|
entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ]
|
||||||
|
|
||||||
|
def shuffle(entries):
|
||||||
|
salt = os.urandom(16)
|
||||||
|
return sorted(entries, key=lambda x: hash((x, salt)))
|
||||||
|
|
||||||
|
# Fair ordering is undecidable
|
||||||
|
entries = shuffle(entries)
|
||||||
|
|
||||||
|
# For a sanity check, we could sort the entries by handle instead.
|
||||||
|
# entries = sorted(entries)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print(f"This release was made possible by the following {len(entries)} contributors:")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
for entry in entries:
|
||||||
|
print(entry)
|
||||||
|
|
||||||
|
for email in emails:
|
||||||
|
print(f"- {emails[email]}")
|
@ -151,6 +151,13 @@ section_title="Release $version_full ($DATE)"
|
|||||||
echo "# $section_title"
|
echo "# $section_title"
|
||||||
echo
|
echo
|
||||||
changelog-d doc/manual/rl-next | sed -e 's/ *$//'
|
changelog-d doc/manual/rl-next | sed -e 's/ *$//'
|
||||||
|
|
||||||
|
if ! $IS_PATCH; then
|
||||||
|
echo
|
||||||
|
echo "# Contributors"
|
||||||
|
echo
|
||||||
|
VERSION=$version_full ./maintainers/release-credits
|
||||||
|
fi
|
||||||
) | tee -a $file
|
) | tee -a $file
|
||||||
|
|
||||||
log "Wrote $file"
|
log "Wrote $file"
|
||||||
|
@ -39,6 +39,10 @@ release:
|
|||||||
* Proof-read / edit / rearrange the release notes if needed. Breaking changes
|
* Proof-read / edit / rearrange the release notes if needed. Breaking changes
|
||||||
and highlights should go to the top.
|
and highlights should go to the top.
|
||||||
|
|
||||||
|
* Run `maintainers/release-credits` to make sure the credits script works
|
||||||
|
and produces a sensible output. Some emails might not automatically map to
|
||||||
|
a GitHub handle.
|
||||||
|
|
||||||
* Push.
|
* Push.
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
Loading…
Reference in New Issue
Block a user