mirror of
https://github.com/Nostalgica-Reverie/Content-Monorepo.git
synced 2026-05-09 00:24:15 +00:00
chore(repo): improve readmes and update licenses and mcmetas
This commit is contained in:
2
tools/README
Normal file
2
tools/README
Normal file
@@ -0,0 +1,2 @@
|
||||
# Tools
|
||||
Tools is intended for CI/CD to utilize, via TypeScript.
|
||||
204
tools/auto-update/pr.ts
Normal file
204
tools/auto-update/pr.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { execFileSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const FORGEJO_URL = 'https://git.nostalgica.net';
|
||||
const BRANCH = 'auto-update';
|
||||
const TARGET = 'main';
|
||||
const PR_TITLE = 'chore: auto-update mods';
|
||||
|
||||
const token = process.env.FORGEJO_TOKEN;
|
||||
const repo = process.env.GITHUB_REPOSITORY;
|
||||
|
||||
if (!token) {
|
||||
console.error('FORGEJO_TOKEN not set');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!repo) {
|
||||
console.error('GITHUB_REPOSITORY not set');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const apiBase = `${FORGEJO_URL}/api/v1/repos/${repo}`;
|
||||
|
||||
async function api(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: unknown,
|
||||
): Promise<Response> {
|
||||
const res = await fetch(`${apiBase}${path}`, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
interface ForgejoPR {
|
||||
number: number;
|
||||
state: string;
|
||||
head: { ref: string };
|
||||
base: { ref: string };
|
||||
title: string;
|
||||
}
|
||||
|
||||
async function findOpenPR(): Promise<ForgejoPR | null> {
|
||||
const owner = repo!.split('/')[0];
|
||||
const res = await api(
|
||||
'GET',
|
||||
`/pulls?state=open&head=${owner}:${BRANCH}&base=${TARGET}&limit=50`,
|
||||
);
|
||||
if (!res.ok) {
|
||||
throw new Error(`list PRs failed: ${res.status} ${await res.text()}`);
|
||||
}
|
||||
const prs = (await res.json()) as ForgejoPR[];
|
||||
const match = prs.find(
|
||||
(p) => p.head.ref === BRANCH && p.base.ref === TARGET && p.state === 'open',
|
||||
);
|
||||
return match ?? null;
|
||||
}
|
||||
|
||||
async function postComment(prNumber: number, body: string): Promise<void> {
|
||||
const res = await api('POST', `/issues/${prNumber}/comments`, { body });
|
||||
if (!res.ok) {
|
||||
throw new Error(`comment failed: ${res.status} ${await res.text()}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function createPR(body: string): Promise<number> {
|
||||
const res = await api('POST', '/pulls', {
|
||||
title: PR_TITLE,
|
||||
body,
|
||||
head: BRANCH,
|
||||
base: TARGET,
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`create PR failed: ${res.status} ${await res.text()}`);
|
||||
}
|
||||
const pr = (await res.json()) as ForgejoPR;
|
||||
return pr.number;
|
||||
}
|
||||
|
||||
async function updatePR(prNumber: number, body: string): Promise<void> {
|
||||
const res = await api('PATCH', `/pulls/${prNumber}`, { body });
|
||||
if (!res.ok) {
|
||||
throw new Error(`update PR failed: ${res.status} ${await res.text()}`);
|
||||
}
|
||||
}
|
||||
|
||||
function runModDiff(
|
||||
oldRef: string,
|
||||
newRef: string,
|
||||
pathPrefix: string,
|
||||
): string {
|
||||
const bin = process.env.MOD_DIFF_BIN || './updater-bin/mod-diff';
|
||||
if (!fs.existsSync(bin)) {
|
||||
console.warn(`mod-diff bin not found at ${bin}; skipping diff`);
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return execFileSync(bin, [oldRef, newRef, pathPrefix], {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`mod-diff failed for ${pathPrefix}: ${e instanceof Error ? e.message : e}`,
|
||||
);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function findModpackSubdirs(): Array<[string, string]> {
|
||||
const out: Array<[string, string]> = [];
|
||||
const root = 'modpacks';
|
||||
if (!fs.existsSync(root)) return out;
|
||||
|
||||
for (const pack of fs.readdirSync(root)) {
|
||||
const packPath = path.join(root, pack);
|
||||
if (!fs.statSync(packPath).isDirectory()) continue;
|
||||
for (const sub of fs.readdirSync(packPath)) {
|
||||
if (!/-mr$|-cf$/.test(sub)) continue;
|
||||
const subPath = path.join(packPath, sub);
|
||||
if (fs.statSync(subPath).isDirectory()) {
|
||||
out.push([`${pack}/${sub}`, subPath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildPRBody(): string {
|
||||
const sections: string[] = [];
|
||||
sections.push(
|
||||
'Automated PR from the auto update action.);
|
||||
|
||||
const subdirs = findModpackSubdirs();
|
||||
const diffSections: string[] = [];
|
||||
|
||||
for (const [label, subdir] of subdirs) {
|
||||
const md = runModDiff(`origin/${TARGET}`, 'HEAD', subdir);
|
||||
if (md) {
|
||||
diffSections.push(`### ${label}\n\n${md}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (diffSections.length === 0) {
|
||||
sections.push(
|
||||
'\n## Mod Updates\n\n_No mod version changes detected.',
|
||||
);
|
||||
} else {
|
||||
sections.push('\n## Mod Updates\n\n' + diffSections.join('\n\n'));
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
sections.push(`\n---\n_Last updated: ${timestamp}_`);
|
||||
|
||||
return sections.join('\n');
|
||||
}
|
||||
|
||||
async function prePush(): Promise<void> {
|
||||
const existing = await findOpenPR();
|
||||
if (!existing) {
|
||||
console.log('no existing open PR; nothing to announce.');
|
||||
return;
|
||||
}
|
||||
const timestamp = new Date().toISOString();
|
||||
await postComment(
|
||||
existing.number,
|
||||
`Force-pushing updated auto-update branch (${timestamp}).`,
|
||||
);
|
||||
console.log(`posted force-push notice to PR #${existing.number}`);
|
||||
}
|
||||
|
||||
async function postPush(): Promise<void> {
|
||||
const body = buildPRBody();
|
||||
const existing = await findOpenPR();
|
||||
if (existing) {
|
||||
await updatePR(existing.number, body);
|
||||
console.log(`updated PR #${existing.number}`);
|
||||
} else {
|
||||
const num = await createPR(body);
|
||||
console.log(`created PR #${num}`);
|
||||
}
|
||||
}
|
||||
|
||||
const phase = process.argv[2];
|
||||
(async () => {
|
||||
try {
|
||||
if (phase === 'pre-push') {
|
||||
await prePush();
|
||||
} else if (phase === 'post-push') {
|
||||
await postPush();
|
||||
} else {
|
||||
console.error('usage: tsx pr.ts <pre-push|post-push>');
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`pr.ts ${phase} failed:`, e instanceof Error ? e.message : e);
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user