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

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 (117) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +5 -0
  3. package/containers/cms/Dockerfile +35 -0
  4. package/containers/cms/Dockerfile.dockerignore +20 -0
  5. package/containers/cms/healthz.php +17 -0
  6. package/containers/cms/install-languages.sh +16 -0
  7. package/containers/cms/mpm.conf +10 -0
  8. package/containers/cms/php.ini +16 -0
  9. package/containers/cms/sanitize-integrations.php +14 -0
  10. package/containers/cms/sanitize.php +37 -0
  11. package/containers/cms/smtp.php +13 -0
  12. package/containers/cms/vhost.conf +61 -0
  13. package/containers/cms/wp-cli.yml +1 -0
  14. package/containers/local/cms/Dockerfile +18 -0
  15. package/containers/local/cms/entrypoint.sh +7 -0
  16. package/containers/local/cms/php.ini +8 -0
  17. package/containers/local/cms/vhost.conf +14 -0
  18. package/containers/local/web/Dockerfile +8 -0
  19. package/containers/local/web/entrypoint.sh +10 -0
  20. package/containers/php-runtime/Dockerfile +21 -0
  21. package/containers/web-ssr/Dockerfile +27 -0
  22. package/containers/web-ssr/Dockerfile.dockerignore +10 -0
  23. package/containers/web-static/Caddyfile +40 -0
  24. package/containers/web-static/Dockerfile +17 -0
  25. package/containers/web-static/Dockerfile.dockerignore +7 -0
  26. package/dist/development.d.ts +4 -0
  27. package/dist/development.d.ts.map +1 -0
  28. package/dist/development.js +10 -0
  29. package/dist/development.js.map +1 -0
  30. package/dist/node-fetch.d.ts +3 -0
  31. package/dist/node-fetch.d.ts.map +1 -0
  32. package/dist/node-fetch.js +20 -0
  33. package/dist/node-fetch.js.map +1 -0
  34. package/dist/ops/bootstrap.d.ts +21 -0
  35. package/dist/ops/bootstrap.d.ts.map +1 -0
  36. package/dist/ops/bootstrap.js +123 -0
  37. package/dist/ops/bootstrap.js.map +1 -0
  38. package/dist/ops/build.d.ts +39 -0
  39. package/dist/ops/build.d.ts.map +1 -0
  40. package/dist/ops/build.js +110 -0
  41. package/dist/ops/build.js.map +1 -0
  42. package/dist/ops/check.d.ts +18 -0
  43. package/dist/ops/check.d.ts.map +1 -0
  44. package/dist/ops/check.js +46 -0
  45. package/dist/ops/check.js.map +1 -0
  46. package/dist/ops/cli.d.ts +3 -0
  47. package/dist/ops/cli.d.ts.map +1 -0
  48. package/dist/ops/cli.js +166 -0
  49. package/dist/ops/cli.js.map +1 -0
  50. package/dist/ops/config.d.ts +54 -0
  51. package/dist/ops/config.d.ts.map +1 -0
  52. package/dist/ops/config.js +202 -0
  53. package/dist/ops/config.js.map +1 -0
  54. package/dist/ops/coolify.d.ts +2 -0
  55. package/dist/ops/coolify.d.ts.map +1 -0
  56. package/dist/ops/coolify.js +44 -0
  57. package/dist/ops/coolify.js.map +1 -0
  58. package/dist/ops/data.d.ts +31 -0
  59. package/dist/ops/data.d.ts.map +1 -0
  60. package/dist/ops/data.js +163 -0
  61. package/dist/ops/data.js.map +1 -0
  62. package/dist/ops/deploy.d.ts +38 -0
  63. package/dist/ops/deploy.d.ts.map +1 -0
  64. package/dist/ops/deploy.js +127 -0
  65. package/dist/ops/deploy.js.map +1 -0
  66. package/dist/ops/editorial.d.ts +23 -0
  67. package/dist/ops/editorial.d.ts.map +1 -0
  68. package/dist/ops/editorial.js +79 -0
  69. package/dist/ops/editorial.js.map +1 -0
  70. package/dist/ops/environment-files.d.ts +9 -0
  71. package/dist/ops/environment-files.d.ts.map +1 -0
  72. package/dist/ops/environment-files.js +125 -0
  73. package/dist/ops/environment-files.js.map +1 -0
  74. package/dist/ops/environment.d.ts +29 -0
  75. package/dist/ops/environment.d.ts.map +1 -0
  76. package/dist/ops/environment.js +159 -0
  77. package/dist/ops/environment.js.map +1 -0
  78. package/dist/ops/health.d.ts +26 -0
  79. package/dist/ops/health.d.ts.map +1 -0
  80. package/dist/ops/health.js +27 -0
  81. package/dist/ops/health.js.map +1 -0
  82. package/dist/ops/local.d.ts +161 -0
  83. package/dist/ops/local.d.ts.map +1 -0
  84. package/dist/ops/local.js +173 -0
  85. package/dist/ops/local.js.map +1 -0
  86. package/dist/ops/monitor.d.ts +23 -0
  87. package/dist/ops/monitor.d.ts.map +1 -0
  88. package/dist/ops/monitor.js +143 -0
  89. package/dist/ops/monitor.js.map +1 -0
  90. package/dist/ops/process.d.ts +9 -0
  91. package/dist/ops/process.d.ts.map +1 -0
  92. package/dist/ops/process.js +56 -0
  93. package/dist/ops/process.js.map +1 -0
  94. package/dist/ops/provision.d.ts +41 -0
  95. package/dist/ops/provision.d.ts.map +1 -0
  96. package/dist/ops/provision.js +268 -0
  97. package/dist/ops/provision.js.map +1 -0
  98. package/dist/ops/registry.d.ts +7 -0
  99. package/dist/ops/registry.d.ts.map +1 -0
  100. package/dist/ops/registry.js +73 -0
  101. package/dist/ops/registry.js.map +1 -0
  102. package/dist/ops/release.d.ts +26 -0
  103. package/dist/ops/release.d.ts.map +1 -0
  104. package/dist/ops/release.js +79 -0
  105. package/dist/ops/release.js.map +1 -0
  106. package/dist/ops/s3.d.ts +8 -0
  107. package/dist/ops/s3.d.ts.map +1 -0
  108. package/dist/ops/s3.js +38 -0
  109. package/dist/ops/s3.js.map +1 -0
  110. package/dist/ops/static-config.d.ts +14 -0
  111. package/dist/ops/static-config.d.ts.map +1 -0
  112. package/dist/ops/static-config.js +96 -0
  113. package/dist/ops/static-config.js.map +1 -0
  114. package/flowpress-deploy.php +2 -2
  115. package/includes/automatic-build.php +22 -5
  116. package/package.json +40 -6
  117. package/schema/deployment.schema.json +185 -0
