mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 22:17:19 +00:00
feat(railpack): add buildpack control var filtering and dev seeder
Extract NIXPACKS_/RAILPACK_ prefix filtering into a reusable `scopeWithoutBuildpackControlVariables` query scope on EnvironmentVariable. Apply scope consistently to runtime vars, runtime preview vars, and buildtime var generation in ApplicationDeploymentJob. Refactor `generate_railpack_env_variables` to return a Collection. Add `RAILPACK_FRONTEND_IMAGE` constant and bake it into the coolify-helper Dockerfile as a build arg. Add DevelopmentRailpackExamplesSeeder (dev/local env only) for seeding example Railpack apps, wired into DatabaseSeeder. Add tests: - ApplicationDeploymentControlVarFilteringTest: verifies control vars are excluded from runtime and buildtime envs - DevelopmentRailpackExamplesSeederTest: verifies seeder behavior - ApplicationDeploymentRailpackEnvParityTest: parity checks for env handling across build/runtime paths
This commit is contained in:
1 parent
5cef7cc092
commit
b3339d1034
12 files changed
+1177
-77
No files matched your search
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Environment;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
class TestableControlVarFilteringDeploymentJob extends ApplicationDeploymentJob
|
||||
{
|
||||
public array $recordedCommands = [];
|
||||
|
||||
public ?string $writtenDockerfile = null;
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
public function execute_remote_command(...$commands)
|
||||
{
|
||||
$this->recordedCommands[] = $commands;
|
||||
|
||||
foreach ($commands as $command) {
|
||||
$commandString = is_array($command) ? ($command['command'] ?? $command[0] ?? null) : $command;
|
||||
|
||||
if (! is_string($commandString)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('/echo .*?([A-Za-z0-9+\\/=]{16,}).*?\\| base64 -d \\| tee \\/artifacts\\/test-app\\/Dockerfile > \\/dev\\/null/', $commandString, $matches) === 1) {
|
||||
$this->writtenDockerfile = base64_decode($matches[1]) ?: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeDeploymentControlVarFixture(array $applicationAttributes = []): array
|
||||
{
|
||||
$team = Team::create([
|
||||
'name' => 'Control Var Team',
|
||||
'description' => 'Team for deployment control var tests.',
|
||||
'personal_team' => false,
|
||||
'show_boarding' => false,
|
||||
]);
|
||||
$project = Project::create([
|
||||
'name' => 'Control Var Project',
|
||||
'team_id' => $team->id,
|
||||
]);
|
||||
$environment = Environment::where('project_id', $project->id)->firstOrFail();
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
]);
|
||||
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $environment->id,
|
||||
'build_pack' => 'dockerfile',
|
||||
...$applicationAttributes,
|
||||
]);
|
||||
|
||||
$application->settings()->update([
|
||||
'inject_build_args_to_dockerfile' => true,
|
||||
'include_source_commit_in_build' => false,
|
||||
'is_env_sorting_enabled' => false,
|
||||
]);
|
||||
|
||||
return [$application->fresh(), $server];
|
||||
}
|
||||
|
||||
function createApplicationEnvironmentVariable(Application $application, array $attributes): EnvironmentVariable
|
||||
{
|
||||
return EnvironmentVariable::create([
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $application->id,
|
||||
'is_preview' => false,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => true,
|
||||
'is_multiline' => false,
|
||||
'is_literal' => false,
|
||||
...$attributes,
|
||||
]);
|
||||
}
|
||||
|
||||
function makeControlVarFilteringJob(Application $application, Server $server, array $overrides = []): array
|
||||
{
|
||||
$job = new TestableControlVarFilteringDeploymentJob;
|
||||
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
|
||||
|
||||
$queue = Mockery::mock(ApplicationDeploymentQueue::class);
|
||||
$queue->shouldReceive('addLogEntry')->andReturnNull();
|
||||
|
||||
$properties = [
|
||||
'application' => $application->fresh(),
|
||||
'application_deployment_queue' => $queue,
|
||||
'build_pack' => $application->build_pack,
|
||||
'mainServer' => $server,
|
||||
'pull_request_id' => 0,
|
||||
'commit' => 'HEAD',
|
||||
'workdir' => '/artifacts/test-app',
|
||||
'deployment_uuid' => 'deployment-uuid',
|
||||
'dockerfile_location' => '/Dockerfile',
|
||||
'container_name' => 'control-var-app',
|
||||
'coolify_variables' => null,
|
||||
'dockerSecretsSupported' => false,
|
||||
];
|
||||
|
||||
$mergedProperties = array_merge($properties, $overrides);
|
||||
$mergedProperties['saved_outputs'] = new Collection($overrides['saved_outputs'] ?? []);
|
||||
|
||||
if (($mergedProperties['pull_request_id'] ?? 0) !== 0 && ! array_key_exists('preview', $mergedProperties)) {
|
||||
$mergedProperties['preview'] = ApplicationPreview::create([
|
||||
'application_id' => $application->id,
|
||||
'pull_request_id' => $mergedProperties['pull_request_id'],
|
||||
'pull_request_html_url' => 'https://example.com/pr/'.$mergedProperties['pull_request_id'],
|
||||
'fqdn' => 'https://preview.example.com',
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($mergedProperties as $property => $value) {
|
||||
$reflectionProperty = $reflection->getProperty($property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$reflectionProperty->setValue($job, $value);
|
||||
}
|
||||
|
||||
return [$job, $reflection];
|
||||
}
|
||||
|
||||
function invokeDeploymentJobMethod(object $job, ReflectionClass $reflection, string $method): mixed
|
||||
{
|
||||
$reflectionMethod = $reflection->getMethod($method);
|
||||
$reflectionMethod->setAccessible(true);
|
||||
|
||||
return $reflectionMethod->invoke($job);
|
||||
}
|
||||
|
||||
function readDeploymentJobProperty(object $job, ReflectionClass $reflection, string $property): mixed
|
||||
{
|
||||
$reflectionProperty = $reflection->getProperty($property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
return $reflectionProperty->getValue($job);
|
||||
}
|
||||
|
||||
it('filters buildpack control vars from generic build args', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_ENV',
|
||||
'value' => 'production',
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server);
|
||||
|
||||
invokeDeploymentJobMethod($job, $reflection, 'generate_env_variables');
|
||||
|
||||
/** @var Collection $envArgs */
|
||||
$envArgs = readDeploymentJobProperty($job, $reflection, 'env_args');
|
||||
|
||||
expect($envArgs->get('APP_ENV'))->toBe('production');
|
||||
expect($envArgs->has('NIXPACKS_NODE_VERSION'))->toBeFalse();
|
||||
expect($envArgs->has('RAILPACK_NODE_VERSION'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('filters buildpack control vars from preview build-time env files', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_ENV',
|
||||
'value' => 'production',
|
||||
'is_preview' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_preview' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_preview' => true,
|
||||
]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
'pull_request_id' => 42,
|
||||
]);
|
||||
|
||||
/** @var Collection $buildtimeEnvs */
|
||||
$buildtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_buildtime_environment_variables');
|
||||
|
||||
expect($buildtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_ENV=')))->toBeTrue();
|
||||
expect($buildtimeEnvs->contains(fn (string $env) => str($env)->startsWith('NIXPACKS_NODE_VERSION=')))->toBeFalse();
|
||||
expect($buildtimeEnvs->contains(fn (string $env) => str($env)->startsWith('RAILPACK_NODE_VERSION=')))->toBeFalse();
|
||||
});
|
||||
|
||||
it('filters buildpack control vars from preview runtime env fallback', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_NAME',
|
||||
'value' => 'coolify',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_FLAG',
|
||||
'value' => 'enabled',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
|
||||
$application->environment_variables_preview()
|
||||
->whereIn('key', ['APP_NAME', 'NIXPACKS_NODE_VERSION', 'RAILPACK_NODE_VERSION'])
|
||||
->delete();
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
'pull_request_id' => 99,
|
||||
]);
|
||||
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_NAME=')))->toBeTrue();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('PREVIEW_FLAG=')))->toBeTrue();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('NIXPACKS_NODE_VERSION=')))->toBeFalse();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('RAILPACK_NODE_VERSION=')))->toBeFalse();
|
||||
});
|
||||
|
||||
it('filters buildpack control vars from dockerfile arg injection', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_ENV',
|
||||
'value' => 'production',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
'saved_outputs' => [
|
||||
'dockerfile' => "FROM php:8.4-cli\nRUN php -v",
|
||||
],
|
||||
]);
|
||||
|
||||
invokeDeploymentJobMethod($job, $reflection, 'add_build_env_variables_to_dockerfile');
|
||||
|
||||
expect($job->writtenDockerfile)->toContain('ARG APP_ENV=production');
|
||||
expect($job->writtenDockerfile)->not->toContain('ARG NIXPACKS_NODE_VERSION=');
|
||||
expect($job->writtenDockerfile)->not->toContain('ARG RAILPACK_NODE_VERSION=');
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
use Database\Seeders\DevelopmentRailpackExamplesSeeder;
|
||||
use Database\Seeders\GithubAppSeeder;
|
||||
use Database\Seeders\PrivateKeySeeder;
|
||||
use Database\Seeders\ProjectSeeder;
|
||||
use Database\Seeders\ServerSeeder;
|
||||
use Database\Seeders\StandaloneDockerSeeder;
|
||||
use Database\Seeders\TeamSeeder;
|
||||
use Database\Seeders\UserSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
function seedRailpackExamplePrerequisites(): void
|
||||
{
|
||||
test()->seed([
|
||||
UserSeeder::class,
|
||||
TeamSeeder::class,
|
||||
PrivateKeySeeder::class,
|
||||
ServerSeeder::class,
|
||||
ProjectSeeder::class,
|
||||
StandaloneDockerSeeder::class,
|
||||
GithubAppSeeder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
it('can seed the railpack examples directly on a clean development database', function () {
|
||||
config()->set('app.env', 'local');
|
||||
|
||||
$this->seed(DevelopmentRailpackExamplesSeeder::class);
|
||||
|
||||
expect(Team::query()->find(0))->not->toBeNull();
|
||||
expect(PrivateKey::query()->find(1))->not->toBeNull();
|
||||
expect(Server::query()->find(0))->not->toBeNull();
|
||||
expect(StandaloneDocker::query()->find(0))->not->toBeNull();
|
||||
expect(GithubApp::query()->find(0))->not->toBeNull();
|
||||
expect(Project::query()->where('uuid', DevelopmentRailpackExamplesSeeder::PROJECT_UUID)->exists())->toBeTrue();
|
||||
expect(Application::query()->count())->toBe(count(DevelopmentRailpackExamplesSeeder::examples()));
|
||||
});
|
||||
|
||||
it('seeds the railpack examples in development mode', function () {
|
||||
config()->set('app.env', 'local');
|
||||
|
||||
seedRailpackExamplePrerequisites();
|
||||
$this->seed(DevelopmentRailpackExamplesSeeder::class);
|
||||
|
||||
$project = Project::query()
|
||||
->where('uuid', DevelopmentRailpackExamplesSeeder::PROJECT_UUID)
|
||||
->first();
|
||||
|
||||
expect($project)
|
||||
->not->toBeNull()
|
||||
->and($project->name)->toBe('Railpack Examples')
|
||||
->and($project->environments)->toHaveCount(1)
|
||||
->and($project->environments->first()->uuid)->toBe(DevelopmentRailpackExamplesSeeder::ENVIRONMENT_UUID);
|
||||
|
||||
$applications = $project->applications()->with('settings')->orderBy('uuid')->get();
|
||||
|
||||
expect($applications)->toHaveCount(count(DevelopmentRailpackExamplesSeeder::examples()));
|
||||
expect($applications->every(fn (Application $application) => $application->build_pack === 'railpack'))->toBeTrue();
|
||||
expect($applications->every(fn (Application $application) => $application->git_repository === DevelopmentRailpackExamplesSeeder::GIT_REPOSITORY))->toBeTrue();
|
||||
expect($applications->every(fn (Application $application) => $application->git_branch === DevelopmentRailpackExamplesSeeder::GIT_BRANCH))->toBeTrue();
|
||||
|
||||
$nestjs = $applications->firstWhere('uuid', 'railpack-nestjs');
|
||||
$angularStatic = $applications->firstWhere('uuid', 'railpack-angular-static');
|
||||
$eleventyStatic = $applications->firstWhere('uuid', 'railpack-eleventy-static');
|
||||
|
||||
expect($nestjs)
|
||||
->not->toBeNull()
|
||||
->and($nestjs->base_directory)->toBe('/node/nestjs')
|
||||
->and($nestjs->ports_exposes)->toBe('3000')
|
||||
->and($nestjs->build_command)->toBe('npm run build')
|
||||
->and($nestjs->start_command)->toBe('npm run start:prod')
|
||||
->and($nestjs->settings->is_static)->toBeFalse();
|
||||
|
||||
expect($angularStatic)
|
||||
->not->toBeNull()
|
||||
->and($angularStatic->publish_directory)->toBe('/dist/static/browser')
|
||||
->and($angularStatic->ports_exposes)->toBe('80')
|
||||
->and($angularStatic->settings->is_static)->toBeTrue()
|
||||
->and($angularStatic->settings->is_spa)->toBeTrue();
|
||||
|
||||
expect($eleventyStatic)
|
||||
->not->toBeNull()
|
||||
->and($eleventyStatic->publish_directory)->toBe('/_site')
|
||||
->and($eleventyStatic->settings->is_static)->toBeTrue()
|
||||
->and($eleventyStatic->settings->is_spa)->toBeFalse();
|
||||
});
|
||||
|
||||
it('skips the railpack examples outside development mode', function () {
|
||||
config()->set('app.env', 'testing');
|
||||
|
||||
seedRailpackExamplePrerequisites();
|
||||
$this->seed(DevelopmentRailpackExamplesSeeder::class);
|
||||
|
||||
expect(Project::query()->where('uuid', DevelopmentRailpackExamplesSeeder::PROJECT_UUID)->exists())->toBeFalse();
|
||||
expect(Application::query()->where('uuid', 'railpack-nextjs-ssr')->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
it('is idempotent when run multiple times', function () {
|
||||
config()->set('app.env', 'local');
|
||||
|
||||
seedRailpackExamplePrerequisites();
|
||||
$this->seed(DevelopmentRailpackExamplesSeeder::class);
|
||||
$this->seed(DevelopmentRailpackExamplesSeeder::class);
|
||||
|
||||
$project = Project::query()
|
||||
->where('uuid', DevelopmentRailpackExamplesSeeder::PROJECT_UUID)
|
||||
->first();
|
||||
|
||||
expect($project)->not->toBeNull();
|
||||
expect($project->applications()->count())->toBe(count(DevelopmentRailpackExamplesSeeder::examples()));
|
||||
});
|
||||
@@ -30,6 +30,9 @@ function makeRailpackDeploymentJob(array $applicationAttributes = [], array $sav
|
||||
'deployment_uuid' => 'deployment-uuid',
|
||||
'saved_outputs' => new Collection($savedOutputs),
|
||||
'env_railpack_args' => "--env 'RAILPACK_NODE_VERSION=22'",
|
||||
'force_rebuild' => false,
|
||||
'addHosts' => '',
|
||||
'secrets_hash_key' => 'testing-app-key',
|
||||
] as $property => $value) {
|
||||
$reflectionProperty = $reflection->getProperty($property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
@@ -175,6 +178,9 @@ it('builds railpack prepare command using railpack env for install and cli flags
|
||||
'start_command' => 'node server.js',
|
||||
],
|
||||
);
|
||||
$envRailpackArgsProperty = $reflection->getProperty('env_railpack_args');
|
||||
$envRailpackArgsProperty->setAccessible(true);
|
||||
$envRailpackArgsProperty->setValue($job, "--env 'RAILPACK_NODE_VERSION=22' --env 'RAILPACK_INSTALL_CMD=npm ci'");
|
||||
|
||||
$command = invokeRailpackMethod(
|
||||
$job,
|
||||
@@ -184,8 +190,8 @@ it('builds railpack prepare command using railpack env for install and cli flags
|
||||
);
|
||||
|
||||
expect($command)->toContain('railpack prepare');
|
||||
expect($command)->toContain('--env '.escapeshellarg('RAILPACK_INSTALL_CMD=npm ci'));
|
||||
expect($command)->toContain("--env 'RAILPACK_NODE_VERSION=22'");
|
||||
expect($command)->toContain("--env 'RAILPACK_INSTALL_CMD=npm ci'");
|
||||
expect($command)->toContain('--build-cmd '.escapeshellarg('npm run build'));
|
||||
expect($command)->toContain('--start-cmd '.escapeshellarg('node server.js'));
|
||||
expect($command)->toContain('--config-file '.escapeshellarg('.coolify/railpack.generated.json'));
|
||||
@@ -195,3 +201,32 @@ it('builds railpack prepare command using railpack env for install and cli flags
|
||||
expect($command)->not->toContain('RAILPACK_BUILD_CMD=');
|
||||
expect($command)->not->toContain('RAILPACK_START_CMD=');
|
||||
});
|
||||
|
||||
it('builds railpack docker command with matching env and secret flags for all railpack variables', function () {
|
||||
[$job, $reflection] = makeRailpackDeploymentJob([
|
||||
'uuid' => 'application-uuid',
|
||||
]);
|
||||
|
||||
$command = invokeRailpackMethod(
|
||||
$job,
|
||||
$reflection,
|
||||
'railpack_build_command',
|
||||
[
|
||||
'coollabsio/coolify:test',
|
||||
collect([
|
||||
'RAILPACK_NODE_VERSION' => '22',
|
||||
'RAILPACK_INSTALL_CMD' => 'npm ci && npm run postinstall',
|
||||
'SECRET_JSON' => '{"token":"abc"}',
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
expect($command)->toContain("env RAILPACK_NODE_VERSION='22'");
|
||||
expect($command)->toContain("RAILPACK_INSTALL_CMD='npm ci && npm run postinstall'");
|
||||
expect($command)->toContain("SECRET_JSON='{\"token\":\"abc\"}'");
|
||||
expect($command)->toContain('--secret id=RAILPACK_NODE_VERSION,env=RAILPACK_NODE_VERSION');
|
||||
expect($command)->toContain('--secret id=RAILPACK_INSTALL_CMD,env=RAILPACK_INSTALL_CMD');
|
||||
expect($command)->toContain('--secret id=SECRET_JSON,env=SECRET_JSON');
|
||||
expect($command)->toContain(' --build-arg secrets-hash=');
|
||||
expect($command)->toContain('--build-arg BUILDKIT_SYNTAX="${RAILPACK_FRONTEND_IMAGE}"');
|
||||
});
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Server;
|
||||
|
||||
it('generates escaped railpack env args from resolved values and includes install command', function () {
|
||||
$application = Mockery::mock(Application::class);
|
||||
$application->shouldReceive('getAttribute')->with('install_command')->andReturn('npm ci && npm run postinstall');
|
||||
|
||||
$nodeVersion = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$nodeVersion->forceFill([
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'is_literal' => false,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$nodeVersion->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('22');
|
||||
|
||||
$literalValue = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$literalValue->forceFill([
|
||||
'key' => 'RAILPACK_CUSTOM_FLAG',
|
||||
'is_literal' => true,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$literalValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn("'hello world'");
|
||||
|
||||
$jsonValue = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$jsonValue->forceFill([
|
||||
'key' => 'RAILPACK_JSON',
|
||||
'is_literal' => false,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$jsonValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('{"token":"abc"}');
|
||||
|
||||
$nullValue = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$nullValue->forceFill([
|
||||
'key' => 'RAILPACK_NULL',
|
||||
'is_literal' => false,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$nullValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn(null);
|
||||
|
||||
$application->shouldReceive('getAttribute')
|
||||
->with('railpack_environment_variables')
|
||||
->andReturn(collect([$nodeVersion, $literalValue, $jsonValue, $nullValue]));
|
||||
|
||||
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
|
||||
$job->shouldAllowMockingProtectedMethods();
|
||||
|
||||
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
|
||||
$applicationProperty = $reflection->getProperty('application');
|
||||
$applicationProperty->setAccessible(true);
|
||||
$applicationProperty->setValue($job, $application);
|
||||
|
||||
$pullRequestProperty = $reflection->getProperty('pull_request_id');
|
||||
$pullRequestProperty->setAccessible(true);
|
||||
$pullRequestProperty->setValue($job, 0);
|
||||
|
||||
$mainServerProperty = $reflection->getProperty('mainServer');
|
||||
$mainServerProperty->setAccessible(true);
|
||||
$mainServerProperty->setValue($job, Mockery::mock(Server::class));
|
||||
|
||||
$method = $reflection->getMethod('generate_railpack_env_variables');
|
||||
$method->setAccessible(true);
|
||||
$variables = $method->invoke($job);
|
||||
|
||||
$envArgsProperty = $reflection->getProperty('env_railpack_args');
|
||||
$envArgsProperty->setAccessible(true);
|
||||
$envArgs = $envArgsProperty->getValue($job);
|
||||
|
||||
expect($variables->all())->toBe([
|
||||
'RAILPACK_NODE_VERSION' => '22',
|
||||
'RAILPACK_CUSTOM_FLAG' => 'hello world',
|
||||
'RAILPACK_JSON' => '{"token":"abc"}',
|
||||
'RAILPACK_INSTALL_CMD' => 'npm ci && npm run postinstall',
|
||||
]);
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_NODE_VERSION=22'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_CUSTOM_FLAG=hello world'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_JSON={\"token\":\"abc\"}'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_INSTALL_CMD=npm ci && npm run postinstall'");
|
||||
expect($envArgs)->not->toContain('RAILPACK_NULL');
|
||||
});
|
||||
|
||||
it('uses preview railpack environment variables for preview deployments', function () {
|
||||
$application = Mockery::mock(Application::class);
|
||||
$application->shouldReceive('getAttribute')->with('install_command')->andReturn(null);
|
||||
|
||||
$previewValue = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$previewValue->forceFill([
|
||||
'key' => 'RAILPACK_PREVIEW_ONLY',
|
||||
'is_literal' => false,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$previewValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('preview-value');
|
||||
|
||||
$application->shouldReceive('getAttribute')
|
||||
->with('railpack_environment_variables_preview')
|
||||
->andReturn(collect([$previewValue]));
|
||||
|
||||
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
|
||||
$job->shouldAllowMockingProtectedMethods();
|
||||
|
||||
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
|
||||
$applicationProperty = $reflection->getProperty('application');
|
||||
$applicationProperty->setAccessible(true);
|
||||
$applicationProperty->setValue($job, $application);
|
||||
|
||||
$pullRequestProperty = $reflection->getProperty('pull_request_id');
|
||||
$pullRequestProperty->setAccessible(true);
|
||||
$pullRequestProperty->setValue($job, 42);
|
||||
|
||||
$mainServerProperty = $reflection->getProperty('mainServer');
|
||||
$mainServerProperty->setAccessible(true);
|
||||
$mainServerProperty->setValue($job, Mockery::mock(Server::class));
|
||||
|
||||
$method = $reflection->getMethod('generate_railpack_env_variables');
|
||||
$method->setAccessible(true);
|
||||
$variables = $method->invoke($job);
|
||||
|
||||
expect($variables->all())->toBe([
|
||||
'RAILPACK_PREVIEW_ONLY' => 'preview-value',
|
||||
]);
|
||||
});
|
||||
Reference in new issue
Block a user