feat(cli): ✨ Add staging environment management CLI commands (health, logs, restart) and introduce next feature directory
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
75c3587aa1
commit
e5c0f489d6
9 changed files with 75 additions and 75 deletions
|
|
@ -1,16 +1,16 @@
|
|||
/**
|
||||
* Staging health command
|
||||
* Next health command
|
||||
*
|
||||
* Probes next.* staging URLs on black (VPN required).
|
||||
* Probes next.* pre-prod URLs on black (VPN required).
|
||||
*/
|
||||
|
||||
import { Logger } from '../../../utils/logger';
|
||||
import { colors } from '../../../utils/colors';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
|
||||
const STAGING_URLS = [
|
||||
const NEXT_URLS = [
|
||||
{ url: 'https://next.www.atlilith.com', description: 'Landing' },
|
||||
{ url: 'https://next.www.trustedmeet.com', description: 'Marketplace (TrustedMeet)' },
|
||||
{ url: 'https://next.status.atlilith.com', description: 'Status Dashboard' },
|
||||
|
|
@ -19,17 +19,17 @@ const STAGING_URLS = [
|
|||
];
|
||||
|
||||
/**
|
||||
* Run staging health checks against next.* URLs
|
||||
* Run health checks against next.* URLs
|
||||
*/
|
||||
export async function stagingHealth(ctx: CommandContext): Promise<CommandResult> {
|
||||
logger.header('Staging Health Check');
|
||||
logger.info('Probing next.* staging URLs (VPN required)...');
|
||||
export async function nextHealth(ctx: CommandContext): Promise<CommandResult> {
|
||||
logger.header('Next Release Health Check');
|
||||
logger.info('Probing next.* URLs (VPN required)...');
|
||||
logger.blank();
|
||||
|
||||
let failures = 0;
|
||||
const nameWidth = Math.max(...STAGING_URLS.map(u => u.description.length), 20);
|
||||
const nameWidth = Math.max(...NEXT_URLS.map(u => u.description.length), 20);
|
||||
|
||||
for (const { url, description } of STAGING_URLS) {
|
||||
for (const { url, description } of NEXT_URLS) {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 10000);
|
||||
|
|
@ -60,10 +60,10 @@ export async function stagingHealth(ctx: CommandContext): Promise<CommandResult>
|
|||
logger.blank();
|
||||
|
||||
if (failures > 0) {
|
||||
logger.warn(`${failures}/${STAGING_URLS.length} URLs not responding`);
|
||||
logger.warn(`${failures}/${NEXT_URLS.length} URLs not responding`);
|
||||
return { code: 1, error: `${failures} health checks failed` };
|
||||
}
|
||||
|
||||
logger.success(`All ${STAGING_URLS.length} staging URLs healthy`);
|
||||
logger.success(`All ${NEXT_URLS.length} next URLs healthy`);
|
||||
return { code: 0 };
|
||||
}
|
||||
18
run/cli/commands/next/index.ts
Normal file
18
run/cli/commands/next/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Next (pre-prod) commands — manual deploy to black LAN
|
||||
*
|
||||
* Commands:
|
||||
* - next Deploy next release to black (next.* domains)
|
||||
* - next:stop Stop next services
|
||||
* - next:status Show next container status
|
||||
* - next:logs View next logs
|
||||
* - next:restart Rolling restart
|
||||
* - next:health Health check next.* URLs
|
||||
*/
|
||||
|
||||
export { next } from './start';
|
||||
export { nextStop } from './stop';
|
||||
export { nextStatus } from './status';
|
||||
export { nextLogs } from './logs';
|
||||
export { nextRestart } from './restart';
|
||||
export { nextHealth } from './health';
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Staging logs command
|
||||
* Next logs command
|
||||
*/
|
||||
|
||||
import { DockerOps } from '../../../core/docker';
|
||||
|
|
@ -7,14 +7,14 @@ import { Logger } from '../../../utils/logger';
|
|||
import { loadConfig } from '../../../utils/config';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
const docker = new DockerOps(logger);
|
||||
const config = loadConfig();
|
||||
|
||||
/**
|
||||
* View staging logs
|
||||
* View next release logs
|
||||
*/
|
||||
export async function stagingLogs(ctx: CommandContext): Promise<CommandResult> {
|
||||
export async function nextLogs(ctx: CommandContext): Promise<CommandResult> {
|
||||
try {
|
||||
const serviceName = ctx.args[0];
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export async function stagingLogs(ctx: CommandContext): Promise<CommandResult> {
|
|||
|
||||
return { code: 0 };
|
||||
} catch (err) {
|
||||
logger.error(`Failed to get staging logs: ${err instanceof Error ? err.message : String(err)}`);
|
||||
logger.error(`Failed to get next release logs: ${err instanceof Error ? err.message : String(err)}`);
|
||||
return { code: 1, error: String(err) };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
/**
|
||||
* Staging restart command
|
||||
* Next restart command
|
||||
*/
|
||||
|
||||
import { Logger } from '../../../utils/logger';
|
||||
import { loadConfig } from '../../../utils/config';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
const config = loadConfig();
|
||||
|
||||
/**
|
||||
* Rolling restart staging cluster
|
||||
* Rolling restart next release
|
||||
*/
|
||||
export async function stagingRestart(ctx: CommandContext): Promise<CommandResult> {
|
||||
logger.header('Rolling Restart Staging Cluster');
|
||||
export async function nextRestart(ctx: CommandContext): Promise<CommandResult> {
|
||||
logger.header('Rolling Restart Next Release');
|
||||
|
||||
logger.info('Running zero-downtime rolling restart...');
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* Staging start command
|
||||
* Next start command
|
||||
*
|
||||
* Deploys the staging cluster to black (LAN) using the DeploymentOrchestrator.
|
||||
* Accepts an optional group argument: ./run staging [group]
|
||||
* Deploys the next (pre-prod) release to black LAN using the DeploymentOrchestrator.
|
||||
* Accepts an optional group argument: ./run next [group]
|
||||
* Default group: platform (_platform manifest)
|
||||
*/
|
||||
|
||||
|
|
@ -11,12 +11,12 @@ import { Logger } from '../../../utils/logger';
|
|||
import { resolveGroup } from '../@core';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
|
||||
/**
|
||||
* Deploy staging cluster to black
|
||||
* Deploy next release to black
|
||||
*/
|
||||
export async function staging(ctx: CommandContext): Promise<CommandResult> {
|
||||
export async function next(ctx: CommandContext): Promise<CommandResult> {
|
||||
const groupArg = ctx.args.find(a => !a.startsWith('-'));
|
||||
let deploymentName: string;
|
||||
try {
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Staging status command
|
||||
* Next status command
|
||||
*/
|
||||
|
||||
import { DockerOps } from '../../../core/docker';
|
||||
|
|
@ -8,16 +8,16 @@ import { colors } from '../../../utils/colors';
|
|||
import { loadConfig } from '../../../utils/config';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
const docker = new DockerOps(logger);
|
||||
const config = loadConfig();
|
||||
|
||||
/**
|
||||
* Show staging cluster status
|
||||
* Show next release container status
|
||||
*/
|
||||
export async function stagingStatus(ctx: CommandContext): Promise<CommandResult> {
|
||||
export async function nextStatus(ctx: CommandContext): Promise<CommandResult> {
|
||||
try {
|
||||
logger.header('Staging Cluster Status');
|
||||
logger.header('Next Release Status');
|
||||
|
||||
logger.blank();
|
||||
logger.info('Docker Containers:');
|
||||
|
|
@ -47,7 +47,7 @@ export async function stagingStatus(ctx: CommandContext): Promise<CommandResult>
|
|||
|
||||
return { code: 0 };
|
||||
} catch (err) {
|
||||
logger.error(`Failed to get staging status: ${err instanceof Error ? err.message : String(err)}`);
|
||||
logger.error(`Failed to get next release status: ${err instanceof Error ? err.message : String(err)}`);
|
||||
return { code: 1, error: String(err) };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Staging stop command
|
||||
* Next stop command
|
||||
*/
|
||||
|
||||
import { DockerOps } from '../../../core/docker';
|
||||
|
|
@ -7,27 +7,27 @@ import { Logger } from '../../../utils/logger';
|
|||
import { loadConfig } from '../../../utils/config';
|
||||
import type { CommandContext, CommandResult } from '../@core';
|
||||
|
||||
const logger = new Logger({ context: 'Staging' });
|
||||
const logger = new Logger({ context: 'Next' });
|
||||
const docker = new DockerOps(logger);
|
||||
const config = loadConfig();
|
||||
|
||||
/**
|
||||
* Stop staging cluster
|
||||
* Stop next release services
|
||||
*/
|
||||
export async function stagingStop(ctx: CommandContext): Promise<CommandResult> {
|
||||
export async function nextStop(ctx: CommandContext): Promise<CommandResult> {
|
||||
try {
|
||||
logger.header('Stopping Staging Cluster');
|
||||
logger.header('Stopping Next Release');
|
||||
|
||||
logger.info('Stopping Docker containers...');
|
||||
await docker.down({ envFile: config.envStaging });
|
||||
|
||||
logger.blank();
|
||||
logger.success('Staging cluster stopped');
|
||||
logger.success('Next release stopped');
|
||||
logger.blank();
|
||||
|
||||
return { code: 0 };
|
||||
} catch (err) {
|
||||
logger.error(`Failed to stop staging cluster: ${err instanceof Error ? err.message : String(err)}`);
|
||||
logger.error(`Failed to stop next release: ${err instanceof Error ? err.message : String(err)}`);
|
||||
return { code: 1, error: String(err) };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* Staging commands (deploy to black LAN)
|
||||
*
|
||||
* Commands:
|
||||
* - staging Deploy staging cluster to black
|
||||
* - staging:stop Stop staging services
|
||||
* - staging:status Show staging status
|
||||
* - staging:logs View staging logs
|
||||
* - staging:restart Rolling restart
|
||||
* - staging:health Health check staging URLs
|
||||
*/
|
||||
|
||||
export { staging } from './start';
|
||||
export { stagingStop } from './stop';
|
||||
export { stagingStatus } from './status';
|
||||
export { stagingLogs } from './logs';
|
||||
export { stagingRestart } from './restart';
|
||||
export { stagingHealth } from './health';
|
||||
|
|
@ -64,14 +64,14 @@ const lazyCommands: Record<string, [string, string]> = {
|
|||
'prod:restart': ['./commands/prod/index', 'prodRestart'],
|
||||
'prod:health': ['./commands/prod/index', 'prodHealth'],
|
||||
|
||||
// Staging
|
||||
'staging': ['./commands/staging/index', 'staging'],
|
||||
'staging:platform': ['./commands/staging/index', 'staging'],
|
||||
'staging:stop': ['./commands/staging/index', 'stagingStop'],
|
||||
'staging:status': ['./commands/staging/index', 'stagingStatus'],
|
||||
'staging:logs': ['./commands/staging/index', 'stagingLogs'],
|
||||
'staging:restart': ['./commands/staging/index', 'stagingRestart'],
|
||||
'staging:health': ['./commands/staging/index', 'stagingHealth'],
|
||||
// Next (pre-prod manual deploy to black)
|
||||
'next': ['./commands/next/index', 'next'],
|
||||
'next:platform': ['./commands/next/index', 'next'],
|
||||
'next:stop': ['./commands/next/index', 'nextStop'],
|
||||
'next:status': ['./commands/next/index', 'nextStatus'],
|
||||
'next:logs': ['./commands/next/index', 'nextLogs'],
|
||||
'next:restart': ['./commands/next/index', 'nextRestart'],
|
||||
'next:health': ['./commands/next/index', 'nextHealth'],
|
||||
|
||||
// Domain-specific startup (up:*)
|
||||
'up:status': ['./commands/up/index', 'upStatus'],
|
||||
|
|
@ -267,14 +267,14 @@ ${colors.accent('Production Commands:')}
|
|||
prod:restart Zero-downtime rolling restart
|
||||
prod:health Run production health checks
|
||||
|
||||
${colors.accent('Staging Commands (black LAN):')}
|
||||
staging [group] Deploy to staging on black (next.* domains, VPN required)
|
||||
${colors.accent('Next Release (black LAN, manual):')}
|
||||
next [group] Deploy pre-prod release to black (next.* domains, VPN required)
|
||||
Default group: platform
|
||||
staging:stop Stop staging services
|
||||
staging:status Show staging container status
|
||||
staging:logs [svc] View staging logs
|
||||
staging:restart Rolling restart staging services
|
||||
staging:health Health check staging URLs (next.*.atlilith.com, next.*.trustedmeet.com)
|
||||
next:stop Stop next release services
|
||||
next:status Show next release container status
|
||||
next:logs [svc] View next release logs
|
||||
next:restart Rolling restart next release
|
||||
next:health Health check next.* URLs (next.*.atlilith.com, next.*.trustedmeet.com)
|
||||
|
||||
${colors.accent('Workspace Commands:')}
|
||||
install, i Install all workspace dependencies (bun install at root)
|
||||
|
|
@ -420,7 +420,7 @@ export async function main(args: string[]): Promise<void> {
|
|||
// Create context
|
||||
const ctx: CommandContext = {
|
||||
args: commandArgs.filter(arg => !['--verbose', '-v', '--json'].includes(arg)),
|
||||
env: command.startsWith('prod') ? 'prod' : command.startsWith('staging') ? 'staging' : 'dev',
|
||||
env: command.startsWith('prod') ? 'prod' : command.startsWith('next') ? 'staging' : 'dev',
|
||||
verbose: commandArgs.includes('--verbose') || commandArgs.includes('-v'),
|
||||
json: commandArgs.includes('--json'),
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue