@styleflow.app/flowpress-deploy 1.0.0-beta.3 → 1.0.0-beta.31

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.
Files changed (175) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/README.md +5 -0
  3. package/composer.json +1 -1
  4. package/containers/cms/Dockerfile +35 -0
  5. package/containers/cms/Dockerfile.dockerignore +20 -0
  6. package/containers/cms/healthz.php +17 -0
  7. package/containers/cms/install-languages.sh +16 -0
  8. package/containers/cms/mpm.conf +10 -0
  9. package/containers/cms/php.ini +16 -0
  10. package/containers/cms/sanitize-integrations.php +14 -0
  11. package/containers/cms/sanitize.php +40 -0
  12. package/containers/cms/smtp.php +13 -0
  13. package/containers/cms/vhost.conf +61 -0
  14. package/containers/cms/wp-cli.yml +1 -0
  15. package/containers/local/cms/Dockerfile +18 -0
  16. package/containers/local/cms/entrypoint.sh +7 -0
  17. package/containers/local/cms/php.ini +8 -0
  18. package/containers/local/cms/vhost.conf +14 -0
  19. package/containers/local/runtimes.json +14 -0
  20. package/containers/local/web/Dockerfile +8 -0
  21. package/containers/local/web/entrypoint.sh +21 -0
  22. package/containers/php-runtime/Dockerfile +21 -0
  23. package/containers/web-ssr/Dockerfile +27 -0
  24. package/containers/web-ssr/Dockerfile.dockerignore +10 -0
  25. package/containers/web-static/Caddyfile +40 -0
  26. package/containers/web-static/Dockerfile +17 -0
  27. package/containers/web-static/Dockerfile.dockerignore +7 -0
  28. package/dist/development.d.ts +12 -0
  29. package/dist/development.d.ts.map +1 -0
  30. package/dist/development.js +57 -0
  31. package/dist/development.js.map +1 -0
  32. package/dist/node-fetch.d.ts +3 -0
  33. package/dist/node-fetch.d.ts.map +1 -0
  34. package/dist/node-fetch.js +20 -0
  35. package/dist/node-fetch.js.map +1 -0
  36. package/dist/ops/bootstrap.d.ts +24 -0
  37. package/dist/ops/bootstrap.d.ts.map +1 -0
  38. package/dist/ops/bootstrap.js +156 -0
  39. package/dist/ops/bootstrap.js.map +1 -0
  40. package/dist/ops/build.d.ts +45 -0
  41. package/dist/ops/build.d.ts.map +1 -0
  42. package/dist/ops/build.js +119 -0
  43. package/dist/ops/build.js.map +1 -0
  44. package/dist/ops/check.d.ts +14 -0
  45. package/dist/ops/check.d.ts.map +1 -0
  46. package/dist/ops/check.js +42 -0
  47. package/dist/ops/check.js.map +1 -0
  48. package/dist/ops/cli.d.ts +3 -0
  49. package/dist/ops/cli.d.ts.map +1 -0
  50. package/dist/ops/cli.js +198 -0
  51. package/dist/ops/cli.js.map +1 -0
  52. package/dist/ops/cms-migrate.d.ts +14 -0
  53. package/dist/ops/cms-migrate.d.ts.map +1 -0
  54. package/dist/ops/cms-migrate.js +133 -0
  55. package/dist/ops/cms-migrate.js.map +1 -0
  56. package/dist/ops/cms-template/command.d.mts +19 -0
  57. package/dist/ops/cms-template/command.d.mts.map +1 -0
  58. package/dist/ops/cms-template/command.mjs +145 -0
  59. package/dist/ops/cms-template/command.mjs.map +1 -0
  60. package/dist/ops/cms-template/confirm.d.mts +11 -0
  61. package/dist/ops/cms-template/confirm.d.mts.map +1 -0
  62. package/dist/ops/cms-template/confirm.mjs +18 -0
  63. package/dist/ops/cms-template/confirm.mjs.map +1 -0
  64. package/dist/ops/cms-template/entry.d.mts +2 -0
  65. package/dist/ops/cms-template/entry.d.mts.map +1 -0
  66. package/dist/ops/cms-template/entry.mjs +22 -0
  67. package/dist/ops/cms-template/entry.mjs.map +1 -0
  68. package/dist/ops/cms-template/git.d.mts +7 -0
  69. package/dist/ops/cms-template/git.d.mts.map +1 -0
  70. package/dist/ops/cms-template/git.mjs +22 -0
  71. package/dist/ops/cms-template/git.mjs.map +1 -0
  72. package/dist/ops/cms-template/plan.d.mts +7 -0
  73. package/dist/ops/cms-template/plan.d.mts.map +1 -0
  74. package/dist/ops/cms-template/plan.mjs +54 -0
  75. package/dist/ops/cms-template/plan.mjs.map +1 -0
  76. package/dist/ops/cms-template/store.d.mts +21 -0
  77. package/dist/ops/cms-template/store.d.mts.map +1 -0
  78. package/dist/ops/cms-template/store.mjs +184 -0
  79. package/dist/ops/cms-template/store.mjs.map +1 -0
  80. package/dist/ops/cms-template/target.d.mts +15 -0
  81. package/dist/ops/cms-template/target.d.mts.map +1 -0
  82. package/dist/ops/cms-template/target.mjs +24 -0
  83. package/dist/ops/cms-template/target.mjs.map +1 -0
  84. package/dist/ops/cms-template/transport.d.mts +26 -0
  85. package/dist/ops/cms-template/transport.d.mts.map +1 -0
  86. package/dist/ops/cms-template/transport.mjs +115 -0
  87. package/dist/ops/cms-template/transport.mjs.map +1 -0
  88. package/dist/ops/config.d.ts +54 -0
  89. package/dist/ops/config.d.ts.map +1 -0
  90. package/dist/ops/config.js +221 -0
  91. package/dist/ops/config.js.map +1 -0
  92. package/dist/ops/coolify.d.ts +2 -0
  93. package/dist/ops/coolify.d.ts.map +1 -0
  94. package/dist/ops/coolify.js +44 -0
  95. package/dist/ops/coolify.js.map +1 -0
  96. package/dist/ops/data.d.ts +31 -0
  97. package/dist/ops/data.d.ts.map +1 -0
  98. package/dist/ops/data.js +165 -0
  99. package/dist/ops/data.js.map +1 -0
  100. package/dist/ops/deploy.d.ts +38 -0
  101. package/dist/ops/deploy.d.ts.map +1 -0
  102. package/dist/ops/deploy.js +127 -0
  103. package/dist/ops/deploy.js.map +1 -0
  104. package/dist/ops/editorial.d.ts +23 -0
  105. package/dist/ops/editorial.d.ts.map +1 -0
  106. package/dist/ops/editorial.js +79 -0
  107. package/dist/ops/editorial.js.map +1 -0
  108. package/dist/ops/environment-files.d.ts +9 -0
  109. package/dist/ops/environment-files.d.ts.map +1 -0
  110. package/dist/ops/environment-files.js +127 -0
  111. package/dist/ops/environment-files.js.map +1 -0
  112. package/dist/ops/environment.d.ts +31 -0
  113. package/dist/ops/environment.d.ts.map +1 -0
  114. package/dist/ops/environment.js +165 -0
  115. package/dist/ops/environment.js.map +1 -0
  116. package/dist/ops/features.d.ts +12 -0
  117. package/dist/ops/features.d.ts.map +1 -0
  118. package/dist/ops/features.js +71 -0
  119. package/dist/ops/features.js.map +1 -0
  120. package/dist/ops/health.d.ts +26 -0
  121. package/dist/ops/health.d.ts.map +1 -0
  122. package/dist/ops/health.js +27 -0
  123. package/dist/ops/health.js.map +1 -0
  124. package/dist/ops/local.d.ts +198 -0
  125. package/dist/ops/local.d.ts.map +1 -0
  126. package/dist/ops/local.js +187 -0
  127. package/dist/ops/local.js.map +1 -0
  128. package/dist/ops/media-config.d.ts +61 -0
  129. package/dist/ops/media-config.d.ts.map +1 -0
  130. package/dist/ops/media-config.js +96 -0
  131. package/dist/ops/media-config.js.map +1 -0
  132. package/dist/ops/media.d.ts +47 -0
  133. package/dist/ops/media.d.ts.map +1 -0
  134. package/dist/ops/media.js +81 -0
  135. package/dist/ops/media.js.map +1 -0
  136. package/dist/ops/monitor.d.ts +25 -0
  137. package/dist/ops/monitor.d.ts.map +1 -0
  138. package/dist/ops/monitor.js +143 -0
  139. package/dist/ops/monitor.js.map +1 -0
  140. package/dist/ops/process.d.ts +9 -0
  141. package/dist/ops/process.d.ts.map +1 -0
  142. package/dist/ops/process.js +56 -0
  143. package/dist/ops/process.js.map +1 -0
  144. package/dist/ops/provision.d.ts +41 -0
  145. package/dist/ops/provision.d.ts.map +1 -0
  146. package/dist/ops/provision.js +268 -0
  147. package/dist/ops/provision.js.map +1 -0
  148. package/dist/ops/registry.d.ts +14 -0
  149. package/dist/ops/registry.d.ts.map +1 -0
  150. package/dist/ops/registry.js +112 -0
  151. package/dist/ops/registry.js.map +1 -0
  152. package/dist/ops/release.d.ts +26 -0
  153. package/dist/ops/release.d.ts.map +1 -0
  154. package/dist/ops/release.js +79 -0
  155. package/dist/ops/release.js.map +1 -0
  156. package/dist/ops/runtimes.d.ts +16 -0
  157. package/dist/ops/runtimes.d.ts.map +1 -0
  158. package/dist/ops/runtimes.js +70 -0
  159. package/dist/ops/runtimes.js.map +1 -0
  160. package/dist/ops/s3.d.ts +8 -0
  161. package/dist/ops/s3.d.ts.map +1 -0
  162. package/dist/ops/s3.js +38 -0
  163. package/dist/ops/s3.js.map +1 -0
  164. package/dist/ops/static-config.d.ts +14 -0
  165. package/dist/ops/static-config.d.ts.map +1 -0
  166. package/dist/ops/static-config.js +92 -0
  167. package/dist/ops/static-config.js.map +1 -0
  168. package/dist/ops/storage.d.ts +39 -0
  169. package/dist/ops/storage.d.ts.map +1 -0
  170. package/dist/ops/storage.js +108 -0
  171. package/dist/ops/storage.js.map +1 -0
  172. package/flowpress-deploy.php +2 -2
  173. package/includes/pilot-aliases.php +69 -0
  174. package/package.json +43 -6
  175. package/schema/deployment.schema.json +306 -0
