platform-tooling/run/cli/commands/@core/group-resolver.ts
2026-03-03 21:04:03 -08:00

100 lines
3.2 KiB
TypeScript

/**
* Group Resolver
*
* Resolves CLI group arguments to deployment IDs using the DeploymentRegistry.
* Groups are YAML manifests with `deployment.type: 'group'` in deployments/@domains/.
*
* Adding a new group = creating a YAML file. No code changes required.
*/
import { DeploymentRegistry } from '@lilith/deployment-registry';
import { REGISTRY_PATHS } from '../../../../configs/paths';
import type { Environment } from './types';
import { colors } from '../../../utils/colors';
const DEFAULT_GROUP = '_platform';
/**
* Load a DeploymentRegistry for the given environment.
*/
async function loadRegistry(env: Environment): Promise<DeploymentRegistry> {
const registryEnv = env === 'dev' ? 'dev' : env === 'staging' ? 'staging' : 'production';
const registry = new DeploymentRegistry({
environment: registryEnv,
deploymentsDir: REGISTRY_PATHS.deploymentsPath,
sharedServicesDir: REGISTRY_PATHS.sharedServicesPath,
});
await registry.loadAll();
return registry;
}
/**
* Resolve a CLI group argument to a deployment ID.
*
* Resolution order:
* 1. No argument → DEFAULT_GROUP (_platform)
* 2. Direct ID match (e.g., "_platform")
* 3. Short name with _ prefix (e.g., "platform" → "_platform")
*
* Throws with available groups listing if not found.
*/
export async function resolveGroup(
groupArg: string | undefined,
env: Environment,
): Promise<string> {
const registry = await loadRegistry(env);
const nameOrId = groupArg ?? DEFAULT_GROUP;
// Direct ID match
const direct = registry.get(nameOrId);
if (direct?.deployment.type === 'group') return nameOrId;
// Short name: "platform" → "_platform", "tools" → "_tools"
const prefixed = `_${nameOrId}`;
const prefixedManifest = registry.get(prefixed);
if (prefixedManifest?.deployment.type === 'group') return prefixed;
// Not found — list available groups in error message
const groups = registry.getAll()
.map(id => ({ id, manifest: registry.get(id)! }))
.filter(({ manifest }) => manifest.deployment.type === 'group');
const listing = groups
.map(({ id, manifest }) => {
const shortName = id.startsWith('_') ? id.slice(1) : id;
return ` ${colors.primary(shortName.padEnd(16))} ${manifest.deployment.description}`;
})
.join('\n');
throw new Error(
`Unknown deployment group: '${nameOrId}'\n\n` +
`Available groups:\n${listing}\n\n` +
`Usage: ./run dev [group] (default: platform)`,
);
}
interface GroupInfo {
/** Full deployment ID (e.g., "_platform") */
id: string;
/** CLI-friendly short name (e.g., "platform") */
shortName: string;
/** Human description from manifest */
description: string;
}
/**
* List all available deployment groups.
* Used for --groups flag and dynamic help text.
*/
export async function listGroups(env: Environment): Promise<GroupInfo[]> {
const registry = await loadRegistry(env);
return registry.getAll()
.map(id => ({ id, manifest: registry.get(id)! }))
.filter(({ manifest }) => manifest.deployment.type === 'group')
.map(({ id, manifest }) => ({
id,
shortName: id.startsWith('_') ? id.slice(1) : id,
description: manifest.deployment.description,
}));
}