redkite 0.1.6

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 (179) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +819 -0
  3. package/bin/redkite.mjs +12 -0
  4. package/dist/build.d.ts +27 -0
  5. package/dist/build.d.ts.map +1 -0
  6. package/dist/build.js +132 -0
  7. package/dist/build.js.map +1 -0
  8. package/dist/checks.d.ts +4 -0
  9. package/dist/checks.d.ts.map +1 -0
  10. package/dist/checks.js +49 -0
  11. package/dist/checks.js.map +1 -0
  12. package/dist/cli/agent.d.ts +2 -0
  13. package/dist/cli/agent.d.ts.map +1 -0
  14. package/dist/cli/agent.js +24 -0
  15. package/dist/cli/agent.js.map +1 -0
  16. package/dist/cli/config.d.ts +5 -0
  17. package/dist/cli/config.d.ts.map +1 -0
  18. package/dist/cli/config.js +217 -0
  19. package/dist/cli/config.js.map +1 -0
  20. package/dist/cli/index.d.ts +11 -0
  21. package/dist/cli/index.d.ts.map +1 -0
  22. package/dist/cli/index.js +438 -0
  23. package/dist/cli/index.js.map +1 -0
  24. package/dist/cli/log.d.ts +9 -0
  25. package/dist/cli/log.d.ts.map +1 -0
  26. package/dist/cli/log.js +128 -0
  27. package/dist/cli/log.js.map +1 -0
  28. package/dist/cli/screen.d.ts +34 -0
  29. package/dist/cli/screen.d.ts.map +1 -0
  30. package/dist/cli/screen.js +298 -0
  31. package/dist/cli/screen.js.map +1 -0
  32. package/dist/cli/viewer.d.ts +9 -0
  33. package/dist/cli/viewer.d.ts.map +1 -0
  34. package/dist/cli/viewer.js +139 -0
  35. package/dist/cli/viewer.js.map +1 -0
  36. package/dist/config.d.ts +7 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +79 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/deploy.d.ts +19 -0
  41. package/dist/deploy.d.ts.map +1 -0
  42. package/dist/deploy.js +275 -0
  43. package/dist/deploy.js.map +1 -0
  44. package/dist/docker.d.ts +96 -0
  45. package/dist/docker.d.ts.map +1 -0
  46. package/dist/docker.js +325 -0
  47. package/dist/docker.js.map +1 -0
  48. package/dist/dockerfile.d.ts +14 -0
  49. package/dist/dockerfile.d.ts.map +1 -0
  50. package/dist/dockerfile.js +160 -0
  51. package/dist/dockerfile.js.map +1 -0
  52. package/dist/health.d.ts +13 -0
  53. package/dist/health.d.ts.map +1 -0
  54. package/dist/health.js +54 -0
  55. package/dist/health.js.map +1 -0
  56. package/dist/host.d.ts +21 -0
  57. package/dist/host.d.ts.map +1 -0
  58. package/dist/host.js +27 -0
  59. package/dist/host.js.map +1 -0
  60. package/dist/index.d.ts +26 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +26 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/layout.d.ts +5 -0
  65. package/dist/layout.d.ts.map +1 -0
  66. package/dist/layout.js +45 -0
  67. package/dist/layout.js.map +1 -0
  68. package/dist/localHost.d.ts +7 -0
  69. package/dist/localHost.d.ts.map +1 -0
  70. package/dist/localHost.js +32 -0
  71. package/dist/localHost.js.map +1 -0
  72. package/dist/log.d.ts +14 -0
  73. package/dist/log.d.ts.map +1 -0
  74. package/dist/log.js +15 -0
  75. package/dist/log.js.map +1 -0
  76. package/dist/nginx.d.ts +4 -0
  77. package/dist/nginx.d.ts.map +1 -0
  78. package/dist/nginx.js +53 -0
  79. package/dist/nginx.js.map +1 -0
  80. package/dist/pipeline.d.ts +103 -0
  81. package/dist/pipeline.d.ts.map +1 -0
  82. package/dist/pipeline.js +142 -0
  83. package/dist/pipeline.js.map +1 -0
  84. package/dist/presets/index.d.ts +3 -0
  85. package/dist/presets/index.d.ts.map +1 -0
  86. package/dist/presets/index.js +3 -0
  87. package/dist/presets/index.js.map +1 -0
  88. package/dist/presets/nextApp.d.ts +15 -0
  89. package/dist/presets/nextApp.d.ts.map +1 -0
  90. package/dist/presets/nextApp.js +53 -0
  91. package/dist/presets/nextApp.js.map +1 -0
  92. package/dist/presets/nodeApp.d.ts +19 -0
  93. package/dist/presets/nodeApp.d.ts.map +1 -0
  94. package/dist/presets/nodeApp.js +29 -0
  95. package/dist/presets/nodeApp.js.map +1 -0
  96. package/dist/secrets/bitwarden.d.ts +3 -0
  97. package/dist/secrets/bitwarden.d.ts.map +1 -0
  98. package/dist/secrets/bitwarden.js +6 -0
  99. package/dist/secrets/bitwarden.js.map +1 -0
  100. package/dist/secrets/refs.d.ts +9 -0
  101. package/dist/secrets/refs.d.ts.map +1 -0
  102. package/dist/secrets/refs.js +20 -0
  103. package/dist/secrets/refs.js.map +1 -0
  104. package/dist/secrets/store.d.ts +9 -0
  105. package/dist/secrets/store.d.ts.map +1 -0
  106. package/dist/secrets/store.js +104 -0
  107. package/dist/secrets/store.js.map +1 -0
  108. package/dist/services/ensure.d.ts +18 -0
  109. package/dist/services/ensure.d.ts.map +1 -0
  110. package/dist/services/ensure.js +93 -0
  111. package/dist/services/ensure.js.map +1 -0
  112. package/dist/services/index.d.ts +19 -0
  113. package/dist/services/index.d.ts.map +1 -0
  114. package/dist/services/index.js +27 -0
  115. package/dist/services/index.js.map +1 -0
  116. package/dist/services/planned.d.ts +20 -0
  117. package/dist/services/planned.d.ts.map +1 -0
  118. package/dist/services/planned.js +77 -0
  119. package/dist/services/planned.js.map +1 -0
  120. package/dist/shell.d.ts +11 -0
  121. package/dist/shell.d.ts.map +1 -0
  122. package/dist/shell.js +65 -0
  123. package/dist/shell.js.map +1 -0
  124. package/dist/source.d.ts +16 -0
  125. package/dist/source.d.ts.map +1 -0
  126. package/dist/source.js +108 -0
  127. package/dist/source.js.map +1 -0
  128. package/dist/sshHost.d.ts +15 -0
  129. package/dist/sshHost.d.ts.map +1 -0
  130. package/dist/sshHost.js +91 -0
  131. package/dist/sshHost.js.map +1 -0
  132. package/dist/steps.d.ts +27 -0
  133. package/dist/steps.d.ts.map +1 -0
  134. package/dist/steps.js +69 -0
  135. package/dist/steps.js.map +1 -0
  136. package/dist/topology.d.ts +40 -0
  137. package/dist/topology.d.ts.map +1 -0
  138. package/dist/topology.js +113 -0
  139. package/dist/topology.js.map +1 -0
  140. package/dist/types.d.ts +100 -0
  141. package/dist/types.d.ts.map +1 -0
  142. package/dist/types.js +2 -0
  143. package/dist/types.js.map +1 -0
  144. package/package.json +64 -0
  145. package/src/build.ts +236 -0
  146. package/src/checks.ts +65 -0
  147. package/src/cli/agent.ts +28 -0
  148. package/src/cli/config.ts +275 -0
  149. package/src/cli/index.ts +559 -0
  150. package/src/cli/log.ts +168 -0
  151. package/src/cli/screen.ts +409 -0
  152. package/src/cli/viewer.ts +192 -0
  153. package/src/config.ts +90 -0
  154. package/src/deploy.ts +409 -0
  155. package/src/docker.ts +433 -0
  156. package/src/dockerfile.ts +228 -0
  157. package/src/health.ts +83 -0
  158. package/src/host.ts +54 -0
  159. package/src/index.ts +25 -0
  160. package/src/layout.ts +41 -0
  161. package/src/localHost.ts +50 -0
  162. package/src/log.ts +38 -0
  163. package/src/nginx.ts +63 -0
  164. package/src/pipeline.ts +335 -0
  165. package/src/presets/index.ts +2 -0
  166. package/src/presets/nextApp.ts +70 -0
  167. package/src/presets/nodeApp.ts +46 -0
  168. package/src/secrets/bitwarden.ts +7 -0
  169. package/src/secrets/refs.ts +32 -0
  170. package/src/secrets/store.ts +147 -0
  171. package/src/services/ensure.ts +160 -0
  172. package/src/services/index.ts +48 -0
  173. package/src/services/planned.ts +119 -0
  174. package/src/shell.ts +95 -0
  175. package/src/source.ts +171 -0
  176. package/src/sshHost.ts +145 -0
  177. package/src/steps.ts +101 -0
  178. package/src/topology.ts +199 -0
  179. package/src/types.ts +243 -0
