@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/.buwp-local.examplejson +27 -0
- package/.buwp-local.json +27 -0
- package/IMPLEMENTATION_SUMMARY.md +385 -0
- package/MULTI_PROJECT_GUIDE.md +929 -0
- package/PROJECT_OVERVIEW.md +307 -0
- package/QUICK_REFERENCE.md +234 -0
- package/ROADMAP.md +362 -0
- package/SHARED_ENVIRONMENT_EXAMPLES.md +578 -0
- package/USAGE.md +258 -0
- package/bin/buwp-local.js +95 -0
- package/bostonuniversity-buwp-local-0.1.0.tgz +0 -0
- package/docker-compose.yml +106 -0
- package/lib/commands/config.js +131 -0
- package/lib/commands/destroy.js +96 -0
- package/lib/commands/logs.js +66 -0
- package/lib/commands/start.js +98 -0
- package/lib/commands/stop.js +62 -0
- package/lib/compose-generator.js +279 -0
- package/lib/config.js +292 -0
- package/lib/index.js +23 -0
- package/macos-keychain-notes.md +11 -0
- package/package.json +35 -0
- package/readme.md +3 -0
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
|
+
}
|
|
Binary file
|
|
@@ -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;
|