forge-deploy-cli
Version:
Professional CLI for local deployments with automatic subdomain routing, SSL certificates, and infrastructure management
1,024 lines • 40.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkspaceManager = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const child_process_1 = require("child_process");
class WorkspaceManager {
constructor(projectPath) {
this.packageManager = 'npm';
this.projectPath = projectPath;
this.detectPackageManager();
}
/**
* Get predefined workspace templates for common project types
*/
static getWorkspaceTemplates() {
return {
'nextjs-fullstack': {
preDeploySteps: [
{
name: 'Environment Setup',
command: 'npm run env:setup',
optional: true,
timeout: 60
},
{
name: 'Database Migration',
command: 'npm run db:migrate',
optional: false,
timeout: 120
},
{
name: 'Prisma Generate',
command: 'npx prisma generate',
optional: false,
timeout: 60
}
],
buildSteps: [
{
name: 'Next.js Build',
command: 'npm run build',
optional: false,
timeout: 600,
environment: {
'NEXT_TELEMETRY_DISABLED': '1',
'NODE_ENV': 'production'
}
}
],
postDeploySteps: [
{
name: 'Cache Warming',
command: 'npm run warm-cache',
optional: true,
timeout: 60
}
]
},
'react-monorepo': {
preDeploySteps: [
{
name: 'Monorepo Bootstrap',
command: 'npm run bootstrap',
optional: false,
timeout: 300
},
{
name: 'Linting',
command: 'npm run lint',
optional: true,
timeout: 120
},
{
name: 'Type Checking',
command: 'npm run type-check',
optional: true,
timeout: 120
}
],
buildSteps: [
{
name: 'Build All Packages',
command: 'npm run build:all',
optional: false,
timeout: 600
}
]
},
'vite-spa': {
preDeploySteps: [
{
name: 'Code Generation',
command: 'npm run codegen',
optional: true,
timeout: 120
}
],
buildSteps: [
{
name: 'Vite Build',
command: 'npm run build',
optional: false,
timeout: 300,
environment: {
'NODE_ENV': 'production'
}
}
]
},
'nuxt-ssr': {
preDeploySteps: [
{
name: 'Nuxt Prepare',
command: 'npm run prepare',
optional: false,
timeout: 60
},
{
name: 'Database Setup',
command: 'npm run db:setup',
optional: true,
timeout: 180
}
],
buildSteps: [
{
name: 'Nuxt Build',
command: 'npm run build',
optional: false,
timeout: 600
}
]
},
'node-api': {
preDeploySteps: [
{
name: 'Database Migration',
command: 'npm run db:migrate',
optional: false,
timeout: 120
},
{
name: 'Seed Database',
command: 'npm run db:seed',
optional: true,
timeout: 60
}
],
buildSteps: [
{
name: 'TypeScript Build',
command: 'npm run build',
optional: false,
timeout: 180
}
],
postDeploySteps: [
{
name: 'Health Check',
command: 'npm run health-check',
optional: true,
timeout: 30
}
]
}
};
}
detectPackageManager() {
const lockFiles = {
'pnpm-lock.yaml': 'pnpm',
'yarn.lock': 'yarn',
'bun.lockb': 'bun',
'package-lock.json': 'npm'
};
for (const [file, manager] of Object.entries(lockFiles)) {
if (fs_extra_1.default.existsSync(path_1.default.join(this.projectPath, file))) {
this.packageManager = manager;
console.log(chalk_1.default.gray(`Detected package manager: ${manager}`));
return;
}
}
console.log(chalk_1.default.gray('No lock file found, defaulting to npm'));
}
async analyzeWorkspace() {
const packageJsonPath = path_1.default.join(this.projectPath, 'package.json');
if (!await fs_extra_1.default.pathExists(packageJsonPath)) {
throw new Error('No package.json found in project directory');
}
const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
const workspace = {
packageManager: this.packageManager,
installCommand: this.getInstallCommand(),
preDeploySteps: [],
buildSteps: [],
postDeploySteps: []
};
workspace.monorepo = await this.detectMonorepo(packageJson);
workspace.preDeploySteps = await this.suggestPreDeploySteps(packageJson);
workspace.buildSteps = await this.suggestBuildSteps(packageJson);
workspace.postDeploySteps = await this.suggestPostDeploySteps(packageJson);
await this.optimizeForFramework(workspace, packageJson);
return workspace;
}
async detectMonorepo(packageJson) {
const rootPath = this.projectPath;
if (await fs_extra_1.default.pathExists(path_1.default.join(rootPath, 'nx.json'))) {
return {
type: 'nx',
rootPackageJson: 'package.json',
projectPath: await this.detectNxProjectPath()
};
}
if (await fs_extra_1.default.pathExists(path_1.default.join(rootPath, 'turbo.json'))) {
return {
type: 'turbo',
rootPackageJson: 'package.json',
projectPath: await this.detectTurboProjectPath()
};
}
if (await fs_extra_1.default.pathExists(path_1.default.join(rootPath, 'lerna.json'))) {
return {
type: 'lerna',
rootPackageJson: 'package.json'
};
}
if (await fs_extra_1.default.pathExists(path_1.default.join(rootPath, 'rush.json'))) {
return {
type: 'rush',
rootPackageJson: 'package.json'
};
}
if (packageJson.workspaces) {
return {
type: 'custom',
rootPackageJson: 'package.json'
};
}
return undefined;
}
/**
* Suggest pre-deploy steps based on package.json scripts
*/
async suggestPreDeploySteps(packageJson) {
const steps = [];
const scripts = packageJson.scripts || {};
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
// Database setup steps (priority order)
if (scripts['db:setup']) {
steps.push({
name: 'Database Setup',
command: `${this.packageManager} run db:setup`,
optional: false,
timeout: 300
});
}
else {
// Separate migration and seeding if no combined setup
if (scripts['db:migrate'] || scripts['db:push']) {
steps.push({
name: 'Database Migration',
command: `${this.packageManager} run ${scripts['db:migrate'] ? 'db:migrate' : 'db:push'}`,
optional: false,
timeout: 120
});
}
if (scripts['db:seed']) {
steps.push({
name: 'Database Seeding',
command: `${this.packageManager} run db:seed`,
optional: true,
timeout: 60
});
}
}
// Prisma specific steps
if (dependencies.prisma || dependencies['@prisma/client']) {
steps.push({
name: 'Prisma Generate',
command: `${this.packageManager === 'npm' ? 'npx' : this.packageManager} prisma generate`,
optional: false,
timeout: 60
});
if (!scripts['db:migrate'] && !scripts['db:setup'] && (scripts['db:push'] || scripts['db:deploy'])) {
steps.push({
name: 'Prisma DB Push',
command: `${this.packageManager === 'npm' ? 'npx' : this.packageManager} prisma db ${scripts['db:push'] ? 'push' : 'deploy'}`,
optional: true,
timeout: 120
});
}
}
if (scripts['env:setup'] || scripts['setup:env']) {
steps.push({
name: 'Environment Setup',
command: `${this.packageManager} run ${scripts['env:setup'] ? 'env:setup' : 'setup:env'}`,
optional: true,
timeout: 60
});
}
const codegenScripts = ['codegen', 'generate', 'gql:generate', 'openapi:generate', 'generate:types'];
for (const script of codegenScripts) {
if (scripts[script]) {
steps.push({
name: 'Code Generation',
command: `${this.packageManager} run ${script}`,
optional: false,
timeout: 120
});
break;
}
}
// Husky and git hooks setup
if (dependencies.husky && scripts.prepare) {
steps.push({
name: 'Git Hooks Setup',
command: `${this.packageManager} run prepare`,
optional: true,
timeout: 30
});
}
if (dependencies['pre-commit'] || scripts['setup:pre-commit']) {
steps.push({
name: 'Pre-commit Setup',
command: scripts['setup:pre-commit'] ? `${this.packageManager} run setup:pre-commit` : 'pre-commit install',
optional: true,
timeout: 30
});
}
const typeCheckScripts = ['type-check', 'typecheck', 'tsc', 'types:check'];
for (const script of typeCheckScripts) {
if (scripts[script]) {
steps.push({
name: 'Type Checking',
command: `${this.packageManager} run ${script}`,
optional: true,
timeout: 120
});
break;
}
}
if (scripts.lint || scripts['lint:check']) {
steps.push({
name: 'Linting',
command: `${this.packageManager} run ${scripts.lint ? 'lint' : 'lint:check'}`,
optional: true,
timeout: 120
});
}
if (scripts.test && scripts['test:unit']) {
steps.push({
name: 'Unit Tests',
command: `${this.packageManager} run test:unit`,
optional: true,
timeout: 300
});
}
else if (scripts.test) {
steps.push({
name: 'Tests',
command: `${this.packageManager} run test`,
optional: true,
timeout: 300
});
}
return steps;
}
/**
* Suggest build steps based on framework and package.json
*/
async suggestBuildSteps(packageJson) {
const steps = [];
const scripts = packageJson.scripts || {};
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
// Framework-specific build detection
if (dependencies['next'] || scripts['next:build']) {
steps.push({
name: 'Next.js Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 600
});
}
else if (dependencies['vite'] || scripts['vite:build']) {
steps.push({
name: 'Vite Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 300
});
}
else if (dependencies['@angular/cli'] || scripts['ng:build']) {
steps.push({
name: 'Angular Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 600
});
}
else if (dependencies['vue'] && dependencies['@vue/cli-service']) {
steps.push({
name: 'Vue CLI Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 300
});
}
else if (dependencies['react-scripts']) {
steps.push({
name: 'Create React App Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 300
});
}
else if (dependencies['nuxt'] || dependencies['nuxt3']) {
steps.push({
name: 'Nuxt Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 600
});
}
else if (dependencies['svelte'] && dependencies['@sveltejs/kit']) {
steps.push({
name: 'SvelteKit Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 300
});
}
else if (scripts.build) {
// Generic build command
steps.push({
name: 'Project Build',
command: `${this.packageManager} run build`,
optional: false,
timeout: 300
});
}
// TypeScript compilation (if no framework build)
if (steps.length === 0 && dependencies.typescript && scripts.tsc) {
steps.push({
name: 'TypeScript Compilation',
command: `${this.packageManager} run tsc`,
optional: false,
timeout: 120
});
}
// Webpack build (standalone)
if (steps.length === 0 && dependencies.webpack && scripts.webpack) {
steps.push({
name: 'Webpack Build',
command: `${this.packageManager} run webpack`,
optional: false,
timeout: 300
});
}
// Rollup build
if (steps.length === 0 && dependencies.rollup && scripts.rollup) {
steps.push({
name: 'Rollup Build',
command: `${this.packageManager} run rollup`,
optional: false,
timeout: 180
});
}
return steps;
}
/**
* Suggest post-deploy steps
*/
async suggestPostDeploySteps(packageJson) {
const steps = [];
const scripts = packageJson.scripts || {};
// Post-deploy hooks
if (scripts['post-deploy'] || scripts.postdeploy) {
const postDeployScript = scripts['post-deploy'] || scripts.postdeploy;
steps.push({
name: 'Post Deploy Hook',
command: `${this.packageManager} run ${Object.keys(scripts).find(key => scripts[key] === postDeployScript)}`,
optional: false,
timeout: 180
});
}
// Cache warming
if (scripts['warm-cache'] || scripts.warm) {
const warmScript = scripts['warm-cache'] || scripts.warm;
steps.push({
name: 'Cache Warming',
command: `${this.packageManager} run ${Object.keys(scripts).find(key => scripts[key] === warmScript)}`,
optional: true,
timeout: 60
});
}
return steps;
}
/**
* Execute a build step with proper error handling and logging
*/
async executeBuildStep(step) {
console.log(chalk_1.default.cyan(`Running: ${step.name}`));
console.log(chalk_1.default.gray(`Command: ${step.command}`));
const workingDir = step.workingDirectory
? path_1.default.resolve(this.projectPath, step.workingDirectory)
: this.projectPath;
let attempt = 0;
const maxAttempts = (step.retries || 0) + 1;
while (attempt < maxAttempts) {
try {
const env = {
...process.env,
...step.environment
};
(0, child_process_1.execSync)(step.command, {
cwd: workingDir,
stdio: 'inherit',
env,
timeout: (step.timeout || 300) * 1000
});
console.log(chalk_1.default.green(`✓ ${step.name} completed successfully`));
return true;
}
catch (error) {
attempt++;
if (attempt < maxAttempts) {
console.log(chalk_1.default.yellow(`Retry ${attempt}/${step.retries} for ${step.name}...`));
continue;
}
if (step.optional) {
console.log(chalk_1.default.yellow(`! ${step.name} failed but is optional, continuing...`));
return true;
}
else {
console.log(chalk_1.default.red(`✗ ${step.name} failed`));
throw error;
}
}
}
return false;
}
/**
* Execute all build steps in sequence
*/
async executeWorkflow(steps) {
if (steps.length === 0) {
console.log(chalk_1.default.gray('No build steps to execute'));
return;
}
console.log(chalk_1.default.blue(`Executing ${steps.length} build step(s)...`));
console.log();
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
console.log(chalk_1.default.blue(`[${i + 1}/${steps.length}] ${step.name}`));
try {
await this.executeBuildStep(step);
}
catch (error) {
console.log(chalk_1.default.red(`Build failed at step: ${step.name}`));
throw error;
}
console.log();
}
console.log(chalk_1.default.green('All build steps completed successfully!'));
}
/**
* Get the appropriate install command for the detected package manager
*/
getInstallCommand() {
switch (this.packageManager) {
case 'pnpm':
return 'pnpm install';
case 'yarn':
return 'yarn install';
case 'bun':
return 'bun install';
default:
return 'npm install';
}
}
/**
* Detect Nx project path if in a Nx workspace
*/
async detectNxProjectPath() {
try {
const nxConfig = await fs_extra_1.default.readJSON(path_1.default.join(this.projectPath, 'nx.json'));
// For now, return undefined - would need more sophisticated detection
return undefined;
}
catch {
return undefined;
}
}
/**
* Detect Turbo project path if in a Turbo workspace
*/
async detectTurboProjectPath() {
try {
const turboConfig = await fs_extra_1.default.readJSON(path_1.default.join(this.projectPath, 'turbo.json'));
// For now, return undefined - would need more sophisticated detection
return undefined;
}
catch {
return undefined;
}
}
/**
* Interactive setup for complex workspaces
*/
async interactiveSetup() {
const inquirer = await Promise.resolve().then(() => __importStar(require('inquirer')));
let suggestedSetup = await this.analyzeWorkspace();
console.log(chalk_1.default.blue('Forge Workspace Setup'));
console.log(chalk_1.default.gray('Configuring deployment workflow for your project...'));
console.log();
// Show detected package manager
console.log(chalk_1.default.green(`✓ Package Manager: ${this.packageManager}`));
// Show detected monorepo (if any)
if (suggestedSetup.monorepo) {
console.log(chalk_1.default.green(`✓ Monorepo Type: ${suggestedSetup.monorepo.type}`));
}
// Offer template options
const templates = WorkspaceManager.getWorkspaceTemplates();
const templateNames = Object.keys(templates);
if (templateNames.length > 0) {
const { useTemplate } = await inquirer.default.prompt([{
type: 'list',
name: 'useTemplate',
message: 'Choose a workflow template or customize:',
choices: [
{ name: 'Auto-detect (recommended)', value: 'auto' },
...templateNames.map(name => ({ name: `${name} template`, value: name })),
{ name: 'Custom configuration', value: 'custom' }
]
}]);
if (useTemplate !== 'auto' && useTemplate !== 'custom') {
const template = templates[useTemplate];
if (template) {
suggestedSetup = {
...suggestedSetup,
...template,
preDeploySteps: template.preDeploySteps || [],
buildSteps: template.buildSteps || [],
postDeploySteps: template.postDeploySteps || []
};
console.log(chalk_1.default.green(`Using ${useTemplate} template`));
}
}
else if (useTemplate === 'custom') {
// Clear auto-detected steps for full customization
suggestedSetup.preDeploySteps = [];
suggestedSetup.buildSteps = [];
suggestedSetup.postDeploySteps = [];
}
}
// Continue with step-by-step configuration...
// Allow user to customize pre-deploy steps
if (suggestedSetup.preDeploySteps && suggestedSetup.preDeploySteps.length > 0) {
console.log();
console.log(chalk_1.default.blue('Detected Pre-Deploy Steps:'));
for (const step of suggestedSetup.preDeploySteps) {
const optional = step.optional ? chalk_1.default.gray('(optional)') : chalk_1.default.red('(required)');
console.log(` • ${step.name} ${optional}`);
console.log(` ${chalk_1.default.gray(step.command)}`);
}
const { confirmPreSteps } = await inquirer.default.prompt([{
type: 'confirm',
name: 'confirmPreSteps',
message: 'Use these pre-deploy steps?',
default: true
}]);
if (!confirmPreSteps) {
suggestedSetup.preDeploySteps = await this.promptCustomSteps('pre-deploy');
}
}
else {
// Offer to add custom pre-deploy steps
const { addPreSteps } = await inquirer.default.prompt([{
type: 'confirm',
name: 'addPreSteps',
message: 'Add custom pre-deploy steps (database setup, code generation, etc.)?',
default: false
}]);
if (addPreSteps) {
suggestedSetup.preDeploySteps = await this.promptCustomSteps('pre-deploy');
}
}
// Allow user to customize build steps
if (suggestedSetup.buildSteps && suggestedSetup.buildSteps.length > 0) {
console.log();
console.log(chalk_1.default.blue('Detected Build Steps:'));
for (const step of suggestedSetup.buildSteps) {
const optional = step.optional ? chalk_1.default.gray('(optional)') : chalk_1.default.red('(required)');
console.log(` • ${step.name} ${optional}`);
console.log(` ${chalk_1.default.gray(step.command)}`);
}
const { confirmBuildSteps } = await inquirer.default.prompt([{
type: 'confirm',
name: 'confirmBuildSteps',
message: 'Use these build steps?',
default: true
}]);
if (!confirmBuildSteps) {
suggestedSetup.buildSteps = await this.promptCustomSteps('build');
}
}
else {
// Offer to add custom build steps
const { addBuildSteps } = await inquirer.default.prompt([{
type: 'confirm',
name: 'addBuildSteps',
message: 'Add custom build steps?',
default: false
}]);
if (addBuildSteps) {
suggestedSetup.buildSteps = await this.promptCustomSteps('build');
}
}
// Post-deploy steps
if (suggestedSetup.postDeploySteps && suggestedSetup.postDeploySteps.length > 0) {
console.log();
console.log(chalk_1.default.blue('Detected Post-Deploy Steps:'));
for (const step of suggestedSetup.postDeploySteps) {
const optional = step.optional ? chalk_1.default.gray('(optional)') : chalk_1.default.red('(required)');
console.log(` • ${step.name} ${optional}`);
console.log(` ${chalk_1.default.gray(step.command)}`);
}
const { confirmPostSteps } = await inquirer.default.prompt([{
type: 'confirm',
name: 'confirmPostSteps',
message: 'Use these post-deploy steps?',
default: true
}]);
if (!confirmPostSteps) {
suggestedSetup.postDeploySteps = await this.promptCustomSteps('post-deploy');
}
}
// Advanced configuration options
const { advancedConfig } = await inquirer.default.prompt([{
type: 'confirm',
name: 'advancedConfig',
message: 'Configure advanced options (environment variables, timeouts, etc.)?',
default: false
}]);
if (advancedConfig) {
await this.configureAdvancedOptions(suggestedSetup);
}
return suggestedSetup;
}
/**
* Prompt for custom build steps
*/
async promptCustomSteps(stepType = 'build') {
const inquirer = await Promise.resolve().then(() => __importStar(require('inquirer')));
const steps = [];
console.log(chalk_1.default.blue(`Adding custom ${stepType} steps...`));
let addMore = true;
while (addMore) {
const stepData = await inquirer.default.prompt([
{
type: 'input',
name: 'name',
message: 'Step name:',
validate: (input) => input.trim().length > 0 || 'Name is required'
},
{
type: 'input',
name: 'command',
message: 'Command to run:',
validate: (input) => input.trim().length > 0 || 'Command is required'
},
{
type: 'confirm',
name: 'optional',
message: 'Is this step optional?',
default: stepType === 'post-deploy' // Post-deploy steps are often optional
},
{
type: 'number',
name: 'timeout',
message: 'Timeout in seconds:',
default: stepType === 'build' ? 300 : 120
}
]);
steps.push({
name: stepData.name,
command: stepData.command,
optional: stepData.optional,
timeout: stepData.timeout
});
const { continueAdding } = await inquirer.default.prompt([{
type: 'confirm',
name: 'continueAdding',
message: 'Add another step?',
default: false
}]);
addMore = continueAdding;
}
return steps;
}
/**
* Configure advanced options for workspace setup
*/
async configureAdvancedOptions(workspace) {
const inquirer = await Promise.resolve().then(() => __importStar(require('inquirer')));
console.log(chalk_1.default.blue('Advanced Configuration'));
// Configure global environment variables
const { addEnvVars } = await inquirer.default.prompt([{
type: 'confirm',
name: 'addEnvVars',
message: 'Add global environment variables for all steps?',
default: false
}]);
if (addEnvVars) {
const globalEnv = {};
let addMoreEnv = true;
while (addMoreEnv) {
const envData = await inquirer.default.prompt([
{
type: 'input',
name: 'key',
message: 'Environment variable name:',
validate: (input) => input.trim().length > 0 || 'Variable name is required'
},
{
type: 'input',
name: 'value',
message: 'Environment variable value:',
default: ''
}
]);
globalEnv[envData.key] = envData.value;
const { continueAddingEnv } = await inquirer.default.prompt([{
type: 'confirm',
name: 'continueAddingEnv',
message: 'Add another environment variable?',
default: false
}]);
addMoreEnv = continueAddingEnv;
}
// Apply environment variables to all steps
const allSteps = [
...(workspace.preDeploySteps || []),
...(workspace.buildSteps || []),
...(workspace.postDeploySteps || [])
];
for (const step of allSteps) {
step.environment = { ...globalEnv, ...step.environment };
}
}
// Configure parallel execution (future feature)
const { enableParallel } = await inquirer.default.prompt([{
type: 'confirm',
name: 'enableParallel',
message: 'Enable parallel execution for compatible steps? (experimental)',
default: false
}]);
if (enableParallel) {
console.log(chalk_1.default.yellow('Note: Parallel execution is experimental and may be added in future versions.'));
}
// Configure retry behavior
const { configureRetries } = await inquirer.default.prompt([{
type: 'confirm',
name: 'configureRetries',
message: 'Configure retry behavior for failed steps?',
default: false
}]);
if (configureRetries) {
const { defaultRetries } = await inquirer.default.prompt([{
type: 'number',
name: 'defaultRetries',
message: 'Default number of retries for failed steps:',
default: 1,
validate: (input) => input >= 0 || 'Retries must be 0 or greater'
}]);
// Apply retry settings to non-optional steps
const allSteps = [
...(workspace.preDeploySteps || []),
...(workspace.buildSteps || []),
...(workspace.postDeploySteps || [])
];
for (const step of allSteps) {
if (!step.optional && step.retries === undefined) {
step.retries = defaultRetries;
}
}
}
}
/**
* Optimize workspace configuration for specific frameworks
*/
async optimizeForFramework(workspace, packageJson) {
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
const scripts = packageJson.scripts || {};
// Next.js optimizations
if (dependencies['next']) {
// Add build caching for Next.js
if (workspace.buildSteps && workspace.buildSteps.length > 0) {
workspace.buildSteps[0].environment = {
...workspace.buildSteps[0].environment,
'NEXT_TELEMETRY_DISABLED': '1'
};
}
// Add post-install for Next.js if using custom server
if (scripts['dev:server'] || scripts['start:server']) {
workspace.postInstallSteps = workspace.postInstallSteps || [];
workspace.postInstallSteps.push({
name: 'Next.js Setup',
command: 'npx next telemetry disable',
optional: true,
timeout: 30
});
}
}
// Vite optimizations
if (dependencies['vite']) {
if (workspace.buildSteps && workspace.buildSteps.length > 0) {
workspace.buildSteps[0].environment = {
...workspace.buildSteps[0].environment,
'VITE_NODE_ENV': 'production'
};
}
}
// React Native optimizations (if detected)
if (dependencies['react-native']) {
console.log(chalk_1.default.yellow('Warning: React Native project detected. This CLI is optimized for web applications.'));
}
// Add monorepo-specific optimizations
if (workspace.monorepo) {
await this.optimizeForMonorepo(workspace, packageJson);
}
// Docker optimizations (if Dockerfile exists)
if (await fs_extra_1.default.pathExists(path_1.default.join(this.projectPath, 'Dockerfile'))) {
workspace.preDeploySteps = workspace.preDeploySteps || [];
workspace.preDeploySteps.push({
name: 'Docker Build',
command: 'docker build -t forge-deployment .',
optional: true,
timeout: 600
});
}
}
/**
* Add monorepo-specific optimizations
*/
async optimizeForMonorepo(workspace, packageJson) {
if (!workspace.monorepo)
return;
const scripts = packageJson.scripts || {};
switch (workspace.monorepo.type) {
case 'nx':
// Add Nx cache optimization
if (scripts['nx:reset']) {
workspace.preDeploySteps?.unshift({
name: 'Nx Cache Reset',
command: `${this.packageManager} run nx:reset`,
optional: true,
timeout: 30
});
}
break;
case 'turbo':
// Add Turbo cache configuration
if (workspace.buildSteps && workspace.buildSteps.length > 0) {
workspace.buildSteps[0].environment = {
...workspace.buildSteps[0].environment,
'TURBO_TOKEN': process.env.TURBO_TOKEN || '',
'TURBO_TEAM': process.env.TURBO_TEAM || ''
};
}
break;
case 'lerna':
// Add Lerna bootstrap if needed
if (scripts.bootstrap) {
workspace.preDeploySteps?.unshift({
name: 'Lerna Bootstrap',
command: `${this.packageManager} run bootstrap`,
optional: false,
timeout: 300
});
}
break;
}
}
}
exports.WorkspaceManager = WorkspaceManager;
//# sourceMappingURL=workspaceManager.js.map