111 lines
2.7 KiB
TypeScript
111 lines
2.7 KiB
TypeScript
#!/usr/bin/env bun
|
|
import { chmod, rm, writeFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
|
|
const root = resolve(import.meta.dir, "..");
|
|
const modules = [
|
|
"amezing",
|
|
"analysis",
|
|
"astro",
|
|
"database",
|
|
"latex",
|
|
"mdx",
|
|
"optimize",
|
|
"prettier-plugin",
|
|
"shadcn",
|
|
"tailwind",
|
|
];
|
|
const api = (process.env.MUDDLE_FORGEJO_URL ?? "https://git.crit.rip").replace(
|
|
/\/$/,
|
|
"",
|
|
);
|
|
const owner = process.env.MUDDLE_FORGEJO_OWNER ?? "mdl";
|
|
const arguments_ = process.argv.slice(2);
|
|
const push = arguments_.includes("--push");
|
|
const requested = arguments_.filter((argument) => !argument.startsWith("--"));
|
|
const selected = requested.length > 0 ? requested : modules;
|
|
const token = process.env.MUDDLE_FORGEJO_TOKEN;
|
|
|
|
if (push && !token) {
|
|
throw new Error(
|
|
"Set MUDDLE_FORGEJO_TOKEN before using --push.",
|
|
);
|
|
}
|
|
for (const name of selected) {
|
|
if (!modules.includes(name)) {
|
|
throw new Error(`Unknown integration module ${name}`);
|
|
}
|
|
}
|
|
|
|
async function run(
|
|
command: string[],
|
|
environment: Record<string, string | undefined> = {},
|
|
): Promise<string> {
|
|
const child = Bun.spawn(command, {
|
|
cwd: root,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
env: { ...process.env, ...environment },
|
|
});
|
|
const [stdout, stderr, status] = await Promise.all([
|
|
new Response(child.stdout).text(),
|
|
new Response(child.stderr).text(),
|
|
child.exited,
|
|
]);
|
|
if (status !== 0) {
|
|
throw new Error(`${command.join(" ")} failed\n${stderr || stdout}`);
|
|
}
|
|
return stdout.trim();
|
|
}
|
|
|
|
async function changed(path: string): Promise<boolean> {
|
|
const tracked = await run(["git", "status", "--porcelain", "--", path]);
|
|
return tracked.length > 0;
|
|
}
|
|
|
|
let askPass: string | undefined;
|
|
try {
|
|
if (push && token) {
|
|
askPass = join(import.meta.dir, `.forgejo-askpass-${process.pid}`);
|
|
await writeFile(
|
|
askPass,
|
|
`#!/bin/sh\ncase "$1" in *Username*) printf '%s' token ;; *) printf '%s' "$MUDDLE_FORGEJO_TOKEN" ;; esac\n`,
|
|
);
|
|
await chmod(askPass, 0o700);
|
|
}
|
|
|
|
for (const name of selected) {
|
|
const prefix = `modules/${name}`;
|
|
const hasChanges = await changed(prefix);
|
|
const message = `chore: update @muddle/${name} integration`;
|
|
if (!push) {
|
|
console.log(
|
|
`would ${hasChanges ? `commit with "${message}" and ` : ""}sync @muddle/${name} to ${api}/${owner}/${name}.git`,
|
|
);
|
|
continue;
|
|
}
|
|
if (hasChanges) {
|
|
await run(["git", "add", "--", prefix]);
|
|
await run(["git", "commit", "-m", message, "--", prefix]);
|
|
}
|
|
const branch = await run([
|
|
"git",
|
|
"subtree",
|
|
"split",
|
|
"--prefix",
|
|
prefix,
|
|
]);
|
|
await run([
|
|
"git",
|
|
"push",
|
|
`${api}/${owner}/${name}.git`,
|
|
`${branch}:refs/heads/main`,
|
|
], {
|
|
GIT_ASKPASS: askPass,
|
|
GIT_TERMINAL_PROMPT: "0",
|
|
});
|
|
console.log(`pushed @muddle/${name}`);
|
|
}
|
|
} finally {
|
|
if (askPass) await rm(askPass, { force: true });
|
|
}
|