mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(docs): keep the owner, aliases and tool identities out of the acknowledgments table
This commit is contained in:
1 parent
ac681f524d
commit
16933b79d4
8 files changed
+114
-39
No files matched your search
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ownerAliases": ["yhh"],
|
||||
"nonPersonIdentities": ["reasonix", "merge-order-check"]
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
// plain with a warning; a lookup GitHub could not answer fails by code.
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
export const defaultRepository = "esengine/DeepSeek-Reasonix";
|
||||
|
||||
@@ -42,6 +43,20 @@ export function creditExclusions(repository = defaultRepository, env = process.e
|
||||
return new Set(names.filter(Boolean));
|
||||
}
|
||||
|
||||
// The owner's anonymous-commit aliases and the tool or CI identities that author
|
||||
// commits without being people; declared once in credit-identities.json.
|
||||
export function declaredNonCredits() {
|
||||
const declared = JSON.parse(readFileSync(new URL("./credit-identities.json", import.meta.url), "utf8"));
|
||||
const names = [...declared.ownerAliases, ...declared.nonPersonIdentities];
|
||||
return new Set(names.map((name) => String(name).trim().toLowerCase()).filter(Boolean));
|
||||
}
|
||||
|
||||
// creditExclusions plus the declared aliases and tool identities, for lists that
|
||||
// thank people by name.
|
||||
export function thanksExclusions(repository = defaultRepository, env = process.env) {
|
||||
return new Set([...creditExclusions(repository, env), ...declaredNonCredits()]);
|
||||
}
|
||||
|
||||
export function tokenFromEnvironment(env = process.env, runGh = defaultRunGh) {
|
||||
const token = env.GH_TOKEN || env.GITHUB_TOKEN;
|
||||
if (token) return token;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { isBotAccount, thanksExclusions } from './release-credits.mjs';
|
||||
|
||||
const repo = 'esengine/DeepSeek-Reasonix';
|
||||
const api = `https://api.github.com/repos/${repo}/contributors?per_page=30&anon=1`;
|
||||
const api = `https://api.github.com/repos/${repo}/contributors?per_page=50&anon=1`;
|
||||
const startMarker = '<!-- reasonix-top-contributors:start -->';
|
||||
const endMarker = '<!-- reasonix-top-contributors:end -->';
|
||||
|
||||
@@ -14,29 +16,33 @@ if (process.env.GITHUB_TOKEN) {
|
||||
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
||||
}
|
||||
|
||||
const res = await fetch(api, { headers });
|
||||
if (!res.ok) {
|
||||
throw new Error(`GitHub contributors API failed: ${res.status} ${res.statusText}`);
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
const res = await fetch(api, { headers });
|
||||
if (!res.ok) {
|
||||
throw new Error(`GitHub contributors API failed: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
const contributors = await res.json();
|
||||
if (!Array.isArray(contributors) || contributors.length === 0) {
|
||||
throw new Error('GitHub contributors API returned no contributors');
|
||||
}
|
||||
|
||||
const top = selectTop(contributors, thanksExclusions(repo));
|
||||
await updateReadme('README.md', renderTable(top));
|
||||
await updateReadme('README.zh-CN.md', renderTable(top));
|
||||
}
|
||||
|
||||
const contributors = await res.json();
|
||||
if (!Array.isArray(contributors) || contributors.length === 0) {
|
||||
throw new Error('GitHub contributors API returned no contributors');
|
||||
export function selectTop(contributors, excluded, limit = 20) {
|
||||
return contributors
|
||||
.filter((c) => c && typeof c.login === 'string' && c.login !== '' && typeof c.html_url === 'string' && c.html_url !== '')
|
||||
.filter((c) => !isBotAccount(c.login, c.type) && !excluded.has(c.login.toLowerCase()))
|
||||
.map((c) => ({ login: c.login, url: c.html_url, commits: Number(c.contributions) || 0 }))
|
||||
.sort((a, b) => b.commits - a.commits || a.login.localeCompare(b.login))
|
||||
.slice(0, limit)
|
||||
.map((row, index) => ({ rank: index + 1, ...row }));
|
||||
}
|
||||
|
||||
const top = contributors.filter((c) => c.type !== 'Bot').slice(0, 20).map((c, index) => ({
|
||||
rank: index + 1,
|
||||
login: typeof c.login === 'string' ? c.login : '',
|
||||
name: typeof c.name === 'string' ? c.name : '',
|
||||
url: typeof c.html_url === 'string' ? c.html_url : '',
|
||||
type: typeof c.type === 'string' ? c.type : '',
|
||||
commits: Number(c.contributions) || 0,
|
||||
}));
|
||||
|
||||
await updateReadme('README.md', renderTable(top));
|
||||
await updateReadme('README.zh-CN.md', renderTable(top));
|
||||
|
||||
function renderTable(rows) {
|
||||
export function renderTable(rows) {
|
||||
const header = '| Contributor | Contributor | Contributor | Contributor |\n| --- | --- | --- | --- |';
|
||||
const cells = rows.map((row) => renderContributor(row));
|
||||
const tableRows = [];
|
||||
@@ -52,12 +58,7 @@ function renderTable(rows) {
|
||||
}
|
||||
|
||||
function renderContributor(row) {
|
||||
const label = row.login || row.name || `anonymous-${row.rank}`;
|
||||
const escaped = escapeMarkdown(label);
|
||||
if (row.url) {
|
||||
return `[**${escaped}**](${row.url})`;
|
||||
}
|
||||
return `**${escaped}**`;
|
||||
return `[**${escapeMarkdown(row.login)}**](${row.url})`;
|
||||
}
|
||||
|
||||
async function updateReadme(path, replacement) {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { renderTable, selectTop } from "./update-acknowledgments.mjs";
|
||||
import { thanksExclusions } from "./release-credits.mjs";
|
||||
|
||||
const person = (login, contributions) => ({ login, type: "User", contributions, html_url: `https://github.com/${login}` });
|
||||
|
||||
function fixture() {
|
||||
const rows = [
|
||||
person("esengine", 900),
|
||||
{ type: "Anonymous", name: "yhh", email: "", contributions: 800 },
|
||||
{ type: "Anonymous", name: "reasonix", email: "", contributions: 700 },
|
||||
{ type: "Anonymous", name: "merge-order-check", email: "", contributions: 600 },
|
||||
{ login: "dependabot[bot]", type: "Bot", contributions: 500, html_url: "https://github.com/apps/dependabot" },
|
||||
{ type: "Anonymous", name: "Some Person", email: "", contributions: 450 },
|
||||
];
|
||||
for (let i = 0; i < 24; i += 1) rows.push(person(`user${String(i).padStart(2, "0")}`, 400 - i));
|
||||
rows.push(person("zed", 400 - 23), person("amy", 400 - 23));
|
||||
return rows;
|
||||
}
|
||||
|
||||
const excluded = thanksExclusions("esengine/DeepSeek-Reasonix", {});
|
||||
|
||||
test("owner, alias, tool identity, bot and unlinked anonymous entries are left out", () => {
|
||||
const top = selectTop(fixture(), excluded);
|
||||
const labels = top.map((row) => row.login);
|
||||
for (const gone of ["esengine", "yhh", "reasonix", "merge-order-check", "dependabot[bot]", "Some Person"]) {
|
||||
assert.ok(!labels.includes(gone), gone);
|
||||
}
|
||||
assert.ok(top.every((row) => row.login && row.url));
|
||||
});
|
||||
|
||||
test("the cap applies after filtering, so the table stays 20 long", () => {
|
||||
const top = selectTop(fixture(), excluded);
|
||||
assert.equal(top.length, 20);
|
||||
assert.deepEqual(top.map((row) => row.rank), Array.from({ length: 20 }, (_, i) => i + 1));
|
||||
assert.equal(top[0].login, "user00");
|
||||
});
|
||||
|
||||
test("order is by commits, ties by login, whatever order the API answered in", () => {
|
||||
const rows = [person("b", 5), person("a", 5), person("top", 9)];
|
||||
assert.deepEqual(selectTop(rows, excluded).map((row) => row.login), ["top", "a", "b"]);
|
||||
assert.deepEqual(selectTop([...rows].reverse(), excluded), selectTop(rows, excluded));
|
||||
});
|
||||
|
||||
test("a declared name is excluded case-insensitively, as a login too", () => {
|
||||
const rows = [person("ESENGINE", 9), person("Reasonix", 8), person("ok", 1)];
|
||||
assert.deepEqual(selectTop(rows, excluded).map((row) => row.login), ["ok"]);
|
||||
});
|
||||
|
||||
test("rendering is idempotent and a pure function of the rows", () => {
|
||||
const table = renderTable(selectTop(fixture(), excluded));
|
||||
assert.equal(table, renderTable(selectTop(fixture(), excluded)));
|
||||
assert.equal(table.split("\n").length, 2 + 5 + 2);
|
||||
});
|
||||
Reference in new issue
Block a user