mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-29 01:54:37 +08:00
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Furox <177975472+Furox-Art@users.noreply.github.com> Co-authored-by: johandenhollander <johan.den.hollander@finalist.nl> Co-authored-by: João Cruz <161638790+SrJooJ@users.noreply.github.com> Co-authored-by: Saim Hashmi <hashmisaim037@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
247 lines
10 KiB
PHP
247 lines
10 KiB
PHP
<?php
|
|
|
|
use App\Actions\Database\StartDragonfly;
|
|
use App\Actions\Database\StartPostgresql;
|
|
use App\Jobs\ApplicationDeploymentJob;
|
|
use App\Livewire\Project\Shared\EnvironmentVariable\All;
|
|
use App\Models\Application;
|
|
use App\Models\ApplicationDeploymentQueue;
|
|
use App\Models\Environment;
|
|
use App\Models\InstanceSettings;
|
|
use App\Models\Project;
|
|
use App\Models\Server;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceApplication;
|
|
use App\Models\StandaloneDragonfly;
|
|
use App\Models\StandalonePostgresql;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Bus;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
InstanceSettings::query()->firstOrCreate(['id' => 0]);
|
|
Bus::fake();
|
|
|
|
$this->team = Team::factory()->create();
|
|
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
|
$this->destination = $this->server->standaloneDockers()->firstOrFail();
|
|
$project = Project::factory()->create(['team_id' => $this->team->id, 'name' => 'Shop $live']);
|
|
$this->environment = Environment::factory()->create(['project_id' => $project->id, 'name' => 'staging']);
|
|
});
|
|
|
|
function coolifyDefaultsPostgres(array $attributes = []): StandalonePostgresql
|
|
{
|
|
return StandalonePostgresql::forceCreate([
|
|
'uuid' => 'coolify-defaults-db',
|
|
'name' => 'Main database',
|
|
'image' => 'postgres:16-alpine',
|
|
'postgres_password' => 'password',
|
|
'environment_id' => test()->environment->id,
|
|
'destination_id' => test()->destination->id,
|
|
'destination_type' => test()->destination->getMorphClass(),
|
|
...$attributes,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
function coolifyDefaultsDatabaseEnvironment(object $action, $database): array
|
|
{
|
|
$action->database = $database->fresh();
|
|
|
|
return (new ReflectionMethod($action, 'generate_environment_variables'))->invoke($action);
|
|
}
|
|
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
function coolifyDefaultsApplicationEnv(Application $application, bool $forBuildTime = false, ?Server $deploymentServer = null): array
|
|
{
|
|
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
|
|
$job = $reflection->newInstanceWithoutConstructor();
|
|
$queue = Mockery::mock(ApplicationDeploymentQueue::class);
|
|
$queue->shouldReceive('addLogEntry')->andReturnNull();
|
|
|
|
$properties = [
|
|
'application' => $application->fresh(),
|
|
'application_deployment_queue' => $queue,
|
|
'build_pack' => 'nixpacks',
|
|
'mainServer' => $deploymentServer ?? $application->destination->server,
|
|
'pull_request_id' => 0,
|
|
'commit' => 'HEAD',
|
|
'branch' => 'main',
|
|
'container_name' => 'coolify-defaults-app',
|
|
'saved_outputs' => collect(),
|
|
];
|
|
foreach ($properties as $property => $value) {
|
|
$reflection->getProperty($property)->setValue($job, $value);
|
|
}
|
|
|
|
if ($forBuildTime) {
|
|
return collect($reflection->getMethod('generate_coolify_env_variables')->invoke($job, true))
|
|
->map(fn ($value, $key) => "{$key}={$value}")->values()->all();
|
|
}
|
|
|
|
return collect($reflection->getMethod('generate_runtime_environment_variables')->invoke($job))->all();
|
|
}
|
|
|
|
function coolifyDefaultsApplication(): Application
|
|
{
|
|
return Application::factory()->create([
|
|
'name' => 'My web app',
|
|
'environment_id' => test()->environment->id,
|
|
'destination_id' => test()->destination->id,
|
|
'destination_type' => test()->destination->getMorphClass(),
|
|
]);
|
|
}
|
|
|
|
function coolifyDefaultsService(string $environmentYaml = ''): Service
|
|
{
|
|
$service = Service::factory()->create([
|
|
'name' => 'blog',
|
|
'environment_id' => test()->environment->id,
|
|
'server_id' => test()->server->id,
|
|
'destination_id' => test()->destination->id,
|
|
'destination_type' => test()->destination->getMorphClass(),
|
|
'docker_compose_raw' => "services:\n app:\n image: nginx:latest\n{$environmentYaml}",
|
|
]);
|
|
ServiceApplication::create(['name' => 'app', 'service_id' => $service->id]);
|
|
|
|
return $service;
|
|
}
|
|
|
|
test('a database gets the same Coolify variables as other resources, with compose-safe values', function () {
|
|
$environment = coolifyDefaultsDatabaseEnvironment(new StartPostgresql, coolifyDefaultsPostgres());
|
|
|
|
expect($environment)
|
|
->toContain('COOLIFY_RESOURCE_UUID=coolify-defaults-db')
|
|
->toContain('COOLIFY_CONTAINER_NAME=coolify-defaults-db')
|
|
->toContain('COOLIFY_APP_NAME=Main database')
|
|
->toContain('COOLIFY_PROJECT_NAME=Shop $$live')
|
|
->toContain('COOLIFY_ENVIRONMENT_NAME=staging')
|
|
->toContain("COOLIFY_SERVER_IP={$this->server->ip}");
|
|
});
|
|
|
|
test('a Dragonfly database also gets the Coolify variables', function () {
|
|
$database = StandaloneDragonfly::forceCreate([
|
|
'uuid' => 'coolify-defaults-dragonfly',
|
|
'name' => 'Cache',
|
|
'image' => 'docker.dragonflydb.io/dragonflydb/dragonfly',
|
|
'dragonfly_password' => 'password',
|
|
'environment_id' => $this->environment->id,
|
|
'destination_id' => $this->destination->id,
|
|
'destination_type' => $this->destination->getMorphClass(),
|
|
]);
|
|
|
|
expect(coolifyDefaultsDatabaseEnvironment(new StartDragonfly, $database))
|
|
->toContain('COOLIFY_RESOURCE_UUID=coolify-defaults-dragonfly')
|
|
->toContain('COOLIFY_APP_NAME=Cache');
|
|
});
|
|
|
|
test('a database variable that the user defines wins over the Coolify variable', function () {
|
|
$database = coolifyDefaultsPostgres();
|
|
$database->runtime_environment_variables()->create(['key' => 'COOLIFY_APP_NAME', 'value' => 'custom']);
|
|
|
|
$appNames = collect(coolifyDefaultsDatabaseEnvironment(new StartPostgresql, $database))
|
|
->filter(fn ($variable) => str_starts_with($variable, 'COOLIFY_APP_NAME='));
|
|
|
|
expect($appNames->values()->all())->toBe(['COOLIFY_APP_NAME=custom']);
|
|
});
|
|
|
|
test('a database without the option gets no default variables but keeps its identity variables', function () {
|
|
$environment = coolifyDefaultsDatabaseEnvironment(new StartPostgresql, coolifyDefaultsPostgres(['include_coolify_default_variables' => false]));
|
|
|
|
expect(collect($environment)->first(fn ($variable) => str_starts_with($variable, 'COOLIFY_APP_NAME=')))->toBeNull()
|
|
->and(collect($environment)->first(fn ($variable) => str_starts_with($variable, 'COOLIFY_SERVER_IP=')))->toBeNull()
|
|
->and($environment)->toContain('COOLIFY_RESOURCE_UUID=coolify-defaults-db');
|
|
});
|
|
|
|
test('an application gets the default variables as quoted lines in its runtime .env file', function () {
|
|
$environment = coolifyDefaultsApplicationEnv(coolifyDefaultsApplication());
|
|
|
|
expect($environment)
|
|
->toContain('COOLIFY_APP_NAME="My web app"')
|
|
->toContain('COOLIFY_PROJECT_NAME="Shop \\$live"')
|
|
->toContain('COOLIFY_ENVIRONMENT_NAME="staging"')
|
|
->toContain("COOLIFY_SERVER_IP=\"{$this->server->ip}\"");
|
|
});
|
|
|
|
test('an application on an additional server gets the IP of the server it runs on', function () {
|
|
$additionalServer = Server::factory()->create(['team_id' => $this->team->id, 'ip' => '203.0.113.7']);
|
|
|
|
expect(coolifyDefaultsApplicationEnv(coolifyDefaultsApplication(), deploymentServer: $additionalServer))
|
|
->toContain('COOLIFY_SERVER_IP="203.0.113.7"');
|
|
});
|
|
|
|
test('an application build does not get the default variables', function () {
|
|
$environment = coolifyDefaultsApplicationEnv(coolifyDefaultsApplication(), forBuildTime: true);
|
|
|
|
expect(collect($environment)->first(fn ($variable) => str_starts_with($variable, 'COOLIFY_APP_NAME=')))->toBeNull()
|
|
->and(collect($environment)->first(fn ($variable) => str_starts_with($variable, 'COOLIFY_RESOURCE_UUID=')))->not->toBeNull();
|
|
});
|
|
|
|
test('an application without the option gets no default variables', function () {
|
|
$application = coolifyDefaultsApplication();
|
|
$application->settings->update(['include_coolify_default_variables' => false]);
|
|
|
|
$environment = coolifyDefaultsApplicationEnv($application);
|
|
|
|
expect(collect($environment)->first(fn ($variable) => str_starts_with($variable, 'COOLIFY_APP_NAME=')))->toBeNull();
|
|
});
|
|
|
|
test('a service variable defined in the compose file wins over the Coolify variable', function () {
|
|
$service = coolifyDefaultsService(" environment:\n COOLIFY_APP_NAME: custom\n");
|
|
|
|
$environment = data_get(serviceParser($service), 'services.app.environment');
|
|
|
|
expect($environment['COOLIFY_APP_NAME'])->toBe('custom')
|
|
->and($environment['COOLIFY_PROJECT_NAME'])->toBe('Shop $$live');
|
|
});
|
|
|
|
test('a service without the option gets no default variables', function () {
|
|
$service = coolifyDefaultsService();
|
|
$service->forceFill(['include_coolify_default_variables' => false])->save();
|
|
|
|
$environment = collect(data_get(serviceParser($service->fresh()), 'services.app.environment'));
|
|
|
|
expect($environment->has('COOLIFY_APP_NAME'))->toBeFalse()
|
|
->and($environment->has('COOLIFY_RESOURCE_UUID'))->toBeTrue();
|
|
});
|
|
|
|
test('a team admin turns the option off from the environment variables page', function (string $resourceType) {
|
|
$user = User::factory()->create();
|
|
$this->team->members()->attach($user, ['role' => 'admin']);
|
|
$this->actingAs($user);
|
|
$resource = match ($resourceType) {
|
|
'application' => coolifyDefaultsApplication(),
|
|
'service' => coolifyDefaultsService(),
|
|
'database' => coolifyDefaultsPostgres(),
|
|
};
|
|
|
|
Livewire::test(All::class, ['resource' => $resource])
|
|
->assertSet('include_coolify_default_variables', true)
|
|
->set('include_coolify_default_variables', false)
|
|
->call('saveCoolifyDefaultVariables');
|
|
|
|
$owner = $resource instanceof Application ? $resource->settings->fresh() : $resource->fresh();
|
|
expect($owner->include_coolify_default_variables)->toBeFalse();
|
|
})->with(['application', 'service', 'database']);
|
|
|
|
test('a team member cannot change the option', function () {
|
|
$user = User::factory()->create();
|
|
$this->team->members()->attach($user, ['role' => 'member']);
|
|
$this->actingAs($user);
|
|
$database = coolifyDefaultsPostgres();
|
|
|
|
Livewire::test(All::class, ['resource' => $database])
|
|
->set('include_coolify_default_variables', false)
|
|
->call('saveCoolifyDefaultVariables');
|
|
|
|
expect($database->fresh()->include_coolify_default_variables)->toBeTrue();
|
|
});
|