@bostonuniversity/buwp-local 0.4.0 → 0.4.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.
@@ -0,0 +1,22 @@
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
+ }
@@ -0,0 +1,26 @@
1
+ # BU WordPress Local Development Environment Variables
2
+ # Copy this file to .env.local and fill in your actual credentials
3
+ # NEVER commit .env.local to version control!
4
+
5
+ # Database Credentials
6
+ WORDPRESS_DB_PASSWORD=password
7
+ DB_ROOT_PASSWORD=rootpassword
8
+
9
+ # Shibboleth Configuration (if enabled)
10
+ SP_ENTITY_ID=https://your-sp-entity-id
11
+ IDP_ENTITY_ID=https://shib-test.bu.edu/idp/shibboleth
12
+ SHIB_IDP_LOGOUT=https://shib-test.bu.edu/idp/logout.jsp
13
+ SHIB_SP_KEY=your-private-key-here
14
+ SHIB_SP_CERT=your-certificate-here
15
+
16
+ # AWS S3 Configuration (if enabled)
17
+ S3_UPLOADS_BUCKET=your-bucket-name
18
+ S3_UPLOADS_REGION=us-east-1
19
+ S3_UPLOADS_ACCESS_KEY_ID=AKIA...
20
+ S3_UPLOADS_SECRET_ACCESS_KEY=your-secret-key
21
+ S3_ACCESS_RULES_TABLE=your-access-rules-table
22
+
23
+ # OLAP Configuration (if using S3 Object Lambda)
24
+ OLAP=your-olap-name
25
+ OLAP_ACCT_NBR=123456789
26
+ OLAP_REGION=us-east-1
package/USAGE.md CHANGED
@@ -51,7 +51,13 @@ Edit `.buwp-local.json` to map your local repository into the container:
51
51
 
52
52
  ### 4. Create `.env.local` for secrets
53
53
 
54
- Create `.env.local` (never commit this file!):
54
+ Create `.env.local` (never commit this file!) or copy from the example:
55
+
56
+ ```bash
57
+ cp .env.local.example .env.local
58
+ ```
59
+
60
+ Then edit `.env.local` with your actual credentials:
55
61
 
56
62
  ```bash
57
63
  # Database
@@ -70,6 +76,7 @@ S3_UPLOADS_BUCKET=your-bucket
70
76
  S3_UPLOADS_REGION=us-east-1
71
77
  S3_UPLOADS_ACCESS_KEY_ID=your-access-key
72
78
  S3_UPLOADS_SECRET_ACCESS_KEY=your-secret-key
79
+ S3_ACCESS_RULES_TABLE=your-access-rules-table
73
80
 
74
81
  # OLAP
75
82
  OLAP=your-olap-name
@@ -77,6 +84,8 @@ OLAP_ACCT_NBR=your-account-number
77
84
  OLAP_REGION=us-east-1
78
85
  ```
79
86
 
87
+ **Important:** Credentials are read directly from `.env.local` by Docker Compose. They are never written to the generated `docker-compose.yml` file, making it safe to review or share the generated compose file without exposing secrets.
88
+
80
89
  ### 5. Add hostname to /etc/hosts
81
90
 
