mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 00:23:27 +00:00
Merge remote-tracking branch 'origin/next' into feat/noindex-domains
This commit is contained in:
@@ -35,6 +35,36 @@ class ApplicationsController extends Controller
|
||||
{
|
||||
use Concerns\HandlesTagsApi;
|
||||
|
||||
private const APPLICATION_SETTING_FIELDS = [
|
||||
'is_git_submodules_enabled',
|
||||
'is_git_lfs_enabled',
|
||||
'is_git_shallow_clone_enabled',
|
||||
'disable_build_cache',
|
||||
'inject_build_args_to_dockerfile',
|
||||
'include_source_commit_in_build',
|
||||
'is_env_sorting_enabled',
|
||||
'is_pr_deployments_public_enabled',
|
||||
'stop_grace_period',
|
||||
'docker_images_to_keep',
|
||||
'is_gzip_enabled',
|
||||
'is_stripprefix_enabled',
|
||||
'is_raw_compose_deployment_enabled',
|
||||
];
|
||||
|
||||
private const BOOLEAN_APPLICATION_SETTING_FIELDS = [
|
||||
'is_git_submodules_enabled',
|
||||
'is_git_lfs_enabled',
|
||||
'is_git_shallow_clone_enabled',
|
||||
'disable_build_cache',
|
||||
'inject_build_args_to_dockerfile',
|
||||
'include_source_commit_in_build',
|
||||
'is_env_sorting_enabled',
|
||||
'is_pr_deployments_public_enabled',
|
||||
'is_gzip_enabled',
|
||||
'is_stripprefix_enabled',
|
||||
'is_raw_compose_deployment_enabled',
|
||||
];
|
||||
|
||||
protected function findTaggableResource(string $uuid, int|string $teamId): mixed
|
||||
{
|
||||
return Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $uuid)->first();
|
||||
@@ -87,9 +117,48 @@ class ApplicationsController extends Controller
|
||||
$application->makeHidden(['value', 'real_value']);
|
||||
}
|
||||
|
||||
if ($application->relationLoaded('settings')) {
|
||||
$application->settings?->makeHidden(['id', 'application_id', 'created_at', 'updated_at']);
|
||||
}
|
||||
|
||||
return serializeApiResponse($application);
|
||||
}
|
||||
|
||||
private function applicationSettingsFromRequest(Request $request): array
|
||||
{
|
||||
$settings = [];
|
||||
|
||||
foreach (self::APPLICATION_SETTING_FIELDS as $field) {
|
||||
if (! array_key_exists($field, $request->all())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$settings[$field] = in_array($field, self::BOOLEAN_APPLICATION_SETTING_FIELDS, true)
|
||||
? $request->boolean($field)
|
||||
: $request->input($field);
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
private function applyApplicationSettings(Application $application, array $settings): void
|
||||
{
|
||||
if ($settings === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$regenerateLabels = ! $application->wasRecentlyCreated
|
||||
&& $application->settings->is_container_label_readonly_enabled
|
||||
&& (array_key_exists('is_gzip_enabled', $settings) || array_key_exists('is_stripprefix_enabled', $settings));
|
||||
|
||||
$application->settings->fill($settings)->save();
|
||||
|
||||
if ($regenerateLabels) {
|
||||
$application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n");
|
||||
$application->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose sensitive fields on eager-loaded nested Server + ServerSetting
|
||||
* relations for callers with the `read:sensitive` or `root` token ability.
|
||||
@@ -286,6 +355,20 @@ class ApplicationsController extends Controller
|
||||
],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'default' => false, 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
@@ -455,6 +538,20 @@ class ApplicationsController extends Controller
|
||||
],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'default' => false, 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
@@ -624,6 +721,20 @@ class ApplicationsController extends Controller
|
||||
],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'default' => false, 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
@@ -765,6 +876,20 @@ class ApplicationsController extends Controller
|
||||
'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, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'default' => false, 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
@@ -902,6 +1027,20 @@ class ApplicationsController extends Controller
|
||||
'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, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'default' => false, 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
@@ -986,7 +1125,7 @@ class ApplicationsController extends Controller
|
||||
if ($return instanceof JsonResponse) {
|
||||
return $return;
|
||||
}
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'is_preview_deployments_enabled', 'domains', 'noindex_domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_type', 'health_check_command', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'dockerfile_location', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled', 'tags', 'is_preserve_repository_enabled'];
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'is_preview_deployments_enabled', 'domains', 'noindex_domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_type', 'health_check_command', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'dockerfile_location', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'use_build_secrets', 'static_image', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'autogenerate_domain', 'is_container_label_escape_enabled', 'tags', 'is_preserve_repository_enabled', ...self::APPLICATION_SETTING_FIELDS];
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'name' => 'string|max:255',
|
||||
@@ -1041,6 +1180,7 @@ class ApplicationsController extends Controller
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
$githubAppUuid = $request->github_app_uuid;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$useBuildSecrets = $request->use_build_secrets;
|
||||
$isStatic = $request->is_static;
|
||||
$isSpa = $request->is_spa;
|
||||
$isAutoDeployEnabled = $request->is_auto_deploy_enabled;
|
||||
@@ -1050,6 +1190,19 @@ class ApplicationsController extends Controller
|
||||
$customNginxConfiguration = $request->custom_nginx_configuration;
|
||||
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled', true);
|
||||
$isPreserveRepositoryEnabled = $request->boolean('is_preserve_repository_enabled', false);
|
||||
$applicationSettings = $this->applicationSettingsFromRequest($request);
|
||||
|
||||
$requestedBuildPack = in_array($type, ['public', 'private-gh-app', 'private-deploy-key'], true)
|
||||
? $request->input('build_pack')
|
||||
: $type;
|
||||
if (($applicationSettings['is_raw_compose_deployment_enabled'] ?? false) && $requestedBuildPack !== 'dockercompose') {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'is_raw_compose_deployment_enabled' => 'Raw compose deployment can only be enabled for Docker Compose applications.',
|
||||
],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if (! is_null($customNginxConfiguration)) {
|
||||
if (! isBase64Encoded($customNginxConfiguration)) {
|
||||
@@ -1237,6 +1390,7 @@ class ApplicationsController extends Controller
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->save();
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
if (isset($isStatic)) {
|
||||
$application->settings->is_static = $isStatic;
|
||||
$application->settings->save();
|
||||
@@ -1265,6 +1419,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isContainerLabelEscapeEnabled)) {
|
||||
$application->settings->is_container_label_escape_enabled = $isContainerLabelEscapeEnabled;
|
||||
$application->settings->save();
|
||||
@@ -1483,6 +1641,7 @@ class ApplicationsController extends Controller
|
||||
$application->repository_project_id = $repository_project_id;
|
||||
|
||||
$application->save();
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
$application->refresh();
|
||||
// Auto-generate domain if requested and no custom domain provided
|
||||
if ($autogenerateDomain && blank($fqdn)) {
|
||||
@@ -1517,6 +1676,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isContainerLabelEscapeEnabled)) {
|
||||
$application->settings->is_container_label_escape_enabled = $isContainerLabelEscapeEnabled;
|
||||
$application->settings->save();
|
||||
@@ -1699,6 +1862,7 @@ class ApplicationsController extends Controller
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->save();
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
$application->refresh();
|
||||
// Auto-generate domain if requested and no custom domain provided
|
||||
if ($autogenerateDomain && blank($fqdn)) {
|
||||
@@ -1733,6 +1897,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isContainerLabelEscapeEnabled)) {
|
||||
$application->settings->is_container_label_escape_enabled = $isContainerLabelEscapeEnabled;
|
||||
$application->settings->save();
|
||||
@@ -1842,6 +2010,7 @@ class ApplicationsController extends Controller
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
$application->save();
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
$application->refresh();
|
||||
// Auto-generate domain if requested and no custom domain provided
|
||||
if ($autogenerateDomain && blank($fqdn)) {
|
||||
@@ -1864,6 +2033,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isContainerLabelEscapeEnabled)) {
|
||||
$application->settings->is_container_label_escape_enabled = $isContainerLabelEscapeEnabled;
|
||||
$application->settings->save();
|
||||
@@ -1968,6 +2141,7 @@ class ApplicationsController extends Controller
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
$application->save();
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
$application->refresh();
|
||||
// Auto-generate domain if requested and no custom domain provided
|
||||
if ($autogenerateDomain && blank($fqdn)) {
|
||||
@@ -1990,6 +2164,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($isContainerLabelEscapeEnabled)) {
|
||||
$application->settings->is_container_label_escape_enabled = $isContainerLabelEscapeEnabled;
|
||||
$application->settings->save();
|
||||
@@ -2095,7 +2273,7 @@ class ApplicationsController extends Controller
|
||||
if (! $uuid) {
|
||||
return response()->json(['message' => 'UUID is required.'], 400);
|
||||
}
|
||||
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->route('uuid'))->first();
|
||||
$application = Application::ownedByCurrentTeamAPI($teamId)->with('settings')->where('uuid', $request->route('uuid'))->first();
|
||||
if (! $application) {
|
||||
return response()->json(['message' => 'Application not found.'], 404);
|
||||
}
|
||||
@@ -2411,11 +2589,24 @@ class ApplicationsController extends Controller
|
||||
],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
'use_build_secrets' => ['type' => 'boolean', 'description' => 'Use Docker Build Secrets for build-time environment variables.'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean', 'description' => 'Clone Git submodules.'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean', 'description' => 'Enable Git LFS.'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean', 'description' => 'Use a shallow Git clone.'],
|
||||
'disable_build_cache' => ['type' => 'boolean', 'description' => 'Disable the build cache.'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean', 'description' => 'Inject build arguments into the Dockerfile build.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include the source commit in the build.'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean', 'description' => 'Sort environment variables.'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean', 'description' => 'Make pull request deployments public.'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true, 'minimum' => 1, 'maximum' => 3600, 'description' => 'Container stop grace period in seconds.'],
|
||||
'docker_images_to_keep' => ['type' => 'integer', 'minimum' => 0, 'maximum' => 100, 'description' => 'Number of Docker images to retain.'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean', 'description' => 'Enable gzip compression.'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean', 'description' => 'Enable path prefix stripping.'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean', 'description' => 'Deploy the raw Docker Compose definition.'],
|
||||
'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'],
|
||||
'force_domain_override' => ['type' => 'boolean', 'description' => 'Force domain usage even if conflicts are detected. Default is false.'],
|
||||
'is_container_label_escape_enabled' => ['type' => 'boolean', 'default' => true, 'description' => 'Escape special characters in labels. By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$. If you want to use env variables inside the labels, turn this off.'],
|
||||
'is_preserve_repository_enabled' => ['type' => 'boolean', 'description' => 'Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false.'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean', 'description' => 'Include source commit information in the build. Default is false.'],
|
||||
],
|
||||
)
|
||||
),
|
||||
@@ -2501,7 +2692,7 @@ class ApplicationsController extends Controller
|
||||
$this->authorize('update', $application);
|
||||
|
||||
$server = $application->destination->server;
|
||||
$allowedFields = ['name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'is_preview_deployments_enabled', 'domains', 'noindex_domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_type', 'health_check_command', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'dockerfile_location', 'dockerfile_target_build', 'docker_compose_location', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'is_container_label_escape_enabled', 'is_preserve_repository_enabled', 'include_source_commit_in_build'];
|
||||
$allowedFields = ['name', 'description', 'is_static', 'is_spa', 'is_auto_deploy_enabled', 'is_force_https_enabled', 'is_preview_deployments_enabled', 'domains', 'noindex_domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'custom_network_aliases', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_type', 'health_check_command', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'dockerfile_location', 'dockerfile_target_build', 'docker_compose_location', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server', 'use_build_secrets', 'custom_nginx_configuration', 'is_http_basic_auth_enabled', 'http_basic_auth_username', 'http_basic_auth_password', 'connect_to_docker_network', 'force_domain_override', 'is_container_label_escape_enabled', 'is_preserve_repository_enabled', ...self::APPLICATION_SETTING_FIELDS];
|
||||
|
||||
$validationRules = [
|
||||
'name' => 'string|max:255',
|
||||
@@ -2580,6 +2771,17 @@ class ApplicationsController extends Controller
|
||||
], 422);
|
||||
}
|
||||
|
||||
$applicationSettings = $this->applicationSettingsFromRequest($request);
|
||||
$requestedBuildPack = $request->input('build_pack', $application->build_pack);
|
||||
if (($applicationSettings['is_raw_compose_deployment_enabled'] ?? false) && $requestedBuildPack !== 'dockercompose') {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => [
|
||||
'is_raw_compose_deployment_enabled' => 'Raw compose deployment can only be enabled for Docker Compose applications.',
|
||||
],
|
||||
], 422);
|
||||
}
|
||||
|
||||
if ($request->has('is_http_basic_auth_enabled') && $request->is_http_basic_auth_enabled === true) {
|
||||
if (blank($application->http_basic_auth_username) || blank($application->http_basic_auth_password)) {
|
||||
$validationErrors = [];
|
||||
@@ -2734,6 +2936,7 @@ class ApplicationsController extends Controller
|
||||
$isPreviewDeploymentsEnabled = $request->is_preview_deployments_enabled;
|
||||
$connectToDockerNetwork = $request->connect_to_docker_network;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
$useBuildSecrets = $request->use_build_secrets;
|
||||
$isContainerLabelEscapeEnabled = $request->boolean('is_container_label_escape_enabled');
|
||||
$isPreserveRepositoryEnabled = $request->boolean('is_preserve_repository_enabled');
|
||||
$includeSourceCommitInBuild = $request->boolean('include_source_commit_in_build');
|
||||
@@ -2741,6 +2944,10 @@ class ApplicationsController extends Controller
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
if (isset($useBuildSecrets)) {
|
||||
$application->settings->use_build_secrets = $useBuildSecrets;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
if (isset($isStatic)) {
|
||||
$application->settings->is_static = $isStatic;
|
||||
@@ -2784,6 +2991,7 @@ class ApplicationsController extends Controller
|
||||
$application->settings->include_source_commit_in_build = $includeSourceCommitInBuild;
|
||||
$application->settings->save();
|
||||
}
|
||||
$this->applyApplicationSettings($application, $applicationSettings);
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$data = $request->only($allowedFields);
|
||||
@@ -4035,7 +4243,7 @@ class ApplicationsController extends Controller
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function move_by_uuid(Request $request): \Illuminate\Http\JsonResponse
|
||||
public function move_by_uuid(Request $request): JsonResponse
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
|
||||
@@ -286,6 +286,7 @@ class Advanced extends Component
|
||||
$this->application->settings->save();
|
||||
|
||||
$this->dispatch('success', 'Stop grace period updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (ValidationException $e) {
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -496,6 +496,7 @@ class General extends Component
|
||||
if ($this->isContainerLabelReadonlyEnabled) {
|
||||
$this->resetDefaultLabels(false);
|
||||
}
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ class Source extends Component
|
||||
'source_id' => $source->id,
|
||||
'source_type' => $sourceType,
|
||||
]);
|
||||
$this->dispatch('configurationChanged');
|
||||
|
||||
['repository' => $customRepository] = $this->application->customRepository();
|
||||
$repository = githubApi($this->application->source, "repos/{$customRepository}");
|
||||
|
||||
@@ -57,6 +57,7 @@ class Swarm extends Component
|
||||
$this->authorize('update', $this->application);
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -68,6 +69,7 @@ class Swarm extends Component
|
||||
$this->authorize('update', $this->application);
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ class All extends Component
|
||||
$this->resource->settings->save();
|
||||
$this->getDevView();
|
||||
$this->dispatch('success', 'Environment variable settings updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ class HealthChecks extends Component
|
||||
$this->resource->custom_healthcheck_found = $this->customHealthcheckFound;
|
||||
$this->resource->save();
|
||||
$this->dispatch('success', 'Health check updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -178,6 +179,7 @@ class HealthChecks extends Component
|
||||
$this->resource->custom_healthcheck_found = $this->customHealthcheckFound;
|
||||
$this->resource->save();
|
||||
$this->dispatch('success', 'Health check updated.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -213,6 +215,7 @@ class HealthChecks extends Component
|
||||
} else {
|
||||
$this->dispatch('success', 'Health check '.($this->healthCheckEnabled ? 'enabled' : 'disabled').'.');
|
||||
}
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
'is_http_basic_auth_enabled' => ['type' => 'boolean', 'description' => 'HTTP Basic Authentication enabled.'],
|
||||
'http_basic_auth_username' => ['type' => 'string', 'nullable' => true, 'description' => 'Username for HTTP Basic Authentication'],
|
||||
'http_basic_auth_password' => ['type' => 'string', 'nullable' => true, 'description' => 'Password for HTTP Basic Authentication'],
|
||||
'settings' => new OA\Property(ref: '#/components/schemas/ApplicationSetting'),
|
||||
]
|
||||
)]
|
||||
|
||||
|
||||
@@ -4,7 +4,49 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Application settings.',
|
||||
type: 'object',
|
||||
properties: [
|
||||
'is_static' => ['type' => 'boolean'],
|
||||
'is_git_submodules_enabled' => ['type' => 'boolean'],
|
||||
'is_git_lfs_enabled' => ['type' => 'boolean'],
|
||||
'is_auto_deploy_enabled' => ['type' => 'boolean'],
|
||||
'is_force_https_enabled' => ['type' => 'boolean'],
|
||||
'is_debug_enabled' => ['type' => 'boolean'],
|
||||
'is_preview_deployments_enabled' => ['type' => 'boolean'],
|
||||
'is_log_drain_enabled' => ['type' => 'boolean'],
|
||||
'is_gpu_enabled' => ['type' => 'boolean'],
|
||||
'gpu_driver' => ['type' => 'string', 'nullable' => true],
|
||||
'gpu_count' => ['type' => 'string', 'nullable' => true],
|
||||
'gpu_device_ids' => ['type' => 'string', 'nullable' => true],
|
||||
'gpu_options' => ['type' => 'string', 'nullable' => true],
|
||||
'is_include_timestamps' => ['type' => 'boolean'],
|
||||
'is_swarm_only_worker_nodes' => ['type' => 'boolean'],
|
||||
'is_raw_compose_deployment_enabled' => ['type' => 'boolean'],
|
||||
'is_build_server_enabled' => ['type' => 'boolean'],
|
||||
'is_consistent_container_name_enabled' => ['type' => 'boolean'],
|
||||
'is_gzip_enabled' => ['type' => 'boolean'],
|
||||
'is_stripprefix_enabled' => ['type' => 'boolean'],
|
||||
'connect_to_docker_network' => ['type' => 'boolean'],
|
||||
'custom_internal_name' => ['type' => 'string', 'nullable' => true],
|
||||
'is_container_label_escape_enabled' => ['type' => 'boolean'],
|
||||
'is_env_sorting_enabled' => ['type' => 'boolean'],
|
||||
'is_container_label_readonly_enabled' => ['type' => 'boolean'],
|
||||
'is_preserve_repository_enabled' => ['type' => 'boolean'],
|
||||
'disable_build_cache' => ['type' => 'boolean'],
|
||||
'is_spa' => ['type' => 'boolean'],
|
||||
'is_git_shallow_clone_enabled' => ['type' => 'boolean'],
|
||||
'is_pr_deployments_public_enabled' => ['type' => 'boolean'],
|
||||
'use_build_secrets' => ['type' => 'boolean'],
|
||||
'inject_build_args_to_dockerfile' => ['type' => 'boolean'],
|
||||
'include_source_commit_in_build' => ['type' => 'boolean'],
|
||||
'docker_images_to_keep' => ['type' => 'integer'],
|
||||
'stop_grace_period' => ['type' => 'integer', 'nullable' => true],
|
||||
]
|
||||
)]
|
||||
class ApplicationSetting extends Model
|
||||
{
|
||||
protected $casts = [
|
||||
@@ -27,6 +69,17 @@ class ApplicationSetting extends Model
|
||||
'is_git_shallow_clone_enabled' => 'boolean',
|
||||
'docker_images_to_keep' => 'integer',
|
||||
'stop_grace_period' => 'integer',
|
||||
'is_log_drain_enabled' => 'boolean',
|
||||
'is_gpu_enabled' => 'boolean',
|
||||
'is_include_timestamps' => 'boolean',
|
||||
'is_swarm_only_worker_nodes' => 'boolean',
|
||||
'is_raw_compose_deployment_enabled' => 'boolean',
|
||||
'is_consistent_container_name_enabled' => 'boolean',
|
||||
'is_gzip_enabled' => 'boolean',
|
||||
'is_stripprefix_enabled' => 'boolean',
|
||||
'connect_to_docker_network' => 'boolean',
|
||||
'is_env_sorting_enabled' => 'boolean',
|
||||
'disable_build_cache' => 'boolean',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
|
||||
@@ -102,6 +102,8 @@ class ApplicationConfigurationSnapshot
|
||||
$this->item('git_repository', 'Repository', $this->application->git_repository, 'build'),
|
||||
$this->item('git_branch', 'Branch', $this->application->git_branch, 'build'),
|
||||
$this->item('git_commit_sha', 'Commit SHA', $this->application->git_commit_sha, 'build'),
|
||||
$this->item('source_id', 'Source ID', $this->application->source_id, 'build'),
|
||||
$this->item('source_type', 'Source type', $this->application->source_type, 'build'),
|
||||
$this->item('private_key_id', 'Private key', $this->application->private_key_id, 'build'),
|
||||
];
|
||||
}
|
||||
@@ -113,6 +115,8 @@ class ApplicationConfigurationSnapshot
|
||||
{
|
||||
return [
|
||||
$this->item('build_pack', 'Build pack', $this->application->build_pack, 'build'),
|
||||
$this->item('is_static', 'Static site', data_get($this->application, 'settings.is_static'), 'build'),
|
||||
$this->item('is_spa', 'Single-page application', data_get($this->application, 'settings.is_spa'), 'build'),
|
||||
$this->item('static_image', 'Static image', $this->application->static_image, 'build'),
|
||||
$this->item('base_directory', 'Base directory', $this->application->base_directory, 'build'),
|
||||
$this->item('publish_directory', 'Publish directory', $this->application->publish_directory, 'build'),
|
||||
@@ -127,7 +131,11 @@ class ApplicationConfigurationSnapshot
|
||||
// so comparing it would flag a permanent change for git-based compose apps.
|
||||
$this->item('docker_compose_raw', 'Docker Compose', $this->application->docker_compose_raw, 'build', displayValue: $this->summarizeText($this->application->docker_compose_raw), displayFull: $this->application->docker_compose_raw, diffMode: 'lines'),
|
||||
$this->item('docker_compose_custom_build_command', 'Docker Compose custom build command', $this->application->docker_compose_custom_build_command, 'build'),
|
||||
$this->item('custom_docker_run_options', 'Custom Docker run options', $this->application->custom_docker_run_options, 'build'),
|
||||
$this->item('is_git_submodules_enabled', 'Git submodules', data_get($this->application, 'settings.is_git_submodules_enabled'), 'build'),
|
||||
$this->item('is_git_lfs_enabled', 'Git LFS', data_get($this->application, 'settings.is_git_lfs_enabled'), 'build'),
|
||||
$this->item('is_git_shallow_clone_enabled', 'Shallow clone', data_get($this->application, 'settings.is_git_shallow_clone_enabled'), 'build'),
|
||||
$this->item('is_env_sorting_enabled', 'Sort environment variables', data_get($this->application, 'settings.is_env_sorting_enabled'), 'build'),
|
||||
$this->item('custom_docker_run_options', 'Custom Docker run options', $this->application->custom_docker_run_options, 'redeploy'),
|
||||
$this->item('use_build_secrets', 'Use build secrets', data_get($this->application, 'settings.use_build_secrets'), 'build'),
|
||||
$this->item('inject_build_args_to_dockerfile', 'Inject build args to Dockerfile', data_get($this->application, 'settings.inject_build_args_to_dockerfile'), 'build'),
|
||||
$this->item('include_source_commit_in_build', 'Include source commit in build', data_get($this->application, 'settings.include_source_commit_in_build'), 'build'),
|
||||
@@ -142,13 +150,26 @@ class ApplicationConfigurationSnapshot
|
||||
private function runtimeItems(): array
|
||||
{
|
||||
return [
|
||||
$this->item('docker_registry_image_name', 'Docker image', $this->application->docker_registry_image_name, 'redeploy'),
|
||||
$this->item('docker_registry_image_tag', 'Docker image tag or hash', $this->application->docker_registry_image_tag, 'redeploy'),
|
||||
$this->item('start_command', 'Start command', $this->application->start_command, 'redeploy'),
|
||||
$this->item('pre_deployment_command', 'Pre-deployment command', $this->application->pre_deployment_command, 'redeploy'),
|
||||
$this->item('pre_deployment_command_container', 'Pre-deployment command container', $this->application->pre_deployment_command_container, 'redeploy'),
|
||||
$this->item('post_deployment_command', 'Post-deployment command', $this->application->post_deployment_command, 'redeploy'),
|
||||
$this->item('post_deployment_command_container', 'Post-deployment command container', $this->application->post_deployment_command_container, 'redeploy'),
|
||||
$this->item('docker_compose_custom_start_command', 'Docker Compose custom start command', $this->application->docker_compose_custom_start_command, 'redeploy'),
|
||||
$this->item('ports_exposes', 'Exposed ports', $this->application->ports_exposes, 'redeploy'),
|
||||
$this->item('ports_mappings', 'Port mappings', $this->application->ports_mappings, 'redeploy'),
|
||||
$this->item('custom_network_aliases', 'Network aliases', $this->application->custom_network_aliases, 'redeploy'),
|
||||
$this->item('connect_to_docker_network', 'Connect to Docker network', data_get($this->application, 'settings.connect_to_docker_network'), 'redeploy'),
|
||||
$this->item('custom_internal_name', 'Custom container name', data_get($this->application, 'settings.custom_internal_name'), 'redeploy'),
|
||||
$this->item('is_consistent_container_name_enabled', 'Consistent container name', data_get($this->application, 'settings.is_consistent_container_name_enabled'), 'redeploy'),
|
||||
$this->item('is_container_label_escape_enabled', 'Escape container labels', data_get($this->application, 'settings.is_container_label_escape_enabled'), 'redeploy'),
|
||||
$this->item('is_container_label_readonly_enabled', 'Read-only container labels', data_get($this->application, 'settings.is_container_label_readonly_enabled'), 'redeploy'),
|
||||
$this->item('is_log_drain_enabled', 'Log drain', data_get($this->application, 'settings.is_log_drain_enabled'), 'redeploy'),
|
||||
$this->item('is_swarm_only_worker_nodes', 'Swarm worker nodes only', data_get($this->application, 'settings.is_swarm_only_worker_nodes'), 'redeploy'),
|
||||
$this->item('stop_grace_period', 'Stop grace period', $this->normalizedStopGracePeriod(), 'redeploy'),
|
||||
$this->item('is_preserve_repository_enabled', 'Preserve repository', data_get($this->application, 'settings.is_preserve_repository_enabled'), 'redeploy'),
|
||||
$this->item('is_raw_compose_deployment_enabled', 'Raw Compose deployment', data_get($this->application, 'settings.is_raw_compose_deployment_enabled'), 'redeploy'),
|
||||
$this->item('is_gpu_enabled', 'GPU enabled', data_get($this->application, 'settings.is_gpu_enabled'), 'redeploy'),
|
||||
$this->item('gpu_driver', 'GPU driver', data_get($this->application, 'settings.gpu_driver'), 'redeploy'),
|
||||
@@ -170,7 +191,7 @@ class ApplicationConfigurationSnapshot
|
||||
$this->item('docker_compose_domains', 'Service domains', $this->decodedComposeDomains(), 'redeploy', displayValue: $this->summarizeText($this->composeDomainsText()), displayFull: $this->composeDomainsText(), diffMode: 'lines'),
|
||||
$this->item('redirect', 'Redirect', $this->application->redirect, 'redeploy'),
|
||||
$this->item('custom_labels', 'Container labels', $this->application->custom_labels, 'redeploy', displayValue: $this->summarizeText($this->decodeCustomLabels($this->application->custom_labels)), displayFull: $this->decodeCustomLabels($this->application->custom_labels), diffMode: 'lines'),
|
||||
$this->item('custom_nginx_configuration', 'Custom Nginx configuration', $this->application->custom_nginx_configuration, 'redeploy', displayValue: $this->summarizeText($this->application->custom_nginx_configuration), displayFull: $this->application->custom_nginx_configuration),
|
||||
$this->item('custom_nginx_configuration', 'Custom Nginx configuration', $this->application->custom_nginx_configuration, 'build', displayValue: $this->summarizeText($this->application->custom_nginx_configuration), displayFull: $this->application->custom_nginx_configuration),
|
||||
$this->item('is_force_https_enabled', 'Force HTTPS', data_get($this->application, 'settings.is_force_https_enabled'), 'redeploy'),
|
||||
$this->item('is_gzip_enabled', 'Gzip', data_get($this->application, 'settings.is_gzip_enabled'), 'redeploy'),
|
||||
$this->item('is_stripprefix_enabled', 'Strip prefix', data_get($this->application, 'settings.is_stripprefix_enabled'), 'redeploy'),
|
||||
@@ -327,6 +348,17 @@ class ApplicationConfigurationSnapshot
|
||||
return $flags ? "Hidden ({$flags})" : 'Hidden';
|
||||
}
|
||||
|
||||
private function normalizedStopGracePeriod(): ?int
|
||||
{
|
||||
$stopGracePeriod = data_get($this->application, 'settings.stop_grace_period');
|
||||
|
||||
if ($stopGracePeriod === null || (int) $stopGracePeriod === DEFAULT_STOP_GRACE_PERIOD_SECONDS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $stopGracePeriod;
|
||||
}
|
||||
|
||||
private function environmentFlags(EnvironmentVariable $environmentVariable): string
|
||||
{
|
||||
return collect([
|
||||
|
||||
@@ -17,6 +17,28 @@ class ConfigurationDiffer
|
||||
*/
|
||||
private const IGNORED_KEYS = ['build.docker_compose'];
|
||||
|
||||
/**
|
||||
* Defaults for fields introduced after configuration snapshots were first
|
||||
* stored. Older snapshots omitted these keys, which should not make an
|
||||
* unchanged default look like a pending configuration change.
|
||||
*
|
||||
* @var array<string, bool|array<int, bool>>
|
||||
*/
|
||||
private const INTRODUCED_DEFAULTS = [
|
||||
'build.is_static' => false,
|
||||
'build.is_spa' => false,
|
||||
'build.is_git_submodules_enabled' => true,
|
||||
'build.is_git_lfs_enabled' => true,
|
||||
'build.is_git_shallow_clone_enabled' => true,
|
||||
'build.is_env_sorting_enabled' => [false, true],
|
||||
'runtime.is_consistent_container_name_enabled' => false,
|
||||
'runtime.is_container_label_escape_enabled' => true,
|
||||
'runtime.is_container_label_readonly_enabled' => true,
|
||||
'runtime.is_log_drain_enabled' => false,
|
||||
'runtime.is_swarm_only_worker_nodes' => true,
|
||||
'runtime.is_preserve_repository_enabled' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $previousSnapshot
|
||||
* @param array<string, mixed> $currentSnapshot
|
||||
@@ -36,6 +58,14 @@ class ConfigurationDiffer
|
||||
$previous = $previousItems[$key] ?? null;
|
||||
$current = $currentItems[$key] ?? null;
|
||||
|
||||
if (
|
||||
$previous === null
|
||||
&& array_key_exists($key, self::INTRODUCED_DEFAULTS)
|
||||
&& in_array((bool) data_get($current, 'compare_value'), (array) self::INTRODUCED_DEFAULTS[$key], true)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($previous['compare_value'] ?? null) === ($current['compare_value'] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,20 @@ function sharedDataApplications()
|
||||
'is_auto_deploy_enabled' => 'boolean',
|
||||
'is_force_https_enabled' => 'boolean',
|
||||
'is_preview_deployments_enabled' => 'boolean',
|
||||
'use_build_secrets' => 'boolean',
|
||||
'is_git_submodules_enabled' => 'boolean',
|
||||
'is_git_lfs_enabled' => 'boolean',
|
||||
'is_git_shallow_clone_enabled' => 'boolean',
|
||||
'disable_build_cache' => 'boolean',
|
||||
'inject_build_args_to_dockerfile' => 'boolean',
|
||||
'include_source_commit_in_build' => 'boolean',
|
||||
'is_env_sorting_enabled' => 'boolean',
|
||||
'is_pr_deployments_public_enabled' => 'boolean',
|
||||
'is_gzip_enabled' => 'boolean',
|
||||
'is_stripprefix_enabled' => 'boolean',
|
||||
'is_raw_compose_deployment_enabled' => 'boolean',
|
||||
'stop_grace_period' => 'nullable|integer|min:'.MIN_STOP_GRACE_PERIOD_SECONDS.'|max:'.MAX_STOP_GRACE_PERIOD_SECONDS,
|
||||
'docker_images_to_keep' => 'integer|min:0|max:100',
|
||||
'static_image' => Rule::enum(StaticImageTypes::class),
|
||||
'domains' => ValidationPatterns::applicationDomainRules(),
|
||||
'noindex_domains' => 'array|nullable',
|
||||
@@ -274,6 +288,7 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
||||
$request->offsetUnset('github_app_uuid');
|
||||
$request->offsetUnset('private_key_uuid');
|
||||
$request->offsetUnset('use_build_server');
|
||||
$request->offsetUnset('use_build_secrets');
|
||||
$request->offsetUnset('is_static');
|
||||
$request->offsetUnset('is_spa');
|
||||
$request->offsetUnset('is_auto_deploy_enabled');
|
||||
@@ -285,6 +300,18 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
||||
$request->offsetUnset('is_container_label_escape_enabled');
|
||||
$request->offsetUnset('is_preserve_repository_enabled');
|
||||
$request->offsetUnset('include_source_commit_in_build');
|
||||
$request->offsetUnset('is_git_submodules_enabled');
|
||||
$request->offsetUnset('is_git_lfs_enabled');
|
||||
$request->offsetUnset('is_git_shallow_clone_enabled');
|
||||
$request->offsetUnset('disable_build_cache');
|
||||
$request->offsetUnset('inject_build_args_to_dockerfile');
|
||||
$request->offsetUnset('is_env_sorting_enabled');
|
||||
$request->offsetUnset('is_pr_deployments_public_enabled');
|
||||
$request->offsetUnset('stop_grace_period');
|
||||
$request->offsetUnset('docker_images_to_keep');
|
||||
$request->offsetUnset('is_gzip_enabled');
|
||||
$request->offsetUnset('is_stripprefix_enabled');
|
||||
$request->offsetUnset('is_raw_compose_deployment_enabled');
|
||||
$request->offsetUnset('docker_compose_raw');
|
||||
$request->offsetUnset('tags');
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ ARG CLOUDFLARED_VERSION=2025.7.0
|
||||
# Note: We are using version 18 of the postgres client (while still using postgres 15 for the postgres server) as version 15 has been removed from Alpine 3.23+ https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.23&repo=&arch=x86_64&origin=&flagged=&maintainer=
|
||||
ARG POSTGRES_VERSION=18
|
||||
# https://nginx.org/en/linux_packages.html
|
||||
ARG NGINX_VERSION=1.31.0-r1
|
||||
ARG NGINX_VERSION=1.31.2-r1
|
||||
|
||||
# =================================================================
|
||||
# Get MinIO client
|
||||
|
||||
@@ -9,7 +9,7 @@ ARG CLOUDFLARED_VERSION=2025.7.0
|
||||
# Note: We are using version 18 of the postgres client (while still using postgres 15 for the postgres server) as version 15 has been removed from Alpine 3.23+ https://pkgs.alpinelinux.org/packages?name=postgresql*-client&branch=v3.23&repo=&arch=x86_64&origin=&flagged=&maintainer=
|
||||
ARG POSTGRES_VERSION=18
|
||||
# https://nginx.org/en/linux_packages.html
|
||||
ARG NGINX_VERSION=1.31.0-r1
|
||||
ARG NGINX_VERSION=1.31.2-r1
|
||||
|
||||
# Add user/group
|
||||
ARG USER_ID=9999
|
||||
|
||||
+551
-4
@@ -137,6 +137,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
"description": "The git commit SHA."
|
||||
@@ -380,6 +387,68 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"is_http_basic_auth_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "HTTP Basic Authentication enabled."
|
||||
@@ -598,6 +667,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
"description": "The git commit SHA."
|
||||
@@ -841,6 +917,68 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"is_http_basic_auth_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "HTTP Basic Authentication enabled."
|
||||
@@ -1059,6 +1197,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
"description": "The git commit SHA."
|
||||
@@ -1302,6 +1447,68 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"is_http_basic_auth_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "HTTP Basic Authentication enabled."
|
||||
@@ -1505,6 +1712,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"docker_registry_image_name": {
|
||||
"type": "string",
|
||||
"description": "The docker registry image name."
|
||||
@@ -1668,6 +1882,68 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"is_http_basic_auth_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "HTTP Basic Authentication enabled."
|
||||
@@ -1863,6 +2139,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"ports_mappings": {
|
||||
"type": "string",
|
||||
"description": "The ports mappings."
|
||||
@@ -2014,6 +2297,68 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"is_http_basic_auth_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "HTTP Basic Authentication enabled."
|
||||
@@ -2360,6 +2705,13 @@
|
||||
"type": "string",
|
||||
"description": "The application URLs in a comma-separated list."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored."
|
||||
},
|
||||
"git_commit_sha": {
|
||||
"type": "string",
|
||||
"description": "The git commit SHA."
|
||||
@@ -2596,6 +2948,67 @@
|
||||
"nullable": true,
|
||||
"description": "Use build server."
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean",
|
||||
"description": "Use Docker Build Secrets for build-time environment variables."
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Clone Git submodules."
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable Git LFS."
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Use a shallow Git clone."
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean",
|
||||
"description": "Disable the build cache."
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean",
|
||||
"description": "Inject build arguments into the Dockerfile build."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include the source commit in the build."
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Sort environment variables."
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Make pull request deployments public."
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"minimum": 1,
|
||||
"maximum": 3600,
|
||||
"description": "Container stop grace period in seconds."
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"description": "Number of Docker images to retain."
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable gzip compression."
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable path prefix stripping."
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Deploy the raw Docker Compose definition."
|
||||
},
|
||||
"connect_to_docker_network": {
|
||||
"type": "boolean",
|
||||
"description": "The flag to connect the service to the predefined Docker network."
|
||||
@@ -2612,10 +3025,6 @@
|
||||
"is_preserve_repository_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false."
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean",
|
||||
"description": "Include source commit information in the build. Default is false."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -12212,6 +12621,16 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"noindex_domains": {
|
||||
"description": "The subset of the service application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the domains are ignored.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"human_name": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -14783,6 +15202,14 @@
|
||||
"nullable": true,
|
||||
"description": "The application domains."
|
||||
},
|
||||
"noindex_domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header."
|
||||
},
|
||||
"config_hash": {
|
||||
"type": "string",
|
||||
"description": "Configuration hash."
|
||||
@@ -15146,6 +15573,9 @@
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Password for HTTP Basic Authentication"
|
||||
},
|
||||
"": {
|
||||
"$ref": "#\/components\/schemas\/ApplicationSetting"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@@ -15241,6 +15671,123 @@
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ApplicationSetting": {
|
||||
"description": "Application settings.",
|
||||
"properties": {
|
||||
"is_static": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_git_submodules_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_git_lfs_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_auto_deploy_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_force_https_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_debug_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_preview_deployments_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_log_drain_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_gpu_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"gpu_driver": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"gpu_count": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"gpu_device_ids": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"gpu_options": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"is_include_timestamps": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_swarm_only_worker_nodes": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_raw_compose_deployment_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_build_server_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_consistent_container_name_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_gzip_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_stripprefix_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"connect_to_docker_network": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"custom_internal_name": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"is_container_label_escape_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_env_sorting_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_container_label_readonly_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_preserve_repository_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disable_build_cache": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_spa": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_git_shallow_clone_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pr_deployments_public_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_build_secrets": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"inject_build_args_to_dockerfile": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"include_source_commit_in_build": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"docker_images_to_keep": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stop_grace_period": {
|
||||
"type": "integer",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"Environment": {
|
||||
"description": "Environment model",
|
||||
"properties": {
|
||||
|
||||
+403
-3
@@ -97,6 +97,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
@@ -268,6 +272,54 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
default: false
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
is_http_basic_auth_enabled:
|
||||
type: boolean
|
||||
description: 'HTTP Basic Authentication enabled.'
|
||||
@@ -391,6 +443,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
@@ -562,6 +618,54 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
default: false
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
is_http_basic_auth_enabled:
|
||||
type: boolean
|
||||
description: 'HTTP Basic Authentication enabled.'
|
||||
@@ -685,6 +789,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
@@ -856,6 +964,54 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
default: false
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
is_http_basic_auth_enabled:
|
||||
type: boolean
|
||||
description: 'HTTP Basic Authentication enabled.'
|
||||
@@ -970,6 +1126,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
docker_registry_image_name:
|
||||
type: string
|
||||
description: 'The docker registry image name.'
|
||||
@@ -1091,6 +1251,54 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
default: false
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
is_http_basic_auth_enabled:
|
||||
type: boolean
|
||||
description: 'HTTP Basic Authentication enabled.'
|
||||
@@ -1200,6 +1408,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
ports_mappings:
|
||||
type: string
|
||||
description: 'The ports mappings.'
|
||||
@@ -1312,6 +1524,54 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
default: false
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
is_http_basic_auth_enabled:
|
||||
type: boolean
|
||||
description: 'HTTP Basic Authentication enabled.'
|
||||
@@ -1521,6 +1781,10 @@ paths:
|
||||
domains:
|
||||
type: string
|
||||
description: 'The application URLs in a comma-separated list.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the application domains are ignored.'
|
||||
git_commit_sha:
|
||||
type: string
|
||||
description: 'The git commit SHA.'
|
||||
@@ -1688,6 +1952,53 @@ paths:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
description: 'Use Docker Build Secrets for build-time environment variables.'
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
description: 'Clone Git submodules.'
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
description: 'Enable Git LFS.'
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
description: 'Use a shallow Git clone.'
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
description: 'Disable the build cache.'
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
description: 'Inject build arguments into the Dockerfile build.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include the source commit in the build.'
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
description: 'Sort environment variables.'
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
description: 'Make pull request deployments public.'
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
minimum: 1
|
||||
maximum: 3600
|
||||
description: 'Container stop grace period in seconds.'
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
description: 'Number of Docker images to retain.'
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
description: 'Enable gzip compression.'
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
description: 'Enable path prefix stripping.'
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
description: 'Deploy the raw Docker Compose definition.'
|
||||
connect_to_docker_network:
|
||||
type: boolean
|
||||
description: 'The flag to connect the service to the predefined Docker network.'
|
||||
@@ -1701,9 +2012,6 @@ paths:
|
||||
is_preserve_repository_enabled:
|
||||
type: boolean
|
||||
description: 'Preserve git repository during application update. If false, the existing repository will be removed and replaced with the new one. If true, the existing repository will be kept and the new one will be ignored. Default is false.'
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
description: 'Include source commit information in the build. Default is false.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -7805,6 +8113,10 @@ paths:
|
||||
url:
|
||||
description: 'Comma-separated list of URLs (e.g. "http://app.example.com:8080,https://app2.example.com"). Stored as fqdn.'
|
||||
type: [string, 'null']
|
||||
noindex_domains:
|
||||
description: 'The subset of the service application domains served with an X-Robots-Tag: noindex, nofollow response header, keeping them out of search engines. Entries that are not among the domains are ignored.'
|
||||
type: [array, 'null']
|
||||
items: { type: string }
|
||||
human_name:
|
||||
type: [string, 'null']
|
||||
description:
|
||||
@@ -9399,6 +9711,12 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: 'The application domains.'
|
||||
noindex_domains:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
description: 'The subset of the application domains served with an X-Robots-Tag: noindex, nofollow response header.'
|
||||
config_hash:
|
||||
type: string
|
||||
description: 'Configuration hash.'
|
||||
@@ -9683,6 +10001,8 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: 'Password for HTTP Basic Authentication'
|
||||
'':
|
||||
$ref: '#/components/schemas/ApplicationSetting'
|
||||
type: object
|
||||
ApplicationDeploymentQueue:
|
||||
description: 'Project model'
|
||||
@@ -9746,6 +10066,86 @@ components:
|
||||
commit_message:
|
||||
type: string
|
||||
type: object
|
||||
ApplicationSetting:
|
||||
description: 'Application settings.'
|
||||
properties:
|
||||
is_static:
|
||||
type: boolean
|
||||
is_git_submodules_enabled:
|
||||
type: boolean
|
||||
is_git_lfs_enabled:
|
||||
type: boolean
|
||||
is_auto_deploy_enabled:
|
||||
type: boolean
|
||||
is_force_https_enabled:
|
||||
type: boolean
|
||||
is_debug_enabled:
|
||||
type: boolean
|
||||
is_preview_deployments_enabled:
|
||||
type: boolean
|
||||
is_log_drain_enabled:
|
||||
type: boolean
|
||||
is_gpu_enabled:
|
||||
type: boolean
|
||||
gpu_driver:
|
||||
type: string
|
||||
nullable: true
|
||||
gpu_count:
|
||||
type: string
|
||||
nullable: true
|
||||
gpu_device_ids:
|
||||
type: string
|
||||
nullable: true
|
||||
gpu_options:
|
||||
type: string
|
||||
nullable: true
|
||||
is_include_timestamps:
|
||||
type: boolean
|
||||
is_swarm_only_worker_nodes:
|
||||
type: boolean
|
||||
is_raw_compose_deployment_enabled:
|
||||
type: boolean
|
||||
is_build_server_enabled:
|
||||
type: boolean
|
||||
is_consistent_container_name_enabled:
|
||||
type: boolean
|
||||
is_gzip_enabled:
|
||||
type: boolean
|
||||
is_stripprefix_enabled:
|
||||
type: boolean
|
||||
connect_to_docker_network:
|
||||
type: boolean
|
||||
custom_internal_name:
|
||||
type: string
|
||||
nullable: true
|
||||
is_container_label_escape_enabled:
|
||||
type: boolean
|
||||
is_env_sorting_enabled:
|
||||
type: boolean
|
||||
is_container_label_readonly_enabled:
|
||||
type: boolean
|
||||
is_preserve_repository_enabled:
|
||||
type: boolean
|
||||
disable_build_cache:
|
||||
type: boolean
|
||||
is_spa:
|
||||
type: boolean
|
||||
is_git_shallow_clone_enabled:
|
||||
type: boolean
|
||||
is_pr_deployments_public_enabled:
|
||||
type: boolean
|
||||
use_build_secrets:
|
||||
type: boolean
|
||||
inject_build_args_to_dockerfile:
|
||||
type: boolean
|
||||
include_source_commit_in_build:
|
||||
type: boolean
|
||||
docker_images_to_keep:
|
||||
type: integer
|
||||
stop_grace_period:
|
||||
type: integer
|
||||
nullable: true
|
||||
type: object
|
||||
Environment:
|
||||
description: 'Environment model'
|
||||
properties:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
services:
|
||||
frontend:
|
||||
image: ghcr.io/smaug6739/alexandrie-frontend:v8.7.2
|
||||
image: ghcr.io/smaug6739/alexandrie-frontend:v8.10.0
|
||||
environment:
|
||||
- SERVICE_URL_FRONTEND_8200
|
||||
- PORT=8200
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
image: ghcr.io/smaug6739/alexandrie-backend:v8.7.2
|
||||
image: ghcr.io/smaug6739/alexandrie-backend:v8.10.0
|
||||
environment:
|
||||
- SERVICE_URL_BACKEND_8201
|
||||
- BACKEND_PORT=8201
|
||||
@@ -74,7 +74,7 @@ services:
|
||||
retries: 5
|
||||
|
||||
rustfs:
|
||||
image: rustfs/rustfs:1.0.0-alpha.90
|
||||
image: rustfs/rustfs:1.0.0-beta.8
|
||||
environment:
|
||||
- SERVICE_URL_RUSTFS_9000
|
||||
- RUSTFS_ACCESS_KEY=${SERVICE_USER_RUSTFS}
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
services:
|
||||
espocrm:
|
||||
image: espocrm/espocrm:9
|
||||
image: espocrm/espocrm:10
|
||||
container_name: espocrm
|
||||
environment:
|
||||
- SERVICE_URL_ESPOCRM
|
||||
- ESPOCRM_ADMIN_USERNAME=${ESPOCRM_ADMIN_USERNAME:-admin}
|
||||
@@ -19,30 +20,39 @@ services:
|
||||
- ESPOCRM_DATABASE_PASSWORD=${SERVICE_PASSWORD_MARIADB}
|
||||
- ESPOCRM_SITE_URL=${SERVICE_URL_ESPOCRM}
|
||||
volumes:
|
||||
- espocrm:/var/www/html
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
start_period: 60s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
- espocrm-data:/var/www/html/data
|
||||
- espocrm-custom:/var/www/html/custom
|
||||
- espocrm-custom-client:/var/www/html/client/custom
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
espocrm-db:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "bin/command", "app-check"]
|
||||
start_period: 20s
|
||||
interval: 60s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
espocrm-daemon:
|
||||
image: espocrm/espocrm:9
|
||||
image: espocrm/espocrm:10
|
||||
container_name: espocrm-daemon
|
||||
volumes:
|
||||
- espocrm:/var/www/html
|
||||
restart: always
|
||||
volumes_from:
|
||||
- espocrm
|
||||
restart: unless-stopped
|
||||
entrypoint: docker-daemon.sh
|
||||
depends_on:
|
||||
espocrm:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "bin/command", "app-check"]
|
||||
start_period: 20s
|
||||
interval: 180s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
espocrm-websocket:
|
||||
image: espocrm/espocrm:9
|
||||
image: espocrm/espocrm:10
|
||||
container_name: espocrm-websocket
|
||||
environment:
|
||||
- SERVICE_URL_ESPOCRM_WEBSOCKET_8080
|
||||
@@ -50,16 +60,23 @@ services:
|
||||
- ESPOCRM_CONFIG_WEB_SOCKET_URL=$SERVICE_URL_ESPOCRM_WEBSOCKET
|
||||
- ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBSCRIBER_DSN=tcp://*:7777
|
||||
- ESPOCRM_CONFIG_WEB_SOCKET_ZERO_M_Q_SUBMISSION_DSN=tcp://espocrm-websocket:7777
|
||||
volumes:
|
||||
- espocrm:/var/www/html
|
||||
restart: always
|
||||
volumes_from:
|
||||
- espocrm
|
||||
restart: unless-stopped
|
||||
entrypoint: docker-websocket.sh
|
||||
depends_on:
|
||||
espocrm:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "bin/command", "app-check"]
|
||||
start_period: 20s
|
||||
interval: 180s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
espocrm-db:
|
||||
image: mariadb:11.8
|
||||
image: mariadb:12.3
|
||||
container_name: espocrm-db
|
||||
environment:
|
||||
- MARIADB_DATABASE=${MARIADB_DATABASE:-espocrm}
|
||||
- MARIADB_USER=${SERVICE_USER_MARIADB}
|
||||
@@ -67,6 +84,7 @@ services:
|
||||
- MARIADB_ROOT_PASSWORD=${SERVICE_PASSWORD_ROOT}
|
||||
volumes:
|
||||
- espocrm-db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 20s
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"alexandrie": {
|
||||
"documentation": "https://github.com/Smaug6739/Alexandrie/tree/main/docs?utm_source=coolify.io",
|
||||
"slogan": "A powerful Markdown workspace designed for speed, clarity, and creativity.",
|
||||
"compose": "c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ2doY3IuaW8vc21hdWc2NzM5L2FsZXhhbmRyaWUtZnJvbnRlbmQ6djguNy4yJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9VUkxfRlJPTlRFTkRfODIwMAogICAgICAtIFBPUlQ9ODIwMAogICAgICAtICdOVVhUX1BVQkxJQ19DT05GSUdfRElTQUJMRV9TSUdOVVBfUEFHRT0ke0NPTkZJR19ESVNBQkxFX1NJR05VUDotZmFsc2V9JwogICAgICAtICdOVVhUX1BVQkxJQ19DT05GSUdfRElTQUJMRV9MQU5ESU5HX1BBR0U9JHtDT05GSUdfRElTQUJMRV9MQU5ESU5HOi1mYWxzZX0nCiAgICAgIC0gJ05VWFRfUFVCTElDX0JBU0VfQVBJPSR7U0VSVklDRV9VUkxfQkFDS0VORH0nCiAgICAgIC0gJ05VWFRfUFVCTElDX0JBU0VfQ0ROPSR7U0VSVklDRV9VUkxfUlVTVEZTfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQ0ROX0VORFBPSU5UPSR7Q0ROX0VORFBPSU5UOi0vYWxleGFuZHJpZS99JwogICAgICAtICdOVVhUX1BVQkxJQ19CQVNFX1VSTD0ke1NFUlZJQ0VfVVJMX0ZST05URU5EfScKICAgIGRlcGVuZHNfb246CiAgICAgIC0gYmFja2VuZAogIGJhY2tlbmQ6CiAgICBpbWFnZTogJ2doY3IuaW8vc21hdWc2NzM5L2FsZXhhbmRyaWUtYmFja2VuZDp2OC43LjInCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9CQUNLRU5EXzgyMDEKICAgICAgLSBCQUNLRU5EX1BPUlQ9ODIwMQogICAgICAtIEdJTl9NT0RFPXJlbGVhc2UKICAgICAgLSAnSldUX1NFQ1JFVD0ke1NFUlZJQ0VfUEFTU1dPUkRfSldUfScKICAgICAgLSAnQ09PS0lFX0RPTUFJTj0ke1NFUlZJQ0VfVVJMX0ZST05URU5EfScKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9VUkxfRlJPTlRFTkR9JwogICAgICAtICdBTExPV19VTlNFQ1VSRT0ke0FMTE9XX1VOU0VDVVJFOi1mYWxzZX0nCiAgICAgIC0gREFUQUJBU0VfSE9TVD1teXNxbAogICAgICAtIERBVEFCQVNFX1BPUlQ9MzMwNgogICAgICAtICdEQVRBQkFTRV9OQU1FPSR7TVlTUUxfREFUQUJBU0U6LWFsZXhhbmRyaWUtZGJ9JwogICAgICAtICdEQVRBQkFTRV9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTUlOSU9fRU5EUE9JTlQ9cnVzdGZzOjkwMDAnCiAgICAgIC0gJ01JTklPX1BVQkxJQ19VUkw9JHtTRVJWSUNFX1VSTF9SVVNURlN9JwogICAgICAtICdNSU5JT19TRUNVUkU9JHtNSU5JT19TRUNVUkU6LWZhbHNlfScKICAgICAgLSAnTUlOSU9fQUNDRVNTS0VZPSR7U0VSVklDRV9VU0VSX1JVU1RGU30nCiAgICAgIC0gJ01JTklPX1NFQ1JFVEtFWT0ke1NFUlZJQ0VfUEFTU1dPUkRfUlVTVEZTfScKICAgICAgLSAnTUlOSU9fQlVDS0VUPSR7TUlOSU9fQlVDS0VUOi1hbGV4YW5kcmllfScKICAgICAgLSAnU01UUF9IT1NUPSR7U01UUF9IT1NUOi19JwogICAgICAtICdTTVRQX01BSUw9JHtTTVRQX01BSUw6LX0nCiAgICAgIC0gJ1NNVFBfUEFTU1dPUkQ9JHtTTVRQX1BBU1NXT1JEOi19JwogICAgZGVwZW5kc19vbjoKICAgICAgbXlzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgICAgcnVzdGZzOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgbXlzcWw6CiAgICBpbWFnZTogJ215c3FsOjguMCcKICAgIGVudmlyb25tZW50OgogICAgICAtICdNWVNRTF9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTFJPT1R9JwogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnTVlTUUxfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTVlTUUxfREFUQUJBU0U9JHtNWVNRTF9EQVRBQkFTRTotYWxleGFuZHJpZS1kYn0nCiAgICB2b2x1bWVzOgogICAgICAtICdteXNxbC1kYXRhOi92YXIvbGliL215c3FsJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIGxvY2FsaG9zdAogICAgICAgIC0gJy11JwogICAgICAgIC0gcm9vdAogICAgICAgIC0gJy1wJHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIHRpbWVvdXQ6IDVzCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgcmV0cmllczogNQogIHJ1c3RmczoKICAgIGltYWdlOiAncnVzdGZzL3J1c3RmczoxLjAuMC1hbHBoYS45MCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1JVU1RGU185MDAwCiAgICAgIC0gJ1JVU1RGU19BQ0NFU1NfS0VZPSR7U0VSVklDRV9VU0VSX1JVU1RGU30nCiAgICAgIC0gJ1JVU1RGU19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdSVVNURlNfQ09OU09MRV9FTkFCTEU9JHtSVVNURlNfQ09OU09MRV9FTkFCTEU6LWZhbHNlfScKICAgICAgLSAnUlVTVEZTX0xPR19MRVZFTD0ke1JVU1RGU19MT0dfTEVWRUw6LWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAncnVzdGZzLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3J1c3Rmcy1sb2dzOi9sb2dzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICduYyAteiBsb2NhbGhvc3QgOTAwMCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQo=",
|
||||
"compose": "c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ2doY3IuaW8vc21hdWc2NzM5L2FsZXhhbmRyaWUtZnJvbnRlbmQ6djguMTAuMCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0ZST05URU5EXzgyMDAKICAgICAgLSBQT1JUPTgyMDAKICAgICAgLSAnTlVYVF9QVUJMSUNfQ09ORklHX0RJU0FCTEVfU0lHTlVQX1BBR0U9JHtDT05GSUdfRElTQUJMRV9TSUdOVVA6LWZhbHNlfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQ09ORklHX0RJU0FCTEVfTEFORElOR19QQUdFPSR7Q09ORklHX0RJU0FCTEVfTEFORElORzotZmFsc2V9JwogICAgICAtICdOVVhUX1BVQkxJQ19CQVNFX0FQST0ke1NFUlZJQ0VfVVJMX0JBQ0tFTkR9JwogICAgICAtICdOVVhUX1BVQkxJQ19CQVNFX0NETj0ke1NFUlZJQ0VfVVJMX1JVU1RGU30nCiAgICAgIC0gJ05VWFRfUFVCTElDX0NETl9FTkRQT0lOVD0ke0NETl9FTkRQT0lOVDotL2FsZXhhbmRyaWUvfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQkFTRV9VUkw9JHtTRVJWSUNFX1VSTF9GUk9OVEVORH0nCiAgICBkZXBlbmRzX29uOgogICAgICAtIGJhY2tlbmQKICBiYWNrZW5kOgogICAgaW1hZ2U6ICdnaGNyLmlvL3NtYXVnNjczOS9hbGV4YW5kcmllLWJhY2tlbmQ6djguMTAuMCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0JBQ0tFTkRfODIwMQogICAgICAtIEJBQ0tFTkRfUE9SVD04MjAxCiAgICAgIC0gR0lOX01PREU9cmVsZWFzZQogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdDT09LSUVfRE9NQUlOPSR7U0VSVklDRV9VUkxfRlJPTlRFTkR9JwogICAgICAtICdGUk9OVEVORF9VUkw9JHtTRVJWSUNFX1VSTF9GUk9OVEVORH0nCiAgICAgIC0gJ0FMTE9XX1VOU0VDVVJFPSR7QUxMT1dfVU5TRUNVUkU6LWZhbHNlfScKICAgICAgLSBEQVRBQkFTRV9IT1NUPW15c3FsCiAgICAgIC0gREFUQUJBU0VfUE9SVD0zMzA2CiAgICAgIC0gJ0RBVEFCQVNFX05BTUU9JHtNWVNRTF9EQVRBQkFTRTotYWxleGFuZHJpZS1kYn0nCiAgICAgIC0gJ0RBVEFCQVNFX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdEQVRBQkFTRV9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNSU5JT19FTkRQT0lOVD1ydXN0ZnM6OTAwMCcKICAgICAgLSAnTUlOSU9fUFVCTElDX1VSTD0ke1NFUlZJQ0VfVVJMX1JVU1RGU30nCiAgICAgIC0gJ01JTklPX1NFQ1VSRT0ke01JTklPX1NFQ1VSRTotZmFsc2V9JwogICAgICAtICdNSU5JT19BQ0NFU1NLRVk9JHtTRVJWSUNFX1VTRVJfUlVTVEZTfScKICAgICAgLSAnTUlOSU9fU0VDUkVUS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdNSU5JT19CVUNLRVQ9JHtNSU5JT19CVUNLRVQ6LWFsZXhhbmRyaWV9JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1Q6LX0nCiAgICAgIC0gJ1NNVFBfTUFJTD0ke1NNVFBfTUFJTDotfScKICAgICAgLSAnU01UUF9QQVNTV09SRD0ke1NNVFBfUEFTU1dPUkQ6LX0nCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBydXN0ZnM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICBteXNxbDoKICAgIGltYWdlOiAnbXlzcWw6OC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1hbGV4YW5kcmllLWRifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ215c3FsLWRhdGE6L3Zhci9saWIvbXlzcWwnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbXlzcWxhZG1pbgogICAgICAgIC0gcGluZwogICAgICAgIC0gJy1oJwogICAgICAgIC0gbG9jYWxob3N0CiAgICAgICAgLSAnLXUnCiAgICAgICAgLSByb290CiAgICAgICAgLSAnLXAke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICByZXRyaWVzOiA1CiAgcnVzdGZzOgogICAgaW1hZ2U6ICdydXN0ZnMvcnVzdGZzOjEuMC4wLWJldGEuOCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX1JVU1RGU185MDAwCiAgICAgIC0gJ1JVU1RGU19BQ0NFU1NfS0VZPSR7U0VSVklDRV9VU0VSX1JVU1RGU30nCiAgICAgIC0gJ1JVU1RGU19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdSVVNURlNfQ09OU09MRV9FTkFCTEU9JHtSVVNURlNfQ09OU09MRV9FTkFCTEU6LWZhbHNlfScKICAgICAgLSAnUlVTVEZTX0xPR19MRVZFTD0ke1JVU1RGU19MT0dfTEVWRUw6LWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAncnVzdGZzLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3J1c3Rmcy1sb2dzOi9sb2dzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICduYyAteiBsb2NhbGhvc3QgOTAwMCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQo=",
|
||||
"tags": [
|
||||
"note-taking",
|
||||
"markdown",
|
||||
@@ -64,7 +64,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/alexandrie.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-05T13:36:24+02:00",
|
||||
"template_last_updated_at": "2026-07-07T13:24:35+02:00",
|
||||
"port": "8200"
|
||||
},
|
||||
"anythingllm": {
|
||||
@@ -1310,7 +1310,7 @@
|
||||
"espocrm": {
|
||||
"documentation": "https://docs.espocrm.com?utm_source=coolify.io",
|
||||
"slogan": "EspoCRM is a free and open-source CRM platform.",
|
||||
"compose": "c2VydmljZXM6CiAgZXNwb2NybToKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjknCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9FU1BPQ1JNCiAgICAgIC0gJ0VTUE9DUk1fQURNSU5fVVNFUk5BTUU9JHtFU1BPQ1JNX0FETUlOX1VTRVJOQU1FOi1hZG1pbn0nCiAgICAgIC0gJ0VTUE9DUk1fQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0FETUlOfScKICAgICAgLSBFU1BPQ1JNX0RBVEFCQVNFX1BMQVRGT1JNPU15c3FsCiAgICAgIC0gRVNQT0NSTV9EQVRBQkFTRV9IT1NUPWVzcG9jcm0tZGIKICAgICAgLSAnRVNQT0NSTV9EQVRBQkFTRV9OQU1FPSR7TUFSSUFEQl9EQVRBQkFTRTotZXNwb2NybX0nCiAgICAgIC0gJ0VTUE9DUk1fREFUQUJBU0VfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NQVJJQURCfScKICAgICAgLSAnRVNQT0NSTV9EQVRBQkFTRV9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQn0nCiAgICAgIC0gJ0VTUE9DUk1fU0lURV9VUkw9JHtTRVJWSUNFX1VSTF9FU1BPQ1JNfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VzcG9jcm06L3Zhci93d3cvaHRtbCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHN0YXJ0X3BlcmlvZDogNjBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQogICAgZGVwZW5kc19vbjoKICAgICAgZXNwb2NybS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIGVzcG9jcm0tZGFlbW9uOgogICAgaW1hZ2U6ICdlc3BvY3JtL2VzcG9jcm06OScKICAgIGNvbnRhaW5lcl9uYW1lOiBlc3BvY3JtLWRhZW1vbgogICAgdm9sdW1lczoKICAgICAgLSAnZXNwb2NybTovdmFyL3d3dy9odG1sJwogICAgcmVzdGFydDogYWx3YXlzCiAgICBlbnRyeXBvaW50OiBkb2NrZXItZGFlbW9uLnNoCiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgZXNwb2NybS13ZWJzb2NrZXQ6CiAgICBpbWFnZTogJ2VzcG9jcm0vZXNwb2NybTo5JwogICAgY29udGFpbmVyX25hbWU6IGVzcG9jcm0td2Vic29ja2V0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9FU1BPQ1JNX1dFQlNPQ0tFVF84MDgwCiAgICAgIC0gRVNQT0NSTV9DT05GSUdfVVNFX1dFQl9TT0NLRVQ9dHJ1ZQogICAgICAtIEVTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfVVJMPSRTRVJWSUNFX1VSTF9FU1BPQ1JNX1dFQlNPQ0tFVAogICAgICAtICdFU1BPQ1JNX0NPTkZJR19XRUJfU09DS0VUX1pFUk9fTV9RX1NVQlNDUklCRVJfRFNOPXRjcDovLyo6Nzc3NycKICAgICAgLSAnRVNQT0NSTV9DT05GSUdfV0VCX1NPQ0tFVF9aRVJPX01fUV9TVUJNSVNTSU9OX0RTTj10Y3A6Ly9lc3BvY3JtLXdlYnNvY2tldDo3Nzc3JwogICAgdm9sdW1lczoKICAgICAgLSAnZXNwb2NybTovdmFyL3d3dy9odG1sJwogICAgcmVzdGFydDogYWx3YXlzCiAgICBlbnRyeXBvaW50OiBkb2NrZXItd2Vic29ja2V0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgZXNwb2NybS1kYjoKICAgIGltYWdlOiAnbWFyaWFkYjoxMS44JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01BUklBREJfREFUQUJBU0U9JHtNQVJJQURCX0RBVEFCQVNFOi1lc3BvY3JtfScKICAgICAgLSAnTUFSSUFEQl9VU0VSPSR7U0VSVklDRV9VU0VSX01BUklBREJ9JwogICAgICAtICdNQVJJQURCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NQVJJQURCfScKICAgICAgLSAnTUFSSUFEQl9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9ST09UfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VzcG9jcm0tZGI6L3Zhci9saWIvbXlzcWwnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gaGVhbHRoY2hlY2suc2gKICAgICAgICAtICctLWNvbm5lY3QnCiAgICAgICAgLSAnLS1pbm5vZGJfaW5pdGlhbGl6ZWQnCiAgICAgIGludGVydmFsOiAyMHMKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMK",
|
||||
"compose": "c2VydmljZXM6CiAgZXNwb2NybToKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjEwJwogICAgY29udGFpbmVyX25hbWU6IGVzcG9jcm0KICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfVVJMX0VTUE9DUk0KICAgICAgLSAnRVNQT0NSTV9BRE1JTl9VU0VSTkFNRT0ke0VTUE9DUk1fQURNSU5fVVNFUk5BTUU6LWFkbWlufScKICAgICAgLSAnRVNQT0NSTV9BRE1JTl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQURNSU59JwogICAgICAtIEVTUE9DUk1fREFUQUJBU0VfUExBVEZPUk09TXlzcWwKICAgICAgLSBFU1BPQ1JNX0RBVEFCQVNFX0hPU1Q9ZXNwb2NybS1kYgogICAgICAtICdFU1BPQ1JNX0RBVEFCQVNFX05BTUU9JHtNQVJJQURCX0RBVEFCQVNFOi1lc3BvY3JtfScKICAgICAgLSAnRVNQT0NSTV9EQVRBQkFTRV9VU0VSPSR7U0VSVklDRV9VU0VSX01BUklBREJ9JwogICAgICAtICdFU1BPQ1JNX0RBVEFCQVNFX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NQVJJQURCfScKICAgICAgLSAnRVNQT0NSTV9TSVRFX1VSTD0ke1NFUlZJQ0VfVVJMX0VTUE9DUk19JwogICAgdm9sdW1lczoKICAgICAgLSAnZXNwb2NybS1kYXRhOi92YXIvd3d3L2h0bWwvZGF0YScKICAgICAgLSAnZXNwb2NybS1jdXN0b206L3Zhci93d3cvaHRtbC9jdXN0b20nCiAgICAgIC0gJ2VzcG9jcm0tY3VzdG9tLWNsaWVudDovdmFyL3d3dy9odG1sL2NsaWVudC9jdXN0b20nCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZGVwZW5kc19vbjoKICAgICAgZXNwb2NybS1kYjoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGJpbi9jb21tYW5kCiAgICAgICAgLSBhcHAtY2hlY2sKICAgICAgc3RhcnRfcGVyaW9kOiAyMHMKICAgICAgaW50ZXJ2YWw6IDYwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwogIGVzcG9jcm0tZGFlbW9uOgogICAgaW1hZ2U6ICdlc3BvY3JtL2VzcG9jcm06MTAnCiAgICBjb250YWluZXJfbmFtZTogZXNwb2NybS1kYWVtb24KICAgIHZvbHVtZXNfZnJvbToKICAgICAgLSBlc3BvY3JtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZW50cnlwb2ludDogZG9ja2VyLWRhZW1vbi5zaAogICAgZGVwZW5kc19vbjoKICAgICAgZXNwb2NybToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGJpbi9jb21tYW5kCiAgICAgICAgLSBhcHAtY2hlY2sKICAgICAgc3RhcnRfcGVyaW9kOiAyMHMKICAgICAgaW50ZXJ2YWw6IDE4MHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDMKICBlc3BvY3JtLXdlYnNvY2tldDoKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjEwJwogICAgY29udGFpbmVyX25hbWU6IGVzcG9jcm0td2Vic29ja2V0CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX1VSTF9FU1BPQ1JNX1dFQlNPQ0tFVF84MDgwCiAgICAgIC0gRVNQT0NSTV9DT05GSUdfVVNFX1dFQl9TT0NLRVQ9dHJ1ZQogICAgICAtIEVTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfVVJMPSRTRVJWSUNFX1VSTF9FU1BPQ1JNX1dFQlNPQ0tFVAogICAgICAtICdFU1BPQ1JNX0NPTkZJR19XRUJfU09DS0VUX1pFUk9fTV9RX1NVQlNDUklCRVJfRFNOPXRjcDovLyo6Nzc3NycKICAgICAgLSAnRVNQT0NSTV9DT05GSUdfV0VCX1NPQ0tFVF9aRVJPX01fUV9TVUJNSVNTSU9OX0RTTj10Y3A6Ly9lc3BvY3JtLXdlYnNvY2tldDo3Nzc3JwogICAgdm9sdW1lc19mcm9tOgogICAgICAtIGVzcG9jcm0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnRyeXBvaW50OiBkb2NrZXItd2Vic29ja2V0LnNoCiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gYmluL2NvbW1hbmQKICAgICAgICAtIGFwcC1jaGVjawogICAgICBzdGFydF9wZXJpb2Q6IDIwcwogICAgICBpbnRlcnZhbDogMTgwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwogIGVzcG9jcm0tZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTIuMycKICAgIGNvbnRhaW5lcl9uYW1lOiBlc3BvY3JtLWRiCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTUFSSUFEQl9EQVRBQkFTRT0ke01BUklBREJfREFUQUJBU0U6LWVzcG9jcm19JwogICAgICAtICdNQVJJQURCX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUFSSUFEQn0nCiAgICAgIC0gJ01BUklBREJfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01BUklBREJ9JwogICAgICAtICdNQVJJQURCX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1JPT1R9JwogICAgdm9sdW1lczoKICAgICAgLSAnZXNwb2NybS1kYjovdmFyL2xpYi9teXNxbCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gaGVhbHRoY2hlY2suc2gKICAgICAgICAtICctLWNvbm5lY3QnCiAgICAgICAgLSAnLS1pbm5vZGJfaW5pdGlhbGl6ZWQnCiAgICAgIGludGVydmFsOiAyMHMKICAgICAgc3RhcnRfcGVyaW9kOiAxMHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDMK",
|
||||
"tags": [
|
||||
"crm",
|
||||
"self-hosted",
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/espocrm.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-06T11:35:16-05:00",
|
||||
"template_last_updated_at": "2026-07-03T15:15:43+03:00",
|
||||
"port": "80"
|
||||
},
|
||||
"evolution-api": {
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"alexandrie": {
|
||||
"documentation": "https://github.com/Smaug6739/Alexandrie/tree/main/docs?utm_source=coolify.io",
|
||||
"slogan": "A powerful Markdown workspace designed for speed, clarity, and creativity.",
|
||||
"compose": "c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ2doY3IuaW8vc21hdWc2NzM5L2FsZXhhbmRyaWUtZnJvbnRlbmQ6djguNy4yJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZST05URU5EXzgyMDAKICAgICAgLSBQT1JUPTgyMDAKICAgICAgLSAnTlVYVF9QVUJMSUNfQ09ORklHX0RJU0FCTEVfU0lHTlVQX1BBR0U9JHtDT05GSUdfRElTQUJMRV9TSUdOVVA6LWZhbHNlfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQ09ORklHX0RJU0FCTEVfTEFORElOR19QQUdFPSR7Q09ORklHX0RJU0FCTEVfTEFORElORzotZmFsc2V9JwogICAgICAtICdOVVhUX1BVQkxJQ19CQVNFX0FQST0ke1NFUlZJQ0VfRlFETl9CQUNLRU5EfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQkFTRV9DRE49JHtTRVJWSUNFX0ZRRE5fUlVTVEZTfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQ0ROX0VORFBPSU5UPSR7Q0ROX0VORFBPSU5UOi0vYWxleGFuZHJpZS99JwogICAgICAtICdOVVhUX1BVQkxJQ19CQVNFX1VSTD0ke1NFUlZJQ0VfRlFETl9GUk9OVEVORH0nCiAgICBkZXBlbmRzX29uOgogICAgICAtIGJhY2tlbmQKICBiYWNrZW5kOgogICAgaW1hZ2U6ICdnaGNyLmlvL3NtYXVnNjczOS9hbGV4YW5kcmllLWJhY2tlbmQ6djguNy4yJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0JBQ0tFTkRfODIwMQogICAgICAtIEJBQ0tFTkRfUE9SVD04MjAxCiAgICAgIC0gR0lOX01PREU9cmVsZWFzZQogICAgICAtICdKV1RfU0VDUkVUPSR7U0VSVklDRV9QQVNTV09SRF9KV1R9JwogICAgICAtICdDT09LSUVfRE9NQUlOPSR7U0VSVklDRV9GUUROX0ZST05URU5EfScKICAgICAgLSAnRlJPTlRFTkRfVVJMPSR7U0VSVklDRV9GUUROX0ZST05URU5EfScKICAgICAgLSAnQUxMT1dfVU5TRUNVUkU9JHtBTExPV19VTlNFQ1VSRTotZmFsc2V9JwogICAgICAtIERBVEFCQVNFX0hPU1Q9bXlzcWwKICAgICAgLSBEQVRBQkFTRV9QT1JUPTMzMDYKICAgICAgLSAnREFUQUJBU0VfTkFNRT0ke01ZU1FMX0RBVEFCQVNFOi1hbGV4YW5kcmllLWRifScKICAgICAgLSAnREFUQUJBU0VfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NWVNRTH0nCiAgICAgIC0gJ0RBVEFCQVNFX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTH0nCiAgICAgIC0gJ01JTklPX0VORFBPSU5UPXJ1c3Rmczo5MDAwJwogICAgICAtICdNSU5JT19QVUJMSUNfVVJMPSR7U0VSVklDRV9GUUROX1JVU1RGU30nCiAgICAgIC0gJ01JTklPX1NFQ1VSRT0ke01JTklPX1NFQ1VSRTotZmFsc2V9JwogICAgICAtICdNSU5JT19BQ0NFU1NLRVk9JHtTRVJWSUNFX1VTRVJfUlVTVEZTfScKICAgICAgLSAnTUlOSU9fU0VDUkVUS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdNSU5JT19CVUNLRVQ9JHtNSU5JT19CVUNLRVQ6LWFsZXhhbmRyaWV9JwogICAgICAtICdTTVRQX0hPU1Q9JHtTTVRQX0hPU1Q6LX0nCiAgICAgIC0gJ1NNVFBfTUFJTD0ke1NNVFBfTUFJTDotfScKICAgICAgLSAnU01UUF9QQVNTV09SRD0ke1NNVFBfUEFTU1dPUkQ6LX0nCiAgICBkZXBlbmRzX29uOgogICAgICBteXNxbDoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgICBydXN0ZnM6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICBteXNxbDoKICAgIGltYWdlOiAnbXlzcWw6OC4wJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMUk9PVH0nCiAgICAgIC0gJ01ZU1FMX1VTRVI9JHtTRVJWSUNFX1VTRVJfTVlTUUx9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUx9JwogICAgICAtICdNWVNRTF9EQVRBQkFTRT0ke01ZU1FMX0RBVEFCQVNFOi1hbGV4YW5kcmllLWRifScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ215c3FsLWRhdGE6L3Zhci9saWIvbXlzcWwnCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gbXlzcWxhZG1pbgogICAgICAgIC0gcGluZwogICAgICAgIC0gJy1oJwogICAgICAgIC0gbG9jYWxob3N0CiAgICAgICAgLSAnLXUnCiAgICAgICAgLSByb290CiAgICAgICAgLSAnLXAke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgICAgdGltZW91dDogNXMKICAgICAgaW50ZXJ2YWw6IDEwcwogICAgICByZXRyaWVzOiA1CiAgcnVzdGZzOgogICAgaW1hZ2U6ICdydXN0ZnMvcnVzdGZzOjEuMC4wLWFscGhhLjkwJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1JVU1RGU185MDAwCiAgICAgIC0gJ1JVU1RGU19BQ0NFU1NfS0VZPSR7U0VSVklDRV9VU0VSX1JVU1RGU30nCiAgICAgIC0gJ1JVU1RGU19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdSVVNURlNfQ09OU09MRV9FTkFCTEU9JHtSVVNURlNfQ09OU09MRV9FTkFCTEU6LWZhbHNlfScKICAgICAgLSAnUlVTVEZTX0xPR19MRVZFTD0ke1JVU1RGU19MT0dfTEVWRUw6LWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAncnVzdGZzLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3J1c3Rmcy1sb2dzOi9sb2dzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICduYyAteiBsb2NhbGhvc3QgOTAwMCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQo=",
|
||||
"compose": "c2VydmljZXM6CiAgZnJvbnRlbmQ6CiAgICBpbWFnZTogJ2doY3IuaW8vc21hdWc2NzM5L2FsZXhhbmRyaWUtZnJvbnRlbmQ6djguMTAuMCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GUk9OVEVORF84MjAwCiAgICAgIC0gUE9SVD04MjAwCiAgICAgIC0gJ05VWFRfUFVCTElDX0NPTkZJR19ESVNBQkxFX1NJR05VUF9QQUdFPSR7Q09ORklHX0RJU0FCTEVfU0lHTlVQOi1mYWxzZX0nCiAgICAgIC0gJ05VWFRfUFVCTElDX0NPTkZJR19ESVNBQkxFX0xBTkRJTkdfUEFHRT0ke0NPTkZJR19ESVNBQkxFX0xBTkRJTkc6LWZhbHNlfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQkFTRV9BUEk9JHtTRVJWSUNFX0ZRRE5fQkFDS0VORH0nCiAgICAgIC0gJ05VWFRfUFVCTElDX0JBU0VfQ0ROPSR7U0VSVklDRV9GUUROX1JVU1RGU30nCiAgICAgIC0gJ05VWFRfUFVCTElDX0NETl9FTkRQT0lOVD0ke0NETl9FTkRQT0lOVDotL2FsZXhhbmRyaWUvfScKICAgICAgLSAnTlVYVF9QVUJMSUNfQkFTRV9VUkw9JHtTRVJWSUNFX0ZRRE5fRlJPTlRFTkR9JwogICAgZGVwZW5kc19vbjoKICAgICAgLSBiYWNrZW5kCiAgYmFja2VuZDoKICAgIGltYWdlOiAnZ2hjci5pby9zbWF1ZzY3MzkvYWxleGFuZHJpZS1iYWNrZW5kOnY4LjEwLjAnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fQkFDS0VORF84MjAxCiAgICAgIC0gQkFDS0VORF9QT1JUPTgyMDEKICAgICAgLSBHSU5fTU9ERT1yZWxlYXNlCiAgICAgIC0gJ0pXVF9TRUNSRVQ9JHtTRVJWSUNFX1BBU1NXT1JEX0pXVH0nCiAgICAgIC0gJ0NPT0tJRV9ET01BSU49JHtTRVJWSUNFX0ZRRE5fRlJPTlRFTkR9JwogICAgICAtICdGUk9OVEVORF9VUkw9JHtTRVJWSUNFX0ZRRE5fRlJPTlRFTkR9JwogICAgICAtICdBTExPV19VTlNFQ1VSRT0ke0FMTE9XX1VOU0VDVVJFOi1mYWxzZX0nCiAgICAgIC0gREFUQUJBU0VfSE9TVD1teXNxbAogICAgICAtIERBVEFCQVNFX1BPUlQ9MzMwNgogICAgICAtICdEQVRBQkFTRV9OQU1FPSR7TVlTUUxfREFUQUJBU0U6LWFsZXhhbmRyaWUtZGJ9JwogICAgICAtICdEQVRBQkFTRV9VU0VSPSR7U0VSVklDRV9VU0VSX01ZU1FMfScKICAgICAgLSAnREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01ZU1FMfScKICAgICAgLSAnTUlOSU9fRU5EUE9JTlQ9cnVzdGZzOjkwMDAnCiAgICAgIC0gJ01JTklPX1BVQkxJQ19VUkw9JHtTRVJWSUNFX0ZRRE5fUlVTVEZTfScKICAgICAgLSAnTUlOSU9fU0VDVVJFPSR7TUlOSU9fU0VDVVJFOi1mYWxzZX0nCiAgICAgIC0gJ01JTklPX0FDQ0VTU0tFWT0ke1NFUlZJQ0VfVVNFUl9SVVNURlN9JwogICAgICAtICdNSU5JT19TRUNSRVRLRVk9JHtTRVJWSUNFX1BBU1NXT1JEX1JVU1RGU30nCiAgICAgIC0gJ01JTklPX0JVQ0tFVD0ke01JTklPX0JVQ0tFVDotYWxleGFuZHJpZX0nCiAgICAgIC0gJ1NNVFBfSE9TVD0ke1NNVFBfSE9TVDotfScKICAgICAgLSAnU01UUF9NQUlMPSR7U01UUF9NQUlMOi19JwogICAgICAtICdTTVRQX1BBU1NXT1JEPSR7U01UUF9QQVNTV09SRDotfScKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICAgIHJ1c3RmczoKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIG15c3FsOgogICAgaW1hZ2U6ICdteXNxbDo4LjAnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTVlTUUxST09UfScKICAgICAgLSAnTVlTUUxfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NWVNRTH0nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7TVlTUUxfREFUQUJBU0U6LWFsZXhhbmRyaWUtZGJ9JwogICAgdm9sdW1lczoKICAgICAgLSAnbXlzcWwtZGF0YTovdmFyL2xpYi9teXNxbCcKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBteXNxbGFkbWluCiAgICAgICAgLSBwaW5nCiAgICAgICAgLSAnLWgnCiAgICAgICAgLSBsb2NhbGhvc3QKICAgICAgICAtICctdScKICAgICAgICAtIHJvb3QKICAgICAgICAtICctcCR7U0VSVklDRV9QQVNTV09SRF9NWVNRTFJPT1R9JwogICAgICB0aW1lb3V0OiA1cwogICAgICBpbnRlcnZhbDogMTBzCiAgICAgIHJldHJpZXM6IDUKICBydXN0ZnM6CiAgICBpbWFnZTogJ3J1c3Rmcy9ydXN0ZnM6MS4wLjAtYmV0YS44JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX1JVU1RGU185MDAwCiAgICAgIC0gJ1JVU1RGU19BQ0NFU1NfS0VZPSR7U0VSVklDRV9VU0VSX1JVU1RGU30nCiAgICAgIC0gJ1JVU1RGU19TRUNSRVRfS0VZPSR7U0VSVklDRV9QQVNTV09SRF9SVVNURlN9JwogICAgICAtICdSVVNURlNfQ09OU09MRV9FTkFCTEU9JHtSVVNURlNfQ09OU09MRV9FTkFCTEU6LWZhbHNlfScKICAgICAgLSAnUlVTVEZTX0xPR19MRVZFTD0ke1JVU1RGU19MT0dfTEVWRUw6LWluZm99JwogICAgdm9sdW1lczoKICAgICAgLSAncnVzdGZzLWRhdGE6L2RhdGEnCiAgICAgIC0gJ3J1c3Rmcy1sb2dzOi9sb2dzJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQtU0hFTEwKICAgICAgICAtICduYyAteiBsb2NhbGhvc3QgOTAwMCB8fCBleGl0IDEnCiAgICAgIGludGVydmFsOiAxMHMKICAgICAgdGltZW91dDogNXMKICAgICAgcmV0cmllczogNQo=",
|
||||
"tags": [
|
||||
"note-taking",
|
||||
"markdown",
|
||||
@@ -64,7 +64,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/alexandrie.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-05T13:36:24+02:00",
|
||||
"template_last_updated_at": "2026-07-07T13:24:35+02:00",
|
||||
"port": "8200"
|
||||
},
|
||||
"anythingllm": {
|
||||
@@ -1310,7 +1310,7 @@
|
||||
"espocrm": {
|
||||
"documentation": "https://docs.espocrm.com?utm_source=coolify.io",
|
||||
"slogan": "EspoCRM is a free and open-source CRM platform.",
|
||||
"compose": "c2VydmljZXM6CiAgZXNwb2NybToKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjknCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fRVNQT0NSTQogICAgICAtICdFU1BPQ1JNX0FETUlOX1VTRVJOQU1FPSR7RVNQT0NSTV9BRE1JTl9VU0VSTkFNRTotYWRtaW59JwogICAgICAtICdFU1BPQ1JNX0FETUlOX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9BRE1JTn0nCiAgICAgIC0gRVNQT0NSTV9EQVRBQkFTRV9QTEFURk9STT1NeXNxbAogICAgICAtIEVTUE9DUk1fREFUQUJBU0VfSE9TVD1lc3BvY3JtLWRiCiAgICAgIC0gJ0VTUE9DUk1fREFUQUJBU0VfTkFNRT0ke01BUklBREJfREFUQUJBU0U6LWVzcG9jcm19JwogICAgICAtICdFU1BPQ1JNX0RBVEFCQVNFX1VTRVI9JHtTRVJWSUNFX1VTRVJfTUFSSUFEQn0nCiAgICAgIC0gJ0VTUE9DUk1fREFUQUJBU0VfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX01BUklBREJ9JwogICAgICAtICdFU1BPQ1JNX1NJVEVfVVJMPSR7U0VSVklDRV9GUUROX0VTUE9DUk19JwogICAgdm9sdW1lczoKICAgICAgLSAnZXNwb2NybTovdmFyL3d3dy9odG1sJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgc3RhcnRfcGVyaW9kOiA2MHMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgZXNwb2NybS1kYWVtb246CiAgICBpbWFnZTogJ2VzcG9jcm0vZXNwb2NybTo5JwogICAgY29udGFpbmVyX25hbWU6IGVzcG9jcm0tZGFlbW9uCiAgICB2b2x1bWVzOgogICAgICAtICdlc3BvY3JtOi92YXIvd3d3L2h0bWwnCiAgICByZXN0YXJ0OiBhbHdheXMKICAgIGVudHJ5cG9pbnQ6IGRvY2tlci1kYWVtb24uc2gKICAgIGRlcGVuZHNfb246CiAgICAgIGVzcG9jcm06CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICBlc3BvY3JtLXdlYnNvY2tldDoKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjknCiAgICBjb250YWluZXJfbmFtZTogZXNwb2NybS13ZWJzb2NrZXQKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FU1BPQ1JNX1dFQlNPQ0tFVF84MDgwCiAgICAgIC0gRVNQT0NSTV9DT05GSUdfVVNFX1dFQl9TT0NLRVQ9dHJ1ZQogICAgICAtIEVTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfVVJMPSRTRVJWSUNFX0ZRRE5fRVNQT0NSTV9XRUJTT0NLRVQKICAgICAgLSAnRVNQT0NSTV9DT05GSUdfV0VCX1NPQ0tFVF9aRVJPX01fUV9TVUJTQ1JJQkVSX0RTTj10Y3A6Ly8qOjc3NzcnCiAgICAgIC0gJ0VTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfWkVST19NX1FfU1VCTUlTU0lPTl9EU049dGNwOi8vZXNwb2NybS13ZWJzb2NrZXQ6Nzc3NycKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VzcG9jcm06L3Zhci93d3cvaHRtbCcKICAgIHJlc3RhcnQ6IGFsd2F5cwogICAgZW50cnlwb2ludDogZG9ja2VyLXdlYnNvY2tldC5zaAogICAgZGVwZW5kc19vbjoKICAgICAgZXNwb2NybToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogIGVzcG9jcm0tZGI6CiAgICBpbWFnZTogJ21hcmlhZGI6MTEuOCcKICAgIGVudmlyb25tZW50OgogICAgICAtICdNQVJJQURCX0RBVEFCQVNFPSR7TUFSSUFEQl9EQVRBQkFTRTotZXNwb2NybX0nCiAgICAgIC0gJ01BUklBREJfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NQVJJQURCfScKICAgICAgLSAnTUFSSUFEQl9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQn0nCiAgICAgIC0gJ01BUklBREJfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUk9PVH0nCiAgICB2b2x1bWVzOgogICAgICAtICdlc3BvY3JtLWRiOi92YXIvbGliL215c3FsJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGhlYWx0aGNoZWNrLnNoCiAgICAgICAgLSAnLS1jb25uZWN0JwogICAgICAgIC0gJy0taW5ub2RiX2luaXRpYWxpemVkJwogICAgICBpbnRlcnZhbDogMjBzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCg==",
|
||||
"compose": "c2VydmljZXM6CiAgZXNwb2NybToKICAgIGltYWdlOiAnZXNwb2NybS9lc3BvY3JtOjEwJwogICAgY29udGFpbmVyX25hbWU6IGVzcG9jcm0KICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FU1BPQ1JNCiAgICAgIC0gJ0VTUE9DUk1fQURNSU5fVVNFUk5BTUU9JHtFU1BPQ1JNX0FETUlOX1VTRVJOQU1FOi1hZG1pbn0nCiAgICAgIC0gJ0VTUE9DUk1fQURNSU5fUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX0FETUlOfScKICAgICAgLSBFU1BPQ1JNX0RBVEFCQVNFX1BMQVRGT1JNPU15c3FsCiAgICAgIC0gRVNQT0NSTV9EQVRBQkFTRV9IT1NUPWVzcG9jcm0tZGIKICAgICAgLSAnRVNQT0NSTV9EQVRBQkFTRV9OQU1FPSR7TUFSSUFEQl9EQVRBQkFTRTotZXNwb2NybX0nCiAgICAgIC0gJ0VTUE9DUk1fREFUQUJBU0VfVVNFUj0ke1NFUlZJQ0VfVVNFUl9NQVJJQURCfScKICAgICAgLSAnRVNQT0NSTV9EQVRBQkFTRV9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfTUFSSUFEQn0nCiAgICAgIC0gJ0VTUE9DUk1fU0lURV9VUkw9JHtTRVJWSUNFX0ZRRE5fRVNQT0NSTX0nCiAgICB2b2x1bWVzOgogICAgICAtICdlc3BvY3JtLWRhdGE6L3Zhci93d3cvaHRtbC9kYXRhJwogICAgICAtICdlc3BvY3JtLWN1c3RvbTovdmFyL3d3dy9odG1sL2N1c3RvbScKICAgICAgLSAnZXNwb2NybS1jdXN0b20tY2xpZW50Oi92YXIvd3d3L2h0bWwvY2xpZW50L2N1c3RvbScKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtLWRiOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gYmluL2NvbW1hbmQKICAgICAgICAtIGFwcC1jaGVjawogICAgICBzdGFydF9wZXJpb2Q6IDIwcwogICAgICBpbnRlcnZhbDogNjBzCiAgICAgIHRpbWVvdXQ6IDIwcwogICAgICByZXRyaWVzOiAzCiAgZXNwb2NybS1kYWVtb246CiAgICBpbWFnZTogJ2VzcG9jcm0vZXNwb2NybToxMCcKICAgIGNvbnRhaW5lcl9uYW1lOiBlc3BvY3JtLWRhZW1vbgogICAgdm9sdW1lc19mcm9tOgogICAgICAtIGVzcG9jcm0KICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnRyeXBvaW50OiBkb2NrZXItZGFlbW9uLnNoCiAgICBkZXBlbmRzX29uOgogICAgICBlc3BvY3JtOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRAogICAgICAgIC0gYmluL2NvbW1hbmQKICAgICAgICAtIGFwcC1jaGVjawogICAgICBzdGFydF9wZXJpb2Q6IDIwcwogICAgICBpbnRlcnZhbDogMTgwcwogICAgICB0aW1lb3V0OiAyMHMKICAgICAgcmV0cmllczogMwogIGVzcG9jcm0td2Vic29ja2V0OgogICAgaW1hZ2U6ICdlc3BvY3JtL2VzcG9jcm06MTAnCiAgICBjb250YWluZXJfbmFtZTogZXNwb2NybS13ZWJzb2NrZXQKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9FU1BPQ1JNX1dFQlNPQ0tFVF84MDgwCiAgICAgIC0gRVNQT0NSTV9DT05GSUdfVVNFX1dFQl9TT0NLRVQ9dHJ1ZQogICAgICAtIEVTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfVVJMPSRTRVJWSUNFX0ZRRE5fRVNQT0NSTV9XRUJTT0NLRVQKICAgICAgLSAnRVNQT0NSTV9DT05GSUdfV0VCX1NPQ0tFVF9aRVJPX01fUV9TVUJTQ1JJQkVSX0RTTj10Y3A6Ly8qOjc3NzcnCiAgICAgIC0gJ0VTUE9DUk1fQ09ORklHX1dFQl9TT0NLRVRfWkVST19NX1FfU1VCTUlTU0lPTl9EU049dGNwOi8vZXNwb2NybS13ZWJzb2NrZXQ6Nzc3NycKICAgIHZvbHVtZXNfZnJvbToKICAgICAgLSBlc3BvY3JtCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgZW50cnlwb2ludDogZG9ja2VyLXdlYnNvY2tldC5zaAogICAgZGVwZW5kc19vbjoKICAgICAgZXNwb2NybToKICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfaGVhbHRoeQogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGJpbi9jb21tYW5kCiAgICAgICAgLSBhcHAtY2hlY2sKICAgICAgc3RhcnRfcGVyaW9kOiAyMHMKICAgICAgaW50ZXJ2YWw6IDE4MHMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDMKICBlc3BvY3JtLWRiOgogICAgaW1hZ2U6ICdtYXJpYWRiOjEyLjMnCiAgICBjb250YWluZXJfbmFtZTogZXNwb2NybS1kYgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gJ01BUklBREJfREFUQUJBU0U9JHtNQVJJQURCX0RBVEFCQVNFOi1lc3BvY3JtfScKICAgICAgLSAnTUFSSUFEQl9VU0VSPSR7U0VSVklDRV9VU0VSX01BUklBREJ9JwogICAgICAtICdNQVJJQURCX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9NQVJJQURCfScKICAgICAgLSAnTUFSSUFEQl9ST09UX1BBU1NXT1JEPSR7U0VSVklDRV9QQVNTV09SRF9ST09UfScKICAgIHZvbHVtZXM6CiAgICAgIC0gJ2VzcG9jcm0tZGI6L3Zhci9saWIvbXlzcWwnCiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZAogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGhlYWx0aGNoZWNrLnNoCiAgICAgICAgLSAnLS1jb25uZWN0JwogICAgICAgIC0gJy0taW5ub2RiX2luaXRpYWxpemVkJwogICAgICBpbnRlcnZhbDogMjBzCiAgICAgIHN0YXJ0X3BlcmlvZDogMTBzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAzCg==",
|
||||
"tags": [
|
||||
"crm",
|
||||
"self-hosted",
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/espocrm.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-06T11:35:16-05:00",
|
||||
"template_last_updated_at": "2026-07-03T15:15:43+03:00",
|
||||
"port": "80"
|
||||
},
|
||||
"evolution-api": {
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
Storage::fake('ssh-keys');
|
||||
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']);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->bearerToken = $this->user->createToken('build-secrets-api-test', ['*'])->plainTextToken;
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->destination = StandaloneDocker::where('server_id', $this->server->id)->first();
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
$this->application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
});
|
||||
|
||||
function buildSecretsApiHeaders(string $bearerToken): array
|
||||
{
|
||||
return [
|
||||
'Authorization' => 'Bearer '.$bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
}
|
||||
|
||||
function buildSecretsGithubPrivateKey(): string
|
||||
{
|
||||
$key = openssl_pkey_new([
|
||||
'private_key_bits' => 2048,
|
||||
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||||
]);
|
||||
|
||||
openssl_pkey_export($key, $privateKey);
|
||||
|
||||
return $privateKey;
|
||||
}
|
||||
|
||||
describe('PATCH /api/v1/applications/{uuid} use_build_secrets', function () {
|
||||
test('updates the application setting', function () {
|
||||
expect($this->application->settings->use_build_secrets)->toBeFalse();
|
||||
|
||||
$this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'use_build_secrets' => true,
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect($this->application->fresh()->settings->use_build_secrets)->toBeTrue();
|
||||
|
||||
$this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'use_build_secrets' => false,
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect($this->application->fresh()->settings->use_build_secrets)->toBeFalse();
|
||||
});
|
||||
|
||||
test('rejects non boolean values', function () {
|
||||
$this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'use_build_secrets' => 'not-a-boolean',
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('use_build_secrets');
|
||||
});
|
||||
|
||||
test('does not change the setting when omitted', function () {
|
||||
$this->application->settings->update(['use_build_secrets' => true]);
|
||||
|
||||
$this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'name' => 'updated-name',
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect($this->application->fresh()->settings->use_build_secrets)->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /api/v1/applications/public use_build_secrets', function () {
|
||||
test('creates an application with the requested build secrets setting', function (bool $useBuildSecrets) {
|
||||
$response = $this->withHeaders(buildSecretsApiHeaders($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/build-secrets-test',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => '3000',
|
||||
'use_build_secrets' => $useBuildSecrets,
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
|
||||
expect($application->settings->use_build_secrets)->toBe($useBuildSecrets);
|
||||
})->with([
|
||||
'enabled' => true,
|
||||
'disabled' => false,
|
||||
]);
|
||||
|
||||
test('rejects non boolean values', function () {
|
||||
$this->withHeaders(buildSecretsApiHeaders($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/build-secrets-test',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => '3000',
|
||||
'use_build_secrets' => 'not-a-boolean',
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('use_build_secrets');
|
||||
});
|
||||
});
|
||||
|
||||
describe('other application creation endpoints use_build_secrets', function () {
|
||||
test('creates a Dockerfile application with build secrets enabled', function () {
|
||||
$response = $this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/dockerfile', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'dockerfile' => base64_encode("FROM nginx:alpine\nEXPOSE 80"),
|
||||
'use_build_secrets' => true,
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
|
||||
expect($application->settings->use_build_secrets)->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a Docker image application with build secrets enabled', function () {
|
||||
$response = $this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/dockerimage', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'docker_registry_image_name' => 'nginx',
|
||||
'docker_registry_image_tag' => 'alpine',
|
||||
'ports_exposes' => '80',
|
||||
'use_build_secrets' => true,
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
|
||||
expect($application->settings->use_build_secrets)->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a private deploy key application with build secrets enabled', function () {
|
||||
$privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
|
||||
|
||||
$response = $this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/private-deploy-key', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'private_key_uuid' => $privateKey->uuid,
|
||||
'git_repository' => 'git@gitlab.com:coolify/build-secrets-test.git',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => '3000',
|
||||
'use_build_secrets' => true,
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
|
||||
expect($application->settings->use_build_secrets)->toBeTrue();
|
||||
});
|
||||
|
||||
test('creates a private GitHub App application with build secrets enabled', function () {
|
||||
$privateKey = PrivateKey::create([
|
||||
'name' => 'GitHub App Key',
|
||||
'private_key' => buildSecretsGithubPrivateKey(),
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
$githubApp = GithubApp::create([
|
||||
'name' => 'Build Secrets GitHub App',
|
||||
'api_url' => 'https://api.github.com',
|
||||
'html_url' => 'https://github.com',
|
||||
'app_id' => 12345,
|
||||
'installation_id' => 67890,
|
||||
'client_id' => 'build-secrets-client-id',
|
||||
'client_secret' => 'build-secrets-client-secret',
|
||||
'webhook_secret' => 'build-secrets-webhook-secret',
|
||||
'private_key_id' => $privateKey->id,
|
||||
'team_id' => $this->team->id,
|
||||
'is_system_wide' => false,
|
||||
'is_public' => false,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://api.github.com/zen' => Http::response('Keep it logically awesome.', 200, [
|
||||
'Date' => now()->toRfc7231String(),
|
||||
]),
|
||||
'https://api.github.com/app/installations/67890/access_tokens' => Http::response([
|
||||
'token' => 'github-installation-token',
|
||||
], 201),
|
||||
'https://api.github.com/repos/coolify/build-secrets-test' => Http::response([
|
||||
'id' => 123456,
|
||||
]),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(buildSecretsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/private-github-app', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'github_app_uuid' => $githubApp->uuid,
|
||||
'git_repository' => 'coolify/build-secrets-test',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => '3000',
|
||||
'use_build_secrets' => true,
|
||||
'autogenerate_domain' => false,
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
|
||||
|
||||
expect($application->settings->use_build_secrets)->toBeTrue();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,183 @@
|
||||
<?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;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
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']);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->bearerToken = $this->user->createToken('application-settings-api-test', ['*'])->plainTextToken;
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->destination = StandaloneDocker::where('server_id', $this->server->id)->first();
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
$this->application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
});
|
||||
|
||||
function applicationSettingsApiHeaders(string $bearerToken): array
|
||||
{
|
||||
return [
|
||||
'Authorization' => 'Bearer '.$bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
}
|
||||
|
||||
function recommendedApplicationSettingsPayload(): array
|
||||
{
|
||||
return [
|
||||
'is_git_submodules_enabled' => false,
|
||||
'is_git_lfs_enabled' => false,
|
||||
'is_git_shallow_clone_enabled' => false,
|
||||
'disable_build_cache' => true,
|
||||
'inject_build_args_to_dockerfile' => false,
|
||||
'include_source_commit_in_build' => true,
|
||||
'is_env_sorting_enabled' => true,
|
||||
'is_pr_deployments_public_enabled' => true,
|
||||
'stop_grace_period' => 45,
|
||||
'docker_images_to_keep' => 7,
|
||||
'is_gzip_enabled' => false,
|
||||
'is_stripprefix_enabled' => false,
|
||||
'is_raw_compose_deployment_enabled' => true,
|
||||
];
|
||||
}
|
||||
|
||||
test('GET /api/v1/applications/{uuid} includes settings without internal metadata', function () {
|
||||
$this->application->settings->update(recommendedApplicationSettingsPayload());
|
||||
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$this->application->uuid}")
|
||||
->assertOk()
|
||||
->assertJsonPath('settings.disable_build_cache', true)
|
||||
->assertJsonPath('settings.stop_grace_period', 45)
|
||||
->assertJsonMissingPath('settings.id')
|
||||
->assertJsonMissingPath('settings.application_id')
|
||||
->assertJsonMissingPath('settings.created_at')
|
||||
->assertJsonMissingPath('settings.updated_at');
|
||||
});
|
||||
|
||||
test('PATCH /api/v1/applications/{uuid} updates application settings', function () {
|
||||
$this->application->update(['build_pack' => 'dockercompose']);
|
||||
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", recommendedApplicationSettingsPayload())
|
||||
->assertOk();
|
||||
|
||||
$settings = $this->application->fresh()->settings;
|
||||
|
||||
foreach (recommendedApplicationSettingsPayload() as $field => $value) {
|
||||
expect($settings->{$field})->toBe($value);
|
||||
}
|
||||
});
|
||||
|
||||
test('application creation accepts application settings', function () {
|
||||
Queue::fake();
|
||||
|
||||
$response = $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->postJson('/api/v1/applications/public', array_merge([
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_uuid' => $this->environment->uuid,
|
||||
'server_uuid' => $this->server->uuid,
|
||||
'git_repository' => 'https://gitlab.com/coolify/application-settings-test',
|
||||
'git_branch' => 'main',
|
||||
'build_pack' => 'dockercompose',
|
||||
'autogenerate_domain' => false,
|
||||
], recommendedApplicationSettingsPayload()))
|
||||
->assertCreated();
|
||||
|
||||
$settings = Application::where('uuid', $response->json('uuid'))->firstOrFail()->settings;
|
||||
|
||||
foreach (recommendedApplicationSettingsPayload() as $field => $value) {
|
||||
expect($settings->{$field})->toBe($value);
|
||||
}
|
||||
});
|
||||
|
||||
test('proxy settings regenerate managed labels', function () {
|
||||
$this->application->settings->update([
|
||||
'is_container_label_readonly_enabled' => true,
|
||||
'is_gzip_enabled' => true,
|
||||
'is_stripprefix_enabled' => true,
|
||||
]);
|
||||
$this->application->update(['custom_labels' => base64_encode('sentinel-label=true')]);
|
||||
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'is_gzip_enabled' => false,
|
||||
'is_stripprefix_enabled' => false,
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect(base64_decode($this->application->fresh()->custom_labels))->not->toContain('sentinel-label=true');
|
||||
});
|
||||
|
||||
test('rejects invalid boolean application settings', function () {
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'disable_build_cache' => 'not-a-boolean',
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('disable_build_cache');
|
||||
});
|
||||
|
||||
test('validates stop grace period bounds', function (int $stopGracePeriod) {
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'stop_grace_period' => $stopGracePeriod,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('stop_grace_period');
|
||||
})->with([
|
||||
'below minimum' => 0,
|
||||
'above maximum' => 3601,
|
||||
]);
|
||||
|
||||
test('validates Docker image retention bounds', function (int $dockerImagesToKeep) {
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'docker_images_to_keep' => $dockerImagesToKeep,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('docker_images_to_keep');
|
||||
})->with([
|
||||
'below minimum' => -1,
|
||||
'above maximum' => 101,
|
||||
]);
|
||||
|
||||
test('stop grace period can be reset to null', function () {
|
||||
$this->application->settings->update(['stop_grace_period' => 45]);
|
||||
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'stop_grace_period' => null,
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect($this->application->fresh()->settings->stop_grace_period)->toBeNull();
|
||||
});
|
||||
|
||||
test('raw compose deployment can only be enabled for Docker Compose applications', function () {
|
||||
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$this->application->uuid}", [
|
||||
'is_raw_compose_deployment_enabled' => true,
|
||||
])
|
||||
->assertUnprocessable()
|
||||
->assertJsonValidationErrors('is_raw_compose_deployment_enabled');
|
||||
});
|
||||
@@ -3,7 +3,11 @@
|
||||
use App\Livewire\Project\Application\General;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
@@ -81,9 +85,26 @@ describe('Application noindex domains', function () {
|
||||
});
|
||||
|
||||
test('the Livewire toggle persists the flag', function () {
|
||||
InstanceSettings::unguarded(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0], []);
|
||||
});
|
||||
$privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
|
||||
$server = Server::factory()->create([
|
||||
'team_id' => $this->team->id,
|
||||
'private_key_id' => $privateKey->id,
|
||||
]);
|
||||
$destination = StandaloneDocker::where('server_id', $server->id)->first()
|
||||
?? StandaloneDocker::factory()->create(['server_id' => $server->id]);
|
||||
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => StandaloneDocker::class,
|
||||
'fqdn' => 'https://prod.example.com,https://staging.example.com',
|
||||
'static_image' => 'nginx:alpine',
|
||||
'base_directory' => '/',
|
||||
'is_http_basic_auth_enabled' => false,
|
||||
'redirect' => 'no',
|
||||
]);
|
||||
|
||||
Livewire::test(General::class, ['application' => $application])
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Features\SupportLockedProperties\CannotUpdateLockedPropertyException;
|
||||
use Livewire\Livewire;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
@@ -117,6 +118,48 @@ test('changeSource rejects an arbitrary class as source_type', function () {
|
||||
expect($this->applicationA->source_type)->not->toBe(Server::class);
|
||||
});
|
||||
|
||||
test('changeSource dispatches configuration changed for an owned source', function () {
|
||||
Http::fake([
|
||||
'https://api.github.com/repos/*' => Http::response(['id' => 123]),
|
||||
]);
|
||||
$source = GithubApp::create([
|
||||
'name' => 'own-github-app',
|
||||
'team_id' => $this->teamA->id,
|
||||
'api_url' => 'https://api.github.com',
|
||||
'html_url' => 'https://github.com',
|
||||
'is_public' => true,
|
||||
]);
|
||||
$this->applicationA->update(['git_repository' => 'coollabsio/coolify']);
|
||||
|
||||
Livewire::test(Source::class, ['application' => $this->applicationA->fresh()])
|
||||
->call('changeSource', $source->id, GithubApp::class)
|
||||
->assertDispatched('configurationChanged');
|
||||
});
|
||||
|
||||
test('changeSource dispatches configuration changed when repository metadata lookup fails after persistence', function () {
|
||||
Http::fake([
|
||||
'https://api.github.com/repos/*' => Http::response(
|
||||
['message' => 'Unavailable'],
|
||||
503,
|
||||
['X-RateLimit-Reset' => now()->addMinute()->timestamp],
|
||||
),
|
||||
]);
|
||||
$source = GithubApp::create([
|
||||
'name' => 'own-unavailable-github-app',
|
||||
'team_id' => $this->teamA->id,
|
||||
'api_url' => 'https://api.github.com',
|
||||
'html_url' => 'https://github.com',
|
||||
'is_public' => true,
|
||||
]);
|
||||
$this->applicationA->update(['git_repository' => 'coollabsio/coolify']);
|
||||
|
||||
Livewire::test(Source::class, ['application' => $this->applicationA->fresh()])
|
||||
->call('changeSource', $source->id, GithubApp::class)
|
||||
->assertDispatched('configurationChanged');
|
||||
|
||||
expect($this->applicationA->refresh()->source_id)->toBe($source->id);
|
||||
});
|
||||
|
||||
test('privateKeyId is locked so submit() cannot persist a client-supplied foreign id', function () {
|
||||
// Without #[Locked], an attacker could POST {"updates": {"privateKeyId": <foreign_id>},
|
||||
// "calls": [{"method": "submit"}]} and have syncData(true) write the foreign id through
|
||||
|
||||
@@ -16,6 +16,8 @@ uses(RefreshDatabase::class);
|
||||
function createApplicationForAdvancedStopGracePeriodTest(): Application
|
||||
{
|
||||
$team = Team::factory()->create();
|
||||
$team->members()->attach(auth()->id(), ['role' => 'owner']);
|
||||
session(['currentTeam' => $team]);
|
||||
$server = Server::factory()->create(['team_id' => $team->id]);
|
||||
$project = Project::factory()->create(['team_id' => $team->id]);
|
||||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||||
@@ -43,7 +45,8 @@ it('saves a valid stop grace period', function () {
|
||||
->set('stopGracePeriod', '300')
|
||||
->call('saveStopGracePeriod')
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('success');
|
||||
->assertDispatched('success')
|
||||
->assertDispatched('configurationChanged');
|
||||
|
||||
expect($application->settings()->first()->stop_grace_period)->toBe(300);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Shared\EnvironmentVariable\All as EnvironmentVariableAll;
|
||||
use App\Livewire\Project\Shared\HealthChecks;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::unguarded(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0], []);
|
||||
});
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
$this->actingAs($this->user);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||||
$this->application = Application::factory()->create(['environment_id' => $environment->id])->refresh();
|
||||
});
|
||||
|
||||
it('refreshes configuration changes after health check saves', function (string $method) {
|
||||
Livewire::test(HealthChecks::class, ['resource' => $this->application])
|
||||
->call($method)
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('configurationChanged');
|
||||
})->with(['instantSave', 'submit', 'toggleHealthcheck']);
|
||||
|
||||
it('refreshes configuration changes after environment variable settings are saved', function () {
|
||||
Livewire::test(EnvironmentVariableAll::class, ['resource' => $this->application])
|
||||
->set('use_build_secrets', true)
|
||||
->call('instantSave')
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('configurationChanged');
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Project\Application\Swarm;
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('dispatches configuration changed when Swarm settings are saved', function (string $method) {
|
||||
$team = Team::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
$team->members()->attach($user->id, ['role' => 'owner']);
|
||||
$server = Server::factory()->create(['team_id' => $team->id]);
|
||||
$project = Project::factory()->create(['team_id' => $team->id]);
|
||||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $environment->id,
|
||||
'destination_id' => $server->standaloneDockers()->firstOrFail()->id,
|
||||
'destination_type' => $server->standaloneDockers()->firstOrFail()->getMorphClass(),
|
||||
'swarm_replicas' => 1,
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
Livewire::test(Swarm::class, ['application' => $application])
|
||||
->set('isSwarmOnlyWorkerNodes', false)
|
||||
->call($method)
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('configurationChanged');
|
||||
})->with(['instantSave', 'submit']);
|
||||
@@ -118,4 +118,24 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
->assertSee('nixpacks.toml')
|
||||
->assertDontSee('railpack.json');
|
||||
});
|
||||
|
||||
test('saving general settings refreshes configuration changes when label reset is disabled', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => StandaloneDocker::class,
|
||||
'build_pack' => 'nixpacks',
|
||||
'static_image' => 'nginx:alpine',
|
||||
'base_directory' => '/',
|
||||
'is_http_basic_auth_enabled' => false,
|
||||
'redirect' => 'no',
|
||||
]);
|
||||
$application->settings->update(['is_container_label_readonly_enabled' => false]);
|
||||
|
||||
Livewire::test(General::class, ['application' => $application->refresh()])
|
||||
->set('isPreserveRepositoryEnabled', true)
|
||||
->call('instantSave')
|
||||
->assertHasNoErrors()
|
||||
->assertDispatched('configurationChanged');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Environment;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Project;
|
||||
use App\Models\Team;
|
||||
use App\Services\DeploymentConfiguration\ConfigurationDiffer;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
@@ -78,6 +79,192 @@ it('detects redeploy-only domain changes', function () {
|
||||
->and($change['new_full_value'])->toBe($domains);
|
||||
});
|
||||
|
||||
it('detects Docker image reference changes as redeploy-only changes', function (string $field, string $label, string $newValue) {
|
||||
$application = snapshotTestApplication([
|
||||
'build_pack' => 'dockerimage',
|
||||
'docker_registry_image_name' => 'ghcr.io/coollabsio/shoutrrr',
|
||||
'docker_registry_image_tag' => '1.3.0-rc.4',
|
||||
]);
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->update([$field => $newValue]);
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
$change = collect($diff->changes())->firstWhere('label', $label);
|
||||
|
||||
expect($diff->isChanged())->toBeTrue()
|
||||
->and($diff->requiresBuild())->toBeFalse()
|
||||
->and($change)->not->toBeNull()
|
||||
->and($change['old_display_value'])->not->toBe($change['new_display_value'])
|
||||
->and($change['new_display_value'])->toBe($newValue);
|
||||
})->with([
|
||||
'image name' => ['docker_registry_image_name', 'Docker image', 'ghcr.io/coollabsio/coolify'],
|
||||
'image tag' => ['docker_registry_image_tag', 'Docker image tag or hash', '1.3.0-rc.5'],
|
||||
]);
|
||||
|
||||
it('detects deployment hook changes as redeploy-only changes', function (string $field, string $label, string $newValue) {
|
||||
$application = snapshotTestApplication();
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->update([$field => $newValue]);
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
|
||||
expect($diff->requiresBuild())->toBeFalse()
|
||||
->and(collect($diff->changes())->pluck('label'))->toContain($label);
|
||||
})->with([
|
||||
'pre-deployment command' => ['pre_deployment_command', 'Pre-deployment command', 'php artisan migrate --force'],
|
||||
'pre-deployment container' => ['pre_deployment_command_container', 'Pre-deployment command container', 'web'],
|
||||
'post-deployment command' => ['post_deployment_command', 'Post-deployment command', 'php artisan cache:clear'],
|
||||
'post-deployment container' => ['post_deployment_command_container', 'Post-deployment command container', 'worker'],
|
||||
]);
|
||||
|
||||
it('detects source integration changes as build changes', function (string $field, string $label, mixed $newValue) {
|
||||
$application = snapshotTestApplication([
|
||||
'source_id' => 1,
|
||||
'source_type' => 'App\\Models\\GithubApp',
|
||||
]);
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->forceFill([$field => $newValue])->save();
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
|
||||
expect($diff->requiresBuild())->toBeTrue()
|
||||
->and(collect($diff->changes())->pluck('label'))->toContain($label);
|
||||
})->with([
|
||||
'source ID' => ['source_id', 'Source ID', 2],
|
||||
'source type' => ['source_type', 'Source type', 'App\\Models\\GitlabApp'],
|
||||
]);
|
||||
|
||||
it('detects build-affecting application settings', function (string $field, string $label) {
|
||||
$application = snapshotTestApplication();
|
||||
$application->settings->update([$field => false]);
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->settings->update([$field => true]);
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
|
||||
expect($diff->requiresBuild())->toBeTrue()
|
||||
->and(collect($diff->changes())->pluck('label'))->toContain($label);
|
||||
})->with([
|
||||
'static site' => ['is_static', 'Static site'],
|
||||
'single-page application' => ['is_spa', 'Single-page application'],
|
||||
'Git submodules' => ['is_git_submodules_enabled', 'Git submodules'],
|
||||
'Git LFS' => ['is_git_lfs_enabled', 'Git LFS'],
|
||||
'shallow clone' => ['is_git_shallow_clone_enabled', 'Shallow clone'],
|
||||
'environment variable sorting' => ['is_env_sorting_enabled', 'Sort environment variables'],
|
||||
]);
|
||||
|
||||
it('detects runtime-affecting application settings as redeploy-only changes', function (string $field, string $label, mixed $newValue) {
|
||||
$application = snapshotTestApplication();
|
||||
$application->settings->update([$field => is_bool($newValue) ? ! $newValue : null]);
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->settings->update([$field => $newValue]);
|
||||
$diff = $application->refresh()->pendingDeploymentConfigurationDiff();
|
||||
|
||||
expect($diff->requiresBuild())->toBeFalse()
|
||||
->and(collect($diff->changes())->pluck('label'))->toContain($label);
|
||||
})->with([
|
||||
'consistent container name' => ['is_consistent_container_name_enabled', 'Consistent container name', true],
|
||||
'container label escaping' => ['is_container_label_escape_enabled', 'Escape container labels', false],
|
||||
'container labels read-only' => ['is_container_label_readonly_enabled', 'Read-only container labels', false],
|
||||
'log drain' => ['is_log_drain_enabled', 'Log drain', true],
|
||||
'Swarm worker nodes' => ['is_swarm_only_worker_nodes', 'Swarm worker nodes only', true],
|
||||
'stop grace period' => ['stop_grace_period', 'Stop grace period', 45],
|
||||
'preserve repository' => ['is_preserve_repository_enabled', 'Preserve repository', true],
|
||||
]);
|
||||
|
||||
it('classifies runtime options as redeploy-only and custom nginx configuration as build-affecting', function () {
|
||||
$application = snapshotTestApplication();
|
||||
markSnapshotTestApplicationDeployed($application);
|
||||
|
||||
$application->update(['custom_docker_run_options' => '--init']);
|
||||
|
||||
expect($application->refresh()->pendingDeploymentConfigurationDiff()->requiresBuild())->toBeFalse();
|
||||
|
||||
markSnapshotTestApplicationDeployed($application->refresh());
|
||||
$application->update(['custom_nginx_configuration' => 'server { listen 80; }']);
|
||||
|
||||
expect($application->refresh()->pendingDeploymentConfigurationDiff()->requiresBuild())->toBeTrue();
|
||||
});
|
||||
|
||||
it('keeps Docker run options compatible with older build-section snapshots', function () {
|
||||
$application = snapshotTestApplication(['custom_docker_run_options' => '--init'])->refresh();
|
||||
$previousSnapshot = $application->deploymentConfigurationSnapshot();
|
||||
$buildItems = collect(data_get($previousSnapshot, 'sections.build.items'));
|
||||
|
||||
expect($buildItems->pluck('key'))->toContain('custom_docker_run_options');
|
||||
|
||||
data_set(
|
||||
$previousSnapshot,
|
||||
'sections.build.items',
|
||||
$buildItems->map(function (array $item): array {
|
||||
if ($item['key'] === 'custom_docker_run_options') {
|
||||
$item['impact'] = 'build';
|
||||
}
|
||||
|
||||
return $item;
|
||||
})->all(),
|
||||
);
|
||||
|
||||
expect(app(ConfigurationDiffer::class)->diff($previousSnapshot, $application->deploymentConfigurationSnapshot())->isChanged())->toBeFalse();
|
||||
|
||||
$application->update(['custom_docker_run_options' => '--rm']);
|
||||
|
||||
expect(app(ConfigurationDiffer::class)->diff($previousSnapshot, $application->refresh()->deploymentConfigurationSnapshot())->requiresBuild())->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not report newly tracked settings when an older snapshot omitted their default values', function () {
|
||||
$application = snapshotTestApplication();
|
||||
$application->settings->update(['stop_grace_period' => DEFAULT_STOP_GRACE_PERIOD_SECONDS]);
|
||||
$currentSnapshot = $application->deploymentConfigurationSnapshot();
|
||||
$introducedKeys = [
|
||||
'is_static',
|
||||
'is_spa',
|
||||
'is_git_submodules_enabled',
|
||||
'is_git_lfs_enabled',
|
||||
'is_git_shallow_clone_enabled',
|
||||
'is_env_sorting_enabled',
|
||||
'is_consistent_container_name_enabled',
|
||||
'is_container_label_escape_enabled',
|
||||
'is_container_label_readonly_enabled',
|
||||
'is_log_drain_enabled',
|
||||
'is_swarm_only_worker_nodes',
|
||||
'is_preserve_repository_enabled',
|
||||
'stop_grace_period',
|
||||
];
|
||||
$previousSnapshot = $currentSnapshot;
|
||||
|
||||
foreach (['build', 'runtime'] as $section) {
|
||||
data_set(
|
||||
$previousSnapshot,
|
||||
"sections.{$section}.items",
|
||||
collect(data_get($previousSnapshot, "sections.{$section}.items"))
|
||||
->reject(fn (array $item): bool => in_array($item['key'], $introducedKeys, true))
|
||||
->values()
|
||||
->all(),
|
||||
);
|
||||
}
|
||||
|
||||
expect(app(ConfigurationDiffer::class)->diff($previousSnapshot, $currentSnapshot)->isChanged())->toBeFalse();
|
||||
});
|
||||
|
||||
it('accepts the historical environment sorting default in older snapshots', function () {
|
||||
$application = snapshotTestApplication();
|
||||
$application->settings->update(['is_env_sorting_enabled' => true]);
|
||||
$currentSnapshot = $application->deploymentConfigurationSnapshot();
|
||||
$previousSnapshot = $currentSnapshot;
|
||||
data_set(
|
||||
$previousSnapshot,
|
||||
'sections.build.items',
|
||||
collect(data_get($previousSnapshot, 'sections.build.items'))
|
||||
->reject(fn (array $item): bool => $item['key'] === 'is_env_sorting_enabled')
|
||||
->values()
|
||||
->all(),
|
||||
);
|
||||
|
||||
expect(app(ConfigurationDiffer::class)->diff($previousSnapshot, $currentSnapshot)->isChanged())->toBeFalse();
|
||||
});
|
||||
|
||||
it('detects environment variable value changes without exposing secret values', function () {
|
||||
$application = snapshotTestApplication();
|
||||
EnvironmentVariable::create([
|
||||
|
||||
Reference in New Issue
Block a user