feat(api): allow preview deployments on app create

Expose is_preview_deployments_enabled in the application create API
schema and validation, and skip deployment configuration column casts on
sqlite migrations.
This commit is contained in:
Andras Bacsai 2026-07-07 12:36:35 +02:00
parent 77086e28af
commit cf12e1d7ef
5 changed files with 96 additions and 1 deletions

View file

@ -97,6 +97,7 @@ function sharedDataApplications()
'is_spa' => 'boolean',
'is_auto_deploy_enabled' => 'boolean',
'is_force_https_enabled' => 'boolean',
'is_preview_deployments_enabled' => 'boolean',
'static_image' => Rule::enum(StaticImageTypes::class),
'domains' => 'string|nullable',
'redirect' => Rule::enum(RedirectTypes::class),
@ -198,6 +199,7 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
$request->offsetUnset('is_spa');
$request->offsetUnset('is_auto_deploy_enabled');
$request->offsetUnset('is_force_https_enabled');
$request->offsetUnset('is_preview_deployments_enabled');
$request->offsetUnset('connect_to_docker_network');
$request->offsetUnset('force_domain_override');
$request->offsetUnset('autogenerate_domain');

View file

@ -11,12 +11,20 @@
*/
public function up(): void
{
if (DB::getDriverName() === 'sqlite') {
return;
}
DB::statement('ALTER TABLE application_deployment_queues ALTER COLUMN configuration_snapshot TYPE text USING configuration_snapshot::text');
DB::statement('ALTER TABLE application_deployment_queues ALTER COLUMN configuration_diff TYPE text USING configuration_diff::text');
}
public function down(): void
{
if (DB::getDriverName() === 'sqlite') {
return;
}
DB::statement('ALTER TABLE application_deployment_queues ALTER COLUMN configuration_snapshot TYPE json USING configuration_snapshot::json');
DB::statement('ALTER TABLE application_deployment_queues ALTER COLUMN configuration_diff TYPE json USING configuration_diff::json');
}

View file

@ -165,6 +165,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"static_image": {
"type": "string",
"enum": [
@ -615,6 +619,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"static_image": {
"type": "string",
"enum": [
@ -1065,6 +1073,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"static_image": {
"type": "string",
"enum": [
@ -1626,6 +1638,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"use_build_server": {
"type": "boolean",
"nullable": true,
@ -1961,6 +1977,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"use_build_server": {
"type": "boolean",
"nullable": true,
@ -2333,6 +2353,10 @@
"type": "boolean",
"description": "The flag to indicate if HTTPS is forced. Defaults to true."
},
"is_preview_deployments_enabled": {
"type": "boolean",
"description": "Enable preview deployments for pull requests."
},
"install_command": {
"type": "string",
"description": "The install command."

View file

@ -118,6 +118,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
static_image:
type: string
enum: ['nginx:alpine']
@ -405,6 +408,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
static_image:
type: string
enum: ['nginx:alpine']
@ -692,6 +698,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
static_image:
type: string
enum: ['nginx:alpine']
@ -1063,6 +1072,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
use_build_server:
type: boolean
nullable: true
@ -1277,6 +1289,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
use_build_server:
type: boolean
nullable: true
@ -1507,6 +1522,9 @@ paths:
is_force_https_enabled:
type: boolean
description: 'The flag to indicate if HTTPS is forced. Defaults to true.'
is_preview_deployments_enabled:
type: boolean
description: 'Enable preview deployments for pull requests.'
install_command:
type: string
description: 'The install command.'

View file

@ -1,7 +1,7 @@
<?php
use App\Models\Application;
use App\Models\Environment;
use App\Models\InstanceSettings;
use App\Models\Project;
use App\Models\Server;
use App\Models\StandaloneDocker;
@ -13,6 +13,10 @@
uses(RefreshDatabase::class);
beforeEach(function () {
config(['app.maintenance.driver' => 'file']);
InstanceSettings::unguarded(fn () => InstanceSettings::firstOrCreate(['id' => 0]));
$this->team = Team::factory()->create();
$this->user = User::factory()->create();
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
@ -106,3 +110,42 @@ function previewDeploymentsAuthHeaders($bearerToken): array
$response->assertStatus(422);
});
});
describe('POST /api/v1/applications/public is_preview_deployments_enabled', function () {
test('can enable preview deployments on create', function () {
$response = $this->withHeaders(previewDeploymentsAuthHeaders($this->bearerToken))
->postJson('/api/v1/applications/public', [
'project_uuid' => $this->project->uuid,
'environment_uuid' => $this->environment->uuid,
'server_uuid' => $this->server->uuid,
'git_repository' => 'https://gitlab.com/coolify/test-preview-app',
'git_branch' => 'main',
'build_pack' => 'nixpacks',
'ports_exposes' => '3000',
'is_preview_deployments_enabled' => true,
'autogenerate_domain' => false,
]);
$response->assertCreated();
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
expect($application->settings->is_preview_deployments_enabled)->toBeTrue();
});
test('rejects non-boolean value on create', function () {
$response = $this->withHeaders(previewDeploymentsAuthHeaders($this->bearerToken))
->postJson('/api/v1/applications/public', [
'project_uuid' => $this->project->uuid,
'environment_uuid' => $this->environment->uuid,
'server_uuid' => $this->server->uuid,
'git_repository' => 'https://gitlab.com/coolify/test-preview-app',
'git_branch' => 'main',
'build_pack' => 'nixpacks',
'ports_exposes' => '3000',
'is_preview_deployments_enabled' => 'yes',
'autogenerate_domain' => false,
]);
$response->assertUnprocessable();
});
});