@styleflow.app/flowpress-deploy 1.0.0-beta.0 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0-beta.2 — 2026-10-14
4
+
5
+ - Automatic debounced editorial builds and public asset validation/history.
6
+ - Use public taxonomy hooks for scheduled builds and export portable link, asset-history and Worker validation tools.
7
+ - [Canonical integration report](https://github.com/StyleFlow-App/styleflow-docs/blob/main/dev/rapporti/2026-10-14-consolidamento-tessari.md).
8
+
9
+
10
+ ## 1.0.0-beta.1
11
+
12
+ - Added the automatically selected `flowpress-deploy` Core queue provider.
13
+ - Added revision-safe queue, deployed state and batch snapshot tables.
14
+ - Added fingerprint deduplication, WPML post grouping, URI history and safe
15
+ success/failure/stale-batch handling.
16
+ - Added non-destructive migration from the Core database queue.
17
+ - Added end-of-request enqueue, manual editor actions and the expanded
18
+ **Strumenti → FlowPress Deploy** operations screen.
19
+ - Added complete internal documentation and PHP queue contract tests.
20
+
3
21
  ## 1.0.0-beta.0
4
22
 
5
23
  - Adopted the FlowPress WordPress Core v1 Composer contract.
package/README.md CHANGED
@@ -1,17 +1,6 @@
1
1
  # FlowPress Deploy
2
2
 
3
- Installable npm/Composer feature pack for safe Cloudflare Pages and Workers
4
- build orchestration. CMS partial builds require a successful compatible
5
- baseline; code and manual triggers remain full builds.
3
+ Official npm/Composer integration for FlowPress.
6
4
 
7
- The public API validates paths and `_routes.json`, merges partial artifacts
8
- without traversal or case collisions, regenerates sitemaps, selects Pages for
9
- static targets and Workers for dev, requires `ASTRO_KEY` for protected batches,
10
- and acknowledges the CMS queue only after deploy and smoke test. Failed
11
- batches are released for retry.
12
-
13
- ## WordPress Core v1
14
-
15
- The `1.0.0-beta.0` line requires FlowPress WordPress Core v1. It does not
16
- provide a v0.x compatibility constraint or adapter; canary applications pin
17
- the selected Core prerelease exactly.
5
+ - [Canonical guide](https://github.com/StyleFlow-App/styleflow-docs/blob/main/wiki/flowpress-routing-deploy.md)
6
+ - [Release and verification report](https://github.com/StyleFlow-App/styleflow-docs/blob/main/dev/rapporti/2026-10-14-consolidamento-tessari.md)
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowpress/deploy",
3
- "description": "WordPress capability registration for FlowPress deploy.",
3
+ "description": "Revision-safe editorial build queue and deploy capability for FlowPress.",
4
4
  "type": "wordpress-muplugin",
5
5
  "license": "proprietary",
6
6
  "require": {
@@ -0,0 +1,2 @@
1
+ export function retainFrontendAssets(output: any, baselineDirectories: any): Promise<number>;
2
+ //# sourceMappingURL=frontend-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-assets.d.ts","sourceRoot":"","sources":["../src/frontend-assets.js"],"names":[],"mappings":"AAUA,6FA4BC"}
@@ -0,0 +1,44 @@
1
+ import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { frontendFiles } from "./frontend-links.js";
5
+ const manifestName = ".flowpress-asset-generations.json";
6
+ const clientDirectory = output => existsSync(resolve(output, "client")) ? resolve(output, "client") : output;
7
+ // Keep three distinct sets of immutable assets so pages opened before a deploy
8
+ // can still load their styles, scripts and deferred imports. Never retain HTML.
9
+ export async function retainFrontendAssets(output, baselineDirectories) {
10
+ const client = clientDirectory(output);
11
+ const current = (await frontendFiles(client)).filter(path => path.startsWith("/_astro/")).sort();
12
+ const generations = [current];
13
+ const sources = new Map();
14
+ for (const baseline of baselineDirectories.filter(existsSync)) {
15
+ const baselineClient = clientDirectory(baseline);
16
+ const previousFiles = (await frontendFiles(baselineClient)).filter(path => path.startsWith("/_astro/"));
17
+ for (const path of previousFiles)
18
+ if (!sources.has(path))
19
+ sources.set(path, resolve(baselineClient, path.slice(1)));
20
+ const manifest = await readFile(resolve(baseline, manifestName), "utf8").then(JSON.parse).catch(() => null);
21
+ for (const generation of manifest?.generations ?? [previousFiles.sort()]) {
22
+ if (!generations.some(existing => JSON.stringify(existing) === JSON.stringify(generation)))
23
+ generations.push(generation);
24
+ }
25
+ }
26
+ const retained = generations.slice(0, 3);
27
+ let copied = 0;
28
+ for (const path of new Set(retained.flat())) {
29
+ if (!/^\/_astro\/[A-Za-z0-9_./@-]+$/.test(path) || path.split("/").some(part => part === ".." || part === "."))
30
+ throw new Error(`Invalid retained asset: ${path}`);
31
+ const destination = resolve(client, path.slice(1));
32
+ if (existsSync(destination))
33
+ continue;
34
+ const source = sources.get(path);
35
+ if (!source)
36
+ throw new Error(`Baseline is missing retained asset ${path}`);
37
+ await mkdir(dirname(destination), { recursive: true });
38
+ await copyFile(source, destination);
39
+ copied++;
40
+ }
41
+ await writeFile(resolve(output, manifestName), `${JSON.stringify({ generations: retained }, null, 2)}\n`);
42
+ return copied;
43
+ }
44
+ //# sourceMappingURL=frontend-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-assets.js","sourceRoot":"","sources":["../src/frontend-assets.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export function frontendFiles(directory: any): Promise<any[]>;
2
+ export function htmlReferences(html: any): any[];
3
+ export function inspectFrontendLinks(directory: any, { origins, workerPatterns }?: {
4
+ origins?: never[] | undefined;
5
+ workerPatterns?: never[] | undefined;
6
+ }): Promise<{
7
+ paths: string[];
8
+ broken: {
9
+ source: any;
10
+ path: string;
11
+ }[];
12
+ pages: number;
13
+ }>;
14
+ //# sourceMappingURL=frontend-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-links.d.ts","sourceRoot":"","sources":["../src/frontend-links.js"],"names":[],"mappings":"AAIA,8DAWC;AAED,iDAWC;AAED;;;;;;;;;;GAwCC"}
@@ -0,0 +1,82 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { relative, resolve, sep } from "node:path";
3
+ import { invokesWorkerFirst } from "./worker-assets.js";
4
+ export async function frontendFiles(directory) {
5
+ const files = [];
6
+ async function visit(current) {
7
+ for (const entry of await readdir(current, { withFileTypes: true })) {
8
+ const absolute = resolve(current, entry.name);
9
+ if (entry.isDirectory())
10
+ await visit(absolute);
11
+ if (entry.isFile())
12
+ files.push(`/${relative(directory, absolute).split(sep).join("/")}`);
13
+ }
14
+ }
15
+ await visit(directory);
16
+ return files;
17
+ }
18
+ export function htmlReferences(html) {
19
+ const references = [];
20
+ for (const tag of html.matchAll(/<(?:a|link|script|img|source|video|audio|iframe|form)\b[^>]*>/gi)) {
21
+ for (const attribute of tag[0].matchAll(/\s(href|src|poster|action|srcset)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi)) {
22
+ const value = (attribute[2] ?? attribute[3]).replaceAll("&amp;", "&");
23
+ if (attribute[1].toLowerCase() === "srcset") {
24
+ if (!value.startsWith("data:"))
25
+ references.push(...value.split(",").map(item => item.trim().split(/\s+/)[0]));
26
+ }
27
+ else
28
+ references.push(value);
29
+ }
30
+ }
31
+ return references;
32
+ }
33
+ export async function inspectFrontendLinks(directory, { origins = [], workerPatterns = [] } = {}) {
34
+ const files = await frontendFiles(directory);
35
+ const existing = new Set(files);
36
+ const redirectsText = await readFile(resolve(directory, "_redirects"), "utf8").catch(() => "");
37
+ const redirects = new Map(redirectsText.split("\n").filter(line => line.trim() && !line.startsWith("#")).map(line => {
38
+ const [source, target] = line.trim().split(/\s+/);
39
+ return [source, target];
40
+ }));
41
+ const origin = origins[0] || "https://frontend.test";
42
+ const localOrigins = new Set([origin, ...origins]);
43
+ const paths = new Set(["/"]);
44
+ for (const file of files.filter(file => file.startsWith("/_astro/")))
45
+ paths.add(file);
46
+ const broken = [];
47
+ const hasRoute = path => existing.has(path) || existing.has(`${path.replace(/\/$/, "")}/index.html`) || existing.has(`${path}.html`) || invokesWorkerFirst(workerPatterns, path);
48
+ function resolves(path, seen = new Set()) {
49
+ if (seen.has(path))
50
+ return false;
51
+ seen.add(path);
52
+ if (redirects.has(path)) {
53
+ const target = new URL(redirects.get(path), origin);
54
+ return !localOrigins.has(target.origin) || resolves(decodeURIComponent(target.pathname), seen);
55
+ }
56
+ return hasRoute(path);
57
+ }
58
+ for (const file of files.filter(file => file.endsWith(".html") && file !== "/404.html")) {
59
+ const route = file.replace(/index\.html$/, "").replace(/\.html$/, "");
60
+ paths.add(route || "/");
61
+ for (const reference of htmlReferences(await readFile(resolve(directory, file.slice(1)), "utf8"))) {
62
+ if (!reference || reference.startsWith("#"))
63
+ continue;
64
+ const url = new URL(reference, `${origin}${route}`);
65
+ if (!["https:", "http:"].includes(url.protocol) || !localOrigins.has(url.origin))
66
+ continue;
67
+ const path = decodeURIComponent(url.pathname);
68
+ if (!resolves(path))
69
+ broken.push({ source: file, path });
70
+ else if (!invokesWorkerFirst(workerPatterns, path))
71
+ paths.add(path);
72
+ }
73
+ }
74
+ for (const [source] of redirects) {
75
+ if (!resolves(source))
76
+ broken.push({ source: "_redirects", path: source });
77
+ else
78
+ paths.add(source);
79
+ }
80
+ return { paths: [...paths].sort(), broken, pages: files.filter(file => file.endsWith(".html") && file !== "/404.html").length };
81
+ }
82
+ //# sourceMappingURL=frontend-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontend-links.js","sourceRoot":"","sources":["../src/frontend-links.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** Match Wrangler's selective Worker-first patterns, including exclusions. */
2
+ export function invokesWorkerFirst(patterns: any, pathname: any): boolean;
3
+ export function validateWorkerAssets(config: any, staticPaths: any, reservedAreaEnabled?: boolean): void;
4
+ export function assertFreeRuntimeRelease(target: any, env: any): void;
5
+ //# sourceMappingURL=worker-assets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-assets.d.ts","sourceRoot":"","sources":["../src/worker-assets.js"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAMC;AAED,yGAYC;AAED,sEAIC"}
@@ -0,0 +1,35 @@
1
+ /** Match Wrangler's selective Worker-first patterns, including exclusions. */
2
+ export function invokesWorkerFirst(patterns, pathname) {
3
+ const match = pattern => new RegExp(`^${pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*")}$`).test(pathname);
4
+ if (patterns === true)
5
+ return true;
6
+ if (!Array.isArray(patterns))
7
+ return false;
8
+ return patterns.some(pattern => !pattern.startsWith("!") && match(pattern))
9
+ && !patterns.some(pattern => pattern.startsWith("!") && match(pattern.slice(1)));
10
+ }
11
+ export function validateWorkerAssets(config, staticPaths, reservedAreaEnabled = false) {
12
+ const patterns = config.assets?.run_worker_first;
13
+ if (config.assets?.binding !== "ASSETS" || !config.main)
14
+ throw new Error("Worker must have an ASSETS binding and compiled entrypoint");
15
+ if (reservedAreaEnabled && (patterns === true || patterns?.includes("/*")))
16
+ throw new Error("Public assets must bypass the Worker");
17
+ for (const path of (reservedAreaEnabled ? ["/area-riservata/", "/area-riservata/example/", "/api/flowpress/reserved-area/login", "/api/flowpress/reserved-area/logout"] : [])) {
18
+ if (!invokesWorkerFirst(patterns, path))
19
+ throw new Error(`Protected runtime route missing: ${path}`);
20
+ }
21
+ for (const path of staticPaths) {
22
+ if (path.startsWith("/area-riservata/") || path.startsWith("/api/flowpress/reserved-area/"))
23
+ throw new Error(`Private static asset forbidden: ${path}`);
24
+ if (reservedAreaEnabled && invokesWorkerFirst(patterns, path))
25
+ throw new Error(`Static asset invokes Worker: ${path}`);
26
+ }
27
+ if (reservedAreaEnabled && (config.kv_namespaces?.length || config.images))
28
+ throw new Error("Free runtime must not provision session KV or Images bindings");
29
+ }
30
+ export function assertFreeRuntimeRelease(target, env) {
31
+ if (target.environmentName === "production" && target.projectType === "workers" && env.FLOWPRESS_RESERVED_AREA_ENABLED === "true" && env.CF_FREE_RUNTIME_VERIFIED !== "true") {
32
+ throw new Error("Production Workers release is gated: validate staging CPU (p95 < 8 ms, p99 < 10 ms, no exceededCpu), then set CF_FREE_RUNTIME_VERIFIED=true in the production environment.");
33
+ }
34
+ }
35
+ //# sourceMappingURL=worker-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-assets.js","sourceRoot":"","sources":["../src/worker-assets.js"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB,CAAC,QAAQ,EAAE,QAAQ;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/H,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;WACtE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,KAAK;IACnF,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IACjD,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IACvI,IAAI,mBAAmB,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACpI,KAAK,MAAM,IAAI,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,kBAAkB,EAAE,0BAA0B,EAAE,oCAAoC,EAAE,qCAAqC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9K,IAAI,CAAC,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,+BAA+B,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;QACxJ,IAAI,mBAAmB,IAAI,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;IACzH,CAAC;IACD,IAAI,mBAAmB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;AAC/J,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAM,EAAE,GAAG;IAClD,IAAI,MAAM,CAAC,eAAe,KAAK,YAAY,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,IAAI,GAAG,CAAC,+BAA+B,KAAK,MAAM,IAAI,GAAG,CAAC,wBAAwB,KAAK,MAAM,EAAE,CAAC;QAC7K,MAAM,IAAI,KAAK,CAAC,4KAA4K,CAAC,CAAC;IAChM,CAAC;AACH,CAAC"}
package/docs/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # FlowPress Deploy: documentazione
2
+
3
+ FlowPress Deploy collega le modifiche editoriali di WordPress ai workflow di
4
+ build senza salvare token o contenuti nella propria coda.
5
+
6
+ ## Pagine
7
+
8
+ | Argomento | Documento |
9
+ | --- | --- |
10
+ | Modello e responsabilità | [Architettura](architecture.md) |
11
+ | Variabili richieste | [Configurazione](configuration.md) |
12
+ | Deduplica, URI e revisioni | [Queue WordPress](wordpress-queue.md) |
13
+ | Payload e callback | [Frontend e workflow](frontend-workflow.md) |
14
+ | Uso quotidiano | [Operazioni](operations.md) |
15
+ | Callback mancanti e retry | [Recovery](recovery.md) |
16
+ | Diagnostica | [Troubleshooting](troubleshooting.md) |
@@ -0,0 +1,9 @@
1
+ # Release iniziale deploy pack
2
+
3
+ - Obiettivo: estrarre build full/partial, baseline e guardie Cloudflare.
4
+ - Modifiche: package npm, MU-plugin Composer, test e workflow OIDC.
5
+ - Verifiche: typecheck, 7 test, npm pack, Composer validate e PHP lint positivi.
6
+ - Hardening pre-release: merge sicuro full/partial, sitemap, target
7
+ Pages/Workers, guardia `ASTRO_KEY` e lifecycle ack/release della queue.
8
+ - Workflow tag idempotente per il bootstrap della prima pubblicazione npm.
9
+ - Release prevista: `0.1.0-beta.0`; resta da configurare il Trusted Publisher npm.
@@ -0,0 +1,40 @@
1
+ # Deploy su WordPress Core v1
2
+
3
+ ## Esito
4
+
5
+ Preparata la major prerelease `1.0.0-beta.0` npm/Composer con dipendenza
6
+ limitata a `flowpress/wordpress-core ^1.0.0@beta` e senza adapter v0.x. Il
7
+ consumer canary mantiene il pin Core esatto.
8
+
9
+ ## Scope
10
+
11
+ La logica Deploy non cambia. Versione package, Composer contract, lockfile,
12
+ changelog e documentazione vengono pubblicati dallo stesso tag.
13
+
14
+ ## Verifiche
15
+
16
+ | Gate | Esito |
17
+ |---|---|
18
+ | `npm ci && npm run release:check` | OK; 7/7 test e pack 9 file |
19
+ | `npm audit` | OK; 0 vulnerabilita' dopo aggiornamento lockfile `nanoid` |
20
+ | `composer validate --strict --no-check-publish` | OK |
21
+ | PHP lint | OK |
22
+ | `git diff --check` | OK |
23
+
24
+ ## Pubblicazione verificata
25
+
26
+ - PR v1 unita su `main`; merge e `gitHead` npm:
27
+ `24e0033d78566890263249e56da3c15449e80897`.
28
+ - Workflow Publish `32747502576`: verde con trusted publishing e provenance.
29
+ - npm: `@styleflow.app/flowpress-deploy@1.0.0-beta.0`, dist-tag `next`;
30
+ integrity, shasum, tarball e firma presenti. `latest` resta sulla linea
31
+ precedente.
32
+ - Repman: `flowpress/deploy@v1.0.0-beta.0`, sincronizzato dallo stesso tag e
33
+ con requisito `flowpress/wordpress-core ^1.0.0@beta`.
34
+ - Canary DMO: installazione npm/Composer da registry, manifest coerente e gate
35
+ completo verde.
36
+
37
+ ## Rollback
38
+
39
+ La linea `0.1.0-beta.0` resta immutabile. I consumer v1 usano pin esatti e non
40
+ spostano `latest`.
@@ -0,0 +1,41 @@
1
+ # FlowPress Deploy: revision-safe editorial queue
2
+
3
+ - Data: 2026-08-26 09:26 Europe/Rome
4
+ - Repository: `StyleFlow-App/flowpress-deploy`
5
+ - Versione prevista: `1.0.0-beta.1`
6
+
7
+ ## Obiettivo
8
+
9
+ Rendere Deploy proprietario della queue editoriale tramite il provider Core,
10
+ senza cambiare Core e senza assumere configurazioni Bagful/Cloudflare.
11
+
12
+ ## Modifiche
13
+
14
+ - Provider automatico, schema queue/state/batch e migrazione Core non
15
+ distruttiva.
16
+ - Fingerprint, URI history, WPML grouping, snapshot e ack revision-safe.
17
+ - Enqueue a fine richiesta, azioni manuali, UI operativa e GitHub test.
18
+ - Documentazione completa in `docs/` e inclusione nel pacchetto npm.
19
+
20
+ ## Verifiche
21
+
22
+ - `php tests/php/queue-provider-test.php`: superato.
23
+ - `find . -name '*.php' ... php -l`: superato.
24
+ - `git diff --check`: superato.
25
+ - `composer validate --strict`: superato.
26
+ - `npm run release:check`: superato (typecheck, 7 test Vitest, test PHP,
27
+ pack dry-run con `docs/` e `includes/`).
28
+
29
+ ## Verifica manuale
30
+
31
+ La verifica integrata in WordPress DMO sarà registrata nel report DMO; nessun
32
+ dispatch o deploy remoto è autorizzato in questo task.
33
+
34
+ ## Commit suggerito
35
+
36
+ `feat: add revision-safe editorial build queue`
37
+
38
+ ## Rischi e follow-up
39
+
40
+ Il rollout richiede configurazione remota dei token e un test editoriale
41
+ controllato prima di abilitare la pubblicazione production.
@@ -0,0 +1,5 @@
1
+ # Indice attività
2
+
3
+ - [2026-08-26-0926-revision-safe-queue.md](2026-08-26-0926-revision-safe-queue.md)
4
+ - [2026-08-24-1745-wordpress-core-v1.md](2026-08-24-1745-wordpress-core-v1.md)
5
+ - [2026-07-30-2339-initial-pack.md](2026-07-30-2339-initial-pack.md)
@@ -0,0 +1,19 @@
1
+ # Architettura
2
+
3
+ ## Perché
4
+
5
+ Core espone il contratto di dispatch e callback; Deploy possiede lo stato
6
+ editoriale necessario a non perdere aggiornamenti concorrenti.
7
+
8
+ ## Come funziona
9
+
10
+ 1. Il plugin registra il provider `flowpress-deploy` e lo seleziona.
11
+ 2. Gli hook Core inoltrano post, termini e modifiche globali al provider.
12
+ 3. Gli update vengono materializzati a `shutdown`, dopo meta e tassonomie.
13
+ 4. Le tabelle `flowpress_deploy_queue`, `flowpress_deploy_state` e
14
+ `flowpress_deploy_batches` conservano rispettivamente lavoro, ultimo stato
15
+ distribuito e snapshot immutabile del batch.
16
+ 5. Core effettua repository dispatch e callback usando il wire contract
17
+ pubblico. Deploy applica l’ack alla sola revisione fotografata.
18
+
19
+ La coda conserva hash SHA-256 e URI, non il contenuto completo o credenziali.
@@ -0,0 +1,19 @@
1
+ # Configurazione
2
+
3
+ ## Perché
4
+
5
+ Target e credenziali sono configurazione di ambiente e non devono finire nel
6
+ database WordPress.
7
+
8
+ ## Come fare
9
+
10
+ 1. Definire `SF_STATIC_TARGETS_JSON`, per esempio `['production']` in JSON
11
+ valido.
12
+ 2. Definire `GITHUB_REPOSITORY_SLUG=organizzazione/repository`.
13
+ 3. Definire `CMS_GITHUB_DISPATCH_TOKEN` con il minimo accesso necessario al
14
+ repository.
15
+ 4. Definire `CMS_BUILD_CALLBACK_TOKEN` sia nel backend sia nel workflow.
16
+ 5. Aprire **Strumenti → FlowPress Deploy**: lo stato indica soltanto presenza
17
+ o assenza dei token, mai il loro valore.
18
+
19
+ ⚠️ Un target deve corrispondere esattamente a quello gestito dal workflow.
@@ -0,0 +1,23 @@
1
+ # Frontend e workflow
2
+
3
+ ## Wire contract
4
+
5
+ Il repository dispatch riceve:
6
+
7
+ ```json
8
+ {
9
+ "batchId": "uuid",
10
+ "target": "production",
11
+ "mode": "partial",
12
+ "paths": ["/pagina/"],
13
+ "deletedPaths": ["/vecchio-slug/"],
14
+ "queueIds": [12]
15
+ }
16
+ ```
17
+
18
+ Il workflow deve chiamare `POST /flowpress/v1/build-complete` con bearer token
19
+ e body `{"batchId":"uuid","status":"success"}` soltanto dopo build, deploy
20
+ e smoke test. In ogni altro caso invia `failure`.
21
+
22
+ ⚠️ L’assenza del callback non equivale a successo: il batch resta in-flight e
23
+ sarà rilasciato dal recovery.
@@ -0,0 +1,15 @@
1
+ # Operazioni
2
+
3
+ ## Come fare
4
+
5
+ 1. Aprire **Strumenti → FlowPress Deploy**.
6
+ 2. Verificare repository, token, righe pending, URI e batch in-flight.
7
+ 3. Espandere **Payload preview** prima dell’invio.
8
+ 4. Usare **Pubblica queue** per la modalità prevista.
9
+ 5. Usare **Force full** soltanto quando baseline o codice richiedono una build
10
+ completa; il browser chiede conferma.
11
+ 6. Per forzare un singolo contenuto usare **Aggiungi alla build queue** nella
12
+ schermata o nella lista di post/termini.
13
+
14
+ Il pulsante **Test GitHub** verifica autenticazione e accesso al repository,
15
+ senza inviare un deployment.
@@ -0,0 +1,16 @@
1
+ # Recovery
2
+
3
+ ## Callback failure
4
+
5
+ 1. Il callback `failure` libera il batch.
6
+ 2. Le righe tornano pending mantenendo l’ultima revisione editoriale.
7
+ 3. Correggere la causa nel workflow e ripubblicare la queue.
8
+
9
+ ## Callback mancante
10
+
11
+ 1. Il cron orario cerca batch in-flight più vecchi di sei ore.
12
+ 2. Il batch viene rilasciato come failure.
13
+ 3. Il limite è modificabile con `flowpress_deploy_stale_batch_hours`.
14
+
15
+ ⚠️ Non cancellare manualmente righe delle tre tabelle: si perderebbe la
16
+ corrispondenza fra stato distribuito e snapshot. Usare callback/retry.
@@ -0,0 +1,22 @@
1
+ # Troubleshooting
2
+
3
+ ## Queue vuota
4
+
5
+ Controllare `SF_STATIC_TARGETS_JSON`, tipo di contenuto instradabile e che il
6
+ salvataggio non sia autosave/revisione. Un salvataggio identico è correttamente
7
+ deduplicato.
8
+
9
+ ## URI vecchio non eliminato
10
+
11
+ Controllare `previous_uris` nella pagina Deploy e che il workflow consumi
12
+ `deletedPaths`.
13
+
14
+ ## Batch sempre in-flight
15
+
16
+ Verificare token callback e log del workflow. Attendere il recovery oppure
17
+ inviare esplicitamente `failure` per il `batchId` corretto.
18
+
19
+ ## GitHub rifiuta il test
20
+
21
+ Verificare slug repository, scadenza e permessi del token. La pagina non mostra
22
+ né registra il segreto.
@@ -0,0 +1,22 @@
1
+ # Queue WordPress
2
+
3
+ ## Perché
4
+
5
+ Salvataggi ripetuti, traduzioni e cambi slug non devono produrre build inutili
6
+ o lasciare file vecchi online.
7
+
8
+ ## Come funziona
9
+
10
+ 1. Autosave e revisioni sono esclusi da Core.
11
+ 2. Deploy raggruppa per post/term ID; con WPML usa il translation group.
12
+ 3. Il fingerprint include contenuto, meta non volatili e tassonomie. Il filtro
13
+ `flowpress_deploy_volatile_meta_keys` aggiunge esclusioni.
14
+ 4. `flowpress_deploy_fingerprint_fields`, `flowpress_deploy_group_key` e
15
+ `flowpress_deploy_canonical_uris` permettono estensioni senza patch.
16
+ 5. `previous_uris` resta ancorato allo stato distribuito: uno slug change
17
+ produce il nuovo path e inserisce quello vecchio in `deletedPaths`.
18
+ 6. Una modifica durante un batch incrementa la revisione. Il callback completa
19
+ lo snapshot, ma rimette la revisione nuova in pending.
20
+
21
+ La prima installazione importa le righe Core senza cancellarle; ogni vecchio
22
+ batch in-flight viene importato come pending per un retry sicuro.
@@ -1,21 +1,50 @@
1
1
  <?php
2
2
  /**
3
3
  * Plugin Name: FlowPress Deploy
4
- * Description: Installable deployment capability for FlowPress.
5
- * Version: 0.1.0-beta.0
4
+ * Description: Revision-safe editorial build queue and deployment capability for FlowPress.
5
+ * Version: 1.0.0-beta.2
6
6
  */
7
7
 
8
8
  if (! defined('ABSPATH')) {
9
9
  exit;
10
10
  }
11
11
 
12
+ define('FLOWPRESS_DEPLOY_VERSION', '1.0.0-beta.2');
13
+ define('FLOWPRESS_DEPLOY_FILE', __FILE__);
14
+
15
+ require_once __DIR__ . '/includes/queue-provider.php';
16
+ require_once __DIR__ . '/includes/manual-queue.php';
17
+ require_once __DIR__ . '/includes/admin.php';
18
+ require_once __DIR__ . '/includes/automatic-build.php';
19
+
20
+ add_action('muplugins_loaded', static function (): void {
21
+ if (! function_exists('flowpress_register_build_queue_provider')) {
22
+ return;
23
+ }
24
+ flowpress_register_build_queue_provider('flowpress-deploy', [
25
+ 'enqueue' => 'flowpress_deploy_collect_change',
26
+ 'payload' => 'flowpress_deploy_build_payload',
27
+ 'markDispatched' => 'flowpress_deploy_mark_dispatched',
28
+ 'complete' => 'flowpress_deploy_complete_batch',
29
+ 'pendingCount' => 'flowpress_deploy_pending_count',
30
+ ]);
31
+ }, 110);
32
+
33
+ add_filter('flowpress_build_queue_provider', static fn (): string => 'flowpress-deploy', 100);
34
+
12
35
  add_action('muplugins_loaded', static function (): void {
13
36
  if (! function_exists('flowpress_register_feature')) {
14
37
  return;
15
38
  }
16
39
  flowpress_register_feature('deploy', [
17
- 'version' => '0.1.0-beta.0',
18
- 'capabilities' => ['full-build', 'partial-build', 'build-callback'],
40
+ 'version' => FLOWPRESS_DEPLOY_VERSION,
41
+ 'capabilities' => [
42
+ 'full-build',
43
+ 'partial-build',
44
+ 'build-callback',
45
+ 'revision-safe-queue',
46
+ 'manual-enqueue',
47
+ ],
19
48
  'config' => [
20
49
  'targets' => function_exists('flowpress_static_targets')
21
50
  ? flowpress_static_targets()