Files
timebank-cc-public/references/CONFIG_MANAGEMENT.md
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

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.