82
91
  ```bash
@@ -226,10 +235,27 @@ Map your local code into the container:
226
235
  ## Security Best Practices
227
236
 
228
237
  1. **Never commit `.env.local`** - This file contains secrets
229
- 2. **Never commit `.buwp-local/`** - This contains generated files
230
- 3. **Do commit `.buwp-local.json`** - This is your configuration template
231
- 4. **Use environment variables for all secrets**
232
- 5. **Consider using macOS Keychain** for even better security (see docs)
238
+ 2. **Never commit `.buwp-local/`** - This directory contains generated files
239
+ 3. **Do commit `.buwp-local.json`** - This is your configuration template (no secrets)
240
+ 4. **Use environment variables for all secrets** - Credentials stay in `.env.local` and are never written to generated files
241
+ 5. **Review generated compose files** - The generated `docker-compose.yml` only contains variable references like `${WORDPRESS_DB_PASSWORD}`, not actual credentials
242
+ 6. **Copy `.env.local.example`** - Use the provided template to create your `.env.local` file
243
+ 7. **Consider using macOS Keychain** for even better security (future feature)
244
+
245
+ ### How Credentials Work
246
+
247
+ buwp-local uses Docker Compose's native environment variable interpolation:
248
+
249
+ 1. **You provide** credentials in `.env.local`
250
+ 2. **buwp-local generates** `docker-compose.yml` with variable references like `${VAR_NAME}`
251
+ 3. **Docker Compose reads** `.env.local` at runtime and substitutes the values
252
+ 4. **Your secrets never** get written to any generated files
253
+
254
+ This means:
255
+ - ✅ Generated compose files are safe to review or share
256
+ - ✅ No credentials in version control (even accidentally)
257
+ - ✅ Industry-standard Docker Compose pattern
258
+ - ✅ Simple and secure
233
259
 
234
260
  ## File Structure
235
261
 
@@ -133,6 +133,10 @@ async function initCommand(options) {
133
133
  name: 'hostname',
134
134
  message: 'Hostname',
135
135
  initial: (prev, values) => {
136
+ // For sandbox, use just username.local; for other types include project name
137
+ if (values.projectType === 'sandbox') {
138
+ return `${userName}.local`;
139
+ }
136
140
  return `${userName}-${values.projectName}.local`;
137
141
  },
138
142
  validate: value => {
@@ -5,7 +5,8 @@
5
5
  import chalk from 'chalk';
6
6
  import { execSync } from 'child_process';
7
7
  import path from 'path';
8
- import { loadConfig, validateConfig } from '../config.js';
8
+ import fs from 'fs';
9
+ import { loadConfig, validateConfig, ENV_FILE_NAME } from '../config.js';
9
10
  import { generateComposeFile } from '../compose-generator.js';
10
11
 
11
12
  async function startCommand(options) {
@@ -60,9 +61,13 @@ async function startCommand(options) {
60
61
  const composeDir = path.dirname(composePath);
61
62
  const projectName = config.projectName || 'buwp-local';
62
63
 
64
+ // Check if .env.local exists and build env-file flag
65
+ const envFilePath = path.join(projectPath, ENV_FILE_NAME);
66
+ const envFileFlag = fs.existsSync(envFilePath) ? `--env-file ${envFilePath}` : '';
67
+
63
68
  try {
64
69
  execSync(
65
- `docker compose -p ${projectName} -f ${composePath} up -d`,
70
+ `docker compose -p ${projectName} ${envFileFlag} -f ${composePath} up -d`,
66
71
  {
67
72
  cwd: composeDir,
68
73
  stdio: 'inherit'
@@ -57,9 +57,6 @@ function generateComposeConfig(config) {
57
57
  * @returns {object} Database service config
58
58
  */
59
59
  function generateDbService(config, dbVolumeName) {
60
- const dbPassword = config.db?.password || 'password';
61
- const rootPassword = config.db?.rootPassword || 'rootpassword';
62
-
63
60
  return {
64
61
  image: 'mariadb:latest',
65
62
  restart: 'always',
@@ -67,8 +64,8 @@ function generateDbService(config, dbVolumeName) {
67
64
  environment: {
68
65
  MYSQL_DATABASE: 'wordpress',
69
66
  MYSQL_USER: 'wordpress',
70
- MYSQL_PASSWORD: dbPassword,
71
- MYSQL_ROOT_PASSWORD: rootPassword
67
+ MYSQL_PASSWORD: '${WORDPRESS_DB_PASSWORD:-password}',
68
+ MYSQL_ROOT_PASSWORD: '${DB_ROOT_PASSWORD:-rootpassword}'
72
69
  },
73
70
  ports: [`${config.ports.db}:3306`],
74
71
  networks: ['wp-network']
@@ -91,7 +88,7 @@ function generateWordPressService(config, wpVolumeName) {
91
88
  const environment = {
92
89
  WORDPRESS_DB_HOST: 'db:3306',
93
90
  WORDPRESS_DB_USER: 'wordpress',
94
- WORDPRESS_DB_PASSWORD: config.db?.password || 'password',
91
+ WORDPRESS_DB_PASSWORD: '${WORDPRESS_DB_PASSWORD:-password}',
95
92
  WORDPRESS_DB_NAME: 'wordpress',
96
93
  WORDPRESS_DEBUG: config.env?.WP_DEBUG || '0',
97
94
  SERVER_NAME: config.hostname,
@@ -104,11 +101,11 @@ function generateWordPressService(config, wpVolumeName) {
104
101
 
105
102
  // Add Shibboleth config if enabled
106
103
  if (config.services.shibboleth) {
107
- environment.SP_ENTITY_ID = config.shibboleth?.entityId || '';
108
- environment.IDP_ENTITY_ID = config.shibboleth?.idpEntityId || '';
109
- environment.SHIB_IDP_LOGOUT = config.shibboleth?.idpLogout || '';
110
- environment.SHIB_SP_KEY = config.shibboleth?.spKey || '';
111
- environment.SHIB_SP_CERT = config.shibboleth?.spCert || '';
104
+ environment.SP_ENTITY_ID = '${SP_ENTITY_ID:-}';
105
+ environment.IDP_ENTITY_ID = '${IDP_ENTITY_ID:-}';
106
+ environment.SHIB_IDP_LOGOUT = '${SHIB_IDP_LOGOUT:-}';
107
+ environment.SHIB_SP_KEY = '${SHIB_SP_KEY:-}';
108
+ environment.SHIB_SP_CERT = '${SHIB_SP_CERT:-}';
112
109
  }
113
110
 
114
111
  // Add S3 config if enabled
@@ -130,12 +127,12 @@ function generateWordPressService(config, wpVolumeName) {
130
127
  wpConfigExtra += "define('SUBDOMAIN_INSTALL', false);\n";
131
128
  }
132
129
 
133
- if (config.services.s3proxy && config.s3) {
134
- wpConfigExtra += `define('S3_UPLOADS_BUCKET', '${config.s3.bucket || ''}');\n`;
135
- wpConfigExtra += `define('S3_UPLOADS_REGION', '${config.s3.region || 'us-east-1'}');\n`;
136
- wpConfigExtra += `define('S3_UPLOADS_KEY', '${config.s3.accessKeyId || ''}');\n`;
137
- wpConfigExtra += `define('S3_UPLOADS_SECRET', '${config.s3.secretAccessKey || ''}');\n`;
138
- wpConfigExtra += `define('ACCESS_RULES_TABLE', '${config.s3.accessRulesTable || ''}');\n`;
130
+ if (config.services.s3proxy) {
131
+ wpConfigExtra += "define('S3_UPLOADS_BUCKET', '${S3_UPLOADS_BUCKET}');\n";
132
+ wpConfigExtra += "define('S3_UPLOADS_REGION', '${S3_UPLOADS_REGION:-us-east-1}');\n";
133
+ wpConfigExtra += "define('S3_UPLOADS_KEY', '${S3_UPLOADS_ACCESS_KEY_ID}');\n";
134
+ wpConfigExtra += "define('S3_UPLOADS_SECRET', '${S3_UPLOADS_SECRET_ACCESS_KEY}');\n";
135
+ wpConfigExtra += "define('ACCESS_RULES_TABLE', '${S3_ACCESS_RULES_TABLE}');\n";
139
136
  wpConfigExtra += "define('S3_UPLOADS_OBJECT_ACL', null);\n";
140
137
  wpConfigExtra += "define('S3_UPLOADS_AUTOENABLE', true);\n";
141
138
  wpConfigExtra += "define('S3_UPLOADS_DISABLE_REPLACE_UPLOAD_URL', true);\n";
@@ -183,14 +180,10 @@ function generateWordPressService(config, wpVolumeName) {
183
180
 
184
181
  /**
185
182
  * Generate S3 proxy service configuration
186
- * @param {object} config - buwp-local configuration
183
+ * @param {object} _config - buwp-local configuration (unused - env vars used instead)
187
184
  * @returns {object} S3 proxy service config
188
185
  */
189
- function generateS3ProxyService(config) {
190
- const region = config.olap?.region || config.s3?.region || 'us-east-1';
191
- const olapName = config.olap?.name || '';
192
- const olapAcctNbr = config.olap?.accountNumber || '';
193
-
186
+ function generateS3ProxyService(_config) {
194
187
  return {
195
188
  image: 'public.ecr.aws/bostonuniversity-nonprod/aws-sigv4-proxy',
196
189
  restart: 'always',
@@ -199,16 +192,16 @@ function generateS3ProxyService(config) {
199
192
  '--name',
200
193
  's3-object-lambda',
201
194
  '--region',
202
- region,
195
+ '${OLAP_REGION:-us-east-1}',
203
196
  '--no-verify-ssl',
204
197
  '--host',
205
- `${olapName}-${olapAcctNbr}.s3-object-lambda.${region}.amazonaws.com`
198
+ '${OLAP}-${OLAP_ACCT_NBR}.s3-object-lambda.${OLAP_REGION:-us-east-1}.amazonaws.com'
206
199
  ],
207
200
  environment: {
208
201
  healthcheck_path: '/s3proxy-healthcheck',
209
- AWS_ACCESS_KEY_ID: config.s3?.accessKeyId || '',
210
- AWS_SECRET_ACCESS_KEY: config.s3?.secretAccessKey || '',
211
- REGION: region
202
+ AWS_ACCESS_KEY_ID: '${S3_UPLOADS_ACCESS_KEY_ID}',
203
+ AWS_SECRET_ACCESS_KEY: '${S3_UPLOADS_SECRET_ACCESS_KEY}',
204
+ REGION: '${S3_UPLOADS_REGION:-us-east-1}'
212
205
  },
213
206
  networks: ['wp-network']
214
207
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bostonuniversity/buwp-local",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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 start",
13
13
  "lint": "eslint ."
14
14
  },
15
15
  "keywords": [
@@ -0,0 +1,57 @@
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.