@geekmidas/cli 0.45.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 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +254 -135
- 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 +3 -3
- package/src/deploy/dokploy-api.ts +163 -0
- package/src/deploy/index.ts +313 -233
- 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
|
-
|
|
268
|
+
redis = result.redis;
|
|
269
|
+
created = result.created;
|
|
218
270
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
logger.log(' ✓ Redis deployed');
|
|
271
|
+
if (created) {
|
|
272
|
+
logger.log(` ✓ Created Redis: ${redis.redisId}`);
|
|
222
273
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (redis.databasePassword) {
|
|
227
|
-
serviceUrls.REDIS_PASSWORD = redis.databasePassword;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Construct connection URL
|
|
231
|
-
const password = redis.databasePassword
|
|
232
|
-
? `:${redis.databasePassword}@`
|
|
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,21 +884,30 @@ export async function workspaceDeployCommand(
|
|
|
808
884
|
|
|
809
885
|
if (dockerServices.postgres || dockerServices.redis) {
|
|
810
886
|
logger.log('\n🔧 Provisioning infrastructure services...');
|
|
811
|
-
// Pass existing
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
await provisionServices(
|
|
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(
|
|
819
894
|
api,
|
|
820
895
|
project.projectId,
|
|
821
896
|
environmentId,
|
|
822
897
|
workspace.name,
|
|
823
898
|
dockerServices,
|
|
824
|
-
|
|
899
|
+
existingServiceIds,
|
|
825
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
|
+
}
|
|
826
911
|
}
|
|
827
912
|
|
|
828
913
|
// ==================================================================
|
|
@@ -852,30 +937,42 @@ export async function workspaceDeployCommand(
|
|
|
852
937
|
logger.log(`\n ⚙️ Deploying ${appName}...`);
|
|
853
938
|
|
|
854
939
|
try {
|
|
855
|
-
|
|
856
|
-
|
|
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
|
+
}
|
|
857
956
|
|
|
858
|
-
//
|
|
859
|
-
|
|
860
|
-
|
|
957
|
+
// If not found by ID, use findOrCreate
|
|
958
|
+
if (!application) {
|
|
959
|
+
const result = await api.findOrCreateApplication(
|
|
861
960
|
dokployAppName,
|
|
862
961
|
project.projectId,
|
|
863
962
|
environmentId,
|
|
864
963
|
);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
if (
|
|
870
|
-
message.includes('already exists') ||
|
|
871
|
-
message.includes('duplicate')
|
|
872
|
-
) {
|
|
873
|
-
logger.log(` Application already exists`);
|
|
964
|
+
application = result.application;
|
|
965
|
+
|
|
966
|
+
if (result.created) {
|
|
967
|
+
logger.log(` Created application: ${application.applicationId}`);
|
|
874
968
|
} else {
|
|
875
|
-
|
|
969
|
+
logger.log(` Found existing application: ${application.applicationId}`);
|
|
876
970
|
}
|
|
877
971
|
}
|
|
878
972
|
|
|
973
|
+
// Store application ID in state
|
|
974
|
+
setApplicationId(state, appName, application.applicationId);
|
|
975
|
+
|
|
879
976
|
// Get encrypted secrets for this app
|
|
880
977
|
const appSecrets = encryptedSecrets.get(appName);
|
|
881
978
|
const buildArgs: string[] = [];
|
|
@@ -917,70 +1014,55 @@ export async function workspaceDeployCommand(
|
|
|
917
1014
|
}
|
|
918
1015
|
|
|
919
1016
|
// Configure and deploy application in Dokploy
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
});
|
|
1017
|
+
await api.saveDockerProvider(application.applicationId, imageRef, {
|
|
1018
|
+
registryId,
|
|
1019
|
+
});
|
|
924
1020
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1021
|
+
await api.saveApplicationEnv(
|
|
1022
|
+
application.applicationId,
|
|
1023
|
+
envVars.join('\n'),
|
|
1024
|
+
);
|
|
929
1025
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
// Create domain for this app
|
|
934
|
-
try {
|
|
935
|
-
const host = resolveHost(
|
|
936
|
-
appName,
|
|
937
|
-
app,
|
|
938
|
-
stage,
|
|
939
|
-
dokployConfig,
|
|
940
|
-
false, // Backend apps are not main frontend
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
await api.createDomain({
|
|
944
|
-
host,
|
|
945
|
-
port: app.port,
|
|
946
|
-
https: true,
|
|
947
|
-
certificateType: 'letsencrypt',
|
|
948
|
-
applicationId: application.applicationId,
|
|
949
|
-
});
|
|
950
|
-
|
|
951
|
-
const publicUrl = `https://${host}`;
|
|
952
|
-
publicUrls[appName] = publicUrl;
|
|
953
|
-
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
954
|
-
} catch (domainError) {
|
|
955
|
-
// Domain might already exist, try to get public URL anyway
|
|
956
|
-
const host = resolveHost(appName, app, stage, dokployConfig, false);
|
|
957
|
-
publicUrls[appName] = `https://${host}`;
|
|
958
|
-
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
959
|
-
}
|
|
1026
|
+
logger.log(` Deploying to Dokploy...`);
|
|
1027
|
+
await api.deployApplication(application.applicationId);
|
|
960
1028
|
|
|
961
|
-
|
|
1029
|
+
// Create domain for this app
|
|
1030
|
+
try {
|
|
1031
|
+
const host = resolveHost(
|
|
962
1032
|
appName,
|
|
963
|
-
|
|
964
|
-
|
|
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',
|
|
965
1044
|
applicationId: application.applicationId,
|
|
966
|
-
imageRef,
|
|
967
1045
|
});
|
|
968
1046
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
|
972
1052
|
const host = resolveHost(appName, app, stage, dokployConfig, false);
|
|
973
1053
|
publicUrls[appName] = `https://${host}`;
|
|
1054
|
+
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1055
|
+
}
|
|
974
1056
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1057
|
+
results.push({
|
|
1058
|
+
appName,
|
|
1059
|
+
type: app.type,
|
|
1060
|
+
success: true,
|
|
1061
|
+
applicationId: application.applicationId,
|
|
1062
|
+
imageRef,
|
|
1063
|
+
});
|
|
981
1064
|
|
|
982
|
-
|
|
983
|
-
}
|
|
1065
|
+
logger.log(` ✓ ${appName} deployed successfully`);
|
|
984
1066
|
} catch (error) {
|
|
985
1067
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
986
1068
|
logger.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
@@ -1012,30 +1094,42 @@ export async function workspaceDeployCommand(
|
|
|
1012
1094
|
logger.log(`\n 🌐 Deploying ${appName}...`);
|
|
1013
1095
|
|
|
1014
1096
|
try {
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
+
}
|
|
1017
1113
|
|
|
1018
|
-
//
|
|
1019
|
-
|
|
1020
|
-
|
|
1114
|
+
// If not found by ID, use findOrCreate
|
|
1115
|
+
if (!application) {
|
|
1116
|
+
const result = await api.findOrCreateApplication(
|
|
1021
1117
|
dokployAppName,
|
|
1022
1118
|
project.projectId,
|
|
1023
1119
|
environmentId,
|
|
1024
1120
|
);
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
if (
|
|
1030
|
-
message.includes('already exists') ||
|
|
1031
|
-
message.includes('duplicate')
|
|
1032
|
-
) {
|
|
1033
|
-
logger.log(` Application already exists`);
|
|
1121
|
+
application = result.application;
|
|
1122
|
+
|
|
1123
|
+
if (result.created) {
|
|
1124
|
+
logger.log(` Created application: ${application.applicationId}`);
|
|
1034
1125
|
} else {
|
|
1035
|
-
|
|
1126
|
+
logger.log(` Found existing application: ${application.applicationId}`);
|
|
1036
1127
|
}
|
|
1037
1128
|
}
|
|
1038
1129
|
|
|
1130
|
+
// Store application ID in state
|
|
1131
|
+
setApplicationId(state, appName, application.applicationId);
|
|
1132
|
+
|
|
1039
1133
|
// Generate public URL build args from dependencies
|
|
1040
1134
|
const buildArgs = generatePublicUrlBuildArgs(app, publicUrls);
|
|
1041
1135
|
if (buildArgs.length > 0) {
|
|
@@ -1068,64 +1162,41 @@ export async function workspaceDeployCommand(
|
|
|
1068
1162
|
const envVars: string[] = [`NODE_ENV=production`, `PORT=${app.port}`];
|
|
1069
1163
|
|
|
1070
1164
|
// Configure and deploy application in Dokploy
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
});
|
|
1165
|
+
await api.saveDockerProvider(application.applicationId, imageRef, {
|
|
1166
|
+
registryId,
|
|
1167
|
+
});
|
|
1075
1168
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1169
|
+
await api.saveApplicationEnv(
|
|
1170
|
+
application.applicationId,
|
|
1171
|
+
envVars.join('\n'),
|
|
1172
|
+
);
|
|
1080
1173
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
// Create domain for this app
|
|
1085
|
-
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
1086
|
-
try {
|
|
1087
|
-
const host = resolveHost(
|
|
1088
|
-
appName,
|
|
1089
|
-
app,
|
|
1090
|
-
stage,
|
|
1091
|
-
dokployConfig,
|
|
1092
|
-
isMainFrontend,
|
|
1093
|
-
);
|
|
1094
|
-
|
|
1095
|
-
await api.createDomain({
|
|
1096
|
-
host,
|
|
1097
|
-
port: app.port,
|
|
1098
|
-
https: true,
|
|
1099
|
-
certificateType: 'letsencrypt',
|
|
1100
|
-
applicationId: application.applicationId,
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1103
|
-
const publicUrl = `https://${host}`;
|
|
1104
|
-
publicUrls[appName] = publicUrl;
|
|
1105
|
-
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
1106
|
-
} catch (domainError) {
|
|
1107
|
-
const host = resolveHost(
|
|
1108
|
-
appName,
|
|
1109
|
-
app,
|
|
1110
|
-
stage,
|
|
1111
|
-
dokployConfig,
|
|
1112
|
-
isMainFrontend,
|
|
1113
|
-
);
|
|
1114
|
-
publicUrls[appName] = `https://${host}`;
|
|
1115
|
-
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1116
|
-
}
|
|
1174
|
+
logger.log(` Deploying to Dokploy...`);
|
|
1175
|
+
await api.deployApplication(application.applicationId);
|
|
1117
1176
|
|
|
1118
|
-
|
|
1177
|
+
// Create domain for this app
|
|
1178
|
+
const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
|
|
1179
|
+
try {
|
|
1180
|
+
const host = resolveHost(
|
|
1119
1181
|
appName,
|
|
1120
|
-
|
|
1121
|
-
|
|
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',
|
|
1122
1193
|
applicationId: application.applicationId,
|
|
1123
|
-
imageRef,
|
|
1124
1194
|
});
|
|
1125
1195
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1196
|
+
const publicUrl = `https://${host}`;
|
|
1197
|
+
publicUrls[appName] = publicUrl;
|
|
1198
|
+
logger.log(` ✓ Domain: ${publicUrl}`);
|
|
1199
|
+
} catch (domainError) {
|
|
1129
1200
|
const host = resolveHost(
|
|
1130
1201
|
appName,
|
|
1131
1202
|
app,
|
|
@@ -1134,16 +1205,18 @@ export async function workspaceDeployCommand(
|
|
|
1134
1205
|
isMainFrontend,
|
|
1135
1206
|
);
|
|
1136
1207
|
publicUrls[appName] = `https://${host}`;
|
|
1208
|
+
logger.log(` ℹ Domain already configured: https://${host}`);
|
|
1209
|
+
}
|
|
1137
1210
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1211
|
+
results.push({
|
|
1212
|
+
appName,
|
|
1213
|
+
type: app.type,
|
|
1214
|
+
success: true,
|
|
1215
|
+
applicationId: application.applicationId,
|
|
1216
|
+
imageRef,
|
|
1217
|
+
});
|
|
1144
1218
|
|
|
1145
|
-
|
|
1146
|
-
}
|
|
1219
|
+
logger.log(` ✓ ${appName} deployed successfully`);
|
|
1147
1220
|
} catch (error) {
|
|
1148
1221
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
1149
1222
|
logger.log(` ✗ Failed to deploy ${appName}: ${message}`);
|
|
@@ -1159,6 +1232,13 @@ export async function workspaceDeployCommand(
|
|
|
1159
1232
|
}
|
|
1160
1233
|
}
|
|
1161
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
|
+
|
|
1162
1242
|
// ==================================================================
|
|
1163
1243
|
// Summary
|
|
1164
1244
|
// ==================================================================
|