feat: update deployment configuration and encrypted env loader

- Update Ansible playbooks and roles for application deployment
- Add new Gitea/Traefik troubleshooting playbooks
- Update Docker Compose configurations (base, local, staging, production)
- Enhance EncryptedEnvLoader with improved error handling
- Add deployment scripts (autossh setup, migration, secret testing)
- Update CI/CD workflows and documentation
- Add Semaphore stack configuration
This commit is contained in:
2025-11-02 20:38:06 +01:00
parent 7b7f0b41d2
commit 24cbbccf4c
44 changed files with 5280 additions and 276 deletions

View File

@@ -45,6 +45,15 @@ describe('EncryptedEnvLoader', function () {
if (isset($this->envDevelopmentFile) && file_exists($this->envDevelopmentFile)) {
unlink($this->envDevelopmentFile);
}
if (isset($this->envBaseFile) && file_exists($this->envBaseFile)) {
unlink($this->envBaseFile);
}
if (isset($this->envLocalFile) && file_exists($this->envLocalFile)) {
unlink($this->envLocalFile);
}
if (isset($this->envStagingFile) && file_exists($this->envStagingFile)) {
unlink($this->envStagingFile);
}
});
describe('load()', function () {
@@ -197,6 +206,197 @@ ENV);
});
});
describe('loadEnvironment() - Base + Override Pattern', function () {
it('loads .env.base first, then .env.local (local overrides base)', function () {
$_ENV['APP_ENV'] = 'development';
// Base file with common variables
$this->envBaseFile = $this->testDir . '/.env.base';
file_put_contents($this->envBaseFile, <<<ENV
APP_NAME=BaseApp
DB_HOST=db
DB_PORT=5432
DB_DATABASE=michaelschiemer
CACHE_PREFIX=app
ENV);
// Local file with overrides
$this->envLocalFile = $this->testDir . '/.env.local';
file_put_contents($this->envLocalFile, <<<ENV
APP_ENV=development
APP_DEBUG=true
DB_HOST=localhost
DB_PORT=3307
CACHE_PREFIX=local
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// Base values
expect($env->get('APP_NAME'))->toBe('BaseApp');
expect($env->get('DB_DATABASE'))->toBe('michaelschiemer');
// Local overrides
expect($env->get('APP_ENV'))->toBe('development');
expect($env->getBool('APP_DEBUG'))->toBeTrue();
expect($env->get('DB_HOST'))->toBe('localhost');
expect($env->getInt('DB_PORT'))->toBe(3307);
expect($env->get('CACHE_PREFIX'))->toBe('local');
});
it('loads .env.local only if .env.base exists', function () {
$_ENV['APP_ENV'] = 'development';
// Only .env.local (should fallback to legacy .env)
$this->envLocalFile = $this->testDir . '/.env.local';
file_put_contents($this->envLocalFile, <<<ENV
APP_ENV=development
DB_HOST=localhost
ENV);
// Legacy .env file (fallback)
$this->envFile = $this->testDir . '/.env';
file_put_contents($this->envFile, <<<ENV
APP_NAME=LegacyApp
DB_PORT=3306
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// Should load from legacy .env (fallback)
expect($env->get('APP_NAME'))->toBe('LegacyApp');
expect($env->getInt('DB_PORT'))->toBe(3306);
// .env.local should not be loaded if .env.base doesn't exist
// (Fallback logic: only load .env.local if .env.base exists)
});
it('falls back to legacy .env if .env.base and .env.local do not exist', function () {
$_ENV['APP_ENV'] = 'development';
// Only legacy .env file
$this->envFile = $this->testDir . '/.env';
file_put_contents($this->envFile, <<<ENV
APP_NAME=LegacyApp
APP_ENV=development
DB_HOST=localhost
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// Should load from legacy .env
expect($env->get('APP_NAME'))->toBe('LegacyApp');
expect($env->get('APP_ENV'))->toBe('development');
expect($env->get('DB_HOST'))->toBe('localhost');
});
it('prioritizes system ENV over .env.base and .env.local', function () {
$_ENV['APP_ENV'] = 'development';
$_ENV['DB_HOST'] = 'system_host';
$this->envBaseFile = $this->testDir . '/.env.base';
file_put_contents($this->envBaseFile, <<<ENV
APP_NAME=BaseApp
DB_HOST=db
ENV);
$this->envLocalFile = $this->testDir . '/.env.local';
file_put_contents($this->envLocalFile, <<<ENV
DB_HOST=localhost
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// System ENV should win
expect($env->get('DB_HOST'))->toBe('system_host');
// Base values should be loaded
expect($env->get('APP_NAME'))->toBe('BaseApp');
});
it('merges .env.base, .env.local, and .env.secrets correctly', function () {
$_ENV['APP_ENV'] = 'development';
$this->envBaseFile = $this->testDir . '/.env.base';
file_put_contents($this->envBaseFile, <<<ENV
APP_NAME=BaseApp
DB_HOST=db
ENV);
$this->envLocalFile = $this->testDir . '/.env.local';
file_put_contents($this->envLocalFile, <<<ENV
DB_HOST=localhost
ENV);
$this->secretsFile = $this->testDir . '/.env.secrets';
file_put_contents($this->secretsFile, <<<ENV
SECRET_API_KEY=my_secret
ENV);
$encryptionKey = 'test_encryption_key_32_chars_long';
$env = $this->loader->loadEnvironment($this->testDir, $encryptionKey);
// Base + Local + Secrets
expect($env->get('APP_NAME'))->toBe('BaseApp');
expect($env->get('DB_HOST'))->toBe('localhost');
expect($env->get('SECRET_API_KEY'))->toBe('my_secret');
});
it('loads .env.staging in staging environment', function () {
$_ENV['APP_ENV'] = 'staging';
$this->envBaseFile = $this->testDir . '/.env.base';
file_put_contents($this->envBaseFile, <<<ENV
APP_NAME=BaseApp
DB_HOST=db
ENV);
$this->envStagingFile = $this->testDir . '/.env.staging';
file_put_contents($this->envStagingFile, <<<ENV
APP_ENV=staging
APP_DEBUG=false
DB_HOST=staging_db
STAGING_FEATURE=enabled
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// Base values
expect($env->get('APP_NAME'))->toBe('BaseApp');
// Staging overrides
expect($env->get('APP_ENV'))->toBe('staging');
expect($env->getBool('APP_DEBUG'))->toBeFalse();
expect($env->get('DB_HOST'))->toBe('staging_db');
expect($env->get('STAGING_FEATURE'))->toBe('enabled');
});
it('prioritizes .env.staging over .env.local in staging environment', function () {
$_ENV['APP_ENV'] = 'staging';
$this->envBaseFile = $this->testDir . '/.env.base';
file_put_contents($this->envBaseFile, <<<ENV
DB_HOST=db
ENV);
$this->envLocalFile = $this->testDir . '/.env.local';
file_put_contents($this->envLocalFile, <<<ENV
DB_HOST=localhost
ENV);
$this->envStagingFile = $this->testDir . '/.env.staging';
file_put_contents($this->envStagingFile, <<<ENV
DB_HOST=staging_host
ENV);
$env = $this->loader->loadEnvironment($this->testDir);
// Staging should win
expect($env->get('DB_HOST'))->toBe('staging_host');
});
});
describe('loadEnvironment() - Development Priority', function () {
it('allows .env file to override system environment in development', function () {
// Simulate system environment