@geekmidas/cli 0.37.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 +158 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +138 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/build/index.ts +23 -6
- package/src/deploy/docker.ts +20 -20
- package/src/deploy/index.ts +20 -19
- package/src/dev/__tests__/entry-integration.spec.ts +86 -2
- package/src/dev/index.ts +150 -11
- package/src/index.ts +18 -1
- package/src/init/generators/auth.ts +2 -0
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ const node_module = require_chunk.__toESM(require("node:module"));
|
|
|
27
27
|
|
|
28
28
|
//#region package.json
|
|
29
29
|
var name = "@geekmidas/cli";
|
|
30
|
-
var version = "0.
|
|
30
|
+
var version = "0.38.0";
|
|
31
31
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
32
32
|
var private$1 = false;
|
|
33
33
|
var type = "module";
|
|
@@ -240,7 +240,7 @@ async function prompt$1(message, hidden = false) {
|
|
|
240
240
|
if (!process.stdin.isTTY) throw new Error("Interactive input required. Please provide --token option.");
|
|
241
241
|
if (hidden) {
|
|
242
242
|
process.stdout.write(message);
|
|
243
|
-
return new Promise((resolve$
|
|
243
|
+
return new Promise((resolve$2, reject) => {
|
|
244
244
|
let value = "";
|
|
245
245
|
const cleanup = () => {
|
|
246
246
|
process.stdin.setRawMode(false);
|
|
@@ -257,7 +257,7 @@ async function prompt$1(message, hidden = false) {
|
|
|
257
257
|
if (c === "\n" || c === "\r") {
|
|
258
258
|
cleanup();
|
|
259
259
|
process.stdout.write("\n");
|
|
260
|
-
resolve$
|
|
260
|
+
resolve$2(value);
|
|
261
261
|
} else if (c === "") {
|
|
262
262
|
cleanup();
|
|
263
263
|
process.stdout.write("\n");
|
|
@@ -892,15 +892,15 @@ function loadEnvFiles(envConfig, cwd = process.cwd()) {
|
|
|
892
892
|
* @internal Exported for testing
|
|
893
893
|
*/
|
|
894
894
|
async function isPortAvailable(port) {
|
|
895
|
-
return new Promise((resolve$
|
|
895
|
+
return new Promise((resolve$2) => {
|
|
896
896
|
const server = (0, node_net.createServer)();
|
|
897
897
|
server.once("error", (err) => {
|
|
898
|
-
if (err.code === "EADDRINUSE") resolve$
|
|
899
|
-
else resolve$
|
|
898
|
+
if (err.code === "EADDRINUSE") resolve$2(false);
|
|
899
|
+
else resolve$2(false);
|
|
900
900
|
});
|
|
901
901
|
server.once("listening", () => {
|
|
902
902
|
server.close();
|
|
903
|
-
resolve$
|
|
903
|
+
resolve$2(true);
|
|
904
904
|
});
|
|
905
905
|
server.listen(port);
|
|
906
906
|
});
|
|
@@ -1491,7 +1491,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1491
1491
|
};
|
|
1492
1492
|
process.on("SIGINT", shutdown);
|
|
1493
1493
|
process.on("SIGTERM", shutdown);
|
|
1494
|
-
return new Promise((resolve$
|
|
1494
|
+
return new Promise((resolve$2, reject) => {
|
|
1495
1495
|
turboProcess.on("error", (error) => {
|
|
1496
1496
|
logger$8.error("❌ Turbo error:", error);
|
|
1497
1497
|
reject(error);
|
|
@@ -1499,7 +1499,7 @@ async function workspaceDevCommand(workspace, options) {
|
|
|
1499
1499
|
turboProcess.on("exit", (code) => {
|
|
1500
1500
|
if (endpointWatcher) endpointWatcher.close().catch(() => {});
|
|
1501
1501
|
if (code !== null && code !== 0) reject(new Error(`Turbo exited with code ${code}`));
|
|
1502
|
-
else resolve$
|
|
1502
|
+
else resolve$2();
|
|
1503
1503
|
});
|
|
1504
1504
|
});
|
|
1505
1505
|
}
|
|
@@ -1542,19 +1542,43 @@ function findSecretsRoot(startDir) {
|
|
|
1542
1542
|
return startDir;
|
|
1543
1543
|
}
|
|
1544
1544
|
/**
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
1545
|
+
* Generate the credentials injection code snippet.
|
|
1546
|
+
* This is the common logic used by both entry wrapper and exec preload.
|
|
1547
|
+
* @internal
|
|
1547
1548
|
*/
|
|
1548
|
-
|
|
1549
|
-
|
|
1549
|
+
function generateCredentialsInjection(secretsJsonPath) {
|
|
1550
|
+
return `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1550
1551
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1551
1552
|
|
|
1552
|
-
// Inject dev secrets into Credentials
|
|
1553
|
+
// Inject dev secrets into Credentials
|
|
1553
1554
|
const secretsPath = '${secretsJsonPath}';
|
|
1554
1555
|
if (existsSync(secretsPath)) {
|
|
1555
|
-
|
|
1556
|
+
const secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));
|
|
1557
|
+
Object.assign(Credentials, secrets);
|
|
1558
|
+
// Debug: uncomment to verify preload is running
|
|
1559
|
+
// console.log('[gkm preload] Injected', Object.keys(secrets).length, 'credentials');
|
|
1556
1560
|
}
|
|
1557
|
-
|
|
1561
|
+
`;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Create a preload script that injects secrets into Credentials.
|
|
1565
|
+
* Used by `gkm exec` to inject secrets before running any command.
|
|
1566
|
+
* @internal Exported for testing
|
|
1567
|
+
*/
|
|
1568
|
+
async function createCredentialsPreload(preloadPath, secretsJsonPath) {
|
|
1569
|
+
const content = `/**
|
|
1570
|
+
* Credentials preload generated by 'gkm exec'
|
|
1571
|
+
* This file is loaded via NODE_OPTIONS="--import <path>"
|
|
1572
|
+
*/
|
|
1573
|
+
${generateCredentialsInjection(secretsJsonPath)}`;
|
|
1574
|
+
await (0, node_fs_promises.writeFile)(preloadPath, content);
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Create a wrapper script that injects secrets before importing the entry file.
|
|
1578
|
+
* @internal Exported for testing
|
|
1579
|
+
*/
|
|
1580
|
+
async function createEntryWrapper(wrapperPath, entryPath, secretsJsonPath) {
|
|
1581
|
+
const credentialsInjection = secretsJsonPath ? `${generateCredentialsInjection(secretsJsonPath)}
|
|
1558
1582
|
` : "";
|
|
1559
1583
|
const content = `#!/usr/bin/env node
|
|
1560
1584
|
/**
|
|
@@ -1580,7 +1604,8 @@ async function prepareEntryCredentials(options) {
|
|
|
1580
1604
|
workspaceAppPort = appConfig.app.port;
|
|
1581
1605
|
secretsRoot = appConfig.workspaceRoot;
|
|
1582
1606
|
appName = appConfig.appName;
|
|
1583
|
-
} catch {
|
|
1607
|
+
} catch (error) {
|
|
1608
|
+
logger$8.log(`⚠️ Could not load workspace config: ${error.message}`);
|
|
1584
1609
|
secretsRoot = findSecretsRoot(cwd);
|
|
1585
1610
|
appName = require_config.getAppNameFromCwd(cwd) ?? void 0;
|
|
1586
1611
|
}
|
|
@@ -1610,7 +1635,7 @@ async function entryDevCommand(options) {
|
|
|
1610
1635
|
if (!(0, node_fs.existsSync)(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
|
|
1611
1636
|
const defaultEnv = loadEnvFiles(".env");
|
|
1612
1637
|
if (defaultEnv.loaded.length > 0) logger$8.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1613
|
-
const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.port });
|
|
1638
|
+
const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
|
|
1614
1639
|
if (appName) logger$8.log(`📦 App: ${appName} (port ${resolvedPort})`);
|
|
1615
1640
|
logger$8.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
|
|
1616
1641
|
if (Object.keys(credentials).length > 1) logger$8.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
|
|
@@ -1684,12 +1709,12 @@ var EntryRunner = class {
|
|
|
1684
1709
|
if (code !== null && code !== 0 && code !== 143) logger$8.error(`❌ Process exited with code ${code}`);
|
|
1685
1710
|
this.isRunning = false;
|
|
1686
1711
|
});
|
|
1687
|
-
await new Promise((resolve$
|
|
1712
|
+
await new Promise((resolve$2) => setTimeout(resolve$2, 500));
|
|
1688
1713
|
if (this.isRunning) logger$8.log(`\n🎉 Running at http://localhost:${this.port}`);
|
|
1689
1714
|
}
|
|
1690
1715
|
async restart() {
|
|
1691
1716
|
this.stopProcess();
|
|
1692
|
-
await new Promise((resolve$
|
|
1717
|
+
await new Promise((resolve$2) => setTimeout(resolve$2, 500));
|
|
1693
1718
|
await this.runProcess();
|
|
1694
1719
|
}
|
|
1695
1720
|
stop() {
|
|
@@ -1761,7 +1786,7 @@ var DevServer = class {
|
|
|
1761
1786
|
if (code !== null && code !== 0 && signal !== "SIGTERM") logger$8.error(`❌ Server exited with code ${code}`);
|
|
1762
1787
|
this.isRunning = false;
|
|
1763
1788
|
});
|
|
1764
|
-
await new Promise((resolve$
|
|
1789
|
+
await new Promise((resolve$2) => setTimeout(resolve$2, 1e3));
|
|
1765
1790
|
if (this.isRunning) {
|
|
1766
1791
|
logger$8.log(`\n🎉 Server running at http://localhost:${this.actualPort}`);
|
|
1767
1792
|
if (this.enableOpenApi) logger$8.log(`📚 API Docs available at http://localhost:${this.actualPort}/__docs`);
|
|
@@ -1796,7 +1821,7 @@ var DevServer = class {
|
|
|
1796
1821
|
let attempts = 0;
|
|
1797
1822
|
while (attempts < 30) {
|
|
1798
1823
|
if (await isPortAvailable(portToReuse)) break;
|
|
1799
|
-
await new Promise((resolve$
|
|
1824
|
+
await new Promise((resolve$2) => setTimeout(resolve$2, 100));
|
|
1800
1825
|
attempts++;
|
|
1801
1826
|
}
|
|
1802
1827
|
this.requestedPort = portToReuse;
|
|
@@ -1804,9 +1829,9 @@ var DevServer = class {
|
|
|
1804
1829
|
}
|
|
1805
1830
|
async createServerEntry() {
|
|
1806
1831
|
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1807
|
-
const { relative: relative$
|
|
1832
|
+
const { relative: relative$6, dirname: dirname$8 } = await import("node:path");
|
|
1808
1833
|
const serverPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1809
|
-
const relativeAppPath = relative$
|
|
1834
|
+
const relativeAppPath = relative$6(dirname$8(serverPath), (0, node_path.join)(dirname$8(serverPath), "app.js"));
|
|
1810
1835
|
const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1811
1836
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1812
1837
|
|
|
@@ -1859,6 +1884,59 @@ start({
|
|
|
1859
1884
|
await fsWriteFile(serverPath, content);
|
|
1860
1885
|
}
|
|
1861
1886
|
};
|
|
1887
|
+
/**
|
|
1888
|
+
* Run a command with secrets injected into Credentials.
|
|
1889
|
+
* Uses Node's --import flag to preload a script that populates Credentials
|
|
1890
|
+
* before the command loads any modules that depend on them.
|
|
1891
|
+
*
|
|
1892
|
+
* @example
|
|
1893
|
+
* ```bash
|
|
1894
|
+
* gkm exec -- npx @better-auth/cli migrate
|
|
1895
|
+
* gkm exec -- npx prisma migrate dev
|
|
1896
|
+
* ```
|
|
1897
|
+
*/
|
|
1898
|
+
async function execCommand(commandArgs, options = {}) {
|
|
1899
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1900
|
+
if (commandArgs.length === 0) throw new Error("No command specified. Usage: gkm exec -- <command>");
|
|
1901
|
+
const defaultEnv = loadEnvFiles(".env");
|
|
1902
|
+
if (defaultEnv.loaded.length > 0) logger$8.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
|
|
1903
|
+
const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
|
|
1904
|
+
if (appName) logger$8.log(`📦 App: ${appName}`);
|
|
1905
|
+
const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
|
|
1906
|
+
if (secretCount > 0) logger$8.log(`🔐 Loaded ${secretCount} secret(s)`);
|
|
1907
|
+
const preloadDir = (0, node_path.join)(cwd, ".gkm");
|
|
1908
|
+
await (0, node_fs_promises.mkdir)(preloadDir, { recursive: true });
|
|
1909
|
+
const preloadPath = (0, node_path.join)(preloadDir, "credentials-preload.ts");
|
|
1910
|
+
await createCredentialsPreload(preloadPath, secretsJsonPath);
|
|
1911
|
+
const [cmd, ...args] = commandArgs;
|
|
1912
|
+
if (!cmd) throw new Error("No command specified");
|
|
1913
|
+
logger$8.log(`🚀 Running: ${commandArgs.join(" ")}`);
|
|
1914
|
+
const existingNodeOptions = process.env.NODE_OPTIONS ?? "";
|
|
1915
|
+
const tsxImport = "--import tsx";
|
|
1916
|
+
const preloadImport = `--import ${preloadPath}`;
|
|
1917
|
+
const nodeOptions = [
|
|
1918
|
+
existingNodeOptions,
|
|
1919
|
+
tsxImport,
|
|
1920
|
+
preloadImport
|
|
1921
|
+
].filter(Boolean).join(" ");
|
|
1922
|
+
const child = (0, node_child_process.spawn)(cmd, args, {
|
|
1923
|
+
cwd,
|
|
1924
|
+
stdio: "inherit",
|
|
1925
|
+
env: {
|
|
1926
|
+
...process.env,
|
|
1927
|
+
...credentials,
|
|
1928
|
+
NODE_OPTIONS: nodeOptions
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
const exitCode = await new Promise((resolve$2) => {
|
|
1932
|
+
child.on("close", (code) => resolve$2(code ?? 0));
|
|
1933
|
+
child.on("error", (error) => {
|
|
1934
|
+
logger$8.error(`Failed to run command: ${error.message}`);
|
|
1935
|
+
resolve$2(1);
|
|
1936
|
+
});
|
|
1937
|
+
});
|
|
1938
|
+
if (exitCode !== 0) process.exit(exitCode);
|
|
1939
|
+
}
|
|
1862
1940
|
|
|
1863
1941
|
//#endregion
|
|
1864
1942
|
//#region src/build/manifests.ts
|
|
@@ -1929,10 +2007,15 @@ const logger$6 = console;
|
|
|
1929
2007
|
async function buildCommand(options) {
|
|
1930
2008
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
1931
2009
|
if (loadedConfig.type === "workspace") {
|
|
1932
|
-
|
|
1933
|
-
|
|
2010
|
+
const cwd = (0, node_path.resolve)(process.cwd());
|
|
2011
|
+
const workspaceRoot = (0, node_path.resolve)(loadedConfig.workspace.root);
|
|
2012
|
+
const isAtWorkspaceRoot = cwd === workspaceRoot;
|
|
2013
|
+
if (isAtWorkspaceRoot) {
|
|
2014
|
+
logger$6.log("📦 Detected workspace configuration");
|
|
2015
|
+
return workspaceBuildCommand(loadedConfig.workspace, options);
|
|
2016
|
+
}
|
|
1934
2017
|
}
|
|
1935
|
-
const config = await require_config.loadConfig();
|
|
2018
|
+
const config = loadedConfig.type === "workspace" ? (await require_config.loadAppConfig()).gkmConfig : await require_config.loadConfig();
|
|
1936
2019
|
const resolved = resolveProviders(config, options);
|
|
1937
2020
|
const productionConfigFromGkm = getProductionConfigFromGkm(config);
|
|
1938
2021
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
@@ -2098,7 +2181,7 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2098
2181
|
try {
|
|
2099
2182
|
const turboCommand = getTurboCommand(pm);
|
|
2100
2183
|
logger$6.log(`Running: ${turboCommand}`);
|
|
2101
|
-
await new Promise((resolve$
|
|
2184
|
+
await new Promise((resolve$2, reject) => {
|
|
2102
2185
|
const child = (0, node_child_process.spawn)(turboCommand, {
|
|
2103
2186
|
shell: true,
|
|
2104
2187
|
cwd: workspace.root,
|
|
@@ -2109,7 +2192,7 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2109
2192
|
}
|
|
2110
2193
|
});
|
|
2111
2194
|
child.on("close", (code) => {
|
|
2112
|
-
if (code === 0) resolve$
|
|
2195
|
+
if (code === 0) resolve$2();
|
|
2113
2196
|
else reject(new Error(`Turbo build failed with exit code ${code}`));
|
|
2114
2197
|
});
|
|
2115
2198
|
child.on("error", (err) => {
|
|
@@ -3376,26 +3459,22 @@ function getImageRef(registry, imageName, tag) {
|
|
|
3376
3459
|
}
|
|
3377
3460
|
/**
|
|
3378
3461
|
* Build Docker image
|
|
3462
|
+
* @param imageRef - Full image reference (registry/name:tag)
|
|
3463
|
+
* @param appName - Name of the app (used for Dockerfile.{appName} in workspaces)
|
|
3379
3464
|
*/
|
|
3380
|
-
async function buildImage(imageRef) {
|
|
3465
|
+
async function buildImage(imageRef, appName) {
|
|
3381
3466
|
logger$4.log(`\n🔨 Building Docker image: ${imageRef}`);
|
|
3382
3467
|
const cwd = process.cwd();
|
|
3383
|
-
const
|
|
3384
|
-
|
|
3468
|
+
const lockfilePath = findLockfilePath(cwd);
|
|
3469
|
+
const lockfileDir = lockfilePath ? (0, node_path.dirname)(lockfilePath) : cwd;
|
|
3470
|
+
const inMonorepo = lockfileDir !== cwd;
|
|
3471
|
+
if (appName || inMonorepo) logger$4.log(" Generating Dockerfile for monorepo (turbo prune)...");
|
|
3385
3472
|
else logger$4.log(" Generating Dockerfile...");
|
|
3386
3473
|
await dockerCommand({});
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
if (lockfilePath) {
|
|
3392
|
-
const monorepoRoot = (0, node_path.dirname)(lockfilePath);
|
|
3393
|
-
const appRelPath = (0, node_path.relative)(monorepoRoot, cwd);
|
|
3394
|
-
dockerfilePath = (0, node_path.join)(appRelPath, ".gkm/docker/Dockerfile");
|
|
3395
|
-
buildCwd = monorepoRoot;
|
|
3396
|
-
logger$4.log(` Building from monorepo root: ${monorepoRoot}`);
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3474
|
+
const dockerfileSuffix = appName ? `.${appName}` : "";
|
|
3475
|
+
const dockerfilePath = `.gkm/docker/Dockerfile${dockerfileSuffix}`;
|
|
3476
|
+
const buildCwd = lockfilePath && (inMonorepo || appName) ? lockfileDir : cwd;
|
|
3477
|
+
if (buildCwd !== cwd) logger$4.log(` Building from workspace root: ${buildCwd}`);
|
|
3399
3478
|
try {
|
|
3400
3479
|
(0, node_child_process.execSync)(`DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -f ${dockerfilePath} -t ${imageRef} .`, {
|
|
3401
3480
|
cwd: buildCwd,
|
|
@@ -3432,7 +3511,7 @@ async function deployDocker(options) {
|
|
|
3432
3511
|
const { stage, tag, skipPush, masterKey, config } = options;
|
|
3433
3512
|
const imageName = config.imageName;
|
|
3434
3513
|
const imageRef = getImageRef(config.registry, imageName, tag);
|
|
3435
|
-
await buildImage(imageRef);
|
|
3514
|
+
await buildImage(imageRef, config.appName);
|
|
3436
3515
|
if (!skipPush) if (!config.registry) logger$4.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
|
|
3437
3516
|
else await pushImage(imageRef);
|
|
3438
3517
|
logger$4.log("\n✅ Docker deployment ready!");
|
|
@@ -3736,7 +3815,7 @@ async function prompt(message, hidden = false) {
|
|
|
3736
3815
|
if (!process.stdin.isTTY) throw new Error("Interactive input required. Please configure manually.");
|
|
3737
3816
|
if (hidden) {
|
|
3738
3817
|
process.stdout.write(message);
|
|
3739
|
-
return new Promise((resolve$
|
|
3818
|
+
return new Promise((resolve$2) => {
|
|
3740
3819
|
let value = "";
|
|
3741
3820
|
const onData = (char) => {
|
|
3742
3821
|
const c = char.toString();
|
|
@@ -3745,7 +3824,7 @@ async function prompt(message, hidden = false) {
|
|
|
3745
3824
|
process.stdin.pause();
|
|
3746
3825
|
process.stdin.removeListener("data", onData);
|
|
3747
3826
|
process.stdout.write("\n");
|
|
3748
|
-
resolve$
|
|
3827
|
+
resolve$2(value);
|
|
3749
3828
|
} else if (c === "") {
|
|
3750
3829
|
process.stdin.setRawMode(false);
|
|
3751
3830
|
process.stdin.pause();
|
|
@@ -4144,11 +4223,25 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4144
4223
|
await provisionServices(api, project.projectId, environmentId, workspace.name, dockerServices);
|
|
4145
4224
|
}
|
|
4146
4225
|
const deployedAppUrls = {};
|
|
4226
|
+
if (!skipBuild) {
|
|
4227
|
+
logger$1.log("\n🏗️ Building workspace...");
|
|
4228
|
+
try {
|
|
4229
|
+
await buildCommand({
|
|
4230
|
+
provider: "server",
|
|
4231
|
+
production: true,
|
|
4232
|
+
stage
|
|
4233
|
+
});
|
|
4234
|
+
logger$1.log(" ✓ Workspace build complete");
|
|
4235
|
+
} catch (error) {
|
|
4236
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
4237
|
+
logger$1.log(` ✗ Workspace build failed: ${message}`);
|
|
4238
|
+
throw error;
|
|
4239
|
+
}
|
|
4240
|
+
}
|
|
4147
4241
|
logger$1.log("\n📦 Deploying applications...");
|
|
4148
4242
|
const results = [];
|
|
4149
4243
|
for (const appName of appsToDeployNames) {
|
|
4150
4244
|
const app = workspace.apps[appName];
|
|
4151
|
-
const appPath = app.path;
|
|
4152
4245
|
logger$1.log(`\n ${app.type === "backend" ? "⚙️" : "🌐"} Deploying ${appName}...`);
|
|
4153
4246
|
try {
|
|
4154
4247
|
const dokployAppName = `${workspace.name}-${appName}`;
|
|
@@ -4161,21 +4254,6 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4161
4254
|
if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` Application already exists`);
|
|
4162
4255
|
else throw error;
|
|
4163
4256
|
}
|
|
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
4257
|
const imageName = `${workspace.name}-${appName}`;
|
|
4180
4258
|
const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
|
|
4181
4259
|
logger$1.log(` Building Docker image: ${imageRef}`);
|
|
@@ -4185,7 +4263,8 @@ async function workspaceDeployCommand(workspace, options) {
|
|
|
4185
4263
|
skipPush: false,
|
|
4186
4264
|
config: {
|
|
4187
4265
|
registry,
|
|
4188
|
-
imageName
|
|
4266
|
+
imageName,
|
|
4267
|
+
appName
|
|
4189
4268
|
}
|
|
4190
4269
|
});
|
|
4191
4270
|
const envVars = [`NODE_ENV=production`, `PORT=${app.port}`];
|
|
@@ -4553,7 +4632,9 @@ function generateAuthAppFiles(options) {
|
|
|
4553
4632
|
dev: "gkm dev --entry ./src/index.ts",
|
|
4554
4633
|
build: "tsc",
|
|
4555
4634
|
start: "node dist/index.js",
|
|
4556
|
-
typecheck: "tsc --noEmit"
|
|
4635
|
+
typecheck: "tsc --noEmit",
|
|
4636
|
+
"db:migrate": "gkm exec -- npx @better-auth/cli migrate",
|
|
4637
|
+
"db:generate": "gkm exec -- npx @better-auth/cli generate"
|
|
4557
4638
|
},
|
|
4558
4639
|
dependencies: {
|
|
4559
4640
|
[modelsPackage]: "workspace:*",
|
|
@@ -7393,9 +7474,9 @@ async function testCommand(options = {}) {
|
|
|
7393
7474
|
NODE_ENV: "test"
|
|
7394
7475
|
}
|
|
7395
7476
|
});
|
|
7396
|
-
return new Promise((resolve$
|
|
7477
|
+
return new Promise((resolve$2, reject) => {
|
|
7397
7478
|
vitestProcess.on("close", (code) => {
|
|
7398
|
-
if (code === 0) resolve$
|
|
7479
|
+
if (code === 0) resolve$2();
|
|
7399
7480
|
else reject(new Error(`Tests failed with exit code ${code}`));
|
|
7400
7481
|
});
|
|
7401
7482
|
vitestProcess.on("error", (error) => {
|
|
@@ -7467,6 +7548,16 @@ program.command("dev").description("Start development server with automatic relo
|
|
|
7467
7548
|
process.exit(1);
|
|
7468
7549
|
}
|
|
7469
7550
|
});
|
|
7551
|
+
program.command("exec").description("Run a command with secrets injected into Credentials").argument("<command...>", "Command to run (use -- before command)").action(async (commandArgs) => {
|
|
7552
|
+
try {
|
|
7553
|
+
const globalOptions = program.opts();
|
|
7554
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
7555
|
+
await execCommand(commandArgs);
|
|
7556
|
+
} catch (error) {
|
|
7557
|
+
console.error(error instanceof Error ? error.message : "Command failed");
|
|
7558
|
+
process.exit(1);
|
|
7559
|
+
}
|
|
7560
|
+
});
|
|
7470
7561
|
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
7562
|
try {
|
|
7472
7563
|
const globalOptions = program.opts();
|