@geekmidas/cli 10.0.0-alpha.110 → 10.0.0-alpha.112
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/{GoDaddyProvider-CjeOduia.mjs → GoDaddyProvider-CPabc3XW.mjs} +2 -2
- package/dist/{GoDaddyProvider-CjeOduia.mjs.map → GoDaddyProvider-CPabc3XW.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-qNUzTXI7.cjs → GoDaddyProvider-DWrDIXzQ.cjs} +2 -2
- package/dist/{GoDaddyProvider-qNUzTXI7.cjs.map → GoDaddyProvider-DWrDIXzQ.cjs.map} +1 -1
- package/dist/{LegacyStateStore-pgscYsiG.mjs → LegacyStateStore-BdXoAlYY.mjs} +3 -3
- package/dist/{LegacyStateStore-pgscYsiG.mjs.map → LegacyStateStore-BdXoAlYY.mjs.map} +1 -1
- package/dist/{LegacyStateStore-MxiSu7KJ.cjs → LegacyStateStore-fsyPGBWu.cjs} +3 -3
- package/dist/{LegacyStateStore-MxiSu7KJ.cjs.map → LegacyStateStore-fsyPGBWu.cjs.map} +1 -1
- package/dist/{LocalStateStore-Bm8Vmwjb.cjs → LocalStateStore-AtviUBky.cjs} +2 -2
- package/dist/{LocalStateStore-Bm8Vmwjb.cjs.map → LocalStateStore-AtviUBky.cjs.map} +1 -1
- package/dist/{LocalStateStore-BcvyKbAw.mjs → LocalStateStore-BrAX5yy7.mjs} +2 -2
- package/dist/{LocalStateStore-BcvyKbAw.mjs.map → LocalStateStore-BrAX5yy7.mjs.map} +1 -1
- package/dist/{S3StateStore-H2XsAzpJ.mjs → S3StateStore-Cx7njkBz.mjs} +2 -2
- package/dist/{S3StateStore-H2XsAzpJ.mjs.map → S3StateStore-Cx7njkBz.mjs.map} +1 -1
- package/dist/{S3StateStore-DXHSBVsV.cjs → S3StateStore-tn5g80Sx.cjs} +2 -2
- package/dist/{S3StateStore-DXHSBVsV.cjs.map → S3StateStore-tn5g80Sx.cjs.map} +1 -1
- package/dist/{SSMStateStore-BLPSNl-U.cjs → SSMStateStore-CAFde-Fq.cjs} +2 -2
- package/dist/{SSMStateStore-BLPSNl-U.cjs.map → SSMStateStore-CAFde-Fq.cjs.map} +1 -1
- package/dist/{SSMStateStore-CENciQEM.mjs → SSMStateStore-sqZ3oDmn.mjs} +2 -2
- package/dist/{SSMStateStore-CENciQEM.mjs.map → SSMStateStore-sqZ3oDmn.mjs.map} +1 -1
- package/dist/{add-Cdg1kEqN.cjs → add-Cs8yT5iH.cjs} +3 -3
- package/dist/{add-Cdg1kEqN.cjs.map → add-Cs8yT5iH.cjs.map} +1 -1
- package/dist/{add-snhdVmR-.mjs → add-DJE7zqJ4.mjs} +3 -3
- package/dist/{add-snhdVmR-.mjs.map → add-DJE7zqJ4.mjs.map} +1 -1
- package/dist/{bundler-Wd9H4QLJ.mjs → bundler-DrySVbXF.mjs} +5 -5
- package/dist/{bundler-Wd9H4QLJ.mjs.map → bundler-DrySVbXF.mjs.map} +1 -1
- package/dist/{bundler-B4GDxImV.cjs → bundler-NrtItVBp.cjs} +5 -5
- package/dist/{bundler-B4GDxImV.cjs.map → bundler-NrtItVBp.cjs.map} +1 -1
- package/dist/{cli-BZYz55tO.cjs → cli-CHzpFBh-.cjs} +2 -2
- package/dist/{cli-BZYz55tO.cjs.map → cli-CHzpFBh-.cjs.map} +1 -1
- package/dist/{cli-BzrDSNSg.mjs → cli-DDY3jfTt.mjs} +4 -4
- package/dist/{cli-BzrDSNSg.mjs.map → cli-DDY3jfTt.mjs.map} +1 -1
- package/dist/{compose-DN9zySvQ.cjs → compose-ByIbIrzs.cjs} +20 -5
- package/dist/compose-ByIbIrzs.cjs.map +1 -0
- package/dist/{compose-Gsd1Nq7R.mjs → compose-CDC2bCme.mjs} +20 -5
- package/dist/compose-CDC2bCme.mjs.map +1 -0
- package/dist/deploy/public.cjs +6 -6
- package/dist/deploy/public.mjs +6 -6
- package/dist/{deploy-H2S8_mTx.mjs → deploy-DLq9-m6H.mjs} +5 -5
- package/dist/{deploy-H2S8_mTx.mjs.map → deploy-DLq9-m6H.mjs.map} +1 -1
- package/dist/{deploy-CFvq8daZ.cjs → deploy-Dl_NavYE.cjs} +5 -5
- package/dist/{deploy-CFvq8daZ.cjs.map → deploy-Dl_NavYE.cjs.map} +1 -1
- package/dist/{dns-BdETzP10.cjs → dns-C2TGXUF4.cjs} +2 -2
- package/dist/{dns-BdETzP10.cjs.map → dns-C2TGXUF4.cjs.map} +1 -1
- package/dist/{dns-pFMShDqW.mjs → dns-pzftumxg.mjs} +2 -2
- package/dist/{dns-pFMShDqW.mjs.map → dns-pzftumxg.mjs.map} +1 -1
- package/dist/{dokploy-oDJbnvCb.mjs → dokploy-B7-OwY2s.mjs} +2 -2
- package/dist/{dokploy-oDJbnvCb.mjs.map → dokploy-B7-OwY2s.mjs.map} +1 -1
- package/dist/{dokploy-HioAjP7K.cjs → dokploy-OqBh8G4g.cjs} +2 -2
- package/dist/{dokploy-HioAjP7K.cjs.map → dokploy-OqBh8G4g.cjs.map} +1 -1
- package/dist/eas-Cne743nQ.cjs +337 -0
- package/dist/eas-Cne743nQ.cjs.map +1 -0
- package/dist/eas-DEpwISrQ.mjs +334 -0
- package/dist/eas-DEpwISrQ.mjs.map +1 -0
- package/dist/{encryption-D5pDJyp2.mjs → encryption-DJv_jEXi.mjs} +2 -2
- package/dist/{encryption-D5pDJyp2.mjs.map → encryption-DJv_jEXi.mjs.map} +1 -1
- package/dist/{encryption-DVCxD-gV.cjs → encryption-DoVnVU82.cjs} +2 -2
- package/dist/{encryption-DVCxD-gV.cjs.map → encryption-DoVnVU82.cjs.map} +1 -1
- package/dist/{engine-BaJGPzte.cjs → engine-CJhQb50j.cjs} +3 -3
- package/dist/{engine-BaJGPzte.cjs.map → engine-CJhQb50j.cjs.map} +1 -1
- package/dist/{engine-CfAKgr2_.mjs → engine-Db71-OZL.mjs} +3 -3
- package/dist/{engine-CfAKgr2_.mjs.map → engine-Db71-OZL.mjs.map} +1 -1
- package/dist/{github-E-ZxqFPq.mjs → github-D2eABlxM.mjs} +4 -4
- package/dist/{github-E-ZxqFPq.mjs.map → github-D2eABlxM.mjs.map} +1 -1
- package/dist/{github-D902Vwr9.cjs → github-DDOHjdS9.cjs} +4 -4
- package/dist/{github-D902Vwr9.cjs.map → github-DDOHjdS9.cjs.map} +1 -1
- package/dist/googlePlay-B6uiHi65.cjs +931 -0
- package/dist/googlePlay-B6uiHi65.cjs.map +1 -0
- package/dist/googlePlay-BYZ-J5sC.mjs +931 -0
- package/dist/googlePlay-BYZ-J5sC.mjs.map +1 -0
- package/dist/index.cjs +268 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +268 -97
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-BZ0GnSZQ.mjs → migrate-CQUGbptv.mjs} +2 -2
- package/dist/{migrate-BZ0GnSZQ.mjs.map → migrate-CQUGbptv.mjs.map} +1 -1
- package/dist/{migrate-Df81ew0k.cjs → migrate-GLBZlrlJ.cjs} +2 -2
- package/dist/{migrate-Df81ew0k.cjs.map → migrate-GLBZlrlJ.cjs.map} +1 -1
- package/dist/{migrate-CTqhWtev.cjs → migrate-bDQzFvBN.cjs} +2 -2
- package/dist/{migrate-CTqhWtev.cjs.map → migrate-bDQzFvBN.cjs.map} +1 -1
- package/dist/{migrate-DCXwHcWX.mjs → migrate-xHYaeBkd.mjs} +3 -3
- package/dist/{migrate-DCXwHcWX.mjs.map → migrate-xHYaeBkd.mjs.map} +1 -1
- package/dist/publish-CSctCNf4.mjs +123 -0
- package/dist/publish-CSctCNf4.mjs.map +1 -0
- package/dist/publish-SDG1WfYn.cjs +124 -0
- package/dist/publish-SDG1WfYn.cjs.map +1 -0
- package/dist/{resolve-BMpPd8x3.mjs → resolve-CxDqDmCD.mjs} +3 -3
- package/dist/{resolve-BMpPd8x3.mjs.map → resolve-CxDqDmCD.mjs.map} +1 -1
- package/dist/{resolve-D043YmW9.cjs → resolve-wG49Woex.cjs} +3 -3
- package/dist/{resolve-D043YmW9.cjs.map → resolve-wG49Woex.cjs.map} +1 -1
- package/dist/{rollback-CDs_XNb0.mjs → rollback-BQgEdr_p.mjs} +4 -4
- package/dist/{rollback-CDs_XNb0.mjs.map → rollback-BQgEdr_p.mjs.map} +1 -1
- package/dist/{rollback-CBiYsQ3T.cjs → rollback-Cw-Z0mWn.cjs} +4 -4
- package/dist/{rollback-CBiYsQ3T.cjs.map → rollback-Cw-Z0mWn.cjs.map} +1 -1
- package/dist/{seeds-BjcO4DDa.mjs → seeds-CIk0l4SC.mjs} +13 -10
- package/dist/seeds-CIk0l4SC.mjs.map +1 -0
- package/dist/{seeds-DNqRA1aQ.cjs → seeds-Dk8YkLkM.cjs} +18 -15
- package/dist/seeds-Dk8YkLkM.cjs.map +1 -0
- package/dist/{sniffer-DipSS8r8.cjs → sniffer-2aK7cB2u.cjs} +6 -6
- package/dist/{sniffer-DipSS8r8.cjs.map → sniffer-2aK7cB2u.cjs.map} +1 -1
- package/dist/{sniffer-b0XPmSyT.mjs → sniffer-CABMO-Bh.mjs} +6 -6
- package/dist/{sniffer-b0XPmSyT.mjs.map → sniffer-CABMO-Bh.mjs.map} +1 -1
- package/dist/{stage-DrDmZusd.mjs → stage-ClKTHQoM.mjs} +69 -33
- package/dist/stage-ClKTHQoM.mjs.map +1 -0
- package/dist/{stage-C_y2UovO.cjs → stage-DrCS0vKD.cjs} +79 -32
- package/dist/stage-DrCS0vKD.cjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CvRLxz6g.mjs → terminal-CybB-cjk.mjs} +2 -2
- package/dist/{terminal-CvRLxz6g.mjs.map → terminal-CybB-cjk.mjs.map} +1 -1
- package/package.json +15 -15
- package/dist/compose-DN9zySvQ.cjs.map +0 -1
- package/dist/compose-Gsd1Nq7R.mjs.map +0 -1
- package/dist/seeds-BjcO4DDa.mjs.map +0 -1
- package/dist/seeds-DNqRA1aQ.cjs.map +0 -1
- package/dist/stage-C_y2UovO.cjs.map +0 -1
- package/dist/stage-DrDmZusd.mjs.map +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
1
2
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
|
-
import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-
|
|
3
|
+
import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-CDC2bCme.mjs";
|
|
3
4
|
import { N as assertDeployedStage } from "./workspace-D1QbNd55.mjs";
|
|
4
|
-
import { S as deploysWithCompose } from "./dns-
|
|
5
|
-
import { It as secretsStoreFor, dt as constructGlobs } from "./seeds-
|
|
5
|
+
import { S as deploysWithCompose } from "./dns-pzftumxg.mjs";
|
|
6
|
+
import { It as secretsStoreFor, dt as constructGlobs } from "./seeds-CIk0l4SC.mjs";
|
|
6
7
|
import { n as deployIdentity } from "./identity-CBXr4wNi.mjs";
|
|
7
8
|
import { i as discover } from "./discover-Ca8U75dl.mjs";
|
|
8
9
|
import { NotInAnApp, loadWorkspaceAppInfo, loadWorkspaceConfig } from "./config.mjs";
|
|
9
|
-
import {
|
|
10
|
+
import { u as prompt } from "./index.mjs";
|
|
10
11
|
import { constants } from "node:os";
|
|
11
12
|
import { spawn } from "node:child_process";
|
|
12
13
|
//#region src/exec/stage.ts
|
|
@@ -26,6 +27,19 @@ import { spawn } from "node:child_process";
|
|
|
26
27
|
* environment, and its Credentials are filled from there by a preload that
|
|
27
28
|
* is a `data:` URL naming the keys — never their values.
|
|
28
29
|
*/
|
|
30
|
+
var stage_exports = /* @__PURE__ */ __exportAll({
|
|
31
|
+
ExecAppNotInStack: () => ExecAppNotInStack,
|
|
32
|
+
ExecNotConfirmed: () => ExecNotConfirmed,
|
|
33
|
+
ExecStageNotComposed: () => ExecStageNotComposed,
|
|
34
|
+
ExecStageNotDeployed: () => ExecStageNotDeployed,
|
|
35
|
+
appEnvironment: () => appEnvironment,
|
|
36
|
+
credentialsImport: () => credentialsImport,
|
|
37
|
+
defaultStageExecDeps: () => defaultStageExecDeps,
|
|
38
|
+
execOnStage: () => execOnStage,
|
|
39
|
+
loadStage: () => loadStage,
|
|
40
|
+
stackAddresses: () => stackAddresses,
|
|
41
|
+
throughTunnels: () => throughTunnels
|
|
42
|
+
});
|
|
29
43
|
const logger = console;
|
|
30
44
|
/** The stage is not one the compose target deploys. */
|
|
31
45
|
var ExecStageNotComposed = class extends GkmError {
|
|
@@ -79,14 +93,17 @@ const defaultStageExecDeps = {
|
|
|
79
93
|
env: process.env
|
|
80
94
|
};
|
|
81
95
|
/**
|
|
82
|
-
* The environment an app
|
|
83
|
-
* keys of each worker built from it; a site's
|
|
96
|
+
* The environment an app runs with on the stage: a backend's container's,
|
|
97
|
+
* over the keys of each worker built from it; a site's or a mobile app's,
|
|
98
|
+
* the public values its build reads.
|
|
84
99
|
*
|
|
85
100
|
* @throws {ExecAppNotInStack}
|
|
86
101
|
*/
|
|
87
102
|
function appEnvironment(stack, appName) {
|
|
103
|
+
const mobile = stack.mobileApps.find((candidate) => candidate.name === appName);
|
|
104
|
+
if (mobile) return { ...mobile.values };
|
|
88
105
|
const app = stack.apps.find((candidate) => candidate.name === appName);
|
|
89
|
-
if (!app) throw new ExecAppNotInStack(stack.stage, appName, stack.apps.map((candidate) => candidate.name));
|
|
106
|
+
if (!app) throw new ExecAppNotInStack(stack.stage, appName, [...stack.apps.map((candidate) => candidate.name), ...stack.mobileApps.map((candidate) => candidate.name)]);
|
|
90
107
|
if (app.kind === "site") return { ...app.build?.args };
|
|
91
108
|
const workers = stack.workers.filter((worker) => worker.host === app.name);
|
|
92
109
|
return Object.assign({}, ...workers.map((worker) => worker.env ?? {}), app.env ?? {});
|
|
@@ -137,8 +154,14 @@ function credentialsImport(keys) {
|
|
|
137
154
|
const source = `globalThis.__gkm_credentials__=Object.fromEntries(${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;
|
|
138
155
|
return `--import=data:text/javascript,${encodeURIComponent(source)}`;
|
|
139
156
|
}
|
|
140
|
-
/**
|
|
141
|
-
|
|
157
|
+
/**
|
|
158
|
+
* The stage's stack, and the app this directory is, as the deploy has them:
|
|
159
|
+
* its secrets read from its store, its values derived as the deploy derives
|
|
160
|
+
* them. Nothing is reached but the store — no server, no SSH.
|
|
161
|
+
*
|
|
162
|
+
* @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}
|
|
163
|
+
*/
|
|
164
|
+
async function loadStage(options, env = process.env) {
|
|
142
165
|
const cwd = options.cwd ?? process.cwd();
|
|
143
166
|
const { stage } = options;
|
|
144
167
|
const info = await loadWorkspaceAppInfo(cwd).catch((error) => {
|
|
@@ -159,33 +182,38 @@ async function stageStack(options, env) {
|
|
|
159
182
|
const store = await secretsStoreFor(workspace, stage, { ...options.profile ? { profile: options.profile } : {} });
|
|
160
183
|
const { secrets, generated } = deployedStackSecrets(workspace, stage, await store.read(stage), manifest);
|
|
161
184
|
if (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);
|
|
162
|
-
|
|
185
|
+
return {
|
|
163
186
|
workspace,
|
|
164
187
|
manifest,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
stack: composeStack({
|
|
189
|
+
workspace,
|
|
190
|
+
manifest,
|
|
191
|
+
runnables,
|
|
192
|
+
background,
|
|
193
|
+
stage,
|
|
194
|
+
identity: deployIdentity(workspace, stage),
|
|
195
|
+
images: {
|
|
196
|
+
mode: "build",
|
|
197
|
+
tag: "exec"
|
|
198
|
+
},
|
|
199
|
+
secrets,
|
|
200
|
+
ports: edgePorts(env)
|
|
201
|
+
}),
|
|
173
202
|
secrets,
|
|
174
|
-
|
|
175
|
-
|
|
203
|
+
appName: info?.appName,
|
|
204
|
+
protectedStage: workspace.stages.protected?.includes(stage) ?? false
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */
|
|
208
|
+
function stageServer(loaded, options) {
|
|
176
209
|
const server = composeServer({
|
|
177
|
-
stage,
|
|
210
|
+
stage: options.stage,
|
|
178
211
|
local: false,
|
|
179
|
-
config: workspace.deploy?.compose?.server,
|
|
180
|
-
custom: secrets.custom
|
|
212
|
+
config: loaded.workspace.deploy?.compose?.server,
|
|
213
|
+
custom: loaded.secrets.custom
|
|
181
214
|
});
|
|
182
215
|
if (options.sshUser) server.user = options.sshUser;
|
|
183
|
-
return
|
|
184
|
-
stack,
|
|
185
|
-
appName: info?.appName,
|
|
186
|
-
server,
|
|
187
|
-
protectedStage: workspace.stages.protected?.includes(stage) ?? false
|
|
188
|
-
};
|
|
216
|
+
return server;
|
|
189
217
|
}
|
|
190
218
|
/**
|
|
191
219
|
* Run `commandArgs` as the app this directory is, on the stage — through a
|
|
@@ -197,18 +225,26 @@ async function stageStack(options, env) {
|
|
|
197
225
|
*/
|
|
198
226
|
async function execOnStage(commandArgs, options, deps = defaultStageExecDeps) {
|
|
199
227
|
const { stage } = options;
|
|
200
|
-
const
|
|
228
|
+
const loaded = await loadStage(options, deps.env);
|
|
229
|
+
const { stack, appName, protectedStage } = loaded;
|
|
201
230
|
const env = appEnvironment(stack, appName);
|
|
202
231
|
if (protectedStage && !options.yes) {
|
|
203
232
|
const question = `'${stage}' is a protected stage. Run ${commandArgs.join(" ")} against it?`;
|
|
204
233
|
if (!await deps.confirm(question)) throw new ExecNotConfirmed(stage);
|
|
205
234
|
}
|
|
235
|
+
const addresses = stackAddresses(env, stack);
|
|
236
|
+
if (addresses.length === 0) {
|
|
237
|
+
logger.log(`📦 App: ${appName} on '${stage}'`);
|
|
238
|
+
logger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);
|
|
239
|
+
return run(commandArgs, env, options.cwd);
|
|
240
|
+
}
|
|
241
|
+
const server = stageServer(loaded, options);
|
|
206
242
|
logger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);
|
|
207
243
|
await deps.server.check(server);
|
|
208
244
|
const tunnels = [];
|
|
209
245
|
const ports = /* @__PURE__ */ new Map();
|
|
210
246
|
try {
|
|
211
|
-
for (const address of
|
|
247
|
+
for (const address of addresses) {
|
|
212
248
|
const published = await deps.docker.published({ host: dockerHost(server) }, stack.project, address.service, address.port);
|
|
213
249
|
if (published === void 0) {
|
|
214
250
|
logger.log(`⚠️ ${address.service} is not published on the server (no running ${stack.project} ${address.service} publishes ${address.port}), so it is unreachable from here; its address is passed on unchanged.`);
|
|
@@ -261,6 +297,6 @@ function run(commandArgs, credentials, cwd) {
|
|
|
261
297
|
});
|
|
262
298
|
}
|
|
263
299
|
//#endregion
|
|
264
|
-
export {
|
|
300
|
+
export { stage_exports as n, loadStage as t };
|
|
265
301
|
|
|
266
|
-
//# sourceMappingURL=stage-
|
|
302
|
+
//# sourceMappingURL=stage-ClKTHQoM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-ClKTHQoM.mjs","names":[],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
1
2
|
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
2
|
-
const require_compose = require("./compose-
|
|
3
|
+
const require_compose = require("./compose-ByIbIrzs.cjs");
|
|
3
4
|
const require_workspace = require("./workspace-BwqQnool.cjs");
|
|
4
|
-
const require_dns = require("./dns-
|
|
5
|
-
const require_seeds = require("./seeds-
|
|
5
|
+
const require_dns = require("./dns-C2TGXUF4.cjs");
|
|
6
|
+
const require_seeds = require("./seeds-Dk8YkLkM.cjs");
|
|
6
7
|
const require_identity = require("./identity-CbLfgQNT.cjs");
|
|
7
8
|
const require_discover = require("./discover-Bsr7HjqJ.cjs");
|
|
8
9
|
const require_config = require("./config.cjs");
|
|
@@ -26,6 +27,19 @@ let node_child_process = require("node:child_process");
|
|
|
26
27
|
* environment, and its Credentials are filled from there by a preload that
|
|
27
28
|
* is a `data:` URL naming the keys — never their values.
|
|
28
29
|
*/
|
|
30
|
+
var stage_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
31
|
+
ExecAppNotInStack: () => ExecAppNotInStack,
|
|
32
|
+
ExecNotConfirmed: () => ExecNotConfirmed,
|
|
33
|
+
ExecStageNotComposed: () => ExecStageNotComposed,
|
|
34
|
+
ExecStageNotDeployed: () => ExecStageNotDeployed,
|
|
35
|
+
appEnvironment: () => appEnvironment,
|
|
36
|
+
credentialsImport: () => credentialsImport,
|
|
37
|
+
defaultStageExecDeps: () => defaultStageExecDeps,
|
|
38
|
+
execOnStage: () => execOnStage,
|
|
39
|
+
loadStage: () => loadStage,
|
|
40
|
+
stackAddresses: () => stackAddresses,
|
|
41
|
+
throughTunnels: () => throughTunnels
|
|
42
|
+
});
|
|
29
43
|
const logger = console;
|
|
30
44
|
/** The stage is not one the compose target deploys. */
|
|
31
45
|
var ExecStageNotComposed = class extends require_errors.GkmError {
|
|
@@ -79,14 +93,17 @@ const defaultStageExecDeps = {
|
|
|
79
93
|
env: process.env
|
|
80
94
|
};
|
|
81
95
|
/**
|
|
82
|
-
* The environment an app
|
|
83
|
-
* keys of each worker built from it; a site's
|
|
96
|
+
* The environment an app runs with on the stage: a backend's container's,
|
|
97
|
+
* over the keys of each worker built from it; a site's or a mobile app's,
|
|
98
|
+
* the public values its build reads.
|
|
84
99
|
*
|
|
85
100
|
* @throws {ExecAppNotInStack}
|
|
86
101
|
*/
|
|
87
102
|
function appEnvironment(stack, appName) {
|
|
103
|
+
const mobile = stack.mobileApps.find((candidate) => candidate.name === appName);
|
|
104
|
+
if (mobile) return { ...mobile.values };
|
|
88
105
|
const app = stack.apps.find((candidate) => candidate.name === appName);
|
|
89
|
-
if (!app) throw new ExecAppNotInStack(stack.stage, appName, stack.apps.map((candidate) => candidate.name));
|
|
106
|
+
if (!app) throw new ExecAppNotInStack(stack.stage, appName, [...stack.apps.map((candidate) => candidate.name), ...stack.mobileApps.map((candidate) => candidate.name)]);
|
|
90
107
|
if (app.kind === "site") return { ...app.build?.args };
|
|
91
108
|
const workers = stack.workers.filter((worker) => worker.host === app.name);
|
|
92
109
|
return Object.assign({}, ...workers.map((worker) => worker.env ?? {}), app.env ?? {});
|
|
@@ -137,8 +154,14 @@ function credentialsImport(keys) {
|
|
|
137
154
|
const source = `globalThis.__gkm_credentials__=Object.fromEntries(${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;
|
|
138
155
|
return `--import=data:text/javascript,${encodeURIComponent(source)}`;
|
|
139
156
|
}
|
|
140
|
-
/**
|
|
141
|
-
|
|
157
|
+
/**
|
|
158
|
+
* The stage's stack, and the app this directory is, as the deploy has them:
|
|
159
|
+
* its secrets read from its store, its values derived as the deploy derives
|
|
160
|
+
* them. Nothing is reached but the store — no server, no SSH.
|
|
161
|
+
*
|
|
162
|
+
* @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}
|
|
163
|
+
*/
|
|
164
|
+
async function loadStage(options, env = process.env) {
|
|
142
165
|
const cwd = options.cwd ?? process.cwd();
|
|
143
166
|
const { stage } = options;
|
|
144
167
|
const info = await require_config.loadWorkspaceAppInfo(cwd).catch((error) => {
|
|
@@ -159,33 +182,38 @@ async function stageStack(options, env) {
|
|
|
159
182
|
const store = await require_seeds.secretsStoreFor(workspace, stage, { ...options.profile ? { profile: options.profile } : {} });
|
|
160
183
|
const { secrets, generated } = require_compose.deployedStackSecrets(workspace, stage, await store.read(stage), manifest);
|
|
161
184
|
if (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);
|
|
162
|
-
|
|
185
|
+
return {
|
|
163
186
|
workspace,
|
|
164
187
|
manifest,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
188
|
+
stack: require_compose.composeStack({
|
|
189
|
+
workspace,
|
|
190
|
+
manifest,
|
|
191
|
+
runnables,
|
|
192
|
+
background,
|
|
193
|
+
stage,
|
|
194
|
+
identity: require_identity.deployIdentity(workspace, stage),
|
|
195
|
+
images: {
|
|
196
|
+
mode: "build",
|
|
197
|
+
tag: "exec"
|
|
198
|
+
},
|
|
199
|
+
secrets,
|
|
200
|
+
ports: require_compose.edgePorts(env)
|
|
201
|
+
}),
|
|
173
202
|
secrets,
|
|
174
|
-
|
|
175
|
-
|
|
203
|
+
appName: info?.appName,
|
|
204
|
+
protectedStage: workspace.stages.protected?.includes(stage) ?? false
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */
|
|
208
|
+
function stageServer(loaded, options) {
|
|
176
209
|
const server = require_compose.composeServer({
|
|
177
|
-
stage,
|
|
210
|
+
stage: options.stage,
|
|
178
211
|
local: false,
|
|
179
|
-
config: workspace.deploy?.compose?.server,
|
|
180
|
-
custom: secrets.custom
|
|
212
|
+
config: loaded.workspace.deploy?.compose?.server,
|
|
213
|
+
custom: loaded.secrets.custom
|
|
181
214
|
});
|
|
182
215
|
if (options.sshUser) server.user = options.sshUser;
|
|
183
|
-
return
|
|
184
|
-
stack,
|
|
185
|
-
appName: info?.appName,
|
|
186
|
-
server,
|
|
187
|
-
protectedStage: workspace.stages.protected?.includes(stage) ?? false
|
|
188
|
-
};
|
|
216
|
+
return server;
|
|
189
217
|
}
|
|
190
218
|
/**
|
|
191
219
|
* Run `commandArgs` as the app this directory is, on the stage — through a
|
|
@@ -197,18 +225,26 @@ async function stageStack(options, env) {
|
|
|
197
225
|
*/
|
|
198
226
|
async function execOnStage(commandArgs, options, deps = defaultStageExecDeps) {
|
|
199
227
|
const { stage } = options;
|
|
200
|
-
const
|
|
228
|
+
const loaded = await loadStage(options, deps.env);
|
|
229
|
+
const { stack, appName, protectedStage } = loaded;
|
|
201
230
|
const env = appEnvironment(stack, appName);
|
|
202
231
|
if (protectedStage && !options.yes) {
|
|
203
232
|
const question = `'${stage}' is a protected stage. Run ${commandArgs.join(" ")} against it?`;
|
|
204
233
|
if (!await deps.confirm(question)) throw new ExecNotConfirmed(stage);
|
|
205
234
|
}
|
|
235
|
+
const addresses = stackAddresses(env, stack);
|
|
236
|
+
if (addresses.length === 0) {
|
|
237
|
+
logger.log(`📦 App: ${appName} on '${stage}'`);
|
|
238
|
+
logger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);
|
|
239
|
+
return run(commandArgs, env, options.cwd);
|
|
240
|
+
}
|
|
241
|
+
const server = stageServer(loaded, options);
|
|
206
242
|
logger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);
|
|
207
243
|
await deps.server.check(server);
|
|
208
244
|
const tunnels = [];
|
|
209
245
|
const ports = /* @__PURE__ */ new Map();
|
|
210
246
|
try {
|
|
211
|
-
for (const address of
|
|
247
|
+
for (const address of addresses) {
|
|
212
248
|
const published = await deps.docker.published({ host: require_compose.dockerHost(server) }, stack.project, address.service, address.port);
|
|
213
249
|
if (published === void 0) {
|
|
214
250
|
logger.log(`⚠️ ${address.service} is not published on the server (no running ${stack.project} ${address.service} publishes ${address.port}), so it is unreachable from here; its address is passed on unchanged.`);
|
|
@@ -261,6 +297,17 @@ function run(commandArgs, credentials, cwd) {
|
|
|
261
297
|
});
|
|
262
298
|
}
|
|
263
299
|
//#endregion
|
|
264
|
-
exports
|
|
300
|
+
Object.defineProperty(exports, "loadStage", {
|
|
301
|
+
enumerable: true,
|
|
302
|
+
get: function() {
|
|
303
|
+
return loadStage;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
Object.defineProperty(exports, "stage_exports", {
|
|
307
|
+
enumerable: true,
|
|
308
|
+
get: function() {
|
|
309
|
+
return stage_exports;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
265
312
|
|
|
266
|
-
//# sourceMappingURL=stage-
|
|
313
|
+
//# sourceMappingURL=stage-DrCS0vKD.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-DrCS0vKD.cjs","names":["GkmError","dockerCompose","sshAccess","prompt","edgeAlias","loadWorkspaceAppInfo","NotInAnApp","loadWorkspaceConfig","deploysWithCompose","discover","constructGlobs","secretsStoreFor","deployedStackSecrets","composeStack","deployIdentity","edgePorts","composeServer","dockerHost","spawn","constants"],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
|
package/dist/target/public.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_define = require("../define-crmMcXFS.cjs");
|
|
3
|
-
const require_resolve = require("../resolve-
|
|
3
|
+
const require_resolve = require("../resolve-wG49Woex.cjs");
|
|
4
4
|
const require_provider = require("../provider-BqpjG9QM.cjs");
|
|
5
5
|
exports.DeployTargetNotYetSupported = require_define.DeployTargetNotYetSupported;
|
|
6
6
|
exports.InvalidTargetOptions = require_resolve.InvalidTargetOptions;
|
package/dist/target/public.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as UnknownDeployTarget, i as DeployTargetNotYetSupported, n as isDeployTarget, t as defineTarget } from "../define-6poNqm5O.mjs";
|
|
2
|
-
import { i as TargetRuntimeMismatch, l as TargetPackageNotFound, n as TargetEntryInvalid, r as TargetPackageInvalid, s as TargetRuntimeUndeclared, t as InvalidTargetOptions } from "../resolve-
|
|
2
|
+
import { i as TargetRuntimeMismatch, l as TargetPackageNotFound, n as TargetEntryInvalid, r as TargetPackageInvalid, s as TargetRuntimeUndeclared, t as InvalidTargetOptions } from "../resolve-CxDqDmCD.mjs";
|
|
3
3
|
import { t as ProviderRemoved } from "../provider-CEOKk8NS.mjs";
|
|
4
4
|
export { DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, TargetEntryInvalid, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget, defineTarget, isDeployTarget };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
2
|
import { f as storeDokployCredentials, n as chainCredentials, r as storedCredentials } from "./credentials-B5N5yKk6.mjs";
|
|
3
3
|
import { f as output } from "./discover-Ca8U75dl.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { c as validateDokployToken, l as canPrompt, u as prompt } from "./index.mjs";
|
|
5
5
|
//#region src/deploy/terminal.ts
|
|
6
6
|
/**
|
|
7
7
|
* The credentials `gkm deploy` can ask a person for. CLI layer only.
|
|
@@ -82,4 +82,4 @@ function terminalCredentials(base = storedCredentials()) {
|
|
|
82
82
|
//#endregion
|
|
83
83
|
export { terminalCredentials };
|
|
84
84
|
|
|
85
|
-
//# sourceMappingURL=terminal-
|
|
85
|
+
//# sourceMappingURL=terminal-CybB-cjk.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-
|
|
1
|
+
{"version":3,"file":"terminal-CybB-cjk.mjs","names":[],"sources":["../src/deploy/terminal.ts"],"sourcesContent":["/**\n * The credentials `gkm deploy` can ask a person for. CLI layer only.\n *\n * `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`\n * when the answer is nothing. At a terminal, `gkm deploy` would rather ask:\n * this provider reads the environment and the stored login first, and only\n * when both are empty prompts — and stores a Dokploy login it validated, so\n * the next deploy does not ask again. With no terminal it answers nothing, and\n * the deploy says what was missing instead of waiting on a prompt nobody sees.\n */\n\nimport { validateDokployToken } from '../auth';\nimport { storeDokployCredentials } from '../auth/credentials';\nimport { GkmError } from '../errors';\nimport { output } from '../output';\nimport { canPrompt, prompt } from '../prompt';\nimport {\n\ttype CredentialProvider,\n\ttype CredentialRequest,\n\tchainCredentials,\n\tstoredCredentials,\n} from './credentials';\n\nconst logger = output;\n\n/** What answers a deploy prompt without a terminal. */\nconst INSTEAD = 'Please configure manually.';\n\n/** The Dokploy URL typed at the prompt is not a URL. */\nexport class DokployEndpointInvalid extends GkmError {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid URL format');\n\t\tthis.name = 'DokployEndpointInvalid';\n\t}\n}\n\n/** Dokploy refused the token typed at the prompt; nothing was stored. */\nexport class DokployTokenRejected extends GkmError {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid credentials. Please check your token.');\n\t\tthis.name = 'DokployTokenRejected';\n\t}\n}\n\n/** Asks the person at the terminal for what nothing else supplied. */\nfunction asking(): CredentialProvider {\n\treturn {\n\t\tasync get(asked) {\n\t\t\tif (!canPrompt()) return undefined;\n\t\t\t// The union, so checking `kind` narrows it: `K` itself never is.\n\t\t\tconst request = asked as CredentialRequest;\n\n\t\t\tif (request.kind === 'dokploy') {\n\t\t\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\t\t\tconst endpoint = (\n\t\t\t\t\tawait prompt('Dokploy URL (e.g., https://dokploy.example.com): ', {\n\t\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t\t})\n\t\t\t\t).replace(/\\/$/, '');\n\n\t\t\t\ttry {\n\t\t\t\t\tnew URL(endpoint);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new DokployEndpointInvalid(endpoint);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\t\tconst token = await prompt('API Token: ', {\n\t\t\t\t\thidden: true,\n\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t});\n\n\t\t\t\tlogger.log('\\nValidating credentials...');\n\t\t\t\tif (!(await validateDokployToken(endpoint, token))) {\n\t\t\t\t\tthrow new DokployTokenRejected(endpoint);\n\t\t\t\t}\n\n\t\t\t\tawait storeDokployCredentials(token, endpoint);\n\t\t\t\tlogger.log('✓ Credentials saved');\n\t\t\t\treturn { endpoint, token } as never;\n\t\t\t}\n\n\t\t\t// AWS is a profile or a role, set up with the AWS CLI — not keys\n\t\t\t// typed into a deploy.\n\t\t\tif (request.kind !== 'registry') return undefined;\n\n\t\t\tlogger.log(\n\t\t\t\t` Dokploy has no registry for ${request.url}. Let's create one.`,\n\t\t\t);\n\t\t\tconst username = await prompt('Registry username: ', {\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\tconst password = await prompt('Registry password/token: ', {\n\t\t\t\thidden: true,\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\treturn { username, password } as never;\n\t\t},\n\t};\n}\n\n/**\n * The environment, then the stored login, then — at a terminal — the person\n * running the command.\n */\nexport function terminalCredentials(\n\tbase: CredentialProvider = storedCredentials(),\n): CredentialProvider {\n\treturn chainCredentials(base, asking());\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuBA,MAAM,SAAS;;AAGf,MAAM,UAAU;;AAGhB,IAAa,yBAAb,cAA4C,SAAS;CAC/B;CAArB,YAAY,UAA2B;EACtC,MAAM,oBAAoB;EADN,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0C,SAAS;CAC7B;CAArB,YAAY,UAA2B;EACtC,MAAM,+CAA+C;EADjC,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,SAA6B;CACrC,OAAO,EACN,MAAM,IAAI,OAAO;EAChB,IAAI,CAAC,UAAU,GAAG,OAAO,KAAA;EAEzB,MAAM,UAAU;EAEhB,IAAI,QAAQ,SAAS,WAAW;GAC/B,OAAO,IAAI,wDAAwD;GACnE,MAAM,YACL,MAAM,OAAO,qDAAqD,EACjE,SAAS,QACV,CAAC,EAAA,CACA,QAAQ,OAAO,EAAE;GAEnB,IAAI;IACH,IAAI,IAAI,QAAQ;GACjB,QAAQ;IACP,MAAM,IAAI,uBAAuB,QAAQ;GAC1C;GAEA,OAAO,IAAI,0BAA0B,SAAS,oBAAoB;GAClE,MAAM,QAAQ,MAAM,OAAO,eAAe;IACzC,QAAQ;IACR,SAAS;GACV,CAAC;GAED,OAAO,IAAI,6BAA6B;GACxC,IAAI,CAAE,MAAM,qBAAqB,UAAU,KAAK,GAC/C,MAAM,IAAI,qBAAqB,QAAQ;GAGxC,MAAM,wBAAwB,OAAO,QAAQ;GAC7C,OAAO,IAAI,qBAAqB;GAChC,OAAO;IAAE;IAAU;GAAM;EAC1B;EAIA,IAAI,QAAQ,SAAS,YAAY,OAAO,KAAA;EAExC,OAAO,IACN,kCAAkC,QAAQ,IAAI,oBAC/C;EAQA,OAAO;GAAE,UAAA,MAPc,OAAO,uBAAuB,EACpD,SAAS,QACV,CAAC;GAKkB,UAAA,MAJI,OAAO,6BAA6B;IAC1D,QAAQ;IACR,SAAS;GACV,CAAC;EAC2B;CAC7B,EACD;AACD;;;;;AAMA,SAAgB,oBACf,OAA2B,kBAAkB,GACxB;CACrB,OAAO,iBAAiB,MAAM,OAAO,CAAC;AACvC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cli",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.112",
|
|
4
4
|
"description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -120,16 +120,16 @@
|
|
|
120
120
|
"prompts": "~2.4.2",
|
|
121
121
|
"tsx": "~4.23.15",
|
|
122
122
|
"yaml": "~2.9.1",
|
|
123
|
-
"@geekmidas/cache": "10.0.0-alpha.
|
|
124
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
125
|
-
"@geekmidas/db": "10.0.0-alpha.
|
|
126
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
127
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
128
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
129
|
-
"@geekmidas/manifest": "10.0.0-alpha.
|
|
130
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
131
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
132
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
123
|
+
"@geekmidas/cache": "10.0.0-alpha.112",
|
|
124
|
+
"@geekmidas/constructs": "~10.0.0-alpha.112",
|
|
125
|
+
"@geekmidas/db": "10.0.0-alpha.112",
|
|
126
|
+
"@geekmidas/envkit": "~10.0.0-alpha.112",
|
|
127
|
+
"@geekmidas/errors": "~10.0.0-alpha.112",
|
|
128
|
+
"@geekmidas/logger": "~10.0.0-alpha.112",
|
|
129
|
+
"@geekmidas/manifest": "10.0.0-alpha.112",
|
|
130
|
+
"@geekmidas/schema": "~10.0.0-alpha.112",
|
|
131
|
+
"@geekmidas/services": "~10.0.0-alpha.112",
|
|
132
|
+
"@geekmidas/storage": "~10.0.0-alpha.112"
|
|
133
133
|
},
|
|
134
134
|
"devDependencies": {
|
|
135
135
|
"@hono/node-server": "~1.19.5",
|
|
@@ -144,12 +144,12 @@
|
|
|
144
144
|
"typescript": "~7.0.2",
|
|
145
145
|
"vitest": "~5.0.2",
|
|
146
146
|
"zod": "~4.6.5",
|
|
147
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
148
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
149
|
-
"@geekmidas/testkit": "10.0.0-alpha.
|
|
147
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.112",
|
|
148
|
+
"@geekmidas/events": "~10.0.0-alpha.112",
|
|
149
|
+
"@geekmidas/testkit": "10.0.0-alpha.112"
|
|
150
150
|
},
|
|
151
151
|
"peerDependencies": {
|
|
152
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
152
|
+
"@geekmidas/telescope": "~10.0.0-alpha.112"
|
|
153
153
|
},
|
|
154
154
|
"peerDependenciesMeta": {
|
|
155
155
|
"@geekmidas/telescope": {
|