@@ -0,0 +1,79 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { allowedComponents, loadState } from "./config.js";
3
+ import { build } from "./build.js";
4
+ import { deploy, deployedRelease } from "./deploy.js";
5
+ import { capture } from "./process.js";
6
+ import { acknowledgeBatch, validateBuildRequest } from "./editorial.js";
7
+ export function changedComponents(paths, available) {
8
+ if (!paths.length)
9
+ return [];
10
+ const relevant = paths.filter(path => !/^(docs\/|README|CHANGELOG)|\.md$/.test(path));
11
+ if (!relevant.length)
12
+ return [];
13
+ const cms = relevant.some(path => /^apps\/cms\//.test(path));
14
+ const web = relevant.some(path => /^apps\/web\//.test(path));
15
+ const shared = relevant.some(path => !/^apps\/(cms|web)\//.test(path));
16
+ return available.filter(component => shared || (component === "cms" ? cms : web));
17
+ }
18
+ export async function editorialCommit(root, manifest, payload) {
19
+ const request = validateBuildRequest(payload);
20
+ allowedComponents(manifest, request.target, ["web-static"]);
21
+ const app = loadState(root, request.target).apps?.["web-static"];
22
+ if (!app?.url)
23
+ throw new Error("No deployed static frontend for this editorial target.");
24
+ const res = await fetch(app.url + app.health + "?editorial=" + Date.now(), { redirect: "error", signal: AbortSignal.timeout(10000) });
25
+ const data = await res.json();
26
+ if (!res.ok || data.status !== "ok" || !/^[a-f0-9]{40}$/.test(data.release || "") || data.backendTarget !== request.target)
27
+ throw new Error("Editorial target is not a healthy static frontend using this backend.");
28
+ return { request, sha: data.release };
29
+ }
30
+ export async function release(root, manifest, name, { component, sha, buildId, editorial = false } = {}) {
31
+ let selected = allowedComponents(manifest, name, component ? [component] : []);
32
+ const revision = sha || capture("git", ["rev-parse", "HEAD"], { cwd: root });
33
+ let request;
34
+ if (editorial) {
35
+ const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
36
+ const live = await editorialCommit(root, manifest, event.client_payload);
37
+ if (live.request.target !== name || live.sha !== revision)
38
+ throw new Error("Editorial build must check out the exact deployed commit.");
39
+ request = live.request;
40
+ selected = ["web-static"];
41
+ }
42
+ else if (process.env.GITHUB_EVENT_NAME === "push" && process.env.GITHUB_EVENT_PATH) {
43
+ const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
44
+ if (/^[a-f0-9]{40}$/.test(event.before || "") && !/^0+$/.test(event.before)) {
45
+ try {
46
+ selected = changedComponents(capture("git", ["diff", "--name-only", event.before, revision], { cwd: root }).split("\n").filter(Boolean), selected);
47
+ }
48
+ catch { /* shallow checkout: safely build all allowed components */ }
49
+ }
50
+ }
51
+ const results = [];
52
+ try {
53
+ for (const item of selected) {
54
+ if (name === "production" && item === "cms") {
55
+ const verified = await deployedRelease(root, "staging", "cms");
56
+ if (!verified || verified.sha !== revision)
57
+ throw new Error("Production CMS must promote the staging image for this SHA.");
58
+ results.push(await deploy(root, manifest, name, item, { release: { ...verified, environment: "production" } }));
59
+ }
60
+ else {
61
+ await build(root, manifest, name, item, { sha: revision, buildId, push: true });
62
+ results.push(await deploy(root, manifest, name, item));
63
+ }
64
+ }
65
+ if (request)
66
+ await acknowledgeBatch({ request, backend: manifest.environments[name].backendUrl, token: process.env.CMS_BUILD_CALLBACK_TOKEN, status: "success" });
67
+ }
68
+ catch (error) {
69
+ if (request) {
70
+ try {
71
+ await acknowledgeBatch({ request, backend: manifest.environments[name].backendUrl, token: process.env.CMS_BUILD_CALLBACK_TOKEN, status: "failure" });
72
+ }
73
+ catch { /* original failure remains authoritative; batch is retained */ }
74
+ }
75
+ throw error;
76
+ }
77
+ return { environment: name, components: selected, results };
78
+ }
79
+ //# sourceMappingURL=release.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release.js","sourceRoot":"","sources":["../../src/ops/release.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAExE,MAAM,UAAU,iBAAiB,CAAC,KAAK,EAAE,SAAS;IAChD,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtF,IAAI,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAChC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACvE,OAAO,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AACpF,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO;IAC3D,MAAM,OAAO,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9C,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC;IACjE,IAAI,CAAC,GAAG,EAAE,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IACzF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtI,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,KAAK,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IACrN,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;AACxC,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,EAAE;IACrG,IAAI,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/E,MAAM,QAAQ,GAAG,GAAG,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,IAAI,OAAO,CAAC;IACZ,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;QACzE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QACxI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACvB,QAAQ,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;SAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9E,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5E,IAAI,CAAC;gBAAC,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,2DAA2D,CAAC,CAAC;QACnO,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBAC5C,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;gBAC/D,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,GAAG,KAAK,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;gBAC3H,OAAO,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;YAClH,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,OAAO,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QACD,IAAI,OAAO;YAAE,MAAM,gBAAgB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACpK,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBAAC,MAAM,gBAAgB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,+DAA+D,CAAC,CAAC;QACzO,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC9D,CAAC"}
@@ -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 proxiesToCaddy({ 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":";AAoBA,sDAwBC;AAED,oDAmBC;AAED;;;aAkBC;AAED;;;;;WAQC"}
@@ -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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"}
@@ -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.2
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.2');
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';
@@ -55,16 +55,33 @@ function flowpress_deploy_taxonomy_affects_public_build(string $taxonomy): bool
55
55
  return $object && (bool) apply_filters('flowpress_deploy_taxonomy_affects_public_build', $object->public, $taxonomy);
56
56
  }
57
57
 
58
- add_action('set_object_terms', static function (int $id, $terms, $tt_ids, string $taxonomy): void {
59
- if (flowpress_deploy_taxonomy_affects_public_build($taxonomy)) flowpress_deploy_schedule_public_build();
60
- }, 40, 4);
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);
61
72
  foreach (['created_term', 'edited_term', 'pre_delete_term'] as $hook) {
62
73
  $callback = $hook === 'pre_delete_term'
63
74
  ? static function (int $id, string $taxonomy): void {
64
- if (flowpress_deploy_taxonomy_affects_public_build($taxonomy)) flowpress_deploy_schedule_public_build();
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
+ }
65
79
  }
66
80
  : static function (int $id, int $tt_id, string $taxonomy): void {
67
- if (flowpress_deploy_taxonomy_affects_public_build($taxonomy)) flowpress_deploy_schedule_public_build();
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
+ }
68
85
  };
69
86
  add_action($hook, $callback, 40, $hook === 'pre_delete_term' ? 2 : 3);
70
87
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@styleflow.app/flowpress-deploy",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.20",
4
4
  "description": "Safe full and partial deployment primitives for FlowPress.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -28,6 +28,26 @@
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"
31
51
  }
32
52
  },
