@styleflow.app/flowpress-deploy 1.0.0-beta.1 → 1.0.0-beta.11
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 +66 -0
- package/README.md +6 -29
- package/containers/cms/Dockerfile +35 -0
- package/containers/cms/Dockerfile.dockerignore +20 -0
- package/containers/cms/healthz.php +17 -0
- package/containers/cms/install-languages.sh +16 -0
- package/containers/cms/mpm.conf +10 -0
- package/containers/cms/php.ini +16 -0
- package/containers/cms/sanitize.php +37 -0
- package/containers/cms/smtp.php +13 -0
- package/containers/cms/vhost.conf +61 -0
- package/containers/cms/wp-cli.yml +1 -0
- package/containers/php-runtime/Dockerfile +21 -0
- package/containers/web-ssr/Dockerfile +25 -0
- package/containers/web-ssr/Dockerfile.dockerignore +10 -0
- package/containers/web-static/Caddyfile +40 -0
- package/containers/web-static/Dockerfile +15 -0
- package/containers/web-static/Dockerfile.dockerignore +7 -0
- 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/ops/bootstrap.d.ts +21 -0
- package/dist/ops/bootstrap.d.ts.map +1 -0
- package/dist/ops/bootstrap.js +123 -0
- package/dist/ops/bootstrap.js.map +1 -0
- package/dist/ops/build.d.ts +39 -0
- package/dist/ops/build.d.ts.map +1 -0
- package/dist/ops/build.js +108 -0
- package/dist/ops/build.js.map +1 -0
- package/dist/ops/cli.d.ts +3 -0
- package/dist/ops/cli.d.ts.map +1 -0
- package/dist/ops/cli.js +140 -0
- package/dist/ops/cli.js.map +1 -0
- package/dist/ops/config.d.ts +54 -0
- package/dist/ops/config.d.ts.map +1 -0
- package/dist/ops/config.js +177 -0
- package/dist/ops/config.js.map +1 -0
- package/dist/ops/coolify.d.ts +2 -0
- package/dist/ops/coolify.d.ts.map +1 -0
- package/dist/ops/coolify.js +44 -0
- package/dist/ops/coolify.js.map +1 -0
- package/dist/ops/data.d.ts +27 -0
- package/dist/ops/data.d.ts.map +1 -0
- package/dist/ops/data.js +148 -0
- package/dist/ops/data.js.map +1 -0
- package/dist/ops/deploy.d.ts +18 -0
- package/dist/ops/deploy.d.ts.map +1 -0
- package/dist/ops/deploy.js +100 -0
- package/dist/ops/deploy.js.map +1 -0
- package/dist/ops/editorial.d.ts +23 -0
- package/dist/ops/editorial.d.ts.map +1 -0
- package/dist/ops/editorial.js +79 -0
- package/dist/ops/editorial.js.map +1 -0
- package/dist/ops/environment.d.ts +29 -0
- package/dist/ops/environment.d.ts.map +1 -0
- package/dist/ops/environment.js +159 -0
- package/dist/ops/environment.js.map +1 -0
- package/dist/ops/health.d.ts +25 -0
- package/dist/ops/health.d.ts.map +1 -0
- package/dist/ops/health.js +22 -0
- package/dist/ops/health.js.map +1 -0
- package/dist/ops/monitor.d.ts +23 -0
- package/dist/ops/monitor.d.ts.map +1 -0
- package/dist/ops/monitor.js +143 -0
- package/dist/ops/monitor.js.map +1 -0
- package/dist/ops/process.d.ts +9 -0
- package/dist/ops/process.d.ts.map +1 -0
- package/dist/ops/process.js +48 -0
- package/dist/ops/process.js.map +1 -0
- package/dist/ops/provision.d.ts +41 -0
- package/dist/ops/provision.d.ts.map +1 -0
- package/dist/ops/provision.js +262 -0
- package/dist/ops/provision.js.map +1 -0
- package/dist/ops/registry.d.ts +2 -0
- package/dist/ops/registry.d.ts.map +1 -0
- package/dist/ops/registry.js +36 -0
- package/dist/ops/registry.js.map +1 -0
- package/dist/ops/release.d.ts +26 -0
- package/dist/ops/release.d.ts.map +1 -0
- package/dist/ops/release.js +79 -0
- package/dist/ops/release.js.map +1 -0
- package/dist/ops/s3.d.ts +8 -0
- package/dist/ops/s3.d.ts.map +1 -0
- package/dist/ops/s3.js +38 -0
- package/dist/ops/s3.js.map +1 -0
- package/dist/ops/static-config.d.ts +14 -0
- package/dist/ops/static-config.d.ts.map +1 -0
- package/dist/ops/static-config.js +96 -0
- package/dist/ops/static-config.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 +50 -7
- package/schema/deployment.schema.json +45 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Turns the static Astro build's Cloudflare-style `_redirects` and `_headers`
|
|
3
|
+
// into Caddy directives, plus the /media and /app/uploads proxies that the
|
|
4
|
+
// Pages Functions used to provide. Output: <dist>/.container/routes.caddy.
|
|
5
|
+
//
|
|
6
|
+
// APP_ENV=production node FlowPress Deploy apps/web/dist-production
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
function quote(value) {
|
|
11
|
+
if (/["\s{}]/.test(value) && !/^"[^"]*"$/.test(value)) {
|
|
12
|
+
return `"${value.replaceAll('"', '\\"')}"`;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
// FlowPress routing only emits exact paths (see validateRedirects), so every
|
|
17
|
+
// line is "source target status". Both slash variants of a source redirect.
|
|
18
|
+
export function redirectsToCaddy(text) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
let index = 0;
|
|
21
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
22
|
+
const line = raw.trim();
|
|
23
|
+
if (!line || line.startsWith("#"))
|
|
24
|
+
continue;
|
|
25
|
+
const [source, target, status = "301", ...rest] = line.split(/\s+/);
|
|
26
|
+
if (rest.length || !source?.startsWith("/") || !target) {
|
|
27
|
+
throw new Error(`Unsupported _redirects line: ${line}`);
|
|
28
|
+
}
|
|
29
|
+
if (/[*:]/.test(source)) {
|
|
30
|
+
throw new Error(`Splats and placeholders are not supported: ${line}`);
|
|
31
|
+
}
|
|
32
|
+
if (!["301", "302", "307", "308"].includes(status)) {
|
|
33
|
+
throw new Error(`Unsupported redirect status: ${line}`);
|
|
34
|
+
}
|
|
35
|
+
const variants = source === "/"
|
|
36
|
+
? ["/"]
|
|
37
|
+
: [source.replace(/\/+$/, ""), `${source.replace(/\/+$/, "")}/`];
|
|
38
|
+
index += 1;
|
|
39
|
+
lines.push(`@redirect${index} path ${variants.map(quote).join(" ")}`);
|
|
40
|
+
lines.push(`redir @redirect${index} ${quote(target)} ${status}`);
|
|
41
|
+
}
|
|
42
|
+
return lines;
|
|
43
|
+
}
|
|
44
|
+
export function headersToCaddy(text) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
let pattern = null;
|
|
47
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
48
|
+
if (!raw.trim() || raw.trim().startsWith("#"))
|
|
49
|
+
continue;
|
|
50
|
+
if (!/^\s/.test(raw)) {
|
|
51
|
+
pattern = raw.trim();
|
|
52
|
+
if (!pattern.startsWith("/"))
|
|
53
|
+
throw new Error(`Unsupported _headers pattern: ${pattern}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!pattern)
|
|
57
|
+
throw new Error(`Header without a path pattern: ${raw.trim()}`);
|
|
58
|
+
const separator = raw.indexOf(":");
|
|
59
|
+
if (separator < 0)
|
|
60
|
+
throw new Error(`Unsupported _headers line: ${raw.trim()}`);
|
|
61
|
+
const name = raw.slice(0, separator).trim();
|
|
62
|
+
const value = raw.slice(separator + 1).trim();
|
|
63
|
+
const matcher = pattern === "/*" ? "*" : pattern;
|
|
64
|
+
lines.push(`header ${matcher} ${name} ${quote(value)}`);
|
|
65
|
+
}
|
|
66
|
+
return lines;
|
|
67
|
+
}
|
|
68
|
+
export function proxiesToCaddy({ media, backend }) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
const mediaUrl = new URL(media);
|
|
71
|
+
const mediaBase = mediaUrl.pathname.replace(/\/+$/, "");
|
|
72
|
+
lines.push("@flowpress_media path_regexp flowpress_media ^/media(/.+\\.[a-zA-Z0-9]{1,8})$");
|
|
73
|
+
lines.push("handle @flowpress_media {");
|
|
74
|
+
lines.push(`\trewrite * ${mediaBase}{http.regexp.flowpress_media.1}`);
|
|
75
|
+
lines.push(`\treverse_proxy ${mediaUrl.origin} {`);
|
|
76
|
+
lines.push("\t\theader_up Host {upstream_hostport}");
|
|
77
|
+
lines.push("\t}");
|
|
78
|
+
lines.push("}");
|
|
79
|
+
const backendUrl = new URL(backend);
|
|
80
|
+
lines.push("handle /app/uploads/* {");
|
|
81
|
+
lines.push(`\treverse_proxy ${backendUrl.origin} {`);
|
|
82
|
+
lines.push("\t\theader_up Host {upstream_hostport}");
|
|
83
|
+
lines.push("\t}");
|
|
84
|
+
lines.push("}");
|
|
85
|
+
return lines;
|
|
86
|
+
}
|
|
87
|
+
export function buildRoutesCaddy({ redirects = "", headers = "", media, backend }) {
|
|
88
|
+
return [
|
|
89
|
+
"# Generated by FlowPress Deploy. Do not edit.",
|
|
90
|
+
...redirectsToCaddy(redirects),
|
|
91
|
+
...headersToCaddy(headers),
|
|
92
|
+
...proxiesToCaddy({ media, backend }),
|
|
93
|
+
"",
|
|
94
|
+
].join("\n");
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=static-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-config.js","sourceRoot":"","sources":["../../src/ops/static-config.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.5
|
|
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.5');
|
|
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.11",
|
|
4
4
|
"description": "Safe full and partial deployment primitives for FlowPress.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -16,22 +16,65 @@
|
|
|
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"
|
|
31
|
+
},
|
|
32
|
+
"./ops/environment": {
|
|
33
|
+
"import": "./dist/ops/environment.js",
|
|
34
|
+
"types": "./dist/ops/environment.d.ts"
|
|
35
|
+
},
|
|
36
|
+
"./ops": {
|
|
37
|
+
"import": "./dist/ops/config.js",
|
|
38
|
+
"types": "./dist/ops/config.d.ts"
|
|
19
39
|
}
|
|
20
40
|
},
|
|
21
|
-
"files": [
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"includes",
|
|
44
|
+
"docs",
|
|
45
|
+
"flowpress-deploy.php",
|
|
46
|
+
"composer.json",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"containers",
|
|
50
|
+
"schema"
|
|
51
|
+
],
|
|
22
52
|
"scripts": {
|
|
23
53
|
"build": "tsc -b",
|
|
24
54
|
"typecheck": "tsc -b --pretty false",
|
|
25
|
-
"test": "vitest run && php tests/php/queue-provider-test.php",
|
|
26
|
-
"release:check": "npm run typecheck && npm test && npm pack --dry-run",
|
|
55
|
+
"test": "vitest run tests/deploy.test.ts && node --test tests/*-regressions.mjs && php tests/php/queue-provider-test.php && node --test tests/ops-*.test.mjs",
|
|
56
|
+
"release:check": "npm run typecheck && npm test && npm run test:ops:database && npm run test:ops:caddy && npm pack --dry-run",
|
|
27
57
|
"release:publish:next": "npm publish --access public --tag next",
|
|
28
|
-
"prepublishOnly": "npm run typecheck && npm test"
|
|
58
|
+
"prepublishOnly": "npm run typecheck && npm test",
|
|
59
|
+
"test:ops": "node --test tests/ops-*.test.mjs",
|
|
60
|
+
"test:ops:database": "node tests/ops-database-integration.mjs",
|
|
61
|
+
"test:ops:caddy": "node tests/ops-caddy-integration.mjs"
|
|
29
62
|
},
|
|
30
63
|
"devDependencies": {
|
|
31
64
|
"@types/node": "^22.19.3",
|
|
32
65
|
"typescript": "^5.9.3",
|
|
33
66
|
"vitest": "^3.2.4"
|
|
34
67
|
},
|
|
35
|
-
"engines": {
|
|
36
|
-
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=22.0.0"
|
|
70
|
+
},
|
|
71
|
+
"packageManager": "npm@10.9.3",
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"dotenv": "17.4.2",
|
|
74
|
+
"dotenv-expand": "12.0.3",
|
|
75
|
+
"yaml": "2.9.1"
|
|
76
|
+
},
|
|
77
|
+
"bin": {
|
|
78
|
+
"flowpress-ops": "./dist/ops/cli.js"
|
|
79
|
+
}
|
|
37
80
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "FlowPress deployment manifest",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": ["schemaVersion", "platform", "profile", "database", "media", "environments"],
|
|
6
|
+
"properties": {
|
|
7
|
+
"schemaVersion": { "const": 1 },
|
|
8
|
+
"platform": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
9
|
+
"profile": { "enum": ["backend", "static", "headless-static", "headless-ssr"] },
|
|
10
|
+
"database": { "const": "shared" },
|
|
11
|
+
"media": { "enum": ["local", "s3"] },
|
|
12
|
+
"registryProject": { "type": "string" },
|
|
13
|
+
"sources": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"additionalProperties": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["ssh", "user", "path"],
|
|
18
|
+
"properties": { "ssh": { "type": "string" }, "user": { "type": "string" }, "path": { "type": "string", "pattern": "^/" } },
|
|
19
|
+
"additionalProperties": false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"environments": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"propertyNames": { "enum": ["local", "staging", "production", "preview"] },
|
|
25
|
+
"additionalProperties": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["publish"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"publish": { "type": "array", "uniqueItems": true, "items": { "enum": ["cms", "web-static", "web-ssr"] } },
|
|
30
|
+
"profile": { "enum": ["backend", "static", "headless-static", "headless-ssr"] },
|
|
31
|
+
"frontendUrl": { "type": "string", "format": "uri" },
|
|
32
|
+
"backendUrl": { "type": "string", "format": "uri" },
|
|
33
|
+
"graphqlUrl": { "type": "string", "format": "uri" },
|
|
34
|
+
"mediaUrl": { "type": "string", "format": "uri" },
|
|
35
|
+
"backend": { "enum": ["local", "staging", "production"] },
|
|
36
|
+
"aliases": { "type": "array", "items": { "type": "string", "format": "uri" } },
|
|
37
|
+
"mode": { "const": "on-demand" },
|
|
38
|
+
"mail": { "enum": ["disabled", "capture", "smtp"] }
|
|
39
|
+
},
|
|
40
|
+
"additionalProperties": false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
}
|