forge-deploy-cli
Version:
Professional CLI for local deployments with automatic subdomain routing, SSL certificates, and infrastructure management
333 lines (331 loc) • 11.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCommand = initCommand;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const inquirer_1 = __importDefault(require("inquirer"));
const chalk_1 = __importDefault(require("chalk"));
const types_1 = require("../types");
const config_1 = require("../services/config");
const dependencies_1 = require("../installers/dependencies");
async function initCommand(options) {
console.log(chalk_1.default.blue.bold('Initializing Forge project...'));
const configService = new config_1.ConfigService();
const dependencyInstaller = new dependencies_1.DependencyInstaller();
try {
// Check if already initialized
if (await fs_extra_1.default.pathExists('forge.config.json')) {
const { overwrite } = await inquirer_1.default.prompt([{
type: 'confirm',
name: 'overwrite',
message: 'Forge project already initialized. Overwrite?',
default: false
}]);
if (!overwrite) {
console.log(chalk_1.default.yellow('Initialization cancelled.'));
return;
}
}
let projectName;
let framework;
let buildCommand;
let outputDirectory;
if (options.yes) {
// Use defaults
projectName = path_1.default.basename(process.cwd());
framework = await detectFramework();
buildCommand = getDefaultBuildCommand(framework);
outputDirectory = getDefaultOutputDirectory(framework);
}
else {
// Interactive setup
const answers = await inquirer_1.default.prompt([
{
type: 'input',
name: 'projectName',
message: 'Project name:',
default: path_1.default.basename(process.cwd()),
validate: (input) => input.length > 0 || 'Project name is required'
},
{
type: 'list',
name: 'framework',
message: 'Select framework:',
choices: [
{ name: 'Next.js', value: types_1.Framework.NEXTJS },
{ name: 'React', value: types_1.Framework.REACT },
{ name: 'Vue.js', value: types_1.Framework.VUE },
{ name: 'Nuxt.js', value: types_1.Framework.NUXT },
{ name: 'Svelte', value: types_1.Framework.SVELTE },
{ name: 'Angular', value: types_1.Framework.ANGULAR },
{ name: 'Express.js', value: types_1.Framework.EXPRESS },
{ name: 'Fastify', value: types_1.Framework.FASTIFY },
{ name: 'NestJS', value: types_1.Framework.NEST },
{ name: 'Django', value: types_1.Framework.DJANGO },
{ name: 'Flask', value: types_1.Framework.FLASK },
{ name: 'Laravel', value: types_1.Framework.LARAVEL },
{ name: 'Static Site', value: types_1.Framework.STATIC }
],
default: await detectFramework()
},
{
type: 'input',
name: 'buildCommand',
message: 'Build command:',
default: (answers) => getDefaultBuildCommand(answers.framework)
},
{
type: 'input',
name: 'outputDirectory',
message: 'Output directory:',
default: (answers) => getDefaultOutputDirectory(answers.framework)
}
]);
projectName = answers.projectName;
framework = answers.framework;
buildCommand = answers.buildCommand;
outputDirectory = answers.outputDirectory;
}
// Create forge.config.json
const config = {
projectName,
framework,
buildCommand,
outputDirectory,
environmentVariables: {}
};
await configService.saveProjectConfig(config);
// Install system dependencies
console.log(chalk_1.default.blue('Setting up deployment dependencies...'));
await dependencyInstaller.installSystemDependencies(framework);
// Generate deployment templates
await generateDeploymentTemplates(framework);
console.log(chalk_1.default.green.bold('Project initialized successfully!'));
console.log();
console.log(chalk_1.default.gray('Next steps:'));
console.log(chalk_1.default.gray(' 1. forge login'));
console.log(chalk_1.default.gray(' 2. forge deploy'));
}
catch (error) {
console.error(chalk_1.default.red('Failed to initialize project:'), error);
process.exit(1);
}
}
async function detectFramework() {
try {
const packageJson = await fs_extra_1.default.readJSON('package.json');
const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (dependencies.next)
return types_1.Framework.NEXTJS;
if (dependencies.nuxt)
return types_1.Framework.NUXT;
if (dependencies.react)
return types_1.Framework.REACT;
if (dependencies.vue)
return types_1.Framework.VUE;
if (dependencies.svelte)
return types_1.Framework.SVELTE;
if (dependencies['@angular/core'])
return types_1.Framework.ANGULAR;
if (dependencies.express)
return types_1.Framework.EXPRESS;
if (dependencies.fastify)
return types_1.Framework.FASTIFY;
if (dependencies['@nestjs/core'])
return types_1.Framework.NEST;
// Check for Python frameworks
if (await fs_extra_1.default.pathExists('requirements.txt')) {
const requirements = await fs_extra_1.default.readFile('requirements.txt', 'utf8');
if (requirements.includes('Django'))
return types_1.Framework.DJANGO;
if (requirements.includes('Flask'))
return types_1.Framework.FLASK;
}
// Check for PHP frameworks
if (await fs_extra_1.default.pathExists('composer.json')) {
const composer = await fs_extra_1.default.readJSON('composer.json');
if (composer.require && composer.require['laravel/framework']) {
return types_1.Framework.LARAVEL;
}
}
}
catch (error) {
// Ignore errors and return static as default
}
return types_1.Framework.STATIC;
}
function getDefaultBuildCommand(framework) {
switch (framework) {
case types_1.Framework.NEXTJS:
return 'npm run build';
case types_1.Framework.REACT:
return 'npm run build';
case types_1.Framework.VUE:
return 'npm run build';
case types_1.Framework.NUXT:
return 'npm run build';
case types_1.Framework.SVELTE:
return 'npm run build';
case types_1.Framework.ANGULAR:
return 'ng build --prod';
case types_1.Framework.EXPRESS:
return 'npm run build';
case types_1.Framework.FASTIFY:
return 'npm run build';
case types_1.Framework.NEST:
return 'npm run build';
case types_1.Framework.DJANGO:
return 'python manage.py collectstatic --noinput';
case types_1.Framework.FLASK:
return 'pip install -r requirements.txt';
case types_1.Framework.LARAVEL:
return 'composer install --no-dev && php artisan config:cache';
default:
return 'echo "No build command needed"';
}
}
function getDefaultOutputDirectory(framework) {
switch (framework) {
case types_1.Framework.NEXTJS:
return '.next';
case types_1.Framework.REACT:
return 'build';
case types_1.Framework.VUE:
return 'dist';
case types_1.Framework.NUXT:
return '.nuxt';
case types_1.Framework.SVELTE:
return 'public';
case types_1.Framework.ANGULAR:
return 'dist';
case types_1.Framework.EXPRESS:
case types_1.Framework.FASTIFY:
case types_1.Framework.NEST:
return 'dist';
case types_1.Framework.DJANGO:
return 'staticfiles';
case types_1.Framework.FLASK:
return '.';
case types_1.Framework.LARAVEL:
return 'public';
default:
return '.';
}
}
async function generateDeploymentTemplates(framework) {
console.log(chalk_1.default.gray('Generating deployment templates...'));
// Create .forgeignore file
const forgeIgnoreContent = `
node_modules/
.git/
.env*
*.log
.DS_Store
dist/
build/
.next/
coverage/
.nyc_output/
*.tgz
*.tar.gz
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
.venv/
venv/
ENV/
env/
.cache/
.pytest_cache/
.coverage
vendor/
storage/
.idea/
.vscode/
`.trim();
await fs_extra_1.default.writeFile('.forgeignore', forgeIgnoreContent);
// Generate framework-specific configuration files
switch (framework) {
case types_1.Framework.NEXTJS:
await generateNextJsConfig();
break;
case types_1.Framework.DJANGO:
await generateDjangoConfig();
break;
case types_1.Framework.FLASK:
await generateFlaskConfig();
break;
case types_1.Framework.LARAVEL:
await generateLaravelConfig();
break;
}
}
async function generateNextJsConfig() {
const ecosystem = {
apps: [{
name: 'nextjs-app',
script: 'npm',
args: 'start',
env: {
NODE_ENV: 'production',
PORT: 3000
},
instances: 1,
exec_mode: 'cluster'
}]
};
await fs_extra_1.default.writeJSON('ecosystem.config.js', ecosystem, { spaces: 2 });
}
async function generateDjangoConfig() {
const gunicornConfig = `
bind = "0.0.0.0:8000"
workers = 2
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 30
keepalive = 2
`.trim();
await fs_extra_1.default.writeFile('gunicorn.conf.py', gunicornConfig);
}
async function generateFlaskConfig() {
const gunicornConfig = `
bind = "0.0.0.0:5000"
workers = 2
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
timeout = 30
keepalive = 2
`.trim();
await fs_extra_1.default.writeFile('gunicorn.conf.py', gunicornConfig);
}
async function generateLaravelConfig() {
const nginxConfig = `
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
`.trim();
await fs_extra_1.default.ensureDir('deploy');
await fs_extra_1.default.writeFile('deploy/nginx.conf', nginxConfig);
}
//# sourceMappingURL=init.js.map