Skip to content

Commit da62fbb

Browse files
committed
Replace contrib.rocks URL with self-hosted SVG + weekly regen workflow
Approach: - Store the contributor avatar grid as a real file at docs/source/_static/contributors.svg (committed). - README references the in-repo raw URL, so rendering only depends on GitHub itself (no third-party image service at view time). - .github/workflows/contributors.yml regenerates the SVG weekly via a scheduled workflow (Monday 06:00 UTC, plus manual workflow_dispatch). It uses actions/github-script with the GitHub REST contributors API to build the SVG inline, then opens a PR via peter-evans/create-pull-request when the file changes. - No third-party runtime services; all third-party actions are pinned to commit SHA (matching existing repo conventions in zizmor.yml/welcome.yml). - zizmor clean; all prek hooks pass on the modified files. Rationale: contrib.rocks was found to stall/time out for this repo on first request, and cached refreshes are slow/unreliable (2+ weeks per upstream maintainer discussion). This approach delivers the originally requested "weekly auto-triggered PR" workflow with no ongoing human maintenance beyond approving the bot PR. Made-with: Cursor
1 parent 9aaccd9 commit da62fbb

3 files changed

Lines changed: 289 additions & 1 deletion

File tree

.github/workflows/contributors.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Update contributors image
2+
3+
on:
4+
schedule:
5+
# Every Monday at 06:00 UTC
6+
- cron: "0 6 * * 1"
7+
workflow_dispatch: {}
8+
9+
permissions: {}
10+
11+
jobs:
12+
update:
13+
name: Regenerate contributors.svg
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: write
17+
pull-requests: write
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21+
with:
22+
persist-credentials: false
23+
24+
- name: Generate contributors SVG
25+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
26+
with:
27+
script: |
28+
const fs = require('fs');
29+
const path = require('path');
30+
31+
// Layout: circular avatars in a grid, matching the visual style
32+
// used by the main pymc repo README.
33+
const AVATAR = 64; // rendered diameter in px
34+
const GAP = 8; // spacing between avatars
35+
const COLS = 12; // avatars per row
36+
const PAD = 4; // outer padding so circles are not clipped
37+
const BOT_PATTERN = /\[bot\]$|^dependabot$|^renovate$|^renovate-bot$/i;
38+
39+
core.info(`Fetching contributors for ${context.repo.owner}/${context.repo.repo}`);
40+
const contributors = await github.paginate(
41+
github.rest.repos.listContributors,
42+
{ owner: context.repo.owner, repo: context.repo.repo, per_page: 100 }
43+
);
44+
core.info(`Fetched ${contributors.length} contributor records (pre-filter)`);
45+
46+
const people = contributors.filter(
47+
(c) => c && c.type === 'User' && c.login && !BOT_PATTERN.test(c.login)
48+
);
49+
core.info(`Rendering ${people.length} human contributors`);
50+
51+
const rows = Math.max(1, Math.ceil(people.length / COLS));
52+
const width = PAD * 2 + COLS * AVATAR + (COLS - 1) * GAP;
53+
const height = PAD * 2 + rows * AVATAR + (rows - 1) * GAP;
54+
55+
const xmlEscape = (s) =>
56+
String(s).replace(/[&<>"']/g, (c) => ({
57+
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
58+
})[c]);
59+
60+
// Request a sized avatar from GitHub to keep the SVG small.
61+
const avatarUrl = (c) => {
62+
const base = (c.avatar_url || '').split('?')[0];
63+
return base ? `${base}?s=${AVATAR * 2}` : '';
64+
};
65+
66+
const cells = people.map((c, i) => {
67+
const col = i % COLS;
68+
const row = Math.floor(i / COLS);
69+
const x = PAD + col * (AVATAR + GAP);
70+
const y = PAD + row * (AVATAR + GAP);
71+
const r = AVATAR / 2;
72+
const cx = x + r;
73+
const cy = y + r;
74+
const clipId = `c${i}`;
75+
const href = xmlEscape(`https://github.com/${c.login}`);
76+
const img = xmlEscape(avatarUrl(c));
77+
const title = xmlEscape(c.login);
78+
return [
79+
` <a href="${href}" target="_blank" rel="noopener">`,
80+
` <title>${title}</title>`,
81+
` <clipPath id="${clipId}"><circle cx="${cx}" cy="${cy}" r="${r}" /></clipPath>`,
82+
` <image href="${img}" x="${x}" y="${y}" width="${AVATAR}" height="${AVATAR}" clip-path="url(#${clipId})" preserveAspectRatio="xMidYMid slice" />`,
83+
` </a>`,
84+
].join('\n');
85+
}).join('\n');
86+
87+
const svg = [
88+
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="CausalPy contributors">`,
89+
` <title>CausalPy contributors (${people.length})</title>`,
90+
cells,
91+
`</svg>`,
92+
'',
93+
].join('\n');
94+
95+
const outPath = path.join('docs', 'source', '_static', 'contributors.svg');
96+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
97+
fs.writeFileSync(outPath, svg);
98+
core.info(`Wrote ${outPath} (${svg.length} bytes)`);
99+
100+
- name: Create pull request if changed
101+
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
102+
with:
103+
token: ${{ secrets.GITHUB_TOKEN }}
104+
commit-message: "Update contributors image"
105+
title: "Update contributors image"
106+
body: |
107+
Automated refresh of `docs/source/_static/contributors.svg`.
108+
109+
Generated by `.github/workflows/contributors.yml` from the GitHub
110+
contributors API. Bot accounts are filtered out.
111+
112+
Safe to merge once the preview looks right.
113+
branch: bot/update-contributors
114+
delete-branch: true
115+
add-paths: docs/source/_static/contributors.svg
116+
labels: documentation

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,8 @@ Plans for the repository can be seen in the [Issues](https://github.com/pymc-lab
170170

171171
## Thanks to our contributors
172172

173-
[![Contributors](https://contrib.rocks/image?repo=pymc-labs/CausalPy)](https://github.com/pymc-labs/CausalPy/graphs/contributors)
173+
<a href="https://github.com/pymc-labs/CausalPy/graphs/contributors">
174+
<img src="https://raw.githubusercontent.com/pymc-labs/CausalPy/main/docs/source/_static/contributors.svg" alt="CausalPy contributors" />
175+
</a>
176+
177+
The contributor image is regenerated weekly by a GitHub Actions workflow (`.github/workflows/contributors.yml`) that opens a PR when the contributor list changes.
Lines changed: 168 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)