@geekmidas/cli 0.17.0 → 0.19.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-C74EKlNa.cjs → bundler-CyHg1v_T.cjs} +3 -3
- package/dist/{bundler-C74EKlNa.cjs.map → bundler-CyHg1v_T.cjs.map} +1 -1
- package/dist/{bundler-B6z6HEeh.mjs → bundler-DQIuE3Kn.mjs} +3 -3
- package/dist/{bundler-B6z6HEeh.mjs.map → bundler-DQIuE3Kn.mjs.map} +1 -1
- package/dist/{config-DYULeEv8.mjs → config-BaYqrF3n.mjs} +48 -10
- package/dist/config-BaYqrF3n.mjs.map +1 -0
- package/dist/{config-AmInkU7k.cjs → config-CxrLu8ia.cjs} +53 -9
- package/dist/config-CxrLu8ia.cjs.map +1 -0
- package/dist/config.cjs +4 -1
- package/dist/config.d.cts +27 -2
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +27 -2
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +3 -2
- package/dist/dokploy-api-B0w17y4_.mjs +3 -0
- package/dist/{dokploy-api-CaETb2L6.mjs → dokploy-api-B9qR2Yn1.mjs} +1 -1
- package/dist/{dokploy-api-CaETb2L6.mjs.map → dokploy-api-B9qR2Yn1.mjs.map} +1 -1
- package/dist/dokploy-api-BnGeUqN4.cjs +3 -0
- package/dist/{dokploy-api-C7F9VykY.cjs → dokploy-api-C5czOZoc.cjs} +1 -1
- package/dist/{dokploy-api-C7F9VykY.cjs.map → dokploy-api-C5czOZoc.cjs.map} +1 -1
- package/dist/{encryption-D7Efcdi9.cjs → encryption-BAz0xQ1Q.cjs} +1 -1
- package/dist/{encryption-D7Efcdi9.cjs.map → encryption-BAz0xQ1Q.cjs.map} +1 -1
- package/dist/{encryption-h4Nb6W-M.mjs → encryption-JtMsiGNp.mjs} +2 -2
- package/dist/{encryption-h4Nb6W-M.mjs.map → encryption-JtMsiGNp.mjs.map} +1 -1
- package/dist/index-CWN-bgrO.d.mts +495 -0
- package/dist/index-CWN-bgrO.d.mts.map +1 -0
- package/dist/index-DEWYvYvg.d.cts +495 -0
- package/dist/index-DEWYvYvg.d.cts.map +1 -0
- package/dist/index.cjs +2644 -564
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2639 -564
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CZVcfxk-.mjs → openapi-CgqR6Jkw.mjs} +3 -3
- package/dist/{openapi-CZVcfxk-.mjs.map → openapi-CgqR6Jkw.mjs.map} +1 -1
- package/dist/{openapi-C89hhkZC.cjs → openapi-DfpxS0xv.cjs} +8 -2
- package/dist/{openapi-C89hhkZC.cjs.map → openapi-DfpxS0xv.cjs.map} +1 -1
- package/dist/{openapi-react-query-CM2_qlW9.mjs → openapi-react-query-5rSortLH.mjs} +1 -1
- package/dist/{openapi-react-query-CM2_qlW9.mjs.map → openapi-react-query-5rSortLH.mjs.map} +1 -1
- package/dist/{openapi-react-query-iKjfLzff.cjs → openapi-react-query-DvNpdDpM.cjs} +1 -1
- package/dist/{openapi-react-query-iKjfLzff.cjs.map → openapi-react-query-DvNpdDpM.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +3 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -2
- package/dist/{storage-Bn3K9Ccu.cjs → storage-BPRgh3DU.cjs} +136 -5
- package/dist/storage-BPRgh3DU.cjs.map +1 -0
- package/dist/{storage-nkGIjeXt.mjs → storage-DNj_I11J.mjs} +1 -1
- package/dist/storage-Dhst7BhI.mjs +272 -0
- package/dist/storage-Dhst7BhI.mjs.map +1 -0
- package/dist/{storage-UfyTn7Zm.cjs → storage-fOR8dMu5.cjs} +1 -1
- package/dist/{types-iFk5ms7y.d.mts → types-K2uQJ-FO.d.mts} +2 -2
- package/dist/{types-BgaMXsUa.d.cts.map → types-K2uQJ-FO.d.mts.map} +1 -1
- package/dist/{types-BgaMXsUa.d.cts → types-l53qUmGt.d.cts} +2 -2
- package/dist/{types-iFk5ms7y.d.mts.map → types-l53qUmGt.d.cts.map} +1 -1
- package/dist/workspace/index.cjs +19 -0
- package/dist/workspace/index.d.cts +3 -0
- package/dist/workspace/index.d.mts +3 -0
- package/dist/workspace/index.mjs +3 -0
- package/dist/workspace-CPLEZDZf.mjs +3788 -0
- package/dist/workspace-CPLEZDZf.mjs.map +1 -0
- package/dist/workspace-iWgBlX6h.cjs +3885 -0
- package/dist/workspace-iWgBlX6h.cjs.map +1 -0
- package/package.json +9 -4
- package/src/build/__tests__/workspace-build.spec.ts +215 -0
- package/src/build/index.ts +189 -1
- package/src/config.ts +71 -14
- package/src/deploy/__tests__/docker.spec.ts +1 -1
- package/src/deploy/__tests__/index.spec.ts +305 -1
- package/src/deploy/index.ts +426 -4
- package/src/deploy/types.ts +32 -0
- package/src/dev/__tests__/index.spec.ts +572 -1
- package/src/dev/index.ts +582 -2
- package/src/docker/__tests__/compose.spec.ts +425 -0
- package/src/docker/__tests__/templates.spec.ts +145 -0
- package/src/docker/compose.ts +248 -0
- package/src/docker/index.ts +159 -3
- package/src/docker/templates.ts +223 -4
- package/src/index.ts +24 -0
- package/src/init/__tests__/generators.spec.ts +17 -24
- package/src/init/__tests__/init.spec.ts +157 -5
- package/src/init/generators/auth.ts +220 -0
- package/src/init/generators/config.ts +61 -4
- package/src/init/generators/docker.ts +115 -8
- package/src/init/generators/env.ts +7 -127
- package/src/init/generators/index.ts +1 -0
- package/src/init/generators/models.ts +3 -1
- package/src/init/generators/monorepo.ts +154 -10
- package/src/init/generators/package.ts +5 -3
- package/src/init/generators/web.ts +213 -0
- package/src/init/index.ts +290 -58
- package/src/init/templates/api.ts +38 -29
- package/src/init/templates/index.ts +132 -4
- package/src/init/templates/minimal.ts +33 -35
- package/src/init/templates/serverless.ts +16 -19
- package/src/init/templates/worker.ts +50 -25
- package/src/init/versions.ts +47 -0
- package/src/secrets/keystore.ts +144 -0
- package/src/secrets/storage.ts +109 -6
- package/src/test/index.ts +97 -0
- package/src/workspace/__tests__/client-generator.spec.ts +357 -0
- package/src/workspace/__tests__/index.spec.ts +543 -0
- package/src/workspace/__tests__/schema.spec.ts +519 -0
- package/src/workspace/__tests__/type-inference.spec.ts +251 -0
- package/src/workspace/client-generator.ts +307 -0
- package/src/workspace/index.ts +372 -0
- package/src/workspace/schema.ts +368 -0
- package/src/workspace/types.ts +336 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.ts +1 -0
- package/dist/config-AmInkU7k.cjs.map +0 -1
- package/dist/config-DYULeEv8.mjs.map +0 -1
- package/dist/dokploy-api-B7KxOQr3.cjs +0 -3
- package/dist/dokploy-api-DHvfmWbi.mjs +0 -3
- package/dist/storage-BaOP55oq.mjs +0 -147
- package/dist/storage-BaOP55oq.mjs.map +0 -1
- package/dist/storage-Bn3K9Ccu.cjs.map +0 -1
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { HttpResponse, http } from 'msw';
|
|
2
2
|
import { setupServer } from 'msw/node';
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { NormalizedWorkspace } from '../../workspace/types.js';
|
|
4
5
|
import { DokployApi } from '../dokploy-api';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
generateTag,
|
|
8
|
+
provisionServices,
|
|
9
|
+
workspaceDeployCommand,
|
|
10
|
+
} from '../index';
|
|
11
|
+
import type { DeployOptions } from '../types';
|
|
6
12
|
|
|
7
13
|
const BASE_URL = 'https://dokploy.example.com';
|
|
8
14
|
|
|
@@ -399,3 +405,301 @@ describe('generateTag edge cases', () => {
|
|
|
399
405
|
}
|
|
400
406
|
});
|
|
401
407
|
});
|
|
408
|
+
|
|
409
|
+
describe('workspaceDeployCommand', () => {
|
|
410
|
+
/** Create a minimal workspace config for testing */
|
|
411
|
+
function createWorkspace(
|
|
412
|
+
overrides: Partial<NormalizedWorkspace> = {},
|
|
413
|
+
): NormalizedWorkspace {
|
|
414
|
+
return {
|
|
415
|
+
name: 'test-workspace',
|
|
416
|
+
root: '/workspace',
|
|
417
|
+
apps: {
|
|
418
|
+
api: {
|
|
419
|
+
type: 'backend',
|
|
420
|
+
path: 'apps/api',
|
|
421
|
+
port: 3000,
|
|
422
|
+
dependencies: [],
|
|
423
|
+
},
|
|
424
|
+
web: {
|
|
425
|
+
type: 'frontend',
|
|
426
|
+
path: 'apps/web',
|
|
427
|
+
port: 3001,
|
|
428
|
+
dependencies: ['api'],
|
|
429
|
+
framework: 'nextjs',
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
services: {},
|
|
433
|
+
deploy: { default: 'dokploy' },
|
|
434
|
+
shared: { packages: [] },
|
|
435
|
+
secrets: {},
|
|
436
|
+
...overrides,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
describe('provider validation', () => {
|
|
441
|
+
it('should reject non-dokploy providers', async () => {
|
|
442
|
+
const workspace = createWorkspace();
|
|
443
|
+
const options: DeployOptions = {
|
|
444
|
+
provider: 'docker',
|
|
445
|
+
stage: 'production',
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
|
|
449
|
+
'Workspace deployment only supports Dokploy',
|
|
450
|
+
);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should reject aws-lambda provider', async () => {
|
|
454
|
+
const workspace = createWorkspace();
|
|
455
|
+
const options: DeployOptions = {
|
|
456
|
+
provider: 'aws-lambda',
|
|
457
|
+
stage: 'production',
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
|
|
461
|
+
'Workspace deployment only supports Dokploy',
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
describe('selective app deployment', () => {
|
|
467
|
+
it('should validate selected apps exist', async () => {
|
|
468
|
+
const workspace = createWorkspace();
|
|
469
|
+
const options: DeployOptions = {
|
|
470
|
+
provider: 'dokploy',
|
|
471
|
+
stage: 'production',
|
|
472
|
+
apps: ['api', 'nonexistent'],
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// Mock credentials check to fail fast
|
|
476
|
+
vi.mock('../../auth', () => ({
|
|
477
|
+
getDokployCredentials: vi.fn().mockResolvedValue(null),
|
|
478
|
+
getDokployRegistryId: vi.fn().mockResolvedValue(null),
|
|
479
|
+
storeDokployCredentials: vi.fn(),
|
|
480
|
+
validateDokployToken: vi.fn().mockResolvedValue(true),
|
|
481
|
+
}));
|
|
482
|
+
|
|
483
|
+
await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
|
|
484
|
+
'Unknown apps: nonexistent',
|
|
485
|
+
);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should filter selected apps while maintaining dependency order', () => {
|
|
489
|
+
// Test the filtering logic directly
|
|
490
|
+
const buildOrder = ['api', 'auth', 'web', 'admin'];
|
|
491
|
+
const selectedApps = ['web', 'api'];
|
|
492
|
+
|
|
493
|
+
const filtered = buildOrder.filter((name) => selectedApps.includes(name));
|
|
494
|
+
|
|
495
|
+
// Should be in dependency order (api before web)
|
|
496
|
+
expect(filtered).toEqual(['api', 'web']);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
describe('dependency ordering', () => {
|
|
501
|
+
it('should deploy backends before dependent frontends', () => {
|
|
502
|
+
// The getAppBuildOrder function returns topologically sorted order
|
|
503
|
+
// This is a unit test for the ordering logic
|
|
504
|
+
const workspace = createWorkspace({
|
|
505
|
+
apps: {
|
|
506
|
+
api: {
|
|
507
|
+
type: 'backend',
|
|
508
|
+
path: 'apps/api',
|
|
509
|
+
port: 3000,
|
|
510
|
+
dependencies: [],
|
|
511
|
+
},
|
|
512
|
+
auth: {
|
|
513
|
+
type: 'backend',
|
|
514
|
+
path: 'apps/auth',
|
|
515
|
+
port: 3001,
|
|
516
|
+
dependencies: [],
|
|
517
|
+
},
|
|
518
|
+
web: {
|
|
519
|
+
type: 'frontend',
|
|
520
|
+
path: 'apps/web',
|
|
521
|
+
port: 3002,
|
|
522
|
+
dependencies: ['api', 'auth'],
|
|
523
|
+
framework: 'nextjs',
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// Import getAppBuildOrder to verify ordering
|
|
529
|
+
const { getAppBuildOrder } = require('../../workspace/index.js');
|
|
530
|
+
const order = getAppBuildOrder(workspace);
|
|
531
|
+
|
|
532
|
+
// api and auth should come before web
|
|
533
|
+
const apiIndex = order.indexOf('api');
|
|
534
|
+
const authIndex = order.indexOf('auth');
|
|
535
|
+
const webIndex = order.indexOf('web');
|
|
536
|
+
|
|
537
|
+
expect(apiIndex).toBeLessThan(webIndex);
|
|
538
|
+
expect(authIndex).toBeLessThan(webIndex);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it('should handle chain dependencies correctly', () => {
|
|
542
|
+
const workspace = createWorkspace({
|
|
543
|
+
apps: {
|
|
544
|
+
db: {
|
|
545
|
+
type: 'backend',
|
|
546
|
+
path: 'apps/db',
|
|
547
|
+
port: 3000,
|
|
548
|
+
dependencies: [],
|
|
549
|
+
},
|
|
550
|
+
api: {
|
|
551
|
+
type: 'backend',
|
|
552
|
+
path: 'apps/api',
|
|
553
|
+
port: 3001,
|
|
554
|
+
dependencies: ['db'],
|
|
555
|
+
},
|
|
556
|
+
web: {
|
|
557
|
+
type: 'frontend',
|
|
558
|
+
path: 'apps/web',
|
|
559
|
+
port: 3002,
|
|
560
|
+
dependencies: ['api'],
|
|
561
|
+
framework: 'nextjs',
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
const { getAppBuildOrder } = require('../../workspace/index.js');
|
|
567
|
+
const order = getAppBuildOrder(workspace);
|
|
568
|
+
|
|
569
|
+
// db -> api -> web
|
|
570
|
+
expect(order.indexOf('db')).toBeLessThan(order.indexOf('api'));
|
|
571
|
+
expect(order.indexOf('api')).toBeLessThan(order.indexOf('web'));
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
describe('environment variable injection', () => {
|
|
576
|
+
it('should inject APP_URL for dependencies', () => {
|
|
577
|
+
// Test the environment variable construction logic
|
|
578
|
+
const deployedAppUrls: Record<string, string> = {
|
|
579
|
+
api: 'http://test-workspace-api:3000',
|
|
580
|
+
auth: 'http://test-workspace-auth:3001',
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const appDependencies = ['api', 'auth'];
|
|
584
|
+
const envVars: string[] = [];
|
|
585
|
+
|
|
586
|
+
for (const dep of appDependencies) {
|
|
587
|
+
const depUrl = deployedAppUrls[dep];
|
|
588
|
+
if (depUrl) {
|
|
589
|
+
envVars.push(`${dep.toUpperCase()}_URL=${depUrl}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
expect(envVars).toContain('API_URL=http://test-workspace-api:3000');
|
|
594
|
+
expect(envVars).toContain('AUTH_URL=http://test-workspace-auth:3001');
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should inject DATABASE_URL for backend apps', () => {
|
|
598
|
+
const workspaceName = 'test-workspace';
|
|
599
|
+
const hasPostgres = true;
|
|
600
|
+
const appType: 'backend' | 'frontend' = 'backend';
|
|
601
|
+
|
|
602
|
+
const envVars: string[] = [];
|
|
603
|
+
|
|
604
|
+
if (appType === 'backend' && hasPostgres) {
|
|
605
|
+
envVars.push(
|
|
606
|
+
`DATABASE_URL=\${DATABASE_URL:-postgresql://postgres:postgres@${workspaceName}-db:5432/app}`,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
expect(envVars).toHaveLength(1);
|
|
611
|
+
expect(envVars[0]).toContain('test-workspace-db');
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('should not inject DATABASE_URL for frontend apps', () => {
|
|
615
|
+
const hasPostgres = true;
|
|
616
|
+
const appType: 'backend' | 'frontend' = 'frontend';
|
|
617
|
+
|
|
618
|
+
const envVars: string[] = [];
|
|
619
|
+
|
|
620
|
+
if (appType === 'backend' && hasPostgres) {
|
|
621
|
+
envVars.push(`DATABASE_URL=...`);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
expect(envVars).toHaveLength(0);
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
describe('image naming', () => {
|
|
629
|
+
it('should construct image name from workspace and app name', () => {
|
|
630
|
+
const workspaceName = 'my-workspace';
|
|
631
|
+
const appName = 'api';
|
|
632
|
+
const imageTag = 'production-2024-01-01T12-00-00';
|
|
633
|
+
const registry = 'ghcr.io/myorg';
|
|
634
|
+
|
|
635
|
+
const imageName = `${workspaceName}-${appName}`;
|
|
636
|
+
const imageRef = registry
|
|
637
|
+
? `${registry}/${imageName}:${imageTag}`
|
|
638
|
+
: `${imageName}:${imageTag}`;
|
|
639
|
+
|
|
640
|
+
expect(imageName).toBe('my-workspace-api');
|
|
641
|
+
expect(imageRef).toBe(
|
|
642
|
+
'ghcr.io/myorg/my-workspace-api:production-2024-01-01T12-00-00',
|
|
643
|
+
);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('should handle workspace name with special characters', () => {
|
|
647
|
+
const workspaceName = 'my-cool-workspace';
|
|
648
|
+
const appName = 'web-frontend';
|
|
649
|
+
|
|
650
|
+
const imageName = `${workspaceName}-${appName}`;
|
|
651
|
+
|
|
652
|
+
expect(imageName).toBe('my-cool-workspace-web-frontend');
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
describe('result types', () => {
|
|
657
|
+
it('should have correct structure for AppDeployResult', () => {
|
|
658
|
+
const successResult = {
|
|
659
|
+
appName: 'api',
|
|
660
|
+
type: 'backend' as const,
|
|
661
|
+
success: true,
|
|
662
|
+
applicationId: 'app_123',
|
|
663
|
+
imageRef: 'ghcr.io/org/api:v1',
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
expect(successResult.appName).toBe('api');
|
|
667
|
+
expect(successResult.type).toBe('backend');
|
|
668
|
+
expect(successResult.success).toBe(true);
|
|
669
|
+
expect(successResult.applicationId).toBe('app_123');
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('should have correct structure for failed AppDeployResult', () => {
|
|
673
|
+
const failedResult = {
|
|
674
|
+
appName: 'web',
|
|
675
|
+
type: 'frontend' as const,
|
|
676
|
+
success: false,
|
|
677
|
+
error: 'Build failed',
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
expect(failedResult.success).toBe(false);
|
|
681
|
+
expect(failedResult.error).toBe('Build failed');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('should have correct structure for WorkspaceDeployResult', () => {
|
|
685
|
+
const result = {
|
|
686
|
+
apps: [
|
|
687
|
+
{ appName: 'api', type: 'backend' as const, success: true },
|
|
688
|
+
{
|
|
689
|
+
appName: 'web',
|
|
690
|
+
type: 'frontend' as const,
|
|
691
|
+
success: false,
|
|
692
|
+
error: 'Failed',
|
|
693
|
+
},
|
|
694
|
+
],
|
|
695
|
+
projectId: 'proj_123',
|
|
696
|
+
successCount: 1,
|
|
697
|
+
failedCount: 1,
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
expect(result.apps).toHaveLength(2);
|
|
701
|
+
expect(result.successCount).toBe(1);
|
|
702
|
+
expect(result.failedCount).toBe(1);
|
|
703
|
+
});
|
|
704
|
+
});
|
|
705
|
+
});
|