178 lines
5.5 KiB
TypeScript
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) };
|
|
}
|
|
}
|