@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.cjs
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
4
4
|
const require_discover = require("./discover-DXNviAh-.cjs");
|
|
5
|
-
const require_config = require("./config-
|
|
6
|
-
const require_workspace = require("./workspace-
|
|
5
|
+
const require_config = require("./config-DdIu1O_I.cjs");
|
|
6
|
+
const require_workspace = require("./workspace-Bah36xSG.cjs");
|
|
7
7
|
const require_storage = require("./storage-CSGoC3bK.cjs");
|
|
8
8
|
const require_store = require("./store-DQgxT_WR.cjs");
|
|
9
|
-
const require_workspace$1 = require("./workspace-
|
|
10
|
-
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-
|
|
9
|
+
const require_workspace$1 = require("./workspace-B14L2TrY.cjs");
|
|
10
|
+
const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-BCWPRZwD.cjs");
|
|
11
11
|
const require_openapi = require("./openapi.cjs");
|
|
12
|
-
const require_ready = require("./ready-
|
|
13
|
-
const require_backends = require("./backends-
|
|
12
|
+
const require_ready = require("./ready-mR96orkJ.cjs");
|
|
13
|
+
const require_backends = require("./backends-v3xDuhO0.cjs");
|
|
14
14
|
const require_emit = require("./emit-Dmc1nvdS.cjs");
|
|
15
15
|
let commander = require("commander");
|
|
16
16
|
let node_process = require("node:process");
|
|
@@ -48,7 +48,7 @@ let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
|
|
|
48
48
|
let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
|
|
49
49
|
let node_dns_promises = require("node:dns/promises");
|
|
50
50
|
//#region package.json
|
|
51
|
-
var version = "10.0.0-alpha.
|
|
51
|
+
var version = "10.0.0-alpha.58";
|
|
52
52
|
//#endregion
|
|
53
53
|
//#region src/auth/credentials.ts
|
|
54
54
|
/**
|
|
@@ -142,15 +142,6 @@ async function getDokployToken(options) {
|
|
|
142
142
|
return null;
|
|
143
143
|
}
|
|
144
144
|
/**
|
|
145
|
-
* Store Dokploy registry ID
|
|
146
|
-
*/
|
|
147
|
-
async function storeDokployRegistryId(registryId, options) {
|
|
148
|
-
const credentials = await readCredentials(options);
|
|
149
|
-
if (!credentials.dokploy) throw new Error("Dokploy credentials not found. Run \"gkm login --service dokploy\" first.");
|
|
150
|
-
credentials.dokploy.registryId = registryId;
|
|
151
|
-
await writeCredentials(credentials, options);
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
145
|
* Get Dokploy registry ID from stored credentials
|
|
155
146
|
*/
|
|
156
147
|
async function getDokployRegistryId(options) {
|
|
@@ -1746,9 +1737,9 @@ async function workspaceDevCommand(configured, options) {
|
|
|
1746
1737
|
for (const { id, port } of fakes) logger$9.log(`🎭 ${id} fake: http://localhost:${port}`);
|
|
1747
1738
|
logger$9.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
|
|
1748
1739
|
if (options.migrate || options.seed) {
|
|
1749
|
-
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-
|
|
1740
|
+
const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs"));
|
|
1750
1741
|
await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
|
|
1751
|
-
} else await Promise.resolve().then(() => require("./migrate-
|
|
1742
|
+
} else await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
|
|
1752
1743
|
logger$9.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
|
|
1753
1744
|
});
|
|
1754
1745
|
const appUrls = {};
|
|
@@ -2774,14 +2765,34 @@ var CommandTimedOut = class extends Error {
|
|
|
2774
2765
|
* once `timeoutMs` passes (after the child is gone), and with the spawn error
|
|
2775
2766
|
* itself when the program cannot be started at all — `ENOENT` already names it.
|
|
2776
2767
|
*/
|
|
2777
|
-
function run(command, args, options = {}) {
|
|
2768
|
+
async function run(command, args, options = {}) {
|
|
2769
|
+
await spawnChild(command, args, options, false);
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* {@link run}, resolving with what the program wrote to stdout — for a value
|
|
2773
|
+
* the CLI reads back, such as the digest `docker inspect` reports. stderr still
|
|
2774
|
+
* reaches the terminal, so a failure explains itself the same way.
|
|
2775
|
+
*/
|
|
2776
|
+
function runOutput(command, args, options = {}) {
|
|
2777
|
+
return spawnChild(command, args, options, true);
|
|
2778
|
+
}
|
|
2779
|
+
function spawnChild(command, args, options, capture) {
|
|
2778
2780
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, ...spawnOptions } = options;
|
|
2779
2781
|
return new Promise((resolve, reject) => {
|
|
2780
2782
|
const child = (0, node_child_process.spawn)(command, [...args], {
|
|
2781
2783
|
stdio: "inherit",
|
|
2782
2784
|
...spawnOptions,
|
|
2785
|
+
...capture ? { stdio: [
|
|
2786
|
+
"ignore",
|
|
2787
|
+
"pipe",
|
|
2788
|
+
"inherit"
|
|
2789
|
+
] } : {},
|
|
2783
2790
|
shell: false
|
|
2784
2791
|
});
|
|
2792
|
+
let stdout = "";
|
|
2793
|
+
child.stdout?.on("data", (chunk) => {
|
|
2794
|
+
stdout += chunk.toString();
|
|
2795
|
+
});
|
|
2785
2796
|
let timedOut = false;
|
|
2786
2797
|
let killer;
|
|
2787
2798
|
const timer = setTimeout(() => {
|
|
@@ -2802,7 +2813,7 @@ function run(command, args, options = {}) {
|
|
|
2802
2813
|
child.on("close", (code, signal) => {
|
|
2803
2814
|
settle();
|
|
2804
2815
|
if (timedOut) reject(new CommandTimedOut(command, args, timeoutMs));
|
|
2805
|
-
else if (code === 0) resolve();
|
|
2816
|
+
else if (code === 0) resolve(stdout);
|
|
2806
2817
|
else reject(new CommandFailed(command, args, code, signal));
|
|
2807
2818
|
});
|
|
2808
2819
|
});
|
|
@@ -3109,7 +3120,8 @@ async function buildOneApp(input) {
|
|
|
3109
3120
|
hooks,
|
|
3110
3121
|
markOptional: options.markOptional ?? false,
|
|
3111
3122
|
skipBundle: options.skipBundle ?? false,
|
|
3112
|
-
stage: options.stage
|
|
3123
|
+
stage: options.stage,
|
|
3124
|
+
workspaceName: workspace.name
|
|
3113
3125
|
});
|
|
3114
3126
|
}
|
|
3115
3127
|
/**
|
|
@@ -3187,7 +3199,12 @@ async function buildApp(input) {
|
|
|
3187
3199
|
appRoot,
|
|
3188
3200
|
cache: require_ready.cacheBackendsIn(declared, cacheBackend)
|
|
3189
3201
|
}),
|
|
3190
|
-
markOptional: input.markOptional ?? false
|
|
3202
|
+
markOptional: input.markOptional ?? false,
|
|
3203
|
+
...production && { telemetry: require_OpenApiTsGenerator.telemetryFor({
|
|
3204
|
+
appRoot,
|
|
3205
|
+
surfaceId: derived.surface?.id,
|
|
3206
|
+
workspaceName: input.workspaceName
|
|
3207
|
+
}) }
|
|
3191
3208
|
};
|
|
3192
3209
|
const endpointGenerator = new require_OpenApiTsGenerator.EndpointGenerator();
|
|
3193
3210
|
const functionGenerator = new FunctionGenerator();
|
|
@@ -3596,7 +3613,7 @@ const PROVISIONERS = {
|
|
|
3596
3613
|
const name = require_workspace$1.resourceName(declaration.id, declaration.kind, context.stage);
|
|
3597
3614
|
const { postgres } = await context.api.findOrCreatePostgres(serviceName({
|
|
3598
3615
|
stage: context.stage,
|
|
3599
|
-
app: context.project
|
|
3616
|
+
app: context.scope ?? context.project
|
|
3600
3617
|
}, declaration.id), context.projectId, context.environmentId, {
|
|
3601
3618
|
databaseName: name,
|
|
3602
3619
|
databasePassword: derivedPassword(context, `${name}:master`)
|
|
@@ -3730,7 +3747,7 @@ const PROVISIONERS = {
|
|
|
3730
3747
|
if (backend !== "minio") throw new UnprovisionableBucket(declaration.id, backend);
|
|
3731
3748
|
const service = serviceName({
|
|
3732
3749
|
stage: context.stage,
|
|
3733
|
-
app: context.project
|
|
3750
|
+
app: context.scope ?? context.project
|
|
3734
3751
|
}, declaration.id);
|
|
3735
3752
|
const bucket = require_workspace$1.resourceName(declaration.id, declaration.kind, context.stage);
|
|
3736
3753
|
const user = `${bucket}-root`;
|
|
@@ -4191,6 +4208,7 @@ async function provisionDeclared(options) {
|
|
|
4191
4208
|
environmentId: options.environmentId,
|
|
4192
4209
|
stage: options.stage,
|
|
4193
4210
|
project: workspace.name,
|
|
4211
|
+
scope: options.scope ?? workspace.name,
|
|
4194
4212
|
cache: require_backends.cacheBackendFor(require_backends.providerOf(workspace)),
|
|
4195
4213
|
events: require_backends.eventsBackendFor(require_backends.providerOf(workspace)),
|
|
4196
4214
|
storage: require_backends.storageBackendFor(require_backends.providerOf(workspace)),
|
|
@@ -4307,6 +4325,15 @@ function setApplicationId(state, appName, applicationId) {
|
|
|
4307
4325
|
state.applications[appName] = applicationId;
|
|
4308
4326
|
}
|
|
4309
4327
|
/**
|
|
4328
|
+
* Record the image an app was deployed with (mutates state)
|
|
4329
|
+
*/
|
|
4330
|
+
function setDeployedImage(state, appName, image) {
|
|
4331
|
+
state.images = {
|
|
4332
|
+
...state.images,
|
|
4333
|
+
[appName]: image
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
/**
|
|
4310
4337
|
* Get a generated secret for an app
|
|
4311
4338
|
*/
|
|
4312
4339
|
function getGeneratedSecret(state, appName, secretName) {
|
|
@@ -6084,6 +6111,33 @@ async function pushImage(imageRef) {
|
|
|
6084
6111
|
}
|
|
6085
6112
|
}
|
|
6086
6113
|
/**
|
|
6114
|
+
* The digest the registry gave a pushed image, `sha256:…`.
|
|
6115
|
+
*
|
|
6116
|
+
* A tag can be pushed over — by the next deploy, or by anything else that
|
|
6117
|
+
* shares the repository — and the digest is what says which image a stage
|
|
6118
|
+
* actually ran. `RepoDigests` has one entry per repository the image was
|
|
6119
|
+
* pushed to, so the one for this ref's repository is picked by name.
|
|
6120
|
+
*
|
|
6121
|
+
* @internal Exported for testing
|
|
6122
|
+
*/
|
|
6123
|
+
async function pushedDigest(imageRef) {
|
|
6124
|
+
const repository = imageRef.split("@")[0].replace(/:[\w][\w.-]*$/, "");
|
|
6125
|
+
const output = await runOutput("docker", [
|
|
6126
|
+
"image",
|
|
6127
|
+
"inspect",
|
|
6128
|
+
"--format={{json .RepoDigests}}",
|
|
6129
|
+
imageRef
|
|
6130
|
+
], { cwd: process.cwd() });
|
|
6131
|
+
let digests;
|
|
6132
|
+
try {
|
|
6133
|
+
digests = JSON.parse(output.trim() || "[]");
|
|
6134
|
+
} catch {
|
|
6135
|
+
return;
|
|
6136
|
+
}
|
|
6137
|
+
if (!Array.isArray(digests)) return void 0;
|
|
6138
|
+
return digests.find((entry) => typeof entry === "string" && entry.startsWith(`${repository}@`))?.slice(repository.length + 1);
|
|
6139
|
+
}
|
|
6140
|
+
/**
|
|
6087
6141
|
* Deploy using Docker (build and optionally push image)
|
|
6088
6142
|
*/
|
|
6089
6143
|
async function deployDocker(options) {
|
|
@@ -6091,9 +6145,13 @@ async function deployDocker(options) {
|
|
|
6091
6145
|
const imageName = config.imageName;
|
|
6092
6146
|
const imageRef = validateImageRef(getImageRef(config.registry, imageName, tag));
|
|
6093
6147
|
await buildImage(imageRef, buildArgs, credentials);
|
|
6148
|
+
let digest;
|
|
6094
6149
|
if (!skipPush) {
|
|
6095
6150
|
if (!config.registry) logger$5.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
6096
|
-
else
|
|
6151
|
+
else {
|
|
6152
|
+
await pushImage(imageRef);
|
|
6153
|
+
digest = await pushedDigest(imageRef);
|
|
6154
|
+
}
|
|
6097
6155
|
}
|
|
6098
6156
|
logger$5.log("\n✅ Docker deployment ready!");
|
|
6099
6157
|
logger$5.log(`\n📋 Deployment details:`);
|
|
@@ -6102,25 +6160,10 @@ async function deployDocker(options) {
|
|
|
6102
6160
|
if (masterKey) logger$5.log(`\n🔐 Run the container with GKM_MASTER_KEY set to the key with fingerprint ${keyFingerprint(masterKey)}.`);
|
|
6103
6161
|
return {
|
|
6104
6162
|
imageRef,
|
|
6163
|
+
...digest ? { digest } : {},
|
|
6105
6164
|
masterKey
|
|
6106
6165
|
};
|
|
6107
6166
|
}
|
|
6108
|
-
/**
|
|
6109
|
-
* What one application is called on a provider.
|
|
6110
|
-
*
|
|
6111
|
-
* The same rule the constructs use, through the same `scopedName`: the
|
|
6112
|
-
* application beside `production-kitchen-sink-database` is
|
|
6113
|
-
* `production-kitchen-sink-api`, not `api`. Both deploy paths call this — the
|
|
6114
|
-
* workspace one named its applications by the bare app key, so a project held
|
|
6115
|
-
* an `api` and a `web` that every stage would collide on.
|
|
6116
|
-
*
|
|
6117
|
-
* The app id is dropped when it repeats the project, so a project named for its
|
|
6118
|
-
* one application is `production-kitchen-sink` rather than
|
|
6119
|
-
* `production-kitchen-sink-kitchen-sink`.
|
|
6120
|
-
*/
|
|
6121
|
-
function applicationName(stage, project, app) {
|
|
6122
|
-
return app === project ? `${stage}-${project}`.toLowerCase() : (0, _geekmidas_manifest.scopedName)([stage, project], app);
|
|
6123
|
-
}
|
|
6124
6167
|
//#endregion
|
|
6125
6168
|
//#region src/deploy/dokploy-api.ts
|
|
6126
6169
|
var dokploy_api_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
|
|
@@ -6337,6 +6380,18 @@ var DokployApi = class {
|
|
|
6337
6380
|
});
|
|
6338
6381
|
}
|
|
6339
6382
|
/**
|
|
6383
|
+
* Change a project's name or description.
|
|
6384
|
+
*
|
|
6385
|
+
* The name is sent with the description because `project.update` validates
|
|
6386
|
+
* the whole project on some Dokploy versions, and a missing name fails it.
|
|
6387
|
+
*/
|
|
6388
|
+
async updateProject(projectId, updates) {
|
|
6389
|
+
await this.post("project.update", {
|
|
6390
|
+
projectId,
|
|
6391
|
+
...updates
|
|
6392
|
+
});
|
|
6393
|
+
}
|
|
6394
|
+
/**
|
|
6340
6395
|
* Delete a project and all its resources
|
|
6341
6396
|
*/
|
|
6342
6397
|
async deleteProject(projectId) {
|
|
@@ -7036,6 +7091,309 @@ function random() {
|
|
|
7036
7091
|
return (0, node_crypto.randomBytes)(32).toString("base64url");
|
|
7037
7092
|
}
|
|
7038
7093
|
//#endregion
|
|
7094
|
+
//#region src/deploy/identity.ts
|
|
7095
|
+
/**
|
|
7096
|
+
* Who a deploy is, on a target it shares with other workspaces.
|
|
7097
|
+
*
|
|
7098
|
+
* A Dokploy server is one flat namespace of projects, images and Docker
|
|
7099
|
+
* service names. Deploys used to identify everything they made by the
|
|
7100
|
+
* workspace name alone, so two workspaces called `shop` — or `Shop` and
|
|
7101
|
+
* `shop` — deploying to one server landed in the same project, pushed over
|
|
7102
|
+
* each other's images and redeployed each other's applications. A
|
|
7103
|
+
* `DeployIdentity` is what a deploy names and claims its resources by instead.
|
|
7104
|
+
*/
|
|
7105
|
+
/** A namespace that cannot be part of a Dokploy name or an image path. */
|
|
7106
|
+
var DeployNamespaceInvalid = class extends Error {
|
|
7107
|
+
namespace;
|
|
7108
|
+
constructor(namespace) {
|
|
7109
|
+
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.`);
|
|
7110
|
+
this.namespace = namespace;
|
|
7111
|
+
this.name = "DeployNamespaceInvalid";
|
|
7112
|
+
}
|
|
7113
|
+
};
|
|
7114
|
+
/** A workspace name with nothing a target can name a resource by. */
|
|
7115
|
+
var DeployProjectNameInvalid = class extends Error {
|
|
7116
|
+
workspaceName;
|
|
7117
|
+
constructor(workspaceName) {
|
|
7118
|
+
super(`The workspace name '${workspaceName}' has no letters or digits to name deployed resources by. Set "name" in gkm.config.ts.`);
|
|
7119
|
+
this.workspaceName = workspaceName;
|
|
7120
|
+
this.name = "DeployProjectNameInvalid";
|
|
7121
|
+
}
|
|
7122
|
+
};
|
|
7123
|
+
const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7124
|
+
/**
|
|
7125
|
+
* Lowercase `[a-z0-9-]`, runs of anything else collapsed to one `-`: valid as
|
|
7126
|
+
* a Docker image path component, a Docker service name and a Dokploy name.
|
|
7127
|
+
*/
|
|
7128
|
+
function slug$1(value) {
|
|
7129
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7130
|
+
}
|
|
7131
|
+
/** The namespace a workspace is in when it does not name one. */
|
|
7132
|
+
function defaultNamespace(workspaceName) {
|
|
7133
|
+
return slug$1((0, _geekmidas_manifest.kebabCase)(workspaceName));
|
|
7134
|
+
}
|
|
7135
|
+
function deployIdentity(workspace, stage) {
|
|
7136
|
+
const project = slug$1(workspace.name);
|
|
7137
|
+
if (!project) throw new DeployProjectNameInvalid(workspace.name);
|
|
7138
|
+
const fallback = defaultNamespace(workspace.name);
|
|
7139
|
+
const namespace = workspace.deploy?.namespace ?? fallback;
|
|
7140
|
+
if (!NAME.test(namespace)) throw new DeployNamespaceInvalid(namespace);
|
|
7141
|
+
return {
|
|
7142
|
+
namespace,
|
|
7143
|
+
project,
|
|
7144
|
+
stage,
|
|
7145
|
+
key: `${namespace}/${project}`,
|
|
7146
|
+
scope: namespace === fallback ? project : `${namespace}-${project}`
|
|
7147
|
+
};
|
|
7148
|
+
}
|
|
7149
|
+
/**
|
|
7150
|
+
* The token a Dokploy project's description carries to say which identity
|
|
7151
|
+
* created it. A project with the right name and no marker belongs to someone
|
|
7152
|
+
* else, and a deploy never adopts it.
|
|
7153
|
+
*/
|
|
7154
|
+
function ownershipMarker(identity) {
|
|
7155
|
+
return `gkm:${identity.key}`;
|
|
7156
|
+
}
|
|
7157
|
+
/** Whether a project description carries `identity`'s marker. */
|
|
7158
|
+
function isOwnedBy(description, identity) {
|
|
7159
|
+
const marker = ownershipMarker(identity);
|
|
7160
|
+
return (description ?? "").split(/\s+/).includes(marker);
|
|
7161
|
+
}
|
|
7162
|
+
/** Any `gkm:` marker a description carries — whoever's it is. */
|
|
7163
|
+
function markerOf(description) {
|
|
7164
|
+
return (description ?? "").split(/\s+/).find((t) => t.startsWith("gkm:"));
|
|
7165
|
+
}
|
|
7166
|
+
/** What the Dokploy project is called. */
|
|
7167
|
+
function projectName(identity) {
|
|
7168
|
+
return identity.scope;
|
|
7169
|
+
}
|
|
7170
|
+
/**
|
|
7171
|
+
* The repository an app's image is pushed to, below the registry:
|
|
7172
|
+
* `<namespace>/<project>-<app>`.
|
|
7173
|
+
*
|
|
7174
|
+
* The namespace is a path segment of its own so that a registry that maps
|
|
7175
|
+
* paths to owners (an organisation's packages, a Harbor project) can grant it
|
|
7176
|
+
* as one; the app is joined to the project so the two read as one name.
|
|
7177
|
+
*/
|
|
7178
|
+
function imageName(identity, app) {
|
|
7179
|
+
return `${identity.namespace}/${identity.project}-${slug$1((0, _geekmidas_manifest.kebabCase)(app))}`;
|
|
7180
|
+
}
|
|
7181
|
+
/** The full image ref an app is built, pushed and deployed as. */
|
|
7182
|
+
function imageRef(identity, app, registry, tag) {
|
|
7183
|
+
const name = imageName(identity, app);
|
|
7184
|
+
return registry ? `${registry.replace(/\/+$/, "")}/${name}:${tag}` : `${name}:${tag}`;
|
|
7185
|
+
}
|
|
7186
|
+
/**
|
|
7187
|
+
* What one application is called on the target.
|
|
7188
|
+
*
|
|
7189
|
+
* The same rule the constructs use, through the same `scopedName`, scoped by
|
|
7190
|
+
* the stage and the identity: the application beside
|
|
7191
|
+
* `production-kitchen-sink-database` is `production-kitchen-sink-api`. A
|
|
7192
|
+
* Dokploy application's name is also its Docker service name, which is unique
|
|
7193
|
+
* on the whole server, so the namespace has to be in it too — `acme`'s and
|
|
7194
|
+
* `globex`'s `shop` would otherwise both run `production-shop-api`.
|
|
7195
|
+
*
|
|
7196
|
+
* The app id is dropped when it repeats the project, so a project named for its
|
|
7197
|
+
* one application is `production-kitchen-sink` rather than
|
|
7198
|
+
* `production-kitchen-sink-kitchen-sink`.
|
|
7199
|
+
*/
|
|
7200
|
+
function applicationName(identity, app) {
|
|
7201
|
+
return slug$1(app) === identity.project ? `${identity.stage}-${identity.scope}` : (0, _geekmidas_manifest.scopedName)([identity.stage, identity.scope], app);
|
|
7202
|
+
}
|
|
7203
|
+
//#endregion
|
|
7204
|
+
//#region src/deploy/ownership.ts
|
|
7205
|
+
/**
|
|
7206
|
+
* Which Dokploy project a deploy is allowed to deploy into.
|
|
7207
|
+
*
|
|
7208
|
+
* Deploy used to take any project whose name matched the workspace's, in any
|
|
7209
|
+
* case — so a second workspace called `shop` (or `Shop`) on the same server
|
|
7210
|
+
* deployed into the first one's project, over its images and applications. A
|
|
7211
|
+
* project is now claimed by an ownership marker in its description, and a
|
|
7212
|
+
* project with the right name and no marker is somebody else's.
|
|
7213
|
+
*/
|
|
7214
|
+
/**
|
|
7215
|
+
* A Dokploy project with the name this deploy would use exists, and nothing
|
|
7216
|
+
* says this workspace created it.
|
|
7217
|
+
*/
|
|
7218
|
+
var ProjectNotOwned = class extends Error {
|
|
7219
|
+
projectName;
|
|
7220
|
+
projectId;
|
|
7221
|
+
identity;
|
|
7222
|
+
ownedBy;
|
|
7223
|
+
constructor(projectName, projectId, identity, ownedBy) {
|
|
7224
|
+
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.`);
|
|
7225
|
+
this.projectName = projectName;
|
|
7226
|
+
this.projectId = projectId;
|
|
7227
|
+
this.identity = identity;
|
|
7228
|
+
this.ownedBy = ownedBy;
|
|
7229
|
+
this.name = "ProjectNotOwned";
|
|
7230
|
+
}
|
|
7231
|
+
};
|
|
7232
|
+
/** A description with the marker added, keeping what was there. */
|
|
7233
|
+
function withMarker(description, marker) {
|
|
7234
|
+
const existing = (description ?? "").trim();
|
|
7235
|
+
return existing ? `${existing}\n${marker}` : marker;
|
|
7236
|
+
}
|
|
7237
|
+
/** `project.one`, or `null` when Dokploy no longer has the project. */
|
|
7238
|
+
async function projectById(api, projectId) {
|
|
7239
|
+
try {
|
|
7240
|
+
return await api.getProject(projectId) ?? null;
|
|
7241
|
+
} catch (error) {
|
|
7242
|
+
if (error instanceof DokployApiError) return null;
|
|
7243
|
+
throw error;
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
/**
|
|
7247
|
+
* The project this deploy deploys into:
|
|
7248
|
+
*
|
|
7249
|
+
* 1. The one the stage's state names, if Dokploy still has it. State written
|
|
7250
|
+
* before markers existed is trusted — it holds an id this workspace was
|
|
7251
|
+
* given when it created the project — and the marker is written now, so
|
|
7252
|
+
* the project is claimed for the next deploy that has no state.
|
|
7253
|
+
* 2. One with this name *and* this identity's marker.
|
|
7254
|
+
* 3. A new one, marked.
|
|
7255
|
+
*
|
|
7256
|
+
* A project that matches by name (in any case) but carries no marker, or
|
|
7257
|
+
* another identity's, raises `ProjectNotOwned` — it is never adopted.
|
|
7258
|
+
*/
|
|
7259
|
+
async function resolveProject(api, identity, stateProjectId, log = () => {}) {
|
|
7260
|
+
const name = projectName(identity);
|
|
7261
|
+
const marker = ownershipMarker(identity);
|
|
7262
|
+
if (stateProjectId) {
|
|
7263
|
+
const known = await projectById(api, stateProjectId);
|
|
7264
|
+
if (known) {
|
|
7265
|
+
const claimed = markerOf(known.description);
|
|
7266
|
+
if (claimed && claimed !== marker) throw new ProjectNotOwned(known.name, known.projectId, identity.key, claimed);
|
|
7267
|
+
if (!claimed) {
|
|
7268
|
+
await api.updateProject(known.projectId, {
|
|
7269
|
+
name: known.name,
|
|
7270
|
+
description: withMarker(known.description, marker)
|
|
7271
|
+
});
|
|
7272
|
+
log(` Claimed project ${known.name} for ${identity.key}`);
|
|
7273
|
+
}
|
|
7274
|
+
return {
|
|
7275
|
+
projectId: known.projectId,
|
|
7276
|
+
name: known.name,
|
|
7277
|
+
environments: known.environments ?? [],
|
|
7278
|
+
via: "state"
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
}
|
|
7282
|
+
const projects = await api.listProjects();
|
|
7283
|
+
const owned = projects.find((p) => p.name.toLowerCase() === name.toLowerCase() && isOwnedBy(p.description, identity));
|
|
7284
|
+
if (owned) {
|
|
7285
|
+
const details = await api.getProject(owned.projectId);
|
|
7286
|
+
return {
|
|
7287
|
+
projectId: owned.projectId,
|
|
7288
|
+
name: owned.name,
|
|
7289
|
+
environments: details.environments ?? [],
|
|
7290
|
+
via: "marker"
|
|
7291
|
+
};
|
|
7292
|
+
}
|
|
7293
|
+
const clash = projects.find((p) => p.name.toLowerCase() === name.toLowerCase());
|
|
7294
|
+
if (clash) throw new ProjectNotOwned(clash.name, clash.projectId, identity.key, markerOf(clash.description));
|
|
7295
|
+
const created = await api.createProject(name, withMarker("Deployed by gkm.", marker));
|
|
7296
|
+
return {
|
|
7297
|
+
projectId: created.project.projectId,
|
|
7298
|
+
name: created.project.name,
|
|
7299
|
+
environments: [created.environment],
|
|
7300
|
+
via: "created"
|
|
7301
|
+
};
|
|
7302
|
+
}
|
|
7303
|
+
//#endregion
|
|
7304
|
+
//#region src/deploy/registry.ts
|
|
7305
|
+
/**
|
|
7306
|
+
* Which Dokploy registry a stage's images are pulled through.
|
|
7307
|
+
*
|
|
7308
|
+
* It used to be one id stored per machine (`~/.gkm/credentials.json`), and
|
|
7309
|
+
* when that was empty, the first registry Dokploy listed — so an image could be
|
|
7310
|
+
* deployed with credentials for a registry nobody chose, and two workspaces
|
|
7311
|
+
* deployed from one machine shared whichever was stored last. The id is now
|
|
7312
|
+
* kept in the stage's state, and found only from what the workspace
|
|
7313
|
+
* configured.
|
|
7314
|
+
*/
|
|
7315
|
+
/** There is nowhere to push images to, so Dokploy has nothing to pull. */
|
|
7316
|
+
var RegistryNotConfigured = class extends Error {
|
|
7317
|
+
stage;
|
|
7318
|
+
constructor(stage) {
|
|
7319
|
+
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.`);
|
|
7320
|
+
this.stage = stage;
|
|
7321
|
+
this.name = "RegistryNotConfigured";
|
|
7322
|
+
}
|
|
7323
|
+
};
|
|
7324
|
+
/** `deploy.dokploy.registryId` names a registry Dokploy does not have. */
|
|
7325
|
+
var RegistryNotFound = class extends Error {
|
|
7326
|
+
registryId;
|
|
7327
|
+
constructor(registryId) {
|
|
7328
|
+
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.`);
|
|
7329
|
+
this.registryId = registryId;
|
|
7330
|
+
this.name = "RegistryNotFound";
|
|
7331
|
+
}
|
|
7332
|
+
};
|
|
7333
|
+
/** More than one Dokploy registry serves the configured registry. */
|
|
7334
|
+
var RegistryAmbiguous = class extends Error {
|
|
7335
|
+
registry;
|
|
7336
|
+
registryIds;
|
|
7337
|
+
constructor(registry, registryIds) {
|
|
7338
|
+
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.`);
|
|
7339
|
+
this.registry = registry;
|
|
7340
|
+
this.registryIds = registryIds;
|
|
7341
|
+
this.name = "RegistryAmbiguous";
|
|
7342
|
+
}
|
|
7343
|
+
};
|
|
7344
|
+
/** `https://ghcr.io/acme/` → `{ host: 'ghcr.io', path: 'acme' }`. */
|
|
7345
|
+
function parts(url) {
|
|
7346
|
+
const [host = "", ...path] = url.replace(/^[a-z]+:\/\//i, "").replace(/\/+$/, "").toLowerCase().split("/");
|
|
7347
|
+
return {
|
|
7348
|
+
host,
|
|
7349
|
+
path: path.join("/")
|
|
7350
|
+
};
|
|
7351
|
+
}
|
|
7352
|
+
/**
|
|
7353
|
+
* The Dokploy registries that serve `registry`: the same host, and — where
|
|
7354
|
+
* Dokploy's own entry names one — the same path.
|
|
7355
|
+
*/
|
|
7356
|
+
function serving(registries, registry) {
|
|
7357
|
+
const wanted = parts(registry);
|
|
7358
|
+
const sameHost = registries.filter((r) => parts(r.registryUrl ?? "").host === wanted.host);
|
|
7359
|
+
if (sameHost.length <= 1) return sameHost;
|
|
7360
|
+
return sameHost.filter((r) => {
|
|
7361
|
+
return (parts(r.registryUrl ?? "").path || (r.imagePrefix ?? "").toLowerCase()) === wanted.path;
|
|
7362
|
+
});
|
|
7363
|
+
}
|
|
7364
|
+
/** `registry.one`, or `null` when Dokploy says it has no such registry. */
|
|
7365
|
+
async function registryById(api, registryId) {
|
|
7366
|
+
try {
|
|
7367
|
+
return await api.getRegistry(registryId) ?? null;
|
|
7368
|
+
} catch (error) {
|
|
7369
|
+
if (error instanceof DokployApiError) return null;
|
|
7370
|
+
throw error;
|
|
7371
|
+
}
|
|
7372
|
+
}
|
|
7373
|
+
/**
|
|
7374
|
+
* The registry id for a stage, in order: the configured id, the one in the
|
|
7375
|
+
* stage's state, the one Dokploy has for the configured registry URL, and
|
|
7376
|
+
* otherwise a new one. Never a registry the workspace did not point at.
|
|
7377
|
+
*/
|
|
7378
|
+
async function resolveRegistry(api, options) {
|
|
7379
|
+
const { stage, registry, configuredId, stateId, log = () => {} } = options;
|
|
7380
|
+
if (!registry) throw new RegistryNotConfigured(stage);
|
|
7381
|
+
if (configuredId) {
|
|
7382
|
+
const configured = await registryById(api, configuredId);
|
|
7383
|
+
if (!configured) throw new RegistryNotFound(configuredId);
|
|
7384
|
+
return configured;
|
|
7385
|
+
}
|
|
7386
|
+
if (stateId) {
|
|
7387
|
+
const known = await registryById(api, stateId);
|
|
7388
|
+
if (known) return known;
|
|
7389
|
+
log(` ⚠ The stage's registry ${stateId} no longer exists in Dokploy`);
|
|
7390
|
+
}
|
|
7391
|
+
const matches = serving(await api.listRegistries(), registry);
|
|
7392
|
+
if (matches.length > 1) throw new RegistryAmbiguous(registry, matches.map((r) => r.registryId));
|
|
7393
|
+
if (matches[0]) return matches[0];
|
|
7394
|
+
return options.create(registry);
|
|
7395
|
+
}
|
|
7396
|
+
//#endregion
|
|
7039
7397
|
//#region src/deploy/StateProvider.ts
|
|
7040
7398
|
/**
|
|
7041
7399
|
* Check if value is a StateProvider implementation.
|
|
@@ -7808,6 +8166,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7808
8166
|
apps: units
|
|
7809
8167
|
} : configured;
|
|
7810
8168
|
if (provider !== "dokploy") throw new Error(`Workspace deployment only supports Dokploy. Got: ${provider}`);
|
|
8169
|
+
const identity = deployIdentity(workspace, stage);
|
|
7811
8170
|
logger$4.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
|
|
7812
8171
|
logger$4.log(` Stage: ${stage}`);
|
|
7813
8172
|
const imageTag = tag ?? generateTag(stage);
|
|
@@ -7874,38 +8233,29 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7874
8233
|
baseUrl: creds.endpoint,
|
|
7875
8234
|
token: creds.token
|
|
7876
8235
|
});
|
|
7877
|
-
logger$4.log("\n📁 Setting up Dokploy project...");
|
|
7878
|
-
const projectName = workspace.name;
|
|
7879
|
-
let project = (await api.listProjects()).find((p) => p.name.toLowerCase() === projectName.toLowerCase());
|
|
7880
|
-
let environmentId;
|
|
7881
|
-
if (project) {
|
|
7882
|
-
logger$4.log(` Found existing project: ${project.name}`);
|
|
7883
|
-
const matchingEnv = ((await api.getProject(project.projectId)).environments ?? []).find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
7884
|
-
if (matchingEnv) {
|
|
7885
|
-
environmentId = matchingEnv.environmentId;
|
|
7886
|
-
logger$4.log(` Using environment: ${matchingEnv.name}`);
|
|
7887
|
-
} else {
|
|
7888
|
-
logger$4.log(` Creating "${stage}" environment...`);
|
|
7889
|
-
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
7890
|
-
logger$4.log(` ✓ Created environment: ${stage}`);
|
|
7891
|
-
}
|
|
7892
|
-
} else {
|
|
7893
|
-
logger$4.log(` Creating project: ${projectName}`);
|
|
7894
|
-
const result = await api.createProject(projectName);
|
|
7895
|
-
project = result.project;
|
|
7896
|
-
if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
|
|
7897
|
-
logger$4.log(` Creating "${stage}" environment...`);
|
|
7898
|
-
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
7899
|
-
} else environmentId = result.environment.environmentId;
|
|
7900
|
-
logger$4.log(` ✓ Created project: ${project.projectId}`);
|
|
7901
|
-
}
|
|
7902
8236
|
logger$4.log("\n📋 Loading deploy state...");
|
|
7903
8237
|
const stateProvider = await createStateProvider({
|
|
7904
8238
|
config: workspace.state,
|
|
7905
8239
|
workspaceRoot: workspace.root,
|
|
7906
8240
|
workspaceName: workspace.name
|
|
7907
8241
|
});
|
|
7908
|
-
|
|
8242
|
+
const previous = await stateProvider.read(stage);
|
|
8243
|
+
logger$4.log("\n📁 Setting up Dokploy project...");
|
|
8244
|
+
logger$4.log(` Identity: ${identity.key}`);
|
|
8245
|
+
const project = await resolveProject(api, identity, previous?.projectId, (message) => logger$4.log(message));
|
|
8246
|
+
if (project.via === "created") logger$4.log(` ✓ Created project: ${project.projectId}`);
|
|
8247
|
+
else logger$4.log(` Found existing project: ${project.name}`);
|
|
8248
|
+
let environmentId;
|
|
8249
|
+
const matchingEnv = project.environments.find((e) => e.name.toLowerCase() === stage.toLowerCase());
|
|
8250
|
+
if (matchingEnv) {
|
|
8251
|
+
environmentId = matchingEnv.environmentId;
|
|
8252
|
+
logger$4.log(` Using environment: ${matchingEnv.name}`);
|
|
8253
|
+
} else {
|
|
8254
|
+
logger$4.log(` Creating "${stage}" environment...`);
|
|
8255
|
+
environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
|
|
8256
|
+
logger$4.log(` ✓ Created environment: ${stage}`);
|
|
8257
|
+
}
|
|
8258
|
+
let state = previous;
|
|
7909
8259
|
if (state) {
|
|
7910
8260
|
logger$4.log(` Found existing state for stage "${stage}"`);
|
|
7911
8261
|
if (state.projectId !== project.projectId) {
|
|
@@ -7920,33 +8270,27 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7920
8270
|
logger$4.log(` Creating new state for stage "${stage}"`);
|
|
7921
8271
|
state = createEmptyState(stage, project.projectId, environmentId);
|
|
7922
8272
|
}
|
|
8273
|
+
state.identity = identity.key;
|
|
7923
8274
|
logger$4.log("\n🐳 Checking registry...");
|
|
7924
|
-
let registryId = await getDokployRegistryId();
|
|
7925
8275
|
const registry = workspace.deploy.dokploy?.registry;
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
if (!registryId) {
|
|
7935
|
-
const registries = await api.listRegistries();
|
|
7936
|
-
if (registries.length > 0) {
|
|
7937
|
-
registryId = registries[0].registryId;
|
|
7938
|
-
await storeDokployRegistryId(registryId);
|
|
7939
|
-
logger$4.log(` Using registry: ${registries[0].registryName}`);
|
|
7940
|
-
} else if (registry) {
|
|
7941
|
-
logger$4.log(" No registries found in Dokploy. Let's create one.");
|
|
7942
|
-
logger$4.log(` Registry URL: ${registry}`);
|
|
8276
|
+
const dokployRegistry = await resolveRegistry(api, {
|
|
8277
|
+
stage,
|
|
8278
|
+
registry,
|
|
8279
|
+
configuredId: workspace.deploy.dokploy?.registryId,
|
|
8280
|
+
stateId: state.registryId,
|
|
8281
|
+
log: (message) => logger$4.log(message),
|
|
8282
|
+
create: async (url) => {
|
|
8283
|
+
logger$4.log(` Dokploy has no registry for ${url}. Let's create one.`);
|
|
7943
8284
|
const username = await prompt("Registry username: ");
|
|
7944
8285
|
const password = await prompt("Registry password/token: ", true);
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
}
|
|
7949
|
-
}
|
|
8286
|
+
const created = await api.createRegistry("Default Registry", url, username, password);
|
|
8287
|
+
logger$4.log(` ✓ Registry created: ${created.registryId}`);
|
|
8288
|
+
return created;
|
|
8289
|
+
}
|
|
8290
|
+
});
|
|
8291
|
+
const registryId = dokployRegistry.registryId;
|
|
8292
|
+
state.registryId = registryId;
|
|
8293
|
+
logger$4.log(` Using registry: ${dokployRegistry.registryName}`);
|
|
7950
8294
|
const skippedMobileApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "mobile");
|
|
7951
8295
|
if (skippedMobileApps.length > 0) logger$4.log(`\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(", ")}`);
|
|
7952
8296
|
const backendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "backend");
|
|
@@ -7967,6 +8311,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7967
8311
|
projectId: project.projectId,
|
|
7968
8312
|
environmentId,
|
|
7969
8313
|
stage,
|
|
8314
|
+
scope: identity.scope,
|
|
7970
8315
|
appUrls,
|
|
7971
8316
|
supplied: stageSecrets.custom,
|
|
7972
8317
|
seed: stageSecrets.seed,
|
|
@@ -7992,7 +8337,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
7992
8337
|
const backupState = await provisionBackupDestination({
|
|
7993
8338
|
api,
|
|
7994
8339
|
projectId: project.projectId,
|
|
7995
|
-
projectName:
|
|
8340
|
+
projectName: identity.scope,
|
|
7996
8341
|
stage,
|
|
7997
8342
|
config: workspace.deploy.backups,
|
|
7998
8343
|
existingState: getBackupState(state),
|
|
@@ -8033,7 +8378,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8033
8378
|
const app = workspace.apps[appName];
|
|
8034
8379
|
logger$4.log(`\n ⚙️ Deploying ${appName}...`);
|
|
8035
8380
|
try {
|
|
8036
|
-
const dokployAppName = applicationName(
|
|
8381
|
+
const dokployAppName = applicationName(identity, appName);
|
|
8037
8382
|
let application = null;
|
|
8038
8383
|
const cachedAppId = getApplicationId(state, appName);
|
|
8039
8384
|
if (cachedAppId) {
|
|
@@ -8055,20 +8400,23 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8055
8400
|
iv: appSecrets.payload.iv
|
|
8056
8401
|
} : void 0;
|
|
8057
8402
|
if (appSecrets && credentials) logger$4.log(` Encrypted ${appSecrets.secretCount} secrets`);
|
|
8058
|
-
const
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
await deployDocker({
|
|
8403
|
+
const imageRef$1 = imageRef(identity, appName, registry, imageTag);
|
|
8404
|
+
logger$4.log(` Building Docker image: ${imageRef$1}`);
|
|
8405
|
+
const built = await deployDocker({
|
|
8062
8406
|
stage,
|
|
8063
8407
|
tag: imageTag,
|
|
8064
8408
|
skipPush: false,
|
|
8065
8409
|
config: {
|
|
8066
8410
|
registry,
|
|
8067
|
-
imageName,
|
|
8411
|
+
imageName: imageName(identity, appName),
|
|
8068
8412
|
appName
|
|
8069
8413
|
},
|
|
8070
8414
|
credentials
|
|
8071
8415
|
});
|
|
8416
|
+
setDeployedImage(state, appName, {
|
|
8417
|
+
ref: built.imageRef ?? imageRef$1,
|
|
8418
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8419
|
+
});
|
|
8072
8420
|
const backendHost = resolveHost(appName, app, stage, workspace.deploy?.domains, false);
|
|
8073
8421
|
const dependencyUrls = {};
|
|
8074
8422
|
if (app.dependencies) {
|
|
@@ -8099,7 +8447,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8099
8447
|
};
|
|
8100
8448
|
const envVars = Object.entries(withDeclared).map(([key, value]) => `${key}=${value}`);
|
|
8101
8449
|
if (Object.keys(withDeclared).length > 0) logger$4.log(` Resolved ${Object.keys(withDeclared).length} env vars: ${Object.keys(withDeclared).sort().join(", ")}`);
|
|
8102
|
-
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
8450
|
+
await api.saveDockerProvider(application.applicationId, imageRef$1, { registryId });
|
|
8103
8451
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
8104
8452
|
logger$4.log(` Deploying to Dokploy...`);
|
|
8105
8453
|
await api.deployApplication(application.applicationId);
|
|
@@ -8132,7 +8480,8 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8132
8480
|
type: app.type,
|
|
8133
8481
|
success: true,
|
|
8134
8482
|
applicationId: application.applicationId,
|
|
8135
|
-
imageRef
|
|
8483
|
+
imageRef: imageRef$1,
|
|
8484
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8136
8485
|
});
|
|
8137
8486
|
logger$4.log(` ✓ ${appName} deployed successfully`);
|
|
8138
8487
|
} catch (error) {
|
|
@@ -8154,7 +8503,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8154
8503
|
const app = workspace.apps[appName];
|
|
8155
8504
|
logger$4.log(`\n 🌐 Deploying ${appName}...`);
|
|
8156
8505
|
try {
|
|
8157
|
-
const dokployAppName = applicationName(
|
|
8506
|
+
const dokployAppName = applicationName(identity, appName);
|
|
8158
8507
|
let application = null;
|
|
8159
8508
|
const cachedAppId = getApplicationId(state, appName);
|
|
8160
8509
|
if (cachedAppId) {
|
|
@@ -8199,28 +8548,31 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8199
8548
|
}
|
|
8200
8549
|
}
|
|
8201
8550
|
if (buildArgs.length > 0) logger$4.log(` Build args: ${publicUrlArgNames.join(", ")}`);
|
|
8202
|
-
const
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
await deployDocker({
|
|
8551
|
+
const imageRef$2 = imageRef(identity, appName, registry, imageTag);
|
|
8552
|
+
logger$4.log(` Building Docker image: ${imageRef$2}`);
|
|
8553
|
+
const built = await deployDocker({
|
|
8206
8554
|
stage,
|
|
8207
8555
|
tag: imageTag,
|
|
8208
8556
|
skipPush: false,
|
|
8209
8557
|
config: {
|
|
8210
8558
|
registry,
|
|
8211
|
-
imageName,
|
|
8559
|
+
imageName: imageName(identity, appName),
|
|
8212
8560
|
appName
|
|
8213
8561
|
},
|
|
8214
8562
|
buildArgs,
|
|
8215
8563
|
publicUrlArgs: publicUrlArgNames
|
|
8216
8564
|
});
|
|
8565
|
+
setDeployedImage(state, appName, {
|
|
8566
|
+
ref: built.imageRef ?? imageRef$2,
|
|
8567
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8568
|
+
});
|
|
8217
8569
|
const envVars = [
|
|
8218
8570
|
`NODE_ENV=production`,
|
|
8219
8571
|
`PORT=${app.port}`,
|
|
8220
8572
|
`STAGE=${stage}`
|
|
8221
8573
|
];
|
|
8222
8574
|
for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
|
|
8223
|
-
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
8575
|
+
await api.saveDockerProvider(application.applicationId, imageRef$2, { registryId });
|
|
8224
8576
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
8225
8577
|
logger$4.log(` Deploying to Dokploy...`);
|
|
8226
8578
|
await api.deployApplication(application.applicationId);
|
|
@@ -8253,7 +8605,8 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
8253
8605
|
type: app.type,
|
|
8254
8606
|
success: true,
|
|
8255
8607
|
applicationId: application.applicationId,
|
|
8256
|
-
imageRef
|
|
8608
|
+
imageRef: imageRef$2,
|
|
8609
|
+
...built.digest ? { digest: built.digest } : {}
|
|
8257
8610
|
});
|
|
8258
8611
|
logger$4.log(` ✓ ${appName} deployed successfully`);
|
|
8259
8612
|
} catch (error) {
|
|
@@ -8849,7 +9202,7 @@ async function createStateStore(options) {
|
|
|
8849
9202
|
}
|
|
8850
9203
|
if (config.provider === "ssm") {
|
|
8851
9204
|
if (!workspaceName) throw new StateStoreNeedsWorkspaceName("ssm");
|
|
8852
|
-
const { SSMStateStore } = await Promise.resolve().then(() => require("./SSMStateStore-
|
|
9205
|
+
const { SSMStateStore } = await Promise.resolve().then(() => require("./SSMStateStore-DC43R7zG.cjs"));
|
|
8853
9206
|
const ssm = config;
|
|
8854
9207
|
return SSMStateStore.create({
|
|
8855
9208
|
workspaceName,
|
|
@@ -10119,25 +10472,25 @@ function loadPackageJson() {
|
|
|
10119
10472
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
10120
10473
|
*/
|
|
10121
10474
|
const GEEKMIDAS_VERSIONS = {
|
|
10122
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
10123
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
10124
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
10125
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
10126
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
10127
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
10128
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
10129
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
10130
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
10131
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
10132
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
10133
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
10134
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
10135
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
10136
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
10137
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
10138
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
10139
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
10140
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
10475
|
+
"@geekmidas/audit": "~10.0.0-alpha.58",
|
|
10476
|
+
"@geekmidas/auth": "~10.0.0-alpha.58",
|
|
10477
|
+
"@geekmidas/cache": "~10.0.0-alpha.58",
|
|
10478
|
+
"@geekmidas/client": "~10.0.0-alpha.58",
|
|
10479
|
+
"@geekmidas/cloud": "~10.0.0-alpha.58",
|
|
10480
|
+
"@geekmidas/constructs": "~10.0.0-alpha.58",
|
|
10481
|
+
"@geekmidas/db": "~10.0.0-alpha.58",
|
|
10482
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.58",
|
|
10483
|
+
"@geekmidas/envkit": "~10.0.0-alpha.58",
|
|
10484
|
+
"@geekmidas/errors": "~10.0.0-alpha.58",
|
|
10485
|
+
"@geekmidas/events": "~10.0.0-alpha.58",
|
|
10486
|
+
"@geekmidas/logger": "~10.0.0-alpha.58",
|
|
10487
|
+
"@geekmidas/manifest": "~10.0.0-alpha.58",
|
|
10488
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.58",
|
|
10489
|
+
"@geekmidas/schema": "~10.0.0-alpha.58",
|
|
10490
|
+
"@geekmidas/services": "~10.0.0-alpha.58",
|
|
10491
|
+
"@geekmidas/storage": "~10.0.0-alpha.58",
|
|
10492
|
+
"@geekmidas/telescope": "~10.0.0-alpha.58",
|
|
10493
|
+
"@geekmidas/testkit": "~10.0.0-alpha.58",
|
|
10141
10494
|
"@geekmidas/cli": `~${loadPackageJson().version}`
|
|
10142
10495
|
};
|
|
10143
10496
|
//#endregion
|
|
@@ -16019,7 +16372,7 @@ function testDatabases(reconciled) {
|
|
|
16019
16372
|
* starts without the reference data the app needs.
|
|
16020
16373
|
*/
|
|
16021
16374
|
async function migrateTestStage(workspace, env) {
|
|
16022
|
-
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-
|
|
16375
|
+
const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs"));
|
|
16023
16376
|
const { discover } = await Promise.resolve().then(() => require("./discover-DXNviAh-.cjs")).then((n) => n.discover_exports);
|
|
16024
16377
|
const sources = {};
|
|
16025
16378
|
const manifest = await discover({
|
|
@@ -16450,7 +16803,7 @@ program.command("migrate").description("Apply each database construct's migratio
|
|
|
16450
16803
|
try {
|
|
16451
16804
|
const globalOptions = program.opts();
|
|
16452
16805
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16453
|
-
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16806
|
+
const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs"));
|
|
16454
16807
|
await migrateCommand({
|
|
16455
16808
|
...options,
|
|
16456
16809
|
...construct ? { construct } : {}
|
|
@@ -16464,7 +16817,7 @@ program.command("seed").description("Migrate, then run each database construct's
|
|
|
16464
16817
|
try {
|
|
16465
16818
|
const globalOptions = program.opts();
|
|
16466
16819
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16467
|
-
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16820
|
+
const { seedCommand } = await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs"));
|
|
16468
16821
|
await seedCommand({
|
|
16469
16822
|
...options,
|
|
16470
16823
|
...construct ? { construct } : {}
|
|
@@ -16478,7 +16831,7 @@ program.command("migration").description("Write a construct's next migration: an
|
|
|
16478
16831
|
try {
|
|
16479
16832
|
const globalOptions = program.opts();
|
|
16480
16833
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16481
|
-
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-
|
|
16834
|
+
const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-DzYqA-e3.cjs"));
|
|
16482
16835
|
await migrationCommand({
|
|
16483
16836
|
construct,
|
|
16484
16837
|
...name ? { name } : {}
|
|
@@ -16576,7 +16929,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
|
|
|
16576
16929
|
try {
|
|
16577
16930
|
const globalOptions = program.opts();
|
|
16578
16931
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16579
|
-
const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-
|
|
16932
|
+
const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-BspqzZIb.cjs"));
|
|
16580
16933
|
await encryptionRotateCommand(construct, options);
|
|
16581
16934
|
} catch (error) {
|
|
16582
16935
|
console.error(formatError(error));
|
|
@@ -16587,7 +16940,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
|
|
|
16587
16940
|
try {
|
|
16588
16941
|
const globalOptions = program.opts();
|
|
16589
16942
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16590
|
-
const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-
|
|
16943
|
+
const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-BspqzZIb.cjs"));
|
|
16591
16944
|
await encryptionRetireCommand(construct, key, options);
|
|
16592
16945
|
} catch (error) {
|
|
16593
16946
|
console.error(formatError(error));
|
|
@@ -16639,7 +16992,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
16639
16992
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
16640
16993
|
process.exit(1);
|
|
16641
16994
|
}
|
|
16642
|
-
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-
|
|
16995
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-B14L2TrY.cjs")).then((n) => n.workspace_exports);
|
|
16643
16996
|
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
16644
16997
|
if (!result) {
|
|
16645
16998
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
@@ -16698,7 +17051,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
16698
17051
|
try {
|
|
16699
17052
|
const globalOptions = program.opts();
|
|
16700
17053
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
16701
|
-
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-
|
|
17054
|
+
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-mQorSS36.cjs"));
|
|
16702
17055
|
await deployGithubCommand(options);
|
|
16703
17056
|
} catch (error) {
|
|
16704
17057
|
console.error(formatError(error));
|