@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.
@@ -0,0 +1,838 @@
1
+ # Architecture
2
+
3
+ Technical overview of how buwp-local works under the hood.
4
+
5
+ ## System Overview
6
+
7
+ buwp-local is a CLI tool that generates and manages Docker Compose configurations for BU WordPress development environments.
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ buwp-local CLI │
12
+ │ (Node.js/Commander) │
13
+ └─────────────────────────────────────────────────────────────┘
14
+
15
+ ├─ Read Configuration
16
+ │ (.buwp-local.json)
17
+
18
+ ├─ Load Credentials
19
+ │ (Keychain or .env.local)
20
+
21
+ ├─ Generate Docker Compose
22
+ │ (docker-compose.yml)
23
+
24
+ └─ Execute Docker Compose
25
+ (via child process)
26
+
27
+ ┌─────────────────────────────────────────────────────────────┐
28
+ │ Docker Compose │
29
+ │ │
30
+ │ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ │
31
+ │ │WordPress │ │ MySQL │ │ Redis │ │S3 Proxy │ │
32
+ │ │ Container│ │Container │ │Container│ │Container│ │
33
+ │ └──────────┘ └──────────┘ └─────────┘ └─────────┘ │
34
+ │ │
35
+ │ ┌──────────────────┐ ┌──────────────────┐ │
36
+ │ │ db_data volume │ │ wp_build volume │ │
37
+ │ └──────────────────┘ └──────────────────┘ │
38
+ └─────────────────────────────────────────────────────────────┘
39
+ ```
40
+
41
+ ## Core Components
42
+
43
+ ### 1. CLI Framework
44
+
45
+ **Technology:** Commander.js
46
+
47
+ **Entry Point:** `bin/buwp-local.js`
48
+
49
+ **Structure:**
50
+ ```
51
+ bin/
52
+ buwp-local.js # CLI entry point, command routing
53
+
54
+ lib/
55
+ commands/
56
+ init.js # Interactive setup wizard
57
+ start.js # Start Docker Compose environment
58
+ stop.js # Stop containers
59
+ destroy.js # Remove containers and volumes
60
+ logs.js # View container logs
61
+ keychain.js # Keychain credential management
62
+ wp.js # WP-CLI proxy command
63
+
64
+ config.js # Configuration loading/validation
65
+ keychain.js # macOS Keychain integration
66
+ docker-compose.js # Docker Compose generation
67
+ utils.js # Utility functions
68
+ ```
69
+
70
+ ### 2. Configuration Management
71
+
72
+ **Config File:** `.buwp-local.json`
73
+
74
+ **Schema:**
75
+ ```json
76
+ {
77
+ "projectName": "string", // Unique project identifier
78
+ "hostname": "string", // Local hostname (e.g., project.local)
79
+ "multisite": "boolean", // Enable WordPress multisite
80
+ "image": "string", // Docker image to use
81
+ "services": {
82
+ "redis": "boolean", // Enable Redis cache
83
+ "s3proxy": "boolean", // Enable S3 proxy service
84
+ "shibboleth": "boolean" // Enable Shibboleth SSO
85
+ },
86
+ "ports": {
87
+ "http": "number", // HTTP port (default: 80)
88
+ "https": "number", // HTTPS port (default: 443)
89
+ "db": "number", // MySQL port (default: 3306)
90
+ "redis": "number" // Redis port (default: 6379)
91
+ },
92
+ "mappings": [
93
+ {
94
+ "local": "string", // Local path (relative or absolute)
95
+ "container": "string" // Container path (absolute)
96
+ }
97
+ ],
98
+ "env": {
99
+ "KEY": "value" // Custom environment variables
100
+ }
101
+ }
102
+ ```
103
+
104
+ **Loading Process:**
105
+
106
+ 1. Read `.buwp-local.json` from project directory
107
+ 2. Apply defaults for missing values
108
+ 3. Validate schema and paths
109
+ 4. Sanitize project name for Docker compatibility
110
+ 5. Merge with command-line options
111
+
112
+ **Module:** `lib/config.js`
113
+
114
+ ### 3. Credential Management
115
+
116
+ #### macOS Keychain Integration
117
+
118
+ **Service:** `buwp-local`
119
+ **Account:** `<CREDENTIAL_NAME>`
120
+ **Access:** Via `security` command-line tool
121
+
122
+ **Implementation:**
123
+ The current implementation uses the macOS `security` CLI to store, retrieve, and delete credentials securely in the Keychain. It is essentially a wrapper around these commands:
124
+
125
+ ```bash
126
+ # Set credential
127
+ security add-generic-password \
128
+ -s "buwp-local" \
129
+ -a "WORDPRESS_DB_PASSWORD" \
130
+ -w "password123" \
131
+ -U
132
+
133
+ # Get credential
134
+ security find-generic-password \
135
+ -s "buwp-local" \
136
+ -a "WORDPRESS_DB_PASSWORD" \
137
+ -w
138
+
139
+ # Delete credential
140
+ security delete-generic-password \
141
+ -s "buwp-local" \
142
+ -a "WORDPRESS_DB_PASSWORD"
143
+ ```
144
+
145
+ **Features:**
146
+ - Stores 15 credential types
147
+ - Automatic hex decoding for legacy multiline credentials
148
+ - Secure storage with macOS encryption
149
+ - Global access across all projects
150
+
151
+ #### Credential Loading Flow
152
+
153
+ ```
154
+ Start Command
155
+
156
+ ├─ Check .env.local exists?
157
+ │ ├─ Yes → Load from .env.local (dotenv)
158
+ │ └─ No → Check Keychain
159
+ │ ├─ Found → Load from Keychain
160
+ │ └─ Not found → Prompt for setup
161
+
162
+ ├─ Validate required credentials
163
+ │ └─ Missing? → Offer interactive setup
164
+
165
+ └─ Create secure temp env file
166
+ └─ Pass to Docker Compose
167
+ ```
168
+
169
+ **Priority:**
170
+ 1. `.env.local` (highest - overrides Keychain)
171
+ 2. macOS Keychain (fallback)
172
+ 3. Error if not found
173
+
174
+ **Module:** `lib/config.js` (`loadKeychainCredentials()`)
175
+
176
+ ### 4. Secure Temporary Files
177
+
178
+ **Purpose:** Pass credentials to Docker Compose securely
179
+
180
+ **Location:** `.buwp-local/.env.XXXXXX` (random suffix)
181
+
182
+ **Permissions:** `0600` (owner read/write only)
183
+
184
+ **Lifecycle:**
185
+ 1. **Create:** Before starting containers
186
+ 2. **Use:** Docker Compose reads via `env_file` directive
187
+ 3. **Delete:** After stopping containers or on process exit
188
+
189
+ **Implementation:**
190
+ ```javascript
191
+ // lib/config.js
192
+
193
+ export function createSecureTempEnvFile(credentials) {
194
+ const tmpDir = path.join(process.cwd(), '.buwp-local');
195
+ fs.mkdirSync(tmpDir, { recursive: true });
196
+
197
+ const tmpFile = fs.mkdtempSync(path.join(tmpDir, '.env.'));
198
+ const envFilePath = path.join(tmpFile, '.env');
199
+
200
+ // Write credentials with escaped newlines
201
+ const content = Object.entries(credentials)
202
+ .map(([key, value]) => {
203
+ const escaped = value.replace(/\n/g, '\\n');
204
+ return `${key}=${escaped}`;
205
+ })
206
+ .join('\n');
207
+
208
+ fs.writeFileSync(envFilePath, content, { mode: 0o600 });
209
+
210
+ return envFilePath;
211
+ }
212
+
213
+ export function secureDeleteTempEnvFile(filePath) {
214
+ if (fs.existsSync(filePath)) {
215
+ fs.unlinkSync(filePath);
216
+ fs.rmdirSync(path.dirname(filePath));
217
+ }
218
+ }
219
+ ```
220
+
221
+ **Why Not Process Environment?**
222
+ - ❌ Process env vars visible in `ps` output
223
+ - ❌ Can't handle multiline values reliably
224
+ - ✅ Temp files are more secure
225
+ - ✅ Docker Compose `env_file` is standard pattern
226
+
227
+ ### 5. Docker Compose Generation
228
+
229
+ **Template:** Generated dynamically in memory
230
+
231
+ **Generation Process:**
232
+
233
+ 1. **Base Services** (always included):
234
+ ```yaml
235
+ services:
236
+ wordpress:
237
+ image: ghcr.io/bu-ist/bu-wp-docker-mod_shib:arm64-latest
238
+ environment:
239
+ WORDPRESS_DB_HOST: db
240
+ WORDPRESS_DB_USER: wordpress
241
+ env_file: .buwp-local/.env.XXXXXX
242
+ volumes:
243
+ - wp_build:/var/www/html
244
+
245
+ db:
246
+ image: mysql:8.0
247
+ environment:
248
+ MYSQL_DATABASE: wordpress
249
+ MYSQL_USER: wordpress
250
+ env_file: .buwp-local/.env.XXXXXX
251
+ volumes:
252
+ - db_data:/var/lib/mysql
253
+ ```
254
+
255
+ 2. **Add Optional Services** (if enabled):
256
+ ```yaml
257
+ redis:
258
+ image: redis:alpine
259
+
260
+ s3proxy:
261
+ image: ghcr.io/bu-ist/s3-proxy:latest
262
+ ```
263
+
264
+ 3. **Add Volume Mappings**:
265
+ ```yaml
266
+ wordpress:
267
+ volumes:
268
+ - ./:/var/www/html/wp-content/plugins/my-plugin
269
+ ```
270
+
271
+ 4. **Add Port Mappings**:
272
+ ```yaml
273
+ wordpress:
274
+ ports:
275
+ - "80:80"
276
+ - "443:443"
277
+ ```
278
+
279
+ 5. **Add Named Volumes**:
280
+ ```yaml
281
+ volumes:
282
+ db_data:
283
+ name: ${projectName}_db_data
284
+ wp_build:
285
+ name: ${projectName}_wp_build
286
+ ```
287
+
288
+ **Module:** `lib/docker-compose.js`
289
+
290
+ **Output:** `.buwp-local/docker-compose.yml`
291
+
292
+ **Important:** Generated file contains **variable references** (`${VAR_NAME}`), not actual credential values.
293
+
294
+ ### 6. Docker Compose Execution
295
+
296
+ **Process:** Node.js child process spawning `docker compose` command
297
+
298
+ Once the docker-compose.yml is generated, buwp-local executes Docker Compose commands using the following pattern:
299
+
300
+ **Commands:**
301
+ ```bash
302
+ # Start
303
+ docker compose -f .buwp-local/docker-compose.yml \
304
+ --project-name ${projectName} \
305
+ --env-file .buwp-local/.env.XXXXXX \
306
+ up -d
307
+
308
+ # Stop
309
+ docker compose -f .buwp-local/docker-compose.yml \
310
+ --project-name ${projectName} \
311
+ down
312
+
313
+ # Destroy
314
+ docker compose -f .buwp-local/docker-compose.yml \
315
+ --project-name ${projectName} \
316
+ down -v
317
+
318
+ # Logs
319
+ docker compose -f .buwp-local/docker-compose.yml \
320
+ --project-name ${projectName} \
321
+ logs
322
+ ```
323
+
324
+ **Project Names:**
325
+ - Each project gets unique Docker Compose project name
326
+ - Format: sanitized directory name (lowercase, alphanumeric)
327
+ - Enables running multiple projects simultaneously
328
+ - All containers/volumes prefixed with project name
329
+
330
+ **Module:** `lib/commands/start.js`, `lib/commands/stop.js`
331
+
332
+ ### 7. Volume Management
333
+
334
+ **Per-Project Volumes:**
335
+
336
+ ```yaml
337
+ volumes:
338
+ db_data:
339
+ name: ${projectName}_db_data # MySQL data
340
+ wp_build:
341
+ name: ${projectName}_wp_build # WordPress files
342
+ ```
343
+
344
+ **Bind Mounts (Volume Mappings):**
345
+
346
+ ```yaml
347
+ volumes:
348
+ - ./:/var/www/html/wp-content/plugins/my-plugin
349
+ - ../theme:/var/www/html/wp-content/themes/my-theme
350
+ ```
351
+
352
+ **Volume Isolation:**
353
+ - Each project name gets unique volumes
354
+ - Prevents database conflicts between projects
355
+ - Allows multiple projects to run simultaneously
356
+ - Volumes persist until `destroy` command
357
+
358
+ **Shared Environment:**
359
+ - Multiple repos with same `projectName` share volumes
360
+ - Enables integration testing
361
+ - All mappings accumulate
362
+
363
+ ### 8. WP-CLI Proxy
364
+
365
+ **Implementation:**
366
+ ```bash
367
+ docker compose --project-name ${projectName} \
368
+ -f .buwp-local/docker-compose.yml \
369
+ exec wordpress \
370
+ wp ${args}
371
+ ```
372
+
373
+ **Usage:**
374
+ ```bash
375
+ npx buwp-local wp plugin list
376
+ npx buwp-local wp user create username user@bu.edu
377
+ ```
378
+
379
+ **Features:**
380
+ - Passes all arguments through to WP-CLI
381
+ - Runs inside WordPress container
382
+ - Requires containers to be running
383
+
384
+ **Module:** `lib/commands/wp.js`
385
+
386
+ ## Architectural Advantages
387
+
388
+ ### Why Docker + Volume Mappings Over VM Sandboxes
389
+
390
+ Traditional VM sandbox environments face a fundamental architectural limitation: **WordPress core and developer code share the same filesystem**. This creates a destructive update cycle.
391
+
392
+ #### The VM Sandbox Problem
393
+
394
+ ```
395
+ ┌───────────────────────────────────────┐
396
+ │ VM Filesystem │
397
+ │ │
398
+ │ /var/www/html/ │
399
+ │ ├── wp-admin/ (WordPress core)│
400
+ │ ├── wp-includes/ (WordPress core)│
401
+ │ └── wp-content/ │
402
+ │ ├── plugins/ │
403
+ │ │ └── my-plugin/ ← Your code
404
+ │ └── themes/ │
405
+ │ └── my-theme/ ← Your code
406
+ │ │
407
+ │ ALL IN ONE PLACE = Problem! │
408
+ └───────────────────────────────────────┘
409
+
410
+ To update WordPress → Must rebuild entire VM
411
+ Rebuild entire VM → Your code gets overwritten
412
+ ```
413
+
414
+ **Consequences:**
415
+ - Monthly rebuild schedules required
416
+ - Developers must backup code before rebuilds
417
+ - Team-wide coordination overhead
418
+ - All-or-nothing updates (everyone updates together)
419
+ - Risk of losing work if backup/restore fails
420
+
421
+ #### The buwp-local Solution
422
+
423
+ Docker's **volume mapping architecture** creates a clean separation:
424
+
425
+ ```
426
+ ┌─────────────────────────────────┐
427
+ │ Your Mac's Filesystem │ ← Developer code lives here
428
+ │ │ (permanent, survives updates)
429
+ │ ~/projects/my-plugin/ │
430
+ │ ├── my-plugin.php │
431
+ │ ├── includes/ │
432
+ │ └── assets/ │
433
+ └────────────┬────────────────────┘
434
+ │ Volume mapping (bind mount)
435
+
436
+ ┌─────────────────────────────────┐
437
+ │ Docker Container │ ← WordPress core lives here
438
+ │ │ (disposable, updates don't affect code)
439
+ │ /var/www/html/ │
440
+ │ ├── wp-admin/ (from image)│
441
+ │ ├── wp-includes/ (from image)│
442
+ │ └── wp-content/ │
443
+ │ └── plugins/ │
444
+ │ └── my-plugin/ ──┐ │ (mapped from Mac)
445
+ │ │ │
446
+ │ SEPARATED = No conflicts! │ │
447
+ └────────────────────────────────┴─┘
448
+ ```
449
+
450
+ **Technical Benefits:**
451
+
452
+ 1. **Persistent Code** - Your code lives on the host filesystem, not in the container. Destroy and recreate containers as much as you want—code never changes.
453
+
454
+ 2. **Independent Updates** - Pull new WordPress images (`docker pull`) without affecting your development code. Update on your schedule, not a team calendar.
455
+
456
+ 3. **Instant Rollback** - Switch between WordPress versions by changing the image tag. Your code stays constant while you test different WordPress versions.
457
+
458
+ 4. **Zero Coordination** - Each developer controls their own environment. No rebuild meetings, no freeze periods, no waiting for others.
459
+
460
+ 5. **Safe Testing** - Test breaking changes in WordPress without risk. If something goes wrong, recreate the container—your code was never in danger.
461
+
462
+ #### Workflow Comparison
463
+
464
+ **VM Sandbox Workflow:**
465
+ ```
466
+ Week 1-3: Develop in VM
467
+ Week 4: Monthly rebuild
468
+ 1. Backup your code manually
469
+ 2. Coordinate with team
470
+ 3. Wait for new VM image
471
+ 4. Restore code manually
472
+ 5. Re-test everything
473
+ 6. Hope nothing broke
474
+ Time lost: 2-4 hours + coordination overhead
475
+ ```
476
+
477
+ **buwp-local Workflow:**
478
+ ```
479
+ Anytime: New WordPress version available
480
+ 1. docker pull ghcr.io/bu-ist/buwp:latest
481
+ 2. npx buwp-local start
482
+ 3. Test (your code unchanged)
483
+ Time: 2 minutes
484
+ ```
485
+
486
+ #### Real-World Example
487
+
488
+ **Scenario:** 6-week plugin development project. WordPress update lands in week 3.
489
+
490
+ **VM Approach:**
491
+ - Wait for scheduled monthly rebuild (week 4)
492
+ - Backup your work
493
+ - Rebuild overwrites everything
494
+ - Restore and re-test
495
+ - **Impact:** 2-4 hours lost, risk of data loss
496
+
497
+ **buwp-local Approach:**
498
+ - Pull new image when convenient (any time in week 3-6)
499
+ - Containers recreate with new WordPress version
500
+ - Your code untouched on local filesystem
501
+ - **Impact:** 2 minutes, zero risk
502
+
503
+ ### Separation of Concerns
504
+
505
+ This architecture provides clean boundaries:
506
+
507
+ | Concern | Location | Update Mechanism |
508
+ |---------|----------|------------------|
509
+ | WordPress core | Docker image | `docker pull` |
510
+ | BU plugins | Docker image | `docker pull` |
511
+ | BU theme | Docker image | `docker pull` |
512
+ | **Your plugin** | **Local filesystem** | **Your editor** |
513
+ | **Your theme** | **Local filesystem** | **Your editor** |
514
+ | Database | Docker volume | Persists across updates |
515
+ | Uploads | Docker volume | Persists across updates |
516
+
517
+ Updates to WordPress never touch your development code. Updates to your code never require rebuilding WordPress.
518
+
519
+ ### Additional Architectural Benefits
520
+
521
+ 1. **Live Reload** - File changes sync instantly via volume mappings. Save in your editor, refresh browser, see changes.
522
+
523
+ 2. **IDE Integration** - Use any editor (VS Code, PHPStorm, Sublime) with full IDE features (autocomplete, debugging, git integration).
524
+
525
+ 3. **Version Control** - Your code is already on the host filesystem where git lives. No special workflows to commit from inside VMs.
526
+
527
+ 4. **Multiple Projects** - Run multiple projects simultaneously, each with isolated environments. No VM resource conflicts.
528
+
529
+ 5. **Portable Configuration** - Commit `.buwp-local.json` to version control. Teammates get identical environments with one command.
530
+
531
+ For detailed migration guidance, see [MIGRATION_FROM_VM.md](MIGRATION_FROM_VM.md).
532
+
533
+ ## Security Model
534
+
535
+ ### Credential Security
536
+
537
+ **Storage:**
538
+ - **Keychain:** Encrypted by macOS, protected by user authentication
539
+ - **`.env.local`:** File permissions should be `0600` (user-managed)
540
+
541
+ **In Transit:**
542
+ - Loaded in Node.js process memory
543
+ - Written to temp file with `0600` permissions
544
+ - Never logged or displayed (except explicit `keychain get` command)
545
+
546
+ **In Docker:**
547
+ - Passed via `env_file` directive (not command line)
548
+ - Not visible in `docker ps` or process list
549
+ - Not written to generated `docker-compose.yml`
550
+
551
+ **Cleanup:**
552
+ - Temp files deleted on stop/destroy
553
+ - Process exit handlers ensure cleanup
554
+
555
+ ### File Permissions
556
+
557
+ ```
558
+ .buwp-local/
559
+ docker-compose.yml # 0644 (safe - no credentials)
560
+ .env.XXXXXX/ # 0700 directory
561
+ .env # 0600 file (credentials)
562
+ ```
563
+
564
+ ### Git Ignore
565
+
566
+ **Must be ignored:**
567
+ ```gitignore
568
+ .env.local
569
+ .buwp-local/
570
+ ```
571
+
572
+ **Safe to commit:**
573
+ ```
574
+ .buwp-local.json # Configuration template (no secrets)
575
+ .env.local.example # Example with placeholder values
576
+ ```
577
+
578
+ ## Multi-Project Architecture
579
+
580
+ ### Isolated Projects
581
+
582
+ ```
583
+ Project A (bu-custom-analytics):
584
+ Volumes:
585
+ - bu-custom-analytics_db_data
586
+ - bu-custom-analytics_wp_build
587
+ Containers:
588
+ - bu-custom-analytics-wordpress-1
589
+ - bu-custom-analytics-db-1
590
+ Hostname: bu-custom-analytics.local
591
+ Ports: 8080, 8443
592
+
593
+ Project B (bu-slideshow):
594
+ Volumes:
595
+ - bu-slideshow_db_data
596
+ - bu-slideshow_wp_build
597
+ Containers:
598
+ - bu-slideshow-wordpress-1
599
+ - bu-slideshow-db-1
600
+ Hostname: bu-slideshow.local
601
+ Ports: 8081, 8444
602
+ ```
603
+
604
+ **Key:** Different `projectName` = Complete isolation
605
+
606
+ ### Shared Projects
607
+
608
+ ```
609
+ Shared Environment (bu-sandbox):
610
+ Volumes:
611
+ - bu-sandbox_db_data # Shared
612
+ - bu-sandbox_wp_build # Shared
613
+ Containers:
614
+ - bu-sandbox-wordpress-1
615
+ - bu-sandbox-db-1
616
+ Mappings:
617
+ - ~/projects/plugin-a → /wp-content/plugins/plugin-a
618
+ - ~/projects/plugin-b → /wp-content/plugins/plugin-b
619
+ - ~/projects/theme → /wp-content/themes/theme
620
+ Hostname: bu-sandbox.local
621
+ Ports: 80, 443
622
+ ```
623
+
624
+ **Key:** Same `projectName` = Shared volumes and containers
625
+
626
+ ## Performance Considerations
627
+
628
+ ### Volume Performance
629
+
630
+ **Bind Mounts:**
631
+ - Direct host filesystem access
632
+ - Fast on macOS (Docker Desktop optimizations)
633
+ - Real-time code changes
634
+
635
+ **Named Volumes:**
636
+ - Better performance than bind mounts
637
+ - Used for database and WordPress core
638
+ - Persist between starts
639
+
640
+ ### Resource Usage
641
+
642
+ **Typical Project:**
643
+ - WordPress container: ~200-500 MB RAM
644
+ - MySQL container: ~200-400 MB RAM
645
+ - Redis container: ~10-20 MB RAM
646
+ - S3 Proxy container: ~50-100 MB RAM
647
+ - Total: ~500 MB - 1 GB per project
648
+
649
+ **Multiple Projects:**
650
+ - Linear scaling per project
651
+ - 3 projects ≈ 1.5-3 GB RAM
652
+ - Docker Desktop overhead: ~500 MB
653
+
654
+ ## Extensibility
655
+
656
+ ### Adding New Services
657
+
658
+ Edit `lib/docker-compose.js`:
659
+
660
+ ```javascript
661
+ if (config.services.myservice) {
662
+ compose.services.myservice = {
663
+ image: 'my/service:latest',
664
+ environment: {
665
+ SERVICE_CONFIG: '${MY_SERVICE_CONFIG}'
666
+ }
667
+ };
668
+ }
669
+ ```
670
+
671
+ Add credential type to `lib/keychain.js`:
672
+
673
+ ```javascript
674
+ const CREDENTIAL_TYPES = [
675
+ // ... existing types
676
+ 'MY_SERVICE_CONFIG'
677
+ ];
678
+ ```
679
+
680
+ ### Adding New Commands
681
+
682
+ Create `lib/commands/mycommand.js`:
683
+
684
+ ```javascript
685
+ import { Command } from 'commander';
686
+
687
+ export function myCommand(program) {
688
+ program
689
+ .command('mycommand')
690
+ .description('Description of my command')
691
+ .action(async (options) => {
692
+ // Implementation
693
+ });
694
+ }
695
+ ```
696
+
697
+ Register in `bin/buwp-local.js`:
698
+
699
+ ```javascript
700
+ import { myCommand } from '../lib/commands/mycommand.js';
701
+
702
+ myCommand(program);
703
+ ```
704
+
705
+ ### Custom Docker Images
706
+
707
+ Override in `.buwp-local.json`:
708
+
709
+ ```json
710
+ {
711
+ "image": "ghcr.io/bu-ist/custom-image:latest"
712
+ }
713
+ ```
714
+
715
+ ## Error Handling
716
+
717
+ ### Configuration Errors
718
+
719
+ **Validation:** Runs before Docker Compose generation
720
+
721
+ **Common Issues:**
722
+ - Missing required fields → Prompt with defaults
723
+ - Invalid JSON → Clear error message with line number
724
+ - Invalid paths → Resolve and validate before use
725
+
726
+ ### Docker Errors
727
+
728
+ **Detection:** Check exit codes from `docker compose` commands
729
+
730
+ **Common Issues:**
731
+ - Docker not running → Check `docker info`
732
+ - Port conflicts → Suggest alternate ports
733
+ - Image pull failures → Check authentication
734
+ - Volume permission issues → Suggest cleanup
735
+
736
+ ### Credential Errors
737
+
738
+ **Validation:** Before starting containers
739
+
740
+ **Common Issues:**
741
+ - Missing credentials → Offer interactive setup
742
+ - Keychain access denied → Provide Keychain Access instructions
743
+ - Invalid format → Clear error about expected format
744
+
745
+ ### Cleanup on Failure
746
+
747
+ These are the handlers to ensure temporary files are deleted on process exit:
748
+
749
+ **Process Exit Handlers:**
750
+ ```javascript
751
+ process.on('SIGINT', cleanup);
752
+ process.on('SIGTERM', cleanup);
753
+ process.on('exit', cleanup);
754
+
755
+ function cleanup() {
756
+ secureDeleteTempEnvFile(tempEnvPath);
757
+ }
758
+ ```
759
+
760
+ **Graceful Shutdown:**
761
+ - Delete temporary credential files
762
+ - Log cleanup actions
763
+ - Preserve volumes (unless `destroy`)
764
+
765
+ ## Dependencies
766
+
767
+ ### Runtime Dependencies
768
+
769
+ ```json
770
+ {
771
+ "commander": "^12.x", // CLI framework
772
+ "js-yaml": "^4.x", // YAML generation
773
+ "chalk": "^5.x", // Terminal colors
774
+ "prompts": "^2.x", // Interactive prompts
775
+ "dotenv": "^16.x" // .env.local parsing
776
+ }
777
+ ```
778
+
779
+ ### External Requirements
780
+
781
+ - **Node.js:** >=18.0.0 (ESM support)
782
+ - **Docker Desktop:** Latest stable
783
+ - **macOS:** 10.15+ (for Keychain integration)
784
+ - **Shell:** zsh or bash
785
+
786
+ ### Platform Support
787
+
788
+ **Current:**
789
+ - ✅ macOS (Intel and Apple Silicon)
790
+ - ✅ Keychain integration (macOS only)
791
+
792
+ **Future:**
793
+ - 🔄 Linux (credential storage TBD)
794
+ - 🔄 Windows (WSL2 + credential storage TBD)
795
+
796
+ ## Testing Strategy
797
+
798
+ ### Manual Testing
799
+
800
+ **Test Commands:**
801
+
802
+ Unit tests not implemented yet, recommend vitest as with bu-protected-s3-object-lambda.
803
+
804
+ ### Validation
805
+
806
+ **Config Validation:**
807
+ ```bash
808
+ npx buwp-local config --validate
809
+ ```
810
+
811
+ **Credential Validation:**
812
+ ```bash
813
+ npx buwp-local keychain status
814
+ ```
815
+
816
+ **Docker Validation:**
817
+ ```bash
818
+ docker compose -f .buwp-local/docker-compose.yml config
819
+ ```
820
+
821
+ ## Future Enhancements
822
+
823
+ ### Planned Features
824
+
825
+ - **Cross-platform credential storage** (Linux, Windows)
826
+ - **Automatic /etc/hosts management** (detect missing entries)
827
+ - **SSL certificate generation** (local HTTPS)
828
+ - **Central registry** (shared team configurations)
829
+ - **Health checks** (verify services are running)
830
+ - **Performance monitoring** (container resource usage)
831
+ - **Unit tests** vitest
832
+
833
+ ## See Also
834
+
835
+ - [Getting Started](GETTING_STARTED.md) - User guide
836
+ - [Commands Reference](COMMANDS.md) - Full command list
837
+ - [Credentials Management](CREDENTIALS.md) - Security details
838
+ - [Multi-Project Setup](MULTI_PROJECT.md) - Running multiple projects