33
53
  "files": [
@@ -37,15 +57,21 @@
37
57
  "flowpress-deploy.php",
38
58
  "composer.json",
39
59
  "README.md",
40
- "CHANGELOG.md"
60
+ "CHANGELOG.md",
61
+ "containers",
62
+ "schema"
41
63
  ],
42
64
  "scripts": {
43
65
  "build": "tsc -b",
44
66
  "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",
67
+ "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",
68
+ "release:check": "npm run typecheck && npm test && npm run test:ops:database && npm run test:ops:caddy && npm run test:ops:sanitizer && node tests/ops-local-apache-integration.mjs && npm pack --dry-run",
47
69
  "release:publish:next": "npm publish --access public --tag next",
48
- "prepublishOnly": "npm run typecheck && npm test"
70
+ "prepublishOnly": "npm run typecheck && npm test",
71
+ "test:ops": "node --test tests/ops-*.test.mjs",
72
+ "test:ops:database": "node tests/ops-database-integration.mjs",
73
+ "test:ops:caddy": "node tests/ops-caddy-integration.mjs",
74
+ "test:ops:sanitizer": "node tests/ops-sanitizer-integration.mjs"
49
75
  },
50
76
  "devDependencies": {
51
77
  "@types/node": "^22.19.3",
@@ -55,5 +81,13 @@
55
81
  "engines": {
56
82
  "node": ">=22.0.0"
57
83
  },
58
- "packageManager": "npm@10.9.3"
84
+ "packageManager": "npm@10.9.3",
85
+ "dependencies": {
86
+ "dotenv": "17.4.2",
87
+ "dotenv-expand": "12.0.3",
88
+ "yaml": "2.9.1"
89
+ },
90
+ "bin": {
91
+ "flowpress-ops": "./dist/ops/cli.js"
92
+ }
59
93
  }
