@geekmidas/cli 0.13.0 → 0.15.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/{bundler-DskIqW2t.mjs → bundler-D7cM_FWw.mjs} +34 -10
- package/dist/bundler-D7cM_FWw.mjs.map +1 -0
- package/dist/{bundler-B1qy9b-j.cjs → bundler-Nuew7Xcn.cjs} +33 -9
- package/dist/bundler-Nuew7Xcn.cjs.map +1 -0
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/dokploy-api-B7KxOQr3.cjs +3 -0
- package/dist/dokploy-api-C7F9VykY.cjs +317 -0
- package/dist/dokploy-api-C7F9VykY.cjs.map +1 -0
- package/dist/dokploy-api-CaETb2L6.mjs +305 -0
- package/dist/dokploy-api-CaETb2L6.mjs.map +1 -0
- package/dist/dokploy-api-DHvfmWbi.mjs +3 -0
- package/dist/{encryption-Dyf_r1h-.cjs → encryption-D7Efcdi9.cjs} +1 -1
- package/dist/{encryption-Dyf_r1h-.cjs.map → encryption-D7Efcdi9.cjs.map} +1 -1
- package/dist/{encryption-C8H-38Yy.mjs → encryption-h4Nb6W-M.mjs} +1 -1
- package/dist/{encryption-C8H-38Yy.mjs.map → encryption-h4Nb6W-M.mjs.map} +1 -1
- package/dist/index.cjs +1508 -1073
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1508 -1073
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-Bt_1FDpT.cjs → openapi-C89hhkZC.cjs} +3 -3
- package/dist/{openapi-Bt_1FDpT.cjs.map → openapi-C89hhkZC.cjs.map} +1 -1
- package/dist/{openapi-BfFlOBCG.mjs → openapi-CZVcfxk-.mjs} +3 -3
- package/dist/{openapi-BfFlOBCG.mjs.map → openapi-CZVcfxk-.mjs.map} +1 -1
- package/dist/{openapi-react-query-B6XTeGqS.mjs → openapi-react-query-CM2_qlW9.mjs} +1 -1
- package/dist/{openapi-react-query-B6XTeGqS.mjs.map → openapi-react-query-CM2_qlW9.mjs.map} +1 -1
- package/dist/{openapi-react-query-B-sNWHFU.cjs → openapi-react-query-iKjfLzff.cjs} +1 -1
- package/dist/{openapi-react-query-B-sNWHFU.cjs.map → openapi-react-query-iKjfLzff.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{storage-kSxTjkNb.mjs → storage-BaOP55oq.mjs} +16 -2
- package/dist/storage-BaOP55oq.mjs.map +1 -0
- package/dist/{storage-Bj1E26lU.cjs → storage-Bn3K9Ccu.cjs} +21 -1
- package/dist/storage-Bn3K9Ccu.cjs.map +1 -0
- package/dist/storage-UfyTn7Zm.cjs +7 -0
- package/dist/storage-nkGIjeXt.mjs +3 -0
- package/dist/{types-BhkZc-vm.d.cts → types-BgaMXsUa.d.cts} +3 -1
- package/dist/{types-BR0M2v_c.d.mts.map → types-BgaMXsUa.d.cts.map} +1 -1
- package/dist/{types-BR0M2v_c.d.mts → types-iFk5ms7y.d.mts} +3 -1
- package/dist/{types-BhkZc-vm.d.cts.map → types-iFk5ms7y.d.mts.map} +1 -1
- package/package.json +4 -4
- package/src/auth/__tests__/credentials.spec.ts +127 -0
- package/src/auth/__tests__/index.spec.ts +69 -0
- package/src/auth/credentials.ts +33 -0
- package/src/auth/index.ts +57 -50
- package/src/build/__tests__/bundler.spec.ts +5 -4
- package/src/build/__tests__/endpoint-analyzer.spec.ts +623 -0
- package/src/build/__tests__/handler-templates.spec.ts +272 -0
- package/src/build/bundler.ts +61 -8
- package/src/build/index.ts +21 -0
- package/src/build/types.ts +6 -0
- package/src/deploy/__tests__/docker.spec.ts +44 -6
- package/src/deploy/__tests__/dokploy-api.spec.ts +698 -0
- package/src/deploy/__tests__/dokploy.spec.ts +196 -6
- package/src/deploy/__tests__/index.spec.ts +401 -0
- package/src/deploy/__tests__/init.spec.ts +147 -16
- package/src/deploy/docker.ts +109 -5
- package/src/deploy/dokploy-api.ts +581 -0
- package/src/deploy/dokploy.ts +66 -93
- package/src/deploy/index.ts +630 -32
- package/src/deploy/init.ts +192 -249
- package/src/deploy/types.ts +24 -2
- package/src/dev/__tests__/index.spec.ts +95 -0
- package/src/docker/__tests__/templates.spec.ts +144 -0
- package/src/docker/index.ts +96 -6
- package/src/docker/templates.ts +114 -27
- package/src/generators/EndpointGenerator.ts +2 -2
- package/src/index.ts +34 -13
- package/src/secrets/storage.ts +15 -0
- package/src/types.ts +2 -0
- package/dist/bundler-B1qy9b-j.cjs.map +0 -1
- package/dist/bundler-DskIqW2t.mjs.map +0 -1
- package/dist/storage-BOOpAF8N.cjs +0 -5
- package/dist/storage-Bj1E26lU.cjs.map +0 -1
- package/dist/storage-kSxTjkNb.mjs.map +0 -1
- package/dist/storage-tgZSUnKl.mjs +0 -3
package/src/deploy/init.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getDokployCredentials,
|
|
6
|
+
getDokployRegistryId,
|
|
7
|
+
getDokployToken,
|
|
8
|
+
storeDokployRegistryId,
|
|
9
|
+
} from '../auth';
|
|
10
|
+
import { DokployApi } from './dokploy-api';
|
|
5
11
|
import type { DokployDeployConfig } from './types';
|
|
6
12
|
|
|
7
13
|
const logger = console;
|
|
@@ -15,37 +21,23 @@ export interface DeployInitOptions {
|
|
|
15
21
|
appName: string;
|
|
16
22
|
/** Use existing project ID instead of creating/finding */
|
|
17
23
|
projectId?: string;
|
|
18
|
-
/** Registry ID in Dokploy (optional) */
|
|
24
|
+
/** Registry ID in Dokploy (optional, uses stored if available) */
|
|
19
25
|
registryId?: string;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
interface
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
createdAt: string;
|
|
27
|
-
adminId: string;
|
|
28
|
-
environments?: Array<{
|
|
29
|
-
environmentId: string;
|
|
30
|
-
name: string;
|
|
31
|
-
description: string | null;
|
|
32
|
-
}>;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
interface DokployApplication {
|
|
36
|
-
applicationId: string;
|
|
37
|
-
name: string;
|
|
38
|
-
appName: string;
|
|
39
|
-
projectId: string;
|
|
40
|
-
environmentId?: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface DokployRegistry {
|
|
44
|
-
registryId: string;
|
|
28
|
+
export interface RegistrySetupOptions {
|
|
29
|
+
/** Dokploy endpoint URL (optional if logged in) */
|
|
30
|
+
endpoint?: string;
|
|
31
|
+
/** Registry name (for display in Dokploy) */
|
|
45
32
|
registryName: string;
|
|
33
|
+
/** Registry URL (e.g., ghcr.io, docker.io) */
|
|
46
34
|
registryUrl: string;
|
|
35
|
+
/** Registry username */
|
|
47
36
|
username: string;
|
|
48
|
-
|
|
37
|
+
/** Registry password or token */
|
|
38
|
+
password: string;
|
|
39
|
+
/** Image prefix (optional, e.g., org-name) */
|
|
40
|
+
imagePrefix?: string;
|
|
49
41
|
}
|
|
50
42
|
|
|
51
43
|
/**
|
|
@@ -63,169 +55,30 @@ async function getApiToken(): Promise<string> {
|
|
|
63
55
|
}
|
|
64
56
|
|
|
65
57
|
/**
|
|
66
|
-
*
|
|
58
|
+
* Get Dokploy endpoint from options or stored credentials
|
|
67
59
|
*/
|
|
68
|
-
async function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
baseUrl: string,
|
|
72
|
-
token: string,
|
|
73
|
-
body?: Record<string, unknown>,
|
|
74
|
-
): Promise<T> {
|
|
75
|
-
const url = `${baseUrl}/api/${endpoint}`;
|
|
76
|
-
|
|
77
|
-
const response = await fetch(url, {
|
|
78
|
-
method,
|
|
79
|
-
headers: {
|
|
80
|
-
'Content-Type': 'application/json',
|
|
81
|
-
Authorization: `Bearer ${token}`,
|
|
82
|
-
},
|
|
83
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
let errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
const errorBody = (await response.json()) as { message?: string };
|
|
91
|
-
if (errorBody.message) {
|
|
92
|
-
errorMessage = `Dokploy API error: ${errorBody.message}`;
|
|
93
|
-
}
|
|
94
|
-
} catch {
|
|
95
|
-
// Ignore JSON parse errors
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
throw new Error(errorMessage);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Handle empty responses
|
|
102
|
-
const text = await response.text();
|
|
103
|
-
if (!text) {
|
|
104
|
-
return {} as T;
|
|
60
|
+
async function getEndpoint(providedEndpoint?: string): Promise<string> {
|
|
61
|
+
if (providedEndpoint) {
|
|
62
|
+
return providedEndpoint;
|
|
105
63
|
}
|
|
106
64
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get all projects from Dokploy
|
|
112
|
-
*/
|
|
113
|
-
async function getProjects(
|
|
114
|
-
baseUrl: string,
|
|
115
|
-
token: string,
|
|
116
|
-
): Promise<DokployProject[]> {
|
|
117
|
-
return dokployRequest<DokployProject[]>('GET', 'project.all', baseUrl, token);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Create a new project in Dokploy
|
|
122
|
-
*/
|
|
123
|
-
async function createProject(
|
|
124
|
-
baseUrl: string,
|
|
125
|
-
token: string,
|
|
126
|
-
name: string,
|
|
127
|
-
description?: string,
|
|
128
|
-
): Promise<DokployProject> {
|
|
129
|
-
return dokployRequest<DokployProject>(
|
|
130
|
-
'POST',
|
|
131
|
-
'project.create',
|
|
132
|
-
baseUrl,
|
|
133
|
-
token,
|
|
134
|
-
{
|
|
135
|
-
name,
|
|
136
|
-
description: description || `Created by gkm CLI`,
|
|
137
|
-
},
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Get project by ID to get environment info
|
|
143
|
-
*/
|
|
144
|
-
async function getProject(
|
|
145
|
-
baseUrl: string,
|
|
146
|
-
token: string,
|
|
147
|
-
projectId: string,
|
|
148
|
-
): Promise<DokployProject> {
|
|
149
|
-
return dokployRequest<DokployProject>('POST', 'project.one', baseUrl, token, {
|
|
150
|
-
projectId,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Create a new application in Dokploy
|
|
156
|
-
*/
|
|
157
|
-
async function createApplication(
|
|
158
|
-
baseUrl: string,
|
|
159
|
-
token: string,
|
|
160
|
-
name: string,
|
|
161
|
-
projectId: string,
|
|
162
|
-
): Promise<DokployApplication> {
|
|
163
|
-
// First get the project to find its environment ID
|
|
164
|
-
const project = await getProject(baseUrl, token, projectId);
|
|
165
|
-
|
|
166
|
-
// Use the first environment or create one
|
|
167
|
-
let environmentId: string;
|
|
168
|
-
|
|
169
|
-
const firstEnv = project.environments?.[0];
|
|
170
|
-
if (firstEnv) {
|
|
171
|
-
environmentId = firstEnv.environmentId;
|
|
172
|
-
} else {
|
|
173
|
-
// Create a default environment
|
|
174
|
-
const env = await dokployRequest<{ environmentId: string }>(
|
|
175
|
-
'POST',
|
|
176
|
-
'environment.create',
|
|
177
|
-
baseUrl,
|
|
178
|
-
token,
|
|
179
|
-
{
|
|
180
|
-
projectId,
|
|
181
|
-
name: 'production',
|
|
182
|
-
description: 'Production environment',
|
|
183
|
-
},
|
|
184
|
-
);
|
|
185
|
-
environmentId = env.environmentId;
|
|
65
|
+
const stored = await getDokployCredentials();
|
|
66
|
+
if (stored) {
|
|
67
|
+
return stored.endpoint;
|
|
186
68
|
}
|
|
187
69
|
|
|
188
|
-
|
|
189
|
-
'
|
|
190
|
-
|
|
191
|
-
baseUrl,
|
|
192
|
-
token,
|
|
193
|
-
{
|
|
194
|
-
name,
|
|
195
|
-
projectId,
|
|
196
|
-
environmentId,
|
|
197
|
-
},
|
|
70
|
+
throw new Error(
|
|
71
|
+
'Dokploy endpoint not specified.\n' +
|
|
72
|
+
'Either run "gkm login --service dokploy" first, or provide --endpoint.',
|
|
198
73
|
);
|
|
199
74
|
}
|
|
200
75
|
|
|
201
76
|
/**
|
|
202
|
-
*
|
|
77
|
+
* Create a Dokploy API client
|
|
203
78
|
*/
|
|
204
|
-
async function
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
applicationId: string,
|
|
208
|
-
registryId: string,
|
|
209
|
-
): Promise<void> {
|
|
210
|
-
await dokployRequest('POST', 'application.update', baseUrl, token, {
|
|
211
|
-
applicationId,
|
|
212
|
-
registryId,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Get available registries
|
|
218
|
-
*/
|
|
219
|
-
async function getRegistries(
|
|
220
|
-
baseUrl: string,
|
|
221
|
-
token: string,
|
|
222
|
-
): Promise<DokployRegistry[]> {
|
|
223
|
-
return dokployRequest<DokployRegistry[]>(
|
|
224
|
-
'GET',
|
|
225
|
-
'registry.all',
|
|
226
|
-
baseUrl,
|
|
227
|
-
token,
|
|
228
|
-
);
|
|
79
|
+
async function createApi(endpoint: string): Promise<DokployApi> {
|
|
80
|
+
const token = await getApiToken();
|
|
81
|
+
return new DokployApi({ baseUrl: endpoint, token });
|
|
229
82
|
}
|
|
230
83
|
|
|
231
84
|
/**
|
|
@@ -259,31 +112,29 @@ export async function updateConfig(
|
|
|
259
112
|
logger.log(' Updating with new values...');
|
|
260
113
|
}
|
|
261
114
|
|
|
115
|
+
// Build the dokploy config string
|
|
116
|
+
const registryLine = config.registryId
|
|
117
|
+
? `\n\t\t\tregistryId: '${config.registryId}',`
|
|
118
|
+
: '';
|
|
119
|
+
const dokployConfigStr = `dokploy: {
|
|
120
|
+
endpoint: '${config.endpoint}',
|
|
121
|
+
projectId: '${config.projectId}',
|
|
122
|
+
applicationId: '${config.applicationId}',${registryLine}
|
|
123
|
+
}`;
|
|
124
|
+
|
|
262
125
|
// Try to add or update the dokploy config
|
|
263
126
|
let newContent: string;
|
|
264
127
|
|
|
265
128
|
if (content.includes('providers:')) {
|
|
266
129
|
// Add dokploy to existing providers
|
|
267
130
|
if (content.includes('dokploy:')) {
|
|
268
|
-
// Update existing dokploy config
|
|
269
|
-
newContent = content.replace(
|
|
270
|
-
/dokploy:\s*\{[^}]*\}/,
|
|
271
|
-
`dokploy: {
|
|
272
|
-
endpoint: '${config.endpoint}',
|
|
273
|
-
projectId: '${config.projectId}',
|
|
274
|
-
applicationId: '${config.applicationId}',
|
|
275
|
-
}`,
|
|
276
|
-
);
|
|
131
|
+
// Update existing dokploy config (handle multi-line with registryId)
|
|
132
|
+
newContent = content.replace(/dokploy:\s*\{[^}]*\}/s, dokployConfigStr);
|
|
277
133
|
} else {
|
|
278
134
|
// Add dokploy to providers
|
|
279
135
|
newContent = content.replace(
|
|
280
136
|
/providers:\s*\{/,
|
|
281
|
-
`providers: {
|
|
282
|
-
dokploy: {
|
|
283
|
-
endpoint: '${config.endpoint}',
|
|
284
|
-
projectId: '${config.projectId}',
|
|
285
|
-
applicationId: '${config.applicationId}',
|
|
286
|
-
},`,
|
|
137
|
+
`providers: {\n\t\t${dokployConfigStr},`,
|
|
287
138
|
);
|
|
288
139
|
}
|
|
289
140
|
} else {
|
|
@@ -292,11 +143,7 @@ export async function updateConfig(
|
|
|
292
143
|
/}\s*\)\s*;?\s*$/,
|
|
293
144
|
`
|
|
294
145
|
providers: {
|
|
295
|
-
|
|
296
|
-
endpoint: '${config.endpoint}',
|
|
297
|
-
projectId: '${config.projectId}',
|
|
298
|
-
applicationId: '${config.applicationId}',
|
|
299
|
-
},
|
|
146
|
+
${dokployConfigStr},
|
|
300
147
|
},
|
|
301
148
|
});`,
|
|
302
149
|
);
|
|
@@ -319,25 +166,12 @@ export async function deployInitCommand(
|
|
|
319
166
|
registryId,
|
|
320
167
|
} = options;
|
|
321
168
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (!endpoint) {
|
|
325
|
-
const stored = await getDokployCredentials();
|
|
326
|
-
if (stored) {
|
|
327
|
-
endpoint = stored.endpoint;
|
|
328
|
-
} else {
|
|
329
|
-
throw new Error(
|
|
330
|
-
'Dokploy endpoint not specified.\n' +
|
|
331
|
-
'Either run "gkm login --service dokploy" first, or provide --endpoint.',
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
169
|
+
const endpoint = await getEndpoint(options.endpoint);
|
|
170
|
+
const api = await createApi(endpoint);
|
|
335
171
|
|
|
336
172
|
logger.log(`\n🚀 Initializing Dokploy deployment...`);
|
|
337
173
|
logger.log(` Endpoint: ${endpoint}`);
|
|
338
174
|
|
|
339
|
-
const token = await getApiToken();
|
|
340
|
-
|
|
341
175
|
// Step 1: Find or create project
|
|
342
176
|
let projectId: string;
|
|
343
177
|
|
|
@@ -347,7 +181,7 @@ export async function deployInitCommand(
|
|
|
347
181
|
} else {
|
|
348
182
|
logger.log(`\n📁 Looking for project: ${projectName}`);
|
|
349
183
|
|
|
350
|
-
const projects = await
|
|
184
|
+
const projects = await api.listProjects();
|
|
351
185
|
const existingProject = projects.find(
|
|
352
186
|
(p) => p.name.toLowerCase() === projectName.toLowerCase(),
|
|
353
187
|
);
|
|
@@ -357,36 +191,44 @@ export async function deployInitCommand(
|
|
|
357
191
|
logger.log(` Found existing project: ${projectId}`);
|
|
358
192
|
} else {
|
|
359
193
|
logger.log(` Creating new project...`);
|
|
360
|
-
const
|
|
361
|
-
projectId = project.projectId;
|
|
194
|
+
const result = await api.createProject(projectName);
|
|
195
|
+
projectId = result.project.projectId;
|
|
362
196
|
logger.log(` ✓ Created project: ${projectId}`);
|
|
363
197
|
}
|
|
364
198
|
}
|
|
365
199
|
|
|
366
|
-
// Step 2:
|
|
200
|
+
// Step 2: Get project to find environment
|
|
201
|
+
const project = await api.getProject(projectId);
|
|
202
|
+
let environmentId: string;
|
|
203
|
+
|
|
204
|
+
const firstEnv = project.environments?.[0];
|
|
205
|
+
if (firstEnv) {
|
|
206
|
+
environmentId = firstEnv.environmentId;
|
|
207
|
+
} else {
|
|
208
|
+
// Create a default environment
|
|
209
|
+
logger.log(` Creating production environment...`);
|
|
210
|
+
const env = await api.createEnvironment(projectId, 'production');
|
|
211
|
+
environmentId = env.environmentId;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Step 3: Create application
|
|
367
215
|
logger.log(`\n📦 Creating application: ${appName}`);
|
|
368
|
-
const application = await createApplication(
|
|
369
|
-
endpoint,
|
|
370
|
-
token,
|
|
216
|
+
const application = await api.createApplication(
|
|
371
217
|
appName,
|
|
372
218
|
projectId,
|
|
219
|
+
environmentId,
|
|
373
220
|
);
|
|
374
221
|
logger.log(` ✓ Created application: ${application.applicationId}`);
|
|
375
222
|
|
|
376
|
-
// Step
|
|
223
|
+
// Step 4: Configure registry if provided
|
|
377
224
|
if (registryId) {
|
|
378
225
|
logger.log(`\n🔧 Configuring registry: ${registryId}`);
|
|
379
|
-
await
|
|
380
|
-
endpoint,
|
|
381
|
-
token,
|
|
382
|
-
application.applicationId,
|
|
383
|
-
registryId,
|
|
384
|
-
);
|
|
226
|
+
await api.updateApplication(application.applicationId, { registryId });
|
|
385
227
|
logger.log(` ✓ Registry configured`);
|
|
386
228
|
} else {
|
|
387
229
|
// List available registries
|
|
388
230
|
try {
|
|
389
|
-
const registries = await
|
|
231
|
+
const registries = await api.listRegistries();
|
|
390
232
|
if (registries.length > 0) {
|
|
391
233
|
logger.log(`\n📋 Available registries:`);
|
|
392
234
|
for (const reg of registries) {
|
|
@@ -401,14 +243,14 @@ export async function deployInitCommand(
|
|
|
401
243
|
}
|
|
402
244
|
}
|
|
403
245
|
|
|
404
|
-
// Step
|
|
246
|
+
// Step 5: Build config
|
|
405
247
|
const config: DokployDeployConfig = {
|
|
406
248
|
endpoint,
|
|
407
249
|
projectId,
|
|
408
250
|
applicationId: application.applicationId,
|
|
409
251
|
};
|
|
410
252
|
|
|
411
|
-
// Step
|
|
253
|
+
// Step 6: Update gkm.config.ts
|
|
412
254
|
await updateConfig(config);
|
|
413
255
|
|
|
414
256
|
logger.log(`\n✅ Dokploy deployment initialized!`);
|
|
@@ -430,26 +272,14 @@ export async function deployListCommand(options: {
|
|
|
430
272
|
endpoint?: string;
|
|
431
273
|
resource: 'projects' | 'registries';
|
|
432
274
|
}): Promise<void> {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if (!endpoint) {
|
|
436
|
-
const stored = await getDokployCredentials();
|
|
437
|
-
if (stored) {
|
|
438
|
-
endpoint = stored.endpoint;
|
|
439
|
-
} else {
|
|
440
|
-
throw new Error(
|
|
441
|
-
'Dokploy endpoint not specified.\n' +
|
|
442
|
-
'Either run "gkm login --service dokploy" first, or provide --endpoint.',
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
275
|
+
const endpoint = await getEndpoint(options.endpoint);
|
|
276
|
+
const api = await createApi(endpoint);
|
|
446
277
|
|
|
447
278
|
const { resource } = options;
|
|
448
|
-
const token = await getApiToken();
|
|
449
279
|
|
|
450
280
|
if (resource === 'projects') {
|
|
451
281
|
logger.log(`\n📁 Projects in ${endpoint}:`);
|
|
452
|
-
const projects = await
|
|
282
|
+
const projects = await api.listProjects();
|
|
453
283
|
|
|
454
284
|
if (projects.length === 0) {
|
|
455
285
|
logger.log(' No projects found');
|
|
@@ -464,16 +294,22 @@ export async function deployListCommand(options: {
|
|
|
464
294
|
}
|
|
465
295
|
} else if (resource === 'registries') {
|
|
466
296
|
logger.log(`\n🐳 Registries in ${endpoint}:`);
|
|
467
|
-
const registries = await
|
|
297
|
+
const registries = await api.listRegistries();
|
|
468
298
|
|
|
469
299
|
if (registries.length === 0) {
|
|
470
300
|
logger.log(' No registries configured');
|
|
471
|
-
logger.log('
|
|
301
|
+
logger.log(' Run "gkm registry:setup" to configure a registry');
|
|
472
302
|
return;
|
|
473
303
|
}
|
|
474
304
|
|
|
305
|
+
const storedRegistryId = await getDokployRegistryId();
|
|
306
|
+
|
|
475
307
|
for (const registry of registries) {
|
|
476
|
-
|
|
308
|
+
const isDefault = registry.registryId === storedRegistryId;
|
|
309
|
+
const marker = isDefault ? ' (default)' : '';
|
|
310
|
+
logger.log(
|
|
311
|
+
`\n ${registry.registryName}${marker} (${registry.registryId})`,
|
|
312
|
+
);
|
|
477
313
|
logger.log(` URL: ${registry.registryUrl}`);
|
|
478
314
|
logger.log(` Username: ${registry.username}`);
|
|
479
315
|
if (registry.imagePrefix) {
|
|
@@ -482,3 +318,110 @@ export async function deployListCommand(options: {
|
|
|
482
318
|
}
|
|
483
319
|
}
|
|
484
320
|
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Setup a Docker registry in Dokploy
|
|
324
|
+
*/
|
|
325
|
+
export async function registrySetupCommand(
|
|
326
|
+
options: RegistrySetupOptions,
|
|
327
|
+
): Promise<string> {
|
|
328
|
+
const { registryName, registryUrl, username, password, imagePrefix } =
|
|
329
|
+
options;
|
|
330
|
+
|
|
331
|
+
const endpoint = await getEndpoint(options.endpoint);
|
|
332
|
+
const api = await createApi(endpoint);
|
|
333
|
+
|
|
334
|
+
logger.log(`\n🐳 Setting up Docker registry in Dokploy...`);
|
|
335
|
+
logger.log(` Endpoint: ${endpoint}`);
|
|
336
|
+
|
|
337
|
+
// Check if registry with same URL already exists
|
|
338
|
+
const existingRegistries = await api.listRegistries();
|
|
339
|
+
const existing = existingRegistries.find(
|
|
340
|
+
(r) =>
|
|
341
|
+
r.registryUrl === registryUrl ||
|
|
342
|
+
r.registryName.toLowerCase() === registryName.toLowerCase(),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
let registryId: string;
|
|
346
|
+
|
|
347
|
+
if (existing) {
|
|
348
|
+
logger.log(`\n📋 Found existing registry: ${existing.registryName}`);
|
|
349
|
+
logger.log(` Updating credentials...`);
|
|
350
|
+
|
|
351
|
+
await api.updateRegistry(existing.registryId, {
|
|
352
|
+
registryName,
|
|
353
|
+
username,
|
|
354
|
+
password,
|
|
355
|
+
imagePrefix,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
registryId = existing.registryId;
|
|
359
|
+
logger.log(` ✓ Registry updated: ${registryId}`);
|
|
360
|
+
} else {
|
|
361
|
+
logger.log(`\n📦 Creating registry: ${registryName}`);
|
|
362
|
+
|
|
363
|
+
const registry = await api.createRegistry(
|
|
364
|
+
registryName,
|
|
365
|
+
registryUrl,
|
|
366
|
+
username,
|
|
367
|
+
password,
|
|
368
|
+
{ imagePrefix },
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
registryId = registry.registryId;
|
|
372
|
+
logger.log(` ✓ Registry created: ${registryId}`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Store registry ID in credentials
|
|
376
|
+
await storeDokployRegistryId(registryId);
|
|
377
|
+
logger.log(`\n💾 Saved registry ID to ~/.gkm/credentials.json`);
|
|
378
|
+
|
|
379
|
+
logger.log(`\n✅ Registry setup complete!`);
|
|
380
|
+
logger.log(`\n📋 Registry Details:`);
|
|
381
|
+
logger.log(` ID: ${registryId}`);
|
|
382
|
+
logger.log(` Name: ${registryName}`);
|
|
383
|
+
logger.log(` URL: ${registryUrl}`);
|
|
384
|
+
logger.log(` Username: ${username}`);
|
|
385
|
+
if (imagePrefix) {
|
|
386
|
+
logger.log(` Prefix: ${imagePrefix}`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
logger.log(
|
|
390
|
+
`\n📝 The registry ID is now stored and will be used automatically`,
|
|
391
|
+
);
|
|
392
|
+
logger.log(` when deploying with "gkm deploy --provider dokploy"`);
|
|
393
|
+
|
|
394
|
+
return registryId;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Use an existing registry (set as default)
|
|
399
|
+
*/
|
|
400
|
+
export async function registryUseCommand(options: {
|
|
401
|
+
endpoint?: string;
|
|
402
|
+
registryId: string;
|
|
403
|
+
}): Promise<void> {
|
|
404
|
+
const { registryId } = options;
|
|
405
|
+
|
|
406
|
+
const endpoint = await getEndpoint(options.endpoint);
|
|
407
|
+
const api = await createApi(endpoint);
|
|
408
|
+
|
|
409
|
+
logger.log(`\n🔧 Setting default registry...`);
|
|
410
|
+
|
|
411
|
+
// Verify the registry exists
|
|
412
|
+
try {
|
|
413
|
+
const registry = await api.getRegistry(registryId);
|
|
414
|
+
logger.log(` Found registry: ${registry.registryName}`);
|
|
415
|
+
} catch {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`Registry not found: ${registryId}\n` +
|
|
418
|
+
'Run "gkm deploy:list registries" to see available registries.',
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Store registry ID in credentials
|
|
423
|
+
await storeDokployRegistryId(registryId);
|
|
424
|
+
|
|
425
|
+
logger.log(`\n✅ Default registry set: ${registryId}`);
|
|
426
|
+
logger.log(` This registry will be used for future deployments.`);
|
|
427
|
+
}
|
package/src/deploy/types.ts
CHANGED
|
@@ -31,8 +31,12 @@ export interface DeployResult {
|
|
|
31
31
|
export interface DockerDeployConfig {
|
|
32
32
|
/** Container registry URL */
|
|
33
33
|
registry?: string;
|
|
34
|
-
/** Image name (default: from package.json) */
|
|
34
|
+
/** Image name for Docker (default: from root package.json) */
|
|
35
35
|
imageName?: string;
|
|
36
|
+
/** Project name for Dokploy (default: from root package.json) */
|
|
37
|
+
projectName?: string;
|
|
38
|
+
/** App name within Dokploy project (default: from cwd package.json) */
|
|
39
|
+
appName?: string;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/** Dokploy provider configuration */
|
|
@@ -43,6 +47,24 @@ export interface DokployDeployConfig {
|
|
|
43
47
|
projectId: string;
|
|
44
48
|
/** Application ID in Dokploy */
|
|
45
49
|
applicationId: string;
|
|
46
|
-
/** Container registry (inherits from docker if not set) */
|
|
50
|
+
/** Container registry URL (inherits from docker if not set) */
|
|
47
51
|
registry?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Registry ID in Dokploy (recommended for private registries).
|
|
54
|
+
* Configure your registry in Dokploy Settings > Docker Registry first.
|
|
55
|
+
*/
|
|
56
|
+
registryId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Docker registry credentials (alternative to registryId).
|
|
59
|
+
* Only needed if not using Dokploy's registry feature.
|
|
60
|
+
* Can also use env vars: DOCKER_REGISTRY_USERNAME, DOCKER_REGISTRY_PASSWORD
|
|
61
|
+
*/
|
|
62
|
+
registryCredentials?: {
|
|
63
|
+
/** Registry URL (e.g., ghcr.io, docker.io) */
|
|
64
|
+
registryUrl: string;
|
|
65
|
+
/** Registry username */
|
|
66
|
+
username: string;
|
|
67
|
+
/** Registry password or token */
|
|
68
|
+
password: string;
|
|
69
|
+
};
|
|
48
70
|
}
|