Skip to content

Commit f47731a

Browse files
update GraphQL helper
1 parent c6805d7 commit f47731a

File tree

1 file changed

+66
-34
lines changed

1 file changed

+66
-34
lines changed

scripts/update_stats.cjs

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
* Update the org profile README stats block using GitHub GraphQL.
44
*
55
* Env:
6-
* ORG - required, your org login ("kir-rescomp")
7-
* GH_TOKEN - a token; PAT with read:org + repo if you want private repos included,
8-
* otherwise the default GITHUB_TOKEN works for public.
6+
* ORG - org login (default: "kir-rescomp")
7+
* GH_TOKEN - PAT (read:org + repo) to include private repos,
8+
* else falls back to GITHUB_TOKEN (public only)
99
*/
1010
const fs = require("fs");
1111
const path = require("path");
@@ -18,14 +18,15 @@ if (!GH_TOKEN) {
1818
process.exit(1);
1919
}
2020

21-
// Tweak filters:
21+
// Filters (tweak as you like)
2222
const INCLUDE_FORKS = false;
2323
const INCLUDE_ARCHIVED = false;
24-
// null => both public & private; or "PUBLIC" / "PRIVATE"
24+
// null => both; "PUBLIC" or "PRIVATE" to restrict
2525
const PRIVACY = null;
2626

2727
const README_PATH = path.join(process.cwd(), "profile", "README.md");
2828

