@bostonuniversity/buwp-local 0.5.0 ā 0.5.2
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/lib/commands/init.js +3 -3
- package/lib/commands/keychain.js +48 -0
- package/lib/commands/start.js +2 -1
- package/lib/config.js +5 -4
- package/lib/keychain.js +0 -2
- package/package.json +2 -2
- package/readme.md +92 -1
- package/.buwp-local.json +0 -22
- package/plan-environmentBasedCredentials.prompt.md +0 -57
package/lib/commands/init.js
CHANGED
|
@@ -294,16 +294,16 @@ async function initCommand(options) {
|
|
|
294
294
|
|
|
295
295
|
// Show next steps
|
|
296
296
|
console.log(chalk.cyan('š Next steps:'));
|
|
297
|
-
console.log(chalk.gray(' 1.
|
|
297
|
+
console.log(chalk.gray(' 1. Check keychain credentials or add .env.local with your credentials'));
|
|
298
298
|
console.log(chalk.gray(` 2. Add to /etc/hosts: 127.0.0.1 ${answers.hostname}`));
|
|
299
299
|
|
|
300
300
|
if (answers.projectType === 'sandbox') {
|
|
301
301
|
console.log(chalk.gray(' 3. Edit .buwp-local.json and add volume mappings to the mappings array'));
|
|
302
|
-
console.log(chalk.gray(' 4. Run: buwp-local start'));
|
|
302
|
+
console.log(chalk.gray(' 4. Run: npx buwp-local start'));
|
|
303
303
|
console.log(chalk.gray(`\n Example mapping for .buwp-local.json mappings array:`));
|
|
304
304
|
console.log(chalk.gray(` { "local": "/path/to/plugin", "container": "/var/www/html/wp-content/plugins/my-plugin" }`));
|
|
305
305
|
} else {
|
|
306
|
-
console.log(chalk.gray(' 3. Run: buwp-local start'));
|
|
306
|
+
console.log(chalk.gray(' 3. Run: npx buwp-local start'));
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
console.log(chalk.gray(`\n Then access: https://${answers.hostname}\n`));
|
package/lib/commands/keychain.js
CHANGED
|
@@ -174,6 +174,44 @@ async function setupCommand(options) {
|
|
|
174
174
|
console.log(chalk.gray('You can override specific credentials per-project using .env.local files.\n'));
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Prompt user to delete the source credentials file
|
|
179
|
+
* @param {string} filePath - Path to the file to delete
|
|
180
|
+
* @param {boolean} hasFailures - Whether some credentials failed to import
|
|
181
|
+
*/
|
|
182
|
+
async function promptToDeleteSourceFile(filePath, hasFailures = false) {
|
|
183
|
+
const fileExists = fs.existsSync(filePath);
|
|
184
|
+
if (!fileExists) {
|
|
185
|
+
return; // File already deleted or doesn't exist
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(chalk.yellow('š Source Credentials File Security'));
|
|
189
|
+
console.log(chalk.gray(` Location: ${filePath}`));
|
|
190
|
+
console.log(chalk.gray(' This file contains plaintext credentials and should be deleted.'));
|
|
191
|
+
console.log('');
|
|
192
|
+
|
|
193
|
+
const defaultDelete = !hasFailures; // Default to yes if all imported successfully
|
|
194
|
+
const { shouldDelete } = await prompts({
|
|
195
|
+
type: 'confirm',
|
|
196
|
+
name: 'shouldDelete',
|
|
197
|
+
message: 'Delete the source credentials file now?',
|
|
198
|
+
initial: defaultDelete
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (shouldDelete) {
|
|
202
|
+
try {
|
|
203
|
+
fs.unlinkSync(filePath);
|
|
204
|
+
console.log(chalk.green(`ā Deleted: ${filePath}\n`));
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.log(chalk.red(`ā Failed to delete file: ${err.message}`));
|
|
207
|
+
console.log(chalk.yellow(`ā ļø Please manually delete: ${filePath}\n`));
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
console.log(chalk.yellow(`ā ļø Remember to manually delete: ${filePath}`));
|
|
211
|
+
console.log(chalk.yellow(' This file contains plaintext credentials.\n'));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
177
215
|
/**
|
|
178
216
|
* Bulk import credentials from JSON file
|
|
179
217
|
* @param {string} filePath - Path to credentials JSON file
|
|
@@ -289,6 +327,16 @@ async function bulkImportFromFile(filePath, force) {
|
|
|
289
327
|
|
|
290
328
|
console.log(chalk.gray('These credentials will be used automatically by all buwp-local projects.'));
|
|
291
329
|
console.log(chalk.gray('You can override specific credentials per-project using .env.local files.\n'));
|
|
330
|
+
|
|
331
|
+
// Offer to delete the source file
|
|
332
|
+
if (failCount === 0) {
|
|
333
|
+
// All credentials imported successfully - safe to delete
|
|
334
|
+
await promptToDeleteSourceFile(filePath);
|
|
335
|
+
} else if (successCount > 0) {
|
|
336
|
+
// Some imported successfully - ask with a warning
|
|
337
|
+
console.log(chalk.yellow('ā ļø Not all credentials imported successfully.\n'));
|
|
338
|
+
await promptToDeleteSourceFile(filePath, true);
|
|
339
|
+
}
|
|
292
340
|
}
|
|
293
341
|
|
|
294
342
|
/**
|
package/lib/commands/start.js
CHANGED
|
@@ -81,6 +81,8 @@ async function startCommand(options) {
|
|
|
81
81
|
const tempEnvFileFlag = tempEnvPath ? `--env-file ${tempEnvPath}` : '';
|
|
82
82
|
|
|
83
83
|
try {
|
|
84
|
+
// This is the actual docker compose up command that starts everything
|
|
85
|
+
// If there is a local .env file, it will take over the temp env file generated from the keychain, because it is specified last.
|
|
84
86
|
execSync(
|
|
85
87
|
`docker compose -p ${projectName} ${tempEnvFileFlag} ${envFileFlag} -f ${composePath} up -d`,
|
|
86
88
|
{
|
|
@@ -102,7 +104,6 @@ async function startCommand(options) {
|
|
|
102
104
|
console.log(chalk.green('\nā
Environment started successfully!\n'));
|
|
103
105
|
console.log(chalk.cyan(`Project: ${projectName}`));
|
|
104
106
|
console.log(chalk.cyan('Access your site at:'));
|
|
105
|
-
console.log(chalk.white(` http://${config.hostname}`));
|
|
106
107
|
console.log(chalk.white(` https://${config.hostname}\n`));
|
|
107
108
|
|
|
108
109
|
console.log(chalk.gray('Useful commands:'));
|
package/lib/config.js
CHANGED
|
@@ -286,7 +286,7 @@ function sanitizeProjectName(name) {
|
|
|
286
286
|
|
|
287
287
|
/**
|
|
288
288
|
* Load credentials from macOS keychain
|
|
289
|
-
* Returns all
|
|
289
|
+
* Returns all available credentials if present, with platform and keychain support checks
|
|
290
290
|
* @returns {object} Credentials object or empty object if not supported
|
|
291
291
|
*/
|
|
292
292
|
export function loadKeychainCredentials() {
|
|
@@ -322,10 +322,11 @@ export function createSecureTempEnvFile(credentials, projectName) {
|
|
|
322
322
|
// Build env file content with proper escaping for multiline values
|
|
323
323
|
const lines = Object.entries(credentials).map(([key, value]) => {
|
|
324
324
|
// Escape special characters in values
|
|
325
|
+
// Order matters: backslashes first, then quotes, then newlines
|
|
325
326
|
const escaped = String(value)
|
|
326
|
-
.replace(
|
|
327
|
-
.replace(/"/g, '\\"')
|
|
328
|
-
.replace(
|
|
327
|
+
.replace(/\\/g, '\\\\') // Escape backslashes first (\ -> \\)
|
|
328
|
+
.replace(/"/g, '\\"') // Escape double quotes (" -> \")
|
|
329
|
+
.replace(/\n/g, '\\n'); // Escape actual newlines (\n -> \n literal in file)
|
|
329
330
|
|
|
330
331
|
return `${key}="${escaped}"`;
|
|
331
332
|
});
|
package/lib/keychain.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bostonuniversity/buwp-local",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Local WordPress development environment for Boston University projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
-
"buwp-local": "node bin/buwp-local.js
|
|
12
|
+
"buwp-local": "node bin/buwp-local.js",
|
|
13
13
|
"lint": "eslint ."
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
package/readme.md
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
1
|
# BU WordPress Local Development
|
|
2
2
|
|
|
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
|
|
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
|
+
|
|
5
|
+
|
|
6
|
+
## Quickstart for plugin or theme development
|
|
7
|
+
|
|
8
|
+
1. **Install Docker**: Make sure you have [Docker Desktop](https://www.docker.com/products/docker-desktop) installed and running on your machine.
|
|
9
|
+
|
|
10
|
+
2. Login to GitHub Packages to access the BU WordPress Docker image (you will need a GitHub access token with `read:packages` scope):
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
echo YOUR_GITHUB_TOKEN | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
3. **Install buwp-local CLI**: Install the `buwp-local` CLI tool in your project directory:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @bostonuniversity/buwp-local --save-dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
4. **One time credential keychain setup**: Install credentials in the macOS Keychain for secure storage (optional, macOS only right now):
|
|
23
|
+
|
|
24
|
+
First, download a credentials JSON file through ssh from the dev server:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
scp user@devserver:/path/to/buwp-local-credentials.json ~/Downloads/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run the setup command:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx buwp-local keychain setup --file ~/Downloads/buwp-local-credentials.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This will store all necessary credentials in your Keychain for future use, for this project and any other buwp-local projects (Keychain is global). (The global Keychain can also be overridden by `.env.local` files in each project.)
|
|
37
|
+
|
|
38
|
+
5. **Initialize your project**: Run the interactive setup to create your `.buwp-local.json` configuration file:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx buwp-local init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This will guide you through setting up your project name, hostname, port mappings, volume mappings, and service options.
|
|
45
|
+
|
|
46
|
+
If you choose plugin, theme, or mu-plugin project type, the setup will automatically add volume mappings for your current directory into the appropriate WordPress location in the container.
|
|
47
|
+
|
|
48
|
+
If you choose sandbox project type, you will need to manually add volume mappings to your `.buwp-local.json` file later, or you can run without any volume mappings.
|
|
49
|
+
|
|
50
|
+
6. **Setup local hostname**: Add your project's local hostname (e.g. `myproject.local`) to your `/etc/hosts` file
|
|
51
|
+
|
|
52
|
+
7. **Start your local environment**:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx buwp-local start
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This will read your configuration, load credentials from Keychain (or `.env.local` if present), and start the Docker containers for your WordPress project.
|
|
59
|
+
|
|
60
|
+
Your local WordPress site should now be accessible at the hostname you configured (e.g. `http://myproject.local`).
|
|
61
|
+
|
|
62
|
+
## Basic Local setup
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
1. **Setup local user**: Create a local WordPress user and add it to the super admin role:
|
|
66
|
+
|
|
67
|
+
If running with Shibboleth enabled, you can set up a local WordPress user with super admin privileges:
|
|
68
|
+
|
|
69
|
+
Create the user:
|
|
70
|
+
```bash
|
|
71
|
+
npx buwp-local wp user create username username@bu.edu --role=administrator
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Promote to super admin:
|
|
75
|
+
```bash
|
|
76
|
+
npx buwp-local wp super-admin add username@bu.edu
|
|
77
|
+
```
|
|
78
|
+
2. Pull snapshot site content:
|
|
79
|
+
|
|
80
|
+
You can pull a snapshot of the production or staging site database and media files into your local environment for testing and development.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx buwp-local wp site-manager snapshot-pull --source=https://www.bu.edu/admissions/ --destination=http://myproject.local/admissions
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This will download the latest snapshot from the specified source and import it into your local WordPress environment.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
scp username@ist-wp-app-dv01.bu.edu:/etc/ist-apps/buwp-local-credentials.json ~/Downloads/
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
scp jaydub@ist-wp-app-dv01.bu.edu:/etc/ist-apps/buwp-local-credentials.json ~/Downloads/
|
package/.buwp-local.json
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"projectName": "buwp-local",
|
|
3
|
-
"image": "ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest",
|
|
4
|
-
"hostname": "jaydub.local",
|
|
5
|
-
"multisite": true,
|
|
6
|
-
"services": {
|
|
7
|
-
"redis": true,
|
|
8
|
-
"s3proxy": true,
|
|
9
|
-
"shibboleth": true
|
|
10
|
-
},
|
|
11
|
-
"ports": {
|
|
12
|
-
"http": 80,
|
|
13
|
-
"https": 443,
|
|
14
|
-
"db": 3306,
|
|
15
|
-
"redis": 6379
|
|
16
|
-
},
|
|
17
|
-
"mappings": [],
|
|
18
|
-
"env": {
|
|
19
|
-
"WP_DEBUG": true,
|
|
20
|
-
"XDEBUG": false
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
## Plan: Environment-Based Credentials Migration (v0.4.1)
|
|
2
|
-
|
|
3
|
-
We've successfully shipped v0.4.0 with interactive init, sandbox support, configurable Docker images, and smart hostname defaults. The codebase is in excellent shape with real-world testing validated in both plugin and sandbox scenarios.
|
|
4
|
-
|
|
5
|
-
**Current Problem:** Credentials (database passwords, AWS keys, Shibboleth certs) are currently being read from the config/environment and then **written directly into the generated `docker-compose.yml`**. This means sensitive data sits in a generated file, which isn't ideal for security.
|
|
6
|
-
|
|
7
|
-
**Better Approach:** Use Docker Compose's native environment variable interpolation (`${VAR_NAME}`) so credentials stay in `.env.local` and never get written to the compose file.
|
|
8
|
-
|
|
9
|
-
### Steps
|
|
10
|
-
|
|
11
|
-
1. **Update `compose-generator.js` to use environment variable references**
|
|
12
|
-
- Database service: Change from `MYSQL_PASSWORD: dbPassword` to `MYSQL_PASSWORD: '${WORDPRESS_DB_PASSWORD:-password}'`
|
|
13
|
-
- WordPress service: Change from `WORDPRESS_DB_PASSWORD: config.db?.password` to `WORDPRESS_DB_PASSWORD: '${WORDPRESS_DB_PASSWORD:-password}'`
|
|
14
|
-
- S3 proxy service: Change from `AWS_ACCESS_KEY_ID: config.s3?.accessKeyId` to `AWS_ACCESS_KEY_ID: '${S3_UPLOADS_ACCESS_KEY_ID}'`
|
|
15
|
-
- WordPress `WORDPRESS_CONFIG_EXTRA`: Change from injecting actual S3 keys to using `${S3_UPLOADS_ACCESS_KEY_ID}` references
|
|
16
|
-
|
|
17
|
-
2. **Update `start.js` to pass `.env.local` to docker compose**
|
|
18
|
-
- Add `--env-file` flag to `docker compose up` command
|
|
19
|
-
- Ensure `.env.local` path is correctly resolved relative to project
|
|
20
|
-
|
|
21
|
-
3. **Update config loading to remove credential reading**
|
|
22
|
-
- Keep `extractEnvVars()` for backward compatibility but mark as deprecated
|
|
23
|
-
- Config validation no longer needs to check for credential presence in config object
|
|
24
|
-
- Document that credentials should only live in `.env.local`
|
|
25
|
-
|
|
26
|
-
4. **Update documentation**
|
|
27
|
-
- `USAGE.md` - Clarify that `.env.local` is the source of truth for credentials
|
|
28
|
-
- Add example showing compose file will have `${VAR}` references
|
|
29
|
-
- Security best practices section emphasizes this approach
|
|
30
|
-
|
|
31
|
-
5. **Test migration path**
|
|
32
|
-
- Verify existing projects with credentials in config still work (backward compat)
|
|
33
|
-
- Test new projects using only `.env.local`
|
|
34
|
-
- Verify generated compose file contains variable references, not actual values
|
|
35
|
-
- Ensure `docker compose` properly reads `.env.local`
|
|
36
|
-
|
|
37
|
-
### Further Considerations
|
|
38
|
-
|
|
39
|
-
1. **Backward compatibility**: Should we warn users if credentials exist in config? Or silently prefer `.env.local`?
|
|
40
|
-
2. **`.env.local` template**: Should we create `.env.local.example` during `init` command?
|
|
41
|
-
3. **Validation**: Should `config --validate` check that `.env.local` exists and has required variables?
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
**Why This Matters:**
|
|
46
|
-
- **Security**: Credentials never written to files, only referenced
|
|
47
|
-
- **Git safety**: Generated compose files are safe to accidentally commit (no secrets)
|
|
48
|
-
- **Best practices**: Aligns with Docker Compose's standard env var pattern
|
|
49
|
-
- **Simplicity**: Reduces complexity in config merging logic
|
|
50
|
-
|
|
51
|
-
**Effort Estimate:** Low-Medium (4-6 hours)
|
|
52
|
-
- Compose generation changes: ~2 hours
|
|
53
|
-
- Start command changes: ~1 hour
|
|
54
|
-
- Documentation: ~1 hour
|
|
55
|
-
- Testing: ~1-2 hours
|
|
56
|
-
|
|
57
|
-
**Ready to proceed?** This sets up a cleaner foundation before tackling Phase 2 features like keychain integration.
|