@bostonuniversity/buwp-local 0.5.3 → 0.6.1
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/ROADMAP.md +2 -1
- package/docs/ARCHITECTURE.md +838 -0
- package/docs/CHANGELOG.md +149 -0
- package/docs/COMMANDS.md +462 -0
- package/docs/CREDENTIALS.md +484 -0
- package/docs/GETTING_STARTED.md +234 -0
- package/docs/MIGRATION_FROM_VM.md +237 -0
- package/docs/MULTI_PROJECT.md +513 -0
- package/docs/ROADMAP.md +237 -0
- package/lib/commands/destroy.js +1 -1
- package/lib/commands/init.js +2 -1
- package/lib/commands/keychain.js +1 -1
- package/lib/commands/start.js +233 -8
- package/package.json +1 -1
- package/readme.md +26 -0
- /package/{IMPLEMENTATION_NOTES_V0.5.0_PHASE3.md → docs/archive/IMPLEMENTATION_NOTES_V0.5.0_PHASE3.md} +0 -0
- /package/{IMPLEMENTATION_SUMMARY.md → docs/archive/IMPLEMENTATION_SUMMARY.md} +0 -0
- /package/{KEYCHAIN_IMPLEMENTATION.md → docs/archive/KEYCHAIN_IMPLEMENTATION.md} +0 -0
- /package/{macos-keychain-notes.md → docs/archive/macos-keychain-notes.md} +0 -0
package/docs/ROADMAP.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
## v0.6.0 - Quality & Robustness
|
|
23
|
+
**Status:** Shipped
|
|
24
|
+
**Focus:** Foundation improvements before team rollout
|
|
25
|
+
|
|
26
|
+
### Features in Development
|
|
27
|
+
|
|
28
|
+
1. **Credential Validation** ✅ (Complete)
|
|
29
|
+
- Validates credentials before starting containers
|
|
30
|
+
- Offers interactive setup if credentials missing
|
|
31
|
+
- Clear error messages and guidance
|
|
32
|
+
|
|
33
|
+
2. **Documentation Consolidation** ✅ (Complete)
|
|
34
|
+
- Reorganized docs in `/docs` directory
|
|
35
|
+
- Comprehensive guides for all user levels
|
|
36
|
+
- Architecture documentation for contributors
|
|
37
|
+
|
|
38
|
+
3. **Robust /etc/hosts Detection** ✅ (Complete)
|
|
39
|
+
- Detects if hostname exists in `/etc/hosts` during start
|
|
40
|
+
- Provides copy-paste sudo command if missing
|
|
41
|
+
- Smart messaging (only shows once per project)
|
|
42
|
+
- Non-blocking (user can continue without adding)
|
|
43
|
+
|
|
44
|
+
### Success Criteria
|
|
45
|
+
- ✅ Zero setup confusion for new users
|
|
46
|
+
- ✅ All common workflows documented
|
|
47
|
+
- ✅ Hostname setup guidance clear and actionable
|
|
48
|
+
|
|
49
|
+
**Status:** All v0.6.0 features complete. Ready for initial user rollout.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Lessons Learned: /etc/hosts Management
|
|
54
|
+
|
|
55
|
+
### What We Built (v0.6.0)
|
|
56
|
+
The detection-only approach proved to be the right choice:
|
|
57
|
+
- **Non-intrusive** - Checks once, shows clear instructions
|
|
58
|
+
- **No sudo required** - Detection runs without elevated permissions
|
|
59
|
+
- **Smart persistence** - Doesn't nag on every start
|
|
60
|
+
- **User control** - Provides copy-paste command, user decides when to run
|
|
61
|
+
|
|
62
|
+
### Future Consideration: Automatic Management
|
|
63
|
+
|
|
64
|
+
**Library Identified:** [`hostile`](https://www.npmjs.com/package/hostile) npm package
|
|
65
|
+
- ~500k weekly downloads, well-maintained
|
|
66
|
+
- Cross-platform (macOS, Linux, Windows)
|
|
67
|
+
- Programmatic API: `hostile.set('127.0.0.1', 'hostname')`
|
|
68
|
+
- Sync and async methods available
|
|
69
|
+
|
|
70
|
+
**Why We're Not Implementing It Now:**
|
|
71
|
+
|
|
72
|
+
1. **Requires sudo** - Password prompt interrupts smooth startup flow
|
|
73
|
+
2. **Security sensitivity** - Modifying system files needs user trust
|
|
74
|
+
3. **Current solution works** - Detection + copy-paste is clear and effective
|
|
75
|
+
4. **Premature optimization** - Need real user feedback first
|
|
76
|
+
|
|
77
|
+
**If We Revisit (v0.8.0+):**
|
|
78
|
+
|
|
79
|
+
Implementation would be straightforward:
|
|
80
|
+
```javascript
|
|
81
|
+
import hostile from 'hostile';
|
|
82
|
+
|
|
83
|
+
// Add hostname (requires sudo, prompts for password)
|
|
84
|
+
hostile.set('127.0.0.1', config.hostname, (err) => {
|
|
85
|
+
if (err) {
|
|
86
|
+
// Fall back to manual instructions
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Remove on destroy
|
|
91
|
+
hostile.remove('127.0.0.1', config.hostname);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Decision criteria for future:**
|
|
95
|
+
- Do >50% of users request automatic management?
|
|
96
|
+
- Is sudo prompt acceptable to most users?
|
|
97
|
+
- Would opt-in flag (`autoManageHosts: true`) address concerns?
|
|
98
|
+
- Does it meaningfully improve onboarding over current approach?
|
|
99
|
+
|
|
100
|
+
**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.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Incremental Functional Improvements: v0.6.1
|
|
105
|
+
**Status:** Currently Ongoing
|
|
106
|
+
**Focus:** Enhancements based on initial user experience
|
|
107
|
+
|
|
108
|
+
- **Container Registry Assistance**
|
|
109
|
+
- Guide users on setting up access to private registries (ghcr.io)
|
|
110
|
+
- Automatic check for registry login or existing image on `start`
|
|
111
|
+
|
|
112
|
+
- **Basic docs on existing Xdebug features**
|
|
113
|
+
- Quickstart guide for enabling and using Xdebug in containers
|
|
114
|
+
|
|
115
|
+
- **Volume Mapping pattern guide**
|
|
116
|
+
- Documentation on different volume mapping strategies for various development workflows
|
|
117
|
+
|
|
118
|
+
## Next Phase: v0.7.0 - Developer Experience
|
|
119
|
+
|
|
120
|
+
**Status:** Planned
|
|
121
|
+
**Timeline:** After team onboarding feedback
|
|
122
|
+
**Focus:** Ease of use and visibility
|
|
123
|
+
|
|
124
|
+
### Potential Features
|
|
125
|
+
|
|
126
|
+
- **Security Checks**
|
|
127
|
+
- Check database access on db port (e.g. `localhost:3306`)
|
|
128
|
+
- Consider more stringent default database passwords
|
|
129
|
+
- The database can have restricted content in it, so we need to ensure that users are aware of this and take appropriate measures.
|
|
130
|
+
|
|
131
|
+
- **Xdebug Integration**
|
|
132
|
+
- Command to help generate Xdebug configuration for IDEs (VSCode, Zed)
|
|
133
|
+
- Documentation on usage patterns
|
|
134
|
+
|
|
135
|
+
- **Improved Windows and Linux support**
|
|
136
|
+
- Multiplatform /etc/hosts hostname management
|
|
137
|
+
- Evaluate credential storage solutions for non-macOS platforms (https://www.npmjs.com/package/keytar)
|
|
138
|
+
|
|
139
|
+
- **Project Status & Listing**
|
|
140
|
+
- Central tracking of all buwp-local projects in `~/.buwp-local/projects.json`
|
|
141
|
+
- View all running projects: `buwp-local list`
|
|
142
|
+
- Quick status checks: `buwp-local status`
|
|
143
|
+
|
|
144
|
+
- **Health Checks**
|
|
145
|
+
- Verify services are running properly
|
|
146
|
+
- Database connectivity validation
|
|
147
|
+
- Clear diagnostics on failures
|
|
148
|
+
|
|
149
|
+
- **Improved Error Messages**
|
|
150
|
+
- Docker startup failures → actionable solutions
|
|
151
|
+
- Credential issues → clear next steps
|
|
152
|
+
- Port conflicts → suggest alternatives
|
|
153
|
+
|
|
154
|
+
- **Multi project experience**
|
|
155
|
+
- There is a problem when starting a new project when an existing project exists in docker but is stopped. When starting the new project, docker first starts the container for the stopped project for unknown reasons. If the new project uses the same ports, this causes conflicts. Need to investigate and resolve, projects should be isolated and not interfere with each other.
|
|
156
|
+
|
|
157
|
+
- **Docker Volume management assistant**
|
|
158
|
+
- listing and cleanup of unused volumes
|
|
159
|
+
|
|
160
|
+
### Prioritization
|
|
161
|
+
Will be informed by feedback from initial 2-3 users and actual pain points observed during rollout.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Future Phases: v0.8.0+
|
|
166
|
+
|
|
167
|
+
**Status:** Conceptual
|
|
168
|
+
**Timeline:** TBD based on team feedback
|
|
169
|
+
|
|
170
|
+
### Potential Features (Lower Priority)
|
|
171
|
+
|
|
172
|
+
- **SSL Certificate Generation** - Local HTTPS with mkcert
|
|
173
|
+
- **Database Backup/Restore** - Simplified snapshots and recovery
|
|
174
|
+
- **Performance Monitoring** - Real-time resource usage tracking
|
|
175
|
+
- **Team Configuration Sync** - Share project configurations across team
|
|
176
|
+
- **Cross-Platform Support** - Windows WSL2 and Linux credential storage
|
|
177
|
+
|
|
178
|
+
**Note:** Automatic `/etc/hosts` management deferred pending user feedback. See "Lessons Learned" section above for details on the `hostile` library approach.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Roadmap by User Stage
|
|
183
|
+
|
|
184
|
+
### Stage 1: Initial Users
|
|
185
|
+
**Users:** 1-3 developers
|
|
186
|
+
**Goal:** Validate core functionality
|
|
187
|
+
**Release:** v0.6.0
|
|
188
|
+
**Focus:** Robustness, clear setup, good documentation
|
|
189
|
+
|
|
190
|
+
### Stage 2: Team Rollout
|
|
191
|
+
**Users:** 10-15 developers
|
|
192
|
+
**Goal:** Find and fix real-world issues
|
|
193
|
+
**Release:** v0.7.0+
|
|
194
|
+
**Focus:** Developer experience, error handling
|
|
195
|
+
|
|
196
|
+
### Stage 3: Broader Adoption
|
|
197
|
+
**Users:** 20+ developers
|
|
198
|
+
**Goal:** Self-service onboarding
|
|
199
|
+
**Release:** v1.0.0+
|
|
200
|
+
**Focus:** Advanced features, automation
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Technical Debt
|
|
205
|
+
|
|
206
|
+
### Testing
|
|
207
|
+
- Unit tests for core modules (config, keychain, docker-compose)
|
|
208
|
+
- Integration tests for Docker operations
|
|
209
|
+
- E2E tests for full workflows
|
|
210
|
+
|
|
211
|
+
### Documentation
|
|
212
|
+
- Troubleshooting guide for common issues
|
|
213
|
+
- FAQ section
|
|
214
|
+
- Video tutorials for setup
|
|
215
|
+
|
|
216
|
+
### Quality
|
|
217
|
+
- Standardized help for all CLI commands
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Decision Framework
|
|
222
|
+
|
|
223
|
+
Features will be prioritized based on:
|
|
224
|
+
|
|
225
|
+
1. **User Feedback** - What's actually blocking users?
|
|
226
|
+
2. **Adoption Impact** - Does it help onboard new users?
|
|
227
|
+
3. **Implementation Effort** - Can it be done quickly?
|
|
228
|
+
4. **Maintenance Burden** - Will it create ongoing support overhead?
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Technical Details
|
|
233
|
+
|
|
234
|
+
For detailed information about planned features, implementation approaches, and architecture decisions, see:
|
|
235
|
+
|
|
236
|
+
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Planned Features section
|
|
237
|
+
- **[CHANGELOG.md](CHANGELOG.md)** - Version history and release notes
|
package/lib/commands/destroy.js
CHANGED
|
@@ -85,7 +85,7 @@ function confirmDestroy(projectName) {
|
|
|
85
85
|
console.log(chalk.yellow(`This will destroy project: ${chalk.bold(projectName)}`));
|
|
86
86
|
console.log(chalk.yellow(' - Stop all containers'));
|
|
87
87
|
console.log(chalk.yellow(' - Remove all containers'));
|
|
88
|
-
console.log(chalk.yellow(' - Delete all volumes (including database data)\n'));
|
|
88
|
+
console.log(chalk.yellow(' - Delete all volumes (including database data) (except maybe not, please fix)\n'));
|
|
89
89
|
|
|
90
90
|
rl.question(chalk.red('Are you sure you want to continue? (yes/no): '), (answer) => {
|
|
91
91
|
rl.close();
|
package/lib/commands/init.js
CHANGED
|
@@ -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(
|
|
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'));
|
package/lib/commands/keychain.js
CHANGED
|
@@ -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
|
|
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');
|
package/lib/commands/start.js
CHANGED
|
@@ -6,8 +6,164 @@ 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
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check if image is accessible
|
|
141
|
+
async function checkImageAccess(imageName) {
|
|
142
|
+
try {
|
|
143
|
+
// Try to inspect image locally
|
|
144
|
+
execSync(`docker image inspect ${imageName}`, { stdio: 'pipe' });
|
|
145
|
+
return true; // Image exists locally
|
|
146
|
+
} catch {
|
|
147
|
+
// Image doesn't exist, try to pull
|
|
148
|
+
try {
|
|
149
|
+
console.log(chalk.gray(`Checking access to ${imageName}...`));
|
|
150
|
+
execSync(`docker pull ${imageName}`, { stdio: 'pipe' });
|
|
151
|
+
return true;
|
|
152
|
+
} catch (pullError) {
|
|
153
|
+
if (pullError.message.includes('unauthorized') ||
|
|
154
|
+
pullError.message.includes('denied')) {
|
|
155
|
+
console.log(chalk.yellow('\n⚠️ Cannot access container image\n'));
|
|
156
|
+
console.log('The BU WordPress image requires GitHub Packages authentication.\n');
|
|
157
|
+
console.log('Run this command to authenticate:\n');
|
|
158
|
+
console.log(chalk.cyan(' docker login ghcr.io\n'));
|
|
159
|
+
console.log('You will need a GitHub personal access token with "read:packages" scope.');
|
|
160
|
+
console.log('See: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry\n');
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
throw pullError; // Other error, let it surface
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
11
167
|
|
|
12
168
|
async function startCommand(options) {
|
|
13
169
|
console.log(chalk.blue('🚀 Starting BU WordPress local environment...\n'));
|
|
@@ -43,6 +199,80 @@ async function startCommand(options) {
|
|
|
43
199
|
process.exit(1);
|
|
44
200
|
}
|
|
45
201
|
|
|
202
|
+
// Load and validate credentials early
|
|
203
|
+
const envFilePath = path.join(projectPath, ENV_FILE_NAME);
|
|
204
|
+
const keychainCredentials = loadKeychainCredentials();
|
|
205
|
+
const credentialValidation = validateCredentials(keychainCredentials, envFilePath);
|
|
206
|
+
|
|
207
|
+
if (!credentialValidation.isValid) {
|
|
208
|
+
const shouldSetup = await promptCredentialSetup(credentialValidation.missing);
|
|
209
|
+
|
|
210
|
+
if (shouldSetup) {
|
|
211
|
+
// Run keychain setup command
|
|
212
|
+
console.log(chalk.gray('\nLaunching Keychain Setup...\n'));
|
|
213
|
+
await keychainCommand('setup', [], {});
|
|
214
|
+
|
|
215
|
+
// Reload credentials after setup
|
|
216
|
+
const reloadedCreds = loadKeychainCredentials();
|
|
217
|
+
const revalidation = validateCredentials(reloadedCreds, envFilePath);
|
|
218
|
+
|
|
219
|
+
if (!revalidation.isValid) {
|
|
220
|
+
console.log(chalk.red('\n❌ Setup incomplete. Required credentials still missing:\n'));
|
|
221
|
+
revalidation.missing.forEach(key => {
|
|
222
|
+
console.log(chalk.red(` - ${key}`));
|
|
223
|
+
});
|
|
224
|
+
console.log(chalk.gray('\nPlease complete setup or add credentials to .env.local\n'));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
console.log(chalk.red('\n❌ Cannot start without required credentials.\n'));
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
console.log(chalk.green('✓ Required credentials validated\n'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Check image accessibility
|
|
236
|
+
console.log(chalk.gray('Checking container image access...'));
|
|
237
|
+
const imageAccessible = await checkImageAccess(config.image);
|
|
238
|
+
|
|
239
|
+
if (!imageAccessible) {
|
|
240
|
+
console.log(chalk.red('\n❌ Cannot proceed without access to container image.'));
|
|
241
|
+
console.log(chalk.gray('Please authenticate with GitHub Packages and try again.\n'));
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log(chalk.green('✓ Container image accessible\n'));
|
|
246
|
+
|
|
247
|
+
// Check /etc/hosts for hostname entry
|
|
248
|
+
if (!hasShownHostsWarning(projectPath)) {
|
|
249
|
+
const hostsCheck = checkHostsFile(config.hostname);
|
|
250
|
+
|
|
251
|
+
if (!hostsCheck.found) {
|
|
252
|
+
console.log(chalk.yellow('⚠️ Hostname not found in /etc/hosts\n'));
|
|
253
|
+
console.log(`Your site won't be accessible at ${chalk.cyan(`http://${config.hostname}`)}`);
|
|
254
|
+
console.log('until you add this entry:\n');
|
|
255
|
+
console.log(chalk.green(` 127.0.0.1 ${config.hostname}\n`));
|
|
256
|
+
console.log('Run this command to add it (copy and paste, and then enter your password):\n');
|
|
257
|
+
console.log(chalk.cyan(` echo "127.0.0.1 ${config.hostname}" | sudo tee -a /etc/hosts\n`));
|
|
258
|
+
|
|
259
|
+
const { continueStart } = await prompts({
|
|
260
|
+
type: 'confirm',
|
|
261
|
+
name: 'continueStart',
|
|
262
|
+
message: 'Continue starting containers?',
|
|
263
|
+
initial: true
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
if (!continueStart) {
|
|
267
|
+
console.log(chalk.gray('\nStart cancelled. Add hostname to /etc/hosts and try again.'));
|
|
268
|
+
process.exit(0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Mark warning as shown so we don't annoy user on every start
|
|
272
|
+
markHostsWarningShown(projectPath);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
46
276
|
// Generate docker-compose.yml
|
|
47
277
|
console.log(chalk.gray('Generating docker-compose.yml...'));
|
|
48
278
|
const composePath = generateComposeFile(config, projectPath);
|
|
@@ -63,12 +293,12 @@ async function startCommand(options) {
|
|
|
63
293
|
|
|
64
294
|
// Load keychain credentials and create secure temp env file if available
|
|
65
295
|
let tempEnvPath = null;
|
|
66
|
-
const
|
|
67
|
-
const keychainCredCount = Object.keys(
|
|
296
|
+
const finalKeychainCredentials = loadKeychainCredentials();
|
|
297
|
+
const keychainCredCount = Object.keys(finalKeychainCredentials).length;
|
|
68
298
|
|
|
69
299
|
if (keychainCredCount > 0) {
|
|
70
300
|
try {
|
|
71
|
-
tempEnvPath = createSecureTempEnvFile(
|
|
301
|
+
tempEnvPath = createSecureTempEnvFile(finalKeychainCredentials, projectName);
|
|
72
302
|
console.log(chalk.gray(`✓ Loaded ${keychainCredCount} credentials from keychain\n`));
|
|
73
303
|
} catch (err) {
|
|
74
304
|
console.warn(chalk.yellow(`⚠️ Could not load keychain credentials: ${err.message}`));
|
|
@@ -76,7 +306,6 @@ async function startCommand(options) {
|
|
|
76
306
|
}
|
|
77
307
|
|
|
78
308
|
// Check if .env.local exists and build env-file flags
|
|
79
|
-
const envFilePath = path.join(projectPath, ENV_FILE_NAME);
|
|
80
309
|
const envFileFlag = fs.existsSync(envFilePath) ? `--env-file ${envFilePath}` : '';
|
|
81
310
|
const tempEnvFileFlag = tempEnvPath ? `--env-file ${tempEnvPath}` : '';
|
|
82
311
|
|
|
@@ -111,10 +340,6 @@ async function startCommand(options) {
|
|
|
111
340
|
console.log(chalk.white(' buwp-local stop - Stop environment'));
|
|
112
341
|
console.log(chalk.white(' buwp-local destroy - Remove environment\n'));
|
|
113
342
|
|
|
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
343
|
} catch (err) {
|
|
119
344
|
console.error(chalk.red('\n❌ Error:'), err.message);
|
|
120
345
|
process.exit(1);
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
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.
|
|
6
|
+
|
|
7
|
+
> **Why buwp-local over VM sandboxes?**
|
|
8
|
+
>
|
|
9
|
+
> Traditional VM sandboxes require **monthly rebuilds that wipe your development code**. With buwp-local's Docker architecture, your code lives on your local filesystem while only WordPress core updates. This means:
|
|
10
|
+
> - ✅ **Keep your work** - No more monthly rebuild cycles that erase local changes
|
|
11
|
+
> - ✅ **Update independently** - Pull WordPress updates on your schedule, not a global calendar
|
|
12
|
+
> - ✅ **Instant rollback** - Switch between WordPress versions without losing work
|
|
13
|
+
>
|
|
14
|
+
> Learn more: [Migration from VM Sandboxes](docs/MIGRATION_FROM_VM.md)
|
|
5
15
|
|
|
6
16
|
## Quickstart for plugin or theme development
|
|
7
17
|
|
|
@@ -84,3 +94,19 @@ Your local WordPress site should now be accessible at the hostname you configure
|
|
|
84
94
|
```
|
|
85
95
|
|
|
86
96
|
This will download the latest snapshot from the specified source and import it into your local WordPress environment.
|
|
97
|
+
|
|
98
|
+
## Documentation
|
|
99
|
+
|
|
100
|
+
- 📘 [Getting Started Guide](docs/GETTING_STARTED.md)
|
|
101
|
+
- 📖 [Command Reference](docs/COMMANDS.md)
|
|
102
|
+
- 🔐 [Credential Management](docs/CREDENTIALS.md)
|
|
103
|
+
- 🏗️ [Architecture](docs/ARCHITECTURE.md) (for contributors)
|
|
104
|
+
|
|
105
|
+
## Features
|
|
106
|
+
|
|
107
|
+
- ✅ One-time credential setup with macOS Keychain
|
|
108
|
+
- ✅ Isolated environments for multiple projects
|
|
109
|
+
- ✅ Pre-configured BU infrastructure (Shibboleth, S3, Redis)
|
|
110
|
+
- ✅ Smart initialization for plugins, themes, and mu-plugins
|
|
111
|
+
- ✅ Volume mapping for live code sync
|
|
112
|
+
- ✅ Xdebug support for step debugging
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|