@bostonuniversity/buwp-local 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/USAGE.md ADDED
@@ -0,0 +1,258 @@
1
+ # buwp-local Development Guide
2
+
3
+ ## Quick Start
4
+
5
+ ### 1. Install in your project
6
+
7
+ ```bash
8
+ npm install --save-dev buwp-local
9
+ ```
10
+
11
+ ### 2. Initialize configuration
12
+
13
+ ```bash
14
+ npx buwp-local config --init
15
+ ```
16
+
17
+ This creates `.buwp-local.json` in your project directory.
18
+
19
+ ### 3. Edit configuration
20
+
21
+ Edit `.buwp-local.json` to map your local repository into the container:
22
+
23
+ ```json
24
+ {
25
+ "image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest",
26
+ "hostname": "myproject.local",
27
+ "multisite": true,
28
+ "mappings": [
29
+ {
30
+ "local": "./",
31
+ "container": "/var/www/html/wp-content/plugins/my-plugin"
32
+ }
33
+ ]
34
+ }
35
+ ```
36
+
37
+ ### 4. Create `.env.local` for secrets
38
+
39
+ Create `.env.local` (never commit this file!):
40
+
41
+ ```bash
42
+ # Database
43
+ WORDPRESS_DB_PASSWORD=password
44
+ DB_ROOT_PASSWORD=rootpassword
45
+
46
+ # Shibboleth (if needed)
47
+ SP_ENTITY_ID=https://your-sp-entity-id
48
+ IDP_ENTITY_ID=https://shib-test.bu.edu/idp/shibboleth
49
+ SHIB_IDP_LOGOUT=https://shib-test.bu.edu/idp/logout.jsp
50
+ SHIB_SP_KEY=your-key-here
51
+ SHIB_SP_CERT=your-cert-here
52
+
53
+ # AWS S3 (if needed)
54
+ S3_UPLOADS_BUCKET=your-bucket
55
+ S3_UPLOADS_REGION=us-east-1
56
+ S3_UPLOADS_ACCESS_KEY_ID=your-access-key
57
+ S3_UPLOADS_SECRET_ACCESS_KEY=your-secret-key
58
+
59
+ # OLAP
60
+ OLAP=your-olap-name
61
+ OLAP_ACCT_NBR=your-account-number
62
+ OLAP_REGION=us-east-1
63
+ ```
64
+
65
+ ### 5. Add hostname to /etc/hosts
66
+
67
+ ```bash
68
+ sudo bash -c 'echo "127.0.0.1 username.local" >> /etc/hosts'
69
+ ```
70
+
71
+ ### 6. Start the environment
72
+
73
+ ```bash
74
+ npx buwp-local start
75
+ ```
76
+
77
+ ### 7. Access your site
78
+
79
+ Open http://username.local or https://username.local in your browser.
80
+
81
+ ## Commands
82
+
83
+ ### Start environment
84
+ ```bash
85
+ npx buwp-local start [options]
86
+
87
+ Options:
88
+ --xdebug Enable Xdebug
89
+ --no-s3 Disable S3 proxy service
90
+ --no-redis Disable Redis service
91
+ ```
92
+
93
+ ### Stop environment
94
+ ```bash
95
+ npx buwp-local stop
96
+ ```
97
+
98
+ ### View logs
99
+ ```bash
100
+ npx buwp-local logs [options]
101
+
102
+ Options:
103
+ -f, --follow Follow log output
104
+ -s, --service <service> Show logs for specific service
105
+ (wordpress, db, s3proxy, redis)
106
+ ```
107
+
108
+ ### Destroy environment
109
+ ```bash
110
+ npx buwp-local destroy [options]
111
+
112
+ Options:
113
+ -f, --force Skip confirmation prompt
114
+ ```
115
+
116
+ ### Configuration management
117
+ ```bash
118
+ npx buwp-local config [options]
119
+
120
+ Options:
121
+ --init Initialize configuration file
122
+ --validate Validate configuration file
123
+ --show Show resolved configuration (with masked secrets)
124
+ ```
125
+
126
+ ## Configuration File
127
+
128
+ ### Basic Structure
129
+
130
+ ```json
131
+ {
132
+ "image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest",
133
+ "hostname": "wordpress.local",
134
+ "multisite": true,
135
+ "services": {
136
+ "redis": true,
137
+ "s3proxy": true,
138
+ "shibboleth": true
139
+ },
140
+ "ports": {
141
+ "http": 80,
142
+ "https": 443,
143
+ "db": 3306,
144
+ "redis": 6379
145
+ },
146
+ "mappings": [],
147
+ "env": {}
148
+ }
149
+ ```
150
+
151
+ ### Volume Mappings
152
+
153
+ Map your local code into the container:
154
+
155
+ ```json
156
+ {
157
+ "mappings": [
158
+ {
159
+ "local": "./",
160
+ "container": "/var/www/html/wp-content/plugins/my-plugin"
161
+ },
162
+ {
163
+ "local": "../my-theme",
164
+ "container": "/var/www/html/wp-content/themes/my-theme"
165
+ }
166
+ ]
167
+ }
168
+ ```
169
+
170
+ ### Custom Environment Variables
171
+
172
+ ```json
173
+ {
174
+ "env": {
175
+ "WP_DEBUG": true,
176
+ "WP_DEBUG_LOG": true,
177
+ "XDEBUG": false
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### Disabling Services
183
+
184
+ ```json
185
+ {
186
+ "services": {
187
+ "redis": false,
188
+ "s3proxy": false,
189
+ "shibboleth": false
190
+ }
191
+ }
192
+ ```
193
+
194
+ ## Security Best Practices
195
+
196
+ 1. **Never commit `.env.local`** - This file contains secrets
197
+ 2. **Never commit `.buwp-local/`** - This contains generated files
198
+ 3. **Do commit `.buwp-local.json`** - This is your configuration template
199
+ 4. **Use environment variables for all secrets**
200
+ 5. **Consider using macOS Keychain** for even better security (see docs)
201
+
202
+ ## File Structure
203
+
204
+ When you use `buwp-local`, these files are created:
205
+
206
+ ```
207
+ your-project/
208
+ ├── .buwp-local.json # Configuration (commit this)
209
+ ├── .env.local # Secrets (NEVER commit)
210
+ ├── .buwp-local/ # Generated files (don't commit)
211
+ │ └── docker-compose.yml # Generated compose file
212
+ └── package.json # Your project
213
+ ```
214
+
215
+ ## Troubleshooting
216
+
217
+ ### Port conflicts
218
+
219
+ If you get port conflicts, you can change ports in `.buwp-local.json`:
220
+
221
+ ```json
222
+ {
223
+ "ports": {
224
+ "http": 8080,
225
+ "https": 8443,
226
+ "db": 3307,
227
+ "redis": 6380
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### Docker not running
233
+
234
+ Make sure Docker Desktop is running:
235
+ ```bash
236
+ docker info
237
+ ```
238
+
239
+ ### Configuration errors
240
+
241
+ Validate your configuration:
242
+ ```bash
243
+ npx buwp-local config --validate
244
+ ```
245
+
246
+ ### View current configuration
247
+
248
+ See the resolved configuration (with masked secrets):
249
+ ```bash
250
+ npx buwp-local config --show
251
+ ```
252
+
253
+ ## Next Steps
254
+
255
+ - [ ] Phase 2: WP-CLI proxy command
256
+ - [ ] Phase 2: macOS Keychain integration
257
+ - [ ] Phase 3: Automatic /etc/hosts management
258
+ - [ ] Phase 3: SSL certificate generation
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * buwp-local CLI
5
+ * Main entry point for the BU WordPress Local development environment tool
6
+ */
7
+
8
+ const { Command } = require('commander');
9
+ const chalk = require('chalk');
10
+ const packageJson = require('../package.json');
11
+
12
+ // Import commands
13
+ const startCommand = require('../lib/commands/start');
14
+ const stopCommand = require('../lib/commands/stop');
15
+ const destroyCommand = require('../lib/commands/destroy');
16
+ const logsCommand = require('../lib/commands/logs');
17
+ const configCommand = require('../lib/commands/config');
18
+
19
+ const program = new Command();
20
+
21
+ program
22
+ .name('buwp-local')
23
+ .description('Local WordPress development environment for Boston University projects')
24
+ .version(packageJson.version);
25
+
26
+ // Start command
27
+ program
28
+ .command('start')
29
+ .description('Start the local WordPress environment')
30
+ .option('--xdebug', 'Enable Xdebug')
31
+ .option('--no-s3', 'Disable S3 proxy service')
32
+ .option('--no-redis', 'Disable Redis service')
33
+ .action(startCommand);
34
+
35
+ // Stop command
36
+ program
37
+ .command('stop')
38
+ .description('Stop the local WordPress environment')
39
+ .action(stopCommand);
40
+
41
+ // Destroy command
42
+ program
43
+ .command('destroy')
44
+ .description('Destroy the local WordPress environment (removes volumes)')
45
+ .option('-f, --force', 'Skip confirmation prompt')
46
+ .action(destroyCommand);
47
+
48
+ // Logs command
49
+ program
50
+ .command('logs')
51
+ .description('View logs from the WordPress environment')
52
+ .option('-f, --follow', 'Follow log output')
53
+ .option('-s, --service <service>', 'Show logs for specific service (wordpress, db, s3proxy, redis)')
54
+ .action(logsCommand);
55
+
56
+ // Config command
57
+ program
58
+ .command('config')
59
+ .description('Configuration management')
60
+ .option('--init', 'Initialize configuration file')
61
+ .option('--plugin', 'Initialize with plugin mapping (use with --init)')
62
+ .option('--mu-plugin', 'Initialize with mu-plugin mapping (use with --init)')
63
+ .option('--theme', 'Initialize with theme mapping (use with --init)')
64
+ .option('--validate', 'Validate configuration file')
65
+ .option('--show', 'Show resolved configuration')
66
+ .action(configCommand);
67
+
68
+ // WP-CLI proxy command
69
+ program
70
+ .command('wp <args...>')
71
+ .description('Run WP-CLI commands in the WordPress container')
72
+ .allowUnknownOption()
73
+ .action((args) => {
74
+ console.log(chalk.yellow('WP-CLI proxy not yet implemented'));
75
+ console.log('Would run:', args.join(' '));
76
+ });
77
+
78
+ // Error handling
79
+ program.exitOverride();
80
+
81
+ try {
82
+ program.parse(process.argv);
83
+ } catch (err) {
84
+ if (err.code === 'commander.help' || err.code === 'commander.version') {
85
+ // Normal exit for help/version
86
+ process.exit(0);
87
+ }
88
+ console.error(chalk.red('Error:'), err.message);
89
+ process.exit(1);
90
+ }
91
+
92
+ // Show help if no command provided
93
+ if (!process.argv.slice(2).length) {
94
+ program.outputHelp();
95
+ }
@@ -0,0 +1,106 @@
1
+ # Complete local development environment for BU WordPress
2
+ # Includes database, WordPress, S3 proxy, and Redis services
3
+
4
+ services:
5
+ db:
6
+ image: mariadb:latest
7
+ restart: always
8
+ volumes:
9
+ - db_data:/var/lib/mysql
10
+ environment:
11
+ MYSQL_DATABASE: wordpress
12
+ MYSQL_USER: wordpress
13
+ MYSQL_PASSWORD: ${WORDPRESS_DB_PASSWORD:-password}
14
+ MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpassword}
15
+ ports:
16
+ - "3306:3306"
17
+ networks:
18
+ - wp-network
19
+
20
+ wordpress:
21
+ # If DOCKER_REGISTRY is set, pull the image from there; otherwise, use the locally built image
22
+ image: ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest
23
+ depends_on:
24
+ - db
25
+ - s3proxy
26
+ - redis
27
+ restart: always
28
+ ports:
29
+ - "80:80"
30
+ - "443:443"
31
+ hostname: ${WP_HOSTNAME:-wordpress.local}
32
+ environment:
33
+ WORDPRESS_DB_HOST: db:3306
34
+ WORDPRESS_DB_USER: wordpress
35
+ WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD:-password}
36
+ WORDPRESS_DB_NAME: wordpress
37
+ WORDPRESS_DEBUG: ${WORDPRESS_DEBUG:-0}
38
+ SERVER_NAME: ${WP_HOSTNAME:-wordpress.local}
39
+ HTTP_HOST: ${WP_HOSTNAME:-wordpress.local}
40
+ SP_ENTITY_ID: ${SP_ENTITY_ID:-https://*.kualitest.research.bu.edu/shibboleth}
41
+ IDP_ENTITY_ID: ${IDP_ENTITY_ID:-https://shib-test.bu.edu/idp/shibboleth}
42
+ SHIB_IDP_LOGOUT: ${SHIB_IDP_LOGOUT:-https://shib-test.bu.edu/idp/logout.jsp}
43
+ SHIB_SP_KEY: ${SHIB_SP_KEY}
44
+ SHIB_SP_CERT: ${SHIB_SP_CERT}
45
+ S3PROXY_HOST: http://s3proxy:8080
46
+ FORWARDED_FOR_HOST: ${FORWARDED_FOR_HOST:-localhost}
47
+ REDIS_HOST: redis
48
+ REDIS_PORT: 6379
49
+ TZ: America/New_York
50
+ MULTISITE: true
51
+ XDEBUG: ${XDEBUG:-false}
52
+ WP_CLI_ALLOW_ROOT: true
53
+ # S3 WordPress config
54
+ WORDPRESS_CONFIG_EXTRA: |
55
+ define('MULTISITE', true);
56
+ define('SUBDOMAIN_INSTALL', false);
57
+ define( 'S3_UPLOADS_BUCKET', '${S3_UPLOADS_BUCKET}');
58
+ define( 'S3_UPLOADS_REGION', '${S3_UPLOADS_REGION}');
59
+ define( 'S3_UPLOADS_SECRET', '${S3_UPLOADS_SECRET_ACCESS_KEY}');
60
+ define( 'S3_UPLOADS_KEY', '${S3_UPLOADS_ACCESS_KEY_ID}');
61
+ define( 'ACCESS_RULES_TABLE', '${ACCESS_RULES_TABLE}');
62
+ define( 'S3_UPLOADS_OBJECT_ACL', null);
63
+ define( 'S3_UPLOADS_AUTOENABLE', true );
64
+ define( 'S3_UPLOADS_DISABLE_REPLACE_UPLOAD_URL', true);
65
+ define( 'BU_INCLUDES_PATH', '/var/www/html/bu-includes' );
66
+ volumes:
67
+ - wp_build:/var/www/html
68
+ - /Users/jaydub/CodeProjects/github/bu-media-s3:/var/www/html/wp-content/mu-plugins/bu-media-s3
69
+ networks:
70
+ - wp-network
71
+
72
+ s3proxy:
73
+ image: public.ecr.aws/bostonuniversity-nonprod/aws-sigv4-proxy
74
+ restart: always
75
+ command:
76
+ - "-v"
77
+ - "--name"
78
+ - "s3-object-lambda"
79
+ - "--region"
80
+ - "${OLAP_REGION:-us-east-1}"
81
+ - "--no-verify-ssl"
82
+ - "--host"
83
+ - "${OLAP}-${OLAP_ACCT_NBR}.s3-object-lambda.${OLAP_REGION:-us-east-1}.amazonaws.com"
84
+ environment:
85
+ healthcheck_path: '/s3proxy-healthcheck'
86
+ AWS_ACCESS_KEY_ID: ${S3_UPLOADS_ACCESS_KEY_ID}
87
+ AWS_SECRET_ACCESS_KEY: ${S3_UPLOADS_SECRET_ACCESS_KEY}
88
+ REGION: ${S3_UPLOADS_REGION:-us-east-1}
89
+ networks:
90
+ - wp-network
91
+
92
+ redis:
93
+ image: redis:alpine
94
+ restart: always
95
+ ports:
96
+ - "6379:6379"
97
+ networks:
98
+ - wp-network
99
+
100
+ networks:
101
+ wp-network:
102
+ driver: bridge
103
+
104
+ volumes:
105
+ db_data:
106
+ wp_build:
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Config command - Configuration management
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const { loadConfig, validateConfig, initConfig, CONFIG_FILE_NAME } = require('../config');
7
+
8
+ async function configCommand(options) {
9
+ try {
10
+ const projectPath = process.cwd();
11
+
12
+ // Initialize configuration
13
+ if (options.init) {
14
+ console.log(chalk.blue('📝 Initializing configuration...\n'));
15
+
16
+ // Determine initialization type
17
+ const initOptions = {};
18
+ if (options.plugin) {
19
+ initOptions.plugin = true;
20
+ console.log(chalk.gray('Creating plugin configuration...'));
21
+ } else if (options.muPlugin) {
22
+ initOptions.muPlugin = true;
23
+ console.log(chalk.gray('Creating mu-plugin configuration...'));
24
+ } else if (options.theme) {
25
+ initOptions.theme = true;
26
+ console.log(chalk.gray('Creating theme configuration...'));
27
+ }
28
+
29
+ try {
30
+ const configPath = initConfig(projectPath, initOptions);
31
+ console.log(chalk.green(`✅ Created configuration file: ${configPath}\n`));
32
+
33
+ if (initOptions.plugin || initOptions.muPlugin || initOptions.theme) {
34
+ const path = require('path');
35
+ const projectName = path.basename(projectPath);
36
+ const type = initOptions.plugin ? 'plugin' : initOptions.muPlugin ? 'mu-plugin' : 'theme';
37
+ console.log(chalk.cyan(`Auto-configured mapping for ${type}: ${projectName}`));
38
+ }
39
+
40
+ console.log(chalk.gray('Edit this file to customize your environment.'));
41
+ console.log(chalk.gray('Then run "buwp-local start" to launch your environment.\n'));
42
+ } catch (err) {
43
+ if (err.message.includes('already exists')) {
44
+ console.log(chalk.yellow(`⚠️ ${CONFIG_FILE_NAME} already exists.\n`));
45
+ } else {
46
+ throw err;
47
+ }
48
+ }
49
+ return;
50
+ }
51
+
52
+ // Validate configuration
53
+ if (options.validate) {
54
+ console.log(chalk.blue('🔍 Validating configuration...\n'));
55
+
56
+ const config = loadConfig(projectPath);
57
+ const validation = validateConfig(config);
58
+
59
+ if (validation.valid) {
60
+ console.log(chalk.green('✅ Configuration is valid!\n'));
61
+ } else {
62
+ console.log(chalk.red('❌ Configuration has errors:\n'));
63
+ validation.errors.forEach(error => {
64
+ console.log(chalk.red(` - ${error}`));
65
+ });
66
+ console.log('');
67
+ process.exit(1);
68
+ }
69
+ return;
70
+ }
71
+
72
+ // Show resolved configuration
73
+ if (options.show) {
74
+ console.log(chalk.blue('📄 Resolved configuration:\n'));
75
+
76
+ const config = loadConfig(projectPath);
77
+
78
+ // Mask sensitive values
79
+ const maskedConfig = maskSensitiveData(config);
80
+
81
+ console.log(JSON.stringify(maskedConfig, null, 2));
82
+ console.log('');
83
+ return;
84
+ }
85
+
86
+ // No options provided, show help
87
+ console.log(chalk.yellow('Usage: buwp-local config [options]\n'));
88
+ console.log('Options:');
89
+ console.log(' --init Initialize configuration file');
90
+ console.log(' --init --plugin Initialize with plugin mapping');
91
+ console.log(' --init --mu-plugin Initialize with mu-plugin mapping');
92
+ console.log(' --init --theme Initialize with theme mapping');
93
+ console.log(' --validate Validate configuration file');
94
+ console.log(' --show Show resolved configuration\n');
95
+
96
+ } catch (err) {
97
+ console.error(chalk.red('\n❌ Error:'), err.message);
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Mask sensitive data in configuration
104
+ * @param {object} config - Configuration object
105
+ * @returns {object} Configuration with masked sensitive data
106
+ */
107
+ function maskSensitiveData(config) {
108
+ const masked = JSON.parse(JSON.stringify(config));
109
+
110
+ // Mask database passwords
111
+ if (masked.db) {
112
+ if (masked.db.password) masked.db.password = '***MASKED***';
113
+ if (masked.db.rootPassword) masked.db.rootPassword = '***MASKED***';
114
+ }
115
+
116
+ // Mask Shibboleth credentials
117
+ if (masked.shibboleth) {
118
+ if (masked.shibboleth.spKey) masked.shibboleth.spKey = '***MASKED***';
119
+ if (masked.shibboleth.spCert) masked.shibboleth.spCert = '***MASKED***';
120
+ }
121
+
122
+ // Mask S3 credentials
123
+ if (masked.s3) {
124
+ if (masked.s3.accessKeyId) masked.s3.accessKeyId = '***MASKED***';
125
+ if (masked.s3.secretAccessKey) masked.s3.secretAccessKey = '***MASKED***';
126
+ }
127
+
128
+ return masked;
129
+ }
130
+
131
+ module.exports = configCommand;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Destroy command - Destroys the local WordPress environment (including volumes)
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const { execSync } = require('child_process');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+ const readline = require('readline');
10
+ const { loadConfig } = require('../config');
11
+
12
+ async function destroyCommand(options) {
13
+ console.log(chalk.red('⚠️ DESTROY BU WordPress local environment\n'));
14
+
15
+ try {
16
+ const projectPath = process.cwd();
17
+ const composePath = path.join(projectPath, '.buwp-local', 'docker-compose.yml');
18
+
19
+ // Check if docker-compose.yml exists
20
+ if (!fs.existsSync(composePath)) {
21
+ console.log(chalk.yellow('⚠️ No environment found to destroy.\n'));
22
+ return;
23
+ }
24
+
25
+ // Load config to get project name
26
+ const config = loadConfig(projectPath);
27
+ const projectName = config.projectName || 'buwp-local';
28
+
29
+ // Confirm destruction unless --force flag is used
30
+ if (!options.force) {
31
+ const confirmed = await confirmDestroy(projectName);
32
+ if (!confirmed) {
33
+ console.log(chalk.gray('Destroy cancelled.\n'));
34
+ return;
35
+ }
36
+ }
37
+
38
+ // Check if Docker is running
39
+ try {
40
+ execSync('docker info', { stdio: 'ignore' });
41
+ } catch (err) {
42
+ console.error(chalk.red('❌ Docker is not running.'));
43
+ process.exit(1);
44
+ }
45
+
46
+ // Destroy Docker Compose (down with volumes)
47
+ console.log(chalk.gray('\nRemoving containers and volumes...\n'));
48
+ const composeDir = path.dirname(composePath);
49
+
50
+ try {
51
+ execSync(
52
+ `docker compose -p ${projectName} -f ${composePath} down -v`,
53
+ {
54
+ cwd: composeDir,
55
+ stdio: 'inherit'
56
+ }
57
+ );
58
+ } catch (err) {
59
+ console.error(chalk.red('\n❌ Failed to destroy Docker environment'));
60
+ process.exit(1);
61
+ }
62
+
63
+ console.log(chalk.green('\n✅ Environment destroyed successfully!\n'));
64
+ console.log(chalk.gray('Use "buwp-local start" to create a fresh environment.\n'));
65
+
66
+ } catch (err) {
67
+ console.error(chalk.red('\n❌ Error:'), err.message);
68
+ process.exit(1);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Prompt user for confirmation
74
+ * @param {string} projectName - Name of the project being destroyed
75
+ * @returns {Promise<boolean>}
76
+ */
77
+ function confirmDestroy(projectName) {
78
+ return new Promise((resolve) => {
79
+ const rl = readline.createInterface({
80
+ input: process.stdin,
81
+ output: process.stdout
82
+ });
83
+
84
+ console.log(chalk.yellow(`This will destroy project: ${chalk.bold(projectName)}`));
85
+ console.log(chalk.yellow(' - Stop all containers'));
86
+ console.log(chalk.yellow(' - Remove all containers'));
87
+ console.log(chalk.yellow(' - Delete all volumes (including database data)\n'));
88
+
89
+ rl.question(chalk.red('Are you sure you want to continue? (yes/no): '), (answer) => {
90
+ rl.close();
91
+ resolve(answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y');
92
+ });
93
+ });
94
+ }
95
+
96
+ module.exports = destroyCommand;