From 263e6e081ab5cfa98f9b040ad4cc175d1148f716 Mon Sep 17 00:00:00 2001 From: Sven Date: Tue, 31 Mar 2026 16:24:24 +0200 Subject: [PATCH] feat: dispatch Gitea workflow_dispatch after each Micropub action Gitea Contents API commits don't trigger on:push CI workflows. Patches action.js to fire a workflow_dispatch to giersig.eu/indiekit-blog after every create/update/delete/undelete so the Eleventy build runs immediately after a post is published. Co-Authored-By: Claude Sonnet 4.6 --- package.json | 4 +- scripts/patch-micropub-gitea-dispatch.mjs | 102 ++++++++++++++++++++++ 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 scripts/patch-micropub-gitea-dispatch.mjs diff --git a/package.json b/package.json index 840b3f1e..8fd83570 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "index.js", "scripts": { "preinstall": "node scripts/setup-gitea-url-rewrite.mjs", - "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-store-github-error-message.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-bluesky-syndicator-media-type-guard.mjs && node scripts/patch-ap-skip-draft-syndication.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-ap-webfinger-before-auth.mjs && node scripts/patch-ap-federation-bridge-base-url.mjs && node scripts/patch-ap-compose-default-checked.mjs && node scripts/patch-ap-mastodon-reply-threading.mjs", - "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-store-github-error-message.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-microsub-compose-draft-guard.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-bluesky-syndicator-media-type-guard.mjs && node scripts/patch-ap-skip-draft-syndication.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-ap-webfinger-before-auth.mjs && node scripts/patch-ap-federation-bridge-base-url.mjs && node scripts/patch-ap-compose-default-checked.mjs && node scripts/patch-ap-mastodon-reply-threading.mjs && node --require ./metrics-shim.cjs node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs", + "postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-store-github-error-message.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-bluesky-syndicator-media-type-guard.mjs && node scripts/patch-ap-skip-draft-syndication.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-ap-webfinger-before-auth.mjs && node scripts/patch-ap-federation-bridge-base-url.mjs && node scripts/patch-ap-compose-default-checked.mjs && node scripts/patch-ap-mastodon-reply-threading.mjs && node scripts/patch-micropub-gitea-dispatch.mjs", + "serve": "export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-github-contributions-log.mjs && node scripts/patch-store-github-error-message.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-microsub-compose-draft-guard.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-syndicate-normalize-syndication-array.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-micropub-session-token.mjs && node scripts/patch-indiekit-endpoint-urls-protocol.mjs && node scripts/patch-webmention-sender-hentry-syntax.mjs && node scripts/patch-webmention-sender-retry.mjs && node scripts/patch-webmention-sender-livefetch.mjs && node scripts/patch-webmention-sender-empty-details.mjs && node scripts/patch-bluesky-syndicator-internal-url.mjs && node scripts/patch-bluesky-syndicator-media-type-guard.mjs && node scripts/patch-ap-skip-draft-syndication.mjs && node scripts/patch-ap-og-image.mjs && node scripts/patch-ap-webfinger-before-auth.mjs && node scripts/patch-ap-federation-bridge-base-url.mjs && node scripts/patch-ap-compose-default-checked.mjs && node scripts/patch-ap-mastodon-reply-threading.mjs && node scripts/patch-micropub-gitea-dispatch.mjs && node --require ./metrics-shim.cjs node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], diff --git a/scripts/patch-micropub-gitea-dispatch.mjs b/scripts/patch-micropub-gitea-dispatch.mjs new file mode 100644 index 00000000..374f72b6 --- /dev/null +++ b/scripts/patch-micropub-gitea-dispatch.mjs @@ -0,0 +1,102 @@ +/** + * Patch: fire a Gitea workflow_dispatch after each Micropub create/update/delete/undelete. + * + * Gitea Contents API commits (what store-github does) do NOT trigger `on: push` + * CI workflows. This patch fires a workflow_dispatch to the blog repo after each + * successful Micropub action so the Eleventy build runs immediately. + * + * Env vars read at runtime (all have defaults): + * GITEA_BASE_URL Internal Gitea API base, e.g. http://10.100.0.90:3000/api/v1/ + * GITEA_CONTENT_USER Org/user that owns the blog repo (e.g. giersig.eu) + * GITEA_CONTENT_REPO Blog repo name (e.g. indiekit-blog) + * GH_CONTENT_TOKEN Gitea PAT with Actions write permission + * GITEA_DISPATCH_WORKFLOW Workflow filename, default deploy.yml + * GITEA_DISPATCH_REF Branch to dispatch on, default main + */ + +import { access, readFile, writeFile } from "node:fs/promises"; + +const TARGET = "node_modules/@indiekit/endpoint-micropub/lib/controllers/action.js"; +const MARKER = "// [patch] micropub-gitea-dispatch"; + +const HELPER = `${MARKER} +async function _dispatchGiteaBuild() { + try { + const base = (process.env.GITEA_BASE_URL || "http://10.100.0.90:3000/api/v1/").replace(/\\/+$/, ""); + const owner = process.env.GITEA_CONTENT_USER || "giersig.eu"; + const repo = process.env.GITEA_CONTENT_REPO || "indiekit-blog"; + const token = process.env.GH_CONTENT_TOKEN || process.env.GITHUB_TOKEN || ""; + const workflow = process.env.GITEA_DISPATCH_WORKFLOW || "deploy.yml"; + const ref = process.env.GITEA_DISPATCH_REF || "main"; + if (!token) { console.warn("[micropub-gitea-dispatch] No token — skipping dispatch"); return; } + const url = \`\${base}/repos/\${owner}/\${repo}/actions/workflows/\${workflow}/dispatches\`; + const res = await fetch(url, { + method: "POST", + headers: { "Authorization": \`token \${token}\`, "Content-Type": "application/json" }, + body: JSON.stringify({ ref }), + }); + if (!res.ok) { + const text = await res.text().catch(() => ""); + console.warn(\`[micropub-gitea-dispatch] Dispatch failed \${res.status}: \${text}\`); + } else { + console.log(\`[micropub-gitea-dispatch] Dispatched \${workflow} on \${owner}/\${repo}@\${ref}\`); + } + } catch (err) { + console.warn("[micropub-gitea-dispatch] Dispatch error:", err.message); + } +} +`; + +// Inject the dispatch call before the final response line, and only when +// content was actually written (i.e. content is defined and has a status). +const OLD_RESPONSE = ` response + .status(content.status) + .location(content.location) + .json(content.json);`; + +const NEW_RESPONSE = ` _dispatchGiteaBuild().catch(() => {}); + response + .status(content.status) + .location(content.location) + .json(content.json);`; + +async function exists(filePath) { + try { await access(filePath); return true; } catch { return false; } +} + +if (!(await exists(TARGET))) { + console.log(`[postinstall] micropub-gitea-dispatch: target not found (${TARGET}), skipping`); + process.exit(0); +} + +const source = await readFile(TARGET, "utf8"); + +if (source.includes(MARKER)) { + console.log("[postinstall] micropub-gitea-dispatch: already patched"); + process.exit(0); +} + +if (!source.includes(OLD_RESPONSE)) { + console.warn("[postinstall] micropub-gitea-dispatch: response snippet not found — skipping"); + process.exit(0); +} + +// Insert helper block after the last import statement +const allImportMatches = [...source.matchAll(/^import\s/gm)]; +let insertAt = 0; +if (allImportMatches.length > 0) { + const lastImportStart = allImportMatches.at(-1).index; + const afterLastImport = source.slice(lastImportStart); + const fromMatch = afterLastImport.match(/from\s+["'][^"']+["']\s*;\s*\n/); + if (fromMatch) { + insertAt = lastImportStart + fromMatch.index + fromMatch[0].length; + } +} + +const updated = + source.slice(0, insertAt) + + "\n" + HELPER + "\n" + + source.slice(insertAt).replace(OLD_RESPONSE, NEW_RESPONSE); + +await writeFile(TARGET, updated, "utf8"); +console.log(`[postinstall] micropub-gitea-dispatch: patched ${TARGET}`);