@geekmidas/cli 0.10.0 → 0.12.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/README.md +525 -0
- package/dist/bundler-DRXCw_YR.mjs +70 -0
- package/dist/bundler-DRXCw_YR.mjs.map +1 -0
- package/dist/bundler-WsEvH_b2.cjs +71 -0
- package/dist/bundler-WsEvH_b2.cjs.map +1 -0
- package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
- package/dist/config-AmInkU7k.cjs.map +1 -0
- package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
- package/dist/config-DYULeEv8.mjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/encryption-C8H-38Yy.mjs +42 -0
- package/dist/encryption-C8H-38Yy.mjs.map +1 -0
- package/dist/encryption-Dyf_r1h-.cjs +44 -0
- package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
- package/dist/index.cjs +2116 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2134 -192
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
- package/dist/openapi-BfFlOBCG.mjs.map +1 -0
- package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
- package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
- package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
- package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
- package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
- package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.d.cts.map +1 -1
- package/dist/openapi-react-query.d.mts.map +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/storage-BUYQJgz7.cjs +4 -0
- package/dist/storage-BXoJvmv2.cjs +149 -0
- package/dist/storage-BXoJvmv2.cjs.map +1 -0
- package/dist/storage-C9PU_30f.mjs +101 -0
- package/dist/storage-C9PU_30f.mjs.map +1 -0
- package/dist/storage-DLJAYxzJ.mjs +3 -0
- package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
- package/dist/types-BR0M2v_c.d.mts.map +1 -0
- package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
- package/dist/types-BhkZc-vm.d.cts.map +1 -0
- package/examples/cron-example.ts +27 -27
- package/examples/env.ts +27 -27
- package/examples/function-example.ts +31 -31
- package/examples/gkm.config.json +20 -20
- package/examples/gkm.config.ts +8 -8
- package/examples/gkm.minimal.config.json +5 -5
- package/examples/gkm.production.config.json +25 -25
- package/examples/logger.ts +2 -2
- package/package.json +6 -6
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
- package/src/__tests__/config.spec.ts +55 -55
- package/src/__tests__/loadEnvFiles.spec.ts +93 -93
- package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
- package/src/__tests__/openapi-react-query.spec.ts +497 -497
- package/src/__tests__/openapi.spec.ts +428 -428
- package/src/__tests__/test-helpers.ts +76 -76
- package/src/auth/__tests__/credentials.spec.ts +204 -0
- package/src/auth/__tests__/index.spec.ts +168 -0
- package/src/auth/credentials.ts +187 -0
- package/src/auth/index.ts +226 -0
- package/src/build/__tests__/index-new.spec.ts +474 -474
- package/src/build/__tests__/manifests.spec.ts +333 -333
- package/src/build/bundler.ts +141 -0
- package/src/build/endpoint-analyzer.ts +236 -0
- package/src/build/handler-templates.ts +1253 -0
- package/src/build/index.ts +250 -179
- package/src/build/manifests.ts +52 -52
- package/src/build/providerResolver.ts +145 -145
- package/src/build/types.ts +64 -43
- package/src/config.ts +39 -39
- package/src/deploy/__tests__/docker.spec.ts +111 -0
- package/src/deploy/__tests__/dokploy.spec.ts +245 -0
- package/src/deploy/__tests__/init.spec.ts +662 -0
- package/src/deploy/docker.ts +128 -0
- package/src/deploy/dokploy.ts +204 -0
- package/src/deploy/index.ts +136 -0
- package/src/deploy/init.ts +484 -0
- package/src/deploy/types.ts +48 -0
- package/src/dev/__tests__/index.spec.ts +266 -266
- package/src/dev/index.ts +647 -601
- package/src/docker/__tests__/compose.spec.ts +531 -0
- package/src/docker/__tests__/templates.spec.ts +280 -0
- package/src/docker/compose.ts +273 -0
- package/src/docker/index.ts +230 -0
- package/src/docker/templates.ts +446 -0
- package/src/generators/CronGenerator.ts +72 -72
- package/src/generators/EndpointGenerator.ts +699 -398
- package/src/generators/FunctionGenerator.ts +84 -84
- package/src/generators/Generator.ts +72 -72
- package/src/generators/OpenApiTsGenerator.ts +577 -577
- package/src/generators/SubscriberGenerator.ts +124 -124
- package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
- package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
- package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
- package/src/generators/index.ts +4 -4
- package/src/index.ts +623 -201
- package/src/init/__tests__/generators.spec.ts +334 -334
- package/src/init/__tests__/init.spec.ts +332 -332
- package/src/init/__tests__/utils.spec.ts +89 -89
- package/src/init/generators/config.ts +175 -175
- package/src/init/generators/docker.ts +41 -41
- package/src/init/generators/env.ts +72 -72
- package/src/init/generators/index.ts +1 -1
- package/src/init/generators/models.ts +64 -64
- package/src/init/generators/monorepo.ts +161 -161
- package/src/init/generators/package.ts +71 -71
- package/src/init/generators/source.ts +6 -6
- package/src/init/index.ts +203 -208
- package/src/init/templates/api.ts +115 -115
- package/src/init/templates/index.ts +75 -75
- package/src/init/templates/minimal.ts +98 -98
- package/src/init/templates/serverless.ts +89 -89
- package/src/init/templates/worker.ts +98 -98
- package/src/init/utils.ts +54 -56
- package/src/openapi-react-query.ts +194 -194
- package/src/openapi.ts +63 -63
- package/src/secrets/__tests__/encryption.spec.ts +226 -0
- package/src/secrets/__tests__/generator.spec.ts +319 -0
- package/src/secrets/__tests__/index.spec.ts +91 -0
- package/src/secrets/__tests__/storage.spec.ts +403 -0
- package/src/secrets/encryption.ts +91 -0
- package/src/secrets/generator.ts +164 -0
- package/src/secrets/index.ts +383 -0
- package/src/secrets/storage.ts +134 -0
- package/src/secrets/types.ts +53 -0
- package/src/types.ts +295 -176
- package/tsdown.config.ts +11 -8
- package/dist/config-BrkUalUh.mjs.map +0 -1
- package/dist/config-C9aXOHBe.cjs.map +0 -1
- package/dist/openapi-BeHLKcwP.cjs.map +0 -1
- package/dist/openapi-CZLI4QTr.mjs.map +0 -1
- package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
- package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
- package/dist/types-DXgiA1sF.d.mts.map +0 -1
- package/dist/types-b-vwGpqc.d.cts.map +0 -1
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { HttpResponse, http } from 'msw';
|
|
6
|
+
import { setupServer } from 'msw/node';
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
8
|
+
import { updateConfig } from '../init';
|
|
9
|
+
|
|
10
|
+
// MSW server for mocking Dokploy API calls
|
|
11
|
+
const server = setupServer();
|
|
12
|
+
|
|
13
|
+
describe('Dokploy API interactions', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
server.listen({ onUnhandledRequest: 'bypass' });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
server.resetHandlers();
|
|
20
|
+
server.close();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('project operations', () => {
|
|
24
|
+
it('should list projects', async () => {
|
|
25
|
+
server.use(
|
|
26
|
+
http.get('https://dokploy.example.com/api/project.all', () => {
|
|
27
|
+
return HttpResponse.json([
|
|
28
|
+
{ projectId: 'proj_1', name: 'Project 1', description: null },
|
|
29
|
+
{
|
|
30
|
+
projectId: 'proj_2',
|
|
31
|
+
name: 'Project 2',
|
|
32
|
+
description: 'Test project',
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const response = await fetch(
|
|
39
|
+
'https://dokploy.example.com/api/project.all',
|
|
40
|
+
{
|
|
41
|
+
method: 'GET',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
Authorization: 'Bearer test-token',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(response.ok).toBe(true);
|
|
50
|
+
const projects = await response.json();
|
|
51
|
+
expect(projects).toHaveLength(2);
|
|
52
|
+
expect(projects[0].projectId).toBe('proj_1');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should create a project', async () => {
|
|
56
|
+
server.use(
|
|
57
|
+
http.post(
|
|
58
|
+
'https://dokploy.example.com/api/project.create',
|
|
59
|
+
async ({ request }) => {
|
|
60
|
+
const body = (await request.json()) as {
|
|
61
|
+
name: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
};
|
|
64
|
+
return HttpResponse.json({
|
|
65
|
+
projectId: 'proj_new',
|
|
66
|
+
name: body.name,
|
|
67
|
+
description: body.description || null,
|
|
68
|
+
createdAt: new Date().toISOString(),
|
|
69
|
+
adminId: 'admin_1',
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const response = await fetch(
|
|
76
|
+
'https://dokploy.example.com/api/project.create',
|
|
77
|
+
{
|
|
78
|
+
method: 'POST',
|
|
79
|
+
headers: {
|
|
80
|
+
'Content-Type': 'application/json',
|
|
81
|
+
Authorization: 'Bearer test-token',
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
name: 'New Project',
|
|
85
|
+
description: 'Created by gkm',
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(response.ok).toBe(true);
|
|
91
|
+
const project = await response.json();
|
|
92
|
+
expect(project.projectId).toBe('proj_new');
|
|
93
|
+
expect(project.name).toBe('New Project');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should get a single project', async () => {
|
|
97
|
+
server.use(
|
|
98
|
+
http.post(
|
|
99
|
+
'https://dokploy.example.com/api/project.one',
|
|
100
|
+
async ({ request }) => {
|
|
101
|
+
const body = (await request.json()) as { projectId: string };
|
|
102
|
+
return HttpResponse.json({
|
|
103
|
+
projectId: body.projectId,
|
|
104
|
+
name: 'Test Project',
|
|
105
|
+
environments: [
|
|
106
|
+
{
|
|
107
|
+
environmentId: 'env_1',
|
|
108
|
+
name: 'production',
|
|
109
|
+
description: null,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const response = await fetch(
|
|
118
|
+
'https://dokploy.example.com/api/project.one',
|
|
119
|
+
{
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Content-Type': 'application/json',
|
|
123
|
+
Authorization: 'Bearer test-token',
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify({ projectId: 'proj_1' }),
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(response.ok).toBe(true);
|
|
130
|
+
const project = await response.json();
|
|
131
|
+
expect(project.environments).toHaveLength(1);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('application operations', () => {
|
|
136
|
+
it('should create an application', async () => {
|
|
137
|
+
server.use(
|
|
138
|
+
http.post(
|
|
139
|
+
'https://dokploy.example.com/api/application.create',
|
|
140
|
+
async ({ request }) => {
|
|
141
|
+
const body = (await request.json()) as {
|
|
142
|
+
name: string;
|
|
143
|
+
projectId: string;
|
|
144
|
+
environmentId: string;
|
|
145
|
+
};
|
|
146
|
+
return HttpResponse.json({
|
|
147
|
+
applicationId: 'app_new',
|
|
148
|
+
name: body.name,
|
|
149
|
+
projectId: body.projectId,
|
|
150
|
+
environmentId: body.environmentId,
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const response = await fetch(
|
|
157
|
+
'https://dokploy.example.com/api/application.create',
|
|
158
|
+
{
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: {
|
|
161
|
+
'Content-Type': 'application/json',
|
|
162
|
+
Authorization: 'Bearer test-token',
|
|
163
|
+
},
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
name: 'api',
|
|
166
|
+
projectId: 'proj_1',
|
|
167
|
+
environmentId: 'env_1',
|
|
168
|
+
}),
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
expect(response.ok).toBe(true);
|
|
173
|
+
const app = await response.json();
|
|
174
|
+
expect(app.applicationId).toBe('app_new');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should update application with registry', async () => {
|
|
178
|
+
server.use(
|
|
179
|
+
http.post(
|
|
180
|
+
'https://dokploy.example.com/api/application.update',
|
|
181
|
+
async ({ request }) => {
|
|
182
|
+
const _body = (await request.json()) as {
|
|
183
|
+
applicationId: string;
|
|
184
|
+
registryId: string;
|
|
185
|
+
};
|
|
186
|
+
return HttpResponse.json({ success: true });
|
|
187
|
+
},
|
|
188
|
+
),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const response = await fetch(
|
|
192
|
+
'https://dokploy.example.com/api/application.update',
|
|
193
|
+
{
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: {
|
|
196
|
+
'Content-Type': 'application/json',
|
|
197
|
+
Authorization: 'Bearer test-token',
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
applicationId: 'app_1',
|
|
201
|
+
registryId: 'reg_1',
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
expect(response.ok).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('registry operations', () => {
|
|
211
|
+
it('should list registries', async () => {
|
|
212
|
+
server.use(
|
|
213
|
+
http.get('https://dokploy.example.com/api/registry.all', () => {
|
|
214
|
+
return HttpResponse.json([
|
|
215
|
+
{
|
|
216
|
+
registryId: 'reg_1',
|
|
217
|
+
registryName: 'GitHub Container Registry',
|
|
218
|
+
registryUrl: 'ghcr.io',
|
|
219
|
+
username: 'myorg',
|
|
220
|
+
imagePrefix: null,
|
|
221
|
+
},
|
|
222
|
+
]);
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const response = await fetch(
|
|
227
|
+
'https://dokploy.example.com/api/registry.all',
|
|
228
|
+
{
|
|
229
|
+
method: 'GET',
|
|
230
|
+
headers: {
|
|
231
|
+
'Content-Type': 'application/json',
|
|
232
|
+
Authorization: 'Bearer test-token',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(response.ok).toBe(true);
|
|
238
|
+
const registries = await response.json();
|
|
239
|
+
expect(registries).toHaveLength(1);
|
|
240
|
+
expect(registries[0].registryName).toBe('GitHub Container Registry');
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe('environment operations', () => {
|
|
245
|
+
it('should create an environment', async () => {
|
|
246
|
+
server.use(
|
|
247
|
+
http.post(
|
|
248
|
+
'https://dokploy.example.com/api/environment.create',
|
|
249
|
+
async ({ request }) => {
|
|
250
|
+
const body = (await request.json()) as {
|
|
251
|
+
projectId: string;
|
|
252
|
+
name: string;
|
|
253
|
+
};
|
|
254
|
+
return HttpResponse.json({
|
|
255
|
+
environmentId: 'env_new',
|
|
256
|
+
name: body.name,
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const response = await fetch(
|
|
263
|
+
'https://dokploy.example.com/api/environment.create',
|
|
264
|
+
{
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers: {
|
|
267
|
+
'Content-Type': 'application/json',
|
|
268
|
+
Authorization: 'Bearer test-token',
|
|
269
|
+
},
|
|
270
|
+
body: JSON.stringify({
|
|
271
|
+
projectId: 'proj_1',
|
|
272
|
+
name: 'production',
|
|
273
|
+
description: 'Production environment',
|
|
274
|
+
}),
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(response.ok).toBe(true);
|
|
279
|
+
const env = await response.json();
|
|
280
|
+
expect(env.environmentId).toBe('env_new');
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe('error handling', () => {
|
|
285
|
+
it('should handle API errors', async () => {
|
|
286
|
+
server.use(
|
|
287
|
+
http.get('https://dokploy.example.com/api/project.all', () => {
|
|
288
|
+
return HttpResponse.json(
|
|
289
|
+
{ message: 'Unauthorized' },
|
|
290
|
+
{ status: 401 },
|
|
291
|
+
);
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const response = await fetch(
|
|
296
|
+
'https://dokploy.example.com/api/project.all',
|
|
297
|
+
{
|
|
298
|
+
method: 'GET',
|
|
299
|
+
headers: {
|
|
300
|
+
'Content-Type': 'application/json',
|
|
301
|
+
Authorization: 'Bearer invalid-token',
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
expect(response.ok).toBe(false);
|
|
307
|
+
expect(response.status).toBe(401);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should handle network errors', async () => {
|
|
311
|
+
server.use(
|
|
312
|
+
http.get('https://dokploy.example.com/api/project.all', () => {
|
|
313
|
+
return HttpResponse.error();
|
|
314
|
+
}),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
await fetch('https://dokploy.example.com/api/project.all');
|
|
319
|
+
expect.fail('Should have thrown');
|
|
320
|
+
} catch {
|
|
321
|
+
// Expected network error
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
describe('deploy init', () => {
|
|
328
|
+
let tempDir: string;
|
|
329
|
+
|
|
330
|
+
beforeEach(async () => {
|
|
331
|
+
tempDir = join(tmpdir(), `gkm-deploy-init-test-${Date.now()}`);
|
|
332
|
+
await mkdir(tempDir, { recursive: true });
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
afterEach(async () => {
|
|
336
|
+
if (existsSync(tempDir)) {
|
|
337
|
+
await rm(tempDir, { recursive: true });
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('config file updates', () => {
|
|
342
|
+
it('should handle config without providers section', async () => {
|
|
343
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
344
|
+
const originalConfig = `import { defineConfig } from '@geekmidas/cli';
|
|
345
|
+
|
|
346
|
+
export default defineConfig({
|
|
347
|
+
routes: 'src/endpoints/**/*.ts',
|
|
348
|
+
envParser: './src/env.ts',
|
|
349
|
+
});`;
|
|
350
|
+
|
|
351
|
+
await writeFile(configPath, originalConfig);
|
|
352
|
+
|
|
353
|
+
// Simulate what updateConfig does
|
|
354
|
+
const content = await readFile(configPath, 'utf-8');
|
|
355
|
+
|
|
356
|
+
// Check that providers section doesn't exist
|
|
357
|
+
expect(content.includes('providers:')).toBe(false);
|
|
358
|
+
|
|
359
|
+
// The update would add providers section
|
|
360
|
+
const config = {
|
|
361
|
+
endpoint: 'https://dokploy.example.com',
|
|
362
|
+
projectId: 'proj_123',
|
|
363
|
+
applicationId: 'app_456',
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const newContent = content.replace(
|
|
367
|
+
/}\s*\)\s*;?\s*$/,
|
|
368
|
+
`
|
|
369
|
+
providers: {
|
|
370
|
+
dokploy: {
|
|
371
|
+
endpoint: '${config.endpoint}',
|
|
372
|
+
projectId: '${config.projectId}',
|
|
373
|
+
applicationId: '${config.applicationId}',
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
});`,
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
expect(newContent).toContain('providers:');
|
|
380
|
+
expect(newContent).toContain('dokploy:');
|
|
381
|
+
expect(newContent).toContain("endpoint: 'https://dokploy.example.com'");
|
|
382
|
+
expect(newContent).toContain("projectId: 'proj_123'");
|
|
383
|
+
expect(newContent).toContain("applicationId: 'app_456'");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should handle config with existing providers section', async () => {
|
|
387
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
388
|
+
const originalConfig = `import { defineConfig } from '@geekmidas/cli';
|
|
389
|
+
|
|
390
|
+
export default defineConfig({
|
|
391
|
+
routes: 'src/endpoints/**/*.ts',
|
|
392
|
+
providers: {
|
|
393
|
+
server: true,
|
|
394
|
+
},
|
|
395
|
+
});`;
|
|
396
|
+
|
|
397
|
+
await writeFile(configPath, originalConfig);
|
|
398
|
+
|
|
399
|
+
const content = await readFile(configPath, 'utf-8');
|
|
400
|
+
|
|
401
|
+
// Check that providers section exists
|
|
402
|
+
expect(content.includes('providers:')).toBe(true);
|
|
403
|
+
|
|
404
|
+
// The update would add dokploy to providers
|
|
405
|
+
const config = {
|
|
406
|
+
endpoint: 'https://dokploy.example.com',
|
|
407
|
+
projectId: 'proj_123',
|
|
408
|
+
applicationId: 'app_456',
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const newContent = content.replace(
|
|
412
|
+
/providers:\s*\{/,
|
|
413
|
+
`providers: {
|
|
414
|
+
dokploy: {
|
|
415
|
+
endpoint: '${config.endpoint}',
|
|
416
|
+
projectId: '${config.projectId}',
|
|
417
|
+
applicationId: '${config.applicationId}',
|
|
418
|
+
},`,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
expect(newContent).toContain('dokploy:');
|
|
422
|
+
expect(newContent).toContain('server: true');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should update existing dokploy config', async () => {
|
|
426
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
427
|
+
const originalConfig = `import { defineConfig } from '@geekmidas/cli';
|
|
428
|
+
|
|
429
|
+
export default defineConfig({
|
|
430
|
+
routes: 'src/endpoints/**/*.ts',
|
|
431
|
+
providers: {
|
|
432
|
+
dokploy: {
|
|
433
|
+
endpoint: 'https://old.dokploy.com',
|
|
434
|
+
projectId: 'old_proj',
|
|
435
|
+
applicationId: 'old_app',
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
});`;
|
|
439
|
+
|
|
440
|
+
await writeFile(configPath, originalConfig);
|
|
441
|
+
|
|
442
|
+
const content = await readFile(configPath, 'utf-8');
|
|
443
|
+
|
|
444
|
+
// Check existing dokploy config
|
|
445
|
+
expect(content.includes('dokploy:')).toBe(true);
|
|
446
|
+
expect(content.includes('old.dokploy.com')).toBe(true);
|
|
447
|
+
|
|
448
|
+
// The update would replace dokploy config
|
|
449
|
+
const config = {
|
|
450
|
+
endpoint: 'https://new.dokploy.com',
|
|
451
|
+
projectId: 'new_proj',
|
|
452
|
+
applicationId: 'new_app',
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
const newContent = content.replace(
|
|
456
|
+
/dokploy:\s*\{[^}]*\}/,
|
|
457
|
+
`dokploy: {
|
|
458
|
+
endpoint: '${config.endpoint}',
|
|
459
|
+
projectId: '${config.projectId}',
|
|
460
|
+
applicationId: '${config.applicationId}',
|
|
461
|
+
}`,
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(newContent).toContain('new.dokploy.com');
|
|
465
|
+
expect(newContent).toContain('new_proj');
|
|
466
|
+
expect(newContent).toContain('new_app');
|
|
467
|
+
expect(newContent).not.toContain('old.dokploy.com');
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe('validation', () => {
|
|
472
|
+
it('should require endpoint', () => {
|
|
473
|
+
const options = {
|
|
474
|
+
projectName: 'test',
|
|
475
|
+
appName: 'api',
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// Missing endpoint should fail
|
|
479
|
+
expect(options).not.toHaveProperty('endpoint');
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it('should require project name', () => {
|
|
483
|
+
const options = {
|
|
484
|
+
endpoint: 'https://dokploy.example.com',
|
|
485
|
+
appName: 'api',
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// Missing projectName should fail
|
|
489
|
+
expect(options).not.toHaveProperty('projectName');
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should require app name', () => {
|
|
493
|
+
const options = {
|
|
494
|
+
endpoint: 'https://dokploy.example.com',
|
|
495
|
+
projectName: 'test',
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Missing appName should fail
|
|
499
|
+
expect(options).not.toHaveProperty('appName');
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
describe('DokployDeployConfig type', () => {
|
|
505
|
+
it('should have required fields', () => {
|
|
506
|
+
interface DokployDeployConfig {
|
|
507
|
+
endpoint: string;
|
|
508
|
+
projectId: string;
|
|
509
|
+
applicationId: string;
|
|
510
|
+
registry?: string;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const config: DokployDeployConfig = {
|
|
514
|
+
endpoint: 'https://dokploy.example.com',
|
|
515
|
+
projectId: 'proj_123',
|
|
516
|
+
applicationId: 'app_456',
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
expect(config.endpoint).toBe('https://dokploy.example.com');
|
|
520
|
+
expect(config.projectId).toBe('proj_123');
|
|
521
|
+
expect(config.applicationId).toBe('app_456');
|
|
522
|
+
expect(config.registry).toBeUndefined();
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('should allow optional registry', () => {
|
|
526
|
+
interface DokployDeployConfig {
|
|
527
|
+
endpoint: string;
|
|
528
|
+
projectId: string;
|
|
529
|
+
applicationId: string;
|
|
530
|
+
registry?: string;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const config: DokployDeployConfig = {
|
|
534
|
+
endpoint: 'https://dokploy.example.com',
|
|
535
|
+
projectId: 'proj_123',
|
|
536
|
+
applicationId: 'app_456',
|
|
537
|
+
registry: 'ghcr.io/myorg',
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
expect(config.registry).toBe('ghcr.io/myorg');
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
describe('updateConfig', () => {
|
|
545
|
+
let tempDir: string;
|
|
546
|
+
|
|
547
|
+
beforeEach(async () => {
|
|
548
|
+
tempDir = join(tmpdir(), `gkm-update-config-test-${Date.now()}`);
|
|
549
|
+
await mkdir(tempDir, { recursive: true });
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
afterEach(async () => {
|
|
553
|
+
if (existsSync(tempDir)) {
|
|
554
|
+
await rm(tempDir, { recursive: true });
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('should add providers section when missing', async () => {
|
|
559
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
560
|
+
await writeFile(
|
|
561
|
+
configPath,
|
|
562
|
+
`import { defineConfig } from '@geekmidas/cli';
|
|
563
|
+
|
|
564
|
+
export default defineConfig({
|
|
565
|
+
routes: 'src/endpoints/**/*.ts',
|
|
566
|
+
envParser: './src/env.ts',
|
|
567
|
+
});`,
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
await updateConfig(
|
|
571
|
+
{
|
|
572
|
+
endpoint: 'https://dokploy.example.com',
|
|
573
|
+
projectId: 'proj_123',
|
|
574
|
+
applicationId: 'app_456',
|
|
575
|
+
},
|
|
576
|
+
tempDir,
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const content = await readFile(configPath, 'utf-8');
|
|
580
|
+
expect(content).toContain('providers:');
|
|
581
|
+
expect(content).toContain('dokploy:');
|
|
582
|
+
expect(content).toContain("endpoint: 'https://dokploy.example.com'");
|
|
583
|
+
expect(content).toContain("projectId: 'proj_123'");
|
|
584
|
+
expect(content).toContain("applicationId: 'app_456'");
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it('should add dokploy to existing providers section', async () => {
|
|
588
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
589
|
+
await writeFile(
|
|
590
|
+
configPath,
|
|
591
|
+
`import { defineConfig } from '@geekmidas/cli';
|
|
592
|
+
|
|
593
|
+
export default defineConfig({
|
|
594
|
+
routes: 'src/endpoints/**/*.ts',
|
|
595
|
+
providers: {
|
|
596
|
+
server: true,
|
|
597
|
+
},
|
|
598
|
+
});`,
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
await updateConfig(
|
|
602
|
+
{
|
|
603
|
+
endpoint: 'https://dokploy.example.com',
|
|
604
|
+
projectId: 'proj_123',
|
|
605
|
+
applicationId: 'app_456',
|
|
606
|
+
},
|
|
607
|
+
tempDir,
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
const content = await readFile(configPath, 'utf-8');
|
|
611
|
+
expect(content).toContain('dokploy:');
|
|
612
|
+
expect(content).toContain('server: true');
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it('should update existing dokploy config', async () => {
|
|
616
|
+
const configPath = join(tempDir, 'gkm.config.ts');
|
|
617
|
+
await writeFile(
|
|
618
|
+
configPath,
|
|
619
|
+
`import { defineConfig } from '@geekmidas/cli';
|
|
620
|
+
|
|
621
|
+
export default defineConfig({
|
|
622
|
+
routes: 'src/endpoints/**/*.ts',
|
|
623
|
+
providers: {
|
|
624
|
+
dokploy: {
|
|
625
|
+
endpoint: 'https://old.dokploy.com',
|
|
626
|
+
projectId: 'old_proj',
|
|
627
|
+
applicationId: 'old_app',
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
});`,
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
await updateConfig(
|
|
634
|
+
{
|
|
635
|
+
endpoint: 'https://new.dokploy.com',
|
|
636
|
+
projectId: 'new_proj',
|
|
637
|
+
applicationId: 'new_app',
|
|
638
|
+
},
|
|
639
|
+
tempDir,
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
const content = await readFile(configPath, 'utf-8');
|
|
643
|
+
expect(content).toContain('https://new.dokploy.com');
|
|
644
|
+
expect(content).toContain('new_proj');
|
|
645
|
+
expect(content).toContain('new_app');
|
|
646
|
+
expect(content).not.toContain('old.dokploy.com');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('should handle missing config file gracefully', async () => {
|
|
650
|
+
// No config file exists - should not throw, just warn
|
|
651
|
+
await expect(
|
|
652
|
+
updateConfig(
|
|
653
|
+
{
|
|
654
|
+
endpoint: 'https://dokploy.example.com',
|
|
655
|
+
projectId: 'proj_123',
|
|
656
|
+
applicationId: 'app_456',
|
|
657
|
+
},
|
|
658
|
+
tempDir,
|
|
659
|
+
),
|
|
660
|
+
).resolves.toBeUndefined();
|
|
661
|
+
});
|
|
662
|
+
});
|