@geekmidas/cli 1.10.17 → 1.10.19
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/CHANGELOG.md +12 -0
- package/dist/{bundler-B4AackW5.mjs → bundler-C5xkxnyr.mjs} +2 -2
- package/dist/{bundler-B4AackW5.mjs.map → bundler-C5xkxnyr.mjs.map} +1 -1
- package/dist/{bundler-BhhfkI9T.cjs → bundler-i-az1DZ2.cjs} +2 -2
- package/dist/{bundler-BhhfkI9T.cjs.map → bundler-i-az1DZ2.cjs.map} +1 -1
- package/dist/index.cjs +701 -707
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +689 -695
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-BYxAWwok.cjs → openapi-CsCNpSf8.cjs} +1 -1
- package/dist/{openapi-BYxAWwok.cjs.map → openapi-CsCNpSf8.cjs.map} +1 -1
- package/dist/{openapi-DenF-okj.mjs → openapi-kvwpKbNe.mjs} +1 -1
- package/dist/{openapi-DenF-okj.mjs.map → openapi-kvwpKbNe.mjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{storage-DOEtT2Hr.cjs → storage-ChVQI_G7.cjs} +1 -1
- package/dist/{storage-dbb9RyBl.mjs → storage-CpMNB77O.mjs} +1 -1
- package/dist/{storage-dbb9RyBl.mjs.map → storage-CpMNB77O.mjs.map} +1 -1
- package/dist/{storage-B1wvztiJ.cjs → storage-DLEb8Dkd.cjs} +1 -1
- package/dist/{storage-B1wvztiJ.cjs.map → storage-DLEb8Dkd.cjs.map} +1 -1
- package/dist/{storage-Cs4WBsc4.mjs → storage-mwbL7PhP.mjs} +1 -1
- package/dist/{sync-DGXXSk2v.cjs → sync-BWD_I5Ai.cjs} +2 -2
- package/dist/{sync-DGXXSk2v.cjs.map → sync-BWD_I5Ai.cjs.map} +1 -1
- package/dist/sync-ByaRPBxh.cjs +4 -0
- package/dist/{sync-COnAugP-.mjs → sync-CYBVB64f.mjs} +1 -1
- package/dist/{sync-D_NowTkZ.mjs → sync-lExOTa9t.mjs} +2 -2
- package/dist/{sync-D_NowTkZ.mjs.map → sync-lExOTa9t.mjs.map} +1 -1
- package/package.json +2 -2
- package/src/credentials/__tests__/fullDockerPorts.spec.ts +144 -0
- package/src/credentials/__tests__/helpers.ts +112 -0
- package/src/credentials/__tests__/prepareEntryCredentials.spec.ts +125 -0
- package/src/credentials/__tests__/readonlyDockerPorts.spec.ts +190 -0
- package/src/credentials/__tests__/workspaceCredentials.spec.ts +209 -0
- package/src/credentials/index.ts +826 -0
- package/src/dev/index.ts +48 -830
- package/src/exec/index.ts +120 -0
- package/src/setup/index.ts +4 -1
- package/src/test/index.ts +32 -109
- package/dist/sync-D1Pa30oV.cjs +0 -4
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { prepareEntryCredentials } from '../index';
|
|
6
|
+
import { createPackageJson, createSecretsFile } from './helpers';
|
|
7
|
+
|
|
8
|
+
describe('prepareEntryCredentials', () => {
|
|
9
|
+
let testDir: string;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
testDir = join(
|
|
13
|
+
tmpdir(),
|
|
14
|
+
`gkm-creds-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
15
|
+
);
|
|
16
|
+
mkdirSync(testDir, { recursive: true });
|
|
17
|
+
createPackageJson('my-app', testDir);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
if (existsSync(testDir)) {
|
|
22
|
+
const { rm } = await import('node:fs/promises');
|
|
23
|
+
await rm(testDir, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should return default port 3000', async () => {
|
|
28
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
29
|
+
|
|
30
|
+
expect(result.resolvedPort).toBe(3000);
|
|
31
|
+
expect(result.credentials.PORT).toBe('3000');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should respect explicitPort', async () => {
|
|
35
|
+
const result = await prepareEntryCredentials({
|
|
36
|
+
cwd: testDir,
|
|
37
|
+
explicitPort: 8080,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(result.resolvedPort).toBe(8080);
|
|
41
|
+
expect(result.credentials.PORT).toBe('8080');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should load secrets from development stage by default', async () => {
|
|
45
|
+
createSecretsFile(
|
|
46
|
+
'development',
|
|
47
|
+
{ DATABASE_URL: 'postgresql://localhost/mydb', API_KEY: 'secret' },
|
|
48
|
+
testDir,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
52
|
+
|
|
53
|
+
expect(result.credentials.DATABASE_URL).toBe('postgresql://localhost/mydb');
|
|
54
|
+
expect(result.credentials.API_KEY).toBe('secret');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should load secrets from custom stage', async () => {
|
|
58
|
+
createSecretsFile('staging', { API_KEY: 'staging-key' }, testDir);
|
|
59
|
+
|
|
60
|
+
const result = await prepareEntryCredentials({
|
|
61
|
+
cwd: testDir,
|
|
62
|
+
stages: ['staging'],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(result.credentials.API_KEY).toBe('staging-key');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should not load secrets when stage does not exist', async () => {
|
|
69
|
+
createSecretsFile('development', { API_KEY: 'dev-key' }, testDir);
|
|
70
|
+
|
|
71
|
+
const result = await prepareEntryCredentials({
|
|
72
|
+
cwd: testDir,
|
|
73
|
+
stages: ['production'],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(result.credentials.API_KEY).toBeUndefined();
|
|
77
|
+
expect(result.credentials.PORT).toBe('3000');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should use custom secretsFileName', async () => {
|
|
81
|
+
const result = await prepareEntryCredentials({
|
|
82
|
+
cwd: testDir,
|
|
83
|
+
secretsFileName: 'test-secrets.json',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result.secretsJsonPath).toBe(
|
|
87
|
+
join(testDir, '.gkm', 'test-secrets.json'),
|
|
88
|
+
);
|
|
89
|
+
const content = JSON.parse(readFileSync(result.secretsJsonPath, 'utf-8'));
|
|
90
|
+
expect(content.PORT).toBe('3000');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should have undefined appInfo outside workspace', async () => {
|
|
94
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
95
|
+
|
|
96
|
+
expect(result.appInfo).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should extract appName from package.json', async () => {
|
|
100
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
101
|
+
|
|
102
|
+
expect(result.appName).toBe('my-app');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should extract appName from scoped package', async () => {
|
|
106
|
+
writeFileSync(
|
|
107
|
+
join(testDir, 'package.json'),
|
|
108
|
+
JSON.stringify({ name: '@scope/my-app', version: '0.0.1' }),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
112
|
+
|
|
113
|
+
expect(result.appName).toBe('my-app');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should write credentials JSON with all secrets and PORT', async () => {
|
|
117
|
+
createSecretsFile('development', { JWT_SECRET: 'abc123' }, testDir);
|
|
118
|
+
|
|
119
|
+
const result = await prepareEntryCredentials({ cwd: testDir });
|
|
120
|
+
|
|
121
|
+
const content = JSON.parse(readFileSync(result.secretsJsonPath, 'utf-8'));
|
|
122
|
+
expect(content.PORT).toBe('3000');
|
|
123
|
+
expect(content.JWT_SECRET).toBe('abc123');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { prepareEntryCredentials } from '../index';
|
|
6
|
+
import {
|
|
7
|
+
createDockerCompose,
|
|
8
|
+
createPackageJson,
|
|
9
|
+
createPortState,
|
|
10
|
+
createSecretsFile,
|
|
11
|
+
} from './helpers';
|
|
12
|
+
|
|
13
|
+
describe('readonly Docker port resolution', () => {
|
|
14
|
+
let testDir: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
testDir = join(
|
|
18
|
+
tmpdir(),
|
|
19
|
+
`gkm-ports-ro-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20
|
+
);
|
|
21
|
+
mkdirSync(testDir, { recursive: true });
|
|
22
|
+
createPackageJson('my-app', testDir);
|
|
23
|
+
createDockerCompose(
|
|
24
|
+
[
|
|
25
|
+
{
|
|
26
|
+
name: 'postgres',
|
|
27
|
+
envVar: 'POSTGRES_HOST_PORT',
|
|
28
|
+
defaultPort: 5432,
|
|
29
|
+
containerPort: 5432,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'redis',
|
|
33
|
+
envVar: 'REDIS_HOST_PORT',
|
|
34
|
+
defaultPort: 6379,
|
|
35
|
+
containerPort: 6379,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
testDir,
|
|
39
|
+
);
|
|
40
|
+
createSecretsFile(
|
|
41
|
+
'development',
|
|
42
|
+
{
|
|
43
|
+
DATABASE_URL: 'postgresql://user:pass@postgres:5432/mydb',
|
|
44
|
+
REDIS_URL: 'redis://default:pass@redis:6379',
|
|
45
|
+
POSTGRES_HOST: 'postgres',
|
|
46
|
+
POSTGRES_PORT: '5432',
|
|
47
|
+
CACHE_ENDPOINT: 'redis://default:pass@redis:6379/0',
|
|
48
|
+
EVENT_SUBSCRIBER_CONNECTION_STRING:
|
|
49
|
+
'sqs://key:secret@localstack:4566?region=us-east-1',
|
|
50
|
+
},
|
|
51
|
+
testDir,
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterEach(async () => {
|
|
56
|
+
if (existsSync(testDir)) {
|
|
57
|
+
const { rm } = await import('node:fs/promises');
|
|
58
|
+
await rm(testDir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should rewrite URLs with saved port state', async () => {
|
|
63
|
+
createPortState(
|
|
64
|
+
{ POSTGRES_HOST_PORT: 15432, REDIS_HOST_PORT: 16379 },
|
|
65
|
+
testDir,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const result = await prepareEntryCredentials({
|
|
69
|
+
cwd: testDir,
|
|
70
|
+
resolveDockerPorts: 'readonly',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result.credentials.DATABASE_URL).toBe(
|
|
74
|
+
'postgresql://user:pass@localhost:15432/mydb',
|
|
75
|
+
);
|
|
76
|
+
expect(result.credentials.REDIS_URL).toBe(
|
|
77
|
+
'redis://default:pass@localhost:16379',
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should rewrite _HOST vars to localhost', async () => {
|
|
82
|
+
createPortState({ POSTGRES_HOST_PORT: 15432 }, testDir);
|
|
83
|
+
|
|
84
|
+
const result = await prepareEntryCredentials({
|
|
85
|
+
cwd: testDir,
|
|
86
|
+
resolveDockerPorts: 'readonly',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(result.credentials.POSTGRES_HOST).toBe('localhost');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should rewrite _PORT vars to resolved port', async () => {
|
|
93
|
+
createPortState({ POSTGRES_HOST_PORT: 15432 }, testDir);
|
|
94
|
+
|
|
95
|
+
const result = await prepareEntryCredentials({
|
|
96
|
+
cwd: testDir,
|
|
97
|
+
resolveDockerPorts: 'readonly',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(result.credentials.POSTGRES_PORT).toBe('15432');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should rewrite _ENDPOINT keys', async () => {
|
|
104
|
+
createPortState({ REDIS_HOST_PORT: 16379 }, testDir);
|
|
105
|
+
|
|
106
|
+
const result = await prepareEntryCredentials({
|
|
107
|
+
cwd: testDir,
|
|
108
|
+
resolveDockerPorts: 'readonly',
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(result.credentials.CACHE_ENDPOINT).toBe(
|
|
112
|
+
'redis://default:pass@localhost:16379/0',
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should rewrite _CONNECTION_STRING keys', async () => {
|
|
117
|
+
createDockerCompose(
|
|
118
|
+
[
|
|
119
|
+
{
|
|
120
|
+
name: 'postgres',
|
|
121
|
+
envVar: 'POSTGRES_HOST_PORT',
|
|
122
|
+
defaultPort: 5432,
|
|
123
|
+
containerPort: 5432,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'redis',
|
|
127
|
+
envVar: 'REDIS_HOST_PORT',
|
|
128
|
+
defaultPort: 6379,
|
|
129
|
+
containerPort: 6379,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'localstack',
|
|
133
|
+
envVar: 'LOCALSTACK_HOST_PORT',
|
|
134
|
+
defaultPort: 4566,
|
|
135
|
+
containerPort: 4566,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
testDir,
|
|
139
|
+
);
|
|
140
|
+
createPortState(
|
|
141
|
+
{
|
|
142
|
+
POSTGRES_HOST_PORT: 15432,
|
|
143
|
+
REDIS_HOST_PORT: 16379,
|
|
144
|
+
LOCALSTACK_HOST_PORT: 14566,
|
|
145
|
+
},
|
|
146
|
+
testDir,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const result = await prepareEntryCredentials({
|
|
150
|
+
cwd: testDir,
|
|
151
|
+
resolveDockerPorts: 'readonly',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(result.credentials.EVENT_SUBSCRIBER_CONNECTION_STRING).toContain(
|
|
155
|
+
'@localhost:',
|
|
156
|
+
);
|
|
157
|
+
expect(result.credentials.EVENT_SUBSCRIBER_CONNECTION_STRING).toContain(
|
|
158
|
+
':14566',
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should not rewrite URLs when no saved state exists', async () => {
|
|
163
|
+
const result = await prepareEntryCredentials({
|
|
164
|
+
cwd: testDir,
|
|
165
|
+
resolveDockerPorts: 'readonly',
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(result.credentials.DATABASE_URL).toBe(
|
|
169
|
+
'postgresql://user:pass@postgres:5432/mydb',
|
|
170
|
+
);
|
|
171
|
+
expect(result.credentials.REDIS_URL).toBe(
|
|
172
|
+
'redis://default:pass@redis:6379',
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should not overwrite existing ports.json', async () => {
|
|
177
|
+
const originalPorts = { POSTGRES_HOST_PORT: 15432 };
|
|
178
|
+
createPortState(originalPorts, testDir);
|
|
179
|
+
|
|
180
|
+
await prepareEntryCredentials({
|
|
181
|
+
cwd: testDir,
|
|
182
|
+
resolveDockerPorts: 'readonly',
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const portsAfter = JSON.parse(
|
|
186
|
+
readFileSync(join(testDir, '.gkm', 'ports.json'), 'utf-8'),
|
|
187
|
+
);
|
|
188
|
+
expect(portsAfter).toEqual(originalPorts);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { prepareEntryCredentials } from '../index';
|
|
6
|
+
import {
|
|
7
|
+
createDockerCompose,
|
|
8
|
+
createPackageJson,
|
|
9
|
+
createPortState,
|
|
10
|
+
createSecretsFile,
|
|
11
|
+
createWorkspaceConfig,
|
|
12
|
+
} from './helpers';
|
|
13
|
+
|
|
14
|
+
describe('workspace credentials', () => {
|
|
15
|
+
let testDir: string;
|
|
16
|
+
const originalGkmConfigPath = process.env.GKM_CONFIG_PATH;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
testDir = join(
|
|
20
|
+
tmpdir(),
|
|
21
|
+
`gkm-ws-creds-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
22
|
+
);
|
|
23
|
+
mkdirSync(testDir, { recursive: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(async () => {
|
|
27
|
+
if (originalGkmConfigPath === undefined) {
|
|
28
|
+
delete process.env.GKM_CONFIG_PATH;
|
|
29
|
+
} else {
|
|
30
|
+
process.env.GKM_CONFIG_PATH = originalGkmConfigPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (existsSync(testDir)) {
|
|
34
|
+
const { rm } = await import('node:fs/promises');
|
|
35
|
+
await rm(testDir, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('workspace mode', () => {
|
|
40
|
+
let apiDir: string;
|
|
41
|
+
let webDir: string;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
createWorkspaceConfig(
|
|
45
|
+
{
|
|
46
|
+
api: { type: 'backend', path: 'apps/api', port: 3001 },
|
|
47
|
+
web: {
|
|
48
|
+
type: 'frontend',
|
|
49
|
+
path: 'apps/web',
|
|
50
|
+
port: 3002,
|
|
51
|
+
dependencies: ['api'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
testDir,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
apiDir = join(testDir, 'apps', 'api');
|
|
58
|
+
webDir = join(testDir, 'apps', 'web');
|
|
59
|
+
|
|
60
|
+
createPackageJson('@test/api', apiDir);
|
|
61
|
+
createPackageJson('@test/web', webDir);
|
|
62
|
+
|
|
63
|
+
process.env.GKM_CONFIG_PATH = join(testDir, 'gkm.config.ts');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should populate appInfo in workspace mode', async () => {
|
|
67
|
+
const result = await prepareEntryCredentials({ cwd: apiDir });
|
|
68
|
+
|
|
69
|
+
expect(result.appInfo).toBeDefined();
|
|
70
|
+
expect(result.appInfo?.appName).toBe('api');
|
|
71
|
+
expect(result.appInfo?.workspaceRoot).toBe(testDir);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should resolve port from workspace config', async () => {
|
|
75
|
+
const result = await prepareEntryCredentials({ cwd: apiDir });
|
|
76
|
+
|
|
77
|
+
expect(result.resolvedPort).toBe(3001);
|
|
78
|
+
expect(result.credentials.PORT).toBe('3001');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should inject dependency URLs for frontend app', async () => {
|
|
82
|
+
const result = await prepareEntryCredentials({ cwd: webDir });
|
|
83
|
+
|
|
84
|
+
expect(result.credentials.API_URL).toBe('http://localhost:3001');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should map APP_DATABASE_URL to DATABASE_URL', async () => {
|
|
88
|
+
createSecretsFile(
|
|
89
|
+
'development',
|
|
90
|
+
{
|
|
91
|
+
API_DATABASE_URL: 'postgresql://localhost/apidb',
|
|
92
|
+
WEB_DATABASE_URL: 'postgresql://localhost/webdb',
|
|
93
|
+
},
|
|
94
|
+
testDir,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const result = await prepareEntryCredentials({ cwd: apiDir });
|
|
98
|
+
|
|
99
|
+
expect(result.credentials.DATABASE_URL).toBe(
|
|
100
|
+
'postgresql://localhost/apidb',
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should use app-specific secrets filename by default', async () => {
|
|
105
|
+
const result = await prepareEntryCredentials({ cwd: apiDir });
|
|
106
|
+
|
|
107
|
+
expect(result.secretsJsonPath).toBe(
|
|
108
|
+
join(testDir, '.gkm', 'dev-secrets-api.json'),
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should use custom secretsFileName in workspace mode', async () => {
|
|
113
|
+
const result = await prepareEntryCredentials({
|
|
114
|
+
cwd: apiDir,
|
|
115
|
+
secretsFileName: 'test-secrets.json',
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(result.secretsJsonPath).toBe(
|
|
119
|
+
join(testDir, '.gkm', 'test-secrets.json'),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('exec-style readonly in workspace', () => {
|
|
125
|
+
let apiDir: string;
|
|
126
|
+
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
createWorkspaceConfig(
|
|
129
|
+
{
|
|
130
|
+
api: { type: 'backend', path: 'apps/api', port: 3001 },
|
|
131
|
+
},
|
|
132
|
+
testDir,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
apiDir = join(testDir, 'apps', 'api');
|
|
136
|
+
createPackageJson('@test/api', apiDir);
|
|
137
|
+
|
|
138
|
+
process.env.GKM_CONFIG_PATH = join(testDir, 'gkm.config.ts');
|
|
139
|
+
|
|
140
|
+
createDockerCompose(
|
|
141
|
+
[
|
|
142
|
+
{
|
|
143
|
+
name: 'postgres',
|
|
144
|
+
envVar: 'POSTGRES_HOST_PORT',
|
|
145
|
+
defaultPort: 5432,
|
|
146
|
+
containerPort: 5432,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'redis',
|
|
150
|
+
envVar: 'REDIS_HOST_PORT',
|
|
151
|
+
defaultPort: 6379,
|
|
152
|
+
containerPort: 6379,
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
testDir,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
createSecretsFile(
|
|
159
|
+
'development',
|
|
160
|
+
{
|
|
161
|
+
DATABASE_URL: 'postgresql://user:pass@postgres:5432/mydb',
|
|
162
|
+
REDIS_URL: 'redis://default:pass@redis:6379',
|
|
163
|
+
API_DATABASE_URL: 'postgresql://api:pass@postgres:5432/apidb',
|
|
164
|
+
},
|
|
165
|
+
testDir,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
createPortState(
|
|
169
|
+
{ POSTGRES_HOST_PORT: 25432, REDIS_HOST_PORT: 26379 },
|
|
170
|
+
testDir,
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should resolve ports from saved state and rewrite URLs', async () => {
|
|
175
|
+
const result = await prepareEntryCredentials({
|
|
176
|
+
cwd: apiDir,
|
|
177
|
+
resolveDockerPorts: 'readonly',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(result.credentials.DATABASE_URL).toBe(
|
|
181
|
+
'postgresql://api:pass@localhost:25432/apidb',
|
|
182
|
+
);
|
|
183
|
+
expect(result.credentials.REDIS_URL).toBe(
|
|
184
|
+
'redis://default:pass@localhost:26379',
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should use workspace port, not Docker port, for PORT', async () => {
|
|
189
|
+
const result = await prepareEntryCredentials({
|
|
190
|
+
cwd: apiDir,
|
|
191
|
+
resolveDockerPorts: 'readonly',
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(result.credentials.PORT).toBe('3001');
|
|
195
|
+
expect(result.resolvedPort).toBe(3001);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should have appInfo set alongside readonly ports', async () => {
|
|
199
|
+
const result = await prepareEntryCredentials({
|
|
200
|
+
cwd: apiDir,
|
|
201
|
+
resolveDockerPorts: 'readonly',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(result.appInfo).toBeDefined();
|
|
205
|
+
expect(result.appInfo?.appName).toBe('api');
|
|
206
|
+
expect(result.appName).toBe('api');
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|