@bostonuniversity/buwp-local 0.2.0 → 0.4.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/.eslintrc.json +15 -0
- package/USAGE.md +30 -4
- package/bin/buwp-local.js +33 -10
- package/lib/commands/config.js +4 -4
- package/lib/commands/destroy.js +8 -7
- package/lib/commands/init.js +308 -0
- package/lib/commands/logs.js +6 -6
- package/lib/commands/shell.js +7 -7
- package/lib/commands/start.js +6 -6
- package/lib/commands/stop.js +6 -6
- package/lib/commands/wp.js +7 -7
- package/lib/compose-generator.js +4 -5
- package/lib/config.js +6 -5
- package/lib/index.js +10 -17
- package/package.json +7 -4
- package/.buwp-local.json +0 -27
- package/SHARED_ENVIRONMENT_EXAMPLES.md +0 -578
- package/bostonuniversity-buwp-local-0.1.0.tgz +0 -0
- package/bostonuniversity-buwp-local-0.2.0.tgz +0 -0
- /package/{.buwp-local.examplejson → .buwp-local.example.json} +0 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"es2022": true,
|
|
4
|
+
"node": true
|
|
5
|
+
},
|
|
6
|
+
"extends": "eslint:recommended",
|
|
7
|
+
"parserOptions": {
|
|
8
|
+
"ecmaVersion": "latest",
|
|
9
|
+
"sourceType": "module"
|
|
10
|
+
},
|
|
11
|
+
"rules": {
|
|
12
|
+
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
13
|
+
"no-console": "off"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/USAGE.md
CHANGED
|
@@ -10,14 +10,23 @@ npm install --save-dev @bostonuniversity/buwp-local
|
|
|
10
10
|
|
|
11
11
|
### 2. Initialize configuration
|
|
12
12
|
|
|
13
|
+
**Interactive mode (recommended):**
|
|
13
14
|
```bash
|
|
14
|
-
npx buwp-local
|
|
15
|
+
npx buwp-local init
|
|
15
16
|
```
|
|
16
17
|
|
|
18
|
+
The interactive setup will guide you through:
|
|
19
|
+
- Project name and type (plugin, theme, mu-plugin)
|
|
20
|
+
- Hostname configuration
|
|
21
|
+
- Port selection
|
|
22
|
+
- Service options (Redis, S3, Shibboleth)
|
|
23
|
+
- Debug settings
|
|
24
|
+
|
|
25
|
+
**Non-interactive mode:**
|
|
17
26
|
```bash
|
|
18
|
-
buwp-local config --init --plugin # For plugins
|
|
19
|
-
buwp-local config --init --mu-plugin # For mu-plugins
|
|
20
|
-
buwp-local config --init --theme # For themes
|
|
27
|
+
npx buwp-local config --init --plugin # For plugins
|
|
28
|
+
npx buwp-local config --init --mu-plugin # For mu-plugins
|
|
29
|
+
npx buwp-local config --init --theme # For themes
|
|
21
30
|
```
|
|
22
31
|
|
|
23
32
|
This creates `.buwp-local.json` in your project directory.
|
|
@@ -86,6 +95,23 @@ Open http://username.local or https://username.local in your browser.
|
|
|
86
95
|
|
|
87
96
|
## Commands
|
|
88
97
|
|
|
98
|
+
### Initialize configuration
|
|
99
|
+
```bash
|
|
100
|
+
npx buwp-local init
|
|
101
|
+
|
|
102
|
+
Options:
|
|
103
|
+
--no-interactive Use non-interactive mode with defaults
|
|
104
|
+
--plugin Non-interactive: initialize as plugin
|
|
105
|
+
--mu-plugin Non-interactive: initialize as mu-plugin
|
|
106
|
+
--theme Non-interactive: initialize as theme
|
|
107
|
+
-f, --force Overwrite existing configuration
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The `init` command provides an interactive setup experience with:
|
|
111
|
+
- **Smart defaults** - Detects project type from files
|
|
112
|
+
- **Real-time validation** - Validates inputs as you type
|
|
113
|
+
- **Guided workflow** - Clear prompts for all configuration options
|
|
114
|
+
|
|
89
115
|
### Start environment
|
|
90
116
|
```bash
|
|
91
117
|
npx buwp-local start [options]
|
package/bin/buwp-local.js
CHANGED
|
@@ -5,18 +5,30 @@
|
|
|
5
5
|
* Main entry point for the BU WordPress Local development environment tool
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { readFileSync } from 'fs';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import { dirname, join } from 'path';
|
|
13
|
+
|
|
14
|
+
// Get __dirname equivalent in ESM
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// Load package.json
|
|
19
|
+
const packageJson = JSON.parse(
|
|
20
|
+
readFileSync(join(__dirname, '../package.json'), 'utf8')
|
|
21
|
+
);
|
|
11
22
|
|
|
12
23
|
// Import commands
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
import startCommand from '../lib/commands/start.js';
|
|
25
|
+
import stopCommand from '../lib/commands/stop.js';
|
|
26
|
+
import destroyCommand from '../lib/commands/destroy.js';
|
|
27
|
+
import logsCommand from '../lib/commands/logs.js';
|
|
28
|
+
import wpCommand from '../lib/commands/wp.js';
|
|
29
|
+
import shellCommand from '../lib/commands/shell.js';
|
|
30
|
+
import configCommand from '../lib/commands/config.js';
|
|
31
|
+
import initCommand from '../lib/commands/init.js';
|
|
20
32
|
|
|
21
33
|
const program = new Command();
|
|
22
34
|
|
|
@@ -67,6 +79,17 @@ program
|
|
|
67
79
|
.option('--show', 'Show resolved configuration')
|
|
68
80
|
.action(configCommand);
|
|
69
81
|
|
|
82
|
+
// Init command (interactive configuration)
|
|
83
|
+
program
|
|
84
|
+
.command('init')
|
|
85
|
+
.description('Initialize configuration interactively')
|
|
86
|
+
.option('--no-interactive', 'Use non-interactive mode with defaults')
|
|
87
|
+
.option('--plugin', 'Non-interactive: initialize as plugin')
|
|
88
|
+
.option('--mu-plugin', 'Non-interactive: initialize as mu-plugin')
|
|
89
|
+
.option('--theme', 'Non-interactive: initialize as theme')
|
|
90
|
+
.option('-f, --force', 'Overwrite existing configuration')
|
|
91
|
+
.action(initCommand);
|
|
92
|
+
|
|
70
93
|
// WP-CLI proxy command
|
|
71
94
|
program
|
|
72
95
|
.command('wp <args...>')
|
package/lib/commands/config.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Config command - Configuration management
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { loadConfig, validateConfig, initConfig, CONFIG_FILE_NAME } from '../config.js';
|
|
7
8
|
|
|
8
9
|
async function configCommand(options) {
|
|
9
10
|
try {
|
|
@@ -31,7 +32,6 @@ async function configCommand(options) {
|
|
|
31
32
|
console.log(chalk.green(`✅ Created configuration file: ${configPath}\n`));
|
|
32
33
|
|
|
33
34
|
if (initOptions.plugin || initOptions.muPlugin || initOptions.theme) {
|
|
34
|
-
const path = require('path');
|
|
35
35
|
const projectName = path.basename(projectPath);
|
|
36
36
|
const type = initOptions.plugin ? 'plugin' : initOptions.muPlugin ? 'mu-plugin' : 'theme';
|
|
37
37
|
console.log(chalk.cyan(`Auto-configured mapping for ${type}: ${projectName}`));
|
|
@@ -128,4 +128,4 @@ function maskSensitiveData(config) {
|
|
|
128
128
|
return masked;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
export default configCommand;
|
package/lib/commands/destroy.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Destroy command - Destroys the local WordPress environment (including volumes)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import readline from 'readline';
|
|
10
|
+
import { loadConfig } from '../config.js';
|
|
11
11
|
|
|
12
12
|
async function destroyCommand(options) {
|
|
13
13
|
console.log(chalk.red('⚠️ DESTROY BU WordPress local environment\n'));
|
|
@@ -48,6 +48,7 @@ async function destroyCommand(options) {
|
|
|
48
48
|
const composeDir = path.dirname(composePath);
|
|
49
49
|
|
|
50
50
|
try {
|
|
51
|
+
// This is the actual docker compose down command that removes everything
|
|
51
52
|
execSync(
|
|
52
53
|
`docker compose -p ${projectName} -f ${composePath} down -v`,
|
|
53
54
|
{
|
|
@@ -93,4 +94,4 @@ function confirmDestroy(projectName) {
|
|
|
93
94
|
});
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
export default destroyCommand;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command - Interactive configuration initialization
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import prompts from 'prompts';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import { initConfig, CONFIG_FILE_NAME } from '../config.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect project type from package.json or directory structure
|
|
14
|
+
* @param {string} projectPath - Path to project directory
|
|
15
|
+
* @returns {string|null} Detected project type or null
|
|
16
|
+
*/
|
|
17
|
+
function detectProjectType(projectPath) {
|
|
18
|
+
// Check package.json for hints
|
|
19
|
+
const packageJsonPath = path.join(projectPath, 'package.json');
|
|
20
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
21
|
+
try {
|
|
22
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
23
|
+
|
|
24
|
+
// Check keywords
|
|
25
|
+
if (pkg.keywords) {
|
|
26
|
+
if (pkg.keywords.includes('wordpress-plugin')) return 'plugin';
|
|
27
|
+
if (pkg.keywords.includes('wordpress-theme')) return 'theme';
|
|
28
|
+
if (pkg.keywords.includes('wordpress-muplugin')) return 'mu-plugin';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check name patterns
|
|
32
|
+
if (pkg.name) {
|
|
33
|
+
if (pkg.name.includes('-plugin')) return 'plugin';
|
|
34
|
+
if (pkg.name.includes('-theme')) return 'theme';
|
|
35
|
+
if (pkg.name.includes('mu-')) return 'mu-plugin';
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// Ignore errors reading package.json
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check for common WordPress files
|
|
43
|
+
if (fs.existsSync(path.join(projectPath, 'style.css'))) {
|
|
44
|
+
const styleContent = fs.readFileSync(path.join(projectPath, 'style.css'), 'utf8');
|
|
45
|
+
if (styleContent.includes('Theme Name:')) return 'theme';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check for plugin header
|
|
49
|
+
const phpFiles = fs.readdirSync(projectPath).filter(f => f.endsWith('.php'));
|
|
50
|
+
for (const phpFile of phpFiles) {
|
|
51
|
+
const content = fs.readFileSync(path.join(projectPath, phpFile), 'utf8');
|
|
52
|
+
if (content.includes('Plugin Name:')) return 'plugin';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null; // Could not detect
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Interactive initialization command
|
|
60
|
+
* @param {object} options - Command options
|
|
61
|
+
*/
|
|
62
|
+
async function initCommand(options) {
|
|
63
|
+
const projectPath = process.cwd();
|
|
64
|
+
const configPath = path.join(projectPath, CONFIG_FILE_NAME);
|
|
65
|
+
|
|
66
|
+
const userName = os.userInfo().username;
|
|
67
|
+
|
|
68
|
+
console.log(chalk.blue(`👋 Hello, ${userName}! Let's set up your WordPress local development environment.\n`));
|
|
69
|
+
|
|
70
|
+
// Check if config already exists
|
|
71
|
+
if (fs.existsSync(configPath) && !options.force) {
|
|
72
|
+
console.log(chalk.yellow(`⚠️ Configuration file already exists: ${configPath}`));
|
|
73
|
+
console.log(chalk.gray('Use --force to overwrite.\n'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if we should use interactive mode
|
|
78
|
+
const isInteractive = options.interactive !== false && process.stdin.isTTY;
|
|
79
|
+
|
|
80
|
+
if (!isInteractive) {
|
|
81
|
+
// Fall back to non-interactive mode
|
|
82
|
+
console.log(chalk.gray('Non-interactive mode: using defaults\n'));
|
|
83
|
+
const initOptions = {};
|
|
84
|
+
if (options.plugin) initOptions.plugin = true;
|
|
85
|
+
if (options.muPlugin) initOptions.muPlugin = true;
|
|
86
|
+
if (options.theme) initOptions.theme = true;
|
|
87
|
+
|
|
88
|
+
const configPath = initConfig(projectPath, initOptions);
|
|
89
|
+
console.log(chalk.green(`✅ Created configuration file: ${configPath}\n`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Interactive mode
|
|
94
|
+
console.log(chalk.blue('🚀 Interactive configuration setup\n'));
|
|
95
|
+
console.log(chalk.gray('Press Ctrl+C to cancel at any time\n'));
|
|
96
|
+
|
|
97
|
+
const detectedName = path.basename(projectPath);
|
|
98
|
+
const detectedType = detectProjectType(projectPath);
|
|
99
|
+
|
|
100
|
+
if (detectedType) {
|
|
101
|
+
console.log(chalk.cyan(`ℹ️ Detected project type: ${detectedType}\n`));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Determine default project type index
|
|
105
|
+
let defaultTypeIndex = 0;
|
|
106
|
+
if (detectedType === 'plugin') defaultTypeIndex = 0;
|
|
107
|
+
else if (detectedType === 'mu-plugin') defaultTypeIndex = 1;
|
|
108
|
+
else if (detectedType === 'theme') defaultTypeIndex = 2;
|
|
109
|
+
|
|
110
|
+
const questions = [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
name: 'projectName',
|
|
114
|
+
message: 'Project name',
|
|
115
|
+
initial: detectedName,
|
|
116
|
+
validate: value => value.trim().length > 0 || 'Project name is required'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
type: 'select',
|
|
120
|
+
name: 'projectType',
|
|
121
|
+
message: 'Project type',
|
|
122
|
+
choices: [
|
|
123
|
+
{ title: 'Plugin', value: 'plugin', description: 'WordPress plugin development' },
|
|
124
|
+
{ title: 'MU Plugin', value: 'mu-plugin', description: 'Must-use plugin development' },
|
|
125
|
+
{ title: 'Theme', value: 'theme', description: 'WordPress theme development' },
|
|
126
|
+
{ title: 'Sandbox', value: 'sandbox', description: 'Base WordPress environment (add code mappings later)' },
|
|
127
|
+
{ title: 'Custom', value: 'custom', description: 'Custom mapping configuration' }
|
|
128
|
+
],
|
|
129
|
+
initial: defaultTypeIndex
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: 'text',
|
|
133
|
+
name: 'hostname',
|
|
134
|
+
message: 'Hostname',
|
|
135
|
+
initial: (prev, values) => {
|
|
136
|
+
return `${userName}-${values.projectName}.local`;
|
|
137
|
+
},
|
|
138
|
+
validate: value => {
|
|
139
|
+
if (!value.trim()) return 'Hostname is required';
|
|
140
|
+
if (!value.includes('.')) return 'Hostname should include a domain (e.g., .local)';
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'text',
|
|
146
|
+
name: 'image',
|
|
147
|
+
message: 'WordPress Docker image',
|
|
148
|
+
initial: 'ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest',
|
|
149
|
+
validate: value => {
|
|
150
|
+
if (!value.trim()) return 'Image name is required';
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'text',
|
|
156
|
+
name: 'httpPort',
|
|
157
|
+
message: 'HTTP port (default: 80)',
|
|
158
|
+
initial: '80',
|
|
159
|
+
validate: value => {
|
|
160
|
+
const port = parseInt(value);
|
|
161
|
+
if (isNaN(port) || port < 1 || port > 65535) return 'Port must be a number between 1 and 65535';
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'text',
|
|
167
|
+
name: 'httpsPort',
|
|
168
|
+
message: 'HTTPS port (default: 443)',
|
|
169
|
+
initial: '443',
|
|
170
|
+
validate: value => {
|
|
171
|
+
const port = parseInt(value);
|
|
172
|
+
if (isNaN(port) || port < 1 || port > 65535) return 'Port must be a number between 1 and 65535';
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: 'text',
|
|
178
|
+
name: 'dbPort',
|
|
179
|
+
message: 'Database port (default: 3306)',
|
|
180
|
+
initial: '3306',
|
|
181
|
+
validate: value => {
|
|
182
|
+
const port = parseInt(value);
|
|
183
|
+
if (isNaN(port) || port < 1 || port > 65535) return 'Port must be a number between 1 and 65535';
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'confirm',
|
|
189
|
+
name: 'redis',
|
|
190
|
+
message: 'Enable Redis?',
|
|
191
|
+
initial: true
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
type: 'confirm',
|
|
195
|
+
name: 's3proxy',
|
|
196
|
+
message: 'Enable S3 proxy?',
|
|
197
|
+
initial: true
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: 'confirm',
|
|
201
|
+
name: 'shibboleth',
|
|
202
|
+
message: 'Enable Shibboleth?',
|
|
203
|
+
initial: true
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: 'confirm',
|
|
207
|
+
name: 'xdebug',
|
|
208
|
+
message: 'Enable Xdebug by default?',
|
|
209
|
+
initial: false
|
|
210
|
+
}
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
const answers = await prompts(questions, {
|
|
214
|
+
onCancel: () => {
|
|
215
|
+
console.log(chalk.yellow('\n⚠️ Setup cancelled\n'));
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Build configuration object
|
|
221
|
+
const config = {
|
|
222
|
+
projectName: answers.projectName,
|
|
223
|
+
image: answers.image,
|
|
224
|
+
hostname: answers.hostname,
|
|
225
|
+
multisite: true,
|
|
226
|
+
services: {
|
|
227
|
+
redis: answers.redis || false,
|
|
228
|
+
s3proxy: answers.s3proxy || false,
|
|
229
|
+
shibboleth: answers.shibboleth || false
|
|
230
|
+
},
|
|
231
|
+
ports: {
|
|
232
|
+
http: parseInt(answers.httpPort),
|
|
233
|
+
https: parseInt(answers.httpsPort),
|
|
234
|
+
db: parseInt(answers.dbPort),
|
|
235
|
+
redis: 6379
|
|
236
|
+
},
|
|
237
|
+
mappings: [],
|
|
238
|
+
env: {
|
|
239
|
+
WP_DEBUG: true,
|
|
240
|
+
XDEBUG: answers.xdebug || false
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// Add mapping based on project type
|
|
245
|
+
if (answers.projectType === 'plugin') {
|
|
246
|
+
config.mappings.push({
|
|
247
|
+
local: './',
|
|
248
|
+
container: `/var/www/html/wp-content/plugins/${answers.projectName}`
|
|
249
|
+
});
|
|
250
|
+
} else if (answers.projectType === 'mu-plugin') {
|
|
251
|
+
config.mappings.push({
|
|
252
|
+
local: './',
|
|
253
|
+
container: `/var/www/html/wp-content/mu-plugins/${answers.projectName}`
|
|
254
|
+
});
|
|
255
|
+
} else if (answers.projectType === 'theme') {
|
|
256
|
+
config.mappings.push({
|
|
257
|
+
local: './',
|
|
258
|
+
container: `/var/www/html/wp-content/themes/${answers.projectName}`
|
|
259
|
+
});
|
|
260
|
+
} else if (answers.projectType === 'sandbox') {
|
|
261
|
+
// Sandbox type: no initial mappings
|
|
262
|
+
// User can add mappings manually to config.mappings array
|
|
263
|
+
} else {
|
|
264
|
+
// Custom type
|
|
265
|
+
config.mappings.push({
|
|
266
|
+
local: './',
|
|
267
|
+
container: '/var/www/html/wp-content/plugins/my-plugin',
|
|
268
|
+
comment: 'Customize this mapping for your project'
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Write configuration file
|
|
273
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
274
|
+
|
|
275
|
+
console.log(chalk.green(`\n✅ Created configuration file: ${configPath}\n`));
|
|
276
|
+
|
|
277
|
+
// Show summary
|
|
278
|
+
console.log(chalk.cyan('📋 Configuration summary:'));
|
|
279
|
+
console.log(chalk.gray(` Project: ${answers.projectName}`));
|
|
280
|
+
console.log(chalk.gray(` Type: ${answers.projectType}`));
|
|
281
|
+
console.log(chalk.gray(` Hostname: ${answers.hostname}`));
|
|
282
|
+
console.log(chalk.gray(` Image: ${answers.image}`));
|
|
283
|
+
console.log(chalk.gray(` HTTP port: ${answers.httpPort}`));
|
|
284
|
+
console.log(chalk.gray(` HTTPS port: ${answers.httpsPort}`));
|
|
285
|
+
console.log(chalk.gray(` Services: ${[
|
|
286
|
+
answers.redis && 'Redis',
|
|
287
|
+
answers.s3proxy && 'S3',
|
|
288
|
+
answers.shibboleth && 'Shibboleth'
|
|
289
|
+
].filter(Boolean).join(', ') || 'None'}\n`));
|
|
290
|
+
|
|
291
|
+
// Show next steps
|
|
292
|
+
console.log(chalk.cyan('📝 Next steps:'));
|
|
293
|
+
console.log(chalk.gray(' 1. Create .env.local with your credentials'));
|
|
294
|
+
console.log(chalk.gray(` 2. Add to /etc/hosts: 127.0.0.1 ${answers.hostname}`));
|
|
295
|
+
|
|
296
|
+
if (answers.projectType === 'sandbox') {
|
|
297
|
+
console.log(chalk.gray(' 3. Edit .buwp-local.json and add volume mappings to the mappings array'));
|
|
298
|
+
console.log(chalk.gray(' 4. Run: buwp-local start'));
|
|
299
|
+
console.log(chalk.gray(`\n Example mapping for .buwp-local.json mappings array:`));
|
|
300
|
+
console.log(chalk.gray(` { "local": "/path/to/plugin", "container": "/var/www/html/wp-content/plugins/my-plugin" }`));
|
|
301
|
+
} else {
|
|
302
|
+
console.log(chalk.gray(' 3. Run: buwp-local start'));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
console.log(chalk.gray(`\n Then access: https://${answers.hostname}\n`));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export default initCommand;
|
package/lib/commands/logs.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Logs command - View logs from the WordPress environment
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { loadConfig } from '../config.js';
|
|
10
10
|
|
|
11
11
|
async function logsCommand(options) {
|
|
12
12
|
console.log(chalk.blue('📋 Viewing logs...\n'));
|
|
@@ -63,4 +63,4 @@ async function logsCommand(options) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
export default logsCommand;
|
package/lib/commands/shell.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Shell command - Open an interactive bash shell in the WordPress container
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { spawnSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { loadConfig } from '../config.js';
|
|
10
10
|
|
|
11
|
-
async function shellCommand(
|
|
11
|
+
async function shellCommand(_options) {
|
|
12
12
|
console.log(chalk.blue('🐚 Opening interactive shell...\n'));
|
|
13
13
|
|
|
14
14
|
try {
|
|
@@ -68,4 +68,4 @@ async function shellCommand(options) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
export default shellCommand;
|
package/lib/commands/start.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Start command - Starts the local WordPress environment
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import { loadConfig, validateConfig } from '../config.js';
|
|
9
|
+
import { generateComposeFile } from '../compose-generator.js';
|
|
10
10
|
|
|
11
11
|
async function startCommand(options) {
|
|
12
12
|
console.log(chalk.blue('🚀 Starting BU WordPress local environment...\n'));
|
|
@@ -95,4 +95,4 @@ async function startCommand(options) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
export default startCommand;
|
package/lib/commands/stop.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Stop command - Stops the local WordPress environment
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { loadConfig } from '../config.js';
|
|
10
10
|
|
|
11
11
|
async function stopCommand() {
|
|
12
12
|
console.log(chalk.blue('🛑 Stopping BU WordPress local environment...\n'));
|
|
@@ -59,4 +59,4 @@ async function stopCommand() {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
export default stopCommand;
|
package/lib/commands/wp.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* WP-CLI proxy command - Execute WP-CLI commands in the WordPress container
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import { loadConfig } from '../config.js';
|
|
10
10
|
|
|
11
|
-
async function wpCommand(args,
|
|
11
|
+
async function wpCommand(args, _options) {
|
|
12
12
|
try {
|
|
13
13
|
const projectPath = process.cwd();
|
|
14
14
|
const composePath = path.join(projectPath, '.buwp-local', 'docker-compose.yml');
|
|
@@ -54,4 +54,4 @@ async function wpCommand(args, options) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
export default wpCommand;
|
package/lib/compose-generator.js
CHANGED
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* Generates docker-compose.yml from configuration using js-yaml
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const os = require('os');
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Generate docker-compose configuration from buwp-local config
|
|
@@ -272,7 +271,7 @@ function generateComposeFile(config, projectPath = process.cwd()) {
|
|
|
272
271
|
return writeComposeFile(composeConfig, composePath);
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
|
|
274
|
+
export {
|
|
276
275
|
generateComposeConfig,
|
|
277
276
|
generateComposeFile,
|
|
278
277
|
writeComposeFile
|
package/lib/config.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Handles loading, validating, and merging configuration from various sources
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import dotenv from 'dotenv';
|
|
9
10
|
|
|
10
11
|
const CONFIG_FILE_NAME = '.buwp-local.json';
|
|
11
12
|
const ENV_FILE_NAME = '.env.local';
|
|
@@ -58,7 +59,7 @@ function loadConfig(projectPath = process.cwd()) {
|
|
|
58
59
|
// Load .env.local if it exists
|
|
59
60
|
if (fs.existsSync(envPath)) {
|
|
60
61
|
try {
|
|
61
|
-
|
|
62
|
+
dotenv.config({ path: envPath });
|
|
62
63
|
envVars = extractEnvVars();
|
|
63
64
|
} catch (err) {
|
|
64
65
|
console.warn(chalk.yellow(`Warning: Failed to load ${ENV_FILE_NAME}: ${err.message}`));
|
|
@@ -282,7 +283,7 @@ function sanitizeProjectName(name) {
|
|
|
282
283
|
.replace(/^-+|-+$/g, ''); // Remove leading/trailing dashes
|
|
283
284
|
}
|
|
284
285
|
|
|
285
|
-
|
|
286
|
+
export {
|
|
286
287
|
loadConfig,
|
|
287
288
|
validateConfig,
|
|
288
289
|
initConfig,
|
package/lib/index.js
CHANGED
|
@@ -3,21 +3,14 @@
|
|
|
3
3
|
* Main library exports
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import * as config from './config.js';
|
|
7
|
+
import * as composeGenerator from './compose-generator.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
generateComposeFile: composeGenerator.generateComposeFile,
|
|
18
|
-
|
|
19
|
-
// Constants
|
|
20
|
-
CONFIG_FILE_NAME: config.CONFIG_FILE_NAME,
|
|
21
|
-
ENV_FILE_NAME: config.ENV_FILE_NAME,
|
|
22
|
-
DEFAULT_CONFIG: config.DEFAULT_CONFIG
|
|
23
|
-
};
|
|
9
|
+
export const loadConfig = config.loadConfig;
|
|
10
|
+
export const validateConfig = config.validateConfig;
|
|
11
|
+
export const initConfig = config.initConfig;
|
|
12
|
+
export const generateComposeConfig = composeGenerator.generateComposeConfig;
|
|
13
|
+
export const generateComposeFile = composeGenerator.generateComposeFile;
|
|
14
|
+
export const CONFIG_FILE_NAME = config.CONFIG_FILE_NAME;
|
|
15
|
+
export const ENV_FILE_NAME = config.ENV_FILE_NAME;
|
|
16
|
+
export const DEFAULT_CONFIG = config.DEFAULT_CONFIG;
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bostonuniversity/buwp-local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Local WordPress development environment for Boston University projects",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "lib/index.js",
|
|
6
7
|
"bin": {
|
|
7
8
|
"buwp-local": "./bin/buwp-local.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
-
"
|
|
12
|
+
"buwp-local": "node bin/buwp-local.js",
|
|
12
13
|
"lint": "eslint ."
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
@@ -21,15 +22,17 @@
|
|
|
21
22
|
"author": "Boston University",
|
|
22
23
|
"license": "ISC",
|
|
23
24
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
25
|
+
"node": ">=18.0.0"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"js-yaml": "^4.1.0",
|
|
28
29
|
"chalk": "^4.1.2",
|
|
29
30
|
"commander": "^11.0.0",
|
|
30
|
-
"dotenv": "^16.3.1"
|
|
31
|
+
"dotenv": "^16.3.1",
|
|
32
|
+
"prompts": "^2.4.2"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
35
|
+
"@types/prompts": "^2.4.9",
|
|
33
36
|
"eslint": "^8.50.0"
|
|
34
37
|
}
|
|
35
38
|
}
|
package/.buwp-local.json
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest",
|
|
3
|
-
"hostname": "jaydub.local",
|
|
4
|
-
"multisite": true,
|
|
5
|
-
"services": {
|
|
6
|
-
"redis": true,
|
|
7
|
-
"s3proxy": true,
|
|
8
|
-
"shibboleth": true
|
|
9
|
-
},
|
|
10
|
-
"ports": {
|
|
11
|
-
"http": 80,
|
|
12
|
-
"https": 443,
|
|
13
|
-
"db": 3306,
|
|
14
|
-
"redis": 6379
|
|
15
|
-
},
|
|
16
|
-
"mappings": [
|
|
17
|
-
{
|
|
18
|
-
"local": "./",
|
|
19
|
-
"container": "/var/www/html/wp-content/plugins/my-plugin",
|
|
20
|
-
"comment": "Map current directory to a plugin location"
|
|
21
|
-
}
|
|
22
|
-
],
|
|
23
|
-
"env": {
|
|
24
|
-
"WP_DEBUG": false,
|
|
25
|
-
"XDEBUG": true
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
# Shared Environment - Practical Examples
|
|
2
|
-
|
|
3
|
-
## Example 1: Team Integration Sandbox
|
|
4
|
-
|
|
5
|
-
### Scenario
|
|
6
|
-
A team of 4 developers working on BU's WordPress site needs a shared local environment with:
|
|
7
|
-
- Custom theme (responsive-framework)
|
|
8
|
-
- Navigation plugin (bu-navigation)
|
|
9
|
-
- Analytics plugin (bu-custom-analytics)
|
|
10
|
-
- Slideshow plugin (bu-slideshow)
|
|
11
|
-
|
|
12
|
-
### Setup
|
|
13
|
-
|
|
14
|
-
**Step 1: Create Primary Configuration Repo**
|
|
15
|
-
```bash
|
|
16
|
-
mkdir -p ~/projects/bu-team-sandbox
|
|
17
|
-
cd ~/projects/bu-team-sandbox
|
|
18
|
-
|
|
19
|
-
# Initialize
|
|
20
|
-
buwp-local config --init
|
|
21
|
-
|
|
22
|
-
# Edit .buwp-local.json:
|
|
23
|
-
{
|
|
24
|
-
"projectName": "bu-team-sandbox",
|
|
25
|
-
"hostname": "buteam.local",
|
|
26
|
-
"multisite": true,
|
|
27
|
-
"services": {
|
|
28
|
-
"redis": true,
|
|
29
|
-
"s3proxy": true,
|
|
30
|
-
"shibboleth": true
|
|
31
|
-
},
|
|
32
|
-
"ports": {
|
|
33
|
-
"http": 80,
|
|
34
|
-
"https": 443,
|
|
35
|
-
"db": 3306,
|
|
36
|
-
"redis": 6379
|
|
37
|
-
},
|
|
38
|
-
"mappings": [],
|
|
39
|
-
"env": {
|
|
40
|
-
"WP_DEBUG": true,
|
|
41
|
-
"WP_DEBUG_LOG": true,
|
|
42
|
-
"XDEBUG": false
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
# Create .env.local with credentials
|
|
47
|
-
# Start environment
|
|
48
|
-
buwp-local start
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**Step 2: Each Developer Adds Their Repo**
|
|
52
|
-
|
|
53
|
-
**Developer 1 - Theme:**
|
|
54
|
-
```bash
|
|
55
|
-
cd ~/projects/responsive-framework
|
|
56
|
-
buwp-local config --init --theme
|
|
57
|
-
|
|
58
|
-
# Edit .buwp-local.json to join shared sandbox:
|
|
59
|
-
{
|
|
60
|
-
"projectName": "bu-team-sandbox", # SHARED!
|
|
61
|
-
"hostname": "buteam.local",
|
|
62
|
-
"multisite": true,
|
|
63
|
-
"services": {
|
|
64
|
-
"redis": true,
|
|
65
|
-
"s3proxy": true,
|
|
66
|
-
"shibboleth": true
|
|
67
|
-
},
|
|
68
|
-
"ports": {
|
|
69
|
-
"http": 80,
|
|
70
|
-
"https": 443,
|
|
71
|
-
"db": 3306,
|
|
72
|
-
"redis": 6379
|
|
73
|
-
},
|
|
74
|
-
"mappings": [
|
|
75
|
-
{
|
|
76
|
-
"local": "./",
|
|
77
|
-
"container": "/var/www/html/wp-content/themes/responsive-framework"
|
|
78
|
-
}
|
|
79
|
-
],
|
|
80
|
-
"env": {
|
|
81
|
-
"WP_DEBUG": true
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# Symlink shared credentials
|
|
86
|
-
ln -s ~/projects/bu-team-sandbox/.env.local .env.local
|
|
87
|
-
|
|
88
|
-
# Add theme to shared environment
|
|
89
|
-
buwp-local start
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Developer 2 - Navigation Plugin:**
|
|
93
|
-
```bash
|
|
94
|
-
cd ~/projects/bu-navigation
|
|
95
|
-
buwp-local config --init --plugin
|
|
96
|
-
|
|
97
|
-
# Edit .buwp-local.json (same projectName):
|
|
98
|
-
{
|
|
99
|
-
"projectName": "bu-team-sandbox",
|
|
100
|
-
"hostname": "buteam.local",
|
|
101
|
-
# ... same ports/services ...
|
|
102
|
-
"mappings": [
|
|
103
|
-
{
|
|
104
|
-
"local": "./",
|
|
105
|
-
"container": "/var/www/html/wp-content/plugins/bu-navigation"
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
ln -s ~/projects/bu-team-sandbox/.env.local .env.local
|
|
111
|
-
buwp-local start
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Developers 3 & 4 repeat for their plugins...**
|
|
115
|
-
|
|
116
|
-
**Result:**
|
|
117
|
-
- All 4 developers access http://buteam.local
|
|
118
|
-
- All plugins and theme are active
|
|
119
|
-
- Shared database with realistic test data
|
|
120
|
-
- Each developer's file changes are live
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## Example 2: Solo Developer - Isolated + Shared
|
|
125
|
-
|
|
126
|
-
### Scenario
|
|
127
|
-
One developer working on `bu-custom-analytics` needs:
|
|
128
|
-
- **Isolated environment** for feature development
|
|
129
|
-
- **Shared environment** for integration testing with other plugins
|
|
130
|
-
|
|
131
|
-
### Setup
|
|
132
|
-
|
|
133
|
-
**Isolated Configuration** (`.buwp-local.json`):
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"projectName": "bu-custom-analytics",
|
|
137
|
-
"hostname": "analytics.local",
|
|
138
|
-
"multisite": true,
|
|
139
|
-
"services": {
|
|
140
|
-
"redis": true,
|
|
141
|
-
"s3proxy": false,
|
|
142
|
-
"shibboleth": false
|
|
143
|
-
},
|
|
144
|
-
"ports": {
|
|
145
|
-
"http": 80,
|
|
146
|
-
"https": 443,
|
|
147
|
-
"db": 3306,
|
|
148
|
-
"redis": 6379
|
|
149
|
-
},
|
|
150
|
-
"mappings": [
|
|
151
|
-
{
|
|
152
|
-
"local": "./",
|
|
153
|
-
"container": "/var/www/html/wp-content/plugins/bu-custom-analytics"
|
|
154
|
-
}
|
|
155
|
-
],
|
|
156
|
-
"env": {
|
|
157
|
-
"WP_DEBUG": true,
|
|
158
|
-
"XDEBUG": true
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**Shared Configuration** (`.buwp-local.shared.json`):
|
|
164
|
-
```json
|
|
165
|
-
{
|
|
166
|
-
"projectName": "bu-integration",
|
|
167
|
-
"hostname": "buint.local",
|
|
168
|
-
"multisite": true,
|
|
169
|
-
"services": {
|
|
170
|
-
"redis": true,
|
|
171
|
-
"s3proxy": true,
|
|
172
|
-
"shibboleth": true
|
|
173
|
-
},
|
|
174
|
-
"ports": {
|
|
175
|
-
"http": 8080,
|
|
176
|
-
"https": 8443,
|
|
177
|
-
"db": 3307,
|
|
178
|
-
"redis": 6380
|
|
179
|
-
},
|
|
180
|
-
"mappings": [
|
|
181
|
-
{
|
|
182
|
-
"local": "./",
|
|
183
|
-
"container": "/var/www/html/wp-content/plugins/bu-custom-analytics"
|
|
184
|
-
}
|
|
185
|
-
],
|
|
186
|
-
"env": {
|
|
187
|
-
"WP_DEBUG": true,
|
|
188
|
-
"XDEBUG": false
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Workflow
|
|
194
|
-
|
|
195
|
-
**Morning - Feature Development (Isolated):**
|
|
196
|
-
```bash
|
|
197
|
-
cd ~/projects/bu-custom-analytics
|
|
198
|
-
|
|
199
|
-
# Use isolated config (default)
|
|
200
|
-
buwp-local start
|
|
201
|
-
|
|
202
|
-
# Develop at http://analytics.local
|
|
203
|
-
# Fast, isolated, clean environment
|
|
204
|
-
# Debug with Xdebug enabled
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**Afternoon - Integration Testing (Shared):**
|
|
208
|
-
```bash
|
|
209
|
-
cd ~/projects/bu-custom-analytics
|
|
210
|
-
|
|
211
|
-
# Stop isolated
|
|
212
|
-
buwp-local stop
|
|
213
|
-
|
|
214
|
-
# Switch to shared config
|
|
215
|
-
cp .buwp-local.shared.json .buwp-local.json
|
|
216
|
-
|
|
217
|
-
# Join shared integration environment
|
|
218
|
-
buwp-local start
|
|
219
|
-
|
|
220
|
-
# Test at http://buint.local:8080
|
|
221
|
-
# Now running with other plugins/theme
|
|
222
|
-
# Test interactions and integration points
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
**End of Day - Back to Isolated:**
|
|
226
|
-
```bash
|
|
227
|
-
# Restore isolated config
|
|
228
|
-
git checkout .buwp-local.json
|
|
229
|
-
|
|
230
|
-
# Back to isolated for tomorrow
|
|
231
|
-
buwp-local destroy # Clean up shared
|
|
232
|
-
buwp-local start # Start fresh isolated
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
---
|
|
236
|
-
|
|
237
|
-
## Example 3: Multi-Environment Matrix
|
|
238
|
-
|
|
239
|
-
### Scenario
|
|
240
|
-
Testing plugin compatibility across different configurations:
|
|
241
|
-
- Modern stack (latest everything)
|
|
242
|
-
- Legacy stack (older versions)
|
|
243
|
-
- Production replica
|
|
244
|
-
|
|
245
|
-
### Setup
|
|
246
|
-
|
|
247
|
-
**Create 3 Separate Environments:**
|
|
248
|
-
|
|
249
|
-
**Environment 1: Modern Stack**
|
|
250
|
-
```bash
|
|
251
|
-
cd ~/projects/bu-custom-analytics
|
|
252
|
-
|
|
253
|
-
# .buwp-local.modern.json:
|
|
254
|
-
{
|
|
255
|
-
"projectName": "modern-stack",
|
|
256
|
-
"hostname": "modern.local",
|
|
257
|
-
"image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest",
|
|
258
|
-
# ... modern settings ...
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
cp .buwp-local.modern.json .buwp-local.json
|
|
262
|
-
buwp-local start
|
|
263
|
-
# Test at http://modern.local
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
**Environment 2: Legacy Stack**
|
|
267
|
-
```bash
|
|
268
|
-
# .buwp-local.legacy.json:
|
|
269
|
-
{
|
|
270
|
-
"projectName": "legacy-stack",
|
|
271
|
-
"hostname": "legacy.local",
|
|
272
|
-
"image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:legacy-tag",
|
|
273
|
-
"ports": {
|
|
274
|
-
"http": 8081,
|
|
275
|
-
"db": 3307
|
|
276
|
-
}
|
|
277
|
-
# ... legacy settings ...
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
cp .buwp-local.legacy.json .buwp-local.json
|
|
281
|
-
buwp-local start
|
|
282
|
-
# Test at http://legacy.local:8081
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Environment 3: Production Replica**
|
|
286
|
-
```bash
|
|
287
|
-
# .buwp-local.prod.json:
|
|
288
|
-
{
|
|
289
|
-
"projectName": "prod-replica",
|
|
290
|
-
"hostname": "prodlocal.local",
|
|
291
|
-
"ports": {
|
|
292
|
-
"http": 8082,
|
|
293
|
-
"db": 3308
|
|
294
|
-
}
|
|
295
|
-
# ... production-like settings ...
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
cp .buwp-local.prod.json .buwp-local.json
|
|
299
|
-
buwp-local start
|
|
300
|
-
# Test at http://prodlocal.local:8082
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
**Result:**
|
|
304
|
-
- All 3 environments running simultaneously
|
|
305
|
-
- Same plugin code in all 3
|
|
306
|
-
- Different WordPress/PHP versions
|
|
307
|
-
- Test compatibility across configurations
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## Example 4: Department-Wide Shared Sandbox
|
|
312
|
-
|
|
313
|
-
### Scenario
|
|
314
|
-
Marketing department (10 people) needs shared sandbox with:
|
|
315
|
-
- Content editors
|
|
316
|
-
- Designers
|
|
317
|
-
- Developers
|
|
318
|
-
- QA testers
|
|
319
|
-
|
|
320
|
-
### Setup
|
|
321
|
-
|
|
322
|
-
**Central Configuration Repository:**
|
|
323
|
-
```bash
|
|
324
|
-
# Shared repo that everyone clones
|
|
325
|
-
git clone https://github.com/bu-ist/bu-marketing-sandbox.git
|
|
326
|
-
cd bu-marketing-sandbox
|
|
327
|
-
|
|
328
|
-
# .buwp-local.json:
|
|
329
|
-
{
|
|
330
|
-
"projectName": "bu-marketing",
|
|
331
|
-
"hostname": "marketing.local",
|
|
332
|
-
"multisite": true,
|
|
333
|
-
"services": {
|
|
334
|
-
"redis": true,
|
|
335
|
-
"s3proxy": true,
|
|
336
|
-
"shibboleth": true
|
|
337
|
-
},
|
|
338
|
-
"ports": {
|
|
339
|
-
"http": 80,
|
|
340
|
-
"https": 443,
|
|
341
|
-
"db": 3306,
|
|
342
|
-
"redis": 6379
|
|
343
|
-
},
|
|
344
|
-
"mappings": [
|
|
345
|
-
{
|
|
346
|
-
"local": "../responsive-framework",
|
|
347
|
-
"container": "/var/www/html/wp-content/themes/responsive-framework"
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
"local": "../bu-slideshow",
|
|
351
|
-
"container": "/var/www/html/wp-content/plugins/bu-slideshow"
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
"local": "../bu-analytics",
|
|
355
|
-
"container": "/var/www/html/wp-content/plugins/bu-analytics"
|
|
356
|
-
}
|
|
357
|
-
],
|
|
358
|
-
"env": {
|
|
359
|
-
"WP_DEBUG": false # Production-like for QA
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
# .env.local (shared credentials via 1Password/LastPass)
|
|
364
|
-
# Includes test user accounts for all team members
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
**Each Team Member:**
|
|
368
|
-
```bash
|
|
369
|
-
# Clone central config
|
|
370
|
-
git clone https://github.com/bu-ist/bu-marketing-sandbox.git ~/projects/bu-marketing-sandbox
|
|
371
|
-
|
|
372
|
-
# Clone the repos to develop
|
|
373
|
-
cd ~/projects
|
|
374
|
-
git clone https://github.com/bu-ist/responsive-framework.git
|
|
375
|
-
git clone https://github.com/bu-ist/bu-slideshow.git
|
|
376
|
-
git clone https://github.com/bu-ist/bu-analytics.git
|
|
377
|
-
|
|
378
|
-
# Start shared environment
|
|
379
|
-
cd ~/projects/bu-marketing-sandbox
|
|
380
|
-
# Add .env.local with shared credentials
|
|
381
|
-
buwp-local start
|
|
382
|
-
|
|
383
|
-
# Access http://marketing.local
|
|
384
|
-
# All team members work in same environment
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
**Benefits:**
|
|
388
|
-
- Designers see live changes to theme
|
|
389
|
-
- Developers see plugin interactions
|
|
390
|
-
- Content editors have realistic environment
|
|
391
|
-
- QA tests actual configuration
|
|
392
|
-
- Everyone shares same database with test content
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## Example 5: Feature Branch Testing
|
|
397
|
-
|
|
398
|
-
### Scenario
|
|
399
|
-
Test a feature branch alongside main branch:
|
|
400
|
-
- `main` branch in production-like environment
|
|
401
|
-
- `feature/new-ui` branch in test environment
|
|
402
|
-
|
|
403
|
-
### Setup
|
|
404
|
-
|
|
405
|
-
**Main Branch Environment:**
|
|
406
|
-
```bash
|
|
407
|
-
cd ~/projects/bu-custom-analytics
|
|
408
|
-
git checkout main
|
|
409
|
-
|
|
410
|
-
# .buwp-local.json (default):
|
|
411
|
-
{
|
|
412
|
-
"projectName": "bu-analytics-main",
|
|
413
|
-
"hostname": "analytics-main.local",
|
|
414
|
-
"ports": {
|
|
415
|
-
"http": 80,
|
|
416
|
-
"db": 3306
|
|
417
|
-
},
|
|
418
|
-
"mappings": [
|
|
419
|
-
{
|
|
420
|
-
"local": "./",
|
|
421
|
-
"container": "/var/www/html/wp-content/plugins/bu-custom-analytics"
|
|
422
|
-
}
|
|
423
|
-
]
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
buwp-local start
|
|
427
|
-
# Test main at http://analytics-main.local
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
**Feature Branch Environment:**
|
|
431
|
-
```bash
|
|
432
|
-
cd ~/projects/bu-custom-analytics
|
|
433
|
-
git checkout feature/new-ui
|
|
434
|
-
|
|
435
|
-
# Change projectName in .buwp-local.json:
|
|
436
|
-
{
|
|
437
|
-
"projectName": "bu-analytics-feature",
|
|
438
|
-
"hostname": "analytics-feature.local",
|
|
439
|
-
"ports": {
|
|
440
|
-
"http": 8080,
|
|
441
|
-
"db": 3307
|
|
442
|
-
},
|
|
443
|
-
"mappings": [
|
|
444
|
-
{
|
|
445
|
-
"local": "./",
|
|
446
|
-
"container": "/var/www/html/wp-content/plugins/bu-custom-analytics"
|
|
447
|
-
}
|
|
448
|
-
]
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
buwp-local start
|
|
452
|
-
# Test feature at http://analytics-feature.local:8080
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
**Result:**
|
|
456
|
-
- Both branches running simultaneously
|
|
457
|
-
- Compare behavior side-by-side
|
|
458
|
-
- Separate databases for each
|
|
459
|
-
- No branch switching needed
|
|
460
|
-
|
|
461
|
-
---
|
|
462
|
-
|
|
463
|
-
## Tips for Shared Environments
|
|
464
|
-
|
|
465
|
-
### 1. Use Configuration Management
|
|
466
|
-
```bash
|
|
467
|
-
# Store configs in version control
|
|
468
|
-
~/projects/
|
|
469
|
-
├── bu-team-sandbox/ # Primary config repo
|
|
470
|
-
│ ├── .buwp-local.json
|
|
471
|
-
│ ├── .env.local.example
|
|
472
|
-
│ └── README.md
|
|
473
|
-
├── plugin-a/
|
|
474
|
-
│ ├── .buwp-local.json # Points to shared
|
|
475
|
-
│ └── .env.local -> ../bu-team-sandbox/.env.local
|
|
476
|
-
└── plugin-b/
|
|
477
|
-
├── .buwp-local.json # Points to shared
|
|
478
|
-
└── .env.local -> ../bu-team-sandbox/.env.local
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### 2. Document Your Shared Environments
|
|
482
|
-
Create a team wiki page:
|
|
483
|
-
```markdown
|
|
484
|
-
# Team Shared Environments
|
|
485
|
-
|
|
486
|
-
## bu-team-sandbox
|
|
487
|
-
- **URL:** http://buteam.local
|
|
488
|
-
- **Purpose:** Integration testing
|
|
489
|
-
- **Includes:** theme + all plugins
|
|
490
|
-
- **Managed by:** @teamlead
|
|
491
|
-
- **Config repo:** github.com/bu-ist/bu-team-sandbox
|
|
492
|
-
|
|
493
|
-
## bu-staging-replica
|
|
494
|
-
- **URL:** http://bustaging.local:8080
|
|
495
|
-
- **Purpose:** Pre-production testing
|
|
496
|
-
- **Includes:** Production plugins only
|
|
497
|
-
- **Managed by:** @qa-lead
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
### 3. Use Consistent Naming
|
|
501
|
-
```bash
|
|
502
|
-
# Good naming convention:
|
|
503
|
-
bu-team-sandbox # Team shared environment
|
|
504
|
-
bu-integration # Integration testing
|
|
505
|
-
bu-staging-replica # Staging mirror
|
|
506
|
-
bu-prod-replica # Production mirror
|
|
507
|
-
|
|
508
|
-
# Avoid:
|
|
509
|
-
sandbox
|
|
510
|
-
test
|
|
511
|
-
local
|
|
512
|
-
dev
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
### 4. Regular Cleanup
|
|
516
|
-
```bash
|
|
517
|
-
# Weekly: Clean shared environments
|
|
518
|
-
buwp-local destroy
|
|
519
|
-
buwp-local start
|
|
520
|
-
# Fresh start with clean database
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### 5. Backup Shared Databases
|
|
524
|
-
```bash
|
|
525
|
-
# Before major changes:
|
|
526
|
-
docker exec bu-team-sandbox-db-1 mysqldump -u wordpress -ppassword wordpress > backup-$(date +%Y%m%d).sql
|
|
527
|
-
|
|
528
|
-
# Restore if needed:
|
|
529
|
-
docker exec -i bu-team-sandbox-db-1 mysql -u wordpress -ppassword wordpress < backup-20251110.sql
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## Troubleshooting Common Issues
|
|
535
|
-
|
|
536
|
-
### Issue: "Port already in use"
|
|
537
|
-
**Cause:** Another shared environment using same ports
|
|
538
|
-
**Solution:** Use different ports in shared config:
|
|
539
|
-
```json
|
|
540
|
-
{
|
|
541
|
-
"ports": {
|
|
542
|
-
"http": 8081,
|
|
543
|
-
"db": 3307
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
### Issue: "My changes don't appear"
|
|
549
|
-
**Cause:** Wrong repo's mapping is active
|
|
550
|
-
**Solution:** Check which repo last ran `start`:
|
|
551
|
-
```bash
|
|
552
|
-
docker compose -p bu-team-sandbox ps
|
|
553
|
-
# See which volumes are mounted
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Issue: "Database has wrong data"
|
|
557
|
-
**Cause:** Another developer made changes
|
|
558
|
-
**Solution:** Communicate or use separate environments
|
|
559
|
-
|
|
560
|
-
### Issue: "Config out of sync"
|
|
561
|
-
**Cause:** Repos have different settings
|
|
562
|
-
**Solution:** Use symlinks or central config repo
|
|
563
|
-
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
## Summary
|
|
567
|
-
|
|
568
|
-
Shared environments enable:
|
|
569
|
-
- ✅ Team collaboration on integrated stack
|
|
570
|
-
- ✅ Integration testing without deploy
|
|
571
|
-
- ✅ Multiple development strategies
|
|
572
|
-
- ✅ Flexible workflows (solo + team)
|
|
573
|
-
- ✅ Realistic production-like testing
|
|
574
|
-
|
|
575
|
-
Use the right tool for the job:
|
|
576
|
-
- **Isolated** = Solo development, fast iteration
|
|
577
|
-
- **Shared** = Integration, team collaboration
|
|
578
|
-
- **Both** = Maximum flexibility
|
|
Binary file
|
|
Binary file
|
|
File without changes
|