platform-tooling/run/cli/commands/dev/start.ts

178 lines
5.5 KiB
TypeScript

/**
* Dev start commands - dev, devAll, devCi, devInfra, devTools
*/
import { DockerOps } from '../../../core/docker';
import { ServiceManager, TOOLS_SERVICES } from '../../../core/services';
import { Logger } from '../../../utils/logger';
import { loadConfig, PROFILES } from '../../../utils/config';
import { DeploymentOrchestrator } from '../../../core/deployment-orchestrator';
import { StartupReporter } from '../../../core/startup-reporter';
import { CIReporter } from '../../reporters/index';
import { prepareDevEnvironment, waitForHealthy } from '../dev-helpers';
import { resolveGroup, listGroups } from '../@core';
import type { CommandContext, CommandResult } from '../@core';
const logger = new Logger({ context: 'Dev' });
const docker = new DockerOps(logger);
const services = new ServiceManager(logger);
const config = loadConfig();
/**
* Start full development cluster
*
* Accepts an optional group argument: ./run dev [group]
* Default group: platform (_platform manifest)
*/
export async function dev(ctx: CommandContext): Promise<CommandResult> {
// Handle --groups flag: list available groups and exit
if (ctx.args.includes('--groups')) {
const groups = await listGroups(ctx.env);
logger.header('Available Deployment Groups');
for (const g of groups) {
logger.info(` ${g.shortName.padEnd(16)} ${g.description}`);
}
return { code: 0 };
}
const isTTY = Boolean(process.stdout.isTTY);
const isCI = !isTTY || process.env.CI === 'true';
// Resolve group from first positional argument (default: _platform)
const groupArg = ctx.args.find(a => !a.startsWith('-'));
let deploymentName: string;
try {
deploymentName = await resolveGroup(groupArg, ctx.env);
} catch (err) {
logger.error(err instanceof Error ? err.message : String(err));
return { code: 1, error: String(err) };
}
const reporter = isCI ? undefined : new StartupReporter();
const orchestrator = new DeploymentOrchestrator({
deploymentName,
reporter,
});
return orchestrator.start();
}
/**
* Start extended development cluster (with tools)
* Delegates to the 'extended' deployment group.
*/
export async function devAll(ctx: CommandContext): Promise<CommandResult> {
return dev({ ...ctx, args: ['extended', ...ctx.args] });
}
/**
* Start development tools cluster
* Delegates to the 'tools' deployment group.
*/
export async function devTools(ctx: CommandContext): Promise<CommandResult> {
return dev({ ...ctx, args: ['tools', ...ctx.args] });
}
/**
* Start Docker infrastructure only
*/
export async function devInfra(_ctx: CommandContext): Promise<CommandResult> {
if (!(await docker.checkDocker())) {
logger.error('Docker is not running');
return { code: 1, error: 'Docker is not running' };
}
logger.header('Starting Development Infrastructure');
logger.info('Mode: Docker containers only (no host services)');
try {
await prepareDevEnvironment(config.projectRoot);
logger.section('Starting Docker Infrastructure');
await docker.up({
profiles: [PROFILES.core, PROFILES.platform, PROFILES.featureDbs],
envFile: config.envDev,
});
logger.section('Health Checks');
if (!(await waitForHealthy(docker, config.envDev))) {
logger.warn('Some containers may not be fully healthy');
}
try {
await docker.runMigrations();
await docker.runSeeds();
} catch (err) {
logger.warn(`Database setup warning: ${err instanceof Error ? err.message : err}`);
}
logger.blank();
logger.success('Infrastructure ready');
logger.info('Docker containers running - ready for host services');
logger.blank();
return { code: 0 };
} catch (err) {
logger.error(`Infrastructure startup failed: ${err instanceof Error ? err.message : err}`);
return { code: 1, error: String(err) };
}
}
/**
* Start development cluster in CI mode
*/
export async function devCi(ctx: CommandContext): Promise<CommandResult> {
if (!(await docker.checkDocker())) {
logger.error('Docker is not running');
return { code: 1, error: 'Docker is not running' };
}
const reporter = new CIReporter({ json: ctx.json });
reporter.printHeader();
try {
reporter.startPhase(1, 4, 'Preparing environment');
await prepareDevEnvironment(config.projectRoot);
reporter.endPhase();
reporter.startPhase(2, 4, 'Starting Docker infrastructure');
await docker.up({
profiles: [PROFILES.core, PROFILES.platform, PROFILES.featureDbs],
envFile: config.envDev,
});
reporter.endPhase();
reporter.startPhase(3, 4, 'Waiting for containers');
if (!(await waitForHealthy(docker, config.envDev))) {
reporter.logWarning('Some containers may not be fully healthy');
}
reporter.endPhase();
try {
await docker.runMigrations();
await docker.runSeeds();
} catch (err) {
reporter.logWarning(`Database setup: ${err instanceof Error ? err.message : err}`);
}
reporter.startPhase(4, 4, 'Starting host services');
const result = await services.start({
useTerminalUI: false,
healthTimeoutMs: 300000,
serviceList: undefined,
});
if (!result.success) {
reporter.logError({ message: 'Failed to start services' });
return { code: 1, error: 'Failed to start services' };
}
reporter.endPhase();
reporter.printSummary();
return { code: 0 };
} catch (err) {
reporter.logError({ message: err instanceof Error ? err.message : String(err) });
return { code: 1, error: String(err) };
}
}