From f18a8fd409cd0759023c3cdee3c7202aae027fa7 Mon Sep 17 00:00:00 2001 From: Hellings Date: Fri, 23 Oct 2026 09:10:23 -0400 Subject: [PATCH] Scripts --- docs/install.md | 12 +++++ package.json | 3 +- scripts/forgejo-push.ts | 111 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 scripts/forgejo-push.ts diff --git a/docs/install.md b/docs/install.md index 08c4496..6aaf129 100644 --- a/docs/install.md +++ b/docs/install.md @@ -27,3 +27,15 @@ Preview without network writes. ```sh bun scripts/forgejo-modules.ts --dry-run ``` + +To commit changed integration module directories with generic `chore: update @muddle/ integration` messages and push their subtree history, preview the operation first. Unchanged modules are also synced so newly created Forgejo repositories receive their initial branch. + +```sh +bun scripts/forgejo-push.ts +``` + +Push only after review. This requires the same token and creates local commits. + +```sh +MUDDLE_FORGEJO_TOKEN=token bun scripts/forgejo-push.ts --push +``` diff --git a/package.json b/package.json index c0a7579..54792be 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "lint": "bunx eslint . --ignore-pattern node_modules --ignore-pattern ametrine --ignore-pattern .opencode --ignore-pattern dist --ignore-pattern .git --ignore-pattern .cache --ignore-pattern .next --ignore-pattern .turbo --ignore-pattern .vercel --ignore-pattern .vscode --ignore-pattern .idea", "format": "bun run cli.ts fmt .", "muddle": "bun run cli.ts", - "forgejo:modules": "bun scripts/forgejo-modules.ts --dry-run" + "forgejo:modules": "bun scripts/forgejo-modules.ts --dry-run", + "forgejo:push": "bun scripts/forgejo-push.ts" }, "bin": { "muddle": "./cli.ts" diff --git a/scripts/forgejo-push.ts b/scripts/forgejo-push.ts new file mode 100644 index 0000000..d8fb225 --- /dev/null +++ b/scripts/forgejo-push.ts @@ -0,0 +1,111 @@ +#!/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 = {}, +): Promise { + 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 { + 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 }); +}