@@ -0,0 +1,185 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "FlowPress deployment manifest",
4
+ "type": "object",
5
+ "required": [
6
+ "schemaVersion",
7
+ "platform",
8
+ "profile",
9
+ "database",
10
+ "media",
11
+ "environments"
12
+ ],
13
+ "properties": {
14
+ "schemaVersion": {
15
+ "const": 1
16
+ },
17
+ "platform": {
18
+ "type": "string",
19
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
20
+ },
21
+ "profile": {
22
+ "enum": [
23
+ "backend",
24
+ "static",
25
+ "headless-static",
26
+ "headless-ssr"
27
+ ]
28
+ },
29
+ "database": {
30
+ "const": "shared"
31
+ },
32
+ "media": {
33
+ "enum": [
34
+ "local",
35
+ "s3"
36
+ ]
37
+ },
38
+ "registryProject": {
39
+ "type": "string"
40
+ },
41
+ "sources": {
42
+ "type": "object",
43
+ "additionalProperties": {
44
+ "type": "object",
45
+ "required": [
46
+ "ssh",
47
+ "user",
48
+ "path"
49
+ ],
50
+ "properties": {
51
+ "ssh": {
52
+ "type": "string"
53
+ },
54
+ "user": {
55
+ "type": "string"
56
+ },
57
+ "path": {
58
+ "type": "string",
59
+ "pattern": "^/"
60
+ }
61
+ },
62
+ "additionalProperties": false
63
+ }
64
+ },
65
+ "environments": {
66
+ "type": "object",
67
+ "propertyNames": {
68
+ "enum": [
69
+ "local",
70
+ "staging",
71
+ "production",
72
+ "preview"
73
+ ]
74
+ },
75
+ "additionalProperties": {
76
+ "type": "object",
77
+ "required": [
78
+ "publish"
79
+ ],
80
+ "properties": {
81
+ "publish": {
82
+ "type": "array",
83
+ "uniqueItems": true,
84
+ "items": {
85
+ "enum": [
86
+ "cms",
87
+ "web-static",
88
+ "web-ssr"
89
+ ]
90
+ }
91
+ },
92
+ "profile": {
93
+ "enum": [
94
+ "backend",
95
+ "static",
96
+ "headless-static",
97
+ "headless-ssr"
98
+ ]
99
+ },
100
+ "frontendUrl": {
101
+ "type": "string",
102
+ "format": "uri"
103
+ },
104
+ "backendUrl": {
105
+ "type": "string",
106
+ "format": "uri"
107
+ },
108
+ "graphqlUrl": {
109
+ "type": "string",
110
+ "format": "uri"
111
+ },
112
+ "mediaUrl": {
113
+ "type": "string",
114
+ "format": "uri"
115
+ },
116
+ "backend": {
117
+ "enum": [
118
+ "local",
119
+ "staging",
120
+ "production"
121
+ ]
122
+ },
123
+ "aliases": {
124
+ "type": "array",
125
+ "items": {
126
+ "type": "string",
127
+ "format": "uri"
128
+ }
129
+ },
130
+ "mode": {
131
+ "const": "on-demand"
132
+ },
133
+ "mail": {
134
+ "enum": [
135
+ "disabled",
136
+ "capture",
137
+ "smtp"
138
+ ]
139
+ },
140
+ "backendAliases": {
141
+ "type": "array",
142
+ "items": {
143
+ "type": "string",
144
+ "format": "uri"
145
+ }
146
+ }
147
+ },
148
+ "additionalProperties": false
149
+ }
150
+ },
151
+ "development": {
152
+ "type": "object",
153
+ "properties": {
154
+ "ports": {
155
+ "type": "object",
156
+ "properties": {
157
+ "web": {
158
+ "type": "integer",
159
+ "minimum": 1,
160
+ "maximum": 65535
161
+ },
162
+ "cms": {
163
+ "type": "integer",
164
+ "minimum": 1,
165
+ "maximum": 65535
166
+ },
167
+ "https": {
168
+ "type": "integer",
169
+ "minimum": 1,
170
+ "maximum": 65535
171
+ },
172
+ "database": {
173
+ "type": "integer",
174
+ "minimum": 1,
175
+ "maximum": 65535
176
+ }
177
+ },
178
+ "additionalProperties": false
179
+ }
180
+ },
181
+ "additionalProperties": false
182
+ }
183
+ },
184
+ "additionalProperties": false
185
+ }