@styleflow.app/flowpress-deploy 1.0.0-beta.1 → 1.0.0-beta.3
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 +13 -0
- package/README.md +3 -31
- package/dist/frontend-assets.d.ts +2 -0
- package/dist/frontend-assets.d.ts.map +1 -0
- package/dist/frontend-assets.js +44 -0
- package/dist/frontend-assets.js.map +1 -0
- package/dist/frontend-links.d.ts +14 -0
- package/dist/frontend-links.d.ts.map +1 -0
- package/dist/frontend-links.js +82 -0
- package/dist/frontend-links.js.map +1 -0
- package/dist/worker-assets.d.ts +5 -0
- package/dist/worker-assets.d.ts.map +1 -0
- package/dist/worker-assets.js +35 -0
- package/dist/worker-assets.js.map +1 -0
- package/flowpress-deploy.php +3 -2
- package/includes/automatic-build.php +98 -0
- package/includes/queue-provider.php +2 -0
- package/package.json +26 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.3 — 2026-10-14
|
|
4
|
+
|
|
5
|
+
- Queue complete builds for term-only changes to published content, including withdrawal after a visibility change. Keep unchanged assignments idle and avoid private data in the queue.
|
|
6
|
+
- Verified public → private → public on disposable WordPress with all outbound HTTP intercepted.
|
|
7
|
+
- [Canonical integration report](https://github.com/StyleFlow-App/styleflow-docs/blob/main/dev/rapporti/2026-10-14-consolidamento-tessari.md).
|
|
8
|
+
|
|
9
|
+
## 1.0.0-beta.2 — 2026-10-14
|
|
10
|
+
|
|
11
|
+
- Automatic debounced editorial builds and public asset validation/history.
|
|
12
|
+
- Use public taxonomy hooks for scheduled builds and export portable link, asset-history and Worker validation tools.
|
|
13
|
+
- [Canonical integration report](https://github.com/StyleFlow-App/styleflow-docs/blob/main/dev/rapporti/2026-10-14-consolidamento-tessari.md).
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
## 1.0.0-beta.1
|
|
4
17
|
|
|
5
18
|
- Added the automatically selected `flowpress-deploy` Core queue provider.
|
package/README.md
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
1
|
# FlowPress Deploy
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
safe full/partial build orchestration. WordPress owns a durable queue per
|
|
5
|
-
frontend target; frontend workflows keep the existing `batchId`, `target`,
|
|
6
|
-
`mode`, `paths` and `deletedPaths` wire contract.
|
|
3
|
+
Official npm/Composer integration for FlowPress.
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
static targets and Workers for dev, requires `ASTRO_KEY` for protected batches,
|
|
11
|
-
and acknowledges the CMS queue only after deploy and smoke test. Failed
|
|
12
|
-
batches are released for retry.
|
|
13
|
-
|
|
14
|
-
## WordPress queue
|
|
15
|
-
|
|
16
|
-
When the Composer package is installed, Deploy automatically becomes the
|
|
17
|
-
FlowPress Core queue provider. It fingerprints content at the end of the
|
|
18
|
-
request, groups WPML translations, keeps current and previous URIs and stores
|
|
19
|
-
a revision snapshot for every dispatched batch. A callback acknowledges only
|
|
20
|
-
the dispatched revision; concurrent editorial changes remain pending.
|
|
21
|
-
|
|
22
|
-
Editors can use **Aggiungi alla build queue** from edit screens and list rows.
|
|
23
|
-
Administrators operate the queue in **Strumenti → FlowPress Deploy**.
|
|
24
|
-
|
|
25
|
-
## Documentation
|
|
26
|
-
|
|
27
|
-
Start with [docs/README.md](docs/README.md). It links configuration, queue
|
|
28
|
-
semantics, frontend integration, operations, recovery and troubleshooting.
|
|
29
|
-
|
|
30
|
-
## WordPress Core v1
|
|
31
|
-
|
|
32
|
-
The `1.0.0-beta.x` line requires FlowPress WordPress Core v1. It does not
|
|
33
|
-
provide a v0.x compatibility constraint or adapter; canary applications pin
|
|
34
|
-
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)
|
|
@@ -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("&", "&");
|
|
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/flowpress-deploy.php
CHANGED
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Plugin Name: FlowPress Deploy
|
|
4
4
|
* Description: Revision-safe editorial build queue and deployment capability for FlowPress.
|
|
5
|
-
* Version: 1.0.0-beta.
|
|
5
|
+
* Version: 1.0.0-beta.3
|
|
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.
|
|
12
|
+
define('FLOWPRESS_DEPLOY_VERSION', '1.0.0-beta.3');
|
|
13
13
|
define('FLOWPRESS_DEPLOY_FILE', __FILE__);
|
|
14
14
|
|
|
15
15
|
require_once __DIR__ . '/includes/queue-provider.php';
|
|
16
16
|
require_once __DIR__ . '/includes/manual-queue.php';
|
|
17
17
|
require_once __DIR__ . '/includes/admin.php';
|
|
18
|
+
require_once __DIR__ . '/includes/automatic-build.php';
|
|
18
19
|
|
|
19
20
|
add_action('muplugins_loaded', static function (): void {
|
|
20
21
|
if (! function_exists('flowpress_register_build_queue_provider')) {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
defined('ABSPATH') || exit;
|
|
5
|
+
|
|
6
|
+
/** Debounce editorial changes; local fixtures never dispatch remote builds. */
|
|
7
|
+
function flowpress_deploy_schedule_public_build(int $delay = 60): void
|
|
8
|
+
{
|
|
9
|
+
if (! in_array(flowpress_env('APP_ENV'), ['staging', 'production'], true)
|
|
10
|
+
|| flowpress_env('CMS_GITHUB_DISPATCH_TOKEN') === ''
|
|
11
|
+
|| flowpress_env('GITHUB_REPOSITORY_SLUG') === ''
|
|
12
|
+
|| wp_next_scheduled('flowpress_deploy_dispatch_public_builds')) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
wp_schedule_single_event(time() + $delay, 'flowpress_deploy_dispatch_public_builds');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function flowpress_deploy_dispatch_public_builds(): void
|
|
19
|
+
{
|
|
20
|
+
if (! function_exists('flowpress_deploy_pending_count')) return;
|
|
21
|
+
global $wpdb;
|
|
22
|
+
foreach (flowpress_static_targets() as $target) {
|
|
23
|
+
if (flowpress_deploy_pending_count($target) === 0) continue;
|
|
24
|
+
$active = (int) $wpdb->get_var($wpdb->prepare(
|
|
25
|
+
'SELECT COUNT(*) FROM ' . flowpress_deploy_queue_table() . ' WHERE target = %s AND batch_id IS NOT NULL', $target
|
|
26
|
+
));
|
|
27
|
+
if ($active > 0) {
|
|
28
|
+
flowpress_deploy_schedule_public_build();
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
$result = flowpress_dispatch_build($target, true);
|
|
32
|
+
$failed = is_wp_error($result);
|
|
33
|
+
update_option('flowpress_deploy_build_dispatch_status', ['failed' => $failed, 'time' => time()], false);
|
|
34
|
+
if ($failed) {
|
|
35
|
+
error_log('FlowPress build dispatch failed: ' . $result->get_error_code());
|
|
36
|
+
flowpress_deploy_schedule_public_build(300);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
add_action('flowpress_deploy_dispatch_public_builds', 'flowpress_deploy_dispatch_public_builds');
|
|
41
|
+
|
|
42
|
+
add_action('save_post', static function (int $id, WP_Post $post): void {
|
|
43
|
+
if (! wp_is_post_revision($id) && ! wp_is_post_autosave($id)
|
|
44
|
+
&& in_array($post->post_type, array_merge(array_values(get_post_types(['public' => true])), ['wp_template', 'wp_template_part', 'wp_navigation', 'wp_block']), true)) {
|
|
45
|
+
flowpress_deploy_schedule_public_build();
|
|
46
|
+
}
|
|
47
|
+
}, 40, 2);
|
|
48
|
+
add_action('before_delete_post', static function (int $id, WP_Post $post): void {
|
|
49
|
+
if (in_array($post->post_type, array_merge(array_values(get_post_types(['public' => true])), ['wp_template', 'wp_template_part', 'wp_navigation', 'wp_block']), true)) flowpress_deploy_schedule_public_build();
|
|
50
|
+
}, 40, 2);
|
|
51
|
+
/** Public taxonomy edits can withdraw routes or change shared archive queries. */
|
|
52
|
+
function flowpress_deploy_taxonomy_affects_public_build(string $taxonomy): bool
|
|
53
|
+
{
|
|
54
|
+
$object = get_taxonomy($taxonomy);
|
|
55
|
+
return $object && (bool) apply_filters('flowpress_deploy_taxonomy_affects_public_build', $object->public, $taxonomy);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Term-only changes bypass save_post, including a public-to-private transition. */
|
|
59
|
+
add_action('set_object_terms', static function (int $id, $terms, $tt_ids, string $taxonomy, bool $append, $old_tt_ids): void {
|
|
60
|
+
$post = get_post($id);
|
|
61
|
+
if (! $post instanceof WP_Post || $post->post_status !== 'publish'
|
|
62
|
+
|| ! (get_post_type_object($post->post_type)->public ?? false)) return;
|
|
63
|
+
$current = array_map('intval', (array) $tt_ids);
|
|
64
|
+
$previous = array_map('intval', (array) $old_tt_ids);
|
|
65
|
+
sort($current); sort($previous);
|
|
66
|
+
if (! $append && $current === $previous) return;
|
|
67
|
+
// Hidden visibility/recipient taxonomies also affect public artifacts.
|
|
68
|
+
// A global descriptor avoids placing private titles or URLs in the queue.
|
|
69
|
+
flowpress_enqueue_build_change('global', 0, '/', 'update', true);
|
|
70
|
+
flowpress_deploy_schedule_public_build();
|
|
71
|
+
}, 40, 6);
|
|
72
|
+
foreach (['created_term', 'edited_term', 'pre_delete_term'] as $hook) {
|
|
73
|
+
$callback = $hook === 'pre_delete_term'
|
|
74
|
+
? static function (int $id, string $taxonomy): void {
|
|
75
|
+
if (flowpress_deploy_taxonomy_affects_public_build($taxonomy)) {
|
|
76
|
+
flowpress_enqueue_build_change('global', 0, '/', 'update', true);
|
|
77
|
+
flowpress_deploy_schedule_public_build();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
: static function (int $id, int $tt_id, string $taxonomy): void {
|
|
81
|
+
if (flowpress_deploy_taxonomy_affects_public_build($taxonomy)) {
|
|
82
|
+
flowpress_enqueue_build_change('global', 0, '/', 'update', true);
|
|
83
|
+
flowpress_deploy_schedule_public_build();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
add_action($hook, $callback, 40, $hook === 'pre_delete_term' ? 2 : 3);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
add_action('admin_notices', static function (): void {
|
|
90
|
+
$status = get_option('flowpress_deploy_build_dispatch_status', []);
|
|
91
|
+
if (! empty($status['failed']) && current_user_can('edit_others_posts')) {
|
|
92
|
+
echo '<div class="notice notice-warning"><p>Avvio automatico della build non riuscito. Controlla la connessione GitHub in <a href="'
|
|
93
|
+
. esc_url(admin_url('tools.php?page=flowpress-builds')) . '">FlowPress Deploy</a>. Le modifiche pubbliche attendono il deploy riuscito.</p></div>';
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
add_action('flowpress_build_change_enqueued', 'flowpress_deploy_schedule_public_build');
|
|
98
|
+
add_action('save_post_wp_block', static function():void { flowpress_deploy_schedule_public_build(); });
|
|
@@ -318,6 +318,8 @@ function flowpress_deploy_collect_change(
|
|
|
318
318
|
string $operation = 'update',
|
|
319
319
|
bool $force_full = false
|
|
320
320
|
): void {
|
|
321
|
+
if (function_exists('flowpress_deploy_schedule_public_build')) flowpress_deploy_schedule_public_build();
|
|
322
|
+
|
|
321
323
|
if ($operation === 'delete') {
|
|
322
324
|
flowpress_deploy_enqueue_change($resource_type, $resource_id, $uri, 'delete', $force_full);
|
|
323
325
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@styleflow.app/flowpress-deploy",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "Safe full and partial deployment primitives for FlowPress.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -16,13 +16,33 @@
|
|
|
16
16
|
".": {
|
|
17
17
|
"types": "./dist/index.d.ts",
|
|
18
18
|
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./frontend": {
|
|
21
|
+
"import": "./dist/frontend-links.js",
|
|
22
|
+
"types": "./dist/frontend-links.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./assets": {
|
|
25
|
+
"import": "./dist/frontend-assets.js",
|
|
26
|
+
"types": "./dist/frontend-assets.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./worker-assets": {
|
|
29
|
+
"import": "./dist/worker-assets.js",
|
|
30
|
+
"types": "./dist/worker-assets.d.ts"
|
|
19
31
|
}
|
|
20
32
|
},
|
|
21
|
-
"files": [
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"includes",
|
|
36
|
+
"docs",
|
|
37
|
+
"flowpress-deploy.php",
|
|
38
|
+
"composer.json",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
22
42
|
"scripts": {
|
|
23
43
|
"build": "tsc -b",
|
|
24
44
|
"typecheck": "tsc -b --pretty false",
|
|
25
|
-
"test": "vitest run && php tests/php/queue-provider-test.php",
|
|
45
|
+
"test": "vitest run && node --test tests/*-regressions.mjs && php tests/php/queue-provider-test.php",
|
|
26
46
|
"release:check": "npm run typecheck && npm test && npm pack --dry-run",
|
|
27
47
|
"release:publish:next": "npm publish --access public --tag next",
|
|
28
48
|
"prepublishOnly": "npm run typecheck && npm test"
|
|
@@ -32,6 +52,8 @@
|
|
|
32
52
|
"typescript": "^5.9.3",
|
|
33
53
|
"vitest": "^3.2.4"
|
|
34
54
|
},
|
|
35
|
-
"engines": {
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=22.0.0"
|
|
57
|
+
},
|
|
36
58
|
"packageManager": "npm@10.9.3"
|
|
37
59
|
}
|