Merge branch 'main' into feat/scheduled-tasks-cron
This commit is contained in:
commit
e233ec05b5
@ -19,10 +19,12 @@ # Donations
|
|||||||
|
|
||||||
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
Special thanks to our biggest sponsors, [CCCareers](https://cccareers.org/) and [Appwrite](https://appwrite.io)!
|
||||||
|
|
||||||
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="appwrite logo" width="200"/></a>
|
<a href="https://cccareers.org/" target="_blank"><img src="./other/logos/ccc-logo.webp" alt="cccareers logo" width="200"/></a>
|
||||||
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
<a href="https://appwrite.io" target="_blank"><img src="./other/logos/appwrite.svg" alt="appwrite logo" width="200"/></a>
|
||||||
|
|
||||||
## Github Sponsors ($15+)
|
## Github Sponsors ($15+)
|
||||||
|
<a href="https://bc.direct"><img width="60px" alt="BC Direct" src="https://github.com/coollabsio/coolify/assets/5845193/a4063c41-95ed-4a32-8814-cd1475572e37"/></a>
|
||||||
|
<a href="https://github.com/automazeio"><img src="https://github.com/automazeio.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
<a href="https://github.com/corentinclichy"><img src="https://github.com/corentinclichy.png" width="60px" alt="Corentin Clichy" /></a>
|
||||||
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
<a href="https://github.com/Niki2k1"><img src="https://github.com/Niki2k1.png" width="60px" alt="Niklas Lausch" /></a>
|
||||||
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
<a href="https://github.com/pixelinfinito"><img src="https://github.com/pixelinfinito.png" width="60px" alt="Pixel Infinito" /></a>
|
||||||
@ -32,7 +34,6 @@ ## Github Sponsors ($15+)
|
|||||||
<a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
|
<a href="https://github.com/Illyism"><img src="https://github.com/Illyism.png" width="60px" alt="Ilias Ism" /></a>
|
||||||
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
|
<a href="https://github.com/urtho"><img src="https://github.com/urtho.png" width="60px" alt="Paweł Pierścionek" /></a>
|
||||||
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
|
<a href="https://github.com/monocursive"><img src="https://github.com/monocursive.png" width="60px" alt="Michael Mazurczak" /></a>
|
||||||
<a href="https://github.com/cccareers"><img src="https://github.com/cccareers.png" width="60px" alt="Creating Coding Careers" /></a>
|
|
||||||
|
|
||||||
## Organizations
|
## Organizations
|
||||||
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
<a href="https://opencollective.com/coollabsio/organization/0/website"><img src="https://opencollective.com/coollabsio/organization/0/avatar.svg"></a>
|
||||||
|
@ -21,14 +21,14 @@ public function handle(Service $service)
|
|||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$application->delete();
|
$application->forceDelete();
|
||||||
}
|
}
|
||||||
foreach ($service->databases()->get() as $database) {
|
foreach ($service->databases()->get() as $database) {
|
||||||
$storages = $database->persistentStorages()->get();
|
$storages = $database->persistentStorages()->get();
|
||||||
foreach ($storages as $storage) {
|
foreach ($storages as $storage) {
|
||||||
$storagesToDelete->push($storage);
|
$storagesToDelete->push($storage);
|
||||||
}
|
}
|
||||||
$database->delete();
|
$database->forceDelete();
|
||||||
}
|
}
|
||||||
foreach ($storagesToDelete as $storage) {
|
foreach ($storagesToDelete as $storage) {
|
||||||
$commands[] = "docker volume rm -f $storage->name";
|
$commands[] = "docker volume rm -f $storage->name";
|
||||||
|
@ -55,7 +55,8 @@ public function handle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private function restore_coolify_db_backup() {
|
private function restore_coolify_db_backup()
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
$database = StandalonePostgresql::withTrashed()->find(0);
|
$database = StandalonePostgresql::withTrashed()->find(0);
|
||||||
if ($database && $database->trashed()) {
|
if ($database && $database->trashed()) {
|
||||||
@ -73,9 +74,8 @@ private function restore_coolify_db_backup() {
|
|||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch(\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,6 +138,89 @@ private function cleanup_in_progress_application_deployments()
|
|||||||
}
|
}
|
||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($applications as $application) {
|
||||||
|
echo "Deleting stucked application: {$application->name}\n";
|
||||||
|
$application->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($postgresqls as $postgresql) {
|
||||||
|
echo "Deleting stucked postgresql: {$postgresql->name}\n";
|
||||||
|
$postgresql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked postgresql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$redis = StandaloneRedis::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($redis as $redis) {
|
||||||
|
echo "Deleting stucked redis: {$redis->name}\n";
|
||||||
|
$redis->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked redis: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mongodbs as $mongodb) {
|
||||||
|
echo "Deleting stucked mongodb: {$mongodb->name}\n";
|
||||||
|
$mongodb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked mongodb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mysqls = StandaloneMysql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mysqls as $mysql) {
|
||||||
|
echo "Deleting stucked mysql: {$mysql->name}\n";
|
||||||
|
$mysql->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked mysql: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$mariadbs = StandaloneMariadb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($mariadbs as $mariadb) {
|
||||||
|
echo "Deleting stucked mariadb: {$mariadb->name}\n";
|
||||||
|
$mariadb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked mariadb: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$services = Service::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($services as $service) {
|
||||||
|
echo "Deleting stucked service: {$service->name}\n";
|
||||||
|
$service->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked service: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceApps = ServiceApplication::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceApps as $serviceApp) {
|
||||||
|
echo "Deleting stucked serviceapp: {$serviceApp->name}\n";
|
||||||
|
$serviceApp->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$serviceDbs = ServiceDatabase::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
|
foreach ($serviceDbs as $serviceDb) {
|
||||||
|
echo "Deleting stucked serviceapp: {$serviceDb->name}\n";
|
||||||
|
$serviceDb->forceDelete();
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stucked serviceapp: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup any resources that are not attached to any environment or destination or server
|
// Cleanup any resources that are not attached to any environment or destination or server
|
||||||
try {
|
try {
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
|
@ -164,11 +164,15 @@ public function handle(): void
|
|||||||
$ips = collect([]);
|
$ips = collect([]);
|
||||||
if (count($allContainers) > 0) {
|
if (count($allContainers) > 0) {
|
||||||
$allContainers = $allContainers[0];
|
$allContainers = $allContainers[0];
|
||||||
|
$allContainers = collect($allContainers)->sort()->values();
|
||||||
foreach ($allContainers as $container) {
|
foreach ($allContainers as $container) {
|
||||||
$containerName = data_get($container, 'Name');
|
$containerName = data_get($container, 'Name');
|
||||||
if ($containerName === 'coolify-proxy') {
|
if ($containerName === 'coolify-proxy') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (preg_match('/-(\d{12})/',$containerName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$containerIp = data_get($container, 'IPv4Address');
|
$containerIp = data_get($container, 'IPv4Address');
|
||||||
if ($containerName && $containerIp) {
|
if ($containerName && $containerIp) {
|
||||||
$containerIp = str($containerIp)->before('/');
|
$containerIp = str($containerIp)->before('/');
|
||||||
@ -446,8 +450,12 @@ private function deploy_docker_compose_buildpack()
|
|||||||
$this->generate_image_names();
|
$this->generate_image_names();
|
||||||
$this->cleanup_git();
|
$this->cleanup_git();
|
||||||
$this->application->loadComposeFile(isInit: false);
|
$this->application->loadComposeFile(isInit: false);
|
||||||
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
$yaml = Yaml::dump($composeFile->toArray(), 10);
|
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||||
|
} else {
|
||||||
|
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
|
||||||
|
$yaml = Yaml::dump($composeFile->toArray(), 10);
|
||||||
|
}
|
||||||
$this->docker_compose_base64 = base64_encode($yaml);
|
$this->docker_compose_base64 = base64_encode($yaml);
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d > {$this->workdir}{$this->docker_compose_location}"), "hidden" => true
|
||||||
@ -871,11 +879,11 @@ private function generate_nixpacks_confs()
|
|||||||
private function nixpacks_build_cmd()
|
private function nixpacks_build_cmd()
|
||||||
{
|
{
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$cacheKey = $this->application->uuid;
|
// $cacheKey = $this->application->uuid;
|
||||||
if ($this->pull_request_id !== 0) {
|
// if ($this->pull_request_id !== 0) {
|
||||||
$cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}";
|
// $cacheKey = "{$this->application->uuid}-pr-{$this->pull_request_id}";
|
||||||
}
|
// }
|
||||||
$nixpacks_command = "nixpacks build --cache-key '{$cacheKey}' -o {$this->workdir} {$this->env_args} --no-error-without-start";
|
$nixpacks_command = "nixpacks build {$this->env_args} --no-error-without-start";
|
||||||
if ($this->application->build_command) {
|
if ($this->application->build_command) {
|
||||||
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
$nixpacks_command .= " --build-cmd \"{$this->application->build_command}\"";
|
||||||
}
|
}
|
||||||
@ -885,7 +893,7 @@ private function nixpacks_build_cmd()
|
|||||||
if ($this->application->install_command) {
|
if ($this->application->install_command) {
|
||||||
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
$nixpacks_command .= " --install-cmd \"{$this->application->install_command}\"";
|
||||||
}
|
}
|
||||||
$nixpacks_command .= " {$this->workdir}";
|
$nixpacks_command .= " -o {$this->workdir} {$this->workdir}";
|
||||||
return $nixpacks_command;
|
return $nixpacks_command;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1129,7 +1137,8 @@ private function generate_environment_variables($ports)
|
|||||||
// Add PORT if not exists, use the first port as default
|
// Add PORT if not exists, use the first port as default
|
||||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PORT'))->isEmpty()) {
|
||||||
$environment_variables->push("PORT={$ports[0]}");
|
$environment_variables->push("PORT={$ports[0]}");
|
||||||
} if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) {
|
}
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('SOURCE_COMMIT'))->isEmpty()) {
|
||||||
if (!is_null($this->commit)) {
|
if (!is_null($this->commit)) {
|
||||||
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
$environment_variables->push("SOURCE_COMMIT={$this->commit}");
|
||||||
}
|
}
|
||||||
@ -1192,6 +1201,9 @@ private function build_image()
|
|||||||
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
|
if ($this->application->settings->is_static || $this->application->build_pack === 'static') {
|
||||||
if ($this->application->static_image) {
|
if ($this->application->static_image) {
|
||||||
$this->pull_latest_image($this->application->static_image);
|
$this->pull_latest_image($this->application->static_image);
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Continue with the building process.'"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if ($this->application->build_pack === 'static') {
|
if ($this->application->build_pack === 'static') {
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
@ -1218,9 +1230,23 @@ private function build_image()
|
|||||||
}
|
}
|
||||||
}");
|
}");
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
if ($this->application->build_pack === 'nixpacks') {
|
||||||
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
$this->execute_remote_command(
|
||||||
]);
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($this->force_rebuild) {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
$dockerfile = base64_encode("FROM {$this->application->static_image}
|
||||||
WORKDIR /usr/share/nginx/html/
|
WORKDIR /usr/share/nginx/html/
|
||||||
@ -1263,9 +1289,23 @@ private function build_image()
|
|||||||
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
executeInDocker($this->deployment_uuid, "docker build --pull $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
if ($this->application->build_pack === 'nixpacks') {
|
||||||
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
$this->execute_remote_command(
|
||||||
]);
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "cp {$this->workdir}/Dockerfile {$this->workdir}/.nixpacks/Dockerfile")
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->force_rebuild) {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
executeInDocker($this->deployment_uuid, "docker build --no-cache $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command([
|
||||||
|
executeInDocker($this->deployment_uuid, "docker build $this->buildTarget $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
@ -1291,6 +1331,9 @@ private function stop_running_container(bool $force = false)
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
$this->application_deployment_queue->addLogEntry("New container is not healthy, rolling back to the old container.");
|
||||||
|
$this->application_deployment_queue->update([
|
||||||
|
'status' => ApplicationDeploymentStatus::FAILED->value,
|
||||||
|
]);
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
);
|
);
|
||||||
|
@ -66,6 +66,7 @@ class General extends Component
|
|||||||
'application.settings.is_static' => 'boolean|required',
|
'application.settings.is_static' => 'boolean|required',
|
||||||
'application.docker_compose_custom_start_command' => 'nullable',
|
'application.docker_compose_custom_start_command' => 'nullable',
|
||||||
'application.docker_compose_custom_build_command' => 'nullable',
|
'application.docker_compose_custom_build_command' => 'nullable',
|
||||||
|
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'application.name' => 'name',
|
'application.name' => 'name',
|
||||||
@ -98,6 +99,7 @@ class General extends Component
|
|||||||
'application.settings.is_static' => 'Is static',
|
'application.settings.is_static' => 'Is static',
|
||||||
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
||||||
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||||
|
'application.settings.is_raw_compose_deployment_enabled' => 'Is raw compose deployment enabled',
|
||||||
];
|
];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
@ -114,6 +116,11 @@ public function mount()
|
|||||||
}
|
}
|
||||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||||
$this->customLabels = $this->application->parseContainerLabels();
|
$this->customLabels = $this->application->parseContainerLabels();
|
||||||
|
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
|
||||||
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
|
$this->application->save();
|
||||||
|
}
|
||||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||||
$this->checkLabelUpdates();
|
$this->checkLabelUpdates();
|
||||||
}
|
}
|
||||||
@ -199,7 +206,12 @@ public function updatedApplicationFqdn()
|
|||||||
public function submit($showToaster = true)
|
public function submit($showToaster = true)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ray($this->initialDockerComposeLocation, $this->application->docker_compose_location);
|
if (!$this->customLabels && $this->application->destination->server->proxyType() === 'TRAEFIK_V2') {
|
||||||
|
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||||
|
$this->application->custom_labels = base64_encode($this->customLabels);
|
||||||
|
$this->application->save();
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
if ($this->application->build_pack === 'dockercompose' && $this->initialDockerComposeLocation !== $this->application->docker_compose_location) {
|
||||||
$this->loadComposeFile();
|
$this->loadComposeFile();
|
||||||
}
|
}
|
||||||
@ -207,6 +219,7 @@ public function submit($showToaster = true)
|
|||||||
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
if ($this->ports_exposes !== $this->application->ports_exposes) {
|
||||||
$this->resetDefaultLabels(false);
|
$this->resetDefaultLabels(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'application.docker_registry_image_name' => 'required',
|
'application.docker_registry_image_name' => 'required',
|
||||||
|
@ -430,7 +430,7 @@ public function isLogDrainEnabled()
|
|||||||
public function isConfigurationChanged($save = false)
|
public function isConfigurationChanged($save = false)
|
||||||
{
|
{
|
||||||
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
$newConfigHash = $this->fqdn . $this->git_repository . $this->git_branch . $this->git_commit_sha . $this->build_pack . $this->static_image . $this->install_command . $this->build_command . $this->start_command . $this->port_exposes . $this->port_mappings . $this->base_directory . $this->publish_directory . $this->health_check_path . $this->health_check_port . $this->health_check_host . $this->health_check_method . $this->health_check_return_code . $this->health_check_scheme . $this->health_check_response_text . $this->health_check_interval . $this->health_check_timeout . $this->health_check_retries . $this->health_check_start_period . $this->health_check_enabled . $this->limits_memory . $this->limits_swap . $this->limits_swappiness . $this->limits_reservation . $this->limits_cpus . $this->limits_cpuset . $this->limits_cpu_shares . $this->dockerfile . $this->dockerfile_location . $this->custom_labels;
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0 || $this->pull_request_id === null) {
|
||||||
$newConfigHash .= json_encode($this->environment_variables->all());
|
$newConfigHash .= json_encode($this->environment_variables->all());
|
||||||
} else {
|
} else {
|
||||||
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||||
|
@ -41,11 +41,11 @@ function queue_application_deployment(int $application_id, string $deployment_uu
|
|||||||
dispatch(new ApplicationDeploymentNewJob(
|
dispatch(new ApplicationDeploymentNewJob(
|
||||||
deployment: $deployment,
|
deployment: $deployment,
|
||||||
application: Application::find($application_id)
|
application: Application::find($application_id)
|
||||||
))->onConnection('long-running')->onQueue('long-running');
|
));
|
||||||
} else {
|
} else {
|
||||||
dispatch(new ApplicationDeploymentJob(
|
dispatch(new ApplicationDeploymentJob(
|
||||||
application_deployment_queue_id: $deployment->id,
|
application_deployment_queue_id: $deployment->id,
|
||||||
))->onConnection('long-running')->onQueue('long-running');
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,11 +57,11 @@ function queue_next_deployment(Application $application, bool $isNew = false)
|
|||||||
dispatch(new ApplicationDeploymentNewJob(
|
dispatch(new ApplicationDeploymentNewJob(
|
||||||
deployment: $next_found,
|
deployment: $next_found,
|
||||||
application: $application
|
application: $application
|
||||||
))->onConnection('long-running')->onQueue('long-running');
|
));
|
||||||
} else {
|
} else {
|
||||||
dispatch(new ApplicationDeploymentJob(
|
dispatch(new ApplicationDeploymentJob(
|
||||||
application_deployment_queue_id: $next_found->id,
|
application_deployment_queue_id: $next_found->id,
|
||||||
))->onConnection('long-running')->onQueue('long-running');
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1035,7 +1035,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
if (!data_get($service, 'restart')) {
|
||||||
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
|
}
|
||||||
data_set($service, 'container_name', $containerName);
|
data_set($service, 'container_name', $containerName);
|
||||||
data_forget($service, 'volumes.*.content');
|
data_forget($service, 'volumes.*.content');
|
||||||
data_forget($service, 'volumes.*.isDirectory');
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
@ -1399,6 +1401,11 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
$key = $value;
|
$key = $value;
|
||||||
$defaultValue = null;
|
$defaultValue = null;
|
||||||
}
|
}
|
||||||
|
$foundEnv = EnvironmentVariable::where([
|
||||||
|
'key' => $key,
|
||||||
|
'application_id' => $resource->id,
|
||||||
|
'is_preview' => false,
|
||||||
|
])->first();
|
||||||
if ($foundEnv) {
|
if ($foundEnv) {
|
||||||
$defaultValue = data_get($foundEnv, 'value');
|
$defaultValue = data_get($foundEnv, 'value');
|
||||||
}
|
}
|
||||||
@ -1468,7 +1475,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
data_set($service, 'labels', $serviceLabels->toArray());
|
data_set($service, 'labels', $serviceLabels->toArray());
|
||||||
data_forget($service, 'is_database');
|
data_forget($service, 'is_database');
|
||||||
data_set($service, 'restart', RESTART_MODE);
|
if (!data_get($service, 'restart')) {
|
||||||
|
data_set($service, 'restart', RESTART_MODE);
|
||||||
|
}
|
||||||
data_set($service, 'container_name', $containerName);
|
data_set($service, 'container_name', $containerName);
|
||||||
data_forget($service, 'volumes.*.content');
|
data_forget($service, 'volumes.*.content');
|
||||||
data_forget($service, 'volumes.*.isDirectory');
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
|
@ -184,19 +184,8 @@
|
|||||||
'connection' => 'redis',
|
'connection' => 'redis',
|
||||||
'queue' => ['default'],
|
'queue' => ['default'],
|
||||||
'balance' => 'auto',
|
'balance' => 'auto',
|
||||||
'autoScalingStrategy' => 'time',
|
// 'autoScalingStrategy' => 'time',
|
||||||
'maxProcesses' => 1,
|
// 'maxProcesses' => 1,
|
||||||
'maxTime' => 0,
|
|
||||||
'maxJobs' => 0,
|
|
||||||
'memory' => 128,
|
|
||||||
'tries' => 1,
|
|
||||||
'timeout' => 300,
|
|
||||||
'nice' => 0,
|
|
||||||
],
|
|
||||||
'long-running' => [
|
|
||||||
'connection' => 'redis',
|
|
||||||
'queue' => ['long-running'],
|
|
||||||
'balance' => 'auto',
|
|
||||||
'maxTime' => 0,
|
'maxTime' => 0,
|
||||||
'maxJobs' => 0,
|
'maxJobs' => 0,
|
||||||
'memory' => 128,
|
'memory' => 128,
|
||||||
@ -209,27 +198,15 @@
|
|||||||
'environments' => [
|
'environments' => [
|
||||||
'production' => [
|
'production' => [
|
||||||
's6' => [
|
's6' => [
|
||||||
'autoScalingStrategy' => 'size',
|
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
|
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
|
||||||
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
|
||||||
],
|
|
||||||
'long-running' => [
|
|
||||||
'autoScalingStrategy' => 'size',
|
'autoScalingStrategy' => 'size',
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
||||||
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
||||||
],
|
],
|
||||||
],
|
|
||||||
|
|
||||||
|
],
|
||||||
'local' => [
|
'local' => [
|
||||||
's6' => [
|
's6' => [
|
||||||
'autoScalingStrategy' => 'size',
|
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 2),
|
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
|
||||||
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
|
|
||||||
],
|
|
||||||
'long-running' => [
|
|
||||||
'autoScalingStrategy' => 'size',
|
'autoScalingStrategy' => 'size',
|
||||||
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 6),
|
||||||
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
|
||||||
|
@ -33,14 +33,6 @@
|
|||||||
'sync' => [
|
'sync' => [
|
||||||
'driver' => 'sync',
|
'driver' => 'sync',
|
||||||
],
|
],
|
||||||
'long-running' => [
|
|
||||||
'driver' => 'redis',
|
|
||||||
'connection' => 'default',
|
|
||||||
'queue' => 'long-running',
|
|
||||||
'retry_after' => 3600,
|
|
||||||
'block_for' => null,
|
|
||||||
'after_commit' => true,
|
|
||||||
],
|
|
||||||
'database' => [
|
'database' => [
|
||||||
'driver' => 'database',
|
'driver' => 'database',
|
||||||
'table' => 'jobs',
|
'table' => 'jobs',
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
// The release version of your application
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.175',
|
'release' => '4.0.0-beta.182',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
return '4.0.0-beta.175';
|
return '4.0.0-beta.182';
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_raw_compose_deployment_enabled')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('application_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_raw_compose_deployment_enabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -8,9 +8,9 @@ ARG DOCKER_COMPOSE_VERSION=2.21.0
|
|||||||
# https://github.com/docker/buildx/releases
|
# https://github.com/docker/buildx/releases
|
||||||
ARG DOCKER_BUILDX_VERSION=0.11.2
|
ARG DOCKER_BUILDX_VERSION=0.11.2
|
||||||
# https://github.com/buildpacks/pack/releases
|
# https://github.com/buildpacks/pack/releases
|
||||||
ARG PACK_VERSION=0.31.0
|
ARG PACK_VERSION=0.32.1
|
||||||
# https://github.com/railwayapp/nixpacks/releases
|
# https://github.com/railwayapp/nixpacks/releases
|
||||||
ARG NIXPACKS_VERSION=1.18.0
|
ARG NIXPACKS_VERSION=1.20.0
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
WORKDIR /artifacts
|
WORKDIR /artifacts
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
@isset($title, $action)
|
|
||||||
<div tabindex="0" x-data="{ open: false }"
|
|
||||||
class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
|
|
||||||
:class="open ? 'collapse-open' : 'collapse-close'">
|
|
||||||
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
|
|
||||||
{{ $title }}
|
|
||||||
</div>
|
|
||||||
<div class="collapse-content">
|
|
||||||
{{ $action }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endisset
|
|
@ -5,9 +5,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>Advanced configuration for your application.</div>
|
<div>Advanced configuration for your application.</div>
|
||||||
<div class="flex flex-col w-full pt-4">
|
<div class="flex flex-col w-full pt-4">
|
||||||
|
@if (!$application->settings->is_raw_compose_deployment_enabled)
|
||||||
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||||
instantSave id="application.settings.is_log_drain_enabled" label="Drain Logs" />
|
instantSave id="application.settings.is_log_drain_enabled" label="Drain Logs" />
|
||||||
|
@endif
|
||||||
<x-forms.checkbox
|
<x-forms.checkbox
|
||||||
helper="Your application will be available only on https if your domain starts with https://..."
|
helper="Your application will be available only on https if your domain starts with https://..."
|
||||||
instantSave id="application.settings.is_force_https_enabled" label="Force Https" />
|
instantSave id="application.settings.is_force_https_enabled" label="Force Https" />
|
||||||
|
@ -48,7 +48,10 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if ($application->build_pack === 'dockercompose')
|
@if ($application->build_pack === 'dockercompose')
|
||||||
@if (count($parsedServices) > 0)
|
<x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
|
||||||
|
label="Raw Compose Deployment"
|
||||||
|
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a href='https://coolify.io/docs/docker-compose'>documentation.</a>" />
|
||||||
|
@if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled)
|
||||||
@foreach (data_get($parsedServices, 'services') as $serviceName => $service)
|
@foreach (data_get($parsedServices, 'services') as $serviceName => $service)
|
||||||
@if (!isDatabaseImage(data_get($service, 'image')))
|
@if (!isDatabaseImage(data_get($service, 'image')))
|
||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
@ -184,8 +187,13 @@ class="underline" href="https://coolify.io/docs/docker-registries"
|
|||||||
@endif
|
@endif
|
||||||
@if ($application->build_pack === 'dockercompose')
|
@if ($application->build_pack === 'dockercompose')
|
||||||
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
|
<x-forms.button wire:click="loadComposeFile">Reload Compose File</x-forms.button>
|
||||||
<x-forms.textarea rows="10" readonly id="application.docker_compose"
|
@if ($application->settings->is_raw_compose_deployment_enabled)
|
||||||
label="Docker Compose Content" helper="You need to modify the docker compose file." />
|
<x-forms.textarea rows="10" readonly id="application.docker_compose_raw"
|
||||||
|
label="Docker Compose Content (applicationId: {{$application->id}})" helper="You need to modify the docker compose file." />
|
||||||
|
@else
|
||||||
|
<x-forms.textarea rows="10" readonly id="application.docker_compose"
|
||||||
|
label="Docker Compose Content" helper="You need to modify the docker compose file." />
|
||||||
|
@endif
|
||||||
{{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr"
|
{{-- <x-forms.textarea rows="10" readonly id="application.docker_compose_pr"
|
||||||
label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}}
|
label="Docker PR Compose Content" helper="You need to modify the docker compose file." /> --}}
|
||||||
@endif
|
@endif
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<x-collapsible>
|
<div tabindex="0" x-data="{ open: false }"
|
||||||
<x-slot:title>
|
class="transition border rounded cursor-pointer collapse collapse-arrow border-coolgray-200"
|
||||||
|
:class="open ? 'collapse-open' : 'collapse-close'">
|
||||||
|
<div class="flex flex-col justify-center text-sm select-text collapse-title" x-on:click="open = !open">
|
||||||
<div>{{ $workdir }}{{ $fs_path }} -> {{ $fileStorage->mount_path }}</div>
|
<div>{{ $workdir }}{{ $fs_path }} -> {{ $fileStorage->mount_path }}</div>
|
||||||
</x-slot:title>
|
</div>
|
||||||
<x-slot:action>
|
<div class="collapse-content">
|
||||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||||
<div class="w-64">
|
<div class="w-64">
|
||||||
<x-forms.checkbox instantSave label="Is directory?" id="fileStorage.is_directory"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Is directory?" id="fileStorage.is_directory"></x-forms.checkbox>
|
||||||
@ -21,5 +23,5 @@
|
|||||||
@endif
|
@endif
|
||||||
{{-- @endif --}}
|
{{-- @endif --}}
|
||||||
</form>
|
</form>
|
||||||
</x-slot:action>
|
</div>
|
||||||
</x-collapsible>
|
</div>
|
||||||
|
15
templates/compose/stirling-pdf.yaml
Normal file
15
templates/compose/stirling-pdf.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# documentation: https://github.com/Stirling-Tools/Stirling-PDF
|
||||||
|
# slogan: Stirling is a powerful web based PDF manipulation tool
|
||||||
|
# tags: pdf, manipulation, web, tool
|
||||||
|
|
||||||
|
services:
|
||||||
|
stirling-pdf:
|
||||||
|
image: frooodle/s-pdf:latest
|
||||||
|
volumes:
|
||||||
|
- stirling-training-data:/usr/share/tesseract-ocr/5/tessdata
|
||||||
|
- stirling-configs:/configs
|
||||||
|
- stirling-custom-files:/customFiles/
|
||||||
|
- stirling-logs:/logs/
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_SPDF
|
||||||
|
- DOCKER_ENABLE_SECURITY=false
|
@ -469,6 +469,17 @@
|
|||||||
"internet"
|
"internet"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"stirling-pdf": {
|
||||||
|
"documentation": "https:\/\/github.com\/Stirling-Tools\/Stirling-PDF",
|
||||||
|
"slogan": "Stirling is a powerful web based PDF manipulation tool",
|
||||||
|
"compose": "c2VydmljZXM6CiAgc3RpcmxpbmctcGRmOgogICAgaW1hZ2U6ICdmcm9vb2RsZS9zLXBkZjpsYXRlc3QnCiAgICB2b2x1bWVzOgogICAgICAtICdzdGlybGluZy10cmFpbmluZy1kYXRhOi91c3Ivc2hhcmUvdGVzc2VyYWN0LW9jci81L3Rlc3NkYXRhJwogICAgICAtICdzdGlybGluZy1jb25maWdzOi9jb25maWdzJwogICAgICAtICdzdGlybGluZy1jdXN0b20tZmlsZXM6L2N1c3RvbUZpbGVzLycKICAgICAgLSAnc3RpcmxpbmctbG9nczovbG9ncy8nCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1BERgogICAgICAtIERPQ0tFUl9FTkFCTEVfU0VDVVJJVFk9ZmFsc2UK",
|
||||||
|
"tags": [
|
||||||
|
"pdf",
|
||||||
|
"manipulation",
|
||||||
|
"web",
|
||||||
|
"tool"
|
||||||
|
]
|
||||||
|
},
|
||||||
"trigger-with-external-database": {
|
"trigger-with-external-database": {
|
||||||
"documentation": "https:\/\/trigger.dev\/docs\/documentation\/guides\/self-hosting",
|
"documentation": "https:\/\/trigger.dev\/docs\/documentation\/guides\/self-hosting",
|
||||||
"slogan": "The open source Background Jobs framework for TypeScript",
|
"slogan": "The open source Background Jobs framework for TypeScript",
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"version": "3.12.36"
|
"version": "3.12.36"
|
||||||
},
|
},
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.175"
|
"version": "4.0.0-beta.182"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user