@geekmidas/cli 0.44.0 → 0.46.0
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/{config-C0b0jdmU.mjs → config-C3LSBNSl.mjs} +2 -2
- package/dist/{config-C0b0jdmU.mjs.map → config-C3LSBNSl.mjs.map} +1 -1
- package/dist/{config-xVZsRjN7.cjs → config-HYiM3iQJ.cjs} +2 -2
- package/dist/{config-xVZsRjN7.cjs.map → config-HYiM3iQJ.cjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +2 -2
- package/dist/dokploy-api-C1JgU9Vr.mjs +3 -0
- package/dist/dokploy-api-Cpq_tLSz.cjs +3 -0
- package/dist/{dokploy-api-BdxOMH_V.cjs → dokploy-api-D8a0eQQB.cjs} +110 -1
- package/dist/dokploy-api-D8a0eQQB.cjs.map +1 -0
- package/dist/{dokploy-api-DWsqNjwP.mjs → dokploy-api-b6usLLKk.mjs} +110 -1
- package/dist/dokploy-api-b6usLLKk.mjs.map +1 -0
- package/dist/{index-CXa3odEw.d.mts → index-BtnjoghR.d.mts} +540 -46
- package/dist/index-BtnjoghR.d.mts.map +1 -0
- package/dist/{index-E8Nu2Rxl.d.cts → index-c89X2mi2.d.cts} +540 -46
- package/dist/index-c89X2mi2.d.cts.map +1 -0
- package/dist/index.cjs +254 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +254 -131
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-D3pA6FfZ.mjs → openapi-C3C-BzIZ.mjs} +2 -2
- package/dist/{openapi-D3pA6FfZ.mjs.map → openapi-C3C-BzIZ.mjs.map} +1 -1
- package/dist/{openapi-DhcCtKzM.cjs → openapi-D7WwlpPF.cjs} +2 -2
- package/dist/{openapi-DhcCtKzM.cjs.map → openapi-D7WwlpPF.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.mjs +3 -3
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +1 -1
- package/dist/workspace/index.d.mts +1 -1
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-BDAhr6Kb.cjs → workspace-CaVW6j2q.cjs} +10 -1
- package/dist/{workspace-BDAhr6Kb.cjs.map → workspace-CaVW6j2q.cjs.map} +1 -1
- package/dist/{workspace-D_6ZCaR_.mjs → workspace-DLFRaDc-.mjs} +10 -1
- package/dist/{workspace-D_6ZCaR_.mjs.map → workspace-DLFRaDc-.mjs.map} +1 -1
- package/package.json +4 -4
- package/src/deploy/dokploy-api.ts +163 -0
- package/src/deploy/index.ts +313 -225
- package/src/deploy/state.ts +146 -0
- package/src/workspace/types.ts +566 -47
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/dokploy-api-Bdmk5ImW.cjs +0 -3
- package/dist/dokploy-api-BdxOMH_V.cjs.map +0 -1
- package/dist/dokploy-api-DWsqNjwP.mjs.map +0 -1
- package/dist/dokploy-api-tZSZaHd9.mjs +0 -3
- package/dist/index-CXa3odEw.d.mts.map +0 -1
- package/dist/index-E8Nu2Rxl.d.cts.map +0 -1
package/src/deploy/index.ts
CHANGED
|
@@ -18,7 +18,23 @@ import {
|
|
|
18
18
|
import type { NormalizedWorkspace } from '../workspace/types.js';
|
|
19
19
|
import { deployDocker, resolveDockerConfig } from './docker';
|
|
20
20
|
import { deployDokploy } from './dokploy';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
DokployApi,
|
|
23
|
+
type DokployApplication,
|
|
24
|
+
type DokployPostgres,
|
|
25
|
+
type DokployRedis,
|
|
26
|
+
} from './dokploy-api';
|
|
27
|
+
import {
|
|
28
|
+
createEmptyState,
|
|
29
|
+
getApplicationId,
|
|
30
|
+
getPostgresId,
|
|
31
|
+
getRedisId,
|
|
32
|
+
readStageState,
|
|
33
|
+
setApplicationId,
|
|
34
|
+
setPostgresId,
|
|
35
|
+
setRedisId,
|
|
36
|
+
writeStageState,
|
|
37
|
+
} from './state.js';
|
|
22
38
|
import {
|
|
23
39
|
generatePublicUrlBuildArgs,
|
|
24
40
|
getPublicUrlArgNames,
|
|
@@ -121,6 +137,17 @@ interface DokploySetupResult {
|
|
|
121
137
|
serviceUrls?: ServiceUrls;
|
|
122
138
|
}
|
|
123
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Result from provisioning services
|
|
142
|
+
*/
|
|
143
|
+
export interface ProvisionServicesResult {
|
|
144
|
+
serviceUrls: ServiceUrls;
|
|
145
|
+
serviceIds: {
|
|
146
|
+
postgresId?: string;
|
|
147
|
+
redisId?: string;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
124
151
|
/**
|
|
125
152
|
* Provision docker compose services in Dokploy
|
|
126
153
|
* @internal Exported for testing
|
|
@@ -131,8 +158,8 @@ export async function provisionServices(
|
|
|
131
158
|
environmentId: string | undefined,
|
|
132
159
|
appName: string,
|
|
133
160
|
services?: DockerComposeServices,
|
|
134
|
-
|
|
135
|
-
): Promise<
|
|
161
|
+
existingServiceIds?: { postgresId?: string; redisId?: string },
|
|
162
|
+
): Promise<ProvisionServicesResult | undefined> {
|
|
136
163
|
logger.log(
|
|
137
164
|
`\n🔍 provisionServices called: services=${JSON.stringify(services)}, envId=${environmentId}`,
|
|
138
165
|
);
|
|
@@ -142,113 +169,142 @@ export async function provisionServices(
|
|
|
142
169
|
}
|
|
143
170
|
|
|
144
171
|
const serviceUrls: ServiceUrls = {};
|
|
172
|
+
const serviceIds: { postgresId?: string; redisId?: string } = {};
|
|
145
173
|
|
|
146
174
|
if (services.postgres) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
logger.log('\n🐘 PostgreSQL: Already configured (skipping)');
|
|
150
|
-
} else {
|
|
151
|
-
logger.log('\n🐘 Provisioning PostgreSQL...');
|
|
152
|
-
const postgresName = `${appName}-db`;
|
|
175
|
+
logger.log('\n🐘 Checking PostgreSQL...');
|
|
176
|
+
const postgresName = 'db';
|
|
153
177
|
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
try {
|
|
179
|
+
let postgres: DokployPostgres | null = null;
|
|
180
|
+
let created = false;
|
|
181
|
+
|
|
182
|
+
// Check if we have an existing ID from state
|
|
183
|
+
if (existingServiceIds?.postgresId) {
|
|
184
|
+
logger.log(` Using cached ID: ${existingServiceIds.postgresId}`);
|
|
185
|
+
postgres = await api.getPostgres(existingServiceIds.postgresId);
|
|
186
|
+
if (postgres) {
|
|
187
|
+
logger.log(` ✓ PostgreSQL found: ${postgres.postgresId}`);
|
|
188
|
+
} else {
|
|
189
|
+
logger.log(` ⚠ Cached ID invalid, will create new`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// If not found by ID, use findOrCreate
|
|
194
|
+
if (!postgres) {
|
|
156
195
|
const { randomBytes } = await import('node:crypto');
|
|
157
196
|
const databasePassword = randomBytes(16).toString('hex');
|
|
158
197
|
|
|
159
|
-
const
|
|
198
|
+
const result = await api.findOrCreatePostgres(
|
|
160
199
|
postgresName,
|
|
161
200
|
projectId,
|
|
162
201
|
environmentId,
|
|
163
202
|
{ databasePassword },
|
|
164
203
|
);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
serviceUrls.DATABASE_NAME = postgres.databaseName;
|
|
175
|
-
serviceUrls.DATABASE_USER = postgres.databaseUser;
|
|
176
|
-
serviceUrls.DATABASE_PASSWORD = postgres.databasePassword;
|
|
177
|
-
|
|
178
|
-
// Construct connection URL using internal docker network hostname
|
|
179
|
-
serviceUrls.DATABASE_URL = `postgresql://${postgres.databaseUser}:${postgres.databasePassword}@${postgres.appName}:5432/${postgres.databaseName}`;
|
|
180
|
-
logger.log(` ✓ Database credentials configured`);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
const message =
|
|
183
|
-
error instanceof Error ? error.message : 'Unknown error';
|
|
184
|
-
if (
|
|
185
|
-
message.includes('already exists') ||
|
|
186
|
-
message.includes('duplicate')
|
|
187
|
-
) {
|
|
188
|
-
logger.log(` ℹ PostgreSQL already exists`);
|
|
204
|
+
postgres = result.postgres;
|
|
205
|
+
created = result.created;
|
|
206
|
+
|
|
207
|
+
if (created) {
|
|
208
|
+
logger.log(` ✓ Created PostgreSQL: ${postgres.postgresId}`);
|
|
209
|
+
|
|
210
|
+
// Deploy the database (only for new instances)
|
|
211
|
+
await api.deployPostgres(postgres.postgresId);
|
|
212
|
+
logger.log(' ✓ PostgreSQL deployed');
|
|
189
213
|
} else {
|
|
190
|
-
logger.log(`
|
|
214
|
+
logger.log(` ✓ PostgreSQL already exists: ${postgres.postgresId}`);
|
|
191
215
|
}
|
|
192
216
|
}
|
|
217
|
+
|
|
218
|
+
// Store the ID for state
|
|
219
|
+
serviceIds.postgresId = postgres.postgresId;
|
|
220
|
+
|
|
221
|
+
// Store individual connection parameters
|
|
222
|
+
serviceUrls.DATABASE_HOST = postgres.appName;
|
|
223
|
+
serviceUrls.DATABASE_PORT = '5432';
|
|
224
|
+
serviceUrls.DATABASE_NAME = postgres.databaseName;
|
|
225
|
+
serviceUrls.DATABASE_USER = postgres.databaseUser;
|
|
226
|
+
serviceUrls.DATABASE_PASSWORD = postgres.databasePassword;
|
|
227
|
+
|
|
228
|
+
// Construct connection URL using internal docker network hostname
|
|
229
|
+
serviceUrls.DATABASE_URL = `postgresql://${postgres.databaseUser}:${postgres.databasePassword}@${postgres.appName}:5432/${postgres.databaseName}`;
|
|
230
|
+
logger.log(` ✓ Database credentials configured`);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const message =
|
|
233
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
234
|
+
logger.log(` ⚠ Failed to provision PostgreSQL: ${message}`);
|
|
193
235
|
}
|
|
194
236
|
}
|
|
195
237
|
|
|
196
238
|
if (services.redis) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
logger.log('\n🔴 Redis: Already configured (skipping)');
|
|
200
|
-
} else {
|
|
201
|
-
logger.log('\n🔴 Provisioning Redis...');
|
|
202
|
-
const redisName = `${appName}-cache`;
|
|
239
|
+
logger.log('\n🔴 Checking Redis...');
|
|
240
|
+
const redisName = 'cache';
|
|
203
241
|
|
|
204
|
-
|
|
205
|
-
|
|
242
|
+
try {
|
|
243
|
+
let redis: DokployRedis | null = null;
|
|
244
|
+
let created = false;
|
|
245
|
+
|
|
246
|
+
// Check if we have an existing ID from state
|
|
247
|
+
if (existingServiceIds?.redisId) {
|
|
248
|
+
logger.log(` Using cached ID: ${existingServiceIds.redisId}`);
|
|
249
|
+
redis = await api.getRedis(existingServiceIds.redisId);
|
|
250
|
+
if (redis) {
|
|
251
|
+
logger.log(` ✓ Redis found: ${redis.redisId}`);
|
|
252
|
+
} else {
|
|
253
|
+
logger.log(` ⚠ Cached ID invalid, will create new`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// If not found by ID, use findOrCreate
|
|
258
|
+
if (!redis) {
|
|
206
259
|
const { randomBytes } = await import('node:crypto');
|
|
207
260
|
const databasePassword = randomBytes(16).toString('hex');
|
|
208
261
|
|
|
209
|
-
const
|
|
262
|
+
const result = await api.findOrCreateRedis(
|
|
210
263
|
redisName,
|
|
211
264
|
projectId,
|
|
212
265
|
environmentId,
|
|
213
|
-
{
|
|
214
|
-
databasePassword,
|
|
215
|
-
},
|
|
266
|
+
{ databasePassword },
|
|
216
267
|
);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// Deploy the redis instance
|
|
220
|
-
await api.deployRedis(redis.redisId);
|
|
221
|
-
logger.log(' ✓ Redis deployed');
|
|
268
|
+
redis = result.redis;
|
|
269
|
+
created = result.created;
|
|
222
270
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
serviceUrls.REDIS_PORT = '6379';
|
|
226
|
-
if (redis.databasePassword) {
|
|
227
|
-
serviceUrls.REDIS_PASSWORD = redis.databasePassword;
|
|
228
|
-
}
|
|
271
|
+
if (created) {
|
|
272
|
+
logger.log(` ✓ Created Redis: ${redis.redisId}`);
|
|
229
273
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
: '';
|
|
234
|
-
serviceUrls.REDIS_URL = `redis://${password}${redis.appName}:6379`;
|
|
235
|
-
logger.log(` ✓ Redis credentials configured`);
|
|
236
|
-
} catch (error) {
|
|
237
|
-
const message =
|
|
238
|
-
error instanceof Error ? error.message : 'Unknown error';
|
|
239
|
-
if (
|
|
240
|
-
message.includes('already exists') ||
|
|
241
|
-
message.includes('duplicate')
|
|
242
|
-
) {
|
|
243
|
-
logger.log(` ℹ Redis already exists`);
|
|
274
|
+
// Deploy the redis instance (only for new instances)
|
|
275
|
+
await api.deployRedis(redis.redisId);
|
|
276
|
+
logger.log(' ✓ Redis deployed');
|
|
244
277
|
} else {
|
|
245
|
-
logger.log(`
|
|
278
|
+
logger.log(` ✓ Redis already exists: ${redis.redisId}`);
|
|
246
279
|
}
|
|
247
280
|
}
|
|
281
|
+
|
|
282
|
+
// Store the ID for state
|
|
283
|
+
serviceIds.redisId = redis.redisId;
|
|
284
|
+
|
|
285
|
+
// Store individual connection parameters
|
|
286
|
+
serviceUrls.REDIS_HOST = redis.appName;
|
|
287
|
+
serviceUrls.REDIS_PORT = '6379';
|
|
288
|
+
if (redis.databasePassword) {
|
|
289
|
+
serviceUrls.REDIS_PASSWORD = redis.databasePassword;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Construct connection URL
|
|
293
|
+
const password = redis.databasePassword
|
|
294
|
+
? `:${redis.databasePassword}@`
|
|
295
|
+
: '';
|
|
296
|
+
serviceUrls.REDIS_URL = `redis://${password}${redis.appName}:6379`;
|
|
297
|
+
logger.log(` ✓ Redis credentials configured`);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
const message =
|
|
300
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
301
|
+
logger.log(` ⚠ Failed to provision Redis: ${message}`);
|
|
248
302
|
}
|
|
249
303
|
}
|
|
250
304
|
|
|
251
|
-
return Object.keys(serviceUrls).length > 0
|
|
305
|
+
return Object.keys(serviceUrls).length > 0
|
|
306
|
+
? { serviceUrls, serviceIds }
|
|
307
|
+
: undefined;
|
|
252
308
|
}
|
|
253
309
|
|
|
254
310
|
/**
|
|
@@ -345,13 +401,14 @@ async function ensureDokploySetup(
|
|
|
345
401
|
logger.log(
|
|
346
402
|
` Services config: ${JSON.stringify(services)}, envId: ${environmentId}`,
|
|
347
403
|
);
|
|
348
|
-
|
|
404
|
+
// For single-app mode, we don't have state persistence yet, so pass undefined
|
|
405
|
+
const provisionResult = await provisionServices(
|
|
349
406
|
api,
|
|
350
407
|
existingConfig.projectId,
|
|
351
408
|
environmentId,
|
|
352
409
|
dockerConfig.appName!,
|
|
353
410
|
services,
|
|
354
|
-
|
|
411
|
+
undefined, // No state in single-app mode
|
|
355
412
|
);
|
|
356
413
|
|
|
357
414
|
return {
|
|
@@ -362,7 +419,7 @@ async function ensureDokploySetup(
|
|
|
362
419
|
registry: existingConfig.registry,
|
|
363
420
|
registryId: storedRegistryId ?? undefined,
|
|
364
421
|
},
|
|
365
|
-
serviceUrls,
|
|
422
|
+
serviceUrls: provisionResult?.serviceUrls,
|
|
366
423
|
};
|
|
367
424
|
} catch {
|
|
368
425
|
logger.log('⚠ Project not found, will recover...');
|
|
@@ -546,18 +603,19 @@ async function ensureDokploySetup(
|
|
|
546
603
|
}
|
|
547
604
|
|
|
548
605
|
// Step 8: Provision docker compose services if configured
|
|
549
|
-
|
|
606
|
+
// For single-app mode, we don't have state persistence yet, so pass undefined
|
|
607
|
+
const provisionResult = await provisionServices(
|
|
550
608
|
api,
|
|
551
609
|
project.projectId,
|
|
552
610
|
environmentId,
|
|
553
611
|
dockerConfig.appName!,
|
|
554
612
|
services,
|
|
555
|
-
|
|
613
|
+
undefined, // No state in single-app mode
|
|
556
614
|
);
|
|
557
615
|
|
|
558
616
|
return {
|
|
559
617
|
config: dokployConfig,
|
|
560
|
-
serviceUrls,
|
|
618
|
+
serviceUrls: provisionResult?.serviceUrls,
|
|
561
619
|
};
|
|
562
620
|
}
|
|
563
621
|
|
|
@@ -754,6 +812,24 @@ export async function workspaceDeployCommand(
|
|
|
754
812
|
logger.log(` ✓ Created project: ${project.projectId}`);
|
|
755
813
|
}
|
|
756
814
|
|
|
815
|
+
// ==================================================================
|
|
816
|
+
// STATE: Load or create deploy state for this stage
|
|
817
|
+
// ==================================================================
|
|
818
|
+
logger.log('\n📋 Loading deploy state...');
|
|
819
|
+
let state = await readStageState(workspace.root, stage);
|
|
820
|
+
|
|
821
|
+
if (state) {
|
|
822
|
+
logger.log(` Found existing state for stage "${stage}"`);
|
|
823
|
+
// Verify environment ID matches (in case of recreation)
|
|
824
|
+
if (state.environmentId !== environmentId) {
|
|
825
|
+
logger.log(` ⚠ Environment ID changed, updating state`);
|
|
826
|
+
state.environmentId = environmentId;
|
|
827
|
+
}
|
|
828
|
+
} else {
|
|
829
|
+
logger.log(` Creating new state for stage "${stage}"`);
|
|
830
|
+
state = createEmptyState(stage, environmentId);
|
|
831
|
+
}
|
|
832
|
+
|
|
757
833
|
// Get or set up registry
|
|
758
834
|
logger.log('\n🐳 Checking registry...');
|
|
759
835
|
let registryId = await getDokployRegistryId();
|
|
@@ -808,13 +884,30 @@ export async function workspaceDeployCommand(
|
|
|
808
884
|
|
|
809
885
|
if (dockerServices.postgres || dockerServices.redis) {
|
|
810
886
|
logger.log('\n🔧 Provisioning infrastructure services...');
|
|
811
|
-
|
|
887
|
+
// Pass existing service IDs from state (prefer state over URL sniffing)
|
|
888
|
+
const existingServiceIds = {
|
|
889
|
+
postgresId: getPostgresId(state),
|
|
890
|
+
redisId: getRedisId(state),
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
const provisionResult = await provisionServices(
|
|
812
894
|
api,
|
|
813
895
|
project.projectId,
|
|
814
896
|
environmentId,
|
|
815
897
|
workspace.name,
|
|
816
898
|
dockerServices,
|
|
899
|
+
existingServiceIds,
|
|
817
900
|
);
|
|
901
|
+
|
|
902
|
+
// Update state with returned service IDs
|
|
903
|
+
if (provisionResult?.serviceIds) {
|
|
904
|
+
if (provisionResult.serviceIds.postgresId) {
|
|
905
|
+
setPostgresId(state, provisionResult.serviceIds.postgresId);
|
|
906
|
+
}
|
|
907
|
+
if (provisionResult.serviceIds.redisId) {
|
|
908
|
+
setRedisId(state, provisionResult.serviceIds.redisId);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
818
911
|
}
|
|
819
912
|
|
|
820
913
|
// ==================================================================
|
|
@@ -844,30 +937,42 @@ export async function workspaceDeployCommand(
|
|
|
844
937
|
logger.log(`\n ⚙️ Deploying ${appName}...`);
|
|
845
938
|
|
|
846
939
|
try {
|
|
847
|
-
|
|
848
|
-
|
|
940
|
+
// Use simple app name - project already provides namespace
|
|
941
|
+
const dokployAppName = appName;
|
|
942
|
+
|
|
943
|
+
// Check state for cached application ID
|
|
944
|
+
let application: DokployApplication | null = null;
|
|
945
|
+
const cachedAppId = getApplicationId(state, appName);
|
|
946
|
+
|
|
947
|
+
if (cachedAppId) {
|
|
948
|
+
logger.log(` Using cached ID: ${cachedAppId}`);
|
|
949
|
+
application = await api.getApplication(cachedAppId);
|
|
950
|
+
if (application) {
|
|
951
|
+
logger.log(` ✓ Application found: ${application.applicationId}`);
|
|
952
|
+
} else {
|
|
953
|
+
logger.log(` ⚠ Cached ID invalid, will create new`);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
849
956
|
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
|
|
957
|
+
// If not found by ID, use findOrCreate
|
|
958
|
+
if (!application) {
|
|
959
|
+
const result = await api.findOrCreateApplication(
|
|
853
960
|
dokployAppName,
|
|
854
961
|
project.projectId,
|
|
855
962
|
environmentId,
|
|
856
963
|
);
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
if (
|
|
862
|
-
message.includes('already exists') ||
|
|
863
|
-
message.includes('duplicate')
|
|
864
|
-
) {
|
|
865
|
-
logger.log(` Application already exists`);
|
|
964
|
+
application = result.application;
|
|
965
|
+
|
|
966
|
+
if (result.created) {
|
|
967
|
+
logger.log(` Created application: ${application.applicationId}`);
|
|
866
968
|
} else {
|
|
867
|
-
|
|
969
|
+
logger.log(` Found existing application: ${application.applicationId}`);
|
|
868
970
|
}
|
|
869
971
|
}
|
|
870
972
|
|
|
973
|
+
// Store application ID in state
|
|
974
|
+
setApplicationId(state, appName, application.applicationId);
|
|
975
|
+
|
|
871
976
|
// Get encrypted secrets for this app
|
|
872
977
|
const appSecrets = encryptedSecrets.get(appName);
|
|
873
978
|
const buildArgs: string[] = [];
|
|
@@ -909,70 +1014,55 @@ export async function workspaceDeployCommand(
|
|
|
909
1014
|
}
|
|
910
1015
|
|
|
911
1016
|
// Configure and deploy application in Dokploy
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
});
|
|
1017
|
+
await api.saveDockerProvider(application.applicationId, imageRef, {
|
|
1018
|
+
registryId,
|
|
1019
|
+
});
|
|
916
1020
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1021
|
+
await api.saveApplicationEnv(
|
|
1022
|
+
application.applicationId,
|
|
1023
|
+
envVars.join('\n'),
|
|
1024
|
+
);
|
|
921
1025
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
// Create domain for this app
|
|
926
|
-
try {
|
|
927
|
-
const host = resolveHost(
|
|
928
|
-
appName,
|
|
929
|
-
app,
|
|
930
|
-
stage,
|
|
931
|
-
dokployConfig,
|
|
932
|
-
false, // Backend apps are not main frontend
|
|
933
|
-
);
|
|
934
|
-
|
|
935
|
-
await api.createDomain({
|
|
936
|
-
host,
|
|
937
|
-
port: app.port,
|
|
938
|
-
https: true,
|
|
939
|
-
certificateType: 'letsencrypt',
|
|
940
|
-
applicationId: application.applicationId,
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
const publicUrl = `https://${host}`;
|
|
944
|
-
publicUrls[appName] = publicUrl;
|
|
945
|
-
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
946
|
-
} catch (domainError) {
|
|
947
|
-
// Domain might already exist, try to get public URL anyway
|
|
948
|
-
const host = resolveHost(appName, app, stage, dokployConfig, false);
|
|
949
|
-
publicUrls[appName] = `https://${host}`;
|
|
950
|
-
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
951
|
-
}
|
|
1026
|
+
logger.log(` Deploying to Dokploy...`);
|
|
1027
|
+
await api.deployApplication(application.applicationId);
|
|
952
1028
|
|
|
953
|
-
|
|
1029
|
+
// Create domain for this app
|
|
1030
|
+
try {
|
|
1031
|
+
const host = resolveHost(
|
|
954
1032
|
appName,
|
|
955
|
-
|
|
956
|
-
|
|
1033
|
+
app,
|
|
1034
|
+
stage,
|
|
1035
|
+
dokployConfig,
|
|
1036
|
+
false, // Backend apps are not main frontend
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
await api.createDomain({
|
|
1040
|
+
host,
|
|
1041
|
+
port: app.port,
|
|
1042
|
+
https: true,
|
|
1043
|
+
certificateType: 'letsencrypt',
|
|
957
1044
|
applicationId: application.applicationId,
|
|
958
|
-
imageRef,
|
|
959
1045
|
});
|
|
960
1046
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1047
|
+
const publicUrl = `https://${host}`;
|
|
1048
|
+
publicUrls[appName] = publicUrl;
|
|
1049
|
+
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
1050
|
+
} catch (domainError) {
|
|
1051
|
+
// Domain might already exist, try to get public URL anyway
|
|
964
1052
|
const host = resolveHost(appName, app, stage, dokployConfig, false);
|
|
965
1053
|
publicUrls[appName] = `https://${host}`;
|
|
1054
|
+
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1055
|
+
}
|
|
966
1056
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1057
|
+
results.push({
|
|
1058
|
+
appName,
|
|
1059
|
+
type: app.type,
|
|
1060
|
+
success: true,
|
|
1061
|
+
applicationId: application.applicationId,
|
|
1062
|
+
imageRef,
|
|
1063
|
+
});
|
|
973
1064
|
|
|
974
|
-
|
|
975
|
-
}
|
|
1065
|
+
logger.log(` ✓ ${appName} deployed successfully`);
|
|
976
1066
|
} catch (error) {
|
|
977
1067
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
978
1068
|
logger.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
@@ -1004,30 +1094,42 @@ export async function workspaceDeployCommand(
|
|
|
1004
1094
|
logger.log(`\n 🌐 Deploying ${appName}...`);
|
|
1005
1095
|
|
|
1006
1096
|
try {
|
|
1007
|
-
|
|
1008
|
-
|
|
1097
|
+
// Use simple app name - project already provides namespace
|
|
1098
|
+
const dokployAppName = appName;
|
|
1099
|
+
|
|
1100
|
+
// Check state for cached application ID
|
|
1101
|
+
let application: DokployApplication | null = null;
|
|
1102
|
+
const cachedAppId = getApplicationId(state, appName);
|
|
1103
|
+
|
|
1104
|
+
if (cachedAppId) {
|
|
1105
|
+
logger.log(` Using cached ID: ${cachedAppId}`);
|
|
1106
|
+
application = await api.getApplication(cachedAppId);
|
|
1107
|
+
if (application) {
|
|
1108
|
+
logger.log(` ✓ Application found: ${application.applicationId}`);
|
|
1109
|
+
} else {
|
|
1110
|
+
logger.log(` ⚠ Cached ID invalid, will create new`);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1009
1113
|
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
1114
|
+
// If not found by ID, use findOrCreate
|
|
1115
|
+
if (!application) {
|
|
1116
|
+
const result = await api.findOrCreateApplication(
|
|
1013
1117
|
dokployAppName,
|
|
1014
1118
|
project.projectId,
|
|
1015
1119
|
environmentId,
|
|
1016
1120
|
);
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
if (
|
|
1022
|
-
message.includes('already exists') ||
|
|
1023
|
-
message.includes('duplicate')
|
|
1024
|
-
) {
|
|
1025
|
-
logger.log(` Application already exists`);
|
|
1121
|
+
application = result.application;
|
|
1122
|
+
|
|
1123
|
+
if (result.created) {
|
|
1124
|
+
logger.log(` Created application: ${application.applicationId}`);
|
|
1026
1125
|
} else {
|
|
1027
|
-
|
|
1126
|
+
logger.log(` Found existing application: ${application.applicationId}`);
|
|
1028
1127
|
}
|
|
1029
1128
|
}
|
|
1030
1129
|
|
|
1130
|
+
// Store application ID in state
|
|
1131
|
+
setApplicationId(state, appName, application.applicationId);
|
|
1132
|
+
|
|
1031
1133
|
// Generate public URL build args from dependencies
|
|
1032
1134
|
const buildArgs = generatePublicUrlBuildArgs(app, publicUrls);
|
|
1033
1135
|
if (buildArgs.length > 0) {
|
|
@@ -1060,64 +1162,41 @@ export async function workspaceDeployCommand(
|
|
|
1060
1162
|
const envVars: string[] = [`NODE_ENV=production`, `PORT=${app.port}`];
|
|
1061
1163
|
|
|
1062
1164
|
// Configure and deploy application in Dokploy
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
});
|
|
1165
|
+
await api.saveDockerProvider(application.applicationId, imageRef, {
|
|
1166
|
+
registryId,
|
|
1167
|
+
});
|
|
1067
1168
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1169
|
+
await api.saveApplicationEnv(
|
|
1170
|
+
application.applicationId,
|
|
1171
|
+
envVars.join('\n'),
|
|
1172
|
+
);
|
|
1072
1173
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
// Create domain for this app
|
|
1077
|
-
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
1078
|
-
try {
|
|
1079
|
-
const host = resolveHost(
|
|
1080
|
-
appName,
|
|
1081
|
-
app,
|
|
1082
|
-
stage,
|
|
1083
|
-
dokployConfig,
|
|
1084
|
-
isMainFrontend,
|
|
1085
|
-
);
|
|
1086
|
-
|
|
1087
|
-
await api.createDomain({
|
|
1088
|
-
host,
|
|
1089
|
-
port: app.port,
|
|
1090
|
-
https: true,
|
|
1091
|
-
certificateType: 'letsencrypt',
|
|
1092
|
-
applicationId: application.applicationId,
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
const publicUrl = `https://${host}`;
|
|
1096
|
-
publicUrls[appName] = publicUrl;
|
|
1097
|
-
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
1098
|
-
} catch (domainError) {
|
|
1099
|
-
const host = resolveHost(
|
|
1100
|
-
appName,
|
|
1101
|
-
app,
|
|
1102
|
-
stage,
|
|
1103
|
-
dokployConfig,
|
|
1104
|
-
isMainFrontend,
|
|
1105
|
-
);
|
|
1106
|
-
publicUrls[appName] = `https://${host}`;
|
|
1107
|
-
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1108
|
-
}
|
|
1174
|
+
logger.log(` Deploying to Dokploy...`);
|
|
1175
|
+
await api.deployApplication(application.applicationId);
|
|
1109
1176
|
|
|
1110
|
-
|
|
1177
|
+
// Create domain for this app
|
|
1178
|
+
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
1179
|
+
try {
|
|
1180
|
+
const host = resolveHost(
|
|
1111
1181
|
appName,
|
|
1112
|
-
|
|
1113
|
-
|
|
1182
|
+
app,
|
|
1183
|
+
stage,
|
|
1184
|
+
dokployConfig,
|
|
1185
|
+
isMainFrontend,
|
|
1186
|
+
);
|
|
1187
|
+
|
|
1188
|
+
await api.createDomain({
|
|
1189
|
+
host,
|
|
1190
|
+
port: app.port,
|
|
1191
|
+
https: true,
|
|
1192
|
+
certificateType: 'letsencrypt',
|
|
1114
1193
|
applicationId: application.applicationId,
|
|
1115
|
-
imageRef,
|
|
1116
1194
|
});
|
|
1117
1195
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1196
|
+
const publicUrl = `https://${host}`;
|
|
1197
|
+
publicUrls[appName] = publicUrl;
|
|
1198
|
+
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
1199
|
+
} catch (domainError) {
|
|
1121
1200
|
const host = resolveHost(
|
|
1122
1201
|
appName,
|
|
1123
1202
|
app,
|
|
@@ -1126,16 +1205,18 @@ export async function workspaceDeployCommand(
|
|
|
1126
1205
|
isMainFrontend,
|
|
1127
1206
|
);
|
|
1128
1207
|
publicUrls[appName] = `https://${host}`;
|
|
1208
|
+
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1209
|
+
}
|
|
1129
1210
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1211
|
+
results.push({
|
|
1212
|
+
appName,
|
|
1213
|
+
type: app.type,
|
|
1214
|
+
success: true,
|
|
1215
|
+
applicationId: application.applicationId,
|
|
1216
|
+
imageRef,
|
|
1217
|
+
});
|
|
1136
1218
|
|
|
1137
|
-
|
|
1138
|
-
}
|
|
1219
|
+
logger.log(` ✓ ${appName} deployed successfully`);
|
|
1139
1220
|
} catch (error) {
|
|
1140
1221
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
1141
1222
|
logger.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
@@ -1151,6 +1232,13 @@ export async function workspaceDeployCommand(
|
|
|
1151
1232
|
}
|
|
1152
1233
|
}
|
|
1153
1234
|
|
|
1235
|
+
// ==================================================================
|
|
1236
|
+
// STATE: Save deploy state
|
|
1237
|
+
// ==================================================================
|
|
1238
|
+
logger.log('\n📋 Saving deploy state...');
|
|
1239
|
+
await writeStageState(workspace.root, stage, state);
|
|
1240
|
+
logger.log(` ✓ State saved to .gkm/deploy-${stage}.json`);
|
|
1241
|
+
|
|
1154
1242
|
// ==================================================================
|
|
1155
1243
|
// Summary
|
|
1156
1244
|
// ==================================================================
|