@@ -0,0 +1,70 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, readdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { capture, run } from "./process.js";
6
+ export const runtimeRoot = fileURLToPath(new URL("../../containers/local/", import.meta.url));
7
+ export const runtimeKinds = ["cms", "web"];
8
+ export const managedLabel = "io.flowpress.managed";
9
+ export function recipeHash(kind, directory = runtimeRoot) {
10
+ if (!runtimeKinds.includes(kind))
11
+ throw new Error("Unknown local runtime.");
12
+ const hash = createHash("sha256");
13
+ function visit(relative) {
14
+ for (const item of readdirSync(join(directory, relative), { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name, "en"))) {
15
+ const path = relative + "/" + item.name;
16
+ if (item.isDirectory())
17
+ visit(path);
18
+ else if (item.isFile())
19
+ hash.update(path + "\0").update(readFileSync(join(directory, path))).update("\0");
20
+ else
21
+ throw new Error("Runtime recipes must contain regular files only.");
22
+ }
23
+ }
24
+ visit(kind);
25
+ return hash.digest("hex");
26
+ }
27
+ export function runtimeCatalog(directory = runtimeRoot) {
28
+ const catalog = JSON.parse(readFileSync(join(directory, "runtimes.json"), "utf8"));
29
+ if (catalog.schemaVersion !== 1 || !/^[a-z0-9.-]+(?::[0-9]+)?\/flowpress$/.test(catalog.repository || ""))
30
+ throw new Error("Invalid shared runtime catalog.");
31
+ for (const kind of runtimeKinds) {
32
+ const item = catalog.runtimes?.[kind];
33
+ if (!item || item.recipe !== recipeHash(kind, directory))
34
+ throw new Error("Runtime recipe changed: build in CI and update the catalog before release (" + kind + ").");
35
+ if (!/^sha256:[a-f0-9]{64}$/.test(item.digest || ""))
36
+ throw new Error("Runtime not published: update its verified registry digest (" + kind + ").");
37
+ }
38
+ return catalog;
39
+ }
40
+ export function runtimeImages(catalog = runtimeCatalog()) {
41
+ return Object.fromEntries(runtimeKinds.map(kind => [kind, catalog.repository + "/local-" + kind + "@" + catalog.runtimes[kind].digest]));
42
+ }
43
+ export function assertRuntimeMetadata(metadata, kind, recipe, expectedPlatforms, digest) {
44
+ if (!metadata || digest && metadata.digest !== digest)
45
+ throw new Error("Missing or different immutable runtime: " + kind);
46
+ if (JSON.stringify(metadata.platforms.map(item => item.platform).sort()) !== JSON.stringify([...expectedPlatforms].sort()))
47
+ throw new Error("Incorrect runtime architectures: " + kind);
48
+ for (const item of metadata.platforms) {
49
+ const labels = item.labels;
50
+ if (labels["io.flowpress.managed"] !== "true" || labels["io.flowpress.kind"] !== "local-runtime" || labels["io.flowpress.runtime"] !== kind || labels["io.flowpress.recipe"] !== recipe)
51
+ throw new Error("Runtime recipe/ownership mismatch: " + kind);
52
+ }
53
+ return metadata;
54
+ }
55
+ export function ensureLocalImages(images, docker = { capture, run }) {
56
+ for (const image of [...new Set(images)]) {
57
+ try {
58
+ docker.capture("docker", ["image", "inspect", image], { stdio: ["ignore", "pipe", "ignore"] });
59
+ }
60
+ catch {
61
+ try {
62
+ docker.run("docker", ["pull", image]);
63
+ }
64
+ catch {
65
+ throw new Error("Cannot download " + image + ". Connect to the Tailscale network and run docker login registry.smxapp.com with your read-only account, then retry pnpm dev. Cached images do not require network access.");
66
+ }
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=runtimes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimes.js","sourceRoot":"","sources":["../../src/ops/runtimes.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export function s3Put(bucket: any, key: any, file: any): void;
2
+ export function s3Get(bucket: any, key: any, file: any): void;
3
+ export function s3List(bucket: any, prefix: any, request?: (url: any) => any): {
4
+ key: any;
5
+ modified: any;
6
+ size: number;
7
+ }[];
8
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/ops/s3.js"],"names":[],"mappings":"AAQA,8DAEC;AAED,8DAGC;AAID,4DAAkD,QAAG;;;;IAkBpD"}
package/dist/ops/s3.js ADDED
@@ -0,0 +1,38 @@
1
+ import { chmodSync } from "node:fs";
2
+ import { capture } from "./process.js";
3
+ function curl(bucket, args) {
4
+ return capture("curl", ["-K", "-", "--fail-with-body", "--max-time", "60", "-sS", "--aws-sigv4", "aws:amz:" + (bucket.region || "auto") + ":s3", ...args], {
5
+ input: `user = "${bucket.user}"\n`,
6
+ });
7
+ }
8
+ export function s3Put(bucket, key, file) {
9
+ curl(bucket, ["-T", file, `${bucket.endpoint}/${bucket.bucket}/${key}`]);
10
+ }
11
+ export function s3Get(bucket, key, file) {
12
+ curl(bucket, ["-o", file, `${bucket.endpoint}/${bucket.bucket}/${key}`]);
13
+ chmodSync(file, 0o600); // backups contain personal data and password hashes
14
+ }
15
+ const decodeXml = (value) => value.replaceAll("&amp;", "&").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&quot;", '"').replaceAll("&apos;", "'");
16
+ export function s3List(bucket, prefix, request = (url) => curl(bucket, [url])) {
17
+ const objects = [];
18
+ const tokens = new Set();
19
+ let token;
20
+ do {
21
+ const query = new URLSearchParams({ "list-type": "2", prefix });
22
+ if (token)
23
+ query.set("continuation-token", token);
24
+ const xml = request(`${bucket.endpoint}/${bucket.bucket}?${query}`);
25
+ const field = (source, name) => decodeXml(source.match(new RegExp(`<${name}>([^<]*)</${name}>`))?.[1] ?? "");
26
+ objects.push(...[...xml.matchAll(/<Contents>(.*?)<\/Contents>/gs)].map(([, block]) => ({
27
+ key: field(block, "Key"), modified: field(block, "LastModified"), size: Number(field(block, "Size")),
28
+ })));
29
+ if (field(xml, "IsTruncated") !== "true")
30
+ break;
31
+ token = field(xml, "NextContinuationToken");
32
+ if (!token || tokens.has(token))
33
+ throw new Error("S3 returned an invalid pagination token.");
34
+ tokens.add(token);
35
+ } while (token);
36
+ return objects;
37
+ }
38
+ //# sourceMappingURL=s3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/ops/s3.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ export function redirectsToCaddy(text: any): string[];
3
+ export function headersToCaddy(text: any): string[];
4
+ export function mediaRedirectsToCaddy({ media, backend }: {
5
+ media: any;
6
+ backend: any;
7
+ }): string[];
8
+ export function buildRoutesCaddy({ redirects, headers, media, backend }: {
9
+ redirects?: string | undefined;
10
+ headers?: string | undefined;
11
+ media: any;
12
+ backend: any;
13
+ }): string;
14
+ //# sourceMappingURL=static-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-config.d.ts","sourceRoot":"","sources":["../../src/ops/static-config.js"],"names":[],"mappings":";AAmBA,sDAwBC;AAED,oDAmBC;AAED;;;aAeC;AAED;;;;;WAQC"}
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ // Turns the static Astro build's Cloudflare-style `_redirects` and `_headers`
3
+ // into Caddy directives, plus permanent redirects for historical media URLs. Output: <dist>/.container/routes.caddy.
4
+ //
5
+ // APP_ENV=production node FlowPress Deploy apps/web/dist-production
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { resolve } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ function quote(value) {
10
+ if (/["\s{}]/.test(value) && !/^"[^"]*"$/.test(value)) {
11
+ return `"${value.replaceAll('"', '\\"')}"`;
12
+ }
13
+ return value;
14
+ }
15
+ // FlowPress routing only emits exact paths (see validateRedirects), so every
16
+ // line is "source target status". Both slash variants of a source redirect.
17
+ export function redirectsToCaddy(text) {
18
+ const lines = [];
19
+ let index = 0;
20
+ for (const raw of text.split(/\r?\n/)) {
21
+ const line = raw.trim();
22
+ if (!line || line.startsWith("#"))
23
+ continue;
24
+ const [source, target, status = "301", ...rest] = line.split(/\s+/);
25
+ if (rest.length || !source?.startsWith("/") || !target) {
26
+ throw new Error(`Unsupported _redirects line: ${line}`);
27
+ }
28
+ if (/[*:]/.test(source)) {
29
+ throw new Error(`Splats and placeholders are not supported: ${line}`);
30
+ }
31
+ if (!["301", "302", "307", "308"].includes(status)) {
32
+ throw new Error(`Unsupported redirect status: ${line}`);
33
+ }
34
+ const variants = source === "/"
35
+ ? ["/"]
36
+ : [source.replace(/\/+$/, ""), `${source.replace(/\/+$/, "")}/`];
37
+ index += 1;
38
+ lines.push(`@redirect${index} path ${variants.map(quote).join(" ")}`);
39
+ lines.push(`redir @redirect${index} ${quote(target)} ${status}`);
40
+ }
41
+ return lines;
42
+ }
43
+ export function headersToCaddy(text) {
44
+ const lines = [];
45
+ let pattern = null;
46
+ for (const raw of text.split(/\r?\n/)) {
47
+ if (!raw.trim() || raw.trim().startsWith("#"))
48
+ continue;
49
+ if (!/^\s/.test(raw)) {
50
+ pattern = raw.trim();
51
+ if (!pattern.startsWith("/"))
52
+ throw new Error(`Unsupported _headers pattern: ${pattern}`);
53
+ continue;
54
+ }
55
+ if (!pattern)
56
+ throw new Error(`Header without a path pattern: ${raw.trim()}`);
57
+ const separator = raw.indexOf(":");
58
+ if (separator < 0)
59
+ throw new Error(`Unsupported _headers line: ${raw.trim()}`);
60
+ const name = raw.slice(0, separator).trim();
61
+ const value = raw.slice(separator + 1).trim();
62
+ const matcher = pattern === "/*" ? "*" : pattern;
63
+ lines.push(`header ${matcher} ${name} ${quote(value)}`);
64
+ }
65
+ return lines;
66
+ }
67
+ export function mediaRedirectsToCaddy({ media, backend }) {
68
+ const mediaUrl = new URL(media), backendUrl = new URL(backend);
69
+ const mediaBase = mediaUrl.pathname.replace(/\/+$/, "");
70
+ return [
71
+ "@flowpress_media path_regexp flowpress_media ^/media(/.+\\.[a-zA-Z0-9]{1,8})$",
72
+ "handle @flowpress_media {",
73
+ "\troute {",
74
+ `\t\trewrite * ${mediaBase}{http.regexp.flowpress_media.1}`,
75
+ `\t\tredir ${mediaUrl.origin}{uri} 308`,
76
+ "\t}",
77
+ "}",
78
+ "handle /app/uploads/* {",
79
+ `\tredir ${backendUrl.origin}{uri} 308`,
80
+ "}",
81
+ ];
82
+ }
83
+ export function buildRoutesCaddy({ redirects = "", headers = "", media, backend }) {
84
+ return [
85
+ "# Generated by FlowPress Deploy. Do not edit.",
86
+ ...redirectsToCaddy(redirects),
87
+ ...headersToCaddy(headers),
88
+ ...mediaRedirectsToCaddy({ media, backend }),
89
+ "",
90
+ ].join("\n");
91
+ }
92
+ //# 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,39 @@
1
+ export function dockerInventory(docker?: {
2
+ capture: typeof capture;
3
+ }): {
4
+ images: any;
5
+ containers: any;
6
+ usage: any;
7
+ details: any;
8
+ };
9
+ export function storageScope(root: any, manifest: any): {
10
+ project: string;
11
+ runtimeRepository: any;
12
+ currentRuntimes: string[];
13
+ legacyImages: string[];
14
+ legacyRepositories: string[];
15
+ };
16
+ export function cleanupPlan(inventory: any, scope: any, { includeLegacy }?: {
17
+ includeLegacy?: boolean | undefined;
18
+ }): any;
19
+ export function cleanLocalImages(inventory: any, scope: any, options?: {}, docker?: {
20
+ capture: typeof capture;
21
+ run: typeof run;
22
+ }): {
23
+ project: any;
24
+ applied: boolean;
25
+ candidates: any;
26
+ deleted: any[];
27
+ skipped: {
28
+ id: any;
29
+ reason: string;
30
+ }[];
31
+ preserved: string;
32
+ usage: any;
33
+ };
34
+ export function printLocalStorage(result: any, { clean }?: {
35
+ clean?: boolean | undefined;
36
+ }): void;
37
+ import { capture } from "./process.js";
38
+ import { run } from "./process.js";
39
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/ops/storage.js"],"names":[],"mappings":"AAMA;;;;;;;EAQC;AACD;;;;;;EAQC;AACD;;QA8BC;AACD;;;;;;;;;;;;;;EAgBC;AAED;;SAiBC;wBA1F4B,cAAc;oBAAd,cAAc"}
@@ -0,0 +1,108 @@
1
+ import { capture, run } from "./process.js";
2
+ import { identity, platformConfig } from "./config.js";
3
+ import { managedLabel, runtimeCatalog, runtimeImages } from "./runtimes.js";
4
+ const lines = text => text.trim().split("\n").filter(Boolean);
5
+ const objects = text => lines(text).map(line => JSON.parse(line));
6
+ export function dockerInventory(docker = { capture }) {
7
+ const ids = lines(docker.capture("docker", ["image", "ls", "-aq", "--no-trunc"]));
8
+ const containers = lines(docker.capture("docker", ["ps", "-aq", "--no-trunc"]));
9
+ const images = ids.length ? JSON.parse(docker.capture("docker", ["image", "inspect", ...new Set(ids)])) : [];
10
+ const references = containers.length ? JSON.parse(docker.capture("docker", ["inspect", ...containers])).map(item => ({ id: item.Id, name: item.Name.replace(/^\//, ""), image: item.Image })) : [];
11
+ const usage = objects(docker.capture("docker", ["system", "df", "--format", "{{json .}}"]));
12
+ const details = JSON.parse(docker.capture("docker", ["system", "df", "-v", "--format", "{{json .}}"]));
13
+ return { images, containers: references, usage, details };
14
+ }
15
+ export function storageScope(root, manifest) {
16
+ const project = identity(root).slug;
17
+ let platform;
18
+ try {
19
+ platform = platformConfig(root, manifest);
20
+ }
21
+ catch { /* A new local-only project may have no platform binding yet. */ }
22
+ const catalog = runtimeCatalog();
23
+ return { project, runtimeRepository: catalog.repository, currentRuntimes: Object.values(runtimeImages(catalog)),
24
+ legacyImages: ["flowpress-local-web:22.23.2"],
25
+ legacyRepositories: [project + "-cms", project + "-local-cms", ...(platform ? ["cms", "web-static", "web-ssr"].map(kind => platform.registry + "/" + platform.registryProject + "/" + project + "-" + kind) : [])] };
26
+ }
27
+ export function cleanupPlan(inventory, scope, { includeLegacy = false } = {}) {
28
+ const used = new Map();
29
+ for (const item of inventory.containers)
30
+ used.set(item.image, [...(used.get(item.image) || []), item.name]);
31
+ const images = inventory.images.map(item => ({
32
+ id: item.Id, tags: (item.RepoTags || []).filter(tag => tag !== "<none>:<none>"), digests: item.RepoDigests || [],
33
+ created: item.Created, architecture: item.Os + "/" + item.Architecture, size: item.Size, labels: item.Config?.Labels || {},
34
+ sharedSize: inventory.details?.Images?.find(row => row.ID === item.Id)?.SharedSize,
35
+ uniqueSize: inventory.details?.Images?.find(row => row.ID === item.Id)?.UniqueSize,
36
+ }));
37
+ const matches = (item, reference) => [...item.tags, ...item.digests].includes(reference);
38
+ const runtimes = images.filter(item => item.labels[managedLabel] === "true" && item.labels["io.flowpress.kind"] === "local-runtime"
39
+ && ["cms", "web"].includes(item.labels["io.flowpress.runtime"]) && [...item.tags, ...item.digests].some(ref => ref.startsWith(scope.runtimeRepository + "/local-" + item.labels["io.flowpress.runtime"] + ":") || ref.startsWith(scope.runtimeRepository + "/local-" + item.labels["io.flowpress.runtime"] + "@")));
40
+ const keep = new Set(runtimes.filter(item => scope.currentRuntimes.some(ref => matches(item, ref))).map(item => item.id));
41
+ for (const group of new Set(runtimes.map(item => item.labels["io.flowpress.runtime"] + ":" + item.architecture))) {
42
+ const previous = runtimes.filter(item => item.labels["io.flowpress.runtime"] + ":" + item.architecture === group && !keep.has(item.id)).sort((a, b) => b.created.localeCompare(a.created))[0];
43
+ if (previous)
44
+ keep.add(previous.id);
45
+ }
46
+ return images.map(item => {
47
+ const shared = runtimes.some(runtime => runtime.id === item.id);
48
+ const application = item.labels[managedLabel] === "true" && item.labels["io.flowpress.kind"] === "application" && item.labels["io.flowpress.project"] === scope.project;
49
+ const legacy = !shared && !application && item.labels[managedLabel] !== "true"
50
+ && (!item.labels["com.docker.compose.project"] || item.labels["com.docker.compose.project"] === scope.project)
51
+ && item.tags.length > 0 && item.tags.every(ref => scope.legacyRepositories.includes(ref.slice(0, ref.lastIndexOf(":"))) || scope.legacyImages?.includes(ref));
52
+ const ownedTags = shared ? item.tags.filter(ref => ref.startsWith(scope.runtimeRepository + "/local-")) : item.tags.filter(ref => scope.legacyRepositories.includes(ref.slice(0, ref.lastIndexOf(":"))) || scope.legacyImages?.includes(ref));
53
+ const foreignTag = item.tags.some(ref => !ownedTags.includes(ref));
54
+ const users = used.get(item.id) || [];
55
+ const reason = users.length ? "referenced-by-container" : shared && keep.has(item.id) ? "retained-runtime" : foreignTag || item.tags.length > 1 ? "other-image-reference" : application || shared ? "unused-flowpress-image" : legacy ? includeLegacy ? "unused-legacy-image" : "legacy-requires-opt-in" : "outside-project";
56
+ const eligible = ["unused-flowpress-image", "unused-legacy-image"].includes(reason);
57
+ return { ...item, labels: undefined, users, category: shared ? "shared-runtime" : application ? "application" : legacy ? "legacy" : "other", reason, eligible };
58
+ });
59
+ }
60
+ export function cleanLocalImages(inventory, scope, options = {}, docker = { capture, run }) {
61
+ const plan = cleanupPlan(inventory, scope, options), deleted = [], skipped = [];
62
+ if (options.apply)
63
+ for (const candidate of plan.filter(item => item.eligible)) {
64
+ // Refresh ownership, tags and every container immediately before each deletion.
65
+ const fresh = dockerInventory(docker);
66
+ const current = cleanupPlan(fresh, scope, options).find(item => item.id === candidate.id);
67
+ if (!current?.eligible || JSON.stringify([...current.tags].sort()) !== JSON.stringify([...candidate.tags].sort()) || JSON.stringify([...current.digests].sort()) !== JSON.stringify([...candidate.digests].sort())) {
68
+ skipped.push({ id: candidate.id, reason: "inventory-changed" });
69
+ continue;
70
+ }
71
+ try {
72
+ // The ID cannot be redirected by a concurrent retag; Docker refuses
73
+ // deletion if a container or another tag acquired it in the meantime.
74
+ docker.run("docker", ["image", "rm", current.id], { stdio: ["ignore", "ignore", "pipe"] });
75
+ deleted.push(current.id);
76
+ }
77
+ catch {
78
+ skipped.push({ id: current.id, reason: "docker-refused-removal" });
79
+ }
80
+ }
81
+ return { project: scope.project, applied: Boolean(options.apply), candidates: plan.filter(item => item.category !== "other"), deleted, skipped,
82
+ preserved: "All volumes, containers, networks and build caches; images referenced by running or stopped containers.", usage: inventory.usage };
83
+ }
84
+ export function printLocalStorage(result, { clean = false } = {}) {
85
+ const reasons = { "referenced-by-container": "usata da container", "retained-runtime": "runtime conservato", "other-image-reference": "altri riferimenti", "unused-flowpress-image": "eliminabile", "unused-legacy-image": "legacy eliminabile", "legacy-requires-opt-in": "legacy: serve --include-legacy", "outside-project": "altro progetto" };
86
+ console.log("Docker · " + result.project);
87
+ for (const row of result.usage)
88
+ console.log(`${row.Type}: ${row.Size} · recuperabile globale ${row.Reclaimable}`);
89
+ console.log("\nImmagini del progetto e runtime comuni (dimensioni uniche / condivise):");
90
+ for (const item of result.images || result.candidates) {
91
+ const reference = item.tags[0] || item.digests[0] || item.id;
92
+ console.log(` ${reference.split("@")[0]} [${item.id.replace("sha256:", "").slice(0, 12)}] · ${item.uniqueSize || "?"} / ${item.sharedSize || "?"} · ${reasons[item.reason]}`);
93
+ }
94
+ if (result.volumes?.length) {
95
+ console.log("\nVolumi del progetto (solo inventario):");
96
+ for (const volume of result.volumes)
97
+ console.log(" " + volume.Name + " · " + volume.Size);
98
+ }
99
+ if (clean) {
100
+ if (result.applied)
101
+ console.log(`\nPulizia completata: ${result.deleted.length} immagini eliminate, ${result.skipped.length} saltate. Volumi e cache conservati.`);
102
+ else
103
+ console.log(`\nAnteprima: ${result.candidates.filter(item => item.eligible).length} immagini eliminabili. Esegui pnpm ops local clean --apply per applicare la pulizia.`);
104
+ }
105
+ else
106
+ console.log("\nLe dimensioni condivise non si sommano. La pulizia agisce solo sulle immagini previste: pnpm ops local clean.");
107
+ }
108
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/ops/storage.js"],"names":[],"mappings":"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"}
@@ -2,14 +2,14 @@
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.3
5
+ * Version: 1.0.0-beta.31
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.3');
12
+ define('FLOWPRESS_DEPLOY_VERSION', '1.0.0-beta.31');
13
13
  define('FLOWPRESS_DEPLOY_FILE', __FILE__);
14
14
 
15
15
  require_once __DIR__ . '/includes/queue-provider.php';
@@ -0,0 +1,69 @@
1
+ <?php
2
+ /** Explicit WP-CLI migration library; never loaded during normal requests. */
3
+ declare(strict_types=1);
4
+
5
+ function flowpress_ops_alias_value(mixed $value, bool &$changed): mixed
6
+ {
7
+ if (!is_array($value)) return $value;
8
+ $aliases = ['project/post-field' => 'sf/post-field', 'project/term-field' => 'sf/term-field', 'project/block-binding' => 'sf/block-binding'];
9
+ $next = [];
10
+ foreach ($value as $key => $item) {
11
+ $nextKey = $key === 'project/variables' ? 'sf/variables' : $key;
12
+ if ($nextKey !== $key) $changed = true;
13
+ if ($key === 'blockName' && is_string($item) && str_starts_with($item, 'project/')) {
14
+ if (!isset($aliases[$item])) throw new RuntimeException('Unknown pilot block: ' . $item);
15
+ $item = $aliases[$item];
16
+ $changed = true;
17
+ } elseif ($key === 'source' && is_string($item) && str_starts_with($item, 'project/')) {
18
+ if ($item !== 'project/variable') throw new RuntimeException('Unknown pilot binding: ' . $item);
19
+ $item = 'sf/variable';
20
+ $changed = true;
21
+ }
22
+ $next[$nextKey] = is_array($item) ? flowpress_ops_alias_value($item, $changed) : $item;
23
+ }
24
+ return $next;
25
+ }
26
+
27
+ function flowpress_ops_alias_plan(): array
28
+ {
29
+ global $wpdb;
30
+ $rows = $wpdb->get_results("SELECT ID, post_type, post_status, post_content FROM {$wpdb->posts} ORDER BY ID", ARRAY_A);
31
+ if (!is_array($rows) || $wpdb->last_error) throw new RuntimeException('Cannot inventory WordPress posts.');
32
+ $state = [];
33
+ $changes = [];
34
+ $counts = [];
35
+ foreach ($rows as $row) {
36
+ $state[] = [(int)$row['ID'], $row['post_type'], $row['post_status'], hash('sha256', $row['post_content'])];
37
+ $changed = false;
38
+ $blocks = flowpress_ops_alias_value(parse_blocks($row['post_content']), $changed);
39
+ if (!$changed) continue;
40
+ $content = serialize_blocks($blocks);
41
+ $changes[] = ['id' => (int)$row['ID'], 'type' => $row['post_type'], 'status' => $row['post_status'], 'before' => $row['post_content'], 'after' => $content];
42
+ $key = $row['post_type'] . ':' . $row['post_status'];
43
+ $counts[$key] = ($counts[$key] ?? 0) + 1;
44
+ }
45
+ return ['version' => 1, 'site' => untrailingslashit(home_url()), 'hash' => hash('sha256', wp_json_encode($state)), 'counts' => $counts, 'changes' => $changes];
46
+ }
47
+
48
+ function flowpress_ops_alias_apply(array $expected): array
49
+ {
50
+ global $wpdb;
51
+ if (($expected['version'] ?? null) !== 1 || ($expected['site'] ?? '') !== untrailingslashit(home_url())) throw new RuntimeException('Migration target identity mismatch.');
52
+ $engine = $wpdb->get_var($wpdb->prepare('SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s', $wpdb->posts));
53
+ if (strtoupper((string)$engine) !== 'INNODB') throw new RuntimeException('Pilot migration requires transactional InnoDB posts.');
54
+ if ($wpdb->query('START TRANSACTION') === false) throw new RuntimeException('Cannot start migration transaction.');
55
+ try {
56
+ $wpdb->get_results("SELECT ID FROM {$wpdb->posts} ORDER BY ID FOR UPDATE");
57
+ $plan = flowpress_ops_alias_plan();
58
+ if (!hash_equals($plan['hash'], (string)($expected['hash'] ?? ''))) throw new RuntimeException('Migration plan is stale; run --dry-run again.');
59
+ foreach ($plan['changes'] as $change) {
60
+ if ($wpdb->update($wpdb->posts, ['post_content' => $change['after']], ['ID' => $change['id'], 'post_content' => $change['before']]) !== 1) throw new RuntimeException('Migration update failed for post ' . $change['id']);
61
+ }
62
+ if ($wpdb->query('COMMIT') === false) throw new RuntimeException('Cannot commit migration.');
63
+ } catch (Throwable $error) {
64
+ $wpdb->query('ROLLBACK');
65
+ throw $error;
66
+ }
67
+ foreach ($plan['changes'] as $change) clean_post_cache($change['id']);
68
+ return ['site' => $plan['site'], 'updated' => count($plan['changes']), 'counts' => $plan['counts']];
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@styleflow.app/flowpress-deploy",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.31",
4
4
  "description": "Safe full and partial deployment primitives for FlowPress.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -28,6 +28,29 @@
28
28
  "./worker-assets": {
29
29
  "import": "./dist/worker-assets.js",
30
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"
39
+ },
40
+ "./node-fetch": {
41
+ "import": "./dist/node-fetch.js",
42
+ "types": "./dist/node-fetch.d.ts"
43
+ },
44
+ "./ops/environment-files": {
45
+ "import": "./dist/ops/environment-files.js",
46
+ "types": "./dist/ops/environment-files.d.ts"
47
+ },
48
+ "./development": {
49
+ "import": "./dist/development.js",
50
+ "types": "./dist/development.d.ts"
51
+ },
52
+ "./ops/cms-template": {
53
+ "import": "./dist/ops/cms-template/entry.mjs"
31
54
  }
32
55
  },
33
56
  "files": [
@@ -37,15 +60,21 @@
37
60
  "flowpress-deploy.php",
38
61
  "composer.json",
39
62
  "README.md",
40
- "CHANGELOG.md"
63
+ "CHANGELOG.md",
64
+ "containers",
65
+ "schema"
41
66
  ],
42
67
  "scripts": {
43
68
  "build": "tsc -b",
44
69
  "typecheck": "tsc -b --pretty false",
45
- "test": "vitest run && node --test tests/*-regressions.mjs && php tests/php/queue-provider-test.php",
46
- "release:check": "npm run typecheck && npm test && npm pack --dry-run",
70
+ "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",
71
+ "release:check": "npm run typecheck && npm test && node tests/ops-pilot-aliases-integration.mjs && npm run test:ops:database && npm run test:ops:caddy && npm run test:ops:sanitizer && node tests/ops-local-apache-integration.mjs && node tests/ops-storage-integration.mjs && npm pack --dry-run",
47
72
  "release:publish:next": "npm publish --access public --tag next",
48
- "prepublishOnly": "npm run typecheck && npm test"
73
+ "prepublishOnly": "npm run typecheck && npm test",
74
+ "test:ops": "node --test tests/ops-*.test.mjs",
75
+ "test:ops:database": "node tests/ops-database-integration.mjs",
76
+ "test:ops:caddy": "node tests/ops-caddy-integration.mjs",
77
+ "test:ops:sanitizer": "node tests/ops-sanitizer-integration.mjs"
49
78
  },
50
79
  "devDependencies": {
51
80
  "@types/node": "^22.19.3",
@@ -55,5 +84,13 @@
55
84
  "engines": {
56
85
  "node": ">=22.0.0"
57
86
  },
58
- "packageManager": "npm@10.9.3"
87
+ "packageManager": "npm@10.9.3",
88
+ "dependencies": {
89
+ "dotenv": "17.4.2",
90
+ "dotenv-expand": "12.0.3",
91
+ "yaml": "2.9.1"
92
+ },
93
+ "bin": {
94
+ "flowpress-ops": "./dist/ops/cli.js"
95
+ }
59
96
  }