@bostonuniversity/buwp-local 0.5.3 → 0.6.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.
@@ -0,0 +1,247 @@
1
+ # Roadmap
2
+
3
+ Strategic direction and development priorities for buwp-local.
4
+
5
+ ## Release History
6
+
7
+ ### ✅ v0.5.x - Keychain & Credential Management (Complete)
8
+
9
+ **Status:** Shipped
10
+
11
+ **Key Features:**
12
+ - macOS Keychain integration for secure credential storage
13
+ - Automatic hex decoding for legacy multiline credentials
14
+ - Credential validation on startup with interactive setup
15
+ - Multi-project support with isolated Docker volumes
16
+ - Smart initialization for plugins, themes, and mu-plugins
17
+
18
+ **Result:** Ready for production use by small development teams.
19
+
20
+ ---
21
+
22
+ ## Current Phase: v0.6.0 - Quality & Robustness
23
+ **Status:** Shipped
24
+
25
+ **Status:** In Progress
26
+ **Target Date:** December 2024
27
+ **Focus:** Foundation improvements before team rollout
28
+
29
+ ### Features in Development
30
+
31
+ 1. **Credential Validation** ✅ (Complete)
32
+ - Validates credentials before starting containers
33
+ - Offers interactive setup if credentials missing
34
+ - Clear error messages and guidance
35
+
36
+ 2. **Documentation Consolidation** ✅ (Complete)
37
+ - Reorganized docs in `/docs` directory
38
+ - Comprehensive guides for all user levels
39
+ - Architecture documentation for contributors
40
+
41
+ 3. **Robust /etc/hosts Detection** ✅ (Complete)
42
+ - Detects if hostname exists in `/etc/hosts` during start
43
+ - Provides copy-paste sudo command if missing
44
+ - Smart messaging (only shows once per project)
45
+ - Non-blocking (user can continue without adding)
46
+
47
+ ### Success Criteria
48
+ - ✅ Zero setup confusion for new users
49
+ - ✅ All common workflows documented
50
+ - ✅ Hostname setup guidance clear and actionable
51
+
52
+ **Status:** All v0.6.0 features complete. Ready for initial user rollout.
53
+
54
+ ---
55
+
56
+ ## Lessons Learned: /etc/hosts Management
57
+
58
+ ### What We Built (v0.6.0)
59
+ The detection-only approach proved to be the right choice:
60
+ - **Non-intrusive** - Checks once, shows clear instructions
61
+ - **No sudo required** - Detection runs without elevated permissions
62
+ - **Smart persistence** - Doesn't nag on every start
63
+ - **User control** - Provides copy-paste command, user decides when to run
64
+
65
+ ### Future Consideration: Automatic Management
66
+
67
+ **Library Identified:** [`hostile`](https://www.npmjs.com/package/hostile) npm package
68
+ - ~500k weekly downloads, well-maintained
69
+ - Cross-platform (macOS, Linux, Windows)
70
+ - Programmatic API: `hostile.set('127.0.0.1', 'hostname')`
71
+ - Sync and async methods available
72
+
73
+ **Why We're Not Implementing It Now:**
74
+
75
+ 1. **Requires sudo** - Password prompt interrupts smooth startup flow
76
+ 2. **Security sensitivity** - Modifying system files needs user trust
77
+ 3. **Current solution works** - Detection + copy-paste is clear and effective
78
+ 4. **Premature optimization** - Need real user feedback first
79
+
80
+ **If We Revisit (v0.8.0+):**
81
+
82
+ Implementation would be straightforward:
83
+ ```javascript
84
+ import hostile from 'hostile';
85
+
86
+ // Add hostname (requires sudo, prompts for password)
87
+ hostile.set('127.0.0.1', config.hostname, (err) => {
88
+ if (err) {
89
+ // Fall back to manual instructions
90
+ }
91
+ });
92
+
93
+ // Remove on destroy
94
+ hostile.remove('127.0.0.1', config.hostname);
95
+ ```
96
+
97
+ **Decision criteria for future:**
98
+ - Do >50% of users request automatic management?
99
+ - Is sudo prompt acceptable to most users?
100
+ - Would opt-in flag (`autoManageHosts: true`) address concerns?
101
+ - Does it meaningfully improve onboarding over current approach?
102
+
103
+ **Current recommendation:** Gather feedback from Stage 1 users (2-3 developers) in December 2024. If hostname setup proves to be a friction point, revisit automatic management in Q1 2025.
104
+
105
+ ---
106
+
107
+ ## Next Phase: v0.7.0 - Developer Experience
108
+
109
+ **Status:** Planned
110
+ **Timeline:** January 2025 (after team onboarding feedback)
111
+ **Focus:** Ease of use and visibility
112
+
113
+ ### Potential Features
114
+
115
+ - **Xdebug Integration**
116
+ - Enable Xdebug in containers
117
+ - Documentation for IDE setup
118
+ - Configuration options in `.buwp-local.json`
119
+
120
+ - **Security Checks**
121
+ - Check database access on db port (e.g. `localhost:3306`)
122
+ - Consider more stringent default database passwords
123
+
124
+ - **Improved Windows and Linux support**
125
+ - Multiplatform /etc/hosts hostname management
126
+ - Evaluate credential storage solutions for non-macOS platforms
127
+
128
+ - **Project Status & Listing**
129
+ - View all running projects: `buwp-local list`
130
+ - Quick status checks: `buwp-local status`
131
+
132
+ - **Health Checks**
133
+ - Verify services are running properly
134
+ - Database connectivity validation
135
+ - Clear diagnostics on failures
136
+
137
+ - **Improved Error Messages**
138
+ - Docker startup failures → actionable solutions
139
+ - Credential issues → clear next steps
140
+ - Port conflicts → suggest alternatives
141
+
142
+ ### Prioritization
143
+ Will be informed by feedback from initial 2-3 users and actual pain points observed during rollout.
144
+
145
+ ---
146
+
147
+ ## Future Phases: v0.8.0+
148
+
149
+ **Status:** Conceptual
150
+ **Timeline:** TBD based on team feedback
151
+
152
+ ### Potential Features (Lower Priority)
153
+
154
+ - **SSL Certificate Generation** - Local HTTPS with mkcert
155
+ - **Database Backup/Restore** - Simplified snapshots and recovery
156
+ - **Performance Monitoring** - Real-time resource usage tracking
157
+ - **Team Configuration Sync** - Share project configurations across team
158
+ - **Cross-Platform Support** - Windows WSL2 and Linux credential storage
159
+
160
+ **Note:** Automatic `/etc/hosts` management deferred pending user feedback. See "Lessons Learned" section above for details on the `hostile` library approach.
161
+
162
+ ---
163
+
164
+ ## Roadmap by User Stage
165
+
166
+ ### Stage 1: Initial Users (Now)
167
+ **Users:** 1-3 developers
168
+ **Goal:** Validate core functionality
169
+ **Release:** v0.6.0
170
+ **Focus:** Robustness, clear setup, good documentation
171
+
172
+ ### Stage 2: Team Rollout (Q1 2025)
173
+ **Users:** 10-15 developers
174
+ **Goal:** Find and fix real-world issues
175
+ **Release:** v0.7.0+
176
+ **Focus:** Developer experience, error handling
177
+
178
+ ### Stage 3: Broader Adoption (Q2 2025+)
179
+ **Users:** 20+ developers
180
+ **Goal:** Self-service onboarding
181
+ **Release:** v1.0.0+
182
+ **Focus:** Advanced features, automation
183
+
184
+ ---
185
+
186
+ ## Technical Debt
187
+
188
+ ### Testing
189
+ - Unit tests for core modules (config, keychain, docker-compose)
190
+ - Integration tests for Docker operations
191
+ - E2E tests for full workflows
192
+
193
+ ### Documentation
194
+ - Troubleshooting guide for common issues
195
+ - FAQ section
196
+ - Video tutorials for setup
197
+
198
+ ### Quality
199
+ - Standardized error handling across commands
200
+ - Centralized logging system
201
+ - Better validation for edge cases
202
+
203
+ ---
204
+
205
+ ## Decision Framework
206
+
207
+ Features will be prioritized based on:
208
+
209
+ 1. **User Feedback** - What's actually blocking users?
210
+ 2. **Adoption Impact** - Does it help onboard new users?
211
+ 3. **Implementation Effort** - Can it be done quickly?
212
+ 4. **Maintenance Burden** - Will it create ongoing support overhead?
213
+
214
+ ---
215
+
216
+ ## How to Use This Roadmap
217
+
218
+ **For Users:**
219
+ - See what's coming and when
220
+ - Understand current release focus
221
+ - Provide feedback on priorities
222
+
223
+ **For Contributors:**
224
+ - Clear phases and timeline
225
+ - See future enhancement opportunities
226
+ - Link to technical details in [ARCHITECTURE.md](ARCHITECTURE.md)
227
+
228
+ ---
229
+
230
+ ## Technical Details
231
+
232
+ For detailed information about planned features, implementation approaches, and architecture decisions, see:
233
+
234
+ - **[ARCHITECTURE.md](ARCHITECTURE.md)** - Planned Features section
235
+ - **[CHANGELOG.md](CHANGELOG.md)** - Version history and release notes
236
+
237
+ ---
238
+
239
+ ## Feedback
240
+
241
+ Have ideas for the roadmap? Questions about priorities?
242
+
243
+ - **Early users:** Direct feedback via chat/email
244
+ - **GitHub issues:** Feature requests and suggestions
245
+ - **Team meetings:** Quarterly roadmap reviews
246
+
247
+ Current focus: Getting v0.6.0 stable and ready for initial rollout to 2-3 users. 🚀
@@ -295,7 +295,8 @@ async function initCommand(options) {
295
295
  // Show next steps
296
296
  console.log(chalk.cyan('📝 Next steps:'));
297
297
  console.log(chalk.gray(' 1. Check keychain credentials or add .env.local with your credentials'));
298
- console.log(chalk.gray(` 2. Add to /etc/hosts: 127.0.0.1 ${answers.hostname}`));
298
+ console.log(chalk.gray(' 2. Add to /etc/hosts with this command:'));
299
+ console.log(chalk.cyan(` echo "127.0.0.1 ${answers.hostname}" | sudo tee -a /etc/hosts\n`));
299
300
 
300
301
  if (answers.projectType === 'sandbox') {
301
302
  console.log(chalk.gray(' 3. Edit .buwp-local.json and add volume mappings to the mappings array'));
@@ -635,7 +635,7 @@ function showHelp() {
635
635
  console.log(' # Interactive setup (prompts for each credential)');
636
636
  console.log(' buwp-local keychain setup\n');
637
637
  console.log(' # Bulk import from JSON file');
638
- console.log(' buwp-local keychain setup --file .buwp-credentials.json\n');
638
+ console.log(' buwp-local keychain setup --file buwp-local-credentials.json\n');
639
639
  console.log(' # Interactive prompt for single-line credential');
640
640
  console.log(' buwp-local keychain set WORDPRESS_DB_PASSWORD\n');
641
641
  console.log(' # Set single-line credential directly');
@@ -6,8 +6,136 @@ import chalk from 'chalk';
6
6
  import { execSync } from 'child_process';
7
7
  import path from 'path';
8
8
  import fs from 'fs';
9
+ import prompts from 'prompts';
9
10
  import { loadConfig, validateConfig, ENV_FILE_NAME, loadKeychainCredentials, createSecureTempEnvFile, secureDeleteTempEnvFile } from '../config.js';
10
11
  import { generateComposeFile } from '../compose-generator.js';
12
+ import keychainCommand from './keychain.js';
13
+
14
+ /**
15
+ * Required credentials that must be present for WordPress to function
16
+ */
17
+ const REQUIRED_CREDENTIALS = [
18
+ 'WORDPRESS_DB_PASSWORD',
19
+ 'DB_ROOT_PASSWORD'
20
+ ];
21
+
22
+ /**
23
+ * Check if hostname exists in /etc/hosts
24
+ * @param {string} hostname - Hostname to check
25
+ * @returns {object} { found: boolean, error?: string }
26
+ */
27
+ function checkHostsFile(hostname) {
28
+ try {
29
+ const hostsPath = '/etc/hosts';
30
+ const content = fs.readFileSync(hostsPath, 'utf8');
31
+ const lines = content.split('\n');
32
+
33
+ // Check each line for hostname
34
+ for (const line of lines) {
35
+ // Skip comments and empty lines
36
+ if (line.trim().startsWith('#') || !line.trim()) continue;
37
+
38
+ // Parse: "127.0.0.1 hostname.local" or "127.0.0.1\thostname.local"
39
+ const parts = line.trim().split(/\s+/);
40
+ if (parts.length >= 2) {
41
+ // Check if hostname appears in any position after IP
42
+ const hostnames = parts.slice(1);
43
+ if (hostnames.includes(hostname)) {
44
+ return { found: true };
45
+ }
46
+ }
47
+ }
48
+
49
+ return { found: false };
50
+ } catch (error) {
51
+ // /etc/hosts not readable (unlikely on macOS)
52
+ return { found: false, error: error.message };
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Check if we've already shown the hosts warning for this project
58
+ * @param {string} projectPath - Project directory path
59
+ * @returns {boolean} true if warning already shown
60
+ */
61
+ function hasShownHostsWarning(projectPath) {
62
+ const warningFile = path.join(projectPath, '.buwp-local', '.hosts-warning-shown');
63
+ return fs.existsSync(warningFile);
64
+ }
65
+
66
+ /**
67
+ * Mark that we've shown the hosts warning for this project
68
+ * @param {string} projectPath - Project directory path
69
+ */
70
+ function markHostsWarningShown(projectPath) {
71
+ const dir = path.join(projectPath, '.buwp-local');
72
+ fs.mkdirSync(dir, { recursive: true });
73
+ const warningFile = path.join(dir, '.hosts-warning-shown');
74
+ fs.writeFileSync(warningFile, Date.now().toString());
75
+ }
76
+
77
+ /**
78
+ * Check if required credentials are available in keychain or .env.local
79
+ * @param {object} keychainCreds - Credentials loaded from keychain
80
+ * @param {string} envFilePath - Path to .env.local file
81
+ * @returns {object} { isValid, missing }
82
+ */
83
+ function validateCredentials(keychainCreds, envFilePath) {
84
+ const missing = [];
85
+
86
+ for (const key of REQUIRED_CREDENTIALS) {
87
+ // Check if credential exists in keychain
88
+ if (keychainCreds[key]) {
89
+ continue; // Found in keychain
90
+ }
91
+
92
+ // Check if credential exists in .env.local
93
+ if (fs.existsSync(envFilePath)) {
94
+ const envContent = fs.readFileSync(envFilePath, 'utf8');
95
+ if (envContent.includes(`${key}=`)) {
96
+ continue; // Found in .env.local
97
+ }
98
+ }
99
+
100
+ // Credential not found anywhere
101
+ missing.push(key);
102
+ }
103
+
104
+ return {
105
+ isValid: missing.length === 0,
106
+ missing
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Prompt user to set up missing credentials
112
+ * @param {string[]} missingCreds - List of missing credential keys
113
+ * @returns {Promise<boolean>} true if user wants to set up, false otherwise
114
+ */
115
+ async function promptCredentialSetup(missingCreds) {
116
+ console.log(chalk.yellow('\n⚠️ Missing Required Credentials\n'));
117
+ console.log(chalk.gray('The following credentials are not configured:\n'));
118
+ missingCreds.forEach(key => {
119
+ console.log(chalk.gray(` - ${key}`));
120
+ });
121
+ console.log('');
122
+
123
+ console.log(chalk.cyan('You can set up credentials in two ways:\n'));
124
+ console.log(chalk.white(' 1. Use macOS Keychain (recommended):'));
125
+ console.log(chalk.gray(' npx buwp-local keychain setup\n'));
126
+ console.log(chalk.white(' 2. Create .env.local file in your project:'));
127
+ console.log(chalk.gray(' cp .env.local.example .env.local'));
128
+ console.log(chalk.gray(' # Edit .env.local with your credentials\n'));
129
+
130
+ const { shouldSetup } = await prompts({
131
+ type: 'confirm',
132
+ name: 'shouldSetup',
133
+ message: 'Would you like to set up credentials now using Keychain?',
134
+ initial: true
135
+ });
136
+
137
+ return shouldSetup;
138
+ }
11
139
 
12
140
  async function startCommand(options) {
13
141
  console.log(chalk.blue('🚀 Starting BU WordPress local environment...\n'));
@@ -43,6 +171,68 @@ async function startCommand(options) {
43
171
  process.exit(1);
44
172
  }
45
173
 
174
+ // Load and validate credentials early
175
+ const envFilePath = path.join(projectPath, ENV_FILE_NAME);
176
+ const keychainCredentials = loadKeychainCredentials();
177
+ const credentialValidation = validateCredentials(keychainCredentials, envFilePath);
178
+
179
+ if (!credentialValidation.isValid) {
180
+ const shouldSetup = await promptCredentialSetup(credentialValidation.missing);
181
+
182
+ if (shouldSetup) {
183
+ // Run keychain setup command
184
+ console.log(chalk.gray('\nLaunching Keychain Setup...\n'));
185
+ await keychainCommand('setup', [], {});
186
+
187
+ // Reload credentials after setup
188
+ const reloadedCreds = loadKeychainCredentials();
189
+ const revalidation = validateCredentials(reloadedCreds, envFilePath);
190
+
191
+ if (!revalidation.isValid) {
192
+ console.log(chalk.red('\n❌ Setup incomplete. Required credentials still missing:\n'));
193
+ revalidation.missing.forEach(key => {
194
+ console.log(chalk.red(` - ${key}`));
195
+ });
196
+ console.log(chalk.gray('\nPlease complete setup or add credentials to .env.local\n'));
197
+ process.exit(1);
198
+ }
199
+ } else {
200
+ console.log(chalk.red('\n❌ Cannot start without required credentials.\n'));
201
+ process.exit(1);
202
+ }
203
+ } else {
204
+ console.log(chalk.green('✓ Required credentials validated\n'));
205
+ }
206
+
207
+ // Check /etc/hosts for hostname entry
208
+ if (!hasShownHostsWarning(projectPath)) {
209
+ const hostsCheck = checkHostsFile(config.hostname);
210
+
211
+ if (!hostsCheck.found) {
212
+ console.log(chalk.yellow('⚠️ Hostname not found in /etc/hosts\n'));
213
+ console.log(`Your site won't be accessible at ${chalk.cyan(`http://${config.hostname}`)}`);
214
+ console.log('until you add this entry:\n');
215
+ console.log(chalk.green(` 127.0.0.1 ${config.hostname}\n`));
216
+ console.log('Run this command to add it (copy and paste, and then enter your password):\n');
217
+ console.log(chalk.cyan(` echo "127.0.0.1 ${config.hostname}" | sudo tee -a /etc/hosts\n`));
218
+
219
+ const { continueStart } = await prompts({
220
+ type: 'confirm',
221
+ name: 'continueStart',
222
+ message: 'Continue starting containers?',
223
+ initial: true
224
+ });
225
+
226
+ if (!continueStart) {
227
+ console.log(chalk.gray('\nStart cancelled. Add hostname to /etc/hosts and try again.'));
228
+ process.exit(0);
229
+ }
230
+
231
+ // Mark warning as shown so we don't annoy user on every start
232
+ markHostsWarningShown(projectPath);
233
+ }
234
+ }
235
+
46
236
  // Generate docker-compose.yml
47
237
  console.log(chalk.gray('Generating docker-compose.yml...'));
48
238
  const composePath = generateComposeFile(config, projectPath);
@@ -63,12 +253,12 @@ async function startCommand(options) {
63
253
 
64
254
  // Load keychain credentials and create secure temp env file if available
65
255
  let tempEnvPath = null;
66
- const keychainCredentials = loadKeychainCredentials();
67
- const keychainCredCount = Object.keys(keychainCredentials).length;
256
+ const finalKeychainCredentials = loadKeychainCredentials();
257
+ const keychainCredCount = Object.keys(finalKeychainCredentials).length;
68
258
 
69
259
  if (keychainCredCount > 0) {
70
260
  try {
71
- tempEnvPath = createSecureTempEnvFile(keychainCredentials, projectName);
261
+ tempEnvPath = createSecureTempEnvFile(finalKeychainCredentials, projectName);
72
262
  console.log(chalk.gray(`✓ Loaded ${keychainCredCount} credentials from keychain\n`));
73
263
  } catch (err) {
74
264
  console.warn(chalk.yellow(`⚠️ Could not load keychain credentials: ${err.message}`));
@@ -76,7 +266,6 @@ async function startCommand(options) {
76
266
  }
77
267
 
78
268
  // Check if .env.local exists and build env-file flags
79
- const envFilePath = path.join(projectPath, ENV_FILE_NAME);
80
269
  const envFileFlag = fs.existsSync(envFilePath) ? `--env-file ${envFilePath}` : '';
81
270
  const tempEnvFileFlag = tempEnvPath ? `--env-file ${tempEnvPath}` : '';
82
271
 
@@ -111,10 +300,6 @@ async function startCommand(options) {
111
300
  console.log(chalk.white(' buwp-local stop - Stop environment'));
112
301
  console.log(chalk.white(' buwp-local destroy - Remove environment\n'));
113
302
 
114
- // Reminder about /etc/hosts
115
- console.log(chalk.yellow('⚠️ Remember to add this to your /etc/hosts file:'));
116
- console.log(chalk.white(` 127.0.0.1 ${config.hostname}\n`));
117
-
118
303
  } catch (err) {
119
304
  console.error(chalk.red('\n❌ Error:'), err.message);
120
305
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bostonuniversity/buwp-local",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Local WordPress development environment for Boston University projects",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/readme.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  This repository contains resources and instructions for setting up a local WordPress development environment for Boston University projects. It uses the BU WordPress container image and provides the additional resources needed to run it locally with Docker.
4
4
 
5
+ The package can be installed in a specific repo for development of that one package, or standalone for more general use, mapping local code into the container as needed.
5
6
 
6
7
  ## Quickstart for plugin or theme development
7
8
 
@@ -84,3 +85,19 @@ Your local WordPress site should now be accessible at the hostname you configure
84
85
  ```
85
86
 
86
87
  This will download the latest snapshot from the specified source and import it into your local WordPress environment.
88
+
89
+ ## Documentation
90
+
91
+ - 📘 [Getting Started Guide](docs/GETTING_STARTED.md)
92
+ - 📖 [Command Reference](docs/COMMANDS.md)
93
+ - 🔐 [Credential Management](docs/CREDENTIALS.md)
94
+ - 🏗️ [Architecture](docs/ARCHITECTURE.md) (for contributors)
95
+
96
+ ## Features
97
+
98
+ - ✅ One-time credential setup with macOS Keychain
99
+ - ✅ Isolated environments for multiple projects
100
+ - ✅ Pre-configured BU infrastructure (Shibboleth, S3, Redis)
101
+ - ✅ Smart initialization for plugins, themes, and mu-plugins
102
+ - ✅ Volume mapping for live code sync
103
+ - ✅ Xdebug support for step debugging