UNPKG

dreamhost-deployer

Version:

A stylish, interactive CLI tool for deploying websites to DreamHost shared hosting with automated build integration

346 lines (292 loc) 10.9 kB
/** * DreamHost Deployer * Version 0.6.2 * * Setup command implementation with enhanced UI */ const fs = require('fs'); const path = require('path'); const inquirer = require('inquirer'); const ui = require('../utils/ui'); const configManager = require('../utils/config-manager'); const buildIntegration = require('../utils/build-integration'); // Default config path const DEFAULT_CONFIG_PATH = path.join(process.cwd(), 'deploy.config.json'); /** * Run the interactive setup process */ async function runSetup(options = {}) { ui.sectionHeader('CONFIGURATION SETUP'); try { const configPath = options.configPath || DEFAULT_CONFIG_PATH; // Check if config exists const loadSpinner = ui.spinner('Checking for existing configuration...'); loadSpinner.start(); const existingConfig = configManager.loadConfig(configPath); await new Promise(resolve => setTimeout(resolve, 800)); loadSpinner.stop(); if (existingConfig) { console.log(ui.warning(`Found existing configuration at: ${configPath}`)); const { overwrite } = await inquirer.prompt([ { type: 'confirm', name: 'overwrite', message: 'Do you want to modify this configuration?', default: true } ]); if (!overwrite) { console.log(ui.info('Setup canceled. Using existing configuration.')); return existingConfig; } } // Detect project type for better defaults const detectSpinner = ui.spinner('Detecting project type...'); detectSpinner.start(); // Try to auto-detect project type let projectType = 'unknown'; let buildCommand = ''; let buildOutputDir = ''; let localPath = ''; const projectInfo = buildIntegration.detectProjectType(); projectType = projectInfo.type; await new Promise(resolve => setTimeout(resolve, 1200)); detectSpinner.stop(); if (projectType !== 'unknown') { console.log(ui.success(`Detected ${projectInfo.type} project!`)); console.log(ui.info(projectInfo.details)); console.log(ui.projectTypeBadge(projectType)); buildCommand = projectInfo.buildCommand; buildOutputDir = projectInfo.outputDir; if (buildOutputDir) { localPath = path.join(process.cwd(), buildOutputDir); } } else { console.log(ui.info('Could not automatically detect project type. Will use generic defaults.')); localPath = process.cwd(); } // Create prompts array for inquirer // Use existing config values as defaults if they exist const prompts = [ { type: 'input', name: 'host', message: 'DreamHost server hostname:', default: existingConfig?.host || 'example.dreamhost.com', validate: input => input ? true : 'Server hostname is required' }, { type: 'input', name: 'username', message: 'DreamHost SSH username:', default: existingConfig?.username || '', validate: input => input ? true : 'Username is required' }, { type: 'input', name: 'remotePath', message: 'Remote path on server:', default: existingConfig?.remotePath || '/home/username/example.com', validate: input => input.startsWith('/') ? true : 'Path must be absolute (start with /)' }, { type: 'input', name: 'localPath', message: 'Local path to deploy:', default: existingConfig?.localPath || localPath || process.cwd(), validate: input => input ? true : 'Local path is required' }, { type: 'list', name: 'authType', message: 'Authentication method:', default: existingConfig?.privateKeyPath ? 'key' : 'password', choices: [ { name: 'SSH Key (recommended)', value: 'key' }, { name: 'Password', value: 'password' } ] } ]; // Add SSH key path question if SSH key is selected const initialAnswers = await inquirer.prompt(prompts); let answers = { ...initialAnswers }; if (answers.authType === 'key') { const keyPrompt = await inquirer.prompt([ { type: 'input', name: 'privateKeyPath', message: 'Path to SSH private key:', default: existingConfig?.privateKeyPath || path.join(process.env.HOME || process.env.USERPROFILE, '.ssh', 'id_rsa') } ]); answers.privateKeyPath = keyPrompt.privateKeyPath; } // Add build integration questions console.log(''); ui.subsectionHeader('Build Integration'); const buildPrompt = await inquirer.prompt([ { type: 'confirm', name: 'buildIntegration', message: 'Enable build integration?', default: !!existingConfig?.buildIntegration || projectType !== 'unknown' } ]); answers.buildIntegration = buildPrompt.buildIntegration; if (answers.buildIntegration) { const buildAnswers = await inquirer.prompt([ { type: 'input', name: 'buildCommand', message: 'Build command:', default: existingConfig?.buildCommand || buildCommand || 'npm run build', validate: input => input ? true : 'Build command is required' }, { type: 'input', name: 'buildOutputDir', message: 'Build output directory (relative to project root):', default: existingConfig?.buildOutputDir || buildOutputDir || 'dist', validate: input => input ? true : 'Output directory is required' } ]); answers = { ...answers, ...buildAnswers }; // Ask if they want to use the build output as the local path const { useBuildOutput } = await inquirer.prompt([ { type: 'confirm', name: 'useBuildOutput', message: 'Use build output directory as the local path to deploy?', default: true } ]); if (useBuildOutput) { answers.localPath = path.join(process.cwd(), answers.buildOutputDir); console.log(ui.info(`Local path updated to: ${answers.localPath}`)); } } // Add advanced configuration console.log(''); ui.subsectionHeader('Advanced Options'); const { configureAdvanced } = await inquirer.prompt([ { type: 'confirm', name: 'configureAdvanced', message: 'Configure advanced options?', default: false } ]); if (configureAdvanced) { const advancedPrompt = await inquirer.prompt([ { type: 'list', name: 'webServer', message: 'Web server type:', default: existingConfig?.webServer || 'apache', choices: [ { name: 'Apache', value: 'apache' }, { name: 'Nginx', value: 'nginx' }, { name: 'Other', value: 'other' } ] }, { type: 'input', name: 'excludePatterns', message: 'File patterns to exclude (comma separated):', default: existingConfig?.excludePatterns?.join(',') || '.git,.DS_Store,node_modules' }, { type: 'confirm', name: 'createBackup', message: 'Create backup before deployment?', default: existingConfig?.createBackup !== false } ]); answers = { ...answers, ...advancedPrompt, excludePatterns: advancedPrompt.excludePatterns.split(',').map(p => p.trim()).filter(p => p) }; } else { // Set defaults for advanced options answers.webServer = existingConfig?.webServer || 'apache'; answers.excludePatterns = existingConfig?.excludePatterns || ['.git', '.DS_Store', 'node_modules']; answers.createBackup = existingConfig?.createBackup !== false; } // Create and save the config const saveSpinner = ui.spinner('Saving configuration...'); saveSpinner.start(); const config = { host: answers.host, username: answers.username, remotePath: answers.remotePath, localPath: answers.localPath, webServer: answers.webServer, excludePatterns: answers.excludePatterns, createBackup: answers.createBackup, buildIntegration: answers.buildIntegration }; // Add conditional properties if (answers.privateKeyPath) { config.privateKeyPath = answers.privateKeyPath; } if (answers.buildIntegration) { config.buildCommand = answers.buildCommand; config.buildOutputDir = answers.buildOutputDir; } await new Promise(resolve => setTimeout(resolve, 1000)); saveSpinner.stop(); // Save the config configManager.saveConfig(config, configPath); // Display the saved configuration console.log(ui.success(`Configuration saved to: ${configPath}`)); // Show summary of configuration console.log(''); ui.subsectionHeader('Configuration Summary'); const configTable = ui.createTable(['Setting', 'Value']); configTable.push( ['Host', config.host], ['Username', config.username], ['Remote Path', config.remotePath], ['Local Path', config.localPath], ['Authentication', config.privateKeyPath ? `SSH Key (${config.privateKeyPath})` : 'Password'] ); if (config.buildIntegration) { configTable.push( ['Build Integration', ui.badge('Enabled', 'success')], ['Build Command', config.buildCommand], ['Build Output', config.buildOutputDir] ); } else { configTable.push(['Build Integration', ui.badge('Disabled', 'normal')]); } console.log(configTable.toString()); // Validate the config const validationErrors = configManager.validateConfig(config); if (validationErrors.length === 0) { console.log(ui.success('Configuration validation: All settings are valid')); } else { console.log(ui.warning('Configuration validation: Issues detected')); validationErrors.forEach(error => console.log(ui.warning(` • ${error}`))); } // Show next steps console.log(''); ui.subsectionHeader('Next Steps'); const nextStepsText = ` To deploy your website: ${ui.command('dreamhost deploy')} To test deployment without uploading files: ${ui.command('dreamhost deploy --dry-run')} To set up SSH key authentication: ${ui.command('dreamhost ssh-setup')}`; const nextStepsBox = ui.box(nextStepsText, { title: 'What\'s Next', padding: 1 }); console.log(nextStepsBox); return config; } catch (error) { console.error(ui.error(`Setup error: ${error.message}`)); throw error; } } module.exports = { runSetup };