Skip to content

Commit 5104baa

Browse files
committed
Merge branch 'tools/feat/maintainer-list'
2 parents a09c652 + ba9c13a commit 5104baa

File tree

7 files changed

+176
-5
lines changed

7 files changed

+176
-5
lines changed

.github/workflows/static-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ jobs:
4646
docker run --rm \
4747
-e CI_BASE_BRANCH \
4848
-e GITHUB_RUN_ID=${GITHUB_RUN_ID} \
49+
-e GITHUB_TOKEN=${GITHUB_TOKEN} \
4950
-v $(pwd):/data/riotbuild \
5051
riot/static-test-tools:latest \
5152
make static-test

MAINTAINING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ In case of security relevant backports (both bug fixes and reverts), the
181181
announcement can be skipped and the fix merged once at least two ACKs are
182182
there.
183183

184-
[list of maintainers]: https://github.com/RIOT-OS/RIOT/wiki/Maintainers
184+
[list of maintainers]: https://doc.riot-os.org/maintainer-list.html
185185
[Best Practices]: https://github.com/RIOT-OS/RIOT/wiki/Best-Practice-for-RIOT-Programming
186186
[Comparing build sizes]: https://github.com/RIOT-OS/RIOT/wiki/Comparing-build-sizes
187187
[Coding Conventions]: CODING_CONVENTIONS.md
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#! /usr/bin/env python3
2+
#
3+
# Copyright (C) 2024 TU Dresden
4+
#
5+
# This file is subject to the terms and conditions of the GNU Lesser
6+
# General Public License v2.1. See the file LICENSE in the top level
7+
# directory for more details.
8+
9+
__author__ = "Martine S. Lenders <[email protected]>"
10+
11+
import re
12+
import os
13+
import pathlib
14+
import sys
15+
import urllib.parse
16+
17+
import requests
18+
19+
20+
SCRIPT_PATH = pathlib.Path(__file__).resolve().absolute().parent
21+
RIOTBASE = (SCRIPT_PATH / ".." / ".." / "..").resolve()
22+
TOKEN = os.environ.get("GITHUB_TOKEN")
23+
24+
25+
NO_AREA_TEXT = "Has no chosen area of expertise."
26+
MAINTAINER_FILE_BOILERPLATE = f"""# Maintainer List {{#maintainer-list}}
27+
28+
This file contains the current list of maintainers within the RIOT community.
29+
The file is generated by combining the information from the Maintainers, Owners and
30+
Admin teams from the RIOT-OS GitHub organization and the
31+
[CODEOWNERS](https://github.com/RIOT-OS/RIOT/blob/master/CODEOWNERS) file.
32+
33+
If a maintainer is marked as "{NO_AREA_TEXT}", they did not have added any ownership
34+
within CODEOWNERS. This does not mean that they do not feel responsible for any part of
35+
the code base, they just did not declare it.
36+
37+
If you are a maintainer and want to declare ownership for a part of a code base (and
38+
receive notifications on pull requests against it), please add yourself and the path to
39+
the part of the code base you want to be responsible for to CODEOWNERS.
40+
"""
41+
42+
43+
def get_team_members(team):
44+
members = requests.get(
45+
f"https://api.github.com/orgs/RIOT-OS/teams/{team}/members",
46+
headers={
47+
"Accept": "application/vnd.github+json",
48+
"Authorization": f"Bearer {TOKEN}",
49+
"X-GitHub-Api-Version": "2022-11-28",
50+
},
51+
)
52+
try:
53+
return set(m["login"] for m in members.json())
54+
except Exception as exc:
55+
print(f"Error fetching team {team}: {exc}", file=sys.stderr)
56+
raise
57+
58+
59+
def get_github_user(username):
60+
user = requests.get(
61+
f"https://api.github.com/users/{username}",
62+
headers={
63+
"Accept": "application/vnd.github+json",
64+
"Authorization": f"Bearer {TOKEN}",
65+
"X-GitHub-Api-Version": "2022-11-28",
66+
},
67+
)
68+
return user.json()
69+
70+
71+
def get_maintainer_codeowner_patterns(maintainers):
72+
maintainer_patterns = {m: [] for m in maintainers}
73+
74+
with open(RIOTBASE / "CODEOWNERS") as codeowners_file:
75+
for line in codeowners_file:
76+
if re.search(r"^\s*#", line) or re.match(r"^\s*$", line):
77+
# skip comments and empty lines
78+
continue
79+
pattern, *owners = re.split(r"\s+", line.strip())
80+
for owner in owners:
81+
owner = owner.lstrip("@")
82+
if owner in maintainer_patterns:
83+
maintainer_patterns[owner].append(pattern)
84+
return maintainer_patterns
85+
86+
87+
def collect_maintainer_areas(maintainer_codeownership):
88+
maintainer_areas = {m: set() for m in maintainer_codeownership}
89+
for m in maintainer_areas:
90+
if not maintainer_areas[m] and maintainer_codeownership[m]:
91+
for rule in maintainer_codeownership[m]:
92+
maintainer_areas[m].add(rule)
93+
return maintainer_areas
94+
95+
96+
def create_maintainer_markdown_file(maintainer_areas, owners, admins):
97+
with open(
98+
RIOTBASE / "doc" / "doxygen" / "src" / "maintainers.md", "w"
99+
) as maintainers_file:
100+
print(MAINTAINER_FILE_BOILERPLATE, file=maintainers_file)
101+
for i, maintainer in enumerate(
102+
sorted(maintainer_areas, key=lambda x: x.lower())
103+
):
104+
github_profile = get_github_user(maintainer)
105+
106+
if (
107+
not github_profile["name"]
108+
or github_profile["name"] == github_profile["login"]
109+
):
110+
title = f"\\@{github_profile['login']}"
111+
else:
112+
title = f"{github_profile['name']} (\\@{github_profile['login']})"
113+
anchor = urllib.parse.quote(github_profile["login"])
114+
print(f"## {title} {{#{anchor}}}", file=maintainers_file)
115+
print(
116+
f"[GitHub profile]({github_profile['html_url']})",
117+
file=maintainers_file,
118+
)
119+
120+
if maintainer in owners:
121+
print(
122+
"- **Is one of the GitHub owners of RIOT.**", file=maintainers_file
123+
)
124+
125+
if maintainer in admins:
126+
print(
127+
"- **Is one of the GitHub admins of RIOT.**", file=maintainers_file
128+
)
129+
130+
for area in sorted(
131+
maintainer_areas[maintainer],
132+
):
133+
print(
134+
f"- `{area.lstrip('/')}`",
135+
file=maintainers_file
136+
)
137+
if not maintainer_areas[maintainer]:
138+
print("", file=maintainers_file)
139+
print(NO_AREA_TEXT, file=maintainers_file)
140+
if (i + 1) < len(maintainer_areas):
141+
print("", file=maintainers_file)
142+
143+
144+
def main():
145+
if not TOKEN:
146+
print(
147+
"Please provide a sufficient GitHub token in `GITHUB_TOKEN` "
148+
"environment variable",
149+
file=sys.stderr,
150+
)
151+
sys.exit(1)
152+
maintainers = get_team_members("maintainers")
153+
admins = get_team_members("admin")
154+
owners = get_team_members("owners")
155+
maintainers = maintainers.union(admins)
156+
maintainers = maintainers.union(owners)
157+
maintainer_codeownership = get_maintainer_codeowner_patterns(maintainers)
158+
maintainer_areas = collect_maintainer_areas(maintainer_codeownership)
159+
create_maintainer_markdown_file(maintainer_areas, owners, admins)
160+
161+
162+
if __name__ == "__main__":
163+
main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
requests==2.32.3