29+
// --- GraphQL helper ---------------------------------------------------------
2930
const gql = (query, variables) =>
3031
new Promise((resolve, reject) => {
3132
const data = JSON.stringify({ query, variables });
@@ -47,7 +48,14 @@ const gql = (query, variables) =>
4748
res.on("end", () => {
4849
try {
4950
const json = JSON.parse(body);
50-
if (json.errors) return reject(json.errors);
51+
if (!String(res.statusCode).startsWith("2")) {
52+
return reject(
53+
new Error(
54+
`HTTP ${res.statusCode}: ${JSON.stringify(json.errors || json)}`
55+
)
56+
);
57+
}
58+
if (json.errors) return reject(new Error(JSON.stringify(json.errors)));
5159
resolve(json.data);
5260
} catch (e) {
5361
reject(e);
@@ -60,12 +68,20 @@ const gql = (query, variables) =>
6068
req.end();
6169
});
6270

71+
// --- Query + pagination ------------------------------------------------------
6372
const REPO_PAGE_SIZE = 50;
6473

6574
const REPOS_QUERY = `
6675
query($org:String!, $after:String, $isFork:Boolean, $privacy:RepositoryPrivacy, $pageSize:Int!) {
6776
organization(login: $org) {
68-
repositories(first: $pageSize, after: $after, isFork: $isFork, privacy: $privacy, ownerAffiliations: OWNER, orderBy:{field:NAME, direction:ASC}) {
77+
repositories(
78+
first: $pageSize,
79+
after: $after,
80+
isFork: $isFork,
81+
privacy: $privacy,
82+
ownerAffiliations: OWNER,
83+
orderBy:{field:NAME, direction:ASC}
84+
) {
6985
pageInfo { hasNextPage endCursor }
7086
nodes {
7187
name
@@ -97,6 +113,7 @@ query($org:String!, $after:String, $isFork:Boolean, $privacy:RepositoryPrivacy,
97113
async function fetchAllRepos() {
98114
let after = null;
99115
const all = [];
116+
// eslint-disable-next-line no-constant-condition
100117
while (true) {
101118
const data = await gql(REPOS_QUERY, {
102119
org: ORG,
@@ -116,21 +133,24 @@ async function fetchAllRepos() {
116133
return all;
117134
}
118135

119-
function formatNumber(n) {
120-
return new Intl.NumberFormat("en-GB").format(n);
121-
}
136+
// --- Rendering helpers -------------------------------------------------------
137+
const fmt = (n) => new Intl.NumberFormat("en-GB").format(n);
122138

123139
function shields(label, value, color, opts = {}) {
124140
const { logo, link } = opts;
125-
const src = `https://img.shields.io/badge/${encodeURIComponent(label)}-${encodeURIComponent(String(value))}-${color}?style=for-the-badge${logo ? `&logo=${encodeURIComponent(logo)}` : ""}`;
141+
const src = `https://img.shields.io/badge/${encodeURIComponent(label)}-${encodeURIComponent(
142+
String(value)
143+
)}-${color}?style=for-the-badge${logo ? `&logo=${encodeURIComponent(logo)}` : ""}`;
126144
const img = `<img alt="${label} - ${value}" src="${src}" />`;
127145
return link ? `<a href="${link}">${img}</a>` : img;
128146
}
129147

130148
function renderBadgesHTML(stats) {
131-
const fmt = (n) => new Intl.NumberFormat("en-GB").format(n);
132149
return [
133-
shields("Repos", fmt(stats.repoCount), "0a84ff", { logo: "github", link: `https://github.com/${ORG}?tab=repositories` }),
150+
shields("Repos", fmt(stats.repoCount), "0a84ff", {
151+
logo: "github",
152+
link: `https://github.com/${ORG}?tab=repositories`,
153+
}),
134154
shields("Commits", fmt(stats.totalCommits), "10b981"),
135155
shields("Issues (open)", fmt(stats.openIssues), "f59e0b"),
136156
shields("PRs (open)", fmt(stats.openPRs), "8b5cf6"),
@@ -152,20 +172,10 @@ function renderMarkdown(stats, topRepos) {
152172
forks,
153173
} = stats;
154174

155-
const fmt = (n) => new Intl.NumberFormat("en-GB").format(n);
156-
157-
const badges = [
158-
renderBadge("Repos", fmt(repoCount), "0a84ff", "github", `https://github.com/${ORG}?tab=repositories`),
159-
renderBadge("Commits", fmt(totalCommits), "10b981"),
160-
renderBadge("Issues (open)", fmt(openIssues), "f59e0b"),
161-
renderBadge("PRs (open)", fmt(openPRs), "8b5cf6"),
162-
renderBadge("Stars", fmt(stars), "14b8a6", "github"),
163-
renderBadge("Forks", fmt(forks), "06b6d4", "github"),
164-
].join(" ");
165-
166175
const lines = [];
167176
lines.push(`### 📊 Organisation Stats for **${ORG}**`);
168177
lines.push("");
178+
// HTML badges (centered). Markdown isn’t parsed inside HTML, so we use <img>.
169179
lines.push(`<p align="center">${renderBadgesHTML(stats)}</p>`);
170180
lines.push("");
171181
lines.push(`<table>`);
@@ -176,9 +186,15 @@ function renderMarkdown(stats, topRepos) {
176186
lines.push(`</thead>`);
177187
lines.push(`<tbody>`);
178188
lines.push(`<tr><td>📦 Repositories</td><td align="right"><code>${fmt(repoCount)}</code></td></tr>`);
179-
lines.push(`<tr><td>🧭 Commits (default branches)</td><td align="right"><code>${fmt(totalCommits)}</code></td></tr>`);
189+
lines.push(
190+
`<tr><td>🧭 Commits (default branches)</td><td align="right"><code>${fmt(
191+
totalCommits
192+
)}</code></td></tr>`
193+
);
180194
lines.push(`<tr><td>🐞 Issues — Open</td><td align="right"><code>${fmt(openIssues)}</code></td></tr>`);
181-
lines.push(`<tr><td>✅ Issues — Closed</td><td align="right"><code>${fmt(closedIssues)}</code></td></tr>`);
195+
lines.push(
196+
`<tr><td>✅ Issues — Closed</td><td align="right"><code>${fmt(closedIssues)}</code></td></tr>`
197+
);
182198
lines.push(`<tr><td>🔁 PRs — Open</td><td align="right"><code>${fmt(openPRs)}</code></td></tr>`);
183199
lines.push(`<tr><td>🧹 PRs — Closed</td><td align="right"><code>${fmt(closedPRs)}</code></td></tr>`);
184200
lines.push(`<tr><td>🎉 PRs — Merged</td><td align="right"><code>${fmt(mergedPRs)}</code></td></tr>`);
@@ -187,7 +203,9 @@ function renderMarkdown(stats, topRepos) {
187203
lines.push(`</tbody>`);
188204
lines.push(`</table>`);
189205
lines.push("");
190-
lines.push(`<sub>Updated: ${new Date().toISOString().replace("T", " ").replace("Z", " UTC")}</sub>`);
206+
lines.push(
207+
`<sub>Updated: ${new Date().toISOString().replace("T", " ").replace("Z", " UTC")}</sub>`
208+
);
191209
lines.push("");
192210

193211
if (topRepos.length) {
@@ -198,7 +216,9 @@ function renderMarkdown(stats, topRepos) {
198216
lines.push(`|---|---:|---:|---:|---:|---:|`);
199217
for (const r of topRepos) {
200218
lines.push(
201-
`| [${r.name}](https://github.com/${ORG}/${r.name}) | ${fmt(r.commits)} | ${fmt(r.issuesOpen)} | ${fmt(r.prsOpen)} | ${fmt(r.stars)} | ${fmt(r.forks)} |`
219+
`| [${r.name}](https://github.com/${ORG}/${r.name}) | ${fmt(r.commits)} | ${fmt(
220+
r.issuesOpen
221+
)} | ${fmt(r.prsOpen)} | ${fmt(r.stars)} | ${fmt(r.forks)} |`
202222
);
203223
}
204224
lines.push(`</details>`);
@@ -208,7 +228,6 @@ function renderMarkdown(stats, topRepos) {
208228
return lines.join("\n");
209229
}
210230

211-
212231
function replaceStatsSection(readme, newBlock) {
213232
const start = "<!-- ORG-STATS:START -->";
214233
const end = "<!-- ORG-STATS:END -->";
@@ -218,15 +237,21 @@ function replaceStatsSection(readme, newBlock) {
218237
return readme.replace(pattern, replacement);
219238
}
220239

240+
// --- Main --------------------------------------------------------------------
221241
(async () => {
222242
try {
223243
const repos = await fetchAllRepos();
224244

225245
const aggregated = {
226-
repoCount: 0, totalCommits: 0,
227-
openIssues: 0, closedIssues: 0,
228-
openPRs: 0, closedPRs: 0, mergedPRs: 0,
229-
stars: 0, forks: 0,
246+
repoCount: 0,
247+
totalCommits: 0,
248+
openIssues: 0,
249+
closedIssues: 0,
250+
openPRs: 0,
251+
closedPRs: 0,
252+
mergedPRs: 0,
253+
stars: 0,
254+
forks: 0,
230255
};
231256

232257
const perRepo = [];
@@ -251,7 +276,14 @@ function replaceStatsSection(readme, newBlock) {
251276
aggregated.stars += stars;
252277
aggregated.forks += forks;
253278

254-
perRepo.push({ name: r.name, commits, issuesOpen, prsOpen, stars, forks });
279+
perRepo.push({
280+
name: r.name,
281+
commits,
282+
issuesOpen,
283+
prsOpen,
284+
stars,
285+
forks,
286+
});
255287
}
256288

257289
perRepo.sort((a, b) => b.commits - a.commits);

0 commit comments

Comments
 (0)