update_contributors.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. #!/usr/bin/env python3
  2. import subprocess
  3. from collections import defaultdict
  4. from pathlib import Path
  5. # Path to CONTRIBUTORS.md (in repository root)
  6. contributors_file = Path(__file__).parent.parent / "CONTRIBUTORS.md"
  7. def run_git(command):
  8. """Run a git command and return the output as a list of lines."""
  9. try:
  10. result = subprocess.run(
  11. ["git"] + command, capture_output=True, text=True, check=True
  12. )
  13. return result.stdout.strip().split("\n")
  14. except subprocess.CalledProcessError as e:
  15. print(f"❌ Error: Git command failed. Make sure you're in a Git repository.")
  16. print(f" Command: git {' '.join(command)}")
  17. print(f" Error: {e.stderr}")
  18. raise SystemExit(1)
  19. except FileNotFoundError:
  20. print("❌ Error: Git is not installed or not found in PATH.")
  21. raise SystemExit(1)
  22. def get_contributors():
  23. """Extract contributors from git log."""
  24. log_lines = run_git(["log", "--format=%aN|%aE|%ad", "--date=short"])
  25. contributors = defaultdict(lambda: {"emails": set(), "first": None, "last": None, "count": 0})
  26. for line in log_lines:
  27. if "|" not in line:
  28. continue
  29. name, email, date = line.split("|")
  30. name, email = name.strip(), email.strip()
  31. info = contributors[name]
  32. info["emails"].add(email)
  33. info["count"] += 1
  34. if not info["first"] or date < info["first"]:
  35. info["first"] = date
  36. if not info["last"] or date > info["last"]:
  37. info["last"] = date
  38. return contributors
  39. def generate_table(contributors):
  40. """Generate markdown table."""
  41. header = [
  42. "# 🌍 Project Contributors",
  43. "",
  44. "This file lists all contributors automatically extracted from Git commit history.",
  45. "Do not edit manually — run `python scripts/update_contributors.py` to refresh.",
  46. "",
  47. "| Name | Email | Contributions | First Commit | Last Commit | Reference |",
  48. "|------|--------|----------------|---------------|--------------|-----------|",
  49. ]
  50. rows = []
  51. for name, info in sorted(contributors.items(), key=lambda x: x[0].lower()):
  52. emails = ", ".join(sorted(info["emails"]))
  53. # Default empty reference link
  54. reference = ""
  55. # Use proper pluralization for commits
  56. commit_text = f"{info['count']} commit" if info['count'] == 1 else f"{info['count']} commits"
  57. rows.append(f"| {name} | {emails} | {commit_text} | {info['first']} | {info['last']} | {reference} |")
  58. return "\n".join(header + rows) + "\n"
  59. def main():
  60. contributors = get_contributors()
  61. md = generate_table(contributors)
  62. contributors_file.write_text(md, encoding="utf-8")
  63. print(f"✅ Updated {contributors_file} with {len(contributors)} contributors.")
  64. if __name__ == "__main__":
  65. main()