diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml new file mode 100644 index 00000000..50c8502e --- /dev/null +++ b/.github/workflows/update-contributors.yml @@ -0,0 +1,44 @@ +name: Update contributors + +on: + push: + branches: [main] + schedule: + # GitHub's contributors API is cached and lags a merge by up to ~24h, so a + # weekly pass catches anyone the push-triggered run was too early to see. + - cron: '17 4 * * 1' + workflow_dispatch: + +# Only one run at a time; a burst of merges must not race on README.md. +concurrency: + group: update-contributors + cancel-in-progress: false + +jobs: + update: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Regenerate the contributor wall + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: python3 tools/update-contributors.py + + - name: Commit if it changed + run: | + git config user.name "mukul975" + git config user.email "mukuljangra5@gmail.com" + git add README.md + if git diff --staged --quiet; then + echo "No new contributors." + exit 0 + fi + git commit -m "chore: update contributor wall" + # Another workflow (update-index) may have pushed while this ran. + git pull --rebase --autostash origin main + git push diff --git a/README.md b/README.md index 65d74ae3..dbaaaf0d 100644 --- a/README.md +++ b/README.md @@ -404,18 +404,25 @@ This project follows the [Contributor Covenant](https://www.contributor-covenant This library is built by the community. Thank you to everyone who has contributed: +
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
Ordered by contribution count · see the full contributor graph
+13 contributors, ordered by contribution count · see the full contributor graph
+ ## Community diff --git a/tools/update-contributors.py b/tools/update-contributors.py new file mode 100644 index 00000000..ce3debb8 --- /dev/null +++ b/tools/update-contributors.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +"""Regenerate the contributor avatar wall in README.md from the GitHub API. + +Writes between the markers: + + + ...generated... + + +Avatars are served from github.com/']
+ for person in people:
+ login = person["login"]
+ count = person.get("contributions", 0)
+ plural = "" if count == 1 else "s"
+ title = f"{login} — maintainer" if login == MAINTAINER else f"{login} — {count} contribution{plural}"
+ lines.append(
+ f''
+ f'
'
+ )
+ lines.append("
{len(people)} contributors, ordered by contribution count · ' + f'see the full contributor graph' + "
" + ) + return "\n".join(lines) + + +def splice(readme: str, block: str) -> str: + if START not in readme or END not in readme: + raise SystemExit( + f"ERROR: {README} is missing the {START} / {END} markers. " + "Add them around the contributor wall." + ) + head, rest = readme.split(START, 1) + _, tail = rest.split(END, 1) + return f"{head}{START}\n{block}\n{END}{tail}" + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", + help="exit 1 if README.md is out of date; do not write") + args = parser.parse_args() + + if not os.path.isfile(README): + print(f"ERROR: {README} not found. Run from the repository root.") + return 1 + + people = fetch_contributors() + if not people: + print("ERROR: the API returned no contributors; refusing to blank the section.") + return 1 + + current = open(README, encoding="utf-8").read() + updated = splice(current, render(people)) + + if updated == current: + print(f"OK: contributor wall is current ({len(people)} contributors)") + return 0 + + if args.check: + print(f"ERROR: contributor wall is out of date ({len(people)} contributors). " + "Run: python tools/update-contributors.py") + return 1 + + with open(README, "w", encoding="utf-8", newline="") as handle: + handle.write(updated) + print(f"Updated contributor wall: {len(people)} contributors") + return 0 + + +if __name__ == "__main__": + sys.exit(main())