mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
The census now asks for every Hanzo organization this GitHub account belongs to, and it counts an organization's forks, because a tracked fork is a project whose main we follow. zeekay and zooqueen are read from GitHub's own contribution count for the past year. scripts/forge-mirrors.mjs prints the organizations forge.hanzo.ai does not have yet and, with FORGE_TOKEN, pull-mirrors the public repositories with GitHub kept as the source. Co-authored-by: z <[email protected]>
105 lines
3.8 KiB
JavaScript
105 lines
3.8 KiB
JavaScript
/**
|
|
* GitHub is canonical. forge.hanzo.ai is where those repositories are browsed
|
|
* and where issues are kept. This compares the accounts the census asks for
|
|
* (lib/data/oss.json `asked`) with the organizations the forge already has.
|
|
*
|
|
* Without FORGE_TOKEN it prints the gap and exits 0. A marketing build must
|
|
* not need a forge credential.
|
|
*
|
|
* With FORGE_TOKEN and --apply it creates a missing organization and pulls
|
|
* each of its public GitHub repositories across as a mirror (GitHub stays the
|
|
* source; the forge copy follows it). Issues, pull requests, labels,
|
|
* milestones, releases and the wiki come with the mirror.
|
|
*
|
|
* FORGE_TOKEN=… node scripts/forge-mirrors.mjs --apply
|
|
*/
|
|
import { readFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const FORGE = "https://forge.hanzo.ai/v1";
|
|
const token = process.env.FORGE_TOKEN;
|
|
const apply = process.argv.includes("--apply");
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const census = JSON.parse(readFileSync(join(here, "..", "lib", "data", "oss.json"), "utf8"));
|
|
const asked = census.asked ?? census.orgs.map((o) => o.name);
|
|
|
|
async function forgeOrgs() {
|
|
const names = [];
|
|
for (let page = 1; ; page++) {
|
|
const res = await fetch(`${FORGE}/orgs?limit=50&page=${page}`);
|
|
if (!res.ok) throw new Error(`forge orgs ${res.status}`);
|
|
const rows = await res.json();
|
|
if (!Array.isArray(rows) || !rows.length) break;
|
|
names.push(...rows.map((o) => o.username));
|
|
if (rows.length < 50) break;
|
|
}
|
|
return new Set(names);
|
|
}
|
|
|
|
async function forgeRepoCount(org) {
|
|
let n = 0;
|
|
for (let page = 1; ; page++) {
|
|
const res = await fetch(`${FORGE}/orgs/${org}/repos?limit=50&page=${page}`);
|
|
if (!res.ok) return null;
|
|
const rows = await res.json();
|
|
if (!Array.isArray(rows) || !rows.length) break;
|
|
n += rows.length;
|
|
if (rows.length < 50) break;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
const have = await forgeOrgs();
|
|
const missing = asked.filter((name) => name !== "zeekay" && !have.has(name));
|
|
console.log(`forge has ${have.size} organizations; ${missing.length} Hanzo organizations are not there`);
|
|
if (missing.length) console.log(missing.join("\n"));
|
|
for (const org of asked.filter((name) => have.has(name))) {
|
|
const onForge = await forgeRepoCount(org);
|
|
const onGitHub = census.orgs.find((o) => o.name === org)?.repos ?? 0;
|
|
if (onForge !== onGitHub) console.log(`${org}: forge ${onForge ?? "unread"} repositories, GitHub ${onGitHub}`);
|
|
}
|
|
|
|
if (!apply) {
|
|
console.log("dry run. FORGE_TOKEN and --apply create the missing organizations and pull-mirror their public GitHub repositories.");
|
|
process.exit(0);
|
|
}
|
|
if (!token) {
|
|
console.error("FORGE_TOKEN is not set");
|
|
process.exit(1);
|
|
}
|
|
|
|
const headers = { authorization: `token ${token}`, "content-type": "application/json", accept: "application/json" };
|
|
|
|
for (const org of missing) {
|
|
if (org === "zeekay") continue;
|
|
const res = await fetch(`${FORGE}/orgs`, { method: "POST", headers, body: JSON.stringify({ username: org, visibility: "public" }) });
|
|
console.log(`create ${org}: ${res.status}`);
|
|
}
|
|
|
|
const github = census.public.filter((full) => asked.some((org) => full.startsWith(`${org}/`)));
|
|
for (const full of github) {
|
|
const [owner, repo] = full.split("/");
|
|
const there = await fetch(`${FORGE}/repos/${owner}/${repo}`, { headers });
|
|
if (there.ok) continue;
|
|
const res = await fetch(`${FORGE}/repos/migrate`, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify({
|
|
clone_addr: `https://github.com/${full}`,
|
|
repo_owner: owner,
|
|
repo_name: repo,
|
|
service: "github",
|
|
mirror: true,
|
|
mirror_interval: "8h",
|
|
issues: true,
|
|
pull_requests: true,
|
|
labels: true,
|
|
milestones: true,
|
|
releases: true,
|
|
wiki: true,
|
|
}),
|
|
});
|
|
console.log(`mirror ${full}: ${res.status}`);
|
|
}
|