@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.
@@ -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. Create .env.local with your credentials'));
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`));
@@ -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
  /**
@@ -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 15 credentials if available, with platform and keychain support checks
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(/\\\\/g, '\\\\\\\\') // Escape backslashes first
327
- .replace(/"/g, '\\"') // Escape double quotes
328
- .replace(/\\n/g, '\\\\n'); // Escape newlines for shell
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
@@ -237,8 +237,6 @@ export function deleteCredential(key) {
237
237
  if (!err.message.includes('could not be found')) {
238
238
  throw new Error(`Failed to delete credential from keychain: ${err.message}`);
239
239
  }
240
-
241
- console.log("foobar");
242
240
  }
243
241
  }
244
242
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bostonuniversity/buwp-local",
3
- "version": "0.5.0",
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 start",
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 resoures needed to run it locally with Docker.
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.