doc/doxygen/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
src/css/variables.less
22
src/changelog.md
3+
src/maintainers.md

doc/doxygen/Makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ doc: $(DOCUMENTATION_FORMAT)
1616

1717
# by marking html as phony we force make to re-run Doxygen even if the directory exists.
1818
.PHONY: html
19-
html: src/changelog.md
19+
html: src/changelog.md src/maintainers.md
2020
( cat riot.doxyfile ; echo "GENERATE_HTML = yes" ) | doxygen -
2121
@echo ""
2222
@echo "RIOT documentation successfully generated at file://$(RIOTBASE)/doc/doxygen/html/index.html"
2323

2424
.PHONY: check
25-
check: src/changelog.md
25+
check: src/changelog.md src/maintainers.md
2626
( cat riot.doxyfile) | doxygen -
2727

2828
.PHONY: man
29-
man: src/changelog.md
29+
man: src/changelog.md src/maintainers.md
3030
( cat riot.doxyfile ; echo "GENERATE_MAN = yes" ) | doxygen -
3131

3232
src/css/riot.css: src/css/riot.less src/css/variables.less
@@ -39,8 +39,12 @@ src/css/variables.less: src/config.json
3939
src/changelog.md: src/changelog.md.tmp ../../release-notes.txt
4040
@./generate-changelog.py $+ $@
4141

42+
src/maintainers.md: ../../dist/tools/maintainer-list/maintainer-list.py ../../dist/tools/maintainer-list/requirements.txt ../../CODEOWNERS
43+
-@pip install --upgrade -r ../../dist/tools/maintainer-list/requirements.txt
44+
-@../../dist/tools/maintainer-list/maintainer-list.py
45+
4246
.PHONY:
43-
latex: src/changelog.md
47+
latex: src/changelog.md src/maintainers.md
4448
( cat riot.doxyfile ; echo "GENERATE_LATEX= yes" ) | doxygen -
4549

4650
clean:

doc/doxygen/riot.doxyfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ INPUT = ../../doc.txt \
888888
src/release-cycle.md \
889889
src/io-mapping-and-shields.md \
890890
src/changelog.md \
891+
src/maintainers.md \
891892
../../LOSTANDFOUND.md \
892893
../../makefiles/pseudomodules.inc.mk \
893894
../../makefiles/blob.inc.mk \

0 commit comments

Comments
 (0)