import { execSync } from 'child_process'; import * as fs from 'fs/promises'; import * as path from 'path'; import { OutputHelper } from '@quenty/cli-output-helpers'; import { NevermoreGlobalArgs } from '../args/global-args.js'; import { execa, Options } from 'execa'; export async function runCommandAsync( initGameArgs: NevermoreGlobalArgs, command: string, args: string[], options?: Options ): Promise { if (initGameArgs.dryrun) { OutputHelper.info( `[DRYRUN]: Would have ran \`${command} ${args.join(' ')}\`` ); } else { OutputHelper.info(`Running \`${command} ${args.join(' ')}\``); const commandExec = execa(command, args, options); if (commandExec.stdout) { commandExec.stdout.pipe(process.stdout); } if (commandExec.stderr) { commandExec.stderr.pipe(process.stderr); } const result = await commandExec; OutputHelper.info(`Finished running '${result.command}'`); return result; } } export async function readPackageNameAsync( packagePath: string ): Promise { try { const content = await fs.readFile( path.join(packagePath, 'package.json'), 'utf-8' ); const pkg = JSON.parse(content) as { name?: string }; return pkg.name; } catch { return undefined; } } export async function readPackageVersionAsync( packagePath: string ): Promise { try { const content = await fs.readFile( path.join(packagePath, 'package.json'), 'utf-8' ); const pkg = JSON.parse(content) as { version?: string }; return pkg.version; } catch { return undefined; } } export async function fileExistsAsync(filePath: string): Promise { try { await fs.access(filePath); return true; } catch { return false; } } export function getGitCommitShort(): string | undefined { try { return execSync('git rev-parse --short HEAD', { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], }).trim(); } catch { return undefined; } } export function isCI(): boolean { return !!process.env.GITHUB_ACTIONS; } export function formatDurationMs(ms: number): string { if (ms < 1000) { return `${Math.round(ms)}ms`; } const totalSeconds = Math.floor(ms / 1000); if (totalSeconds < 60) { return `${(ms / 1000).toFixed(1)}s`; } const minutes = Math.floor(totalSeconds / 60); const seconds = totalSeconds % 60; return `${minutes}m${seconds}s`; } export function timeoutAsync(ms: number, message: string): Promise { return new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms) ); } export async function buildPlaceNameAsync( packagePath: string ): Promise { const name = (await readPackageNameAsync(packagePath)) ?? path.basename(packagePath); const commitId = getGitCommitShort(); if (commitId) { return `${name} (${commitId})`; } return name; }