commit 5c0f1bf848b802c7c754b1f775602203b2180660
parent 0a951783f3bacb96c122bd5627ea59be64b53869
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Tue, 6 Oct 2026 12:31:50 -0400
ops: pnpm ops publish (a verb in the body) and pnpm ops get publish; the eight build/deploy actions named as its aliases
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 49 insertions(+), 8 deletions(-)
diff --git a/scripts/archilyzer-ops.mjs b/scripts/archilyzer-ops.mjs
@@ -88,11 +88,14 @@ const GETTERS = {
// single tag's assignments, each carrying the provenance of the pin.
tags: (tag) =>
tag ? `/api/ops/tags?tag=${encodeURIComponent(tag)}` : "/api/ops/tags",
+ // The publish status (release 18): the index, the lane, a row per site, the
+ // hub and the homepage with their chips, and the plan Publish now would run.
+ publish: () => "/api/ops/publish",
};
// Nouns whose read takes no argument. `get channel` without a slug is a
// mistake; `get tags` without one is the whole vocabulary.
-const GET_ARG_OPTIONAL = new Set(["tags"]);
+const GET_ARG_OPTIONAL = new Set(["tags", "publish"]);
const ACTIONS = [
"channel-priority",
@@ -117,15 +120,19 @@ const ACTIONS = [
"fetch-posts",
// A screenshot and the attached media of specific archived posts.
"capture-posts",
+ // Publishing as stages (release 18): {"verb": "index" | "build" | "deploy" |
+ // "hub" | "homepage" | "now" | "stale", …} — one run of stages on the
+ // editor's publish queue. The eight rows below are its aliases.
+ "publish",
"build-index",
"build-deploy",
"build-site",
- // Deploy the ALREADY-BUILT export/out — build-deploy's other half, and the
- // one a preview is for: build once, look at the preview, then ship the same
- // bundle to production without rebuilding it.
+ // Deploy the site's ALREADY-BUILT bundle — build-deploy's other half, and
+ // the one a preview is for: build once, look at the preview, then ship the
+ // same bundle to production without rebuilding it.
"deploy-site",
- // The HUB (the export app in hub mode, into the same export/out a site
- // build uses) and its deploy to homepage.json's Pages project.
+ // The HUB (the export app in hub mode, into its own bundle) and its deploy
+ // to homepage.json's Pages project.
"build-hub",
"deploy-hub",
// The HOMEPAGE (the `homepage` package, Archilyzer's own site, into
@@ -321,6 +328,7 @@ export function usage() {
" [--wait-timeout <seconds>] [--quiet]",
" pnpm ops get channel <slug> [--counts]",
" pnpm ops get tags [<tagId>]",
+ " pnpm ops get publish",
" pnpm ops list",
"",
`Actions: ${ACTIONS.join(", ")}`,
@@ -331,6 +339,22 @@ export function usage() {
"--wait-timeout <seconds> gives up and exits 1 instead of waiting forever.",
" Default: no timeout — the queue may legitimately hold a job for hours.",
"",
+ 'publish runs publish stages on the editor\'s publish queue, one at a time,',
+ ' under one run id: {"verb": …}. "index" updates the index; "build" builds',
+ ' "siteId"/"siteIds" (forced; the index first when stale; "runner":',
+ ' "docker" builds every site in containers); "deploy" ships their built',
+ ' bundles (production, "preview": "<branch>", or "to": "local"; "force"',
+ ' redeploys a bundle already shipped there); "hub" / "homepage" build',
+ ' them, {"deploy": true} deploys after; "now" is Publish now (the stale',
+ ' index, then each policy target); "stale" builds every stale site. The',
+ ' answer lists every job ({target, kind, jobId}) and --wait follows them',
+ ' all. A site never built is refused: "no build of <id> in <dir> —',
+ ' archilyzer publish build <id>". `get publish` is the status.',
+ "",
+ 'build-index, build-site, build-deploy, deploy-site, build-hub, deploy-hub,',
+ ' build-homepage and deploy-homepage are publish\'s aliases, with their old',
+ ' bodies and answers ("skipData" is accepted and ignored).',
+ "",
'build-site, build-deploy and deploy-site all take "siteId" (one) or',
' "siteIds" (a list).',
"",
@@ -390,7 +414,7 @@ export function usage() {
" alone. The alias is printed after the response. Lowercase letters,",
' digits and dashes, up to 28 characters; "main" is refused.',
"",
- 'build-hub builds the hub into export/out; {"deploy": true} deploys it',
+ 'build-hub builds the hub into its bundle; {"deploy": true} deploys it',
' after, and deploy-hub ships the one already built. Both deploy to the',
" Pages project set on /sites under Hub, and take \"preview\" too.",
"",
diff --git a/scripts/archilyzer-ops.test.mjs b/scripts/archilyzer-ops.test.mjs
@@ -359,7 +359,24 @@ test("build-hub and deploy-hub are POSTs to their own routes, named in the usage
assert.equal(deploy.path, "/api/ops/deploy-hub");
assert.deepEqual(deploy.body, {});
assert.match(usage(), /Actions:.*build-hub, deploy-hub/);
- assert.match(usage(), /build-hub builds the hub into export\/out/);
+ assert.match(usage(), /build-hub builds the hub into its bundle/);
+});
+
+// Publishing as stages (release 18): one route, a verb in the body; the status
+// is a read.
+test("publish is a POST to its route, and get publish the status, named in the usage", () => {
+ const p = parseArgs(["publish", "--json", '{"verb":"deploy","siteId":"jer","preview":"r18"}', "--wait"]);
+ assert.equal(p.method, "POST");
+ assert.equal(p.path, "/api/ops/publish");
+ assert.deepEqual(p.body, { verb: "deploy", siteId: "jer", preview: "r18" });
+ assert.equal(p.wait, true);
+ const g = parseArgs(["get", "publish"]);
+ assert.equal(g.method, "GET");
+ assert.equal(g.path, "/api/ops/publish");
+ assert.match(usage(), /Actions:.*capture-posts, publish, build-index/);
+ assert.match(usage(), /pnpm ops get publish/);
+ assert.match(usage(), /"now" is Publish now/);
+ assert.match(usage(), /publish's aliases, with their old/);
});
// The homepage's pair (release 11 slice O4), the hub's twins: a POST each, the