159 lines
5.0 KiB
TypeScript
159 lines
5.0 KiB
TypeScript
import path from "node:path";
|
|
import fs from "node:fs";
|
|
|
|
import pkg from "../package.json";
|
|
import { parseArgs, ParseError, resolvePorts } from "./args";
|
|
import { runDev } from "./dev";
|
|
import { runRelease } from "./run";
|
|
import { runServiceCommand } from "./service";
|
|
import { warnIfStaleServiceUnit } from "./service/stale_check";
|
|
import { runStart } from "./start";
|
|
import { ensureValidPort } from "./ports";
|
|
|
|
function printHelp() {
|
|
console.log(`kandev launcher
|
|
|
|
Usage:
|
|
kandev run [--port <port>] [--verbose] [--debug]
|
|
kandev dev [--port <port>]
|
|
kandev start [--port <port>] [--verbose] [--debug]
|
|
kandev [--port <port>] [--verbose] [--debug]
|
|
kandev --dev [--port <port>]
|
|
kandev service install|uninstall|start|stop|restart|status|logs [--system]
|
|
|
|
Examples:
|
|
kandev
|
|
kandev run
|
|
kandev --dev
|
|
kandev dev
|
|
kandev start
|
|
kandev --version
|
|
kandev --port 3000
|
|
kandev --debug
|
|
|
|
Options:
|
|
dev Use local repo for dev (Go backend + Vite dev server).
|
|
start Use local production build (Go backend serves Vite dist).
|
|
run Use installed runtime bundle (default).
|
|
service Manage kandev as an OS service (systemd / launchd).
|
|
Run 'kandev service --help' for details.
|
|
--dev Alias for "dev".
|
|
--version, -V Print CLI version and exit.
|
|
--port Port for the Go backend (the URL kandev opens on in
|
|
start/run). Alias for --backend-port. Also reads
|
|
KANDEV_PORT or KANDEV_BACKEND_PORT.
|
|
--verbose, -v Show info logs from backend + web.
|
|
--debug Show debug logs + agent message dumps.
|
|
--headless Skip opening the browser. Used by service units.
|
|
--help, -h Show help.
|
|
|
|
Advanced:
|
|
--backend-port Same as --port.
|
|
--web-internal-port Override the internal dev web port. The Go backend
|
|
reverse-proxies to it in dev; start/run serve from Go.
|
|
Also reads KANDEV_WEB_PORT.
|
|
--runtime-version <tag> Download and use a specific release tag instead of
|
|
the installed runtime. For debugging only.
|
|
Example: kandev --runtime-version v0.16.0
|
|
`);
|
|
}
|
|
|
|
function findRepoRoot(startDir: string): string | null {
|
|
let current = path.resolve(startDir);
|
|
while (true) {
|
|
if (current.endsWith(`${path.sep}apps`)) {
|
|
const backendInApps = path.join(current, "backend");
|
|
const webInApps = path.join(current, "web");
|
|
if (fs.existsSync(backendInApps) && fs.existsSync(webInApps)) {
|
|
return path.dirname(current);
|
|
}
|
|
}
|
|
const backendDir = path.join(current, "apps", "backend");
|
|
const webDir = path.join(current, "apps", "web");
|
|
if (fs.existsSync(backendDir) && fs.existsSync(webDir)) {
|
|
return current;
|
|
}
|
|
const parent = path.dirname(current);
|
|
if (parent === current) {
|
|
return null;
|
|
}
|
|
current = parent;
|
|
}
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
// The `service` subcommand has its own argv parser (own subcommands, flags).
|
|
// Handle it before the top-level flag parser to keep the two parsers isolated.
|
|
if (process.argv[2] === "service") {
|
|
await runServiceCommand(process.argv.slice(3));
|
|
return;
|
|
}
|
|
|
|
const { options, showHelp } = parseArgs(process.argv.slice(2));
|
|
|
|
if (options.showVersion) {
|
|
console.log(pkg.version);
|
|
return;
|
|
}
|
|
|
|
if (showHelp) {
|
|
printHelp();
|
|
return;
|
|
}
|
|
|
|
const resolved = resolvePorts(options, process.env);
|
|
const backendPort = ensureValidPort(resolved.backendPort, "backend port");
|
|
const webPort =
|
|
options.command === "dev" ? ensureValidPort(resolved.webPort, "web port") : undefined;
|
|
|
|
// After an npm/brew upgrade, paths baked into an installed service unit may
|
|
// be stale. Warn once before launch so the user knows to re-run install.
|
|
// Skip when invoked from a service unit (--headless) — the warning would
|
|
// land in journalctl/launchd logs and pile up on every restart.
|
|
if (!options.headless) {
|
|
warnIfStaleServiceUnit();
|
|
}
|
|
|
|
if (options.command === "dev") {
|
|
const repoRoot = findRepoRoot(process.cwd());
|
|
if (!repoRoot) {
|
|
throw new Error("Unable to locate repo root for dev. Run from the repo.");
|
|
}
|
|
await runDev({ repoRoot, backendPort, webPort });
|
|
return;
|
|
}
|
|
|
|
if (options.command === "start") {
|
|
const repoRoot = findRepoRoot(process.cwd());
|
|
if (!repoRoot) {
|
|
throw new Error("Unable to locate repo root for start. Run from the repo.");
|
|
}
|
|
await runStart({
|
|
repoRoot,
|
|
backendPort,
|
|
verbose: options.verbose,
|
|
debug: options.debug,
|
|
headless: options.headless,
|
|
});
|
|
return;
|
|
}
|
|
|
|
await runRelease({
|
|
runtimeVersion: options.runtimeVersion,
|
|
backendPort,
|
|
verbose: options.verbose,
|
|
debug: options.debug,
|
|
headless: options.headless,
|
|
});
|
|
}
|
|
|
|
main().catch((err) => {
|
|
if (err instanceof ParseError) {
|
|
console.error(`[kandev] ${err.message}`);
|
|
console.error("[kandev] run --help for usage");
|
|
process.exit(2);
|
|
}
|
|
console.error(`[kandev] ${err instanceof Error ? err.message : String(err)}`);
|
|
process.exit(1);
|
|
});
|