@geekmidas/cli 10.0.0-alpha.56 → 10.0.0-alpha.58
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/{OpenApiTsGenerator-BLRQ7Iu9.cjs → OpenApiTsGenerator-BCWPRZwD.cjs} +153 -4
- package/dist/OpenApiTsGenerator-BCWPRZwD.cjs.map +1 -0
- package/dist/{OpenApiTsGenerator-NThANByQ.mjs → OpenApiTsGenerator-BRWD1Wit.mjs} +149 -6
- package/dist/OpenApiTsGenerator-BRWD1Wit.mjs.map +1 -0
- package/dist/{SSMStateStore-oda9xOGc.mjs → SSMStateStore-BjZZm-nV.mjs} +14 -7
- package/dist/SSMStateStore-BjZZm-nV.mjs.map +1 -0
- package/dist/{SSMStateStore-DD7jKrCO.cjs → SSMStateStore-DC43R7zG.cjs} +14 -7
- package/dist/SSMStateStore-DC43R7zG.cjs.map +1 -0
- package/dist/{backends-BcHL7Gy_.mjs → backends-CjuCiE8h.mjs} +2 -2
- package/dist/{backends-BcHL7Gy_.mjs.map → backends-CjuCiE8h.mjs.map} +1 -1
- package/dist/{backends-_gQdAzBK.cjs → backends-v3xDuhO0.cjs} +2 -2
- package/dist/{backends-_gQdAzBK.cjs.map → backends-v3xDuhO0.cjs.map} +1 -1
- package/dist/{config-20V08LZp.mjs → config-CSOK73pY.mjs} +2 -2
- package/dist/{config-20V08LZp.mjs.map → config-CSOK73pY.mjs.map} +1 -1
- package/dist/{config-CP6wsLHA.cjs → config-DdIu1O_I.cjs} +2 -2
- package/dist/{config-CP6wsLHA.cjs.map → config-DdIu1O_I.cjs.map} +1 -1
- 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/{encryption-10Srqiix.cjs → encryption-BspqzZIb.cjs} +4 -4
- package/dist/{encryption-10Srqiix.cjs.map → encryption-BspqzZIb.cjs.map} +1 -1
- package/dist/{encryption-KQTYFEzU.mjs → encryption-Dlnl5XEU.mjs} +4 -4
- package/dist/{encryption-KQTYFEzU.mjs.map → encryption-Dlnl5XEU.mjs.map} +1 -1
- package/dist/{github-y7OEj59M.mjs → github-BdF9tsvl.mjs} +3 -3
- package/dist/{github-y7OEj59M.mjs.map → github-BdF9tsvl.mjs.map} +1 -1
- package/dist/{github-DtRUkHN8.cjs → github-mQorSS36.cjs} +3 -3
- package/dist/{github-DtRUkHN8.cjs.map → github-mQorSS36.cjs.map} +1 -1
- package/dist/{index-DMcjpvd-.d.cts → index-BorYE_uO.d.cts} +2 -2
- package/dist/{index-DMcjpvd-.d.cts.map → index-BorYE_uO.d.cts.map} +1 -1
- package/dist/{index-RM92lx8A.d.mts → index-C3JmluyO.d.mts} +2 -2
- package/dist/{index-RM92lx8A.d.mts.map → index-C3JmluyO.d.mts.map} +1 -1
- package/dist/index.cjs +488 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +488 -135
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-CS3DkO6y.mjs → migrate-CXVnh1J3.mjs} +4 -4
- package/dist/{migrate-CS3DkO6y.mjs.map → migrate-CXVnh1J3.mjs.map} +1 -1
- package/dist/{migrate-DQ54y2WD.cjs → migrate-DzYqA-e3.cjs} +4 -4
- package/dist/{migrate-DQ54y2WD.cjs.map → migrate-DzYqA-e3.cjs.map} +1 -1
- 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-BSpUZnNo.mjs → ready-DbDHneLR.mjs} +2 -2
- package/dist/{ready-BSpUZnNo.mjs.map → ready-DbDHneLR.mjs.map} +1 -1
- package/dist/{ready-MUhVJDHe.cjs → ready-mR96orkJ.cjs} +2 -2
- package/dist/{ready-MUhVJDHe.cjs.map → ready-mR96orkJ.cjs.map} +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/{types-BIyIQUR-.d.cts → types-D7j6QWeX.d.mts} +32 -2
- package/dist/types-D7j6QWeX.d.mts.map +1 -0
- package/dist/{types-CmQl6zaP.d.mts → types-JV4XOY01.d.cts} +33 -1
- package/dist/types-JV4XOY01.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 +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-BbPJVCto.cjs → workspace-B14L2TrY.cjs} +5 -5
- package/dist/{workspace-BbPJVCto.cjs.map → workspace-B14L2TrY.cjs.map} +1 -1
- package/dist/{workspace-BpdXIeWV.cjs → workspace-Bah36xSG.cjs} +3 -1
- package/dist/workspace-Bah36xSG.cjs.map +1 -0
- package/dist/{workspace-C2mFV53i.mjs → workspace-BvNXAXQI.mjs} +3 -1
- package/dist/workspace-BvNXAXQI.mjs.map +1 -0
- package/dist/{workspace-CgWcE_Yr.mjs → workspace-D-f58jsP.mjs} +5 -5
- package/dist/{workspace-CgWcE_Yr.mjs.map → workspace-D-f58jsP.mjs.map} +1 -1
- package/package.json +13 -13
- package/dist/OpenApiTsGenerator-BLRQ7Iu9.cjs.map +0 -1
- package/dist/OpenApiTsGenerator-NThANByQ.mjs.map +0 -1
- package/dist/SSMStateStore-DD7jKrCO.cjs.map +0 -1
- package/dist/SSMStateStore-oda9xOGc.mjs.map +0 -1
- package/dist/types-BIyIQUR-.d.cts.map +0 -1
- package/dist/types-CmQl6zaP.d.mts.map +0 -1
- package/dist/workspace-BpdXIeWV.cjs.map +0 -1
- package/dist/workspace-C2mFV53i.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
3
3
|
import { i as ConstructGenerator, t as discover } from "./discover-Cf2jAcgF.mjs";
|
|
4
|
-
import { d as parseModuleConfig, f as appKey, l as loadWorkspaceConfig, n as NotABackendApp, o as loadAppConfig, p as derivedApps, r as NotInAnApp, s as loadConfig, t as ConfigNotFound, u as loadWorkspaceSettings } from "./config-
|
|
5
|
-
import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-
|
|
4
|
+
import { d as parseModuleConfig, f as appKey, l as loadWorkspaceConfig, n as NotABackendApp, o as loadAppConfig, p as derivedApps, r as NotInAnApp, s as loadConfig, t as ConfigNotFound, u as loadWorkspaceSettings } from "./config-CSOK73pY.mjs";
|
|
5
|
+
import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-BvNXAXQI.mjs";
|
|
6
6
|
import { a as withCustomSecret, i as toEmbeddableSecrets, n as maskPassword, t as initStageSecrets } from "./storage-BzaUxmqe.mjs";
|
|
7
7
|
import { a as getKeyPath, i as FileSecretsStore, n as secretsStoreFor } from "./store-DeGATCS-.mjs";
|
|
8
|
-
import { C as loadEnvFiles, S as isPortAvailable, T as prepareEntryCredentials, _ as fakeKey, a as reconcileWorkspace, b as createEntryWrapper, c as LOCAL_CA_PATH, d as postgresDatabaseNames, f as quoteIdentifier, g as describeServices, h as rootSite, i as derivedContainers, l as forgetState, m as resourceName, n as backendsOf$1, p as pgClient, r as constructGlobs, s as COMPOSE_PATH, t as FAKE_ENV, u as applyPostgres, v as primaryPortKey, w as loadSecretsForApp, x as findAvailablePort, y as createCredentialsPreload } from "./workspace-
|
|
9
|
-
import { n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-
|
|
8
|
+
import { C as loadEnvFiles, S as isPortAvailable, T as prepareEntryCredentials, _ as fakeKey, a as reconcileWorkspace, b as createEntryWrapper, c as LOCAL_CA_PATH, d as postgresDatabaseNames, f as quoteIdentifier, g as describeServices, h as rootSite, i as derivedContainers, l as forgetState, m as resourceName, n as backendsOf$1, p as pgClient, r as constructGlobs, s as COMPOSE_PATH, t as FAKE_ENV, u as applyPostgres, v as primaryPortKey, w as loadSecretsForApp, x as findAvailablePort, y as createCredentialsPreload } from "./workspace-D-f58jsP.mjs";
|
|
9
|
+
import { i as telemetryFor, n as EndpointGenerator, r as runtimeFor } from "./OpenApiTsGenerator-BRWD1Wit.mjs";
|
|
10
10
|
import { generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi.mjs";
|
|
11
|
-
import { a as writeTestHarness, c as driversFor, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as SubscriberGenerator, s as cacheBackendsIn, t as TEST_READY_ENV } from "./ready-
|
|
12
|
-
import { i as storageBackendFor, n as eventsBackendFor, r as providerOf, t as cacheBackendFor } from "./backends-
|
|
11
|
+
import { a as writeTestHarness, c as driversFor, i as TEST_MANIFEST_ENV, n as TEST_READY_FILE, o as SubscriberGenerator, s as cacheBackendsIn, t as TEST_READY_ENV } from "./ready-DbDHneLR.mjs";
|
|
12
|
+
import { i as storageBackendFor, n as eventsBackendFor, r as providerOf, t as cacheBackendFor } from "./backends-CjuCiE8h.mjs";
|
|
13
13
|
import { n as withCompute, r as withRoutes, t as manifestModule } from "./emit-C1e8qD08.mjs";
|
|
14
14
|
import { createRequire } from "node:module";
|
|
15
15
|
import { Command } from "commander";
|
|
@@ -42,7 +42,7 @@ import { Queue } from "@geekmidas/constructs/queue";
|
|
|
42
42
|
import { Topic } from "@geekmidas/constructs/topic";
|
|
43
43
|
import { lookup } from "node:dns/promises";
|
|
44
44
|
//#region package.json
|
|
45
|
-
var version = "10.0.0-alpha.
|
|
45
|
+
var version = "10.0.0-alpha.58";
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/auth/credentials.ts
|
|
48
48
|
/**
|
|
@@ -136,15 +136,6 @@ async function getDokployToken(options) {
|
|
|
136
136
|
return null;
|
|
137
137
|
}
|
|
138
138
|
/**
|
|
139
|
-
* Store Dokploy registry ID
|
|
140
|
-
*/
|
|
141
|
-
async function storeDokployRegistryId(registryId, options) {
|
|
142
|
-
const credentials = await readCredentials(options);
|
|
143
|
-
if (!credentials.dokploy) throw new Error("Dokploy credentials not found. Run \"gkm login --service dokploy\" first.");
|
|
144
|
-
credentials.dokploy.registryId = registryId;
|
|
145
|
-
await writeCredentials(credentials, options);
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
139
|
* Get Dokploy registry ID from stored credentials
|
|
149
140
|
*/
|
|
150
141
|
async function getDokployRegistryId(options) {
|
|
@@ -1740,9 +1731,9 @@ async function workspaceDevCommand(configured, options) {
|
|
|
1740
1731
|
for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
|
|
1741
1732
|
logger$9.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
|
|
1742
1733
|
if (options.migrate || options.seed) {
|
|
1743
|
-
const { prepareDevDatabases } = await import("./migrate-
|
|
1734
|
+
const { prepareDevDatabases } = await import("./migrate-CXVnh1J3.mjs");
|
|
1744
1735
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1745
|
-
} else await import("./migrate-
|
|
1736
|
+
} else await import("./migrate-CXVnh1J3.mjs").then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1746
1737
|
logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1747
1738
|
});
|
|
1748
1739
|
const appUrls = {};
|
|
@@ -2768,14 +2759,34 @@ var CommandTimedOut = class extends Error {
|
|
|
2768
2759
|
* once `timeoutMs` passes (after the child is gone), and with the spawn error
|
|
2769
2760
|
* itself when the program cannot be started at all — `ENOENT` already names it.
|
|
2770
2761
|
*/
|
|
2771
|
-
function run(command, args, options = {}) {
|
|
2762
|
+
async function run(command, args, options = {}) {
|
|
2763
|
+
await spawnChild(command, args, options, false);
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* {@link run}, resolving with what the program wrote to stdout — for a value
|
|
2767
|
+
* the CLI reads back, such as the digest `docker inspect` reports. stderr still
|
|
2768
|
+
* reaches the terminal, so a failure explains itself the same way.
|
|
2769
|
+
*/
|
|
2770
|
+
function runOutput(command, args, options = {}) {
|
|
2771
|
+
return spawnChild(command, args, options, true);
|
|
2772
|
+
}
|
|
2773
|
+
function spawnChild(command, args, options, capture) {
|
|
2772
2774
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, ...spawnOptions } = options;
|
|
2773
2775
|
return new Promise((resolve, reject) => {
|
|
2774
2776
|
const child = spawn(command, [...args], {
|
|
2775
2777
|
stdio: "inherit",
|
|
2776
2778
|
...spawnOptions,
|
|
2779
|
+
...capture ? { stdio: [
|
|
2780
|
+
"ignore",
|
|
2781
|
+
"pipe",
|
|
2782
|
+
"inherit"
|
|
2783
|
+
] } : {},
|
|
2777
2784
|
shell: false
|
|
2778
2785
|
});
|
|
2786
|
+
let stdout = "";
|
|
2787
|
+
child.stdout?.on("data", (chunk) => {
|
|
2788
|
+
stdout += chunk.toString();
|
|
2789
|
+
});
|
|
2779
2790
|
let timedOut = false;
|
|
2780
2791
|
let killer;
|
|
2781
2792
|
const timer = setTimeout(() => {
|
|
@@ -2796,7 +2807,7 @@ function run(command, args, options = {}) {
|
|
|
2796
2807
|
child.on("close", (code, signal) => {
|
|
2797
2808
|
settle();
|
|
2798
2809
|
if (timedOut) reject(new CommandTimedOut(command, args, timeoutMs));
|
|
2799
|
-
else if (code === 0) resolve();
|
|
2810
|
+
else if (code === 0) resolve(stdout);
|
|
2800
2811
|
else reject(new CommandFailed(command, args, code, signal));
|
|
2801
2812
|
});
|
|
2802
2813
|
});
|
|
@@ -3103,7 +3114,8 @@ async function buildOneApp(input) {
|
|
|
3103
3114
|
hooks,
|
|
3104
3115
|
markOptional: options.markOptional ?? false,
|
|
3105
3116
|
skipBundle: options.skipBundle ?? false,
|
|
3106
|
-
stage: options.stage
|
|
3117
|
+
stage: options.stage,
|
|
3118
|
+
workspaceName: workspace.name
|
|
3107
3119
|
});
|
|
3108
3120
|
}
|
|
3109
3121
|
/**
|
|
@@ -3181,7 +3193,12 @@ async function buildApp(input) {
|
|
|
3181
3193
|
appRoot,
|
|
3182
3194
|
cache: cacheBackendsIn(declared, cacheBackend)
|
|
3183
3195
|
}),
|
|
3184
|
-
markOptional: input.markOptional ?? false
|
|
3196
|
+
markOptional: input.markOptional ?? false,
|
|
3197
|
+
...production && { telemetry: telemetryFor({
|
|
3198
|
+
appRoot,
|
|
3199
|
+
surfaceId: derived.surface?.id,
|
|
3200
|
+
workspaceName: input.workspaceName
|
|
3201
|
+
}) }
|
|
3185
3202
|
};
|
|
3186
3203
|
const endpointGenerator = new EndpointGenerator();
|
|
3187
3204
|
const functionGenerator = new FunctionGenerator();
|
|
@@ -3590,7 +3607,7 @@ const PROVISIONERS = {
|
|
|
3590
3607
|
const name = resourceName(declaration.id, declaration.kind, context.stage);
|
|
3591
3608
|
const { postgres } = await context.api.findOrCreatePostgres(serviceName({
|
|
3592
3609
|
stage: context.stage,
|
|
3593
|
-
app: context.project
|
|
3610
|
+
app: context.scope ?? context.project
|
|
3594
3611
|
}, declaration.id), context.projectId, context.environmentId, {
|
|
3595
3612
|
databaseName: name,
|
|
3596
3613
|
databasePassword: derivedPassword(context, `${name}:master`)
|
|
@@ -3724,7 +3741,7 @@ const PROVISIONERS = {
|
|
|
3724
3741
|
if (backend !== "minio") throw new UnprovisionableBucket(declaration.id, backend);
|
|
3725
3742
|
const service = serviceName({
|
|
3726
3743
|
stage: context.stage,
|
|
3727
|
-
app: context.project
|
|
3744
|
+
app: context.scope ?? context.project
|
|
3728
3745
|
}, declaration.id);
|
|
3729
3746
|
const bucket = resourceName(declaration.id, declaration.kind, context.stage);
|
|
3730
3747
|
const user = `${bucket}-root`;
|
|
@@ -4185,6 +4202,7 @@ async function provisionDeclared(options) {
|
|
|
4185
4202
|
environmentId: options.environmentId,
|
|
4186
4203
|
stage: options.stage,
|
|
4187
4204
|
project: workspace.name,
|
|
4205
|
+
scope: options.scope ?? workspace.name,
|
|
4188
4206
|
cache: cacheBackendFor(providerOf(workspace)),
|
|
4189
4207
|
events: eventsBackendFor(providerOf(workspace)),
|
|
4190
4208
|
storage: storageBackendFor(providerOf(workspace)),
|
|
@@ -4295,6 +4313,15 @@ function setApplicationId(state, appName, applicationId) {
|
|
|
4295
4313
|
state.applications[appName] = applicationId;
|
|
4296
4314
|
}
|
|
4297
4315
|
/**
|
|
4316
|
+
* Record the image an app was deployed with (mutates state)
|
|
4317
|
+
*/
|
|
4318
|
+
function setDeployedImage(state, appName, image) {
|
|
4319
|
+
state.images = {
|
|
4320
|
+
...state.images,
|
|
4321
|
+
[appName]: image
|
|
4322
|
+
};
|
|
4323
|
+
}
|
|
4324
|
+
/**
|
|
4298
4325
|
* Get a generated secret for an app
|
|
4299
4326
|
*/
|
|
4300
4327
|
function getGeneratedSecret(state, appName, secretName) {
|
|
@@ -6072,6 +6099,33 @@ async function pushImage(imageRef) {
|
|
|
6072
6099
|
}
|
|
6073
6100
|
}
|
|
6074
6101
|
/**
|
|
6102
|
+
* The digest the registry gave a pushed image, `sha256:…`.
|
|
6103
|
+
*
|
|
6104
|
+
* A tag can be pushed over — by the next deploy, or by anything else that
|
|
6105
|
+
* shares the repository — and the digest is what says which image a stage
|
|
6106
|
+
* actually ran. `RepoDigests` has one entry per repository the image was
|
|
6107
|
+
* pushed to, so the one for this ref's repository is picked by name.
|
|
6108
|
+
*
|
|
6109
|
+
* @internal Exported for testing
|
|
6110
|
+
*/
|
|
6111
|
+
async function pushedDigest(imageRef) {
|
|
6112
|
+
const repository = imageRef.split("@")[0].replace(/:[\w][\w.-]*$/, "");
|
|
6113
|
+
const output = await runOutput("docker", [
|
|
6114
|
+
"image",
|
|
6115
|
+
"inspect",
|
|
6116
|
+
"--format={{json .RepoDigests}}",
|
|
6117
|
+
imageRef
|
|
6118
|
+
], { cwd: process.cwd() });
|
|
6119
|
+
let digests;
|
|
6120
|
+
try {
|
|
6121
|
+
digests = JSON.parse(output.trim() || "[]");
|
|
6122
|
+
} catch {
|
|
6123
|
+
return;
|
|
6124
|
+
}
|
|
6125
|
+
if (!Array.isArray(digests)) return void 0;
|
|
6126
|
+
return digests.find((entry) => typeof entry === "string" && entry.startsWith(`${repository}@`))?.slice(repository.length + 1);
|
|
6127
|
+
}
|
|
6128
|
+
/**
|
|
6075
6129
|
* Deploy using Docker (build and optionally push image)
|
|
6076
6130
|
*/
|
|
6077
6131
|
async function deployDocker(options) {
|
|
@@ -6079,9 +6133,13 @@ async function deployDocker(options) {
|
|
|
6079
6133
|
const imageName = config.imageName;
|
|
6080
6134
|
const imageRef = validateImageRef(getImageRef(config.registry, imageName, tag));
|
|
6081
6135
|
await buildImage(imageRef, buildArgs, credentials);
|
|
6136
|
+
let digest;
|
|
6082
6137
|
if (!skipPush) {
|
|
6083
6138
|
if (!config.registry) logger$5.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
6084
|
-
else
|
|
6139
|
+
else {
|
|
6140
|
+
await pushImage(imageRef);
|
|
6141
|
+
digest = await pushedDigest(imageRef);
|
|
6142
|
+
}
|
|
6085
6143
|
}
|
|
6086
6144
|
logger$5.log("\n✅ Docker deployment ready!");
|
|
6087
6145
|
logger$5.log(`\n📋 Deployment details:`);
|
|
@@ -6090,25 +6148,10 @@ async function deployDocker(options) {
|
|
|
6090
6148
|
if (masterKey) logger$5.log(`\n🔐 Run the container with GKM_MASTER_KEY set to the key with fingerprint ${keyFingerprint(masterKey)}.`);
|
|
6091
6149
|
return {
|
|
6092
6150
|
imageRef,
|
|
6151
|
+
...digest ? { digest } : {},
|
|
6093
6152
|
masterKey
|
|
6094
6153
|
};
|
|
6095
6154
|
}
|
|
6096
|
-
/**
|
|
6097
|
-
* What one application is called on a provider.
|
|
6098
|
-
*
|
|
6099
|
-
* The same rule the constructs use, through the same `scopedName`: the
|
|
6100
|
-
* application beside `production-kitchen-sink-database` is
|
|
6101
|
-
* `production-kitchen-sink-api`, not `api`. Both deploy paths call this — the
|
|
6102
|
-
* workspace one named its applications by the bare app key, so a project held
|
|
6103
|
-
* an `api` and a `web` that every stage would collide on.
|
|
6104
|
-
*
|
|
6105
|
-
* The app id is dropped when it repeats the project, so a project named for its
|
|
6106
|
-
* one application is `production-kitchen-sink` rather than
|
|
6107
|
-
* `production-kitchen-sink-kitchen-sink`.
|
|
6108
|
-
*/
|
|
6109
|
-
function applicationName(stage, project, app) {
|
|
6110
|
-
return app === project ? `${stage}-${project}`.toLowerCase() : scopedName([stage, project], app);
|
|
6111
|
-
}
|
|
6112
6155
|
//#endregion
|
|
6113
6156
|
//#region src/deploy/dokploy-api.ts
|
|
6114
6157
|
var dokploy_api_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -6325,6 +6368,18 @@ var DokployApi = class {
|
|
|
6325
6368
|
});
|
|
6326
6369
|
}
|
|
6327
6370
|
/**
|
|
6371
|
+
* Change a project's name or description.
|
|
6372
|
+
*
|
|
6373
|
+
* The name is sent with the description because `project.update` validates
|
|
6374
|
+
* the whole project on some Dokploy versions, and a missing name fails it.
|
|
6375
|
+
*/
|
|
6376
|
+
async updateProject(projectId, updates) {
|
|
6377
|
+
await this.post("project.update", {
|
|
6378
|
+
projectId,
|
|
6379
|
+
...updates
|
|
6380
|
+
});
|
|
6381
|
+
}
|
|
6382
|
+
/**
|
|
6328
6383
|
* Delete a project and all its resources
|
|
6329
6384
|
*/
|
|
6330
6385
|
async deleteProject(projectId) {
|
|
@@ -7024,6 +7079,309 @@ function random() {
|
|
|
7024
7079
|
return randomBytes(32).toString("base64url");
|
|
7025
7080
|
}
|
|
7026
7081
|
//#endregion
|
|
7082
|
+
//#region src/deploy/identity.ts
|
|
7083
|
+
/**
|
|
7084
|
+
* Who a deploy is, on a target it shares with other workspaces.
|
|
7085
|
+
*
|
|
7086
|
+
* A Dokploy server is one flat namespace of projects, images and Docker
|
|
7087
|
+
* service names. Deploys used to identify everything they made by the
|
|
7088
|
+
* workspace name alone, so two workspaces called `shop` — or `Shop` and
|
|
7089
|
+
* `shop` — deploying to one server landed in the same project, pushed over
|
|
7090
|
+
* each other's images and redeployed each other's applications. A
|
|
7091
|
+
* `DeployIdentity` is what a deploy names and claims its resources by instead.
|
|
7092
|
+
*/
|
|
7093
|
+
/** A namespace that cannot be part of a Dokploy name or an image path. */
|
|
7094
|
+
var DeployNamespaceInvalid = class extends Error {
|
|
7095
|
+
namespace;
|
|
7096
|
+
constructor(namespace) {
|
|
7097
|
+
super(`deploy.namespace '${namespace}' is not usable as a name: use lowercase letters, digits and single '-' between them (e.g. 'acme' or 'acme-platform') in gkm.config.ts.`);
|
|
7098
|
+
this.namespace = namespace;
|
|
7099
|
+
this.name = "DeployNamespaceInvalid";
|
|
7100
|
+
}
|
|
7101
|
+
};
|
|
7102
|
+
/** A workspace name with nothing a target can name a resource by. */
|
|
7103
|
+
var DeployProjectNameInvalid = class extends Error {
|
|
7104
|
+
workspaceName;
|
|
7105
|
+
constructor(workspaceName) {
|
|
7106
|
+
super(`The workspace name '${workspaceName}' has no letters or digits to name deployed resources by. Set "name" in gkm.config.ts.`);
|
|
7107
|
+
this.workspaceName = workspaceName;
|
|
7108
|
+
this.name = "DeployProjectNameInvalid";
|
|
7109
|
+
}
|
|
7110
|
+
};
|
|
7111
|
+
const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7112
|
+
/**
|
|
7113
|
+
* Lowercase `[a-z0-9-]`, runs of anything else collapsed to one `-`: valid as
|
|
7114
|
+
* a Docker image path component, a Docker service name and a Dokploy name.
|
|
7115
|
+
*/
|
|
7116
|
+
function slug$1(value) {
|
|
7117
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7118
|
+
}
|
|
7119
|
+
/** The namespace a workspace is in when it does not name one. */
|
|
7120
|
+
function defaultNamespace(workspaceName) {
|
|
7121
|
+
return slug$1(kebabCase(workspaceName));
|
|
7122
|
+
}
|
|
7123
|
+
function deployIdentity(workspace, stage) {
|
|
7124
|
+
const project = slug$1(workspace.name);
|
|
7125
|
+
if (!project) throw new DeployProjectNameInvalid(workspace.name);
|
|
7126
|
+
const fallback = defaultNamespace(workspace.name);
|
|
7127
|
+
const namespace = workspace.deploy?.namespace ?? fallback;
|
|
7128
|
+
if (!NAME.test(namespace)) throw new DeployNamespaceInvalid(namespace);
|
|
7129
|
+
return {
|
|
7130
|
+
namespace,
|
|
7131
|
+
project,
|
|
7132
|
+
stage,
|
|
7133
|
+
key: `${namespace}/${project}`,
|
|
7134
|
+
scope: namespace === fallback ? project : `${namespace}-${project}`
|
|
7135
|
+
};
|
|
7136
|
+
}
|
|
7137
|
+
/**
|
|
7138
|
+
* The token a Dokploy project's description carries to say which identity
|
|
7139
|
+
* created it. A project with the right name and no marker belongs to someone
|
|
7140
|
+
* else, and a deploy never adopts it.
|
|
7141
|
+
*/
|
|
7142
|
+
function ownershipMarker(identity) {
|
|
7143
|
+
return `gkm:${identity.key}`;
|
|
7144
|
+
}
|
|
7145
|
+
/** Whether a project description carries `identity`'s marker. */
|
|
7146
|
+
function isOwnedBy(description, identity) {
|
|
7147
|
+
const marker = ownershipMarker(identity);
|
|
7148
|
+
return (description ?? "").split(/\s+/).includes(marker);
|
|
7149
|
+
}
|
|
7150
|
+
/** Any `gkm:` marker a description carries — whoever's it is. */
|
|
7151
|
+
function markerOf(description) {
|
|
7152
|
+
return (description ?? "").split(/\s+/).find((t) => t.startsWith("gkm:"));
|
|
7153
|
+
}
|
|
7154
|
+
/** What the Dokploy project is called. */
|
|
7155
|
+
function projectName(identity) {
|
|
7156
|
+
return identity.scope;
|
|
7157
|
+
}
|
|
7158
|
+
/**
|
|
7159
|
+
* The repository an app's image is pushed to, below the registry:
|
|
7160
|
+
* `<namespace>/<project>-<app>`.
|
|
7161
|
+
*
|
|
7162
|
+
* The namespace is a path segment of its own so that a registry that maps
|
|
7163
|
+
* paths to owners (an organisation's packages, a Harbor project) can grant it
|
|
7164
|
+
* as one; the app is joined to the project so the two read as one name.
|
|
7165
|
+
*/
|
|
7166
|
+
function imageName(identity, app) {
|
|
7167
|
+
return `${identity.namespace}/${identity.project}-${slug$1(kebabCase(app))}`;
|
|
7168
|
+
}
|
|
7169
|
+
/** The full image ref an app is built, pushed and deployed as. */
|
|
7170
|
+
function imageRef(identity, app, registry, tag) {
|
|
7171
|
+
const name = imageName(identity, app);
|
|
7172
|
+
return registry ? `${registry.replace(/\/+$/, "")}/${name}:${tag}` : `${name}:${tag}`;
|
|
7173
|
+
}
|
|
7174
|
+
/**
|
|
7175
|
+
* What one application is called on the target.
|
|
7176
|
+
*
|
|
7177
|
+
* The same rule the constructs use, through the same `scopedName`, scoped by
|
|
7178
|
+
* the stage and the identity: the application beside
|
|
7179
|
+
* `production-kitchen-sink-database` is `production-kitchen-sink-api`. A
|
|
7180
|
+
* Dokploy application's name is also its Docker service name, which is unique
|
|
7181
|
+
* on the whole server, so the namespace has to be in it too — `acme`'s and
|
|
7182
|
+
* `globex`'s `shop` would otherwise both run `production-shop-api`.
|
|
7183
|
+
*
|
|
7184
|
+
* The app id is dropped when it repeats the project, so a project named for its
|
|
7185
|
+
* one application is `production-kitchen-sink` rather than
|
|
7186
|
+
* `production-kitchen-sink-kitchen-sink`.
|
|
7187
|
+
*/
|
|
7188
|
+
function applicationName(identity, app) {
|
|
7189
|
+
return slug$1(app) === identity.project ? `${identity.stage}-${identity.scope}` : scopedName([identity.stage, identity.scope], app);
|
|
7190
|
+
}
|
|
7191
|
+
//#endregion
|
|
7192
|
+
//#region src/deploy/ownership.ts
|
|
7193
|
+
/**
|
|
7194
|
+
* Which Dokploy project a deploy is allowed to deploy into.
|
|
7195
|
+
*
|
|
7196
|
+
* Deploy used to take any project whose name matched the workspace's, in any
|
|
7197
|
+
* case — so a second workspace called `shop` (or `Shop`) on the same server
|
|
7198
|
+
* deployed into the first one's project, over its images and applications. A
|
|
7199
|
+
* project is now claimed by an ownership marker in its description, and a
|
|
7200
|
+
* project with the right name and no marker is somebody else's.
|
|
7201
|
+
*/
|
|
7202
|
+
/**
|
|
7203
|
+
* A Dokploy project with the name this deploy would use exists, and nothing
|
|
7204
|
+
* says this workspace created it.
|
|
7205
|
+
*/
|
|
7206
|
+
var ProjectNotOwned = class extends Error {
|
|
7207
|
+
projectName;
|
|
7208
|
+
projectId;
|
|
7209
|
+
identity;
|
|
7210
|
+
ownedBy;
|
|
7211
|
+
constructor(projectName, projectId, identity, ownedBy) {
|
|
7212
|
+
super(`The Dokploy project '${projectName}' (${projectId}) was not created by this workspace (${identity})${ownedBy ? ` — it is marked '${ownedBy}'` : ""}, so deploy will not use it. Set deploy.namespace in gkm.config.ts to deploy under a name of your own, or, if this project really is this workspace's, add 'gkm:${identity}' to its description in Dokploy.`);
|
|
7213
|
+
this.projectName = projectName;
|
|
7214
|
+
this.projectId = projectId;
|
|
7215
|
+
this.identity = identity;
|
|
7216
|
+
this.ownedBy = ownedBy;
|
|
7217
|
+
this.name = "ProjectNotOwned";
|
|
7218
|
+
}
|
|
7219
|
+
};
|
|
7220
|
+
/** A description with the marker added, keeping what was there. */
|
|
7221
|
+
function withMarker(description, marker) {
|
|
7222
|
+
const existing = (description ?? "").trim();
|
|
7223
|
+
return existing ? `${existing}\n${marker}` : marker;
|
|
7224
|
+
}
|
|
7225
|
+
/** `project.one`, or `null` when Dokploy no longer has the project. */
|
|
7226
|
+
async function projectById(api, projectId) {
|
|
7227
|
+
try {
|
|
7228
|
+
return await api.getProject(projectId) ?? null;
|
|
7229
|
+
} catch (error) {
|
|
7230
|
+
if (error instanceof DokployApiError) return null;
|
|
7231
|
+
throw error;
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7234
|
+
/**
|
|
7235
|
+
* The project this deploy deploys into:
|
|
7236
|
+
*
|
|
7237
|
+
* 1. The one the stage's state names, if Dokploy still has it. State written
|
|
7238
|
+
* before markers existed is trusted — it holds an id this workspace was
|
|
7239
|
+
* given when it created the project — and the marker is written now, so
|
|
7240
|
+
* the project is claimed for the next deploy that has no state.
|
|
7241
|
+
* 2. One with this name *and* this identity's marker.
|
|
7242
|
+
* 3. A new one, marked.
|
|
7243
|
+
*
|
|
7244
|
+
* A project that matches by name (in any case) but carries no marker, or
|
|
7245
|
+
* another identity's, raises `ProjectNotOwned` — it is never adopted.
|
|
7246
|
+
*/
|
|
7247
|
+
async function resolveProject(api, identity, stateProjectId, log = () => {}) {
|
|
7248
|
+
const name = projectName(identity);
|
|
7249
|
+
const marker = ownershipMarker(identity);
|
|
7250
|
+
if (stateProjectId) {
|
|
7251
|
+
const known = await projectById(api, stateProjectId);
|
|
7252
|
+
if (known) {
|
|
7253
|
+
const claimed = markerOf(known.description);
|
|
7254
|
+
if (claimed && claimed !== marker) throw new ProjectNotOwned(known.name, known.projectId, identity.key, claimed);
|
|
7255
|
+
if (!claimed) {
|
|
7256
|
+
await api.updateProject(known.projectId, {
|
|
7257
|
+
name: known.name,
|
|
7258
|
+
description: withMarker(known.description, marker)
|
|
7259
|
+
});
|
|
7260
|
+
log(` Claimed project ${known.name} for ${identity.key}`);
|
|
7261
|
+
}
|
|
7262
|
+
return {
|
|
7263
|
+
projectId: known.projectId,
|
|
7264
|
+
name: known.name,
|
|
7265
|
+
environments: known.environments ?? [],
|
|
7266
|
+
via: "state"
|
|
7267
|
+
};
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
const projects = await api.listProjects();
|
|
7271
|
+
const owned = projects.find((p) => p.name.toLowerCase() === name.toLowerCase() && isOwnedBy(p.description, identity));
|
|
7272
|
+
if (owned) {
|
|
7273
|
+
const details = await api.getProject(owned.projectId);
|
|
7274
|
+
return {
|
|
7275
|
+
projectId: owned.projectId,
|
|
7276
|
+
name: owned.name,
|
|
7277
|
+
environments: details.environments ?? [],
|
|
7278
|
+
via: "marker"
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
const clash = projects.find((p) => p.name.toLowerCase() === name.toLowerCase());
|
|
7282
|
+
if (clash) throw new ProjectNotOwned(clash.name, clash.projectId, identity.key, markerOf(clash.description));
|
|
7283
|
+
const created = await api.createProject(name, withMarker("Deployed by gkm.", marker));
|
|
7284
|
+
return {
|
|
7285
|
+
projectId: created.project.projectId,
|
|
7286
|
+
name: created.project.name,
|
|
7287
|
+
environments: [created.environment],
|
|
7288
|
+
via: "created"
|
|
7289
|
+
};
|
|
7290
|
+
}
|
|
7291
|
+
//#endregion
|
|
7292
|
+
//#region src/deploy/registry.ts
|
|
7293
|
+
/**
|
|
7294
|
+
* Which Dokploy registry a stage's images are pulled through.
|
|
7295
|
+
*
|
|
7296
|
+
* It used to be one id stored per machine (`~/.gkm/credentials.json`), and
|
|
7297
|
+
* when that was empty, the first registry Dokploy listed — so an image could be
|
|
7298
|
+
* deployed with credentials for a registry nobody chose, and two workspaces
|
|
7299
|
+
* deployed from one machine shared whichever was stored last. The id is now
|
|
7300
|
+
* kept in the stage's state, and found only from what the workspace
|
|
7301
|
+
* configured.
|
|
7302
|
+
*/
|
|
7303
|
+
/** There is nowhere to push images to, so Dokploy has nothing to pull. */
|
|
7304
|
+
var RegistryNotConfigured = class extends Error {
|
|
7305
|
+
stage;
|
|
7306
|
+
constructor(stage) {
|
|
7307
|
+
super(`Deploying '${stage}' needs a container registry: Dokploy pulls each app's image from one. Set deploy.dokploy.registry in gkm.config.ts (e.g. 'ghcr.io/acme'), and deploy.dokploy.registryId if Dokploy already holds that registry's credentials.`);
|
|
7308
|
+
this.stage = stage;
|
|
7309
|
+
this.name = "RegistryNotConfigured";
|
|
7310
|
+
}
|
|
7311
|
+
};
|
|
7312
|
+
/** `deploy.dokploy.registryId` names a registry Dokploy does not have. */
|
|
7313
|
+
var RegistryNotFound = class extends Error {
|
|
7314
|
+
registryId;
|
|
7315
|
+
constructor(registryId) {
|
|
7316
|
+
super(`deploy.dokploy.registryId '${registryId}' is not a registry on this Dokploy server. Find the right id under Settings → Docker Registry in Dokploy and set it in gkm.config.ts, or remove it to have deploy find the registry by its URL.`);
|
|
7317
|
+
this.registryId = registryId;
|
|
7318
|
+
this.name = "RegistryNotFound";
|
|
7319
|
+
}
|
|
7320
|
+
};
|
|
7321
|
+
/** More than one Dokploy registry serves the configured registry. */
|
|
7322
|
+
var RegistryAmbiguous = class extends Error {
|
|
7323
|
+
registry;
|
|
7324
|
+
registryIds;
|
|
7325
|
+
constructor(registry, registryIds) {
|
|
7326
|
+
super(`Dokploy has ${registryIds.length} registries for '${registry}' (${registryIds.join(", ")}). Set deploy.dokploy.registryId in gkm.config.ts to the one this workspace pushes with.`);
|
|
7327
|
+
this.registry = registry;
|
|
7328
|
+
this.registryIds = registryIds;
|
|
7329
|
+
this.name = "RegistryAmbiguous";
|
|
7330
|
+
}
|
|
7331
|
+
};
|
|
7332
|
+
/** `https://ghcr.io/acme/` → `{ host: 'ghcr.io', path: 'acme' }`. */
|
|
7333
|
+
function parts(url) {
|
|
7334
|
+
const [host = "", ...path] = url.replace(/^[a-z]+:\/\//i, "").replace(/\/+$/, "").toLowerCase().split("/");
|
|
7335
|
+
return {
|
|
7336
|
+
host,
|
|
7337
|
+
path: path.join("/")
|
|
7338
|
+
};
|
|
7339
|
+
}
|
|
7340
|
+
/**
|
|
7341
|
+
* The Dokploy registries that serve `registry`: the same host, and — where
|
|
7342
|
+
* Dokploy's own entry names one — the same path.
|
|
7343
|
+
*/
|
|
7344
|
+
function serving(registries, registry) {
|
|
7345
|
+
const wanted = parts(registry);
|
|
7346
|
+
const sameHost = registries.filter((r) => parts(r.registryUrl ?? "").host === wanted.host);
|
|
7347
|
+
if (sameHost.length <= 1) return sameHost;
|
|
7348
|
+
return sameHost.filter((r) => {
|
|
7349
|
+
return (parts(r.registryUrl ?? "").path || (r.imagePrefix ?? "").toLowerCase()) === wanted.path;
|
|
7350
|
+
});
|
|
7351
|
+
}
|
|
7352
|
+
/** `registry.one`, or `null` when Dokploy says it has no such registry. */
|
|
7353
|
+
async function registryById(api, registryId) {
|
|
7354
|
+
try {
|
|
7355
|
+
return await api.getRegistry(registryId) ?? null;
|
|
7356
|
+
} catch (error) {
|
|
7357
|
+
if (error instanceof DokployApiError) return null;
|
|
7358
|
+
throw error;
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
/**
|
|
7362
|
+
* The registry id for a stage, in order: the configured id, the one in the
|
|
7363
|
+
* stage's state, the one Dokploy has for the configured registry URL, and
|
|
7364
|
+
* otherwise a new one. Never a registry the workspace did not point at.
|
|
7365
|
+
*/
|
|
7366
|
+
async function resolveRegistry(api, options) {
|
|
7367
|
+
const { stage, registry, configuredId, stateId, log = () => {} } = options;
|
|
7368
|
+
if (!registry) throw new RegistryNotConfigured(stage);
|
|
7369
|
+
if (configuredId) {
|
|
7370
|
+
const configured = await registryById(api, configuredId);
|
|
7371
|
+
if (!configured) throw new RegistryNotFound(configuredId);
|
|
7372
|
+
return configured;
|
|
7373
|
+
}
|
|
7374
|
+
if (stateId) {
|
|
7375
|
+
const known = await registryById(api, stateId);
|
|
7376
|
+
if (known) return known;
|
|
7377
|
+
log(` ⚠ The stage's registry ${stateId} no longer exists in Dokploy`);
|
|
7378
|
+
}
|
|
7379
|
+
const matches = serving(await api.listRegistries(), registry);
|
|
7380
|
+
if (matches.length > 1) throw new RegistryAmbiguous(registry, matches.map((r) => r.registryId));
|
|
7381
|
+
if (matches[0]) return matches[0];
|
|
7382
|
+
return options.create(registry);
|
|
7383
|
+
}
|
|
7384
|
+
//#endregion
|
|
7027
7385
|
//#region src/deploy/StateProvider.ts
|
|
7028
7386
|
/**
|
|
7029
7387
|
* Check if value is a StateProvider implementation.
|
|
@@ -7796,6 +8154,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7796
8154
|
apps: units
|
|
7797
8155
|
} : configured;
|
|
7798
8156
|
if (provider !== "dokploy") throw new Error(`Workspace deployment only supports Dokploy. Got: ${provider}`);
|
|
8157
|
+
const identity = deployIdentity(workspace, stage);
|
|
7799
8158
|
logger$4.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
|
|
7800
8159
|
logger$4.log(` Stage: ${stage}`);
|
|
7801
8160
|
const imageTag = tag ?? generateTag(stage);
|
|
@@ -7862,38 +8221,29 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7862
8221
|
baseUrl: creds.endpoint,
|
|
7863
8222
|
token: creds.token
|
|
7864
8223
|
});
|
|
7865
|
-
logger$4.log("\n📁 Setting up Dokploy project...");
|
|
7866
|
-
const projectName = workspace.name;
|
|
7867
|
-
let project = (await api.listProjects()).find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
7868
|
-
let environmentId;
|
|
7869
|
-
if (project) {
|
|
7870
|
-
logger$4.log(` Found existing project: ${project.name}`);
|
|
7871
|
-
const matchingEnv = ((await api.getProject(project.projectId)).environments ?? []).find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
7872
|
-
if (matchingEnv) {
|
|
7873
|
-
environmentId = matchingEnv.environmentId;
|
|
7874
|
-
logger$4.log(` Using environment: ${matchingEnv.name}`);
|
|
7875
|
-
} else {
|
|
7876
|
-
logger$4.log(` Creating "${stage}" environment...`);
|
|
7877
|
-
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
7878
|
-
logger$4.log(` ✓ Created environment: ${stage}`);
|
|
7879
|
-
}
|
|
7880
|
-
} else {
|
|
7881
|
-
logger$4.log(` Creating project: ${projectName}`);
|
|
7882
|
-
const result = await api.createProject(projectName);
|
|
7883
|
-
project = result.project;
|
|
7884
|
-
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
7885
|
-
logger$4.log(` Creating "${stage}" environment...`);
|
|
7886
|
-
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
7887
|
-
} else environmentId = result.environment.environmentId;
|
|
7888
|
-
logger$4.log(` ✓ Created project: ${project.projectId}`);
|
|
7889
|
-
}
|
|
7890
8224
|
logger$4.log("\n📋 Loading deploy state...");
|
|
7891
8225
|
const stateProvider = await createStateProvider({
|
|
7892
8226
|
config: workspace.state,
|
|
7893
8227
|
workspaceRoot: workspace.root,
|
|
7894
8228
|
workspaceName: workspace.name
|
|
7895
8229
|
});
|
|
7896
|
-
|
|
8230
|
+
const previous = await stateProvider.read(stage);
|
|
8231
|
+
logger$4.log("\n📁 Setting up Dokploy project...");
|
|
8232
|
+
logger$4.log(` Identity: ${identity.key}`);
|
|
8233
|
+
const project = await resolveProject(api, identity, previous?.projectId, (message) => logger$4.log(message));
|
|
8234
|
+
if (project.via === "created") logger$4.log(` ✓ Created project: ${project.projectId}`);
|
|
8235
|
+
else logger$4.log(` Found existing project: ${project.name}`);
|
|
8236
|
+
let environmentId;
|
|
8237
|
+
const matchingEnv = project.environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
8238
|
+
if (matchingEnv) {
|
|
8239
|
+
environmentId = matchingEnv.environmentId;
|
|
8240
|
+
logger$4.log(` Using environment: ${matchingEnv.name}`);
|
|
8241
|
+
} else {
|
|
8242
|
+
logger$4.log(` Creating "${stage}" environment...`);
|
|
8243
|
+
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
8244
|
+
logger$4.log(` ✓ Created environment: ${stage}`);
|
|
8245
|
+
}
|
|
8246
|
+
let state = previous;
|
|
7897
8247
|
if (state) {
|
|
7898
8248
|
logger$4.log(` Found existing state for stage "${stage}"`);
|
|
7899
8249
|
if (state.projectId !== project.projectId) {
|
|
@@ -7908,33 +8258,27 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7908
8258
|
logger$4.log(` Creating new state for stage "${stage}"`);
|
|
7909
8259
|
state = createEmptyState(stage, project.projectId, environmentId);
|
|
7910
8260
|
}
|
|
8261
|
+
state.identity = identity.key;
|
|
7911
8262
|
logger$4.log("\n🐳 Checking registry...");
|
|
7912
|
-
let registryId = await getDokployRegistryId();
|
|
7913
8263
|
const registry = workspace.deploy.dokploy?.registry;
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
if (!registryId) {
|
|
7923
|
-
const registries = await api.listRegistries();
|
|
7924
|
-
if (registries.length > 0) {
|
|
7925
|
-
registryId = registries[0].registryId;
|
|
7926
|
-
await storeDokployRegistryId(registryId);
|
|
7927
|
-
logger$4.log(` Using registry: ${registries[0].registryName}`);
|
|
7928
|
-
} else if (registry) {
|
|
7929
|
-
logger$4.log(" No registries found in Dokploy. Let's create one.");
|
|
7930
|
-
logger$4.log(` Registry URL: ${registry}`);
|
|
8264
|
+
const dokployRegistry = await resolveRegistry(api, {
|
|
8265
|
+
stage,
|
|
8266
|
+
registry,
|
|
8267
|
+
configuredId: workspace.deploy.dokploy?.registryId,
|
|
8268
|
+
stateId: state.registryId,
|
|
8269
|
+
log: (message) => logger$4.log(message),
|
|
8270
|
+
create: async (url) => {
|
|
8271
|
+
logger$4.log(` Dokploy has no registry for ${url}. Let's create one.`);
|
|
7931
8272
|
const username = await prompt("Registry username: ");
|
|
7932
8273
|
const password = await prompt("Registry password/token: ", true);
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
}
|
|
7937
|
-
}
|
|
8274
|
+
const created = await api.createRegistry("Default Registry", url, username, password);
|
|
8275
|
+
logger$4.log(` ✓ Registry created: ${created.registryId}`);
|
|
8276
|
+
return created;
|
|
8277
|
+
}
|
|
8278
|
+
});
|
|
8279
|
+
const registryId = dokployRegistry.registryId;
|
|
8280
|
+
state.registryId = registryId;
|
|
8281
|
+
logger$4.log(` Using registry: ${dokployRegistry.registryName}`);
|
|
7938
8282
|
const skippedMobileApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "mobile");
|
|
7939
8283
|
if (skippedMobileApps.length > 0) logger$4.log(`\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(", ")}`);
|
|
7940
8284
|
const backendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "backend");
|
|
@@ -7955,6 +8299,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7955
8299
|
projectId: project.projectId,
|
|
7956
8300
|
environmentId,
|
|
7957
8301
|
stage,
|
|
8302
|
+
scope: identity.scope,
|
|
7958
8303
|
appUrls,
|
|
7959
8304
|
supplied: stageSecrets.custom,
|
|
7960
8305
|
seed: stageSecrets.seed,
|
|
@@ -7980,7 +8325,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7980
8325
|
const backupState = await provisionBackupDestination({
|
|
7981
8326
|
api,
|
|
7982
8327
|
projectId: project.projectId,
|
|
7983
|
-
projectName:
|
|
8328
|
+
projectName: identity.scope,
|
|
7984
8329
|
stage,
|
|
7985
8330
|
config: workspace.deploy.backups,
|
|
7986
8331
|
existingState: getBackupState(state),
|
|
@@ -8021,7 +8366,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8021
8366
|
const app = workspace.apps[appName];
|
|
8022
8367
|
logger$4.log(`\n ⚙️ Deploying ${appName}...`);
|
|
8023
8368
|
try {
|
|
8024
|
-
const dokployAppName = applicationName(
|
|
8369
|
+
const dokployAppName = applicationName(identity, appName);
|
|
8025
8370
|
let application = null;
|
|
8026
8371
|
const cachedAppId = getApplicationId(state, appName);
|
|
8027
8372
|
if (cachedAppId) {
|
|
@@ -8043,20 +8388,23 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8043
8388
|
iv: appSecrets.payload.iv
|
|
8044
8389
|
} : void 0;
|
|
8045
8390
|
if (appSecrets && credentials) logger$4.log(` Encrypted ${appSecrets.secretCount} secrets`);
|
|
8046
|
-
const
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
await deployDocker({
|
|
8391
|
+
const imageRef$1 = imageRef(identity, appName, registry, imageTag);
|
|
8392
|
+
logger$4.log(` Building Docker image: ${imageRef$1}`);
|
|
8393
|
+
const built = await deployDocker({
|
|
8050
8394
|
stage,
|
|
8051
8395
|
tag: imageTag,
|
|
8052
8396
|
skipPush: false,
|
|
8053
8397
|
config: {
|
|
8054
8398
|
registry,
|
|
8055
|
-
imageName,
|
|
8399
|
+
imageName: imageName(identity, appName),
|
|
8056
8400
|
appName
|
|
8057
8401
|
},
|
|
8058
8402
|
credentials
|
|
8059
8403
|
});
|
|
8404
|
+
setDeployedImage(state, appName, {
|
|
8405
|
+
ref: built.imageRef ?? imageRef$1,
|
|
8406
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8407
|
+
});
|
|
8060
8408
|
const backendHost = resolveHost(appName, app, stage, workspace.deploy?.domains, false);
|
|
8061
8409
|
const dependencyUrls = {};
|
|
8062
8410
|
if (app.dependencies) {
|
|
@@ -8087,7 +8435,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8087
8435
|
};
|
|
8088
8436
|
const envVars = Object.entries(withDeclared).map(([key, value]) => `${key}=${value}`);
|
|
8089
8437
|
if (Object.keys(withDeclared).length > 0) logger$4.log(` Resolved ${Object.keys(withDeclared).length} env vars: ${Object.keys(withDeclared).sort().join(", ")}`);
|
|
8090
|
-
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
8438
|
+
await api.saveDockerProvider(application.applicationId, imageRef$1, { registryId });
|
|
8091
8439
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
8092
8440
|
logger$4.log(` Deploying to Dokploy...`);
|
|
8093
8441
|
await api.deployApplication(application.applicationId);
|
|
@@ -8120,7 +8468,8 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8120
8468
|
type: app.type,
|
|
8121
8469
|
success: true,
|
|
8122
8470
|
applicationId: application.applicationId,
|
|
8123
|
-
imageRef
|
|
8471
|
+
imageRef: imageRef$1,
|
|
8472
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8124
8473
|
});
|
|
8125
8474
|
logger$4.log(` ✓ ${appName} deployed successfully`);
|
|
8126
8475
|
} catch (error) {
|
|
@@ -8142,7 +8491,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8142
8491
|
const app = workspace.apps[appName];
|
|
8143
8492
|
logger$4.log(`\n 🌐 Deploying ${appName}...`);
|
|
8144
8493
|
try {
|
|
8145
|
-
const dokployAppName = applicationName(
|
|
8494
|
+
const dokployAppName = applicationName(identity, appName);
|
|
8146
8495
|
let application = null;
|
|
8147
8496
|
const cachedAppId = getApplicationId(state, appName);
|
|
8148
8497
|
if (cachedAppId) {
|
|
@@ -8187,28 +8536,31 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8187
8536
|
}
|
|
8188
8537
|
}
|
|
8189
8538
|
if (buildArgs.length > 0) logger$4.log(` Build args: ${publicUrlArgNames.join(", ")}`);
|
|
8190
|
-
const
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
await deployDocker({
|
|
8539
|
+
const imageRef$2 = imageRef(identity, appName, registry, imageTag);
|
|
8540
|
+
logger$4.log(` Building Docker image: ${imageRef$2}`);
|
|
8541
|
+
const built = await deployDocker({
|
|
8194
8542
|
stage,
|
|
8195
8543
|
tag: imageTag,
|
|
8196
8544
|
skipPush: false,
|
|
8197
8545
|
config: {
|
|
8198
8546
|
registry,
|
|
8199
|
-
imageName,
|
|
8547
|
+
imageName: imageName(identity, appName),
|
|
8200
8548
|
appName
|
|
8201
8549
|
},
|
|
8202
8550
|
buildArgs,
|
|
8203
8551
|
publicUrlArgs: publicUrlArgNames
|
|
8204
8552
|
});
|
|
8553
|
+
setDeployedImage(state, appName, {
|
|
8554
|
+
ref: built.imageRef ?? imageRef$2,
|
|
8555
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8556
|
+
});
|
|
8205
8557
|
const envVars = [
|
|
8206
8558
|
`NODE_ENV=production`,
|
|
8207
8559
|
`PORT=${app.port}`,
|
|
8208
8560
|
`STAGE=${stage}`
|
|
8209
8561
|
];
|
|
8210
8562
|
for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
|
|
8211
|
-
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
8563
|
+
await api.saveDockerProvider(application.applicationId, imageRef$2, { registryId });
|
|
8212
8564
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
8213
8565
|
logger$4.log(` Deploying to Dokploy...`);
|
|
8214
8566
|
await api.deployApplication(application.applicationId);
|
|
@@ -8241,7 +8593,8 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8241
8593
|
type: app.type,
|
|
8242
8594
|
success: true,
|
|
8243
8595
|
applicationId: application.applicationId,
|
|
8244
|
-
imageRef
|
|
8596
|
+
imageRef: imageRef$2,
|
|
8597
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8245
8598
|
});
|
|
8246
8599
|
logger$4.log(` ✓ ${appName} deployed successfully`);
|
|
8247
8600
|
} catch (error) {
|
|
@@ -8837,7 +9190,7 @@ async function createStateStore(options) {
|
|
|
8837
9190
|
}
|
|
8838
9191
|
if (config.provider === "ssm") {
|
|
8839
9192
|
if (!workspaceName) throw new StateStoreNeedsWorkspaceName("ssm");
|
|
8840
|
-
const { SSMStateStore } = await import("./SSMStateStore-
|
|
9193
|
+
const { SSMStateStore } = await import("./SSMStateStore-BjZZm-nV.mjs");
|
|
8841
9194
|
const ssm = config;
|
|
8842
9195
|
return SSMStateStore.create({
|
|
8843
9196
|
workspaceName,
|
|
@@ -10107,25 +10460,25 @@ function loadPackageJson() {
|
|
|
10107
10460
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
10108
10461
|
*/
|
|
10109
10462
|
const GEEKMIDAS_VERSIONS = {
|
|
10110
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
10111
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
10112
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
10113
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
10114
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
10115
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
10116
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
10117
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
10118
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
10119
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
10120
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
10121
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
10122
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
10123
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
10124
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
10125
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
10126
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
10127
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
10128
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
10463
|
+
"@geekmidas/audit": "~10.0.0-alpha.58",
|
|
10464
|
+
"@geekmidas/auth": "~10.0.0-alpha.58",
|
|
10465
|
+
"@geekmidas/cache": "~10.0.0-alpha.58",
|
|
10466
|
+
"@geekmidas/client": "~10.0.0-alpha.58",
|
|
10467
|
+
"@geekmidas/cloud": "~10.0.0-alpha.58",
|
|
10468
|
+
"@geekmidas/constructs": "~10.0.0-alpha.58",
|
|
10469
|
+
"@geekmidas/db": "~10.0.0-alpha.58",
|
|
10470
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.58",
|
|
10471
|
+
"@geekmidas/envkit": "~10.0.0-alpha.58",
|
|
10472
|
+
"@geekmidas/errors": "~10.0.0-alpha.58",
|
|
10473
|
+
"@geekmidas/events": "~10.0.0-alpha.58",
|
|
10474
|
+
"@geekmidas/logger": "~10.0.0-alpha.58",
|
|
10475
|
+
"@geekmidas/manifest": "~10.0.0-alpha.58",
|
|
10476
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.58",
|
|
10477
|
+
"@geekmidas/schema": "~10.0.0-alpha.58",
|
|
10478
|
+
"@geekmidas/services": "~10.0.0-alpha.58",
|
|
10479
|
+
"@geekmidas/storage": "~10.0.0-alpha.58",
|
|
10480
|
+
"@geekmidas/telescope": "~10.0.0-alpha.58",
|
|
10481
|
+
"@geekmidas/testkit": "~10.0.0-alpha.58",
|
|
10129
10482
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
10130
10483
|
};
|
|
10131
10484
|
//#endregion
|
|
@@ -16007,7 +16360,7 @@ function testDatabases(reconciled) {
|
|
|
16007
16360
|
* starts without the reference data the app needs.
|
|
16008
16361
|
*/
|
|
16009
16362
|
async function migrateTestStage(workspace, env) {
|
|
16010
|
-
const { migrateAndSeed } = await import("./migrate-
|
|
16363
|
+
const { migrateAndSeed } = await import("./migrate-CXVnh1J3.mjs");
|
|
16011
16364
|
const { discover } = await import("./discover-Cf2jAcgF.mjs").then((n) => n.n);
|
|
16012
16365
|
const sources = {};
|
|
16013
16366
|
const manifest = await discover({
|
|
@@ -16438,7 +16791,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
16438
16791
|
try {
|
|
16439
16792
|
const globalOptions = program.opts();
|
|
16440
16793
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16441
|
-
const { migrateCommand } = await import("./migrate-
|
|
16794
|
+
const { migrateCommand } = await import("./migrate-CXVnh1J3.mjs");
|
|
16442
16795
|
await migrateCommand({
|
|
16443
16796
|
...options,
|
|
16444
16797
|
...construct ? { construct } : {}
|
|
@@ -16452,7 +16805,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
16452
16805
|
try {
|
|
16453
16806
|
const globalOptions = program.opts();
|
|
16454
16807
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16455
|
-
const { seedCommand } = await import("./migrate-
|
|
16808
|
+
const { seedCommand } = await import("./migrate-CXVnh1J3.mjs");
|
|
16456
16809
|
await seedCommand({
|
|
16457
16810
|
...options,
|
|
16458
16811
|
...construct ? { construct } : {}
|
|
@@ -16466,7 +16819,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
16466
16819
|
try {
|
|
16467
16820
|
const globalOptions = program.opts();
|
|
16468
16821
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16469
|
-
const { migrationCommand } = await import("./migrate-
|
|
16822
|
+
const { migrationCommand } = await import("./migrate-CXVnh1J3.mjs");
|
|
16470
16823
|
await migrationCommand({
|
|
16471
16824
|
construct,
|
|
16472
16825
|
...name ? { name } : {}
|
|
@@ -16564,7 +16917,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
|
|
|
16564
16917
|
try {
|
|
16565
16918
|
const globalOptions = program.opts();
|
|
16566
16919
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16567
|
-
const { encryptionRotateCommand } = await import("./encryption-
|
|
16920
|
+
const { encryptionRotateCommand } = await import("./encryption-Dlnl5XEU.mjs");
|
|
16568
16921
|
await encryptionRotateCommand(construct, options);
|
|
16569
16922
|
} catch (error) {
|
|
16570
16923
|
console.error(formatError(error));
|
|
@@ -16575,7 +16928,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
|
|
|
16575
16928
|
try {
|
|
16576
16929
|
const globalOptions = program.opts();
|
|
16577
16930
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16578
|
-
const { encryptionRetireCommand } = await import("./encryption-
|
|
16931
|
+
const { encryptionRetireCommand } = await import("./encryption-Dlnl5XEU.mjs");
|
|
16579
16932
|
await encryptionRetireCommand(construct, key, options);
|
|
16580
16933
|
} catch (error) {
|
|
16581
16934
|
console.error(formatError(error));
|
|
@@ -16627,7 +16980,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16627
16980
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16628
16981
|
process.exit(1);
|
|
16629
16982
|
}
|
|
16630
|
-
const { derivedContainers } = await import("./workspace-
|
|
16983
|
+
const { derivedContainers } = await import("./workspace-D-f58jsP.mjs").then((n) => n.o);
|
|
16631
16984
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16632
16985
|
if (!result) {
|
|
16633
16986
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16686,7 +17039,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16686
17039
|
try {
|
|
16687
17040
|
const globalOptions = program.opts();
|
|
16688
17041
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16689
|
-
const { deployGithubCommand } = await import("./github-
|
|
17042
|
+
const { deployGithubCommand } = await import("./github-BdF9tsvl.mjs");
|
|
16690
17043
|
await deployGithubCommand(options);
|
|
16691
17044
|
} catch (error) {
|
|
16692
17045
|
console.error(formatError(error));
|