@geekmidas/cli 0.52.0 → 0.54.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/index.cjs +77 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +77 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/auth.ts +16 -0
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/health.ts +13 -0
- package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/users.ts +15 -0
- package/src/deploy/__tests__/__fixtures__/route-apps/services.ts +46 -0
- package/src/deploy/__tests__/env-resolver.spec.ts +5 -3
- package/src/deploy/__tests__/sniffer.spec.ts +174 -0
- package/src/deploy/env-resolver.ts +6 -1
- package/src/deploy/index.ts +10 -2
- package/src/deploy/sniffer.ts +107 -4
- package/src/docker/templates.ts +6 -4
- package/tsconfig.tsbuildinfo +1 -1
package/dist/index.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import chokidar from "chokidar";
|
|
|
20
20
|
import { config } from "dotenv";
|
|
21
21
|
import fg from "fast-glob";
|
|
22
22
|
import { Cron } from "@geekmidas/constructs/crons";
|
|
23
|
+
import { Endpoint } from "@geekmidas/constructs/endpoints";
|
|
23
24
|
import { Function } from "@geekmidas/constructs/functions";
|
|
24
25
|
import { Subscriber } from "@geekmidas/constructs/subscribers";
|
|
25
26
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -30,7 +31,7 @@ import prompts from "prompts";
|
|
|
30
31
|
|
|
31
32
|
//#region package.json
|
|
32
33
|
var name = "@geekmidas/cli";
|
|
33
|
-
var version = "0.
|
|
34
|
+
var version = "0.54.0";
|
|
34
35
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
35
36
|
var private$1 = false;
|
|
36
37
|
var type = "module";
|
|
@@ -3385,7 +3386,7 @@ WORKDIR /app
|
|
|
3385
3386
|
COPY . .
|
|
3386
3387
|
|
|
3387
3388
|
# Build production server using gkm
|
|
3388
|
-
RUN
|
|
3389
|
+
RUN ${pm.exec} gkm build --provider server --production
|
|
3389
3390
|
|
|
3390
3391
|
# Stage 3: Production
|
|
3391
3392
|
FROM ${baseImage} AS runner
|
|
@@ -3467,7 +3468,7 @@ WORKDIR /app
|
|
|
3467
3468
|
COPY --from=pruner /app/out/full/ ./
|
|
3468
3469
|
|
|
3469
3470
|
# Build production server using gkm
|
|
3470
|
-
RUN
|
|
3471
|
+
RUN ${pm.exec} gkm build --provider server --production
|
|
3471
3472
|
|
|
3472
3473
|
# Stage 4: Production
|
|
3473
3474
|
FROM ${baseImage} AS runner
|
|
@@ -3788,7 +3789,7 @@ RUN if [ -n "$GKM_ENCRYPTED_CREDENTIALS" ]; then \
|
|
|
3788
3789
|
fi
|
|
3789
3790
|
|
|
3790
3791
|
# Build production server using gkm
|
|
3791
|
-
RUN cd ${appPath} &&
|
|
3792
|
+
RUN cd ${appPath} && ${pm.exec} gkm build --provider server --production
|
|
3792
3793
|
|
|
3793
3794
|
# Stage 4: Production
|
|
3794
3795
|
FROM ${baseImage} AS runner
|
|
@@ -4522,7 +4523,8 @@ function buildRedisUrl(redis) {
|
|
|
4522
4523
|
function resolveEnvVar(varName, context) {
|
|
4523
4524
|
switch (varName) {
|
|
4524
4525
|
case "PORT": return String(context.app.port);
|
|
4525
|
-
case "NODE_ENV": return
|
|
4526
|
+
case "NODE_ENV": return "production";
|
|
4527
|
+
case "STAGE": return context.stage;
|
|
4526
4528
|
case "DATABASE_URL":
|
|
4527
4529
|
if (context.appCredentials && context.postgres) return buildDatabaseUrl(context.appCredentials, context.postgres);
|
|
4528
4530
|
break;
|
|
@@ -4856,6 +4858,12 @@ function generateSecretsReport(encryptedApps, sniffedApps) {
|
|
|
4856
4858
|
const __filename = fileURLToPath(import.meta.url);
|
|
4857
4859
|
const __dirname = dirname(__filename);
|
|
4858
4860
|
/**
|
|
4861
|
+
* Check if a value is a gkm construct (Endpoint, Function, Cron, or Subscriber).
|
|
4862
|
+
*/
|
|
4863
|
+
function isConstruct(value) {
|
|
4864
|
+
return Endpoint.isEndpoint(value) || Function.isFunction(value) || Cron.isCron(value) || Subscriber.isSubscriber(value);
|
|
4865
|
+
}
|
|
4866
|
+
/**
|
|
4859
4867
|
* Resolve the path to a sniffer helper file.
|
|
4860
4868
|
* Handles both dev (.ts with tsx) and production (.mjs from dist).
|
|
4861
4869
|
*
|
|
@@ -4880,8 +4888,9 @@ function resolveSnifferFile(baseName) {
|
|
|
4880
4888
|
* 1. Frontend apps: Returns empty (no server secrets)
|
|
4881
4889
|
* 2. Apps with `requiredEnv`: Uses explicit list from config
|
|
4882
4890
|
* 3. Entry apps: Imports entry file in subprocess to capture config.parse() calls
|
|
4883
|
-
* 4.
|
|
4884
|
-
* 5. Apps with
|
|
4891
|
+
* 4. Route-based apps: Loads route files and calls getEnvironment() on each construct
|
|
4892
|
+
* 5. Apps with `envParser` (no routes): Runs SnifferEnvironmentParser to detect usage
|
|
4893
|
+
* 6. Apps with neither: Returns empty
|
|
4885
4894
|
*
|
|
4886
4895
|
* This function handles "fire and forget" async operations gracefully,
|
|
4887
4896
|
* capturing errors and unhandled rejections without failing the build.
|
|
@@ -4910,6 +4919,14 @@ async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
|
|
|
4910
4919
|
requiredEnvVars: result.envVars
|
|
4911
4920
|
};
|
|
4912
4921
|
}
|
|
4922
|
+
if (app.routes) {
|
|
4923
|
+
const result = await sniffRouteFiles(app.routes, app.path, workspacePath);
|
|
4924
|
+
if (logWarnings && result.error) console.warn(`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`);
|
|
4925
|
+
return {
|
|
4926
|
+
appName,
|
|
4927
|
+
requiredEnvVars: result.envVars
|
|
4928
|
+
};
|
|
4929
|
+
}
|
|
4913
4930
|
if (app.envParser) {
|
|
4914
4931
|
const result = await sniffEnvParser(app.envParser, app.path, workspacePath);
|
|
4915
4932
|
if (logWarnings) {
|
|
@@ -5001,6 +5018,47 @@ async function sniffEntryFile(entryPath, appPath, workspacePath) {
|
|
|
5001
5018
|
});
|
|
5002
5019
|
}
|
|
5003
5020
|
/**
|
|
5021
|
+
* Sniff route files by loading constructs and calling getEnvironment().
|
|
5022
|
+
*
|
|
5023
|
+
* Route-based apps have endpoints, functions, crons, and subscribers that
|
|
5024
|
+
* use services. Each service's register() method accesses environment variables.
|
|
5025
|
+
* This function mimics what the bundler does during build to capture those vars.
|
|
5026
|
+
*
|
|
5027
|
+
* @param routes - Glob pattern(s) for route files
|
|
5028
|
+
* @param appPath - The app's path relative to workspace (e.g., 'apps/api')
|
|
5029
|
+
* @param workspacePath - Absolute path to workspace root
|
|
5030
|
+
* @returns EntrySniffResult with env vars and optional error
|
|
5031
|
+
*/
|
|
5032
|
+
async function sniffRouteFiles(routes, appPath, workspacePath) {
|
|
5033
|
+
const fullAppPath = resolve(workspacePath, appPath);
|
|
5034
|
+
const patterns = Array.isArray(routes) ? routes : [routes];
|
|
5035
|
+
const envVars = /* @__PURE__ */ new Set();
|
|
5036
|
+
let error;
|
|
5037
|
+
try {
|
|
5038
|
+
const files = await fg(patterns, {
|
|
5039
|
+
cwd: fullAppPath,
|
|
5040
|
+
absolute: true
|
|
5041
|
+
});
|
|
5042
|
+
for (const file of files) try {
|
|
5043
|
+
const module = await import(file);
|
|
5044
|
+
for (const [, exportValue] of Object.entries(module)) if (isConstruct(exportValue)) try {
|
|
5045
|
+
const constructEnvVars = await exportValue.getEnvironment();
|
|
5046
|
+
constructEnvVars.forEach((v) => envVars.add(v));
|
|
5047
|
+
} catch (e) {
|
|
5048
|
+
console.warn(`[sniffer] Failed to get environment for construct in ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
5049
|
+
}
|
|
5050
|
+
} catch (e) {
|
|
5051
|
+
console.warn(`[sniffer] Failed to import ${file}: ${e instanceof Error ? e.message : String(e)}`);
|
|
5052
|
+
}
|
|
5053
|
+
} catch (e) {
|
|
5054
|
+
error = e instanceof Error ? e : new Error(String(e));
|
|
5055
|
+
}
|
|
5056
|
+
return {
|
|
5057
|
+
envVars: Array.from(envVars).sort(),
|
|
5058
|
+
error
|
|
5059
|
+
};
|
|
5060
|
+
}
|
|
5061
|
+
/**
|
|
5004
5062
|
* Run the SnifferEnvironmentParser on an envParser module to detect
|
|
5005
5063
|
* which environment variables it accesses.
|
|
5006
5064
|
*
|
|
@@ -5788,7 +5846,13 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5788
5846
|
masterKey: appSecrets?.masterKey
|
|
5789
5847
|
};
|
|
5790
5848
|
const appRequirements = sniffedApps.get(appName);
|
|
5791
|
-
const
|
|
5849
|
+
const sniffedVars = appRequirements?.requiredEnvVars ?? [];
|
|
5850
|
+
const requiredVars = [...new Set([
|
|
5851
|
+
"PORT",
|
|
5852
|
+
"NODE_ENV",
|
|
5853
|
+
"STAGE",
|
|
5854
|
+
...sniffedVars
|
|
5855
|
+
])];
|
|
5792
5856
|
const { valid, missing, resolved } = validateEnvVars(requiredVars, envContext);
|
|
5793
5857
|
if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
|
|
5794
5858
|
const envVars = Object.entries(resolved).map(([key, value]) => `${key}=${value}`);
|
|
@@ -5882,7 +5946,11 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
5882
5946
|
buildArgs,
|
|
5883
5947
|
publicUrlArgs: getPublicUrlArgNames(app)
|
|
5884
5948
|
});
|
|
5885
|
-
const envVars = [
|
|
5949
|
+
const envVars = [
|
|
5950
|
+
`NODE_ENV=production`,
|
|
5951
|
+
`PORT=${app.port}`,
|
|
5952
|
+
`STAGE=${stage}`
|
|
5953
|
+
];
|
|
5886
5954
|
await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
|
|
5887
5955
|
await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
|
|
5888
5956
|
logger$1.log(` Deploying to Dokploy...`);
|