commit f92e6ff93ac98827463f17d9acfc338289d09c2c
parent aaf4258a99b76432ffc6624a203336d6347c61fc
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 02:42:17 -0400
editor, common: the build-mode copy says the mode is a label (review S2)
No build path reads `buildPipeline.mode`: Build all / Build & deploy all
fan out in containers whenever `docker version` answers and build serially
on the host otherwise, and a single site always builds in export/ on one
queue. Copy only, no behaviour change (the parent's ruling); whether Build
all should honour the mode is a question for the operator.
- /sites: the toggle's note ("Docker builds are a follow-up — runs the
basic build for now", docker mode only) is now shown in both modes and
says the mode is a label; Build all's paragraph and lane subtitle no
longer claim "Docker mode is off — serial host fallback" (the
`dockerMode` prop that only picked that copy is gone); the per-site
panel's note says these run one at a time whichever mode is set.
- Settings → Build pipeline hint and the Max parallel builds hint; the
schema's buildPipeline description, `mode` and `maxParallelBuilds` docs
and the BuildMode comment (SETTINGS.md regenerated).
- deploy-page.spec.ts: the two asserted sentences, in step.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
8 files changed, 53 insertions(+), 45 deletions(-)
diff --git a/SETTINGS.md b/SETTINGS.md
@@ -556,14 +556,14 @@ Default:
## `buildPipeline`
-How the static export is built: "basic" reuses the single export/ tree and serializes builds on one queue (the long-standing behavior); "docker" builds every site at once, each in its own container (Dockerfile.build), then deploys them serially, and falls back to the basic build when no container engine answers — see PUBLISH.md. This block persists the chosen mode plus the image and concurrency knobs that pipeline reads.
+The build pipeline's settings. A single site builds in the shared export/ tree, serialized on one queue. Build all sites (and Build & deploy all) builds every site at once, each in its own container (Dockerfile.build, the image and maxParallelBuilds below), then deploys them serially, whenever a container engine answers, and serially on the host when none does — see PUBLISH.md. `mode` is persisted and shown on /sites, but no build path reads it today: it is a label, not a switch.
#### `buildPipeline`
| Key | Default | Description |
|---|---|---|
-| `mode` | `"basic"` | "basic" — `pnpm run build` in export/, serialized on the build queue (shared output tree, no safe parallelism). "docker" — isolated per-site container builds, parallel up to `maxParallelBuilds`. |
-| `maxParallelBuilds` | `2` | Cap on concurrent per-site container builds in docker mode. Ignored in basic mode (which is always serial). Clamped to [1, BUILD_MAX_PARALLEL_MAX]. |
+| `mode` | `"basic"` | "basic" or "docker". Persisted and shown on /sites, but no build path reads it today — a label, not a switch: Build all sites uses containers whenever a container engine answers, and builds serially on the host when none does, in either mode. |
+| `maxParallelBuilds` | `2` | Cap on concurrent per-site container builds when Build all sites runs in containers (whenever a container engine answers, whatever `mode` says). Clamped to [1, BUILD_MAX_PARALLEL_MAX]. |
| `dockerImage` | `"yt-dlp-transcript-browser-build"` | Tag of the reusable build image (built once, reused for every site). |
| `dockerfile` | `"Dockerfile.build"` | Dockerfile path relative to the monorepo root, used to (re)build the image. |
diff --git a/common/lib/settingsSchema.ts b/common/lib/settingsSchema.ts
@@ -414,11 +414,12 @@ export const DIGEST_SETTINGS_FIELD_DOCS: FieldDocs<DigestSettings> = {
"fresh. Empty = default.",
};
-// "basic" — `pnpm run build` in export/, serialized on the build queue (shared
-// output tree → no safe parallelism).
-// "docker" — isolated per-site container builds (publish/build.ts,
-// runDockerBuildAllPhase): real parallel multi-site builds capped by
-// maxParallelBuilds.
+// "basic" | "docker" — persisted and shown on /sites, but A LABEL TODAY: no
+// build path reads it. Build all / Build & deploy all (editor buildAction.ts)
+// fan out in containers (publish/build.ts, runDockerBuildAllPhase, capped by
+// maxParallelBuilds) whenever `docker version` answers, and build serially on
+// the host otherwise, whichever mode is set. Whether they should honour it is
+// an open question for the operator (plans/release-11.md, slice O6).
export type BuildMode = "basic" | "docker";
// Each field is documented in BUILD_PIPELINE_SETTINGS_FIELD_DOCS below (rendered into SETTINGS.md).
@@ -431,10 +432,11 @@ export type BuildPipelineSettings = {
export const BUILD_PIPELINE_SETTINGS_FIELD_DOCS: FieldDocs<BuildPipelineSettings> = {
mode:
- "\"basic\" — `pnpm run build` in export/, serialized on the build queue (shared output tree, no safe parallelism). \"docker\" — isolated per-site container builds, parallel up to `maxParallelBuilds`.",
+ "\"basic\" or \"docker\". Persisted and shown on /sites, but no build path reads it today — a label, not a switch: Build all sites uses containers whenever a container engine answers, and builds serially on the host when none does, in either mode.",
maxParallelBuilds:
- "Cap on concurrent per-site container builds in docker mode. Ignored in" +
- " basic mode (which is always serial). Clamped to [1, " +
+ "Cap on concurrent per-site container builds when Build all sites runs in" +
+ " containers (whenever a container engine answers, whatever `mode` says)." +
+ " Clamped to [1, " +
"BUILD_MAX_PARALLEL_MAX].",
dockerImage:
"Tag of the reusable build image (built once, reused for every site).",
@@ -1525,7 +1527,7 @@ export const siteSettingsSchema = z.object({
"Where a channel's downloaded media goes when it is relocated off the corpus disk. A DEFAULT ONLY: the relocate controller never reads it and always takes an explicit root, so this is the value the per-channel Storage panel prefills and the /channels bulk move falls back to. Blank = no default. See StorageSettings.",
),
buildPipeline: settingsField((v): BuildPipelineSettings => sanitizeBuildPipeline(v)).describe(
- "How the static export is built: \"basic\" reuses the single export/ tree and serializes builds on one queue (the long-standing behavior); \"docker\" builds every site at once, each in its own container (Dockerfile.build), then deploys them serially, and falls back to the basic build when no container engine answers — see PUBLISH.md. This block persists the chosen mode plus the image and concurrency knobs that pipeline reads.",
+ "The build pipeline's settings. A single site builds in the shared export/ tree, serialized on one queue. Build all sites (and Build & deploy all) builds every site at once, each in its own container (Dockerfile.build, the image and maxParallelBuilds below), then deploys them serially, whenever a container engine answers, and serially on the host when none does — see PUBLISH.md. `mode` is persisted and shown on /sites, but no build path reads it today: it is a label, not a switch.",
),
digest: settingsField((v): DigestSettings => sanitizeDigest(v)).describe(
"AI digest generation (chapters + topic tags over the existing transcripts). Local-first: the metered lane is off by default. See DigestSettings.",
diff --git a/editor/app/settings/components/SettingsForm.tsx b/editor/app/settings/components/SettingsForm.tsx
@@ -298,12 +298,12 @@ export function SettingsForm({ initial }: Props) {
<fieldset className="flex flex-col gap-3 border border-border rounded p-3">
<legend className="px-1 text-sm font-medium">Build pipeline</legend>
<p className="text-xs text-muted-foreground">
- How the static export is built. <strong>Basic</strong> runs the build
- in <code>export/</code> and serializes builds on one queue.{" "}
- <strong>Docker</strong> builds every site at once, each in its own
- container (capped by Max parallel builds), then deploys them one by
- one; with no container engine it falls back to the basic build. See
- PUBLISH.md. The mode can also be toggled on the{" "}
+ A single site builds in <code>export/</code>, one at a time on one
+ queue. <strong>Build all sites</strong> builds every site at once, each
+ in its own container (capped by Max parallel builds), then deploys them
+ one by one, whenever a container engine answers, and serially on the
+ host when none does. The mode below is a label for now: no build reads
+ it. See PUBLISH.md. It can also be toggled on the{" "}
<a href="/sites" className="underline">
Sites
</a>{" "}
@@ -325,7 +325,7 @@ export function SettingsForm({ initial }: Props) {
name="maxParallelBuilds"
defaultValue={String(initial.buildPipeline.maxParallelBuilds)}
type="number"
- hint="Cap on concurrent per-site container builds in Docker mode (1–16). Ignored in Basic mode, which is always serial."
+ hint="Cap on concurrent per-site container builds (1–16) when Build all sites runs in containers — whenever a container engine answers, whichever mode is set."
/>
<Field
label="Docker image tag"
diff --git a/editor/app/sites/components/BuildAllSitesButton.tsx b/editor/app/sites/components/BuildAllSitesButton.tsx
@@ -10,12 +10,13 @@ import { JobLane } from "./JobLane";
type Lane = { deploy: boolean; skipArchives: boolean; key: number };
// One-click orchestrated pipeline over EVERY site as a SINGLE managed job (one
-// log, one Cancel): in Docker mode it runs the shared data phase + archive warm
-// once, fans the per-site builds out in parallel (capped by maxParallelBuilds),
-// then deploys the built sites serially. Without a container engine it falls back
-// to a serial host build+deploy. Distinct from the per-site panel below, which
-// launches one separate job per selected site.
-export function BuildAllSitesButton({ dockerMode }: { dockerMode: boolean }) {
+// log, one Cancel): whenever a container engine answers — whatever
+// buildPipeline.mode says, which nothing reads — it runs the shared data phase +
+// archive warm once, fans the per-site builds out in parallel (capped by
+// maxParallelBuilds), then deploys the built sites serially. Without one it falls
+// back to a serial host build+deploy. Distinct from the per-site panel below,
+// which launches one separate job per selected site.
+export function BuildAllSitesButton() {
const [deploy, setDeploy] = useState(true);
const [skipArchives, setSkipArchives] = useState(false);
const [lane, setLane] = useState<Lane | null>(null);
@@ -55,16 +56,17 @@ export function BuildAllSitesButton({ dockerMode }: { dockerMode: boolean }) {
</label>
</div>
<p className="text-xs text-muted-foreground">
- {dockerMode
- ? "Docker pipeline: shared data phase runs once, per-site builds run in parallel, then deploys run serially."
- : "Docker mode is off — this runs a serial host build+deploy fallback (one site at a time)."}
+ When a container engine answers (whichever build mode is set): the shared
+ data phase runs once, per-site builds run in parallel in containers, then
+ deploys run serially. With none, a serial host build and deploy, one site
+ at a time.
</p>
{lane && (
<JobLane
key={lane.key}
title={lane.deploy ? "Build & deploy all sites" : "Build all sites"}
subtitle={
- dockerMode ? "Docker pipeline · parallel builds" : "Serial host fallback"
+ "Containers when an engine answers, else serial on the host"
}
trigger={() =>
lane.deploy
diff --git a/editor/app/sites/components/BuildModeToggle.tsx b/editor/app/sites/components/BuildModeToggle.tsx
@@ -4,8 +4,11 @@ import { useState, useTransition } from "react";
import type { BuildMode } from "yt-dlp-transcript-common/lib/settings";
import { setBuildModeAction } from "../lib/buildModeAction";
-// Segmented Basic | Docker control. Persists the choice as the global build-mode
-// default (setBuildModeAction → settings.json) so every subsequent build uses it.
+// Segmented Basic | Docker control. Persists the choice (setBuildModeAction →
+// settings.json `buildPipeline.mode`). TODAY IT IS A LABEL: no build path reads
+// it — Build all / Build & deploy all use containers whenever `docker version`
+// answers (buildAction.ts, dockerAvailable) — and the note below says so rather
+// than promising a switch.
export function BuildModeToggle({ mode: initialMode }: { mode: BuildMode }) {
const [mode, setMode] = useState<BuildMode>(initialMode);
const [pending, startTransition] = useTransition();
@@ -53,11 +56,11 @@ export function BuildModeToggle({ mode: initialMode }: { mode: BuildMode }) {
);
})}
</div>
- {mode === "docker" && (
- <span className="text-xs text-warning">
- Docker builds are a follow-up — runs the basic build for now.
- </span>
- )}
+ <span className="text-xs text-muted-foreground">
+ The mode is a label for now: Build all sites uses containers whenever a
+ container engine answers, and builds serially on the host when none
+ does, in either mode.
+ </span>
</div>
);
}
diff --git a/editor/app/sites/components/BuildSitesPanel.tsx b/editor/app/sites/components/BuildSitesPanel.tsx
@@ -13,9 +13,9 @@ export type SiteOption = {
type Lane = { siteId: string; title: string; deploy: boolean; key: string };
// Batch build (and optionally deploy) several sites at once. Each selected site
-// launches its own managed job, rendered as its own live JobLane. In Basic mode
-// the jobs share the build/deploy queue and run one at a time (the export/ tree
-// is shared); Docker mode (a follow-up) unlocks true parallelism.
+// launches its own managed job, rendered as its own live JobLane. The jobs share
+// the build/deploy queue and run one at a time (the export/ tree is shared),
+// whichever build mode is set; "Build all sites" is the parallel path.
export function BuildSitesPanel({
sites,
serial,
@@ -127,8 +127,9 @@ export function BuildSitesPanel({
)}
{serial && (
<p className="text-xs text-muted-foreground">
- Basic mode runs these one at a time (the build output tree is shared).
- Use “Build all sites” above in Docker mode for true parallel builds.
+ These run one at a time (the build output tree is shared). “Build
+ all sites” above builds every site in parallel, in containers, whenever
+ a container engine answers.
</p>
)}
diff --git a/editor/app/sites/page.tsx b/editor/app/sites/page.tsx
@@ -171,7 +171,7 @@ export default async function SitesPage() {
</p>
</div>
<BuildModeToggle mode={buildMode} />
- <BuildAllSitesButton dockerMode={buildMode === "docker"} />
+ <BuildAllSitesButton />
<div className="mt-4">
<h3 className="font-semibold">Or pick specific sites</h3>
diff --git a/editor/e2e/deploy-page.spec.ts b/editor/e2e/deploy-page.spec.ts
@@ -68,7 +68,7 @@ test("Build & deploy is enabled only when the active site has a Cloudflare proje
).toBeVisible();
});
-test("build-mode toggle persists the choice and shows the Docker follow-up note", async ({
+test("build-mode toggle persists the choice and says the mode is a label", async ({
page,
}) => {
await writeSite("testsite", { cloudflareProject: "proj" });
@@ -93,7 +93,7 @@ test("build-mode toggle persists the choice and shows the Docker follow-up note"
timeout: 1_000,
});
}).toPass({ timeout: 15_000 });
- await expect(page.getByText(/Docker builds are a follow-up/i)).toBeVisible();
+ await expect(page.getByText(/The mode is a label for now/i)).toBeVisible();
// Wait for the choice to actually reach settings.json before reloading.
// BuildModeToggle updates its own state OPTIMISTICALLY — setMode(next) flips
@@ -150,5 +150,5 @@ test("batch panel: selecting sites enables the launch button and reflects deploy
).toBeVisible();
// Basic mode (the default) notes that the batch runs serially.
- await expect(page.getByText(/Basic mode runs these one at a time/i)).toBeVisible();
+ await expect(page.getByText(/These run one at a time/i)).toBeVisible();
});