-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathparse-maintainer.py
More file actions
executable file
·97 lines (80 loc) · 3.71 KB
/
Copy pathparse-maintainer.py
File metadata and controls
executable file
·97 lines (80 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python3
"""GitHub issue form submission → maintainer JSON"""
import json, os, re, subprocess, sys
from datetime import datetime
from pathlib import Path
QUESTIONS = [
"How to support",
"A small brief about your project",
"One FOSS maintainer lesson for your younger self",
"Why do you do it? Why do you bother maintaining a FOSS project?",
"If your repo had a theme song, what would it be?",
"Which file in your project would you most like to set on fire?",
"What's your open-source villain origin story?",
"If you had to use one emoji to convey what it is like to be a FOSS maintainer, what would it be?",
]
LABEL_MAP = {
"github": "GitHub", "Github": "GitHub", "gitlab": "GitLab", "Gitlab": "GitLab",
"codeberg": "Codeberg", "bitbucket": "BitBucket", "linkedin": "LinkedIn",
"Linkedin": "LinkedIn", "mastodon": "Mastodon", "bluesky": "Bluesky",
"substack": "Substack", "twitter": "Twitter", "rss": "RSS",
"web": "Web", "website": "Web", "blog": "Web", "reddit": "Reddit",
"medium": "Medium", "youtube": "Youtube", "YouTube": "Youtube", "email": "Email",
}
PROJ_FIELDS = ("name", "project_link", "website_link", "logo", "short_description", "description")
def sections(md):
parts = re.split(r"^### (.+)$", md, flags=re.MULTILINE)
return {parts[i].strip(): parts[i+1].strip() for i in range(1, len(parts), 2) if i+1 < len(parts)}
def parse_socials(text):
result = []
for line in text.splitlines():
line = line.lstrip("- ").strip()
if ":" in line and not line.startswith("#"):
label, _, link = line.partition(":")
if link := link.strip():
result.append({"label": LABEL_MAP.get(label.strip(), label.strip()), "link": link})
return result
def parse_projects(text):
projects = []
for block in re.findall(r"project:\s*\n((?:^- .+\n?)+)", text, re.MULTILINE):
proj = {f: "" for f in PROJ_FIELDS}
for line in block.splitlines():
if line.startswith("- "):
key, _, val = line[2:].partition(":")
if key.strip() in proj:
proj[key.strip()] = val.strip()
if proj["name"]:
projects.append(proj)
return projects
def parse(md):
s = sections(md)
return {
"username": s.get("GitHub Username", ""),
"full_name": s.get("Full Name", ""),
"photo": s.get("Photo URL", ""),
"designation": s.get("Designation / Role", ""),
"socials": parse_socials(s.get("Socials", "")),
"projects": parse_projects(s.get("Projects", "")),
"form": [{"question": q, "response": s.get(q, "")} for q in QUESTIONS],
"created_on": datetime.now().astimezone().isoformat(),
}
if __name__ == "__main__":
if os.getenv("CI") == "true":
sys.exit(0)
if len(sys.argv) < 2:
print("Usage: python parse-maintainer.py <input.md>")
sys.exit(1)
result = parse(Path(sys.argv[1]).read_text(encoding="utf-8"))
username = result["username"] or "output"
out = f"content/maintainers/{username}.json"
Path(out).write_text(json.dumps(result, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
print(json.dumps(result, indent=2, ensure_ascii=False))
try:
subprocess.run(["check-jsonschema", "--schemafile", "maintainer.schema.json", out], check=True)
print(f"✓ saved to {out}", file=sys.stderr)
except (subprocess.CalledProcessError, FileNotFoundError):
print("[WARN] schema validation skipped or failed", file=sys.stderr)
try:
subprocess.run(["python3", "sync-image.py", f"{username}.json"], check=True)
except subprocess.CalledProcessError as e:
print(f"[WARN] image sync failed: {e}", file=sys.stderr)