@geekmidas/cli 1.10.6 ā 1.10.8
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/README.md +44 -1
- package/dist/index.cjs +54 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +54 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/dev/__tests__/index.spec.ts +69 -0
- package/src/dev/index.ts +27 -9
- package/src/docker/__tests__/compose.spec.ts +6 -1
- package/src/docker/compose.ts +8 -3
- package/src/init/generators/docker.ts +3 -1
- package/src/test/__tests__/index.spec.ts +115 -0
- package/src/test/index.ts +41 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @geekmidas/cli
|
|
2
2
|
|
|
3
|
+
## 1.10.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- š [`ef1754d`](https://github.com/geekmidas/toolbox/commit/ef1754dd96cfc0f6e79a04ac9eaff56e37023f0f) Thanks [@geekmidas](https://github.com/geekmidas)! - fix test and dev commands to inject correct creds on compose
|
|
8
|
+
|
|
9
|
+
## 1.10.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`4a65756`](https://github.com/geekmidas/toolbox/commit/4a6575647cb91b8782182ef0d09cfb685565b6ae) Thanks [@geekmidas](https://github.com/geekmidas)! - Phantom push
|
|
14
|
+
|
|
3
15
|
## 1.10.6
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -549,10 +549,12 @@ services: ['postgres', 'redis', 'rabbitmq']
|
|
|
549
549
|
|
|
550
550
|
| Service | Default Image | Environment Variables |
|
|
551
551
|
|---------|---------------|----------------------|
|
|
552
|
-
| `postgres` | `postgres:
|
|
552
|
+
| `postgres` | `postgres:18-alpine` | `DATABASE_URL`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` |
|
|
553
553
|
| `redis` | `redis:7-alpine` | `REDIS_URL` |
|
|
554
554
|
| `rabbitmq` | `rabbitmq:3-management-alpine` | `RABBITMQ_URL`, `RABBITMQ_USER`, `RABBITMQ_PASSWORD` |
|
|
555
555
|
|
|
556
|
+
> **Note:** Use `gkm dev` or `gkm test` to start services. Running `docker compose up` directly will not inject your encrypted secrets or resolve dynamic ports ā variables like `${POSTGRES_USER:-postgres}` will fall back to defaults that won't match your project credentials.
|
|
557
|
+
|
|
556
558
|
### `gkm dev`
|
|
557
559
|
|
|
558
560
|
Start a development server with hot-reload and optional Telescope debugging dashboard.
|
|
@@ -603,6 +605,47 @@ When files change, the server automatically rebuilds and restarts:
|
|
|
603
605
|
ā
Rebuild complete, restarting server...
|
|
604
606
|
```
|
|
605
607
|
|
|
608
|
+
### `gkm test`
|
|
609
|
+
|
|
610
|
+
Run tests with full environment injection ā secrets, port mappings, and Docker services.
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
gkm test [options]
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**Options:**
|
|
617
|
+
- `--stage <stage>`: Stage to load secrets from (default: `development`)
|
|
618
|
+
- `--run`: Run tests once without watch mode
|
|
619
|
+
- `--watch`: Enable watch mode
|
|
620
|
+
- `--coverage`: Generate coverage report
|
|
621
|
+
- `--ui`: Open Vitest UI
|
|
622
|
+
- `--pattern <pattern>`: Pattern to filter tests
|
|
623
|
+
|
|
624
|
+
**What it does:**
|
|
625
|
+
|
|
626
|
+
1. Loads and decrypts secrets from the specified stage
|
|
627
|
+
2. Starts Docker Compose services (postgres, redis, etc.) with secrets injected
|
|
628
|
+
3. Resolves dynamic host ports and rewrites URLs (`@postgres:5432` ā `@localhost:5434`)
|
|
629
|
+
4. Appends `_test` suffix to all `DATABASE_URL` values
|
|
630
|
+
5. Creates a credentials preload so `Credentials` from `@geekmidas/envkit` is populated
|
|
631
|
+
6. Spawns Vitest with the full environment
|
|
632
|
+
|
|
633
|
+
**Example:**
|
|
634
|
+
```bash
|
|
635
|
+
# Run tests in watch mode
|
|
636
|
+
gkm test
|
|
637
|
+
|
|
638
|
+
# Run tests once
|
|
639
|
+
gkm test --run
|
|
640
|
+
|
|
641
|
+
# Run with coverage
|
|
642
|
+
gkm test --coverage
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
> **Why not `docker compose up` directly?**
|
|
646
|
+
>
|
|
647
|
+
> `gkm dev` and `gkm test` decrypt your secrets and pass them as environment variables to `docker compose up`, ensuring credentials and ports are consistent. Running `docker compose up` directly skips this ā variables like `${POSTGRES_USER:-postgres}` fall back to defaults that won't match your project credentials.
|
|
648
|
+
|
|
606
649
|
### `gkm secrets:init`
|
|
607
650
|
|
|
608
651
|
Initialize secrets for a deployment stage. Generates secure random passwords for configured Docker Compose services.
|
package/dist/index.cjs
CHANGED
|
@@ -35,7 +35,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
35
35
|
|
|
36
36
|
//#region package.json
|
|
37
37
|
var name = "@geekmidas/cli";
|
|
38
|
-
var version = "1.10.
|
|
38
|
+
var version = "1.10.7";
|
|
39
39
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
40
40
|
var private$1 = false;
|
|
41
41
|
var type = "module";
|
|
@@ -1222,10 +1222,25 @@ async function loadSecretsForApp(secretsRoot, appName) {
|
|
|
1222
1222
|
return mapped;
|
|
1223
1223
|
}
|
|
1224
1224
|
/**
|
|
1225
|
+
* Build the environment variables to pass to `docker compose up`.
|
|
1226
|
+
* Merges process.env, secrets, and port mappings so that Docker Compose
|
|
1227
|
+
* can interpolate variables like ${POSTGRES_USER} correctly.
|
|
1228
|
+
* @internal Exported for testing
|
|
1229
|
+
*/
|
|
1230
|
+
function buildDockerComposeEnv(secretsEnv, portEnv) {
|
|
1231
|
+
return {
|
|
1232
|
+
...process.env,
|
|
1233
|
+
...secretsEnv,
|
|
1234
|
+
...portEnv
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1225
1238
|
* Start docker-compose services for the workspace.
|
|
1239
|
+
* Passes both port mappings and secrets to docker-compose so that
|
|
1240
|
+
* variables like POSTGRES_USER/POSTGRES_PASSWORD are available.
|
|
1226
1241
|
* @internal Exported for testing
|
|
1227
1242
|
*/
|
|
1228
|
-
async function startWorkspaceServices(workspace, portEnv) {
|
|
1243
|
+
async function startWorkspaceServices(workspace, portEnv, secretsEnv) {
|
|
1229
1244
|
const services = workspace.services;
|
|
1230
1245
|
if (!services.db && !services.cache && !services.mail) return;
|
|
1231
1246
|
const servicesToStart = [];
|
|
@@ -1243,10 +1258,7 @@ async function startWorkspaceServices(workspace, portEnv) {
|
|
|
1243
1258
|
(0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
|
|
1244
1259
|
cwd: workspace.root,
|
|
1245
1260
|
stdio: "inherit",
|
|
1246
|
-
env:
|
|
1247
|
-
...process.env,
|
|
1248
|
-
...portEnv
|
|
1249
|
-
}
|
|
1261
|
+
env: buildDockerComposeEnv(secretsEnv, portEnv)
|
|
1250
1262
|
});
|
|
1251
1263
|
logger$11.log("ā
Services started");
|
|
1252
1264
|
} catch (error) {
|
|
@@ -1295,8 +1307,9 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1295
1307
|
if (copiedCount > 0) logger$11.log(`\nš¦ Copied ${copiedCount} API client(s)`);
|
|
1296
1308
|
}
|
|
1297
1309
|
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
1298
|
-
await
|
|
1299
|
-
|
|
1310
|
+
const rawSecrets = await loadDevSecrets(workspace);
|
|
1311
|
+
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
|
|
1312
|
+
const secretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
|
|
1300
1313
|
if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
|
|
1301
1314
|
const dependencyEnv = generateAllDependencyEnvVars(workspace);
|
|
1302
1315
|
if (Object.keys(dependencyEnv).length > 0) {
|
|
@@ -2874,7 +2887,9 @@ function generateDockerCompose(options) {
|
|
|
2874
2887
|
const { imageName, registry, port, healthCheckPath, services } = options;
|
|
2875
2888
|
const serviceMap = normalizeServices(services);
|
|
2876
2889
|
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
2877
|
-
let yaml$1 =
|
|
2890
|
+
let yaml$1 = `# Use "gkm dev" or "gkm test" to start services.
|
|
2891
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
2892
|
+
version: '3.8'
|
|
2878
2893
|
|
|
2879
2894
|
services:
|
|
2880
2895
|
api:
|
|
@@ -2990,7 +3005,9 @@ networks:
|
|
|
2990
3005
|
function generateMinimalDockerCompose(options) {
|
|
2991
3006
|
const { imageName, registry, port, healthCheckPath } = options;
|
|
2992
3007
|
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
2993
|
-
return
|
|
3008
|
+
return `# Use "gkm dev" or "gkm test" to start services.
|
|
3009
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
3010
|
+
version: '3.8'
|
|
2994
3011
|
|
|
2995
3012
|
services:
|
|
2996
3013
|
api:
|
|
@@ -3032,7 +3049,8 @@ function generateWorkspaceCompose(workspace, options = {}) {
|
|
|
3032
3049
|
const postgresImage = getInfraServiceImage("postgres", services.db);
|
|
3033
3050
|
const redisImage = getInfraServiceImage("redis", services.cache);
|
|
3034
3051
|
let yaml$1 = `# Docker Compose for ${workspace.name} workspace
|
|
3035
|
-
#
|
|
3052
|
+
# Use "gkm dev" or "gkm test" to start services.
|
|
3053
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
3036
3054
|
|
|
3037
3055
|
services:
|
|
3038
3056
|
`;
|
|
@@ -7057,7 +7075,9 @@ function generateDockerFiles(options, template, dbApps) {
|
|
|
7057
7075
|
environment:
|
|
7058
7076
|
MP_SMTP_AUTH_ACCEPT_ANY: 1
|
|
7059
7077
|
MP_SMTP_AUTH_ALLOW_INSECURE: 1`);
|
|
7060
|
-
let dockerCompose =
|
|
7078
|
+
let dockerCompose = `# Use "gkm dev" or "gkm test" to start services.
|
|
7079
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
7080
|
+
services:
|
|
7061
7081
|
${services.join("\n\n")}
|
|
7062
7082
|
`;
|
|
7063
7083
|
if (volumes.length > 0) dockerCompose += `
|
|
@@ -11364,23 +11384,15 @@ async function testCommand(options = {}) {
|
|
|
11364
11384
|
if (error instanceof Error && error.message.includes("key not found")) console.log(` Decryption key not found for ${stage}`);
|
|
11365
11385
|
else throw error;
|
|
11366
11386
|
}
|
|
11367
|
-
const composePath = (0, node_path.join)(cwd, "docker-compose.yml");
|
|
11368
|
-
const mappings = parseComposePortMappings(composePath);
|
|
11369
|
-
if (mappings.length > 0) {
|
|
11370
|
-
const ports = await loadPortState(cwd);
|
|
11371
|
-
if (Object.keys(ports).length > 0) {
|
|
11372
|
-
secretsEnv = rewriteUrlsWithPorts(secretsEnv, {
|
|
11373
|
-
dockerEnv: {},
|
|
11374
|
-
ports,
|
|
11375
|
-
mappings
|
|
11376
|
-
});
|
|
11377
|
-
console.log(` š Applied ${Object.keys(ports).length} port mapping(s)`);
|
|
11378
|
-
}
|
|
11379
|
-
}
|
|
11380
|
-
secretsEnv = rewriteDatabaseUrlForTests(secretsEnv);
|
|
11381
11387
|
let dependencyEnv = {};
|
|
11382
11388
|
try {
|
|
11383
11389
|
const appInfo = await require_config.loadWorkspaceAppInfo(cwd);
|
|
11390
|
+
const resolvedPorts = await resolveServicePorts(appInfo.workspaceRoot);
|
|
11391
|
+
await startWorkspaceServices(appInfo.workspace, resolvedPorts.dockerEnv, secretsEnv);
|
|
11392
|
+
if (resolvedPorts.mappings.length > 0) {
|
|
11393
|
+
secretsEnv = rewriteUrlsWithPorts(secretsEnv, resolvedPorts);
|
|
11394
|
+
console.log(` š Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
|
|
11395
|
+
}
|
|
11384
11396
|
dependencyEnv = require_workspace.getDependencyEnvVars(appInfo.workspace, appInfo.appName);
|
|
11385
11397
|
if (Object.keys(dependencyEnv).length > 0) console.log(` š Loaded ${Object.keys(dependencyEnv).length} dependency URL(s)`);
|
|
11386
11398
|
const sniffed = await sniffAppEnvironment(appInfo.app, appInfo.appName, appInfo.workspaceRoot, { logWarnings: false });
|
|
@@ -11396,7 +11408,22 @@ async function testCommand(options = {}) {
|
|
|
11396
11408
|
dependencyEnv = filteredEnv;
|
|
11397
11409
|
console.log(` š Sniffed ${sniffed.requiredEnvVars.length} required env var(s)`);
|
|
11398
11410
|
}
|
|
11399
|
-
} catch {
|
|
11411
|
+
} catch {
|
|
11412
|
+
const composePath = (0, node_path.join)(cwd, "docker-compose.yml");
|
|
11413
|
+
const mappings = parseComposePortMappings(composePath);
|
|
11414
|
+
if (mappings.length > 0) {
|
|
11415
|
+
const ports = await loadPortState(cwd);
|
|
11416
|
+
if (Object.keys(ports).length > 0) {
|
|
11417
|
+
secretsEnv = rewriteUrlsWithPorts(secretsEnv, {
|
|
11418
|
+
dockerEnv: {},
|
|
11419
|
+
ports,
|
|
11420
|
+
mappings
|
|
11421
|
+
});
|
|
11422
|
+
console.log(` š Applied ${Object.keys(ports).length} port mapping(s)`);
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11425
|
+
}
|
|
11426
|
+
secretsEnv = rewriteDatabaseUrlForTests(secretsEnv);
|
|
11400
11427
|
console.log("");
|
|
11401
11428
|
const allSecrets = {
|
|
11402
11429
|
...secretsEnv,
|