@geekmidas/cli 0.38.0 ā 0.39.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 +154 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +135 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/build/index.ts +23 -6
- package/src/deploy/docker.ts +20 -20
- package/src/deploy/index.ts +20 -19
- package/src/dev/index.ts +144 -9
- package/src/index.ts +18 -1
- package/src/init/generators/auth.ts +2 -0
package/dist/index.mjs
CHANGED
|
@@ -26,7 +26,7 @@ import prompts from "prompts";
|
|
|
26
26
|
|
|
27
27
|
//#region package.json
|
|
28
28
|
var name = "@geekmidas/cli";
|
|
29
|
-
var version = "0.
|
|
29
|
+
var version = "0.39.0";
|
|
30
30
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
31
31
|
var private$1 = false;
|
|
32
32
|
var type = "module";
|
|
@@ -1541,19 +1541,43 @@ function findSecretsRoot(startDir) {
|
|
|
1541
1541
|
return startDir;
|
|
1542
1542
|
}
|
|
1543
1543
|
/**
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1544
|
+
* Generate the credentials injection code snippet.
|
|
1545
|
+
* This is the common logic used by both entry wrapper and exec preload.
|
|
1546
|
+
* @internal
|
|
1546
1547
|
*/
|
|
1547
|
-
|
|
1548
|
-
|
|
1548
|
+
function generateCredentialsInjection(secretsJsonPath) {
|
|
1549
|
+
return `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1549
1550
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1550
1551
|
|
|
1551
|
-
// Inject dev secrets into Credentials
|
|
1552
|
+
// Inject dev secrets into Credentials
|
|
1552
1553
|
const secretsPath = '${secretsJsonPath}';
|
|
1553
1554
|
if (existsSync(secretsPath)) {
|
|
1554
|
-
|
|
1555
|
+
const secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));
|
|
1556
|
+
Object.assign(Credentials, secrets);
|
|
1557
|
+
// Debug: uncomment to verify preload is running
|
|
1558
|
+
// console.log('[gkm preload] Injected', Object.keys(secrets).length, 'credentials');
|
|
1555
1559
|
}
|
|
1556
|
-
|
|
1560
|
+
`;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Create a preload script that injects secrets into Credentials.
|
|
1564
|
+
* Used by `gkm exec` to inject secrets before running any command.
|
|
1565
|
+
* @internal Exported for testing
|
|
1566
|
+
*/
|
|
1567
|
+
async function createCredentialsPreload(preloadPath, secretsJsonPath) {
|
|
1568
|
+
const content = `/**
|
|
1569
|
+
* Credentials preload generated by 'gkm exec'
|
|
1570
|
+
* This file is loaded via NODE_OPTIONS="--import <path>"
|
|
1571
|
+
*/
|
|
1572
|
+
${generateCredentialsInjection(secretsJsonPath)}`;
|
|
1573
|
+
await writeFile(preloadPath, content);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Create a wrapper script that injects secrets before importing the entry file.
|
|
1577
|
+
* @internal Exported for testing
|
|
1578
|
+
*/
|
|
1579
|
+
async function createEntryWrapper(wrapperPath, entryPath, secretsJsonPath) {
|
|
1580
|
+
const credentialsInjection = secretsJsonPath ? `${generateCredentialsInjection(secretsJsonPath)}
|
|
1557
1581
|
` : "";
|
|
1558
1582
|
const content = `#!/usr/bin/env node
|
|
1559
1583
|
/**
|
|
@@ -1859,6 +1883,59 @@ start({
|
|
|
1859
1883
|
await fsWriteFile(serverPath, content);
|
|
1860
1884
|
}
|
|
1861
1885
|
};
|
|
1886
|
+
/**
|
|
1887
|
+
* Run a command with secrets injected into Credentials.
|
|
1888
|
+
* Uses Node's --import flag to preload a script that populates Credentials
|
|
1889
|
+
* before the command loads any modules that depend on them.
|
|
1890
|
+
*
|
|
1891
|
+
* @example
|
|
1892
|
+
* ```bash
|
|
1893
|
+
* gkm exec -- npx @better-auth/cli migrate
|
|
1894
|
+
* gkm exec -- npx prisma migrate dev
|
|
1895
|
+
* ```
|
|
1896
|
+
*/
|
|
1897
|
+
async function execCommand(commandArgs, options = {}) {
|
|
1898
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1899
|
+
if (commandArgs.length === 0) throw new Error("No command specified. Usage: gkm exec -- <command>");
|
|
1900
|
+
const defaultEnv = loadEnvFiles(".env");
|
|
1901
|
+
if (defaultEnv.loaded.length > 0) logger$8.log(`š¦ Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1902
|
+
const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
|
|
1903
|
+
if (appName) logger$8.log(`š¦ App: ${appName}`);
|
|
1904
|
+
const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
|
|
1905
|
+
if (secretCount > 0) logger$8.log(`š Loaded ${secretCount} secret(s)`);
|
|
1906
|
+
const preloadDir = join(cwd, ".gkm");
|
|
1907
|
+
await mkdir(preloadDir, { recursive: true });
|
|
1908
|
+
const preloadPath = join(preloadDir, "credentials-preload.ts");
|
|
1909
|
+
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
1910
|
+
const [cmd, ...args] = commandArgs;
|
|
1911
|
+
if (!cmd) throw new Error("No command specified");
|
|
1912
|
+
logger$8.log(`š Running: ${commandArgs.join(" ")}`);
|
|
1913
|
+
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
1914
|
+
const tsxImport = "--import tsx";
|
|
1915
|
+
const preloadImport = `--import ${preloadPath}`;
|
|
1916
|
+
const nodeOptions = [
|
|
1917
|
+
existingNodeOptions,
|
|
1918
|
+
tsxImport,
|
|
1919
|
+
preloadImport
|
|
1920
|
+
].filter(Boolean).join(" ");
|
|
1921
|
+
const child = spawn(cmd, args, {
|
|
1922
|
+
cwd,
|
|
1923
|
+
stdio: "inherit",
|
|
1924
|
+
env: {
|
|
1925
|
+
...process.env,
|
|
1926
|
+
...credentials,
|
|
1927
|
+
NODE_OPTIONS: nodeOptions
|
|
1928
|
+
}
|
|
1929
|
+
});
|
|
1930
|
+
const exitCode = await new Promise((resolve$1) => {
|
|
1931
|
+
child.on("close", (code) => resolve$1(code ?? 0));
|
|
1932
|
+
child.on("error", (error) => {
|
|
1933
|
+
logger$8.error(`Failed to run command: ${error.message}`);
|
|
1934
|
+
resolve$1(1);
|
|
1935
|
+
});
|
|
1936
|
+
});
|
|
1937
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
1938
|
+
}
|
|
1862
1939
|
|
|
1863
1940
|
//#endregion
|
|
1864
1941
|
//#region src/build/manifests.ts
|
|
@@ -1929,10 +2006,15 @@ const logger$6 = console;
|
|
|
1929
2006
|
async function buildCommand(options) {
|
|
1930
2007
|
const loadedConfig = await loadWorkspaceConfig();
|
|
1931
2008
|
if (loadedConfig.type === "workspace") {
|
|
1932
|
-
|
|
1933
|
-
|
|
2009
|
+
const cwd = resolve(process.cwd());
|
|
2010
|
+
const workspaceRoot = resolve(loadedConfig.workspace.root);
|
|
2011
|
+
const isAtWorkspaceRoot = cwd === workspaceRoot;
|
|
2012
|
+
if (isAtWorkspaceRoot) {
|
|
2013
|
+
logger$6.log("š¦ Detected workspace configuration");
|
|
2014
|
+
return workspaceBuildCommand(loadedConfig.workspace, options);
|
|
2015
|
+
}
|
|
1934
2016
|
}
|
|
1935
|
-
const config$1 = await loadConfig();
|
|
2017
|
+
const config$1 = loadedConfig.type === "workspace" ? (await loadAppConfig()).gkmConfig : await loadConfig();
|
|
1936
2018
|
const resolved = resolveProviders(config$1, options);
|
|
1937
2019
|
const productionConfigFromGkm = getProductionConfigFromGkm(config$1);
|
|
1938
2020
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
@@ -3376,26 +3458,22 @@ function getImageRef(registry, imageName, tag) {
|
|
|
3376
3458
|
}
|
|
3377
3459
|
/**
|
|
3378
3460
|
* Build Docker image
|
|
3461
|
+
* @param imageRef - Full image reference (registry/name:tag)
|
|
3462
|
+
* @param appName - Name of the app (used for Dockerfile.{appName} in workspaces)
|
|
3379
3463
|
*/
|
|
3380
|
-
async function buildImage(imageRef) {
|
|
3464
|
+
async function buildImage(imageRef, appName) {
|
|
3381
3465
|
logger$4.log(`\nšØ Building Docker image: ${imageRef}`);
|
|
3382
3466
|
const cwd = process.cwd();
|
|
3383
|
-
const
|
|
3384
|
-
|
|
3467
|
+
const lockfilePath = findLockfilePath(cwd);
|
|
3468
|
+
const lockfileDir = lockfilePath ? dirname(lockfilePath) : cwd;
|
|
3469
|
+
const inMonorepo = lockfileDir !== cwd;
|
|
3470
|
+
if (appName || inMonorepo) logger$4.log(" Generating Dockerfile for monorepo (turbo prune)...");
|
|
3385
3471
|
else logger$4.log(" Generating Dockerfile...");
|
|
3386
3472
|
await dockerCommand({});
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
if (lockfilePath) {
|
|
3392
|
-
const monorepoRoot = dirname(lockfilePath);
|
|
3393
|
-
const appRelPath = relative(monorepoRoot, cwd);
|
|
3394
|
-
dockerfilePath = join(appRelPath, ".gkm/docker/Dockerfile");
|
|
3395
|
-
buildCwd = monorepoRoot;
|
|
3396
|
-
logger$4.log(` Building from monorepo root: ${monorepoRoot}`);
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3473
|
+
const dockerfileSuffix = appName ? `.${appName}` : "";
|
|
3474
|
+
const dockerfilePath = `.gkm/docker/Dockerfile${dockerfileSuffix}`;
|
|
3475
|
+
const buildCwd = lockfilePath && (inMonorepo || appName) ? lockfileDir : cwd;
|
|
3476
|
+
if (buildCwd !== cwd) logger$4.log(` Building from workspace root: ${buildCwd}`);
|
|
3399
3477
|
try {
|
|
3400
3478
|
execSync(`DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -f ${dockerfilePath} -t ${imageRef} .`, {
|
|
3401
3479
|
cwd: buildCwd,
|
|
@@ -3432,7 +3510,7 @@ async function deployDocker(options) {
|
|
|
3432
3510
|
const { stage, tag, skipPush, masterKey, config: config$1 } = options;
|
|
3433
3511
|
const imageName = config$1.imageName;
|
|
3434
3512
|
const imageRef = getImageRef(config$1.registry, imageName, tag);
|
|
3435
|
-
await buildImage(imageRef);
|
|
3513
|
+
await buildImage(imageRef, config$1.appName);
|
|
3436
3514
|
if (!skipPush) if (!config$1.registry) logger$4.warn("\nā ļø No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
3437
3515
|
else await pushImage(imageRef);
|
|
3438
3516
|
logger$4.log("\nā
Docker deployment ready!");
|
|
@@ -4144,11 +4222,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4144
4222
|
await provisionServices(api, project.projectId, environmentId, workspace.name, dockerServices);
|
|
4145
4223
|
}
|
|
4146
4224
|
const deployedAppUrls = {};
|
|
4225
|
+
if (!skipBuild) {
|
|
4226
|
+
logger$1.log("\nšļø Building workspace...");
|
|
4227
|
+
try {
|
|
4228
|
+
await buildCommand({
|
|
4229
|
+
provider: "server",
|
|
4230
|
+
production: true,
|
|
4231
|
+
stage
|
|
4232
|
+
});
|
|
4233
|
+
logger$1.log(" ā Workspace build complete");
|
|
4234
|
+
} catch (error) {
|
|
4235
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4236
|
+
logger$1.log(` ā Workspace build failed: ${message}`);
|
|
4237
|
+
throw error;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4147
4240
|
logger$1.log("\nš¦ Deploying applications...");
|
|
4148
4241
|
const results = [];
|
|
4149
4242
|
for (const appName of appsToDeployNames) {
|
|
4150
4243
|
const app = workspace.apps[appName];
|
|
4151
|
-
const appPath = app.path;
|
|
4152
4244
|
logger$1.log(`\n ${app.type === "backend" ? "āļø" : "š"} Deploying ${appName}...`);
|
|
4153
4245
|
try {
|
|
4154
4246
|
const dokployAppName = `${workspace.name}-${appName}`;
|
|
@@ -4161,21 +4253,6 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4161
4253
|
if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` Application already exists`);
|
|
4162
4254
|
else throw error;
|
|
4163
4255
|
}
|
|
4164
|
-
if (!skipBuild) {
|
|
4165
|
-
logger$1.log(` Building ${appName}...`);
|
|
4166
|
-
const originalCwd = process.cwd();
|
|
4167
|
-
const fullAppPath = `${workspace.root}/${appPath}`;
|
|
4168
|
-
try {
|
|
4169
|
-
process.chdir(fullAppPath);
|
|
4170
|
-
await buildCommand({
|
|
4171
|
-
provider: "server",
|
|
4172
|
-
production: true,
|
|
4173
|
-
stage
|
|
4174
|
-
});
|
|
4175
|
-
} finally {
|
|
4176
|
-
process.chdir(originalCwd);
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
4256
|
const imageName = `${workspace.name}-${appName}`;
|
|
4180
4257
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
4181
4258
|
logger$1.log(` Building Docker image: ${imageRef}`);
|
|
@@ -4185,7 +4262,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4185
4262
|
skipPush: false,
|
|
4186
4263
|
config: {
|
|
4187
4264
|
registry,
|
|
4188
|
-
imageName
|
|
4265
|
+
imageName,
|
|
4266
|
+
appName
|
|
4189
4267
|
}
|
|
4190
4268
|
});
|
|
4191
4269
|
const envVars = [`NODE_ENV=production`, `PORT=${app.port}`];
|
|
@@ -4553,7 +4631,9 @@ function generateAuthAppFiles(options) {
|
|
|
4553
4631
|
dev: "gkm dev --entry ./src/index.ts",
|
|
4554
4632
|
build: "tsc",
|
|
4555
4633
|
start: "node dist/index.js",
|
|
4556
|
-
typecheck: "tsc --noEmit"
|
|
4634
|
+
typecheck: "tsc --noEmit",
|
|
4635
|
+
"db:migrate": "gkm exec -- npx @better-auth/cli migrate",
|
|
4636
|
+
"db:generate": "gkm exec -- npx @better-auth/cli generate"
|
|
4557
4637
|
},
|
|
4558
4638
|
dependencies: {
|
|
4559
4639
|
[modelsPackage]: "workspace:*",
|
|
@@ -7467,6 +7547,16 @@ program.command("dev").description("Start development server with automatic relo
|
|
|
7467
7547
|
process.exit(1);
|
|
7468
7548
|
}
|
|
7469
7549
|
});
|
|
7550
|
+
program.command("exec").description("Run a command with secrets injected into Credentials").argument("<command...>", "Command to run (use -- before command)").action(async (commandArgs) => {
|
|
7551
|
+
try {
|
|
7552
|
+
const globalOptions = program.opts();
|
|
7553
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
7554
|
+
await execCommand(commandArgs);
|
|
7555
|
+
} catch (error) {
|
|
7556
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
7557
|
+
process.exit(1);
|
|
7558
|
+
}
|
|
7559
|
+
});
|
|
7470
7560
|
program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from", "development").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
|
|
7471
7561
|
try {
|
|
7472
7562
|
const globalOptions = program.opts();
|