@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 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:16-alpine` | `DATABASE_URL`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` |
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.5";
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 startWorkspaceServices(workspace, resolvedPorts.dockerEnv);
1299
- const secretsEnv = rewriteUrlsWithPorts(await loadDevSecrets(workspace), resolvedPorts);
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 = `version: '3.8'
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 `version: '3.8'
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
- # Generated by gkm - do not edit manually
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 = `services:
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,