623 lines
17 KiB
Markdown
623 lines
17 KiB
Markdown
# Configuration Management System
|
|
|
|
## Overview
|
|
|
|
The configuration management system provides a safe, automated way to merge new configuration keys from version-controlled `.example` files into active configuration files during deployments. This system is essential for white-label installations where each deployment has custom configuration values that must be preserved across updates.
|
|
|
|
### The Problem It Solves
|
|
|
|
In a white-label Laravel application with multiple installations:
|
|
- **Active config files** (`config/timebank_cc.php`, `config/timebank-default.php`, `config/themes.php`) contain installation-specific custom values
|
|
- **Example config files** (`config/*.php.example`) are tracked in git and receive updates with new features
|
|
- During deployment, new configuration keys must be added **without overwriting existing custom values**
|
|
- Manual merging is error-prone and doesn't scale across multiple installations
|
|
|
|
### How It Works
|
|
|
|
The system uses a **deep merge algorithm** that:
|
|
1. Recursively compares active configs with their `.example` counterparts
|
|
2. Identifies new keys that don't exist in the active config
|
|
3. Adds only the new keys while preserving ALL existing custom values
|
|
4. Creates automatic timestamped backups before any changes
|
|
5. Validates the merged configuration can be loaded successfully
|
|
|
|
**Key Principle**: Existing values are NEVER overwritten. Only new keys are added.
|
|
|
|
### Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Deployment Flow │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ 1. deploy.sh runs │
|
|
│ 2. Git pulls latest code (includes updated .example files) │
|
|
│ 3. Config merge check runs │
|
|
│ ├─ Compares: config/file.php vs config/file.php.example │
|
|
│ ├─ Detects: New keys in .example │
|
|
│ └─ Prompts: "Merge new keys? (y/N)" │
|
|
│ 4. If user confirms: │
|
|
│ ├─ Creates backup: storage/config-backups/file.php.backup.* │
|
|
│ ├─ Deep merges: Adds new keys, preserves existing values │
|
|
│ ├─ Validates: Ensures merged config is valid PHP │
|
|
│ └─ Logs: Records merge to Laravel log │
|
|
│ 5. Deployment continues │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Files Managed
|
|
|
|
The system manages three configuration files:
|
|
|
|
| Config File | Purpose | Custom Values Example |
|
|
|------------|---------|----------------------|
|
|
| `config/themes.php` | Theme definitions and color schemes | Custom brand colors, font choices |
|
|
| `config/timebank-default.php` | Default platform configuration | Transaction limits, validation rules |
|
|
| `config/timebank_cc.php` | Installation-specific overrides | Platform name, currency, feature flags |
|
|
|
|
Each has a corresponding `.example` file tracked in git:
|
|
- `config/themes.php.example`
|
|
- `config/timebank-default.php.example`
|
|
- `config/timebank_cc.php.example`
|
|
|
|
## Setup Instructions
|
|
|
|
### Initial Installation
|
|
|
|
When setting up a new installation, the deployment script automatically creates active config files from examples if they don't exist:
|
|
|
|
```bash
|
|
# During first deployment, deploy.sh automatically runs:
|
|
for config_file in config/themes.php config/timebank-default.php config/timebank_cc.php; do
|
|
if [ ! -f "$config_file" ] && [ -f "${config_file}.example" ]; then
|
|
cp "${config_file}.example" "$config_file"
|
|
fi
|
|
done
|
|
```
|
|
|
|
### Customizing Configuration
|
|
|
|
After initial setup, customize your active config files:
|
|
|
|
```php
|
|
// config/timebank_cc.php
|
|
|
|
return [
|
|
'platform_name' => 'My TimeBank', // Custom installation name
|
|
'currency' => 'hours', // Custom currency name
|
|
|
|
'wirechat' => [
|
|
'disappearing_messages' => [
|
|
'duration' => 720, // Custom: 12 hours instead of default 6
|
|
],
|
|
],
|
|
|
|
'transactions' => [
|
|
'limits' => [
|
|
'user' => 1000, // Custom: higher limit than default
|
|
],
|
|
],
|
|
];
|
|
```
|
|
|
|
**Important**: Never edit `.example` files for installation-specific changes. Always edit the active config files.
|
|
|
|
### Backup System
|
|
|
|
The system automatically manages backups:
|
|
|
|
- **Location**: `storage/config-backups/`
|
|
- **Format**: `{filename}.backup.{YYYY-MM-DD_HHmmss}`
|
|
- **Retention**: Last 5 backups per file (older ones auto-deleted)
|
|
- **Created**: Before every merge operation
|
|
|
|
Example backup files:
|
|
```
|
|
storage/config-backups/
|
|
├── timebank_cc.php.backup.2026-01-06_143022
|
|
├── timebank_cc.php.backup.2026-01-05_091534
|
|
├── timebank_cc.php.backup.2026-01-03_164411
|
|
├── themes.php.backup.2026-01-06_143022
|
|
└── themes.php.backup.2026-01-04_102357
|
|
```
|
|
|
|
## Updating Configuration / New Deploys
|
|
|
|
### Automatic Merge During Deployment
|
|
|
|
When running `deploy.sh`, the system automatically detects configuration updates:
|
|
|
|
```bash
|
|
./deploy.sh
|
|
```
|
|
|
|
**What happens**:
|
|
|
|
1. **Detection Phase**:
|
|
```
|
|
═══════════════════════════════════════════════════════════
|
|
CHECKING FOR CONFIGURATION UPDATES
|
|
═══════════════════════════════════════════════════════════
|
|
New configuration keys available in .example files
|
|
Review changes with: php artisan config:merge --all --dry-run
|
|
|
|
Would you like to merge new keys now? (y/N)
|
|
```
|
|
|
|
2. **If you press 'y'**:
|
|
- Creates automatic backup
|
|
- Merges new keys
|
|
- Preserves all existing values
|
|
- Shows summary of changes
|
|
|
|
3. **If you press 'N'**:
|
|
- Deployment continues
|
|
- You can merge manually later
|
|
|
|
### Manual Configuration Merge
|
|
|
|
You can merge configurations manually at any time:
|
|
|
|
#### Preview Changes (Dry-Run Mode)
|
|
|
|
**Recommended first step**: Always preview changes before applying:
|
|
|
|
```bash
|
|
# Preview all config files
|
|
php artisan config:merge --all --dry-run
|
|
|
|
# Preview specific config
|
|
php artisan config:merge timebank_cc --dry-run
|
|
```
|
|
|
|
**Example output**:
|
|
```
|
|
─────────────────────────────────────────────────────
|
|
Config: timebank_cc
|
|
─────────────────────────────────────────────────────
|
|
Found 3 new configuration key(s):
|
|
|
|
+ wirechat.notifications.sound_enabled
|
|
true
|
|
+ wirechat.notifications.desktop_enabled
|
|
true
|
|
+ footer.sections.2.links.3
|
|
[route: static-contact, title: Contact, order: 3, visible: true]
|
|
```
|
|
|
|
#### Apply Changes
|
|
|
|
```bash
|
|
# Merge all configs (with confirmation prompts)
|
|
php artisan config:merge --all
|
|
|
|
# Merge specific config
|
|
php artisan config:merge timebank_cc
|
|
php artisan config:merge timebank-default
|
|
php artisan config:merge themes
|
|
|
|
# Merge without confirmation (automated deployments)
|
|
php artisan config:merge --all --force
|
|
```
|
|
|
|
**Interactive merge process**:
|
|
```
|
|
─────────────────────────────────────────────────────
|
|
Config: timebank_cc
|
|
─────────────────────────────────────────────────────
|
|
Found 3 new configuration key(s):
|
|
|
|
+ wirechat.notifications.sound_enabled
|
|
true
|
|
+ wirechat.notifications.desktop_enabled
|
|
true
|
|
+ footer.tagline
|
|
"Your time is currency"
|
|
|
|
Merge these keys into timebank_cc? (yes/no) [no]:
|
|
> yes
|
|
|
|
Backup created: storage/config-backups/timebank_cc.php.backup.2026-01-06_143022
|
|
✓ timebank_cc: Successfully merged 3 new keys
|
|
```
|
|
|
|
### Restoring from Backup
|
|
|
|
If you need to rollback a configuration change:
|
|
|
|
```bash
|
|
php artisan config:merge --restore
|
|
```
|
|
|
|
**Interactive restore process**:
|
|
```
|
|
Available backups:
|
|
|
|
timebank_cc.php
|
|
1. 2026-01-06 14:30:22
|
|
2. 2026-01-05 09:15:34
|
|
3. 2026-01-03 16:44:11
|
|
|
|
themes.php
|
|
4. 2026-01-06 14:30:22
|
|
5. 2026-01-04 10:23:57
|
|
|
|
Enter backup number to restore (or 0 to cancel):
|
|
> 1
|
|
|
|
Restore timebank_cc.php from backup? (yes/no) [no]:
|
|
> yes
|
|
|
|
Current config backed up to: storage/config-backups/timebank_cc.php.backup.2026-01-06_144512
|
|
✓ Successfully restored timebank_cc.php
|
|
```
|
|
|
|
## Command Reference
|
|
|
|
### php artisan config:merge
|
|
|
|
Merge new configuration keys from `.example` files into active configs.
|
|
|
|
**Syntax**:
|
|
```bash
|
|
php artisan config:merge [file] [options]
|
|
```
|
|
|
|
**Arguments**:
|
|
- `file` - Specific config to merge: `themes`, `timebank-default`, or `timebank_cc` (optional)
|
|
|
|
**Options**:
|
|
- `--all` - Merge all config files
|
|
- `--dry-run` - Preview changes without applying
|
|
- `--force` - Skip confirmation prompts
|
|
- `--restore` - Restore from backup (interactive)
|
|
|
|
**Examples**:
|
|
|
|
```bash
|
|
# Preview all changes
|
|
php artisan config:merge --all --dry-run
|
|
|
|
# Merge all with confirmation
|
|
php artisan config:merge --all
|
|
|
|
# Merge specific file
|
|
php artisan config:merge timebank_cc
|
|
|
|
# Automated merge (no prompts)
|
|
php artisan config:merge --all --force
|
|
|
|
# Restore from backup
|
|
php artisan config:merge --restore
|
|
```
|
|
|
|
**Exit Codes**:
|
|
- `0` - Success (changes applied or no changes needed)
|
|
- `1` - Error (invalid file, backup failed, etc.)
|
|
|
|
## Deep Merge Algorithm
|
|
|
|
### How It Works
|
|
|
|
The deep merge algorithm recursively processes configuration arrays:
|
|
|
|
```php
|
|
function deepMergeNewKeys(current, example):
|
|
result = current // Start with existing config
|
|
|
|
for each key, value in example:
|
|
if key does NOT exist in current:
|
|
// NEW KEY - Add it
|
|
result[key] = value
|
|
|
|
else if value is array AND current[key] is array:
|
|
// Both are arrays - RECURSE
|
|
result[key] = deepMergeNewKeys(current[key], value)
|
|
|
|
else:
|
|
// Key exists and not both arrays - PRESERVE current value
|
|
// Do nothing - keep current[key] unchanged
|
|
|
|
return result
|
|
```
|
|
|
|
### Merge Examples
|
|
|
|
#### Example 1: Adding New Top-Level Keys
|
|
|
|
**Current config**:
|
|
```php
|
|
[
|
|
'platform_name' => 'My TimeBank', // Custom value
|
|
'currency' => 'hours', // Custom value
|
|
]
|
|
```
|
|
|
|
**Example config** (from git update):
|
|
```php
|
|
[
|
|
'platform_name' => 'TimeBank CC', // Default value
|
|
'currency' => 'time', // Default value
|
|
'timezone' => 'UTC', // NEW KEY
|
|
]
|
|
```
|
|
|
|
**Result after merge**:
|
|
```php
|
|
[
|
|
'platform_name' => 'My TimeBank', // PRESERVED - custom value kept
|
|
'currency' => 'hours', // PRESERVED - custom value kept
|
|
'timezone' => 'UTC', // ADDED - new key
|
|
]
|
|
```
|
|
|
|
#### Example 2: Adding Nested Keys
|
|
|
|
**Current config**:
|
|
```php
|
|
[
|
|
'wirechat' => [
|
|
'disappearing_messages' => [
|
|
'duration' => 720, // Custom: 12 hours
|
|
],
|
|
],
|
|
]
|
|
```
|
|
|
|
**Example config** (from git update):
|
|
```php
|
|
[
|
|
'wirechat' => [
|
|
'disappearing_messages' => [
|
|
'duration' => 360, // Default: 6 hours
|
|
'cleanup_schedule' => 'hourly', // NEW KEY
|
|
],
|
|
'notifications' => [ // NEW NESTED SECTION
|
|
'sound_enabled' => true,
|
|
'desktop_enabled' => true,
|
|
],
|
|
],
|
|
]
|
|
```
|
|
|
|
**Result after merge**:
|
|
```php
|
|
[
|
|
'wirechat' => [
|
|
'disappearing_messages' => [
|
|
'duration' => 720, // PRESERVED - custom value kept
|
|
'cleanup_schedule' => 'hourly', // ADDED - new key
|
|
],
|
|
'notifications' => [ // ADDED - entire new section
|
|
'sound_enabled' => true,
|
|
'desktop_enabled' => true,
|
|
],
|
|
],
|
|
]
|
|
```
|
|
|
|
#### Example 3: Array Values
|
|
|
|
**Current config**:
|
|
```php
|
|
[
|
|
'allowed_types' => ['user', 'organization'], // Custom list
|
|
]
|
|
```
|
|
|
|
**Example config** (from git update):
|
|
```php
|
|
[
|
|
'allowed_types' => ['user', 'organization', 'bank'], // Updated list
|
|
]
|
|
```
|
|
|
|
**Result after merge**:
|
|
```php
|
|
[
|
|
'allowed_types' => ['user', 'organization'], // PRESERVED - arrays not merged
|
|
]
|
|
```
|
|
|
|
**Note**: Array values are treated as complete values, not merged element-by-element. If you need the new array values, update them manually after reviewing the diff.
|
|
|
|
## Best Practices
|
|
|
|
### 1. Always Preview First
|
|
|
|
Before applying configuration merges, especially in production:
|
|
|
|
```bash
|
|
# See exactly what will change
|
|
php artisan config:merge --all --dry-run
|
|
```
|
|
|
|
### 2. Review Changes in Detail
|
|
|
|
When new keys are detected:
|
|
1. Review what each new key does (check git commit messages)
|
|
2. Verify default values are appropriate for your installation
|
|
3. Adjust values after merge if needed
|
|
|
|
### 3. Test After Merging
|
|
|
|
After merging configuration changes:
|
|
|
|
```bash
|
|
# Verify config can be loaded
|
|
php artisan config:cache
|
|
|
|
# Clear and rebuild cache
|
|
php artisan optimize:clear
|
|
php artisan optimize
|
|
|
|
# Test critical functionality
|
|
php artisan test
|
|
```
|
|
|
|
### 4. Keep Backups
|
|
|
|
The system keeps 5 backups automatically, but for major updates:
|
|
|
|
```bash
|
|
# Create manual backup before major changes
|
|
cp config/timebank_cc.php config/timebank_cc.php.manual-backup-$(date +%Y%m%d)
|
|
```
|
|
|
|
### 5. Document Custom Changes
|
|
|
|
Add comments to your active config files explaining why values differ from defaults:
|
|
|
|
```php
|
|
return [
|
|
'transactions' => [
|
|
'limits' => [
|
|
// Custom: Increased from 500 to 1000 for high-volume community
|
|
'user' => 1000,
|
|
],
|
|
],
|
|
];
|
|
```
|
|
|
|
### 6. Staged Deployments
|
|
|
|
For multi-server deployments:
|
|
1. Test config merge on staging server first
|
|
2. Verify application functionality
|
|
3. Then deploy to production with `--force` flag
|
|
|
|
```bash
|
|
# Staging (with prompts)
|
|
./deploy.sh
|
|
|
|
# Production (automated)
|
|
./deploy.sh --force
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Config Merge Shows No Changes But I Know There Are Updates
|
|
|
|
**Cause**: The active config may already have the keys (possibly added manually).
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Compare files manually
|
|
diff config/timebank_cc.php config/timebank_cc.php.example
|
|
```
|
|
|
|
### Merge Failed - Config Invalid
|
|
|
|
**Symptom**: Error message "Merged config is invalid"
|
|
|
|
**Cause**: Syntax error in merged configuration.
|
|
|
|
**Solution**: Automatic rollback occurs. Check Laravel log for details:
|
|
```bash
|
|
tail -50 storage/logs/laravel.log | grep "config:merge"
|
|
```
|
|
|
|
### Backup Directory Not Writable
|
|
|
|
**Symptom**: "Failed to create backup"
|
|
|
|
**Solution**: Ensure storage directory is writable:
|
|
```bash
|
|
chmod -R 775 storage
|
|
chown -R www-data:www-data storage # Adjust user/group as needed
|
|
```
|
|
|
|
### Need to Restore But Backups Are Missing
|
|
|
|
**Cause**: Backups older than 5 merges were auto-deleted.
|
|
|
|
**Solution**:
|
|
- If config is in git-ignored files, check git stash
|
|
- Restore from server backups
|
|
- Manually recreate configuration
|
|
|
|
**Prevention**: Create manual backups before major updates.
|
|
|
|
### Config Merge Hangs During Deployment
|
|
|
|
**Cause**: Waiting for user input in automated environment.
|
|
|
|
**Solution**: Use `--force` flag in automated deployments:
|
|
```bash
|
|
# In deploy.sh for automated servers
|
|
php artisan config:merge --all --force
|
|
```
|
|
|
|
## Integration with CI/CD
|
|
|
|
### Automated Deployments
|
|
|
|
For CI/CD pipelines, use non-interactive mode:
|
|
|
|
```bash
|
|
# In deployment script
|
|
php artisan config:merge --all --force || {
|
|
echo "Config merge failed - check logs"
|
|
exit 1
|
|
}
|
|
```
|
|
|
|
### Pre-Deployment Validation
|
|
|
|
Check for config updates before deployment:
|
|
|
|
```bash
|
|
# In CI pipeline
|
|
if php artisan config:merge --all --dry-run | grep -q "Found [0-9]"; then
|
|
echo "⚠️ Configuration updates detected - review required"
|
|
php artisan config:merge --all --dry-run
|
|
fi
|
|
```
|
|
|
|
### Post-Deployment Verification
|
|
|
|
Verify config after deployment:
|
|
|
|
```bash
|
|
# Ensure config is valid
|
|
php artisan config:cache || {
|
|
echo "Config cache failed - configuration may be invalid"
|
|
exit 1
|
|
}
|
|
|
|
# Run config-dependent tests
|
|
php artisan test --filter ConfigTest
|
|
```
|
|
|
|
## Logging and Auditing
|
|
|
|
All configuration merges are logged to Laravel's log system:
|
|
|
|
```bash
|
|
# View recent config merges
|
|
tail -100 storage/logs/laravel.log | grep "Config merged"
|
|
```
|
|
|
|
**Log entry example**:
|
|
```json
|
|
{
|
|
"level": "info",
|
|
"message": "Config merged",
|
|
"context": {
|
|
"file": "timebank_cc",
|
|
"path": "config/timebank_cc.php",
|
|
"new_keys": [
|
|
"wirechat.notifications.sound_enabled",
|
|
"wirechat.notifications.desktop_enabled",
|
|
"footer.tagline"
|
|
],
|
|
"new_key_count": 3,
|
|
"backup": "storage/config-backups/timebank_cc.php.backup.2026-01-06_143022"
|
|
},
|
|
"timestamp": "2026-01-06 14:30:22"
|
|
}
|
|
```
|
|
|
|
This provides a complete audit trail of all configuration changes.
|