Initial commit
This commit is contained in:
622
references/CONFIG_MANAGEMENT.md
Normal file
622
references/CONFIG_MANAGEMENT.md
Normal file
@@ -0,0 +1,622 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user