@geekmidas/cli 10.0.0-alpha.60 → 10.0.0-alpha.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{HostingerProvider-DgzKa_Tj.mjs → HostingerProvider-B92qH4dC.mjs} +2 -2
- package/dist/{HostingerProvider-DgzKa_Tj.mjs.map → HostingerProvider-B92qH4dC.mjs.map} +1 -1
- package/dist/{HostingerProvider-BTZd2u0f.cjs → HostingerProvider-DyD1Hin8.cjs} +2 -2
- package/dist/{HostingerProvider-BTZd2u0f.cjs.map → HostingerProvider-DyD1Hin8.cjs.map} +1 -1
- package/dist/{LegacyStateStore-CToa2hga.mjs → LegacyStateStore-A8d2GdT7.mjs} +2 -2
- package/dist/{LegacyStateStore-CToa2hga.mjs.map → LegacyStateStore-A8d2GdT7.mjs.map} +1 -1
- package/dist/{LegacyStateStore-CidlPT0N.cjs → LegacyStateStore-UHJEBLdn.cjs} +2 -2
- package/dist/{LegacyStateStore-CidlPT0N.cjs.map → LegacyStateStore-UHJEBLdn.cjs.map} +1 -1
- package/dist/{LocalStateStore-BFLiOocU.mjs → LocalStateStore-B51rnfgn.mjs} +2 -2
- package/dist/{LocalStateStore-BFLiOocU.mjs.map → LocalStateStore-B51rnfgn.mjs.map} +1 -1
- package/dist/{LocalStateStore-Dpuqka1N.cjs → LocalStateStore-COW3Ldwa.cjs} +2 -2
- package/dist/{LocalStateStore-Dpuqka1N.cjs.map → LocalStateStore-COW3Ldwa.cjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-BTuUumsV.cjs → OpenApiTsGenerator-BSrgw7B0.cjs} +31 -2
- package/dist/OpenApiTsGenerator-BSrgw7B0.cjs.map +1 -0
- package/dist/{OpenApiTsGenerator-CvVr1Ilz.mjs → OpenApiTsGenerator-q5B0z8Xg.mjs} +31 -2
- package/dist/OpenApiTsGenerator-q5B0z8Xg.mjs.map +1 -0
- package/dist/{S3StateStore-aaGkjcxO.mjs → S3StateStore-CJewPhgz.mjs} +2 -2
- package/dist/{S3StateStore-aaGkjcxO.mjs.map → S3StateStore-CJewPhgz.mjs.map} +1 -1
- package/dist/{S3StateStore-cnm813Xn.cjs → S3StateStore-WUJGdRNL.cjs} +2 -2
- package/dist/{S3StateStore-cnm813Xn.cjs.map → S3StateStore-WUJGdRNL.cjs.map} +1 -1
- package/dist/{SSMStateStore-DwaGprVX.cjs → SSMStateStore-Zejt42V8.cjs} +2 -2
- package/dist/{SSMStateStore-DwaGprVX.cjs.map → SSMStateStore-Zejt42V8.cjs.map} +1 -1
- package/dist/{SSMStateStore-BllaJKGo.mjs → SSMStateStore-iK5UKp9d.mjs} +2 -2
- package/dist/{SSMStateStore-BllaJKGo.mjs.map → SSMStateStore-iK5UKp9d.mjs.map} +1 -1
- package/dist/{bundler-CzXZPSBJ.cjs → bundler-CfH9frOS.cjs} +2 -2
- package/dist/{bundler-CzXZPSBJ.cjs.map → bundler-CfH9frOS.cjs.map} +1 -1
- package/dist/{bundler-DJVR4QlW.mjs → bundler-DN5r4x5b.mjs} +2 -2
- package/dist/{bundler-DJVR4QlW.mjs.map → bundler-DN5r4x5b.mjs.map} +1 -1
- package/dist/{cli-D_qTl20Z.cjs → cli-DOAUp80L.cjs} +12 -16
- package/dist/cli-DOAUp80L.cjs.map +1 -0
- package/dist/{cli-DzR1hpum.mjs → cli-DYwxmfsW.mjs} +10 -14
- package/dist/cli-DYwxmfsW.mjs.map +1 -0
- package/dist/{config-DXnkCEq0.cjs → config-BYh9S0cZ.cjs} +2 -2
- package/dist/config-BYh9S0cZ.cjs.map +1 -0
- package/dist/{config-C5-EEdmr.mjs → config-Bf4-bROQ.mjs} +2 -2
- package/dist/config-Bf4-bROQ.mjs.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/define-CHBIEc5j.mjs +94 -0
- package/dist/define-CHBIEc5j.mjs.map +1 -0
- package/dist/define-Cv_QvDrg.cjs +141 -0
- package/dist/define-Cv_QvDrg.cjs.map +1 -0
- package/dist/deploy/public.cjs +28 -13
- package/dist/deploy/public.d.cts +38 -315
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +38 -315
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +8 -3
- package/dist/{deploy-D6Q09OkA.mjs → deploy-B7jvUORo.mjs} +155 -682
- package/dist/deploy-B7jvUORo.mjs.map +1 -0
- package/dist/deploy-Cg9fae7a.mjs +133 -0
- package/dist/deploy-Cg9fae7a.mjs.map +1 -0
- package/dist/deploy-D8Qr-Xto.cjs +138 -0
- package/dist/deploy-D8Qr-Xto.cjs.map +1 -0
- package/dist/{deploy-0fBVeWO-.cjs → deploy-DSaCYvcO.cjs} +182 -727
- package/dist/deploy-DSaCYvcO.cjs.map +1 -0
- package/dist/dokploy-CD9PY-3D.mjs +32 -0
- package/dist/dokploy-CD9PY-3D.mjs.map +1 -0
- package/dist/dokploy-Ey2foJLv.cjs +32 -0
- package/dist/dokploy-Ey2foJLv.cjs.map +1 -0
- package/dist/{encryption-aDA2AzVx.mjs → encryption-CVwvGbsL.mjs} +4 -4
- package/dist/{encryption-aDA2AzVx.mjs.map → encryption-CVwvGbsL.mjs.map} +1 -1
- package/dist/{encryption-CML4UaPi.cjs → encryption-ChBk3hTj.cjs} +4 -4
- package/dist/{encryption-CML4UaPi.cjs.map → encryption-ChBk3hTj.cjs.map} +1 -1
- package/dist/{github-DmCj2u95.mjs → github-BCB3_LBX.mjs} +3 -3
- package/dist/{github-DmCj2u95.mjs.map → github-BCB3_LBX.mjs.map} +1 -1
- package/dist/{github-Cq57poPj.cjs → github-OUgPDlqg.cjs} +3 -3
- package/dist/{github-Cq57poPj.cjs.map → github-OUgPDlqg.cjs.map} +1 -1
- package/dist/{index-UiWCaGzU.d.cts → index-BtXDZQuF.d.cts} +2 -2
- package/dist/{index-Da1-oa9W.d.mts.map → index-BtXDZQuF.d.cts.map} +1 -1
- package/dist/{index-Da1-oa9W.d.mts → index-DrKzpHl-.d.mts} +2 -2
- package/dist/{index-UiWCaGzU.d.cts.map → index-DrKzpHl-.d.mts.map} +1 -1
- package/dist/index.cjs +1728 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1719 -153
- package/dist/index.mjs.map +1 -1
- package/dist/migrate-BoHMqdCu.mjs +265 -0
- package/dist/migrate-BoHMqdCu.mjs.map +1 -0
- package/dist/migrate-dnaFMvOm.cjs +270 -0
- package/dist/migrate-dnaFMvOm.cjs.map +1 -0
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/{ready-CIHpnUc5.mjs → ready-DQIy21W5.mjs} +2 -2
- package/dist/{ready-CIHpnUc5.mjs.map → ready-DQIy21W5.mjs.map} +1 -1
- package/dist/{ready-CCLIAkwR.cjs → ready-XlNx-Gbo.cjs} +2 -2
- package/dist/{ready-CCLIAkwR.cjs.map → ready-XlNx-Gbo.cjs.map} +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/resolve-CFCIkm7m.mjs +170 -0
- package/dist/resolve-CFCIkm7m.mjs.map +1 -0
- package/dist/resolve-wbv66HjD.cjs +217 -0
- package/dist/resolve-wbv66HjD.cjs.map +1 -0
- package/dist/runtime-CMwtq0AL.cjs +163 -0
- package/dist/runtime-CMwtq0AL.cjs.map +1 -0
- package/dist/runtime-CXPlkZzs.d.mts +90 -0
- package/dist/runtime-CXPlkZzs.d.mts.map +1 -0
- package/dist/runtime-DbakJS8u.mjs +134 -0
- package/dist/runtime-DbakJS8u.mjs.map +1 -0
- package/dist/runtime-Dn_n-29S.d.cts +90 -0
- package/dist/runtime-Dn_n-29S.d.cts.map +1 -0
- package/dist/{sniffer-DIv12ncU.cjs → sniffer-BsfPa-GA.cjs} +2581 -2170
- package/dist/sniffer-BsfPa-GA.cjs.map +1 -0
- package/dist/{sniffer-BM8fpCas.mjs → sniffer-DHE2Nafd.mjs} +2428 -2185
- package/dist/sniffer-DHE2Nafd.mjs.map +1 -0
- package/dist/target/public.cjs +15 -0
- package/dist/target/public.d.cts +28 -0
- package/dist/target/public.d.cts.map +1 -0
- package/dist/target/public.d.mts +28 -0
- package/dist/target/public.d.mts.map +1 -0
- package/dist/target/public.mjs +4 -0
- package/dist/terminal-6oL0vANs.cjs +576 -0
- package/dist/terminal-6oL0vANs.cjs.map +1 -0
- package/dist/terminal-ByZNjuig.mjs +517 -0
- package/dist/terminal-ByZNjuig.mjs.map +1 -0
- package/dist/{types-efNLu9xu.d.cts → types-CfLKGfC_.d.mts} +624 -34
- package/dist/types-CfLKGfC_.d.mts.map +1 -0
- package/dist/{types-Bp1mVxG0.d.mts → types-DIA7Li-J.d.cts} +626 -34
- package/dist/types-DIA7Li-J.d.cts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-B64Xk1Mb.cjs → workspace-B6zQO6RY.cjs} +229 -23
- package/dist/workspace-B6zQO6RY.cjs.map +1 -0
- package/dist/{workspace-Bp1xVaOz.mjs → workspace-BFe57es7.mjs} +229 -23
- package/dist/workspace-BFe57es7.mjs.map +1 -0
- package/package.json +23 -13
- package/dist/OpenApiTsGenerator-BTuUumsV.cjs.map +0 -1
- package/dist/OpenApiTsGenerator-CvVr1Ilz.mjs.map +0 -1
- package/dist/cli-D_qTl20Z.cjs.map +0 -1
- package/dist/cli-DzR1hpum.mjs.map +0 -1
- package/dist/config-C5-EEdmr.mjs.map +0 -1
- package/dist/config-DXnkCEq0.cjs.map +0 -1
- package/dist/deploy-0fBVeWO-.cjs.map +0 -1
- package/dist/deploy-D6Q09OkA.mjs.map +0 -1
- package/dist/migrate-BTqIflwc.cjs +0 -743
- package/dist/migrate-BTqIflwc.cjs.map +0 -1
- package/dist/migrate-B_6KofUo.mjs +0 -736
- package/dist/migrate-B_6KofUo.mjs.map +0 -1
- package/dist/sniffer-BM8fpCas.mjs.map +0 -1
- package/dist/sniffer-DIv12ncU.cjs.map +0 -1
- package/dist/types-Bp1mVxG0.d.mts.map +0 -1
- package/dist/types-efNLu9xu.d.cts.map +0 -1
- package/dist/workspace-B64Xk1Mb.cjs.map +0 -1
- package/dist/workspace-Bp1xVaOz.mjs.map +0 -1
|
@@ -1,110 +1,19 @@
|
|
|
1
|
-
const require_sniffer = require("./sniffer-
|
|
2
|
-
const require_home = require("./home-DOzBaWaL.cjs");
|
|
1
|
+
const require_sniffer = require("./sniffer-BsfPa-GA.cjs");
|
|
3
2
|
const require_discover = require("./discover-Bc-4eA_p.cjs");
|
|
4
|
-
const require_config = require("./config-
|
|
5
|
-
const require_workspace = require("./workspace-
|
|
3
|
+
const require_config = require("./config-BYh9S0cZ.cjs");
|
|
4
|
+
const require_workspace = require("./workspace-B6zQO6RY.cjs");
|
|
6
5
|
const require_storage = require("./storage-CSGoC3bK.cjs");
|
|
7
6
|
const require_store = require("./store-KvNQwYaB.cjs");
|
|
7
|
+
const require_terminal = require("./terminal-6oL0vANs.cjs");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
10
10
|
let node_os = require("node:os");
|
|
11
11
|
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
12
12
|
let node_crypto = require("node:crypto");
|
|
13
|
-
let _geekmidas_constructs_encryption = require("@geekmidas/constructs/encryption");
|
|
14
13
|
let _geekmidas_db_pg_roles = require("@geekmidas/db/pg/roles");
|
|
15
14
|
let pg = require("pg");
|
|
16
15
|
let _geekmidas_cache_postgres = require("@geekmidas/cache/postgres");
|
|
17
16
|
let node_dns_promises = require("node:dns/promises");
|
|
18
|
-
//#region src/deploy/credentials.ts
|
|
19
|
-
/**
|
|
20
|
-
* How a deploy gets the credentials it acts with.
|
|
21
|
-
*
|
|
22
|
-
* `deploy()` never prompts and never reads a terminal: it asks a
|
|
23
|
-
* `CredentialProvider` and, when the provider has nothing, stops with
|
|
24
|
-
* `MissingCredential` naming what it needed and how to supply it. Whether to
|
|
25
|
-
* ask a person, read a vault or fail is the caller's decision — `gkm deploy`
|
|
26
|
-
* prompts at a terminal and stores what was typed; a CI job sets the
|
|
27
|
-
* environment; a host passes its own provider.
|
|
28
|
-
*/
|
|
29
|
-
/** How to supply each kind, for the error that says it is missing. */
|
|
30
|
-
const HOW_TO_PROVIDE = {
|
|
31
|
-
dokploy: "Set DOKPLOY_API_TOKEN (and DOKPLOY_ENDPOINT, or deploy.dokploy.endpoint in gkm.config.ts), run `gkm login --provider dokploy`, or pass them through the deploy's CredentialProvider.",
|
|
32
|
-
registry: "Add the registry in Dokploy (Settings → Docker Registry) and set deploy.dokploy.registryId, set DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD, or run `gkm deploy` at a terminal to be asked for them."
|
|
33
|
-
};
|
|
34
|
-
/** A deploy needed a credential, and its provider had none. */
|
|
35
|
-
var MissingCredential = class extends Error {
|
|
36
|
-
kind;
|
|
37
|
-
target;
|
|
38
|
-
howToProvide;
|
|
39
|
-
constructor(kind, target, howToProvide = HOW_TO_PROVIDE[kind]) {
|
|
40
|
-
const what = kind === "dokploy" ? `No Dokploy credentials${target ? ` for ${target}` : ""}.` : `Dokploy has no registry for ${target ?? "the configured registry"}, and there are no credentials to create one with.`;
|
|
41
|
-
super(`${what} ${howToProvide}`);
|
|
42
|
-
this.kind = kind;
|
|
43
|
-
this.target = target;
|
|
44
|
-
this.howToProvide = howToProvide;
|
|
45
|
-
this.name = "MissingCredential";
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
/** Whatever was asked for, from the first provider that has it. */
|
|
49
|
-
function chainCredentials(...providers) {
|
|
50
|
-
return { async get(request, options) {
|
|
51
|
-
for (const provider of providers) {
|
|
52
|
-
const found = await provider.get(request, options);
|
|
53
|
-
if (found) return found;
|
|
54
|
-
}
|
|
55
|
-
} };
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Credentials from the environment, then from what `gkm login` stored. What
|
|
59
|
-
* `deploy()` uses when it is handed no provider.
|
|
60
|
-
*
|
|
61
|
-
* - Dokploy: `DOKPLOY_API_TOKEN` / `DOKPLOY_ENDPOINT`, each falling back to
|
|
62
|
-
* the stored login, and the endpoint finally to `deploy.dokploy.endpoint`.
|
|
63
|
-
* Each half is read on its own: a token is a secret and belongs in the
|
|
64
|
-
* environment, an endpoint is configuration and belongs in the config.
|
|
65
|
-
* - Registry: `DOCKER_REGISTRY_USERNAME` / `DOCKER_REGISTRY_PASSWORD`.
|
|
66
|
-
*/
|
|
67
|
-
function storedCredentials(options = {}) {
|
|
68
|
-
const env = options.env ?? process.env;
|
|
69
|
-
return { async get(asked) {
|
|
70
|
-
const request = asked;
|
|
71
|
-
switch (request.kind) {
|
|
72
|
-
case "dokploy": {
|
|
73
|
-
const stored = (await require_sniffer.readCredentials(options.home ? { home: options.home } : void 0)).dokploy;
|
|
74
|
-
const token = env.DOKPLOY_API_TOKEN ?? stored?.token;
|
|
75
|
-
const endpoint = env.DOKPLOY_ENDPOINT ?? stored?.endpoint ?? request.endpoint;
|
|
76
|
-
if (!token || !endpoint) return void 0;
|
|
77
|
-
return {
|
|
78
|
-
endpoint: endpoint.replace(/\/$/, ""),
|
|
79
|
-
token
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
case "registry": {
|
|
83
|
-
const username = env.DOCKER_REGISTRY_USERNAME;
|
|
84
|
-
const password = env.DOCKER_REGISTRY_PASSWORD;
|
|
85
|
-
if (!username || !password) return void 0;
|
|
86
|
-
return {
|
|
87
|
-
username,
|
|
88
|
-
password
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
} };
|
|
93
|
-
}
|
|
94
|
-
//#endregion
|
|
95
|
-
//#region src/deploy/events.ts
|
|
96
|
-
/** An error as an event carries it: its name and message, nothing else. */
|
|
97
|
-
function eventError(error) {
|
|
98
|
-
if (error instanceof Error) return {
|
|
99
|
-
name: error.name,
|
|
100
|
-
message: error.message
|
|
101
|
-
};
|
|
102
|
-
return {
|
|
103
|
-
name: "Error",
|
|
104
|
-
message: String(error)
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
17
|
//#region src/deploy/fromManifest.ts
|
|
109
18
|
/**
|
|
110
19
|
* The Dokploy target, read off the construct manifest.
|
|
@@ -855,108 +764,6 @@ function surfaceAddresses(manifest, appUrls) {
|
|
|
855
764
|
return addresses;
|
|
856
765
|
}
|
|
857
766
|
//#endregion
|
|
858
|
-
//#region src/deploy/state.ts
|
|
859
|
-
/**
|
|
860
|
-
* Deploy state management for Dokploy deployments
|
|
861
|
-
*
|
|
862
|
-
* Stores resource IDs (applications, services) per stage to avoid
|
|
863
|
-
* re-creating resources on subsequent deploys.
|
|
864
|
-
*/
|
|
865
|
-
/**
|
|
866
|
-
* Create a new empty state for a stage
|
|
867
|
-
*/
|
|
868
|
-
function createEmptyState(stage, projectId, environmentId) {
|
|
869
|
-
return {
|
|
870
|
-
provider: "dokploy",
|
|
871
|
-
stage,
|
|
872
|
-
projectId,
|
|
873
|
-
environmentId,
|
|
874
|
-
applications: {},
|
|
875
|
-
services: {},
|
|
876
|
-
lastDeployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
/**
|
|
880
|
-
* Set application ID in state (mutates state)
|
|
881
|
-
*/
|
|
882
|
-
function setApplicationId(state, appName, applicationId) {
|
|
883
|
-
state.applications[appName] = applicationId;
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Record the image an app was deployed with (mutates state)
|
|
887
|
-
*/
|
|
888
|
-
function setDeployedImage(state, appName, image) {
|
|
889
|
-
state.images = {
|
|
890
|
-
...state.images,
|
|
891
|
-
[appName]: image
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* Get a generated secret for an app
|
|
896
|
-
*/
|
|
897
|
-
function getGeneratedSecret(state, appName, secretName) {
|
|
898
|
-
return state?.generatedSecrets?.[appName]?.[secretName];
|
|
899
|
-
}
|
|
900
|
-
/**
|
|
901
|
-
* Set a generated secret for an app (mutates state)
|
|
902
|
-
*/
|
|
903
|
-
function setGeneratedSecret(state, appName, secretName, value) {
|
|
904
|
-
if (!state.generatedSecrets) state.generatedSecrets = {};
|
|
905
|
-
if (!state.generatedSecrets[appName]) state.generatedSecrets[appName] = {};
|
|
906
|
-
state.generatedSecrets[appName][secretName] = value;
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Set DNS verification record for a hostname (mutates state)
|
|
910
|
-
*/
|
|
911
|
-
function setDnsVerification(state, hostname, serverIp) {
|
|
912
|
-
if (!state.dnsVerified) state.dnsVerified = {};
|
|
913
|
-
state.dnsVerified[hostname] = {
|
|
914
|
-
serverIp,
|
|
915
|
-
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Check if a hostname is already verified with the given IP
|
|
920
|
-
*/
|
|
921
|
-
function isDnsVerified(state, hostname, serverIp) {
|
|
922
|
-
return (state?.dnsVerified?.[hostname])?.serverIp === serverIp;
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* Get the key for a DNS record in state
|
|
926
|
-
*/
|
|
927
|
-
function getDnsRecordKey(name, type) {
|
|
928
|
-
return `${name}:${type}`;
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* Set a created DNS record in state (mutates state)
|
|
932
|
-
*/
|
|
933
|
-
function setDnsRecord(state, record) {
|
|
934
|
-
if (!state.dnsRecords) state.dnsRecords = {};
|
|
935
|
-
const key = getDnsRecordKey(record.name, record.type);
|
|
936
|
-
state.dnsRecords[key] = {
|
|
937
|
-
...record,
|
|
938
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
939
|
-
};
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Get backup state from state
|
|
943
|
-
*/
|
|
944
|
-
function getBackupState(state) {
|
|
945
|
-
return state?.backups;
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Set backup state (mutates state)
|
|
949
|
-
*/
|
|
950
|
-
function setBackupState(state, backupState) {
|
|
951
|
-
state.backups = backupState;
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
* Set postgres backup ID in state (mutates state)
|
|
955
|
-
*/
|
|
956
|
-
function setPostgresBackupId(state, backupId) {
|
|
957
|
-
if (state.backups) state.backups.postgresBackupId = backupId;
|
|
958
|
-
}
|
|
959
|
-
//#endregion
|
|
960
767
|
//#region src/deploy/dns/DnsProvider.ts
|
|
961
768
|
/**
|
|
962
769
|
* Check if value is a DnsProvider implementation.
|
|
@@ -978,7 +785,7 @@ async function createDnsProvider(options) {
|
|
|
978
785
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
979
786
|
const provider = config.provider;
|
|
980
787
|
if (provider === "hostinger") {
|
|
981
|
-
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-
|
|
788
|
+
const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-DyD1Hin8.cjs"));
|
|
982
789
|
return new HostingerProvider();
|
|
983
790
|
}
|
|
984
791
|
if (provider === "route53") {
|
|
@@ -1000,7 +807,7 @@ async function createDnsProvider(options) {
|
|
|
1000
807
|
*
|
|
1001
808
|
* Handles automatic DNS record creation for deployed applications.
|
|
1002
809
|
*/
|
|
1003
|
-
const logger$
|
|
810
|
+
const logger$2 = require_discover.output;
|
|
1004
811
|
/**
|
|
1005
812
|
* Check if DNS config is legacy format (single domain with `domain` property)
|
|
1006
813
|
*/
|
|
@@ -1037,7 +844,7 @@ function groupHostnamesByDomain(appHostnames, dnsConfig) {
|
|
|
1037
844
|
for (const [appName, hostname] of appHostnames) {
|
|
1038
845
|
const rootDomain = findRootDomain(hostname, dnsConfig);
|
|
1039
846
|
if (!rootDomain) {
|
|
1040
|
-
logger$
|
|
847
|
+
logger$2.log(` ⚠ No DNS config found for hostname: ${hostname}`);
|
|
1041
848
|
continue;
|
|
1042
849
|
}
|
|
1043
850
|
if (!grouped.has(rootDomain)) grouped.set(rootDomain, /* @__PURE__ */ new Map());
|
|
@@ -1087,10 +894,10 @@ function generateRequiredRecords(appHostnames, rootDomain, serverIp) {
|
|
|
1087
894
|
* Print DNS records table
|
|
1088
895
|
*/
|
|
1089
896
|
function printDnsRecordsTable(records, rootDomain) {
|
|
1090
|
-
logger$
|
|
1091
|
-
logger$
|
|
1092
|
-
logger$
|
|
1093
|
-
logger$
|
|
897
|
+
logger$2.log(`\n 📋 DNS Records for ${rootDomain}:`);
|
|
898
|
+
logger$2.log(" ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐");
|
|
899
|
+
logger$2.log(" │ Subdomain │ Type │ Value │ Status │");
|
|
900
|
+
logger$2.log(" ├─────────────────────────────────────┼──────┼─────────────────┼────────┤");
|
|
1094
901
|
for (const record of records) {
|
|
1095
902
|
const subdomain = record.subdomain.padEnd(35);
|
|
1096
903
|
const type = record.type.padEnd(4);
|
|
@@ -1100,18 +907,18 @@ function printDnsRecordsTable(records, rootDomain) {
|
|
|
1100
907
|
else if (record.created) status = "✓ new";
|
|
1101
908
|
else if (record.existed) status = "✓";
|
|
1102
909
|
else status = "?";
|
|
1103
|
-
logger$
|
|
910
|
+
logger$2.log(` │ ${subdomain} │ ${type} │ ${value} │ ${status.padEnd(6)} │`);
|
|
1104
911
|
}
|
|
1105
|
-
logger$
|
|
912
|
+
logger$2.log(" └─────────────────────────────────────┴──────┴─────────────────┴────────┘");
|
|
1106
913
|
}
|
|
1107
914
|
/**
|
|
1108
915
|
* Print DNS records in a simple format for manual setup
|
|
1109
916
|
*/
|
|
1110
917
|
function printDnsRecordsSimple(records, rootDomain) {
|
|
1111
|
-
logger$
|
|
1112
|
-
logger$
|
|
1113
|
-
for (const record of records) logger$
|
|
1114
|
-
logger$
|
|
918
|
+
logger$2.log("\n 📋 Required DNS Records:");
|
|
919
|
+
logger$2.log(` Add these A records to your DNS provider (${rootDomain}):\n`);
|
|
920
|
+
for (const record of records) logger$2.log(` ${record.subdomain} → ${record.value} (A record)`);
|
|
921
|
+
logger$2.log("");
|
|
1115
922
|
}
|
|
1116
923
|
/**
|
|
1117
924
|
* Create DNS records for a single domain using its configured provider
|
|
@@ -1123,7 +930,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
1123
930
|
provider = await createDnsProvider({ config: providerConfig });
|
|
1124
931
|
} catch (error) {
|
|
1125
932
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1126
|
-
logger$
|
|
933
|
+
logger$2.log(` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`);
|
|
1127
934
|
return records.map((r) => ({
|
|
1128
935
|
...r,
|
|
1129
936
|
error: message
|
|
@@ -1177,7 +984,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
1177
984
|
}
|
|
1178
985
|
} catch (error) {
|
|
1179
986
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1180
|
-
logger$
|
|
987
|
+
logger$2.log(` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`);
|
|
1181
988
|
return records.map((r) => ({
|
|
1182
989
|
hostname: r.hostname,
|
|
1183
990
|
subdomain: r.subdomain,
|
|
@@ -1204,20 +1011,20 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
|
|
|
1204
1011
|
async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
1205
1012
|
if (!dnsConfig) return null;
|
|
1206
1013
|
const normalizedConfig = normalizeDnsConfig(dnsConfig);
|
|
1207
|
-
logger$
|
|
1014
|
+
logger$2.log("\n🌐 Setting up DNS records...");
|
|
1208
1015
|
let serverIp;
|
|
1209
1016
|
try {
|
|
1210
1017
|
const endpointUrl = new URL(dokployEndpoint);
|
|
1211
1018
|
serverIp = await resolveHostnameToIp(endpointUrl.hostname);
|
|
1212
|
-
logger$
|
|
1019
|
+
logger$2.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);
|
|
1213
1020
|
} catch (error) {
|
|
1214
1021
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1215
|
-
logger$
|
|
1022
|
+
logger$2.log(` ⚠ Failed to resolve server IP: ${message}`);
|
|
1216
1023
|
return null;
|
|
1217
1024
|
}
|
|
1218
1025
|
const groupedHostnames = groupHostnamesByDomain(appHostnames, normalizedConfig);
|
|
1219
1026
|
if (groupedHostnames.size === 0) {
|
|
1220
|
-
logger$
|
|
1027
|
+
logger$2.log(" No DNS records needed (no hostnames match configured domains)");
|
|
1221
1028
|
return {
|
|
1222
1029
|
records: [],
|
|
1223
1030
|
success: true,
|
|
@@ -1229,26 +1036,26 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
1229
1036
|
for (const [rootDomain, domainHostnames] of groupedHostnames) {
|
|
1230
1037
|
const providerConfig = normalizedConfig[rootDomain];
|
|
1231
1038
|
if (!providerConfig) {
|
|
1232
|
-
logger$
|
|
1039
|
+
logger$2.log(` ⚠ No provider config for ${rootDomain}`);
|
|
1233
1040
|
continue;
|
|
1234
1041
|
}
|
|
1235
1042
|
const providerName = typeof providerConfig.provider === "string" ? providerConfig.provider : "custom";
|
|
1236
1043
|
const requiredRecords = generateRequiredRecords(domainHostnames, rootDomain, serverIp);
|
|
1237
1044
|
if (requiredRecords.length === 0) continue;
|
|
1238
|
-
logger$
|
|
1045
|
+
logger$2.log(` Creating DNS records for ${rootDomain} (${providerName})...`);
|
|
1239
1046
|
const domainRecords = await createDnsRecordsForDomain(requiredRecords, rootDomain, providerConfig);
|
|
1240
1047
|
allRecords.push(...domainRecords);
|
|
1241
1048
|
const created = domainRecords.filter((r) => r.created).length;
|
|
1242
1049
|
const existed = domainRecords.filter((r) => r.existed).length;
|
|
1243
1050
|
const failed = domainRecords.filter((r) => r.error).length;
|
|
1244
|
-
if (created > 0) logger$
|
|
1245
|
-
if (existed > 0) logger$
|
|
1051
|
+
if (created > 0) logger$2.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);
|
|
1052
|
+
if (existed > 0) logger$2.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);
|
|
1246
1053
|
if (failed > 0) {
|
|
1247
|
-
logger$
|
|
1054
|
+
logger$2.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
|
|
1248
1055
|
hasFailures = true;
|
|
1249
1056
|
}
|
|
1250
1057
|
if (state) {
|
|
1251
|
-
for (const record of domainRecords) if (record.created || record.existed) setDnsRecord(state, {
|
|
1058
|
+
for (const record of domainRecords) if (record.created || record.existed) require_sniffer.setDnsRecord(state, {
|
|
1252
1059
|
domain: rootDomain,
|
|
1253
1060
|
name: record.subdomain,
|
|
1254
1061
|
type: record.type,
|
|
@@ -1281,10 +1088,10 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
|
|
|
1281
1088
|
*/
|
|
1282
1089
|
async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
1283
1090
|
const results = [];
|
|
1284
|
-
logger$
|
|
1091
|
+
logger$2.log("\n🔍 Verifying DNS records...");
|
|
1285
1092
|
for (const [appName, hostname] of appHostnames) {
|
|
1286
|
-
if (isDnsVerified(state, hostname, serverIp)) {
|
|
1287
|
-
logger$
|
|
1093
|
+
if (require_sniffer.isDnsVerified(state, hostname, serverIp)) {
|
|
1094
|
+
logger$2.log(` ✓ ${hostname} (previously verified)`);
|
|
1288
1095
|
results.push({
|
|
1289
1096
|
hostname,
|
|
1290
1097
|
appName,
|
|
@@ -1297,8 +1104,8 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
1297
1104
|
try {
|
|
1298
1105
|
const resolvedIp = await resolveHostnameToIp(hostname);
|
|
1299
1106
|
if (resolvedIp === serverIp) {
|
|
1300
|
-
setDnsVerification(state, hostname, serverIp);
|
|
1301
|
-
logger$
|
|
1107
|
+
require_sniffer.setDnsVerification(state, hostname, serverIp);
|
|
1108
|
+
logger$2.log(` ✓ ${hostname} → ${resolvedIp}`);
|
|
1302
1109
|
results.push({
|
|
1303
1110
|
hostname,
|
|
1304
1111
|
appName,
|
|
@@ -1307,7 +1114,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
1307
1114
|
expectedIp: serverIp
|
|
1308
1115
|
});
|
|
1309
1116
|
} else {
|
|
1310
|
-
logger$
|
|
1117
|
+
logger$2.log(` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`);
|
|
1311
1118
|
results.push({
|
|
1312
1119
|
hostname,
|
|
1313
1120
|
appName,
|
|
@@ -1318,7 +1125,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
1318
1125
|
}
|
|
1319
1126
|
} catch (error) {
|
|
1320
1127
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1321
|
-
logger$
|
|
1128
|
+
logger$2.log(` ⚠ ${hostname} DNS not propagated (${message})`);
|
|
1322
1129
|
results.push({
|
|
1323
1130
|
hostname,
|
|
1324
1131
|
appName,
|
|
@@ -1332,14 +1139,14 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
1332
1139
|
const skipped = results.filter((r) => r.skipped).length;
|
|
1333
1140
|
const pending = results.filter((r) => !r.verified).length;
|
|
1334
1141
|
if (pending > 0) {
|
|
1335
|
-
logger$
|
|
1336
|
-
logger$
|
|
1337
|
-
} else if (skipped > 0) logger$
|
|
1142
|
+
logger$2.log(`\n ${verified} verified, ${pending} pending propagation`);
|
|
1143
|
+
logger$2.log(" DNS changes may take 5-30 minutes to propagate");
|
|
1144
|
+
} else if (skipped > 0) logger$2.log(` ${verified} verified (${skipped} from cache)`);
|
|
1338
1145
|
return results;
|
|
1339
1146
|
}
|
|
1340
1147
|
//#endregion
|
|
1341
1148
|
//#region src/deploy/docker.ts
|
|
1342
|
-
const logger$
|
|
1149
|
+
const logger$1 = require_discover.output;
|
|
1343
1150
|
/**
|
|
1344
1151
|
* The id the Dockerfile templates mount the encrypted credentials under:
|
|
1345
1152
|
* `RUN --mount=type=secret,id=gkm_credentials`.
|
|
@@ -1402,8 +1209,8 @@ async function writeCredentialsFile(credentials) {
|
|
|
1402
1209
|
* @param credentials - Encrypted credentials, passed as a build secret
|
|
1403
1210
|
*/
|
|
1404
1211
|
async function buildImage(imageRef, buildArgs, credentials, { cwd, signal, stdio }) {
|
|
1405
|
-
logger$
|
|
1406
|
-
logger$
|
|
1212
|
+
logger$1.log(`\n🔨 Building Docker image: ${imageRef}`);
|
|
1213
|
+
logger$1.log(" Generating Dockerfile for the pre-built bundle...");
|
|
1407
1214
|
await require_sniffer.dockerCommand({
|
|
1408
1215
|
slim: true,
|
|
1409
1216
|
cwd
|
|
@@ -1425,7 +1232,7 @@ async function buildImage(imageRef, buildArgs, credentials, { cwd, signal, stdio
|
|
|
1425
1232
|
...signal ? { signal } : {},
|
|
1426
1233
|
...stdio ? { stdio } : {}
|
|
1427
1234
|
});
|
|
1428
|
-
logger$
|
|
1235
|
+
logger$1.log(`✅ Image built: ${imageRef}`);
|
|
1429
1236
|
} catch (error) {
|
|
1430
1237
|
throw new require_sniffer.DockerBuildFailed(imageRef, error);
|
|
1431
1238
|
} finally {
|
|
@@ -1436,14 +1243,14 @@ async function buildImage(imageRef, buildArgs, credentials, { cwd, signal, stdio
|
|
|
1436
1243
|
* Push Docker image to registry
|
|
1437
1244
|
*/
|
|
1438
1245
|
async function pushImage(imageRef, { cwd, signal, stdio }) {
|
|
1439
|
-
logger$
|
|
1246
|
+
logger$1.log(`\n☁️ Pushing image: ${imageRef}`);
|
|
1440
1247
|
try {
|
|
1441
1248
|
await require_sniffer.run("docker", ["push", imageRef], {
|
|
1442
1249
|
cwd,
|
|
1443
1250
|
...signal ? { signal } : {},
|
|
1444
1251
|
...stdio ? { stdio } : {}
|
|
1445
1252
|
});
|
|
1446
|
-
logger$
|
|
1253
|
+
logger$1.log(`✅ Image pushed: ${imageRef}`);
|
|
1447
1254
|
} catch (error) {
|
|
1448
1255
|
throw new require_sniffer.DockerPushFailed(imageRef, error);
|
|
1449
1256
|
}
|
|
@@ -1493,17 +1300,17 @@ async function deployDocker(options) {
|
|
|
1493
1300
|
await buildImage(imageRef, buildArgs, credentials, docker);
|
|
1494
1301
|
let digest;
|
|
1495
1302
|
if (!skipPush) {
|
|
1496
|
-
if (!config.registry) logger$
|
|
1303
|
+
if (!config.registry) logger$1.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
1497
1304
|
else {
|
|
1498
1305
|
await pushImage(imageRef, docker);
|
|
1499
1306
|
digest = await pushedDigest(imageRef, docker);
|
|
1500
1307
|
}
|
|
1501
1308
|
}
|
|
1502
|
-
logger$
|
|
1503
|
-
logger$
|
|
1504
|
-
logger$
|
|
1505
|
-
logger$
|
|
1506
|
-
if (masterKey) logger$
|
|
1309
|
+
logger$1.log("\n✅ Docker deployment ready!");
|
|
1310
|
+
logger$1.log(`\n📋 Deployment details:`);
|
|
1311
|
+
logger$1.log(` Image: ${imageRef}`);
|
|
1312
|
+
logger$1.log(` Stage: ${stage}`);
|
|
1313
|
+
if (masterKey) logger$1.log(`\n🔐 Run the container with GKM_MASTER_KEY set to the key with fingerprint ${require_sniffer.keyFingerprint(masterKey)}.`);
|
|
1507
1314
|
return {
|
|
1508
1315
|
imageRef,
|
|
1509
1316
|
...digest ? { digest } : {},
|
|
@@ -1531,10 +1338,10 @@ function generateSecret() {
|
|
|
1531
1338
|
* Otherwise generates a new one and stores it.
|
|
1532
1339
|
*/
|
|
1533
1340
|
function getOrGenerateSecret(state, appName, secretName) {
|
|
1534
|
-
const existing = getGeneratedSecret(state, appName, secretName);
|
|
1341
|
+
const existing = require_sniffer.getGeneratedSecret(state, appName, secretName);
|
|
1535
1342
|
if (existing) return existing;
|
|
1536
1343
|
const generated = generateSecret();
|
|
1537
|
-
setGeneratedSecret(state, appName, secretName, generated);
|
|
1344
|
+
require_sniffer.setGeneratedSecret(state, appName, secretName, generated);
|
|
1538
1345
|
return generated;
|
|
1539
1346
|
}
|
|
1540
1347
|
/**
|
|
@@ -1597,57 +1404,6 @@ function validateEnvVars(requiredVars, context) {
|
|
|
1597
1404
|
};
|
|
1598
1405
|
}
|
|
1599
1406
|
//#endregion
|
|
1600
|
-
//#region src/deploy/generated.ts
|
|
1601
|
-
/**
|
|
1602
|
-
* The values a deployed stage generates once and keeps: what nothing can
|
|
1603
|
-
* provision and nobody else issues.
|
|
1604
|
-
*
|
|
1605
|
-
* - **A seed**, which salts every password a deploy derives — a database's
|
|
1606
|
-
* master and roles, a bucket's root user. Derived from repo facts alone
|
|
1607
|
-
* (project, stage, construct id), those passwords were computable by anyone
|
|
1608
|
-
* who could read the repo.
|
|
1609
|
-
* - **Each `secret` construct's value** — an auth server's signing secret —
|
|
1610
|
-
* random, rather than derived.
|
|
1611
|
-
* - **Each `encryption` construct's keyring** — one random key and an index
|
|
1612
|
-
* key. Later keys are added by `gkm encryption:rotate`, never by a deploy.
|
|
1613
|
-
*
|
|
1614
|
-
* Both are generated on the first deploy that needs them and written to the
|
|
1615
|
-
* stage's store, so every later deploy reads the same values: sessions survive
|
|
1616
|
-
* a redeploy, and a role's password does not move. A value set by hand with
|
|
1617
|
-
* `gkm secrets:set` is kept, never replaced.
|
|
1618
|
-
*/
|
|
1619
|
-
/**
|
|
1620
|
-
* The stage's secrets with its seed and every declared `secret`'s value
|
|
1621
|
-
* present, generating what is missing. Pure: the caller writes the result
|
|
1622
|
-
* back to the stage's store when `generated` is not empty.
|
|
1623
|
-
*/
|
|
1624
|
-
function withGeneratedSecrets(secrets, manifest) {
|
|
1625
|
-
const generated = [];
|
|
1626
|
-
const custom = { ...secrets.custom };
|
|
1627
|
-
for (const declaration of Object.values(manifest)) {
|
|
1628
|
-
if (declaration?.kind !== "secret" && declaration?.kind !== "encryption") continue;
|
|
1629
|
-
const key = declaration.provides?.[0];
|
|
1630
|
-
if (!key || custom[key]) continue;
|
|
1631
|
-
custom[key] = declaration.kind === "encryption" ? (0, _geekmidas_constructs_encryption.generateKeyring)() : random();
|
|
1632
|
-
generated.push(key);
|
|
1633
|
-
}
|
|
1634
|
-
const seed = secrets.seed ?? random();
|
|
1635
|
-
if (!secrets.seed) generated.push("seed");
|
|
1636
|
-
return {
|
|
1637
|
-
secrets: generated.length ? {
|
|
1638
|
-
...secrets,
|
|
1639
|
-
seed,
|
|
1640
|
-
custom,
|
|
1641
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1642
|
-
} : secrets,
|
|
1643
|
-
generated
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
|
-
/** 256 bits, URL-safe. */
|
|
1647
|
-
function random() {
|
|
1648
|
-
return (0, node_crypto.randomBytes)(32).toString("base64url");
|
|
1649
|
-
}
|
|
1650
|
-
//#endregion
|
|
1651
1407
|
//#region src/deploy/journal.ts
|
|
1652
1408
|
var DeployJournal = class DeployJournal {
|
|
1653
1409
|
store;
|
|
@@ -2065,83 +1821,6 @@ function generateSecretsReport(encryptedApps, sniffedApps) {
|
|
|
2065
1821
|
};
|
|
2066
1822
|
}
|
|
2067
1823
|
//#endregion
|
|
2068
|
-
//#region src/deploy/terminal.ts
|
|
2069
|
-
/**
|
|
2070
|
-
* The credentials `gkm deploy` can ask a person for. CLI layer only.
|
|
2071
|
-
*
|
|
2072
|
-
* `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`
|
|
2073
|
-
* when the answer is nothing. At a terminal, `gkm deploy` would rather ask:
|
|
2074
|
-
* this provider reads the environment and the stored login first, and only
|
|
2075
|
-
* when both are empty prompts — and stores a Dokploy login it validated, so
|
|
2076
|
-
* the next deploy does not ask again. With no terminal it answers nothing, and
|
|
2077
|
-
* the deploy says what was missing instead of waiting on a prompt nobody sees.
|
|
2078
|
-
*/
|
|
2079
|
-
const logger$1 = require_discover.output;
|
|
2080
|
-
/** What answers a deploy prompt without a terminal. */
|
|
2081
|
-
const INSTEAD = "Please configure manually.";
|
|
2082
|
-
/** The Dokploy URL typed at the prompt is not a URL. */
|
|
2083
|
-
var DokployEndpointInvalid = class extends Error {
|
|
2084
|
-
endpoint;
|
|
2085
|
-
constructor(endpoint) {
|
|
2086
|
-
super("Invalid URL format");
|
|
2087
|
-
this.endpoint = endpoint;
|
|
2088
|
-
this.name = "DokployEndpointInvalid";
|
|
2089
|
-
}
|
|
2090
|
-
};
|
|
2091
|
-
/** Dokploy refused the token typed at the prompt; nothing was stored. */
|
|
2092
|
-
var DokployTokenRejected = class extends Error {
|
|
2093
|
-
endpoint;
|
|
2094
|
-
constructor(endpoint) {
|
|
2095
|
-
super("Invalid credentials. Please check your token.");
|
|
2096
|
-
this.endpoint = endpoint;
|
|
2097
|
-
this.name = "DokployTokenRejected";
|
|
2098
|
-
}
|
|
2099
|
-
};
|
|
2100
|
-
/** Asks the person at the terminal for what nothing else supplied. */
|
|
2101
|
-
function asking() {
|
|
2102
|
-
return { async get(asked) {
|
|
2103
|
-
if (!require_sniffer.canPrompt()) return void 0;
|
|
2104
|
-
const request = asked;
|
|
2105
|
-
if (request.kind === "dokploy") {
|
|
2106
|
-
logger$1.log("\n📋 Dokploy credentials not found. Let's set them up.");
|
|
2107
|
-
const endpoint = (await require_sniffer.prompt("Dokploy URL (e.g., https://dokploy.example.com): ", { instead: INSTEAD })).replace(/\/$/, "");
|
|
2108
|
-
try {
|
|
2109
|
-
new URL(endpoint);
|
|
2110
|
-
} catch {
|
|
2111
|
-
throw new DokployEndpointInvalid(endpoint);
|
|
2112
|
-
}
|
|
2113
|
-
logger$1.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
|
|
2114
|
-
const token = await require_sniffer.prompt("API Token: ", {
|
|
2115
|
-
hidden: true,
|
|
2116
|
-
instead: INSTEAD
|
|
2117
|
-
});
|
|
2118
|
-
logger$1.log("\nValidating credentials...");
|
|
2119
|
-
if (!await require_sniffer.validateDokployToken(endpoint, token)) throw new DokployTokenRejected(endpoint);
|
|
2120
|
-
await require_sniffer.storeDokployCredentials(token, endpoint);
|
|
2121
|
-
logger$1.log("✓ Credentials saved");
|
|
2122
|
-
return {
|
|
2123
|
-
endpoint,
|
|
2124
|
-
token
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
logger$1.log(` Dokploy has no registry for ${request.url}. Let's create one.`);
|
|
2128
|
-
return {
|
|
2129
|
-
username: await require_sniffer.prompt("Registry username: ", { instead: INSTEAD }),
|
|
2130
|
-
password: await require_sniffer.prompt("Registry password/token: ", {
|
|
2131
|
-
hidden: true,
|
|
2132
|
-
instead: INSTEAD
|
|
2133
|
-
})
|
|
2134
|
-
};
|
|
2135
|
-
} };
|
|
2136
|
-
}
|
|
2137
|
-
/**
|
|
2138
|
-
* The environment, then the stored login, then — at a terminal — the person
|
|
2139
|
-
* running the command.
|
|
2140
|
-
*/
|
|
2141
|
-
function terminalCredentials(base = storedCredentials()) {
|
|
2142
|
-
return chainCredentials(base, asking());
|
|
2143
|
-
}
|
|
2144
|
-
//#endregion
|
|
2145
1824
|
//#region src/deploy/index.ts
|
|
2146
1825
|
/**
|
|
2147
1826
|
* Deploy Module
|
|
@@ -2342,22 +2021,6 @@ function getServerHostname(endpoint) {
|
|
|
2342
2021
|
return new URL(endpoint).hostname;
|
|
2343
2022
|
}
|
|
2344
2023
|
/**
|
|
2345
|
-
* Generate image tag from stage and timestamp
|
|
2346
|
-
*/
|
|
2347
|
-
function generateTag(stage) {
|
|
2348
|
-
return `${stage}-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}`;
|
|
2349
|
-
}
|
|
2350
|
-
/**
|
|
2351
|
-
* The things this deploy creates a container for.
|
|
2352
|
-
*
|
|
2353
|
-
* A deploy unit is a *declaration*, not a config entry — and since that is now
|
|
2354
|
-
* true of every app, not just the ones being deployed, this is the workspace's
|
|
2355
|
-
* own derivation under the name the deploy phases call it by.
|
|
2356
|
-
*/
|
|
2357
|
-
function deployUnits(manifest, workspace) {
|
|
2358
|
-
return require_config.derivedApps(manifest, workspace);
|
|
2359
|
-
}
|
|
2360
|
-
/**
|
|
2361
2024
|
* The variables an app cannot start without.
|
|
2362
2025
|
*
|
|
2363
2026
|
* The sniffer reports every key it saw in `requiredEnvVars` and says which of
|
|
@@ -2375,26 +2038,6 @@ function requiredOf(requirements) {
|
|
|
2375
2038
|
const optional = new Set(requirements.optionalEnvVars);
|
|
2376
2039
|
return requirements.requiredEnvVars.map((name) => name.endsWith("?") ? name.slice(0, -1) : name).filter((name) => !optional.has(name));
|
|
2377
2040
|
}
|
|
2378
|
-
/** Apps were asked for by name that the workspace does not have. */
|
|
2379
|
-
var UnknownDeployApps = class extends Error {
|
|
2380
|
-
apps;
|
|
2381
|
-
available;
|
|
2382
|
-
constructor(apps, available) {
|
|
2383
|
-
super(`Unknown apps: ${apps.join(", ")}\nAvailable apps: ${available.join(", ")}`);
|
|
2384
|
-
this.apps = apps;
|
|
2385
|
-
this.available = available;
|
|
2386
|
-
this.name = "UnknownDeployApps";
|
|
2387
|
-
}
|
|
2388
|
-
};
|
|
2389
|
-
/** Every app asked for deploys somewhere other than Dokploy. */
|
|
2390
|
-
var NoDeployableApps = class extends Error {
|
|
2391
|
-
stage;
|
|
2392
|
-
constructor(stage) {
|
|
2393
|
-
super("No apps to deploy. All selected apps have unsupported deploy targets.");
|
|
2394
|
-
this.stage = stage;
|
|
2395
|
-
this.name = "NoDeployableApps";
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2398
2041
|
/** A backend failed, so the run stopped before deploying anything after it. */
|
|
2399
2042
|
var BackendDeployFailed = class extends Error {
|
|
2400
2043
|
app;
|
|
@@ -2417,23 +2060,24 @@ var MissingEnvVars = class extends Error {
|
|
|
2417
2060
|
this.name = "MissingEnvVars";
|
|
2418
2061
|
}
|
|
2419
2062
|
};
|
|
2420
|
-
/** Workspace deploys go to Dokploy; another provider was asked for. */
|
|
2421
|
-
var DeployProviderUnsupported = class extends Error {
|
|
2422
|
-
provider;
|
|
2423
|
-
constructor(provider) {
|
|
2424
|
-
super(`Workspace deployment only supports Dokploy. Got: ${provider}`);
|
|
2425
|
-
this.provider = provider;
|
|
2426
|
-
this.name = "DeployProviderUnsupported";
|
|
2427
|
-
}
|
|
2428
|
-
};
|
|
2429
2063
|
/** How `resolveProject` found a project, in the journal's words. */
|
|
2430
2064
|
const PROJECT_VIA = {
|
|
2431
2065
|
state: "recorded",
|
|
2432
2066
|
marker: "found",
|
|
2433
2067
|
created: "created"
|
|
2434
2068
|
};
|
|
2069
|
+
/** `childOutput` as `spawn` reads it, where it is not the default. */
|
|
2070
|
+
const CHILD_STDIO = {
|
|
2071
|
+
stderr: [
|
|
2072
|
+
"ignore",
|
|
2073
|
+
2,
|
|
2074
|
+
2
|
|
2075
|
+
],
|
|
2076
|
+
ignore: "ignore"
|
|
2077
|
+
};
|
|
2435
2078
|
/**
|
|
2436
|
-
* Deploy every app in a workspace to Dokploy
|
|
2079
|
+
* Deploy every app in a workspace to Dokploy: the engine under the built-in
|
|
2080
|
+
* `dokploy` target.
|
|
2437
2081
|
*
|
|
2438
2082
|
* Two-phase orchestration:
|
|
2439
2083
|
* - PHASE 1: Deploy backend apps (with encrypted secrets)
|
|
@@ -2444,119 +2088,60 @@ const PROJECT_VIA = {
|
|
|
2444
2088
|
* - Only GKM_MASTER_KEY is injected as Dokploy env var
|
|
2445
2089
|
* - Frontend apps get public URLs baked in at build time (no secrets)
|
|
2446
2090
|
*
|
|
2447
|
-
* The
|
|
2448
|
-
*
|
|
2449
|
-
* `ctx.emit` and to `output`, and every failure is thrown.
|
|
2091
|
+
* The run around it — the target, the apps, the lock, the phase events — is
|
|
2092
|
+
* `runDeploy`'s. This says what it is doing and checks what Dokploy needs.
|
|
2450
2093
|
*
|
|
2451
2094
|
* @internal
|
|
2452
2095
|
*/
|
|
2453
|
-
async function
|
|
2454
|
-
const { stage,
|
|
2455
|
-
ctx
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
cwd: configured.root
|
|
2463
|
-
});
|
|
2464
|
-
const units = deployUnits(manifest, configured);
|
|
2465
|
-
const workspace = Object.keys(units).length > 0 ? {
|
|
2466
|
-
...configured,
|
|
2467
|
-
apps: units
|
|
2468
|
-
} : configured;
|
|
2469
|
-
const identity = require_store.deployIdentity(workspace, stage);
|
|
2096
|
+
async function validateDokploy(phase) {
|
|
2097
|
+
const { workspace, stage, identity, tag: imageTag } = phase;
|
|
2098
|
+
const ctx = {
|
|
2099
|
+
emit: phase.emit,
|
|
2100
|
+
credentials: phase.credentials,
|
|
2101
|
+
signal: phase.signal,
|
|
2102
|
+
dryRun: phase.dryRun,
|
|
2103
|
+
...phase.childOutput !== "inherit" ? { stdio: CHILD_STDIO[phase.childOutput] } : {}
|
|
2104
|
+
};
|
|
2470
2105
|
logger.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
|
|
2471
2106
|
logger.log(` Stage: ${stage}`);
|
|
2472
|
-
const imageTag = tag ?? generateTag(stage);
|
|
2473
2107
|
logger.log(` Tag: ${imageTag}`);
|
|
2474
|
-
const
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
logger.log(` Deploying apps: ${appsToDeployNames.join(", ")}`);
|
|
2481
|
-
} else logger.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
|
|
2482
|
-
const skipped = [];
|
|
2483
|
-
const skip = (app, reason) => {
|
|
2484
|
-
skipped.push({
|
|
2485
|
-
app,
|
|
2486
|
-
reason
|
|
2487
|
-
});
|
|
2488
|
-
ctx.emit({
|
|
2489
|
-
type: "app.skipped",
|
|
2490
|
-
app,
|
|
2491
|
-
reason
|
|
2492
|
-
});
|
|
2493
|
-
};
|
|
2494
|
-
const dokployApps = appsToDeployNames.filter((name) => {
|
|
2495
|
-
const target = workspace.apps[name].resolvedDeployTarget;
|
|
2496
|
-
if (target === "sst") {
|
|
2497
|
-
const reason = `it deploys with SST — run \`gkm build && sst deploy --stage ${stage}\``;
|
|
2498
|
-
logger.log(` ⚠️ Skipping ${name}: ${reason}`);
|
|
2499
|
-
skip(name, reason);
|
|
2500
|
-
return false;
|
|
2501
|
-
}
|
|
2502
|
-
if (!require_workspace.isDeployTargetSupported(target)) {
|
|
2503
|
-
const reason = require_workspace.getDeployTargetError(target, name);
|
|
2504
|
-
logger.log(` ⚠️ Skipping ${name}: ${reason}`);
|
|
2505
|
-
skip(name, reason);
|
|
2506
|
-
return false;
|
|
2507
|
-
}
|
|
2508
|
-
return true;
|
|
2509
|
-
});
|
|
2510
|
-
if (dokployApps.length === 0) throw new NoDeployableApps(stage);
|
|
2511
|
-
appsToDeployNames = dokployApps;
|
|
2512
|
-
for (const name of appsToDeployNames) if (workspace.apps[name].type === "mobile") skip(name, MOBILE_SKIP_REASON);
|
|
2513
|
-
ctx.emit({
|
|
2514
|
-
type: "deploy.started",
|
|
2515
|
-
stage,
|
|
2516
|
-
identity: identity.key,
|
|
2517
|
-
tag: imageTag,
|
|
2518
|
-
apps: appsToDeployNames.filter((name) => workspace.apps[name].type !== "mobile"),
|
|
2519
|
-
dryRun: ctx.dryRun
|
|
2520
|
-
});
|
|
2521
|
-
const store = await require_sniffer.createStateStore({
|
|
2522
|
-
config: workspace.state,
|
|
2523
|
-
workspaceRoot: workspace.root,
|
|
2524
|
-
workspaceName: workspace.name
|
|
2525
|
-
});
|
|
2108
|
+
const askedFor = /* @__PURE__ */ new Set([...phase.apps, ...phase.skipped.map((skipped) => skipped.app)]);
|
|
2109
|
+
const asked = require_workspace.getAppBuildOrder(workspace).filter((name) => askedFor.has(name));
|
|
2110
|
+
logger.log(asked.length === Object.keys(workspace.apps).length ? ` Deploying all apps: ${asked.join(", ")}` : ` Deploying apps: ${asked.join(", ")}`);
|
|
2111
|
+
for (const { app, reason } of phase.skipped) logger.log(` ⚠️ Skipping ${app}: ${reason}`);
|
|
2112
|
+
const appsToDeployNames = [...phase.apps];
|
|
2113
|
+
for (const name of appsToDeployNames) if (workspace.apps[name].type === "mobile") phase.skip(name, MOBILE_SKIP_REASON);
|
|
2526
2114
|
const run = {
|
|
2527
2115
|
workspace,
|
|
2528
|
-
manifest,
|
|
2116
|
+
manifest: phase.manifest,
|
|
2529
2117
|
stage,
|
|
2530
2118
|
imageTag,
|
|
2531
2119
|
appsToDeployNames,
|
|
2532
2120
|
identity,
|
|
2533
|
-
store,
|
|
2534
|
-
skipped,
|
|
2121
|
+
store: phase.state,
|
|
2122
|
+
skipped: phase.skipped,
|
|
2123
|
+
secrets: phase.secrets,
|
|
2535
2124
|
ctx
|
|
2536
2125
|
};
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
} finally {
|
|
2542
|
-
await lock.release();
|
|
2543
|
-
}
|
|
2126
|
+
return {
|
|
2127
|
+
...run,
|
|
2128
|
+
preflight: await preflight(run)
|
|
2129
|
+
};
|
|
2544
2130
|
}
|
|
2545
2131
|
/**
|
|
2546
2132
|
* The stage's secrets and every app's environment requirements. A dry run
|
|
2547
2133
|
* reads the secrets and generates nothing into the store.
|
|
2548
2134
|
*/
|
|
2549
|
-
async function preflight({ workspace, manifest, stage, ctx }) {
|
|
2135
|
+
async function preflight({ workspace, manifest, stage, secrets: secretsStore, ctx }) {
|
|
2550
2136
|
logger.log("\n🔐 Loading secrets and analyzing environment requirements...");
|
|
2551
|
-
const
|
|
2552
|
-
const stored = await secretsStore.read(stage);
|
|
2137
|
+
const stored = await secretsStore.read();
|
|
2553
2138
|
if (!stored) logger.log(` ⚠️ No secrets found for stage "${stage}"; starting them`);
|
|
2554
|
-
const { secrets: stageSecrets, generated } = withGeneratedSecrets(stored ?? require_storage.initStageSecrets(stage), manifest);
|
|
2139
|
+
const { secrets: stageSecrets, generated } = require_sniffer.withGeneratedSecrets(stored ?? require_storage.initStageSecrets(stage), manifest);
|
|
2555
2140
|
if (generated.length > 0) {
|
|
2556
|
-
if (ctx.dryRun) logger.log(` 🔑 Would generate for "${stage}" (${secretsStore.
|
|
2141
|
+
if (ctx.dryRun) logger.log(` 🔑 Would generate for "${stage}" (${secretsStore.store}): ${generated.join(", ")}`);
|
|
2557
2142
|
else {
|
|
2558
|
-
await secretsStore.write(
|
|
2559
|
-
logger.log(` 🔑 Generated for "${stage}" (${secretsStore.
|
|
2143
|
+
await secretsStore.write(stageSecrets);
|
|
2144
|
+
logger.log(` 🔑 Generated for "${stage}" (${secretsStore.store}): ${generated.join(", ")}`);
|
|
2560
2145
|
}
|
|
2561
2146
|
}
|
|
2562
2147
|
const sniffedApps = await require_sniffer.sniffAllApps(workspace.apps, workspace.root);
|
|
@@ -2572,14 +2157,14 @@ async function preflight({ workspace, manifest, stage, ctx }) {
|
|
|
2572
2157
|
encryptedSecrets
|
|
2573
2158
|
};
|
|
2574
2159
|
}
|
|
2575
|
-
/**
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
}
|
|
2160
|
+
/**
|
|
2161
|
+
* The project, environment and registry, then what the manifest declares.
|
|
2162
|
+
* Each is looked up — by the id the stage's state recorded, else by name —
|
|
2163
|
+
* before it is created, so a run stopped part way is finished by the next.
|
|
2164
|
+
*/
|
|
2165
|
+
async function provisionDokploy(run) {
|
|
2166
|
+
const { workspace, manifest, stage, appsToDeployNames, identity, store, ctx } = run;
|
|
2167
|
+
const { stageSecrets } = run.preflight;
|
|
2583
2168
|
const changes = [];
|
|
2584
2169
|
const applied = (change, via) => {
|
|
2585
2170
|
changes.push(change);
|
|
@@ -2589,14 +2174,9 @@ async function deployLocked(run) {
|
|
|
2589
2174
|
via
|
|
2590
2175
|
});
|
|
2591
2176
|
};
|
|
2592
|
-
ctx.signal?.throwIfAborted();
|
|
2593
|
-
ctx.emit({
|
|
2594
|
-
type: "phase.started",
|
|
2595
|
-
phase: "provision"
|
|
2596
|
-
});
|
|
2597
2177
|
const { api, endpoint } = await dokployApi(workspace, ctx);
|
|
2598
2178
|
logger.log("\n📋 Loading deploy state...");
|
|
2599
|
-
const journal = await DeployJournal.open(store, stage, () => createEmptyState(stage, "", ""));
|
|
2179
|
+
const journal = await DeployJournal.open(store, stage, () => require_sniffer.createEmptyState(stage, "", ""));
|
|
2600
2180
|
const state = journal.state;
|
|
2601
2181
|
if (journal.existed) logger.log(` Found existing state for stage "${stage}"`);
|
|
2602
2182
|
else logger.log(` Creating new state for stage "${stage}"`);
|
|
@@ -2665,7 +2245,7 @@ async function deployLocked(run) {
|
|
|
2665
2245
|
kind: "registry",
|
|
2666
2246
|
url
|
|
2667
2247
|
}, { signal: ctx.signal });
|
|
2668
|
-
if (!login) throw new MissingCredential("registry", url);
|
|
2248
|
+
if (!login) throw new require_terminal.MissingCredential("registry", url);
|
|
2669
2249
|
const created = await api.createRegistry("Default Registry", url, login.username, login.password);
|
|
2670
2250
|
registryCreated = true;
|
|
2671
2251
|
logger.log(` ✓ Registry created: ${created.registryId}`);
|
|
@@ -2728,15 +2308,15 @@ async function deployLocked(run) {
|
|
|
2728
2308
|
projectName: identity.scope,
|
|
2729
2309
|
stage,
|
|
2730
2310
|
config: workspace.deploy.backups,
|
|
2731
|
-
existingState: getBackupState(state),
|
|
2311
|
+
existingState: require_sniffer.getBackupState(state),
|
|
2732
2312
|
logger
|
|
2733
2313
|
});
|
|
2734
|
-
setBackupState(state, backupState);
|
|
2314
|
+
require_sniffer.setBackupState(state, backupState);
|
|
2735
2315
|
if (!backupState.postgresBackupId) {
|
|
2736
2316
|
const backupSchedule = workspace.deploy.backups.schedule ?? "0 2 * * *";
|
|
2737
2317
|
const backupRetention = workspace.deploy.backups.retention ?? 30;
|
|
2738
2318
|
logger.log(" Creating postgres backup schedule...");
|
|
2739
|
-
|
|
2319
|
+
const backup = await api.createPostgresBackup({
|
|
2740
2320
|
schedule: backupSchedule,
|
|
2741
2321
|
prefix: `${stage}/postgres`,
|
|
2742
2322
|
destinationId: backupState.destinationId,
|
|
@@ -2744,14 +2324,39 @@ async function deployLocked(run) {
|
|
|
2744
2324
|
postgresId: backupCluster.postgresId,
|
|
2745
2325
|
enabled: true,
|
|
2746
2326
|
keepLatestCount: backupRetention
|
|
2747
|
-
})
|
|
2327
|
+
});
|
|
2328
|
+
require_sniffer.setPostgresBackupId(state, backup.backupId);
|
|
2748
2329
|
logger.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
|
|
2749
2330
|
} else logger.log(" ✓ Using existing postgres backup schedule");
|
|
2750
2331
|
}
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2332
|
+
run.provisioned = {
|
|
2333
|
+
api,
|
|
2334
|
+
endpoint,
|
|
2335
|
+
journal,
|
|
2336
|
+
state,
|
|
2337
|
+
projectId: project.projectId,
|
|
2338
|
+
environmentId,
|
|
2339
|
+
registry,
|
|
2340
|
+
registryId,
|
|
2341
|
+
declaredEnv,
|
|
2342
|
+
changes,
|
|
2343
|
+
applied
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Each app's image built and pushed, its application configured and
|
|
2348
|
+
* deployed, and its domain attached: backends first, each failure stopping
|
|
2349
|
+
* the run; then sites, whose build args are the backends' public URLs.
|
|
2350
|
+
*
|
|
2351
|
+
* Building is here rather than in a `build` phase of its own because an
|
|
2352
|
+
* image is built beside its application — a site's build args are resolved
|
|
2353
|
+
* per app, against the backends released before it.
|
|
2354
|
+
*/
|
|
2355
|
+
async function releaseDokploy(run) {
|
|
2356
|
+
const { workspace, stage, imageTag, appsToDeployNames, identity, ctx } = run;
|
|
2357
|
+
const { stageSecrets, sniffedApps, encryptedSecrets } = run.preflight;
|
|
2358
|
+
const { api, journal, state, environmentId, registry, registryId, declaredEnv, changes, applied } = run.provisioned;
|
|
2359
|
+
const project = { projectId: run.provisioned.projectId };
|
|
2755
2360
|
const backendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "backend");
|
|
2756
2361
|
const frontendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "web");
|
|
2757
2362
|
const publicUrls = {};
|
|
@@ -2787,12 +2392,12 @@ async function deployLocked(run) {
|
|
|
2787
2392
|
ctx.emit({
|
|
2788
2393
|
type: "app.failed",
|
|
2789
2394
|
app: appName,
|
|
2790
|
-
error: eventError(error)
|
|
2395
|
+
error: require_terminal.eventError(error)
|
|
2791
2396
|
});
|
|
2792
2397
|
};
|
|
2793
2398
|
/** An image is pushed: worth keeping even if what follows fails. */
|
|
2794
2399
|
const built = async (appName, image, ref) => {
|
|
2795
|
-
setDeployedImage(state, appName, {
|
|
2400
|
+
require_sniffer.setDeployedImage(state, appName, {
|
|
2796
2401
|
ref: image.imageRef ?? ref,
|
|
2797
2402
|
...image.digest ? { digest: image.digest } : {}
|
|
2798
2403
|
});
|
|
@@ -2812,11 +2417,6 @@ async function deployLocked(run) {
|
|
|
2812
2417
|
};
|
|
2813
2418
|
const frontendUrls = [];
|
|
2814
2419
|
for (const appName of frontendApps) frontendUrls.push(`https://${hostOf(workspace, appName, stage)}`);
|
|
2815
|
-
ctx.signal?.throwIfAborted();
|
|
2816
|
-
ctx.emit({
|
|
2817
|
-
type: "phase.started",
|
|
2818
|
-
phase: "release"
|
|
2819
|
-
});
|
|
2820
2420
|
if (backendApps.length > 0) {
|
|
2821
2421
|
logger.log("\n📦 PHASE 1: Deploying backend applications...");
|
|
2822
2422
|
for (const appName of backendApps) {
|
|
@@ -2982,15 +2582,17 @@ async function deployLocked(run) {
|
|
|
2982
2582
|
logger.log("\n📋 Saving deploy state...");
|
|
2983
2583
|
await journal.save();
|
|
2984
2584
|
logger.log(" ✓ State saved");
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
}
|
|
2585
|
+
run.released = {
|
|
2586
|
+
results,
|
|
2587
|
+
publicUrls,
|
|
2588
|
+
appHostnames
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
/** DNS records for each released app, and Dokploy's validation of each domain. */
|
|
2592
|
+
async function verifyDokploy(run) {
|
|
2593
|
+
const { workspace } = run;
|
|
2594
|
+
const { api, endpoint, journal, state } = run.provisioned;
|
|
2595
|
+
const { appHostnames } = run.released;
|
|
2994
2596
|
const dnsConfig = workspace.deploy.dns;
|
|
2995
2597
|
if (dnsConfig && appHostnames.size > 0) {
|
|
2996
2598
|
const dnsResult = await orchestrateDns(appHostnames, dnsConfig, endpoint);
|
|
@@ -3010,10 +2612,14 @@ async function deployLocked(run) {
|
|
|
3010
2612
|
}
|
|
3011
2613
|
}
|
|
3012
2614
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
2615
|
+
}
|
|
2616
|
+
/** What the run did, printed as the summary it always ended with. */
|
|
2617
|
+
function dokployResult(run) {
|
|
2618
|
+
if (run.result) return run.result;
|
|
2619
|
+
const { stage, identity, imageTag } = run;
|
|
2620
|
+
const { projectId, environmentId, changes } = run.provisioned;
|
|
2621
|
+
const { results, publicUrls } = run.released;
|
|
2622
|
+
const project = { projectId };
|
|
3017
2623
|
const successCount = results.filter((r) => r.success).length;
|
|
3018
2624
|
const failedCount = results.filter((r) => !r.success).length;
|
|
3019
2625
|
logger.log(`\n${"─".repeat(50)}`);
|
|
@@ -3035,7 +2641,7 @@ async function deployLocked(run) {
|
|
|
3035
2641
|
identity: identity.key,
|
|
3036
2642
|
tag: imageTag,
|
|
3037
2643
|
dryRun: false,
|
|
3038
|
-
skipped: run.skipped,
|
|
2644
|
+
skipped: [...run.skipped],
|
|
3039
2645
|
urls: publicUrls,
|
|
3040
2646
|
changes
|
|
3041
2647
|
};
|
|
@@ -3071,7 +2677,7 @@ async function ensureApplication(api, journal, appName, dokployAppName, projectI
|
|
|
3071
2677
|
action: via === "created" ? "create" : "reuse",
|
|
3072
2678
|
id: resource.applicationId
|
|
3073
2679
|
}, via);
|
|
3074
|
-
setApplicationId(journal.state, appName, resource.applicationId);
|
|
2680
|
+
require_sniffer.setApplicationId(journal.state, appName, resource.applicationId);
|
|
3075
2681
|
return resource;
|
|
3076
2682
|
}
|
|
3077
2683
|
/**
|
|
@@ -3136,7 +2742,7 @@ async function dokployApi(workspace, ctx) {
|
|
|
3136
2742
|
kind: "dokploy",
|
|
3137
2743
|
...configured ? { endpoint: configured } : {}
|
|
3138
2744
|
}, { signal: ctx.signal });
|
|
3139
|
-
if (!creds) throw new MissingCredential("dokploy", configured);
|
|
2745
|
+
if (!creds) throw new require_terminal.MissingCredential("dokploy", configured);
|
|
3140
2746
|
return {
|
|
3141
2747
|
api: new require_sniffer.DokployApi({
|
|
3142
2748
|
baseUrl: creds.endpoint,
|
|
@@ -3156,19 +2762,9 @@ function hostOf(workspace, appName, stage) {
|
|
|
3156
2762
|
* else: no lock, no state written, no secret generated, no Dokploy resource
|
|
3157
2763
|
* created or changed, no image built or pushed.
|
|
3158
2764
|
*/
|
|
3159
|
-
async function
|
|
2765
|
+
async function planDokploy(run) {
|
|
3160
2766
|
const { workspace, manifest, stage, imageTag, appsToDeployNames, identity } = run;
|
|
3161
2767
|
const { ctx } = run;
|
|
3162
|
-
await preflight(run);
|
|
3163
|
-
ctx.emit({
|
|
3164
|
-
type: "phase.finished",
|
|
3165
|
-
phase: "validate"
|
|
3166
|
-
});
|
|
3167
|
-
ctx.signal?.throwIfAborted();
|
|
3168
|
-
ctx.emit({
|
|
3169
|
-
type: "phase.started",
|
|
3170
|
-
phase: "plan"
|
|
3171
|
-
});
|
|
3172
2768
|
logger.log("\n🔎 Dry run: nothing will be created, changed, built or pushed.");
|
|
3173
2769
|
const changes = [];
|
|
3174
2770
|
const plan = (change, detail) => {
|
|
@@ -3283,11 +2879,7 @@ async function planDeploy(run) {
|
|
|
3283
2879
|
}
|
|
3284
2880
|
const creates = changes.filter((c) => c.action !== "reuse").length;
|
|
3285
2881
|
logger.log(`\n✅ Dry run complete: ${creates} to create or build, ${changes.length - creates} to reuse.`);
|
|
3286
|
-
|
|
3287
|
-
type: "phase.finished",
|
|
3288
|
-
phase: "plan"
|
|
3289
|
-
});
|
|
3290
|
-
return {
|
|
2882
|
+
run.result = {
|
|
3291
2883
|
apps,
|
|
3292
2884
|
projectId: project?.projectId ?? "",
|
|
3293
2885
|
environmentId: environment?.environmentId ?? "",
|
|
@@ -3297,7 +2889,7 @@ async function planDeploy(run) {
|
|
|
3297
2889
|
identity: identity.key,
|
|
3298
2890
|
tag: imageTag,
|
|
3299
2891
|
dryRun: true,
|
|
3300
|
-
skipped: run.skipped,
|
|
2892
|
+
skipped: [...run.skipped],
|
|
3301
2893
|
urls,
|
|
3302
2894
|
changes
|
|
3303
2895
|
};
|
|
@@ -3305,161 +2897,18 @@ async function planDeploy(run) {
|
|
|
3305
2897
|
/** Why a mobile app is left out of a Dokploy deploy. */
|
|
3306
2898
|
const MOBILE_SKIP_REASON = "deploys via its framework toolchain";
|
|
3307
2899
|
//#endregion
|
|
3308
|
-
//#region src/deploy/deploy.ts
|
|
3309
|
-
/**
|
|
3310
|
-
* `deploy()`: a deploy as a library call.
|
|
3311
|
-
*
|
|
3312
|
-
* `gkm deploy` could only be driven from an interactive terminal: it prompted
|
|
3313
|
-
* for what it lacked and exited the process on failure, worked out of
|
|
3314
|
-
* `process.cwd()`, and reported progress by printing. Here the caller says
|
|
3315
|
-
* where the project is, hands over the credentials and a signal, and gets back
|
|
3316
|
-
* a run — events to iterate while it goes, and a structured result at the end.
|
|
3317
|
-
* Nothing below this asks a person anything or ends the process; `gkm deploy`
|
|
3318
|
-
* is a thin wrapper that does both.
|
|
3319
|
-
*/
|
|
3320
|
-
/**
|
|
3321
|
-
* Every event a run emits, kept so that each iteration — however late it
|
|
3322
|
-
* starts — sees the run from its first event to its last.
|
|
3323
|
-
*/
|
|
3324
|
-
var EventLog = class {
|
|
3325
|
-
events = [];
|
|
3326
|
-
closed = false;
|
|
3327
|
-
waiting = [];
|
|
3328
|
-
push(event) {
|
|
3329
|
-
this.events.push(event);
|
|
3330
|
-
this.wake();
|
|
3331
|
-
}
|
|
3332
|
-
close() {
|
|
3333
|
-
this.closed = true;
|
|
3334
|
-
this.wake();
|
|
3335
|
-
}
|
|
3336
|
-
wake() {
|
|
3337
|
-
const waiting = this.waiting;
|
|
3338
|
-
this.waiting = [];
|
|
3339
|
-
for (const resume of waiting) resume();
|
|
3340
|
-
}
|
|
3341
|
-
async *iterate() {
|
|
3342
|
-
let next = 0;
|
|
3343
|
-
while (true) if (next < this.events.length) yield this.events[next++];
|
|
3344
|
-
else if (this.closed) return;
|
|
3345
|
-
else await new Promise((resume) => this.waiting.push(resume));
|
|
3346
|
-
}
|
|
3347
|
-
};
|
|
3348
|
-
/** `childOutput` as `spawn` reads it, where it is not the default. */
|
|
3349
|
-
const CHILD_STDIO = {
|
|
3350
|
-
stderr: [
|
|
3351
|
-
"ignore",
|
|
3352
|
-
2,
|
|
3353
|
-
2
|
|
3354
|
-
],
|
|
3355
|
-
ignore: "ignore"
|
|
3356
|
-
};
|
|
3357
|
-
function forward(logger, level, message) {
|
|
3358
|
-
if (!logger) return;
|
|
3359
|
-
if (level === "info") logger.info(message);
|
|
3360
|
-
else if (level === "error" && logger.error) logger.error(message);
|
|
3361
|
-
else logger.warn(message);
|
|
3362
|
-
}
|
|
3363
|
-
/**
|
|
3364
|
-
* Deploy a project's stage.
|
|
3365
|
-
*
|
|
3366
|
-
* ```ts
|
|
3367
|
-
* import { deploy } from '@geekmidas/cli/deploy';
|
|
3368
|
-
*
|
|
3369
|
-
* const run = deploy({ cwd: '/srv/checkouts/shop', stage: 'production' });
|
|
3370
|
-
* for await (const event of run) {
|
|
3371
|
-
* if (event.type === 'app.deployed') console.log(event.app, event.url);
|
|
3372
|
-
* }
|
|
3373
|
-
* const result = await run.result;
|
|
3374
|
-
* ```
|
|
3375
|
-
*
|
|
3376
|
-
* The run starts at once; iterating it is optional, and an iteration started
|
|
3377
|
-
* late still sees every event from the first.
|
|
3378
|
-
*/
|
|
3379
|
-
function deploy(input) {
|
|
3380
|
-
const log = new EventLog();
|
|
3381
|
-
const emit = (event) => {
|
|
3382
|
-
log.push(event);
|
|
3383
|
-
if (event.type === "log") forward(input.logger, event.level, event.message);
|
|
3384
|
-
};
|
|
3385
|
-
const ctx = {
|
|
3386
|
-
emit,
|
|
3387
|
-
credentials: input.credentials ?? storedCredentials({ home: input.home ?? require_home.gkmHome() }),
|
|
3388
|
-
dryRun: input.dryRun ?? false,
|
|
3389
|
-
home: input.home ?? require_home.gkmHome(),
|
|
3390
|
-
...input.signal ? { signal: input.signal } : {},
|
|
3391
|
-
...input.childOutput && input.childOutput !== "inherit" ? { stdio: CHILD_STDIO[input.childOutput] } : {}
|
|
3392
|
-
};
|
|
3393
|
-
const result = require_discover.withOutput((level, message) => emit({
|
|
3394
|
-
type: "log",
|
|
3395
|
-
level,
|
|
3396
|
-
message
|
|
3397
|
-
}), async () => {
|
|
3398
|
-
try {
|
|
3399
|
-
input.signal?.throwIfAborted();
|
|
3400
|
-
const deployed = await runDeploy(() => load(input), {
|
|
3401
|
-
stage: input.stage,
|
|
3402
|
-
...input.tag ? { tag: input.tag } : {},
|
|
3403
|
-
...input.apps ? { apps: input.apps } : {}
|
|
3404
|
-
}, ctx);
|
|
3405
|
-
emit({
|
|
3406
|
-
type: "deploy.finished",
|
|
3407
|
-
result: deployed
|
|
3408
|
-
});
|
|
3409
|
-
return deployed;
|
|
3410
|
-
} catch (error) {
|
|
3411
|
-
emit({
|
|
3412
|
-
type: "deploy.failed",
|
|
3413
|
-
error: eventError(error)
|
|
3414
|
-
});
|
|
3415
|
-
throw error;
|
|
3416
|
-
} finally {
|
|
3417
|
-
log.close();
|
|
3418
|
-
}
|
|
3419
|
-
});
|
|
3420
|
-
result.catch(() => {});
|
|
3421
|
-
return {
|
|
3422
|
-
result,
|
|
3423
|
-
[Symbol.asyncIterator]: () => log.iterate()
|
|
3424
|
-
};
|
|
3425
|
-
}
|
|
3426
|
-
/** The workspace at `input.cwd`, refusing a stage it does not deploy. */
|
|
3427
|
-
async function load(input) {
|
|
3428
|
-
const { workspace } = await require_config.loadWorkspaceConfig((0, node_path.resolve)(input.cwd));
|
|
3429
|
-
require_workspace.assertDeployedStage(workspace.stages, input.stage);
|
|
3430
|
-
return workspace;
|
|
3431
|
-
}
|
|
3432
|
-
//#endregion
|
|
3433
2900
|
Object.defineProperty(exports, "BackendDeployFailed", {
|
|
3434
2901
|
enumerable: true,
|
|
3435
2902
|
get: function() {
|
|
3436
2903
|
return BackendDeployFailed;
|
|
3437
2904
|
}
|
|
3438
2905
|
});
|
|
3439
|
-
Object.defineProperty(exports, "DeployProviderUnsupported", {
|
|
3440
|
-
enumerable: true,
|
|
3441
|
-
get: function() {
|
|
3442
|
-
return DeployProviderUnsupported;
|
|
3443
|
-
}
|
|
3444
|
-
});
|
|
3445
|
-
Object.defineProperty(exports, "MissingCredential", {
|
|
3446
|
-
enumerable: true,
|
|
3447
|
-
get: function() {
|
|
3448
|
-
return MissingCredential;
|
|
3449
|
-
}
|
|
3450
|
-
});
|
|
3451
2906
|
Object.defineProperty(exports, "MissingEnvVars", {
|
|
3452
2907
|
enumerable: true,
|
|
3453
2908
|
get: function() {
|
|
3454
2909
|
return MissingEnvVars;
|
|
3455
2910
|
}
|
|
3456
2911
|
});
|
|
3457
|
-
Object.defineProperty(exports, "NoDeployableApps", {
|
|
3458
|
-
enumerable: true,
|
|
3459
|
-
get: function() {
|
|
3460
|
-
return NoDeployableApps;
|
|
3461
|
-
}
|
|
3462
|
-
});
|
|
3463
2912
|
Object.defineProperty(exports, "ProjectNotOwned", {
|
|
3464
2913
|
enumerable: true,
|
|
3465
2914
|
get: function() {
|
|
@@ -3484,35 +2933,41 @@ Object.defineProperty(exports, "RegistryNotFound", {
|
|
|
3484
2933
|
return RegistryNotFound;
|
|
3485
2934
|
}
|
|
3486
2935
|
});
|
|
3487
|
-
Object.defineProperty(exports, "
|
|
2936
|
+
Object.defineProperty(exports, "dokployResult", {
|
|
2937
|
+
enumerable: true,
|
|
2938
|
+
get: function() {
|
|
2939
|
+
return dokployResult;
|
|
2940
|
+
}
|
|
2941
|
+
});
|
|
2942
|
+
Object.defineProperty(exports, "planDokploy", {
|
|
3488
2943
|
enumerable: true,
|
|
3489
2944
|
get: function() {
|
|
3490
|
-
return
|
|
2945
|
+
return planDokploy;
|
|
3491
2946
|
}
|
|
3492
2947
|
});
|
|
3493
|
-
Object.defineProperty(exports, "
|
|
2948
|
+
Object.defineProperty(exports, "provisionDokploy", {
|
|
3494
2949
|
enumerable: true,
|
|
3495
2950
|
get: function() {
|
|
3496
|
-
return
|
|
2951
|
+
return provisionDokploy;
|
|
3497
2952
|
}
|
|
3498
2953
|
});
|
|
3499
|
-
Object.defineProperty(exports, "
|
|
2954
|
+
Object.defineProperty(exports, "releaseDokploy", {
|
|
3500
2955
|
enumerable: true,
|
|
3501
2956
|
get: function() {
|
|
3502
|
-
return
|
|
2957
|
+
return releaseDokploy;
|
|
3503
2958
|
}
|
|
3504
2959
|
});
|
|
3505
|
-
Object.defineProperty(exports, "
|
|
2960
|
+
Object.defineProperty(exports, "validateDokploy", {
|
|
3506
2961
|
enumerable: true,
|
|
3507
2962
|
get: function() {
|
|
3508
|
-
return
|
|
2963
|
+
return validateDokploy;
|
|
3509
2964
|
}
|
|
3510
2965
|
});
|
|
3511
|
-
Object.defineProperty(exports, "
|
|
2966
|
+
Object.defineProperty(exports, "verifyDokploy", {
|
|
3512
2967
|
enumerable: true,
|
|
3513
2968
|
get: function() {
|
|
3514
|
-
return
|
|
2969
|
+
return verifyDokploy;
|
|
3515
2970
|
}
|
|
3516
2971
|
});
|
|
3517
2972
|
|
|
3518
|
-
//# sourceMappingURL=deploy-
|
|
2973
|
+
//# sourceMappingURL=deploy-DSaCYvcO.cjs.map
|