@@ -0,0 +1,53 @@
1
+ import { nodeApp } from "./nodeApp.js";
2
+ // The standalone layout is three directories that have to land in the right
3
+ // places, which is the only part of a Next build that is not a node build
4
+ export function nextApp(options = {}) {
5
+ const port = options.port ?? 3000;
6
+ const standalone = options.standalone ?? true;
7
+ const shared = {
8
+ builder: options.builder ?? "24-alpine",
9
+ runtime: options.runtime ?? "22-alpine",
10
+ steps: options.steps ?? ["yarn build"],
11
+ dependencies: options.dependencies,
12
+ };
13
+ // Relative, because the runtime starts at the app's root and a monorepo puts
14
+ // that under its own directory rather than at the top of the image
15
+ const start = (command) => ["sh", "-c", `HOSTNAME=0.0.0.0 PORT=${port} ${command}`];
16
+ if (!standalone) {
17
+ return {
18
+ ...nodeApp({
19
+ ...shared,
20
+ // The whole tree, because next start reads the source layout it built
21
+ // from and resolves its own dependencies at runtime
22
+ output: "/app",
23
+ // npx resolves the binary from node_modules/.bin upwards, so this
24
+ // finds it whether a workspace hoisted it or the app owns it
25
+ entrypoint: start("npx --no-install next start"),
26
+ // Without the modules cache, so node_modules is a layer of the image
27
+ // rather than a mount the runtime stage never sees
28
+ caches: ["yarn", "npm", "next-app"],
29
+ }),
30
+ preset: "nextApp",
31
+ keepsLayout: true,
32
+ };
33
+ }
34
+ return {
35
+ ...nodeApp({
36
+ ...shared,
37
+ output: "/app/.next/standalone",
38
+ // static is what the build produced and the server serves. public is
39
+ // whatever the repository put there, and plenty of apps have none
40
+ carry: ["/app/.next/static", { path: "/app/public", optional: true }],
41
+ entrypoint: start("node server.js"),
42
+ // app-modules is what makes the build fast: node_modules on a mount
43
+ // rather than in a layer, which the standalone tree makes safe by
44
+ // carrying its own copy of everything the server reaches
45
+ caches: ["yarn", "npm", "modules", "app-modules", "next-app"],
46
+ }),
47
+ preset: "nextApp",
48
+ // The standalone tree traces from the workspace root, so an app in a
49
+ // subdirectory arrives at that subdirectory and not at the top of it
50
+ keepsLayout: true,
51
+ };
52
+ }
53
+ //# sourceMappingURL=nextApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextApp.js","sourceRoot":"","sources":["../../src/presets/nextApp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAavC,4EAA4E;AAC5E,0EAA0E;AAC1E,MAAM,UAAU,OAAO,CAAC,UAA0B,EAAE;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAE9C,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,WAAW;QACvC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,WAAW;QACvC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC;QACtC,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,CAAC;IAEF,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,KAAK,GAAG,CAAC,OAAe,EAAE,EAAE,CAChC,CAAC,IAAI,EAAE,IAAI,EAAE,yBAAyB,IAAI,IAAI,OAAO,EAAE,CAAC,CAAC;IAE3D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,GAAG,OAAO,CAAC;gBACT,GAAG,MAAM;gBACT,sEAAsE;gBACtE,oDAAoD;gBACpD,MAAM,EAAE,MAAM;gBACd,kEAAkE;gBAClE,6DAA6D;gBAC7D,UAAU,EAAE,KAAK,CAAC,6BAA6B,CAAC;gBAChD,qEAAqE;gBACrE,mDAAmD;gBACnD,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC;aACpC,CAAC;YACF,MAAM,EAAE,SAAS;YACjB,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,GAAG,OAAO,CAAC;YACT,GAAG,MAAM;YACT,MAAM,EAAE,uBAAuB;YAC/B,qEAAqE;YACrE,kEAAkE;YAClE,KAAK,EAAE,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACrE,UAAU,EAAE,KAAK,CAAC,gBAAgB,CAAC;YACnC,oEAAoE;YACpE,kEAAkE;YAClE,yDAAyD;YACzD,MAAM,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,CAAC;SAC9D,CAAC;QACF,MAAM,EAAE,SAAS;QACjB,qEAAqE;QACrE,qEAAqE;QACrE,WAAW,EAAE,IAAI;KAClB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { BuildSpec, CarryPath, SourcemapSpec } from "../types.js";
2
+ type NodeAppOptions = {
3
+ builder?: string;
4
+ runtime?: string;
5
+ steps: string[];
6
+ dependencies?: {
7
+ files: string[];
8
+ step: string;
9
+ } | false;
10
+ output: string;
11
+ entrypoint: string[];
12
+ carry?: CarryPath[];
13
+ submodules?: boolean;
14
+ caches?: string[];
15
+ sourcemaps?: SourcemapSpec;
16
+ };
17
+ export declare function nodeApp(options: NodeAppOptions): BuildSpec;
18
+ export {};
19
+ //# sourceMappingURL=nodeApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeApp.d.ts","sourceRoot":"","sources":["../../src/presets/nodeApp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEvE,KAAK,cAAc,GAAG;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAGhB,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,aAAa,CAAC;CAC5B,CAAC;AAEF,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CA4B1D"}
@@ -0,0 +1,29 @@
1
+ export function nodeApp(options) {
2
+ return {
3
+ preset: "nodeApp",
4
+ builderImage: `node:${options.builder ?? "22-alpine"}`,
5
+ runtimeImage: `node:${options.runtime ?? "24-alpine"}`,
6
+ dependencies: options.dependencies === false
7
+ ? undefined
8
+ : (options.dependencies ?? {
9
+ files: ["package.json", "yarn.lock"],
10
+ step: "yarn install --frozen-lockfile",
11
+ stripScripts: ["preinstall", "prepare", "postinstall"],
12
+ }),
13
+ steps: options.steps,
14
+ output: options.output,
15
+ carry: options.carry ?? [],
16
+ entrypoint: options.entrypoint,
17
+ // Not the app's own node_modules: a step after the build runs in this image
18
+ // without the mounts, and a migration that cannot find prisma is what a
19
+ // cache mount instead of a layer looks like from the outside
20
+ caches: options.caches ?? ["yarn", "npm", "modules"],
21
+ submodules: options.submodules ?? false,
22
+ // The checkout arrives complete, so this is what a build step itself needs
23
+ aptPackages: ["git"],
24
+ runtimePackages: ["curl"],
25
+ runtimeSteps: [],
26
+ sourcemaps: options.sourcemaps,
27
+ };
28
+ }
29
+ //# sourceMappingURL=nodeApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeApp.js","sourceRoot":"","sources":["../../src/presets/nodeApp.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,OAAO,CAAC,OAAuB;IAC7C,OAAO;QACL,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,QAAQ,OAAO,CAAC,OAAO,IAAI,WAAW,EAAE;QACtD,YAAY,EAAE,QAAQ,OAAO,CAAC,OAAO,IAAI,WAAW,EAAE;QACtD,YAAY,EACV,OAAO,CAAC,YAAY,KAAK,KAAK;YAC5B,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI;gBACvB,KAAK,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC;gBACpC,IAAI,EAAE,gCAAgC;gBACtC,YAAY,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,aAAa,CAAC;aACvD,CAAC;QACR,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;QAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,4EAA4E;QAC5E,wEAAwE;QACxE,6DAA6D;QAC7D,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;QACpD,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,KAAK;QACvC,2EAA2E;QAC3E,WAAW,EAAE,CAAC,KAAK,CAAC;QACpB,eAAe,EAAE,CAAC,MAAM,CAAC;QACzB,YAAY,EAAE,EAAE;QAChB,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SecretRef } from "../types.js";
2
+ export declare function bitwarden(id: string): SecretRef;
3
+ //# sourceMappingURL=bitwarden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitwarden.d.ts","sourceRoot":"","sources":["../../src/secrets/bitwarden.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAE/C"}
@@ -0,0 +1,6 @@
1
+ // Names an item in a Bitwarden vault. The id is not a secret, it is a pointer,
2
+ // so it belongs in the config while the credentials arrive at deploy time
3
+ export function bitwarden(id) {
4
+ return { provider: "bitwarden", id };
5
+ }
6
+ //# sourceMappingURL=bitwarden.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitwarden.js","sourceRoot":"","sources":["../../src/secrets/bitwarden.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { SecretRef, SecretRefs } from "../types.js";
2
+ export type SecretStore = {
3
+ read(id: string): Promise<string>;
4
+ };
5
+ export type SecretStores = Record<string, SecretStore>;
6
+ export declare function listRefs(refs: SecretRefs | undefined): SecretRef[];
7
+ export declare function readRef(ref: SecretRef, stores: SecretStores): Promise<string>;
8
+ export declare function readEnv(refs: SecretRefs | undefined, stores: SecretStores): Promise<string>;
9
+ //# sourceMappingURL=refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.d.ts","sourceRoot":"","sources":["../../src/secrets/refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEvD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,EAAE,CAGlE;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,mBAMjE;AAKD,wBAAsB,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,mBAM/E"}
@@ -0,0 +1,20 @@
1
+ export function listRefs(refs) {
2
+ if (!refs)
3
+ return [];
4
+ return Array.isArray(refs) ? refs : [refs];
5
+ }
6
+ export async function readRef(ref, stores) {
7
+ const store = stores[ref.provider];
8
+ if (store)
9
+ return await store.read(ref.id);
10
+ const known = Object.keys(stores).join(", ") || "none";
11
+ throw new Error(`No store for provider ${ref.provider}, deploy was given ${known}`);
12
+ }
13
+ // Concatenated in the order written. Every dotenv parser builds an object as it
14
+ // reads, so a key that appears twice takes its later value, which is the
15
+ // precedence the config file reads as having
16
+ export async function readEnv(refs, stores) {
17
+ const contents = await Promise.all(listRefs(refs).map(async (ref) => await readRef(ref, stores)));
18
+ return contents.map((text) => (text.endsWith("\n") ? text : `${text}\n`)).join("");
19
+ }
20
+ //# sourceMappingURL=refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refs.js","sourceRoot":"","sources":["../../src/secrets/refs.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,QAAQ,CAAC,IAA4B;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAc,EAAE,MAAoB;IAChE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,KAAK;QAAE,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAE3C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;IACvD,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,CAAC,QAAQ,sBAAsB,KAAK,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,gFAAgF;AAChF,yEAAyE;AACzE,6CAA6C;AAC7C,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAA4B,EAAE,MAAoB;IAC9E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAC9D,CAAC;IAEF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { SecretStore } from "./refs.js";
2
+ export type BitwardenCredentials = {
3
+ clientId: string;
4
+ clientSecret: string;
5
+ password: string;
6
+ detail?: (message: string) => void;
7
+ };
8
+ export declare function bitwardenStore(credentials: BitwardenCredentials): Promise<SecretStore>;
9
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/secrets/store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAW7C,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAUF,wBAAsB,cAAc,CAClC,WAAW,EAAE,oBAAoB,GAChC,OAAO,CAAC,WAAW,CAAC,CA8DtB"}
@@ -0,0 +1,104 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { promisify } from "node:util";
7
+ // One implementation of the interface deploy consumes. A second provider only
8
+ // has to satisfy read().
9
+ //
10
+ // The CLI runs here as an ordinary process. It used to run inside a container
11
+ // purely as a sandbox, which meant installing several thousand packages on
12
+ // every deploy to read three items.
13
+ const run = promisify(execFile);
14
+ // Used when bw is not already on PATH. Pinned, because an unpinned CLI is a
15
+ // different program on a machine that has never run a deploy before
16
+ const CLI = "@bitwarden/cli@2026.4.2";
17
+ // Where that install lands. Keyed by the pinned version, so bumping it installs
18
+ // beside the old one rather than over a vault command that is mid-flight
19
+ const CLIS = join(homedir(), ".cache", "redkite", "cli");
20
+ export async function bitwardenStore(credentials) {
21
+ const detail = credentials.detail ?? (() => { });
22
+ // Its own state directory, so a deploy neither reads nor disturbs whatever
23
+ // vault the person running it happens to be logged into
24
+ const appdata = join(homedir(), ".cache", "redkite", "bitwarden");
25
+ await mkdir(appdata, { recursive: true });
26
+ const command = await resolveCli(detail);
27
+ const bw = async (args, env = {}) => await run(command.file, [...command.prefix, ...args], {
28
+ env: {
29
+ ...process.env,
30
+ BITWARDENCLI_APPDATA_DIR: appdata,
31
+ ...env,
32
+ },
33
+ maxBuffer: 32 * 1024 * 1024,
34
+ });
35
+ detail("unlocking the vault");
36
+ // Already logged in is not an error, the session is what matters
37
+ await bw(["login", "--apikey"], {
38
+ BW_CLIENTID: credentials.clientId,
39
+ BW_CLIENTSECRET: credentials.clientSecret,
40
+ }).catch(() => undefined);
41
+ const unlocked = await bw(["unlock", "--passwordenv", "BW_PASSWORD", "--raw"], {
42
+ BW_PASSWORD: credentials.password,
43
+ }).catch((error) => {
44
+ throw new Error(`Could not unlock the Bitwarden vault: ${messageOf(error)}`);
45
+ });
46
+ const session = unlocked.stdout.trim();
47
+ if (!session)
48
+ throw new Error("Bitwarden unlocked without a session");
49
+ // An item added since the last deploy is not in the local vault otherwise,
50
+ // and bw get answers "not found" rather than fetching it
51
+ await bw(["sync"], { BW_SESSION: session }).catch(() => undefined);
52
+ // Fetched once each. A second read of the same item during a deploy is the
53
+ // same answer, and this avoids a process per call site
54
+ const cache = new Map();
55
+ const fetch = async (id) => {
56
+ const item = await bw(["get", "notes", id, "--raw"], {
57
+ BW_SESSION: session,
58
+ }).catch((error) => {
59
+ throw new Error(`Could not read ${id} from Bitwarden: ${messageOf(error)}`);
60
+ });
61
+ return item.stdout;
62
+ };
63
+ return {
64
+ read: async (id) => {
65
+ const pending = cache.get(id) ?? fetch(id);
66
+ cache.set(id, pending);
67
+ return await pending;
68
+ },
69
+ };
70
+ }
71
+ // A machine with the CLI installed pays nothing. One without it installs the
72
+ // pinned version once, rather than resolving it again on every call below
73
+ async function resolveCli(detail) {
74
+ const override = process.env["REDKITE_BW_BIN"];
75
+ if (override)
76
+ return { file: override, prefix: [] };
77
+ const found = await run("bw", ["--version"]).then(() => true, () => false);
78
+ if (found)
79
+ return { file: "bw", prefix: [] };
80
+ return { file: await install(detail), prefix: [] };
81
+ }
82
+ // npx resolves the package again on every invocation, and unlocking a vault is
83
+ // three commands plus one per secret. That was most of a second, six times over
84
+ async function install(detail) {
85
+ const directory = join(CLIS, CLI.replace(/[^\w.]+/g, "-"));
86
+ const binary = join(directory, "node_modules", ".bin", "bw");
87
+ if (existsSync(binary))
88
+ return binary;
89
+ detail(`installing ${CLI}, once for this machine`);
90
+ await mkdir(directory, { recursive: true });
91
+ await run("npm", ["install", "--prefix", directory, "--no-save", "--no-audit", "--no-fund", CLI], { maxBuffer: 32 * 1024 * 1024 }).catch((error) => {
92
+ throw new Error(`Could not install ${CLI}: ${messageOf(error)}`);
93
+ });
94
+ return binary;
95
+ }
96
+ function messageOf(error) {
97
+ if (error instanceof Error && "stderr" in error) {
98
+ const stderr = String(error.stderr).trim();
99
+ if (stderr)
100
+ return stderr;
101
+ }
102
+ return error instanceof Error ? error.message : String(error);
103
+ }
104
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/secrets/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAItC,8EAA8E;AAC9E,yBAAyB;AACzB,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,oCAAoC;AAEpC,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAShC,4EAA4E;AAC5E,oEAAoE;AACpE,MAAM,GAAG,GAAG,yBAAyB,CAAC;AAEtC,gFAAgF;AAChF,yEAAyE;AACzE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAiC;IAEjC,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAEhD,2EAA2E;IAC3E,wDAAwD;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAClE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;IAEzC,MAAM,EAAE,GAAG,KAAK,EAAE,IAAc,EAAE,MAA8B,EAAE,EAAE,EAAE,CACpE,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE;QACpD,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,wBAAwB,EAAE,OAAO;YACjC,GAAG,GAAG;SACP;QACD,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEL,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAE9B,iEAAiE;IACjE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE;QAC9B,WAAW,EAAE,WAAW,CAAC,QAAQ;QACjC,eAAe,EAAE,WAAW,CAAC,YAAY;KAC1C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAE1B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;QAC7E,WAAW,EAAE,WAAW,CAAC,QAAQ;KAClC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QAC1B,MAAM,IAAI,KAAK,CAAC,yCAAyC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAEtE,2EAA2E;IAC3E,yDAAyD;IACzD,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAEnE,2EAA2E;IAC3E,uDAAuD;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA2B,CAAC;IAEjD,MAAM,KAAK,GAAG,KAAK,EAAE,EAAU,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE;YACnD,UAAU,EAAE,OAAO;SACpB,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YAC1B,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,oBAAoB,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;YACjB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;YAC3C,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACvB,OAAO,MAAM,OAAO,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC;AAID,6EAA6E;AAC7E,0EAA0E;AAC1E,KAAK,UAAU,UAAU,CAAC,MAAiC;IACzD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC/C,IAAI,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEpD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAC/C,GAAG,EAAE,CAAC,IAAI,EACV,GAAG,EAAE,CAAC,KAAK,CACZ,CAAC;IAEF,IAAI,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAE7C,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACrD,CAAC;AAED,+EAA+E;AAC/E,gFAAgF;AAChF,KAAK,UAAU,OAAO,CAAC,MAAiC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAE7D,IAAI,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAEtC,MAAM,CAAC,cAAc,GAAG,yBAAyB,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,GAAG,CACP,KAAK,EACL,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,EAC/E,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CAChC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,YAAY,KAAK,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IAED,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type Docker } from "../docker.js";
2
+ import type { Host } from "../host.js";
3
+ import type { Log } from "../log.js";
4
+ import { type SecretStores } from "../secrets/refs.js";
5
+ import type { ServiceTopology, Topology } from "../topology.js";
6
+ import type { ServiceSpec } from "../types.js";
7
+ export type EnsureContext = {
8
+ host: Host;
9
+ docker: Docker;
10
+ topology: Topology;
11
+ files: Record<string, string>;
12
+ secrets: SecretStores;
13
+ log: Log;
14
+ publish?: number;
15
+ };
16
+ export type Ensured = "adopted" | "started" | "created" | "recreated";
17
+ export declare function ensureService(spec: ServiceSpec, service: ServiceTopology, context: EnsureContext): Promise<Ensured>;
18
+ //# sourceMappingURL=ensure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure.d.ts","sourceRoot":"","sources":["../../src/services/ensure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAG9B,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,EAAE,GAAG,CAAC;IAGT,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAEtE,wBAAsB,aAAa,CACjC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,OAAO,CAAC,CA8BlB"}
@@ -0,0 +1,93 @@
1
+ import { SPEC_LABEL } from "../docker.js";
2
+ import { LISTEN_PORT } from "../nginx.js";
3
+ import { readEnv } from "../secrets/refs.js";
4
+ import { fingerprintOf } from "./planned.js";
5
+ export async function ensureService(spec, service, context) {
6
+ const { docker } = context;
7
+ const name = service.container;
8
+ const fingerprint = fingerprintOf(plannedFrom(spec, service, context), context.topology);
9
+ if (await docker.container.exists(name)) {
10
+ // A service outlives a deploy, so being there is usually the whole answer.
11
+ // Being there having been created from something else is not: a changed
12
+ // public port or a rendered config only reaches a container that is made
13
+ if (await docker.container.specOf(name) === fingerprint) {
14
+ if (await docker.container.isRunning(name))
15
+ return "adopted";
16
+ context.log.warn(`${name} was not running, starting it`);
17
+ await docker.container.start(name);
18
+ return "started";
19
+ }
20
+ context.log(`${name} is not what the deployment says, recreating it`);
21
+ await docker.container.stop(name);
22
+ await docker.container.remove(name);
23
+ await createService(spec, service, context, fingerprint);
24
+ await docker.container.start(name);
25
+ return "recreated";
26
+ }
27
+ context.log(`Creating ${name} at ${service.address}`);
28
+ await createService(spec, service, context, fingerprint);
29
+ await docker.container.start(name);
30
+ return "created";
31
+ }
32
+ // The same shape the plan compares against, assembled from what this call was
33
+ // handed. Two spellings of it would drift from each other rather than from the
34
+ // host, which is the one drift nothing here would report
35
+ function plannedFrom(spec, service, context) {
36
+ return { spec, service, files: context.files, publish: context.publish };
37
+ }
38
+ async function createService(spec, service, context, fingerprint) {
39
+ const { docker, topology } = context;
40
+ const name = service.container;
41
+ await buildImage(spec, name, context);
42
+ const builder = docker.container
43
+ .builder()
44
+ .name(name)
45
+ .image(name)
46
+ .network(topology.network)
47
+ .label(SPEC_LABEL, fingerprint)
48
+ .ip(service.address);
49
+ if (spec.restart)
50
+ builder.restart(spec.restart);
51
+ for (const [name, value] of Object.entries(spec.environment ?? {})) {
52
+ builder.env(name, value);
53
+ }
54
+ // Written to the host and handed over as a file, so the value never appears
55
+ // in an argument list. Docker reads it when the container is created and
56
+ // keeps the values, so the file goes with the rest of the deploy's scratch
57
+ if (spec.secrets) {
58
+ const contents = await readEnv(spec.secrets, context.secrets);
59
+ builder.envFile(await context.host.write(`services/${name}/env`, contents));
60
+ }
61
+ for (const volume of service.volumes) {
62
+ builder.volume(volume.volume, volume.mountPath);
63
+ }
64
+ if (context.publish) {
65
+ for (const [host, ip] of Object.entries(topology.extraHosts)) {
66
+ builder.extraHost(host, ip);
67
+ }
68
+ builder.port(context.publish, LISTEN_PORT);
69
+ }
70
+ await builder.create();
71
+ }
72
+ // Three lines of Dockerfile on the host, in place of a container assembled in
73
+ // an engine, exported as a tarball and loaded back in
74
+ async function buildImage(spec, name, context) {
75
+ const { host, docker } = context;
76
+ const directory = `services/${name}`;
77
+ const copies = await Promise.all(Object.entries(context.files).map(async ([path, contents], index) => {
78
+ await host.write(`${directory}/file.${index}`, contents);
79
+ return `COPY file.${index} ${path}`;
80
+ }));
81
+ const dockerfile = await host.write(`${directory}/Dockerfile`, [
82
+ `FROM ${spec.image}`,
83
+ ...copies,
84
+ ...(context.publish ? [`EXPOSE ${LISTEN_PORT}`] : []),
85
+ "",
86
+ ].join("\n"));
87
+ await docker.image.build({
88
+ context: `${host.directory}/${directory}`,
89
+ dockerfile,
90
+ tags: [name],
91
+ });
92
+ }
93
+ //# sourceMappingURL=ensure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure.js","sourceRoot":"","sources":["../../src/services/ensure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { SecretRefs, ServiceSpec } from "../types.js";
2
+ type RedisOptions = {
3
+ image?: string;
4
+ alias?: string;
5
+ address?: number;
6
+ volumes?: Record<string, string>;
7
+ };
8
+ export declare function redis(options?: RedisOptions): ServiceSpec;
9
+ type PostgresOptions = {
10
+ secrets: SecretRefs;
11
+ image?: string;
12
+ alias?: string;
13
+ address?: number;
14
+ volumes?: Record<string, string>;
15
+ environment?: Record<string, string>;
16
+ };
17
+ export declare function postgres(options: PostgresOptions): ServiceSpec;
18
+ export {};
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE3D,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,WAAW,CAW7D;AAED,KAAK,eAAe,GAAG;IAGrB,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,WAAW,CAa9D"}
@@ -0,0 +1,27 @@
1
+ export function redis(options = {}) {
2
+ return {
3
+ name: "redis",
4
+ image: options.image ?? "redis:7",
5
+ alias: options.alias ?? "redis",
6
+ restart: "always",
7
+ address: options.address,
8
+ // The image declares VOLUME /data, so without this the data lands in an
9
+ // anonymous volume that survives nothing and can be named by no one
10
+ volumes: options.volumes ?? { data: "/data" },
11
+ };
12
+ }
13
+ export function postgres(options) {
14
+ return {
15
+ name: "postgres",
16
+ image: options.image ?? "postgres:17-alpine",
17
+ alias: options.alias ?? "postgres",
18
+ restart: "always",
19
+ address: options.address,
20
+ secrets: options.secrets,
21
+ environment: options.environment,
22
+ // The image declares VOLUME on this path, so without naming it the data
23
+ // lands in an anonymous volume that survives nothing and nobody can name
24
+ volumes: options.volumes ?? { data: "/var/lib/postgresql/data" },
25
+ };
26
+ }
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,KAAK,CAAC,UAAwB,EAAE;IAC9C,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;QACjC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,OAAO;QAC/B,OAAO,EAAE,QAAQ;QACjB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,wEAAwE;QACxE,oEAAoE;QACpE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;KAC9C,CAAC;AACJ,CAAC;AAcD,MAAM,UAAU,QAAQ,CAAC,OAAwB;IAC/C,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,oBAAoB;QAC5C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,UAAU;QAClC,OAAO,EAAE,QAAQ;QACjB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,wEAAwE;QACxE,yEAAyE;QACzE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,IAAI,EAAE,0BAA0B,EAAE;KACjE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Docker } from "../docker.js";
2
+ import type { Run } from "../pipeline.js";
3
+ import type { ServiceTopology, Topology } from "../topology.js";
4
+ import type { Deployment, ServiceSpec } from "../types.js";
5
+ export type PlannedService = {
6
+ spec: ServiceSpec;
7
+ service: ServiceTopology;
8
+ files: Record<string, string>;
9
+ publish?: number;
10
+ };
11
+ export declare function plannedServices(config: Deployment, topology: Topology, run?: Run): PlannedService[];
12
+ export declare function router(config: Deployment): ServiceSpec;
13
+ export declare function fingerprintOf(planned: PlannedService, topology: Topology): string;
14
+ export type Drift = {
15
+ container: string;
16
+ name: string;
17
+ reason: "missing" | "stopped" | "changed" | "unrecognised";
18
+ };
19
+ export declare function driftOf(planned: PlannedService[], topology: Topology, docker: Docker): Promise<Drift[]>;
20
+ //# sourceMappingURL=planned.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planned.d.ts","sourceRoot":"","sources":["../../src/services/planned.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO3D,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,eAAe,CAAC;IAEzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,QAAQ,EAClB,GAAG,GAAE,GAAc,GAClB,cAAc,EAAE,CAsBlB;AAID,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAMtD;AAKD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,UAkBxE;AAID,MAAM,MAAM,KAAK,GAAG;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,cAAc,CAAC;CAC5D,CAAC;AAEF,wBAAsB,OAAO,CAC3B,OAAO,EAAE,cAAc,EAAE,EACzB,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,EAAE,CAAC,CASlB"}
@@ -0,0 +1,77 @@
1
+ import { createHash } from "node:crypto";
2
+ import { LISTEN_PORT, renderNginx } from "../nginx.js";
3
+ export function plannedServices(config, topology, run = "deploy") {
4
+ const rest = config.services.map((spec) => {
5
+ const service = topology.services.find((item) => item.name === spec.name);
6
+ if (!service)
7
+ throw new Error(`No topology for service ${spec.name}`);
8
+ return { spec, service, files: spec.files ?? {} };
9
+ });
10
+ // Nothing serves in a verify run, so a proxy there would resolve upstreams
11
+ // that were never created and publish a port for them
12
+ if (run === "verify")
13
+ return rest;
14
+ const proxy = {
15
+ spec: router(config),
16
+ service: topology.router,
17
+ files: {
18
+ "/etc/nginx/conf.d/default.conf": renderNginx(topology, config.maxBodySize),
19
+ },
20
+ publish: topology.publicPort,
21
+ };
22
+ return [proxy, ...rest];
23
+ }
24
+ // Not something a deployment lists. Apps carry routes, routes need a proxy to
25
+ // resolve them, and the one that renders them is this
26
+ export function router(config) {
27
+ return {
28
+ name: "nginx",
29
+ image: config.proxyImage ?? "nginx:stable",
30
+ restart: "always",
31
+ };
32
+ }
33
+ // Everything a recreate would change, and nothing a deploy resolves. Secrets
34
+ // are named by their refs rather than their values: whether the config is the
35
+ // one running is a question a plan should answer without unlocking a vault
36
+ export function fingerprintOf(planned, topology) {
37
+ const { spec, service, files, publish } = planned;
38
+ const shape = {
39
+ image: spec.image,
40
+ restart: spec.restart,
41
+ address: service.address,
42
+ network: topology.network,
43
+ volumes: service.volumes,
44
+ environment: spec.environment,
45
+ secrets: spec.secrets,
46
+ files,
47
+ // The proxy alone, and the two things being published costs it
48
+ publish: publish && { host: publish, container: LISTEN_PORT },
49
+ extraHosts: publish ? topology.extraHosts : undefined,
50
+ };
51
+ return createHash("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 16);
52
+ }
53
+ export async function driftOf(planned, topology, docker) {
54
+ const drifted = [];
55
+ for (const item of planned) {
56
+ const reason = await reasonFor(item, topology, docker);
57
+ if (reason)
58
+ drifted.push({ container: item.service.container, name: item.spec.name, reason });
59
+ }
60
+ return drifted;
61
+ }
62
+ async function reasonFor(planned, topology, docker) {
63
+ const name = planned.service.container;
64
+ if (!(await docker.container.exists(name)))
65
+ return "missing";
66
+ const held = await docker.container.specOf(name);
67
+ // Created by hand, or by a redkite that did not record what it created from.
68
+ // Either way nothing here can say it matches, so it is rebuilt once
69
+ if (!held)
70
+ return "unrecognised";
71
+ if (held !== fingerprintOf(planned, topology))
72
+ return "changed";
73
+ if (!(await docker.container.isRunning(name)))
74
+ return "stopped";
75
+ return undefined;
76
+ }
77
+ //# sourceMappingURL=planned.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planned.js","sourceRoot":"","sources":["../../src/services/planned.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type OnLine, type Result } from "./host.js";
2
+ export type SpawnOptions = {
3
+ stdin?: string;
4
+ onLine?: OnLine;
5
+ signal?: AbortSignal;
6
+ };
7
+ export declare function signalEverything(name: NodeJS.Signals): number;
8
+ export declare function stillRunning(): number;
9
+ export declare function spawnCollect(command: string, args: string[], options?: SpawnOptions): Promise<Result>;
10
+ export declare function quote(value: string): string;
11
+ //# sourceMappingURL=shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAKvE,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAIhB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAQF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,UAUpD;AAED,wBAAgB,YAAY,WAE3B;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,CA8CjB;AAID,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,UAElC"}