@geekmidas/cli 1.10.7 → 1.10.9
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/{bundler-NpfYPBUo.cjs → bundler-Bm3Az_sv.cjs} +2 -2
- package/dist/{bundler-NpfYPBUo.cjs.map → bundler-Bm3Az_sv.cjs.map} +1 -1
- package/dist/{bundler-DQYjKFPm.mjs → bundler-kk_XJTRp.mjs} +2 -2
- package/dist/{bundler-DQYjKFPm.mjs.map → bundler-kk_XJTRp.mjs.map} +1 -1
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/{fullstack-secrets-ca0Kyrvt.mjs → fullstack-secrets-C2lbdbLZ.mjs} +15 -1
- package/dist/fullstack-secrets-C2lbdbLZ.mjs.map +1 -0
- package/dist/{fullstack-secrets-BctGaE4E.cjs → fullstack-secrets-CtWIYuI0.cjs} +15 -1
- package/dist/fullstack-secrets-CtWIYuI0.cjs.map +1 -0
- package/dist/{index-9tjTQjFt.d.mts → index-BdJZKXCJ.d.cts} +4 -2
- package/dist/index-BdJZKXCJ.d.cts.map +1 -0
- package/dist/{index-VOKKO-lm.d.cts → index-DB9VbcCD.d.mts} +4 -2
- package/dist/index-DB9VbcCD.d.mts.map +1 -0
- package/dist/index.cjs +177 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +177 -61
- package/dist/index.mjs.map +1 -1
- package/dist/openapi-BYxAWwok.cjs.map +1 -1
- package/dist/openapi-DenF-okj.mjs.map +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/{reconcile-C5OyCA7V.mjs → reconcile-BnM6FA6g.mjs} +2 -2
- package/dist/{reconcile-C5OyCA7V.mjs.map → reconcile-BnM6FA6g.mjs.map} +1 -1
- package/dist/{reconcile-TEBsryVn.cjs → reconcile-D6u4HSg8.cjs} +2 -2
- package/dist/{reconcile-TEBsryVn.cjs.map → reconcile-D6u4HSg8.cjs.map} +1 -1
- package/dist/{storage-DmCbr6DI.mjs → storage-B7H2PPCS.mjs} +8 -1
- package/dist/{storage-DmCbr6DI.mjs.map → storage-B7H2PPCS.mjs.map} +1 -1
- package/dist/{storage-Dx_jZbq6.mjs → storage-C1FNm2EP.mjs} +1 -1
- package/dist/{storage-CoCNe0Pt.cjs → storage-Cs13jkJ9.cjs} +8 -1
- package/dist/{storage-CoCNe0Pt.cjs.map → storage-Cs13jkJ9.cjs.map} +1 -1
- package/dist/{storage-C7pmBq1u.cjs → storage-D6BGLgWf.cjs} +1 -1
- package/dist/{sync-6FoT41G3.mjs → sync-CyGe5f1I.mjs} +1 -1
- package/dist/{sync-CbeKrnQV.mjs → sync-CzXruMzP.mjs} +2 -2
- package/dist/{sync-CbeKrnQV.mjs.map → sync-CzXruMzP.mjs.map} +1 -1
- package/dist/sync-DLlwsrBs.cjs +4 -0
- package/dist/{sync-DdkKaHqP.cjs → sync-oCqELfeA.cjs} +2 -2
- package/dist/{sync-DdkKaHqP.cjs.map → sync-oCqELfeA.cjs.map} +1 -1
- package/dist/{types-C7QJJl9f.d.cts → types-D4MLWXSL.d.cts} +2 -2
- package/dist/{types-C7QJJl9f.d.cts.map → types-D4MLWXSL.d.cts.map} +1 -1
- package/dist/{types-Iqsq_FIG.d.mts → types-DwpLq_fp.d.mts} +2 -2
- package/dist/{types-Iqsq_FIG.d.mts.map → types-DwpLq_fp.d.mts.map} +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace-4SP3Gx4Y.cjs.map +1 -1
- package/dist/workspace-D4z4A4cq.mjs.map +1 -1
- package/package.json +5 -5
- package/src/dev/__tests__/index.spec.ts +142 -0
- package/src/dev/index.ts +67 -33
- package/src/docker/__tests__/compose.spec.ts +151 -2
- package/src/docker/compose.ts +105 -8
- package/src/init/generators/docker.ts +3 -1
- package/src/init/index.ts +1 -0
- package/src/init/versions.ts +1 -1
- package/src/secrets/__tests__/generator.spec.ts +68 -0
- package/src/secrets/__tests__/storage.spec.ts +30 -0
- package/src/secrets/generator.ts +18 -0
- package/src/secrets/index.ts +9 -0
- package/src/secrets/storage.ts +7 -0
- package/src/secrets/types.ts +4 -0
- package/src/setup/index.ts +1 -0
- package/src/test/__tests__/index.spec.ts +115 -0
- package/src/test/index.ts +41 -21
- package/src/types.ts +1 -1
- package/src/workspace/types.ts +2 -0
- package/dist/fullstack-secrets-BctGaE4E.cjs.map +0 -1
- package/dist/fullstack-secrets-ca0Kyrvt.mjs.map +0 -1
- package/dist/index-9tjTQjFt.d.mts.map +0 -1
- package/dist/index-VOKKO-lm.d.cts.map +0 -1
- package/dist/sync-RsnjXYwG.cjs +0 -4
package/dist/index.cjs
CHANGED
|
@@ -4,13 +4,13 @@ const require_workspace = require('./workspace-4SP3Gx4Y.cjs');
|
|
|
4
4
|
const require_config = require('./config-D3ORuiUs.cjs');
|
|
5
5
|
const require_credentials = require('./credentials-C8DWtnMY.cjs');
|
|
6
6
|
const require_openapi = require('./openapi-BYxAWwok.cjs');
|
|
7
|
-
const require_storage = require('./storage-
|
|
7
|
+
const require_storage = require('./storage-Cs13jkJ9.cjs');
|
|
8
8
|
const require_dokploy_api = require('./dokploy-api-DLgvEQlr.cjs');
|
|
9
9
|
const require_encryption = require('./encryption-BE0UOb8j.cjs');
|
|
10
10
|
const require_CachedStateProvider = require('./CachedStateProvider-D73dCqfH.cjs');
|
|
11
|
-
const require_fullstack_secrets = require('./fullstack-secrets-
|
|
11
|
+
const require_fullstack_secrets = require('./fullstack-secrets-CtWIYuI0.cjs');
|
|
12
12
|
const require_openapi_react_query = require('./openapi-react-query-DYbBq-WJ.cjs');
|
|
13
|
-
const require_sync = require('./sync-
|
|
13
|
+
const require_sync = require('./sync-oCqELfeA.cjs');
|
|
14
14
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
15
15
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
16
16
|
const commander = require_chunk.__toESM(require("commander"));
|
|
@@ -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.8";
|
|
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";
|
|
@@ -767,25 +767,30 @@ async function resolveServicePorts(workspaceRoot) {
|
|
|
767
767
|
const savedState = await loadPortState(workspaceRoot);
|
|
768
768
|
const dockerEnv = {};
|
|
769
769
|
const ports = {};
|
|
770
|
+
const assignedPorts = /* @__PURE__ */ new Set();
|
|
770
771
|
logger$11.log("\n🔌 Resolving service ports...");
|
|
771
772
|
for (const mapping of mappings) {
|
|
772
773
|
const containerPort = getContainerHostPort(workspaceRoot, mapping.service, mapping.containerPort);
|
|
773
774
|
if (containerPort !== null) {
|
|
774
775
|
ports[mapping.envVar] = containerPort;
|
|
775
776
|
dockerEnv[mapping.envVar] = String(containerPort);
|
|
777
|
+
assignedPorts.add(containerPort);
|
|
776
778
|
logger$11.log(` 🔄 ${mapping.service}:${mapping.containerPort}: reusing existing container on port ${containerPort}`);
|
|
777
779
|
continue;
|
|
778
780
|
}
|
|
779
781
|
const savedPort = savedState[mapping.envVar];
|
|
780
|
-
if (savedPort && await isPortAvailable(savedPort)) {
|
|
782
|
+
if (savedPort && !assignedPorts.has(savedPort) && await isPortAvailable(savedPort)) {
|
|
781
783
|
ports[mapping.envVar] = savedPort;
|
|
782
784
|
dockerEnv[mapping.envVar] = String(savedPort);
|
|
785
|
+
assignedPorts.add(savedPort);
|
|
783
786
|
logger$11.log(` 💾 ${mapping.service}:${mapping.containerPort}: using saved port ${savedPort}`);
|
|
784
787
|
continue;
|
|
785
788
|
}
|
|
786
|
-
|
|
789
|
+
let resolvedPort = await findAvailablePort(mapping.defaultPort);
|
|
790
|
+
while (assignedPorts.has(resolvedPort)) resolvedPort = await findAvailablePort(resolvedPort + 1);
|
|
787
791
|
ports[mapping.envVar] = resolvedPort;
|
|
788
792
|
dockerEnv[mapping.envVar] = String(resolvedPort);
|
|
793
|
+
assignedPorts.add(resolvedPort);
|
|
789
794
|
if (resolvedPort !== mapping.defaultPort) logger$11.log(` ⚡ ${mapping.service}:${mapping.containerPort}: port ${mapping.defaultPort} occupied, using port ${resolvedPort}`);
|
|
790
795
|
else logger$11.log(` ✅ ${mapping.service}:${mapping.containerPort}: using default port ${resolvedPort}`);
|
|
791
796
|
}
|
|
@@ -1222,31 +1227,48 @@ async function loadSecretsForApp(secretsRoot, appName) {
|
|
|
1222
1227
|
return mapped;
|
|
1223
1228
|
}
|
|
1224
1229
|
/**
|
|
1230
|
+
* Build the environment variables to pass to `docker compose up`.
|
|
1231
|
+
* Merges process.env, secrets, and port mappings so that Docker Compose
|
|
1232
|
+
* can interpolate variables like ${POSTGRES_USER} correctly.
|
|
1233
|
+
* @internal Exported for testing
|
|
1234
|
+
*/
|
|
1235
|
+
function buildDockerComposeEnv(secretsEnv, portEnv) {
|
|
1236
|
+
return {
|
|
1237
|
+
...process.env,
|
|
1238
|
+
...secretsEnv,
|
|
1239
|
+
...portEnv
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
/**
|
|
1243
|
+
* Parse all service names from a docker-compose.yml file.
|
|
1244
|
+
* @internal Exported for testing
|
|
1245
|
+
*/
|
|
1246
|
+
function parseComposeServiceNames(composePath) {
|
|
1247
|
+
if (!(0, node_fs.existsSync)(composePath)) return [];
|
|
1248
|
+
const content = (0, node_fs.readFileSync)(composePath, "utf-8");
|
|
1249
|
+
const compose = (0, yaml.parse)(content);
|
|
1250
|
+
return Object.keys(compose?.services ?? {});
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1225
1253
|
* Start docker-compose services for the workspace.
|
|
1254
|
+
* Parses the docker-compose.yml to discover all services and starts
|
|
1255
|
+
* everything except app services (which are managed by turbo).
|
|
1256
|
+
* This ensures manually added services are always started.
|
|
1226
1257
|
* @internal Exported for testing
|
|
1227
1258
|
*/
|
|
1228
|
-
async function startWorkspaceServices(workspace, portEnv) {
|
|
1229
|
-
const
|
|
1230
|
-
if (!
|
|
1231
|
-
const
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
if (services.mail) servicesToStart.push("mailpit");
|
|
1259
|
+
async function startWorkspaceServices(workspace, portEnv, secretsEnv) {
|
|
1260
|
+
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
1261
|
+
if (!(0, node_fs.existsSync)(composeFile)) return;
|
|
1262
|
+
const allServices = parseComposeServiceNames(composeFile);
|
|
1263
|
+
const appNames = new Set(Object.keys(workspace.apps));
|
|
1264
|
+
const servicesToStart = allServices.filter((name$1) => !appNames.has(name$1));
|
|
1235
1265
|
if (servicesToStart.length === 0) return;
|
|
1236
1266
|
logger$11.log(`🐳 Starting services: ${servicesToStart.join(", ")}`);
|
|
1237
1267
|
try {
|
|
1238
|
-
const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
|
|
1239
|
-
if (!(0, node_fs.existsSync)(composeFile)) {
|
|
1240
|
-
logger$11.warn("⚠️ No docker-compose.yml found. Services will not be started.");
|
|
1241
|
-
return;
|
|
1242
|
-
}
|
|
1243
1268
|
(0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
|
|
1244
1269
|
cwd: workspace.root,
|
|
1245
1270
|
stdio: "inherit",
|
|
1246
|
-
env:
|
|
1247
|
-
...process.env,
|
|
1248
|
-
...portEnv
|
|
1249
|
-
}
|
|
1271
|
+
env: buildDockerComposeEnv(secretsEnv, portEnv)
|
|
1250
1272
|
});
|
|
1251
1273
|
logger$11.log("✅ Services started");
|
|
1252
1274
|
} catch (error) {
|
|
@@ -1295,8 +1317,9 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1295
1317
|
if (copiedCount > 0) logger$11.log(`\n📦 Copied ${copiedCount} API client(s)`);
|
|
1296
1318
|
}
|
|
1297
1319
|
const resolvedPorts = await resolveServicePorts(workspace.root);
|
|
1298
|
-
await
|
|
1299
|
-
|
|
1320
|
+
const rawSecrets = await loadDevSecrets(workspace);
|
|
1321
|
+
await startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
|
|
1322
|
+
const secretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
|
|
1300
1323
|
if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
|
|
1301
1324
|
const dependencyEnv = generateAllDependencyEnvVars(workspace);
|
|
1302
1325
|
if (Object.keys(dependencyEnv).length > 0) {
|
|
@@ -2175,7 +2198,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2175
2198
|
let masterKey;
|
|
2176
2199
|
if (context.production?.bundle && !skipBundle) {
|
|
2177
2200
|
logger$9.log(`\n📦 Bundling production server...`);
|
|
2178
|
-
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-
|
|
2201
|
+
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-Bm3Az_sv.cjs"));
|
|
2179
2202
|
const allConstructs = [
|
|
2180
2203
|
...endpoints.map((e) => e.construct),
|
|
2181
2204
|
...functions.map((f) => f.construct),
|
|
@@ -2837,13 +2860,15 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
|
|
|
2837
2860
|
const DEFAULT_SERVICE_IMAGES = {
|
|
2838
2861
|
postgres: "postgres",
|
|
2839
2862
|
redis: "redis",
|
|
2840
|
-
rabbitmq: "rabbitmq"
|
|
2863
|
+
rabbitmq: "rabbitmq",
|
|
2864
|
+
minio: "minio/minio"
|
|
2841
2865
|
};
|
|
2842
2866
|
/** Default Docker image versions for services */
|
|
2843
2867
|
const DEFAULT_SERVICE_VERSIONS = {
|
|
2844
2868
|
postgres: "18-alpine",
|
|
2845
2869
|
redis: "7-alpine",
|
|
2846
|
-
rabbitmq: "3-management-alpine"
|
|
2870
|
+
rabbitmq: "3-management-alpine",
|
|
2871
|
+
minio: "latest"
|
|
2847
2872
|
};
|
|
2848
2873
|
/** Get the default full image reference for a service */
|
|
2849
2874
|
function getDefaultImage(serviceName) {
|
|
@@ -2874,7 +2899,9 @@ function generateDockerCompose(options) {
|
|
|
2874
2899
|
const { imageName, registry, port, healthCheckPath, services } = options;
|
|
2875
2900
|
const serviceMap = normalizeServices(services);
|
|
2876
2901
|
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
2877
|
-
let yaml$1 =
|
|
2902
|
+
let yaml$1 = `# Use "gkm dev" or "gkm test" to start services.
|
|
2903
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
2904
|
+
version: '3.8'
|
|
2878
2905
|
|
|
2879
2906
|
services:
|
|
2880
2907
|
api:
|
|
@@ -2894,6 +2921,13 @@ services:
|
|
|
2894
2921
|
if (serviceMap.has("redis")) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
2895
2922
|
`;
|
|
2896
2923
|
if (serviceMap.has("rabbitmq")) yaml$1 += ` - RABBITMQ_URL=\${RABBITMQ_URL:-amqp://rabbitmq:5672}
|
|
2924
|
+
`;
|
|
2925
|
+
if (serviceMap.has("minio")) yaml$1 += ` - S3_ENDPOINT=\${S3_ENDPOINT:-http://minio:9000}
|
|
2926
|
+
- S3_ACCESS_KEY_ID=\${MINIO_ACCESS_KEY:-app}
|
|
2927
|
+
- S3_SECRET_ACCESS_KEY=\${MINIO_SECRET_KEY:-app}
|
|
2928
|
+
- S3_BUCKET=\${MINIO_BUCKET:-app}
|
|
2929
|
+
- S3_REGION=\${S3_REGION:-eu-west-1}
|
|
2930
|
+
- S3_FORCE_PATH_STYLE=true
|
|
2897
2931
|
`;
|
|
2898
2932
|
yaml$1 += ` healthcheck:
|
|
2899
2933
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
|
|
@@ -2967,6 +3001,29 @@ services:
|
|
|
2967
3001
|
retries: 5
|
|
2968
3002
|
networks:
|
|
2969
3003
|
- app-network
|
|
3004
|
+
`;
|
|
3005
|
+
const minioImage = serviceMap.get("minio");
|
|
3006
|
+
if (minioImage) yaml$1 += `
|
|
3007
|
+
minio:
|
|
3008
|
+
image: ${minioImage}
|
|
3009
|
+
container_name: minio
|
|
3010
|
+
restart: unless-stopped
|
|
3011
|
+
entrypoint: sh
|
|
3012
|
+
command: -c 'mkdir -p /data/\${MINIO_BUCKET:-app} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
3013
|
+
environment:
|
|
3014
|
+
MINIO_ROOT_USER: \${MINIO_ACCESS_KEY:-app}
|
|
3015
|
+
MINIO_ROOT_PASSWORD: \${MINIO_SECRET_KEY:-app}
|
|
3016
|
+
ports:
|
|
3017
|
+
- "9001:9001" # Console UI
|
|
3018
|
+
volumes:
|
|
3019
|
+
- minio_data:/data
|
|
3020
|
+
healthcheck:
|
|
3021
|
+
test: ["CMD", "mc", "ready", "local"]
|
|
3022
|
+
interval: 10s
|
|
3023
|
+
timeout: 5s
|
|
3024
|
+
retries: 5
|
|
3025
|
+
networks:
|
|
3026
|
+
- app-network
|
|
2970
3027
|
`;
|
|
2971
3028
|
yaml$1 += `
|
|
2972
3029
|
volumes:
|
|
@@ -2976,6 +3033,8 @@ volumes:
|
|
|
2976
3033
|
if (serviceMap.has("redis")) yaml$1 += ` redis_data:
|
|
2977
3034
|
`;
|
|
2978
3035
|
if (serviceMap.has("rabbitmq")) yaml$1 += ` rabbitmq_data:
|
|
3036
|
+
`;
|
|
3037
|
+
if (serviceMap.has("minio")) yaml$1 += ` minio_data:
|
|
2979
3038
|
`;
|
|
2980
3039
|
yaml$1 += `
|
|
2981
3040
|
networks:
|
|
@@ -2990,7 +3049,9 @@ networks:
|
|
|
2990
3049
|
function generateMinimalDockerCompose(options) {
|
|
2991
3050
|
const { imageName, registry, port, healthCheckPath } = options;
|
|
2992
3051
|
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
2993
|
-
return
|
|
3052
|
+
return `# Use "gkm dev" or "gkm test" to start services.
|
|
3053
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
3054
|
+
version: '3.8'
|
|
2994
3055
|
|
|
2995
3056
|
services:
|
|
2996
3057
|
api:
|
|
@@ -3029,17 +3090,21 @@ function generateWorkspaceCompose(workspace, options = {}) {
|
|
|
3029
3090
|
const hasPostgres = services.db !== void 0 && services.db !== false;
|
|
3030
3091
|
const hasRedis = services.cache !== void 0 && services.cache !== false;
|
|
3031
3092
|
const hasMail = services.mail !== void 0 && services.mail !== false;
|
|
3093
|
+
const hasMinio = services.storage !== void 0 && services.storage !== false;
|
|
3032
3094
|
const postgresImage = getInfraServiceImage("postgres", services.db);
|
|
3033
3095
|
const redisImage = getInfraServiceImage("redis", services.cache);
|
|
3096
|
+
const minioImage = getInfraServiceImage("minio", services.storage);
|
|
3034
3097
|
let yaml$1 = `# Docker Compose for ${workspace.name} workspace
|
|
3035
|
-
#
|
|
3098
|
+
# Use "gkm dev" or "gkm test" to start services.
|
|
3099
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
3036
3100
|
|
|
3037
3101
|
services:
|
|
3038
3102
|
`;
|
|
3039
3103
|
for (const [appName, app] of apps) yaml$1 += generateAppService(appName, app, apps, {
|
|
3040
3104
|
registry,
|
|
3041
3105
|
hasPostgres,
|
|
3042
|
-
hasRedis
|
|
3106
|
+
hasRedis,
|
|
3107
|
+
hasMinio
|
|
3043
3108
|
});
|
|
3044
3109
|
if (hasPostgres) yaml$1 += `
|
|
3045
3110
|
postgres:
|
|
@@ -3085,6 +3150,28 @@ services:
|
|
|
3085
3150
|
- "1025:1025" # SMTP
|
|
3086
3151
|
networks:
|
|
3087
3152
|
- workspace-network
|
|
3153
|
+
`;
|
|
3154
|
+
if (hasMinio) yaml$1 += `
|
|
3155
|
+
minio:
|
|
3156
|
+
image: ${minioImage}
|
|
3157
|
+
container_name: ${workspace.name}-minio
|
|
3158
|
+
restart: unless-stopped
|
|
3159
|
+
entrypoint: sh
|
|
3160
|
+
command: -c 'mkdir -p /data/\${MINIO_BUCKET:-app} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
3161
|
+
environment:
|
|
3162
|
+
MINIO_ROOT_USER: \${MINIO_ACCESS_KEY:-app}
|
|
3163
|
+
MINIO_ROOT_PASSWORD: \${MINIO_SECRET_KEY:-app}
|
|
3164
|
+
ports:
|
|
3165
|
+
- "9001:9001" # Console UI
|
|
3166
|
+
volumes:
|
|
3167
|
+
- minio_data:/data
|
|
3168
|
+
healthcheck:
|
|
3169
|
+
test: ["CMD", "mc", "ready", "local"]
|
|
3170
|
+
interval: 10s
|
|
3171
|
+
timeout: 5s
|
|
3172
|
+
retries: 5
|
|
3173
|
+
networks:
|
|
3174
|
+
- workspace-network
|
|
3088
3175
|
`;
|
|
3089
3176
|
yaml$1 += `
|
|
3090
3177
|
volumes:
|
|
@@ -3092,6 +3179,8 @@ volumes:
|
|
|
3092
3179
|
if (hasPostgres) yaml$1 += ` dbdata:
|
|
3093
3180
|
`;
|
|
3094
3181
|
if (hasRedis) yaml$1 += ` redis_data:
|
|
3182
|
+
`;
|
|
3183
|
+
if (hasMinio) yaml$1 += ` minio_data:
|
|
3095
3184
|
`;
|
|
3096
3185
|
yaml$1 += `
|
|
3097
3186
|
networks:
|
|
@@ -3106,14 +3195,19 @@ networks:
|
|
|
3106
3195
|
function getInfraServiceImage(serviceName, config) {
|
|
3107
3196
|
const defaults = {
|
|
3108
3197
|
postgres: "postgres:18-alpine",
|
|
3109
|
-
redis: "redis:7-alpine"
|
|
3198
|
+
redis: "redis:7-alpine",
|
|
3199
|
+
minio: "minio/minio:latest"
|
|
3110
3200
|
};
|
|
3111
3201
|
if (!config || config === true) return defaults[serviceName];
|
|
3112
3202
|
if (typeof config === "object") {
|
|
3113
3203
|
if (config.image) return config.image;
|
|
3114
3204
|
if (config.version) {
|
|
3115
|
-
const
|
|
3116
|
-
|
|
3205
|
+
const baseImages = {
|
|
3206
|
+
postgres: "postgres",
|
|
3207
|
+
redis: "redis",
|
|
3208
|
+
minio: "minio/minio"
|
|
3209
|
+
};
|
|
3210
|
+
return `${baseImages[serviceName]}:${config.version}`;
|
|
3117
3211
|
}
|
|
3118
3212
|
}
|
|
3119
3213
|
return defaults[serviceName];
|
|
@@ -3122,7 +3216,7 @@ function getInfraServiceImage(serviceName, config) {
|
|
|
3122
3216
|
* Generate a service definition for an app.
|
|
3123
3217
|
*/
|
|
3124
3218
|
function generateAppService(appName, app, allApps, options) {
|
|
3125
|
-
const { registry, hasPostgres, hasRedis } = options;
|
|
3219
|
+
const { registry, hasPostgres, hasRedis, hasMinio } = options;
|
|
3126
3220
|
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
3127
3221
|
const healthCheckPath = app.type === "frontend" ? "/" : "/health";
|
|
3128
3222
|
const healthCheckCmd = app.type === "frontend" ? `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}/"]` : `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}${healthCheckPath}"]`;
|
|
@@ -3149,6 +3243,13 @@ function generateAppService(appName, app, allApps, options) {
|
|
|
3149
3243
|
if (hasPostgres) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
3150
3244
|
`;
|
|
3151
3245
|
if (hasRedis) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
3246
|
+
`;
|
|
3247
|
+
if (hasMinio) yaml$1 += ` - S3_ENDPOINT=\${S3_ENDPOINT:-http://minio:9000}
|
|
3248
|
+
- S3_ACCESS_KEY_ID=\${MINIO_ACCESS_KEY:-app}
|
|
3249
|
+
- S3_SECRET_ACCESS_KEY=\${MINIO_SECRET_KEY:-app}
|
|
3250
|
+
- S3_BUCKET=\${MINIO_BUCKET:-app}
|
|
3251
|
+
- S3_REGION=\${S3_REGION:-eu-west-1}
|
|
3252
|
+
- S3_FORCE_PATH_STYLE=true
|
|
3152
3253
|
`;
|
|
3153
3254
|
}
|
|
3154
3255
|
yaml$1 += ` healthcheck:
|
|
@@ -3161,6 +3262,7 @@ function generateAppService(appName, app, allApps, options) {
|
|
|
3161
3262
|
if (app.type === "backend") {
|
|
3162
3263
|
if (hasPostgres) dependencies$1.push("postgres");
|
|
3163
3264
|
if (hasRedis) dependencies$1.push("redis");
|
|
3265
|
+
if (hasMinio) dependencies$1.push("minio");
|
|
3164
3266
|
}
|
|
3165
3267
|
if (dependencies$1.length > 0) {
|
|
3166
3268
|
yaml$1 += ` depends_on:
|
|
@@ -6205,7 +6307,7 @@ async function deployCommand(options) {
|
|
|
6205
6307
|
dokployConfig = setupResult.config;
|
|
6206
6308
|
finalRegistry = dokployConfig.registry ?? dockerConfig.registry;
|
|
6207
6309
|
if (setupResult.serviceUrls) {
|
|
6208
|
-
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-
|
|
6310
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
|
|
6209
6311
|
let secrets = await readStageSecrets$1(stage);
|
|
6210
6312
|
if (!secrets) {
|
|
6211
6313
|
logger$3.log(` Creating secrets file for stage "${stage}"...`);
|
|
@@ -6502,7 +6604,7 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
6502
6604
|
"@geekmidas/storage": "~2.0.0",
|
|
6503
6605
|
"@geekmidas/studio": "~1.0.0",
|
|
6504
6606
|
"@geekmidas/telescope": "~1.0.0",
|
|
6505
|
-
"@geekmidas/testkit": "~1.0.
|
|
6607
|
+
"@geekmidas/testkit": "~1.0.5",
|
|
6506
6608
|
"@geekmidas/cli": CLI_VERSION
|
|
6507
6609
|
};
|
|
6508
6610
|
|
|
@@ -7057,7 +7159,9 @@ function generateDockerFiles(options, template, dbApps) {
|
|
|
7057
7159
|
environment:
|
|
7058
7160
|
MP_SMTP_AUTH_ACCEPT_ANY: 1
|
|
7059
7161
|
MP_SMTP_AUTH_ALLOW_INSECURE: 1`);
|
|
7060
|
-
let dockerCompose =
|
|
7162
|
+
let dockerCompose = `# Use "gkm dev" or "gkm test" to start services.
|
|
7163
|
+
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
7164
|
+
services:
|
|
7061
7165
|
${services.join("\n\n")}
|
|
7062
7166
|
`;
|
|
7063
7167
|
if (volumes.length > 0) dockerCompose += `
|
|
@@ -10868,6 +10972,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
10868
10972
|
const secretServices = [];
|
|
10869
10973
|
if (services.db) secretServices.push("postgres");
|
|
10870
10974
|
if (services.cache) secretServices.push("redis");
|
|
10975
|
+
if (services.storage) secretServices.push("minio");
|
|
10871
10976
|
const devSecrets = require_fullstack_secrets.createStageSecrets("development", secretServices, { projectName: name$1 });
|
|
10872
10977
|
const customSecrets = {
|
|
10873
10978
|
NODE_ENV: "development",
|
|
@@ -11026,6 +11131,7 @@ async function secretsInitCommand(options) {
|
|
|
11026
11131
|
if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11027
11132
|
if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11028
11133
|
if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11134
|
+
if (secrets.urls.S3_ENDPOINT) logger$2.log(` S3_ENDPOINT: ${secrets.urls.S3_ENDPOINT}`);
|
|
11029
11135
|
if (Object.keys(secrets.custom).length > 0) logger$2.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
|
|
11030
11136
|
logger$2.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
|
|
11031
11137
|
logger$2.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
|
|
@@ -11087,11 +11193,13 @@ async function secretsShowCommand(options) {
|
|
|
11087
11193
|
logger$2.log(` password: ${reveal ? creds.password : require_storage.maskPassword(creds.password)}`);
|
|
11088
11194
|
if (creds.database) logger$2.log(` database: ${creds.database}`);
|
|
11089
11195
|
if (creds.vhost) logger$2.log(` vhost: ${creds.vhost}`);
|
|
11196
|
+
if (creds.bucket) logger$2.log(` bucket: ${creds.bucket}`);
|
|
11090
11197
|
}
|
|
11091
11198
|
logger$2.log("\nConnection URLs:");
|
|
11092
11199
|
if (secrets.urls.DATABASE_URL) logger$2.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
11093
11200
|
if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
|
|
11094
11201
|
if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
11202
|
+
if (secrets.urls.S3_ENDPOINT) logger$2.log(` S3_ENDPOINT: ${secrets.urls.S3_ENDPOINT}`);
|
|
11095
11203
|
const customKeys = Object.keys(secrets.custom);
|
|
11096
11204
|
if (customKeys.length > 0) {
|
|
11097
11205
|
logger$2.log("\nCustom Secrets:");
|
|
@@ -11295,6 +11403,7 @@ async function generateFreshSecrets(stage, workspace, options) {
|
|
|
11295
11403
|
const serviceNames = [];
|
|
11296
11404
|
if (workspace.services.db) serviceNames.push("postgres");
|
|
11297
11405
|
if (workspace.services.cache) serviceNames.push("redis");
|
|
11406
|
+
if (workspace.services.storage) serviceNames.push("minio");
|
|
11298
11407
|
const secrets = require_fullstack_secrets.createStageSecrets(stage, serviceNames, { projectName: workspace.name });
|
|
11299
11408
|
const isMultiApp = Object.keys(workspace.apps).length > 1;
|
|
11300
11409
|
if (isMultiApp) {
|
|
@@ -11364,23 +11473,15 @@ async function testCommand(options = {}) {
|
|
|
11364
11473
|
if (error instanceof Error && error.message.includes("key not found")) console.log(` Decryption key not found for ${stage}`);
|
|
11365
11474
|
else throw error;
|
|
11366
11475
|
}
|
|
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
11476
|
let dependencyEnv = {};
|
|
11382
11477
|
try {
|
|
11383
11478
|
const appInfo = await require_config.loadWorkspaceAppInfo(cwd);
|
|
11479
|
+
const resolvedPorts = await resolveServicePorts(appInfo.workspaceRoot);
|
|
11480
|
+
await startWorkspaceServices(appInfo.workspace, resolvedPorts.dockerEnv, secretsEnv);
|
|
11481
|
+
if (resolvedPorts.mappings.length > 0) {
|
|
11482
|
+
secretsEnv = rewriteUrlsWithPorts(secretsEnv, resolvedPorts);
|
|
11483
|
+
console.log(` 🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
|
|
11484
|
+
}
|
|
11384
11485
|
dependencyEnv = require_workspace.getDependencyEnvVars(appInfo.workspace, appInfo.appName);
|
|
11385
11486
|
if (Object.keys(dependencyEnv).length > 0) console.log(` 🔗 Loaded ${Object.keys(dependencyEnv).length} dependency URL(s)`);
|
|
11386
11487
|
const sniffed = await sniffAppEnvironment(appInfo.app, appInfo.appName, appInfo.workspaceRoot, { logWarnings: false });
|
|
@@ -11396,7 +11497,22 @@ async function testCommand(options = {}) {
|
|
|
11396
11497
|
dependencyEnv = filteredEnv;
|
|
11397
11498
|
console.log(` 🔍 Sniffed ${sniffed.requiredEnvVars.length} required env var(s)`);
|
|
11398
11499
|
}
|
|
11399
|
-
} catch {
|
|
11500
|
+
} catch {
|
|
11501
|
+
const composePath = (0, node_path.join)(cwd, "docker-compose.yml");
|
|
11502
|
+
const mappings = parseComposePortMappings(composePath);
|
|
11503
|
+
if (mappings.length > 0) {
|
|
11504
|
+
const ports = await loadPortState(cwd);
|
|
11505
|
+
if (Object.keys(ports).length > 0) {
|
|
11506
|
+
secretsEnv = rewriteUrlsWithPorts(secretsEnv, {
|
|
11507
|
+
dockerEnv: {},
|
|
11508
|
+
ports,
|
|
11509
|
+
mappings
|
|
11510
|
+
});
|
|
11511
|
+
console.log(` 🔌 Applied ${Object.keys(ports).length} port mapping(s)`);
|
|
11512
|
+
}
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
secretsEnv = rewriteDatabaseUrlForTests(secretsEnv);
|
|
11400
11516
|
console.log("");
|
|
11401
11517
|
const allSecrets = {
|
|
11402
11518
|
...secretsEnv,
|
|
@@ -11831,9 +11947,9 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
11831
11947
|
const globalOptions = program.opts();
|
|
11832
11948
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11833
11949
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11834
|
-
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-
|
|
11835
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11836
|
-
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-
|
|
11950
|
+
const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-DLlwsrBs.cjs"));
|
|
11951
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D6u4HSg8.cjs"));
|
|
11952
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
|
|
11837
11953
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11838
11954
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
11839
11955
|
if (secrets) {
|
|
@@ -11856,9 +11972,9 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
11856
11972
|
const globalOptions = program.opts();
|
|
11857
11973
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11858
11974
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11859
|
-
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-
|
|
11860
|
-
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-
|
|
11861
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11975
|
+
const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-DLlwsrBs.cjs"));
|
|
11976
|
+
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
|
|
11977
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D6u4HSg8.cjs"));
|
|
11862
11978
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11863
11979
|
let secrets = await pullSecrets$1(options.stage, workspace);
|
|
11864
11980
|
if (!secrets) {
|
|
@@ -11883,8 +11999,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
11883
11999
|
const globalOptions = program.opts();
|
|
11884
12000
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
11885
12001
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
11886
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
11887
|
-
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-
|
|
12002
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D6u4HSg8.cjs"));
|
|
12003
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
|
|
11888
12004
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
11889
12005
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
11890
12006
|
if (!secrets) {
|