Merge branch 'next' into alpine_support

This commit is contained in:
Loïc Tosser
2024-09-09 08:48:36 +04:00
committed by GitHub
12 changed files with 374 additions and 285 deletions

View File

@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
labels: |
coolify.managed=true
aarch64: aarch64:
runs-on: [ self-hosted, arm64 ] runs-on: [ self-hosted, arm64 ]
permissions: permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64 platforms: linux/aarch64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
labels: |
coolify.managed=true
merge-manifest: merge-manifest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@@ -94,3 +98,4 @@ jobs:
if: always() if: always()
with: with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }} webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}

View File

@@ -2,7 +2,7 @@ name: Coolify Helper Image (v4)
on: on:
push: push:
branches: [ "main" ] branches: [ "main", "next" ]
paths: paths:
- .github/workflows/coolify-helper.yml - .github/workflows/coolify-helper.yml
- docker/coolify-helper/Dockerfile - docker/coolify-helper/Dockerfile
@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64 platforms: linux/amd64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
labels: |
coolify.managed=true
aarch64: aarch64:
runs-on: [ self-hosted, arm64 ] runs-on: [ self-hosted, arm64 ]
permissions: permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64 platforms: linux/aarch64
push: true push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
labels: |
coolify.managed=true
merge-manifest: merge-manifest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:

View File

@@ -23,7 +23,7 @@ class CleanupDocker
{ {
$commonCommands = [ $commonCommands = [
'docker container prune -f --filter "label=coolify.managed=true"', 'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -af', 'docker image prune -af --filter "label!=coolify.managed=true"',
'docker builder prune -af', 'docker builder prune -af',
]; ];

View File

@@ -25,6 +25,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use App\Models\InstanceSettings;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
@@ -493,12 +494,15 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} else { } else {
$network = $this->database->destination->network; $network = $this->database->destination->network;
} }
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
$this->ensureHelperImageAvailable();
$fullImageName = $this->getFullImageName();
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret"; $commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/"; $commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server); instant_remote_process($commands, $this->server);
$this->add_to_backup_output('Uploaded to S3.'); $this->add_to_backup_output('Uploaded to S3.');
ray('Uploaded to S3. '.$this->backup_location.' to s3://'.$bucket.$this->backup_dir);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage()); $this->add_to_backup_output($e->getMessage());
throw $e; throw $e;
@@ -507,4 +511,40 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
instant_remote_process([$command], $this->server); instant_remote_process([$command], $this->server);
} }
} }
private function ensureHelperImageAvailable(): void
{
$fullImageName = $this->getFullImageName();
$imageExists = $this->checkImageExists($fullImageName);
if (!$imageExists) {
$this->pullHelperImage($fullImageName);
}
}
private function checkImageExists(string $fullImageName): bool
{
$result = instant_remote_process(["docker image inspect {$fullImageName} >/dev/null 2>&1 && echo 'exists' || echo 'not exists'"], $this->server, false);
return trim($result) === 'exists';
}
private function pullHelperImage(string $fullImageName): void
{
try {
instant_remote_process(["docker pull {$fullImageName}"], $this->server);
} catch (\Exception $e) {
$errorMessage = "Failed to pull helper image: " . $e->getMessage();
$this->add_to_backup_output($errorMessage);
throw new \RuntimeException($errorMessage);
}
}
private function getFullImageName(): string
{
$settings = InstanceSettings::get();
$helperImage = config('coolify.helper_image');
$latestVersion = $settings->helper_version;
return "{$helperImage}:{$latestVersion}";
}
} }

View File

@@ -2864,6 +2864,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return collect($finalServices); return collect($finalServices);
} }
} }
function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
{ {
$isApplication = $resource instanceof Application; $isApplication = $resource instanceof Application;
@@ -2920,6 +2921,178 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
$parsedServices = collect([]); $parsedServices = collect([]);
// parse environments first
$allMagicEnvironments = collect([]);
foreach ($services as $serviceName => $service) {
$magicEnvironments = collect([]);
$image = data_get_str($service, 'image');
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
if ($isService) {
if ($isDatabase) {
$savedService = ServiceDatabase::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
} else {
$savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
}
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
// convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
});
// filter magic environments
foreach ($environment as $key => $value) {
$regex = '/\$(\{?([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\}?)/';
preg_match_all($regex, $value, $valueMatches);
preg_match_all($regex, $key, $keyMatches);
if (count($valueMatches[1]) > 0) {
foreach ($valueMatches[1] as $match) {
if (str($match)->startsWith('SERVICE_')) {
if ($magicEnvironments->has($match)) {
continue;
}
$magicEnvironments->put($match, '');
}
}
}
if (count($keyMatches[1]) > 0) {
foreach ($keyMatches[1] as $match) {
if (str($match)->startsWith('SERVICE_')) {
if ($magicEnvironments->has($match)) {
continue;
}
$magicEnvironments->put($match, '');
}
}
}
if (str($key)->startsWith('SERVICE_')) {
$magicEnvironments->put($key, $value);
if (substr_count(str($key)->value(), '_') === 3) {
$newKey = str($key)->beforeLast('_')->value();
if ($newKey) {
$magicEnvironments->put($newKey, $value);
}
}
}
}
$allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
$key = str($key);
$value = str(replaceVariables(str($value)));
$originalValue = $value;
$keyCommand = $key->after('SERVICE_')->before('_');
$valueCommand = $value->after('SERVICE_')->before('_');
if ($key->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} elseif ($value->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} else {
$fqdnFor = null;
}
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($value === '/') {
$value = "$fqdn";
} else {
$value = "$fqdn$path";
}
} else {
$value = $fqdn;
}
if (! $isDatabase) {
if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $value;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
if ($key->startsWith('SERVICE_FQDN_')) {
$savedService->fqdn = $value;
$savedService->save();
}
}
}
}
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
} elseif ($isService) {
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
$value = "$fqdn$path";
} else {
$value = $fqdn;
}
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
} else {
$generatedValue = generateEnvValue($keyCommand, $resource);
if ($generatedValue) {
$value = $generatedValue;
}
}
if (str($fqdnFor)->startsWith('/')) {
$fqdnFor = null;
}
// Lets save the magic value to the environment variables
if (! $originalValue->startsWith('/')) {
if ($key->startsWith('SERVICE_')) {
$originalValue = $key;
}
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $originalValue->value(),
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
}
}
}
}
// Parse the rest of the services
foreach ($services as $serviceName => $service) { foreach ($services as $serviceName => $service) {
$image = data_get_str($service, 'image'); $image = data_get_str($service, 'image');
$restart = data_get_str($service, 'restart', RESTART_MODE); $restart = data_get_str($service, 'restart', RESTART_MODE);
@@ -2938,6 +3111,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$ports = collect(data_get($service, 'ports', [])); $ports = collect(data_get($service, 'ports', []));
$buildArgs = collect(data_get($service, 'build.args', [])); $buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs); $environment = $environment->merge($buildArgs);
$environment = convertComposeEnvironmentToArray($environment);
$coolifyEnvironments = collect([]);
$isDatabase = isDatabaseImage(data_get_str($service, 'image')); $isDatabase = isDatabaseImage(data_get_str($service, 'image'));
$volumesParsed = collect([]); $volumesParsed = collect([]);
@@ -3069,10 +3246,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($topLevel->get('volumes')->has($source->value())) { if ($topLevel->get('volumes')->has($source->value())) {
$temp = $topLevel->get('volumes')->get($source->value()); $temp = $topLevel->get('volumes')->get($source->value());
if (data_get($temp, 'driver_opts.type') === 'cifs') { if (data_get($temp, 'driver_opts.type') === 'cifs') {
return $volume; continue;
} }
if (data_get($temp, 'driver_opts.type') === 'nfs') { if (data_get($temp, 'driver_opts.type') === 'nfs') {
return $volume; continue;
} }
} }
$slugWithoutUuid = Str::slug($source, '-'); $slugWithoutUuid = Str::slug($source, '-');
@@ -3204,177 +3381,19 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
]); ]);
} }
} }
// convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments $normalEnvironments = $environment->diffKeys($allMagicEnvironments);
$allEnvironments = $resource->environment_variables()->get(['key', 'value']); $normalEnvironments = $normalEnvironments->filter(function ($value, $key) {
return ! str($value)->startsWith('SERVICE_');
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
}); });
// remove $environment from $allEnvironments
$coolifyDefinedEnvironments = $allEnvironments->diffKeys($environment);
// filter magic environments
$magicEnvironments = $environment->filter(function ($value, $key) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
return $match;
}
}
}
$value = str(replaceVariables(str($value)));
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
});
foreach ($environment as $key => $value) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
$magicEnvironments->put($match, '$'.$match);
}
}
$magicEnvironments->forget($key);
}
}
$normalEnvironments = $environment->diffKeys($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
$key = str($key);
$value = str(replaceVariables(str($value)));
$originalValue = $value;
$keyCommand = $key->after('SERVICE_')->before('_');
$valueCommand = $value->after('SERVICE_')->before('_');
if ($key->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} elseif ($value->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} else {
$fqdnFor = null;
}
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($value === '/') {
$value = "$fqdn";
} else {
$value = "$fqdn$path";
}
} else {
$value = $fqdn;
}
if (! $isDatabase) {
if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $value;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
if ($key->startsWith('SERVICE_FQDN_')) {
$savedService->fqdn = $value;
$savedService->save();
}
}
}
}
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
} elseif ($isService) {
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
$value = "$fqdn$path";
} else {
$value = $fqdn;
}
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
} else {
$generatedValue = generateEnvValue($valueCommand, $resource);
if ($generatedValue) {
$value = $generatedValue;
}
}
if (str($fqdnFor)->startsWith('/')) {
$fqdnFor = null;
}
// Lets save the magic value to the environment variables
if (! $originalValue->startsWith('/')) {
if ($key->startsWith('SERVICE_')) {
$originalValue = $key;
}
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $originalValue->value(),
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
// Save the original value to the environment variables
if ($originalValue->startsWith('SERVICE_')) {
$value = "$$originalValue";
}
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => "$value",
'is_build_time' => false,
'is_preview' => false,
]);
}
}
foreach ($normalEnvironments as $key => $value) { foreach ($normalEnvironments as $key => $value) {
$key = str($key); $key = str($key);
$value = str($value); $value = str($value);
if ($value->startsWith('$') || $value->contains('${')) { $parsedValue = str(replaceVariables(str($value)));
if ($value->contains('${')) {
$value = $value->after('${')->before('}'); if ($key->value() === $parsedValue->value()) {
} $value = null;
$value = str(replaceVariables(str($value)));
if ($value->contains(':-')) {
$key = $value->before(':');
$value = $value->after(':-');
} elseif ($value->contains('-')) {
$key = $value->before('-');
$value = $value->after('-');
} elseif ($value->contains(':?')) {
$key = $value->before(':');
$value = $value->after(':?');
} elseif ($value->contains('?')) {
$key = $value->before('?');
$value = $value->after('?');
} else {
$key = $value;
$value = null;
}
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([ $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key, 'key' => $key,
$nameOfId => $resource->id, $nameOfId => $resource->id,
@@ -3383,6 +3402,38 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'is_build_time' => false, 'is_build_time' => false,
'is_preview' => false, 'is_preview' => false,
]); ]);
} else {
if ($value->startsWith('$')) {
if ($value->contains(':-')) {
$value = str(replaceVariables(str($value)));
$key = $value->before(':');
$value = $value->after(':-');
} elseif ($value->contains('-')) {
$value = str(replaceVariables(str($value)));
$key = $value->before('-');
$value = $value->after('-');
} elseif ($value->contains(':?')) {
$value = str(replaceVariables(str($value)));
$key = $value->before(':');
$value = $value->after(':?');
} elseif ($value->contains('?')) {
$value = str(replaceVariables(str($value)));
$key = $value->before('?');
$value = $value->after('?');
}
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key,
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
} }
} }
if ($isApplication) { if ($isApplication) {
@@ -3391,13 +3442,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$branch = "pull/{$pullRequestId}/head"; $branch = "pull/{$pullRequestId}/head";
} }
if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) { if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
$environment->put('COOLIFY_BRANCH', $branch); $coolifyEnvironments->put('COOLIFY_BRANCH', $branch);
} }
} }
// Add COOLIFY_CONTAINER_NAME to environment // Add COOLIFY_CONTAINER_NAME to environment
if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) { if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
$environment->put('COOLIFY_CONTAINER_NAME', $containerName); $coolifyEnvironments->put('COOLIFY_CONTAINER_NAME', $containerName);
} }
if ($isApplication) { if ($isApplication) {
@@ -3451,15 +3502,20 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} }
// Add COOLIFY_FQDN & COOLIFY_URL to environment // Add COOLIFY_FQDN & COOLIFY_URL to environment
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) { if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
$environment->put('COOLIFY_URL', $fqdns->implode(',')); $coolifyEnvironments->put('COOLIFY_URL', $fqdns->implode(','));
$urls = $fqdns->map(function ($fqdn) { $urls = $fqdns->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', ''); return str($fqdn)->replace('http://', '')->replace('https://', '');
}); });
$environment->put('COOLIFY_FQDN', $urls->implode(',')); $coolifyEnvironments->put('COOLIFY_FQDN', $urls->implode(','));
} }
add_coolify_default_environment_variables($resource, $environment, $resource->environment_variables); add_coolify_default_environment_variables($resource, $coolifyEnvironments, $resource->environment_variables);
if ($environment->count() > 0) {
$environment = $environment->filter(function ($value, $key) {
return ! str($key)->startsWith('SERVICE_FQDN_');
});
}
$serviceLabels = $labels->merge($defaultLabels); $serviceLabels = $labels->merge($defaultLabels);
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) { if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
if ($isApplication) { if ($isApplication) {
@@ -3554,8 +3610,8 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($volumesParsed->count() > 0) { if ($volumesParsed->count() > 0) {
$payload['volumes'] = $volumesParsed; $payload['volumes'] = $volumesParsed;
} }
if ($environment->count() > 0 || $coolifyDefinedEnvironments->count() > 0) { if ($environment->count() > 0 || $coolifyEnvironments->count() > 0) {
$payload['environment'] = $environment->merge($coolifyDefinedEnvironments); $payload['environment'] = $environment->merge($coolifyEnvironments);
} }
if ($logging) { if ($logging) {
$payload['logging'] = $logging; $payload['logging'] = $logging;

View File

@@ -7,7 +7,7 @@ return [
// 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.331', 'release' => '4.0.0-beta.332',
// 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'),

View File

@@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.331'; return '4.0.0-beta.332';

View File

@@ -0,0 +1,37 @@
<?php
use App\Models\ServerSetting;
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('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('0 0 * * *')->change();
});
$serverSettings = ServerSetting::all();
foreach ($serverSettings as $serverSetting) {
if ($serverSetting->force_docker_cleanup && $serverSetting->docker_cleanup_frequency === '*/10 * * * *') {
$serverSetting->docker_cleanup_frequency = '0 0 * * *';
$serverSetting->save();
}
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('*/10 * * * *')->change();
});
}
};

View File

@@ -16,7 +16,7 @@ services:
environment: environment:
- SERVICE_FQDN_PLUNK_3000 - SERVICE_FQDN_PLUNK_3000
- REDIS_URL=redis://redis:6379 - REDIS_URL=redis://redis:6379
- DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/docmost?schema=public - DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/plunk?schema=public
- JWT_SECRET=${SERVICE_PASSWORD_JWT_SECRET} - JWT_SECRET=${SERVICE_PASSWORD_JWT_SECRET}
- AWS_REGION=${AWS_REGION} - AWS_REGION=${AWS_REGION}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}

File diff suppressed because one or more lines are too long

View File

@@ -88,114 +88,61 @@ networks:
]); ]);
$this->serviceYaml = ' $this->serviceYaml = '
services: services:
chatwoot: activepieces:
image: chatwoot/chatwoot:latest image: "ghcr.io/activepieces/activepieces:latest"
depends_on:
- postgres
- redis
environment: environment:
- SERVICE_FQDN_CHATWOOT_3000 - SERVICE_FQDN_ACTIVEPIECES
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT} - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE} - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
- FORCE_SSL=false - AP_ENVIRONMENT=prod
- ENABLE_ACCOUNT_SIGNUP=false - AP_EXECUTION_MODE=UNSANDBOXED
- REDIS_URL=redis://default@redis:6379 - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT
- REDIS_OPENSSL_VERIFY_MODE=none - AP_POSTGRES_DATABASE=activepieces
- POSTGRES_DATABASE=chatwoot - AP_POSTGRES_HOST=postgres
- POSTGRES_HOST=postgres - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER - AP_POSTGRES_PORT=5432
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES
- RAILS_MAX_THREADS=5 - AP_REDIS_HOST=redis
- NODE_ENV=production - AP_REDIS_PORT=6379
- RAILS_ENV=production - AP_SANDBOX_RUN_TIME_SECONDS=600
- INSTALLATION_ENV=docker - AP_TELEMETRY_ENABLED=true
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL} - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates"
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS} - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION} - AP_WEBHOOK_TIMEOUT_SECONDS=30
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN} depends_on:
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO} postgres:
- SMTP_PORT=${CHATWOOT_SMTP_PORT} condition: service_healthy
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME} redis:
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD} condition: service_started
- ACTIVE_STORAGE_SERVICE=local
entrypoint: docker/entrypoints/rails.sh
command: sh -c "bundle exec rails db:chatwoot_prepare && bundle exec rails s -p 3000 -b 0.0.0.0"
volumes:
- rails-data:/app/storage
healthcheck: healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:3000"] test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
interval: 5s interval: 5s
timeout: 20s timeout: 20s
retries: 10 retries: 10
sidekiq:
image: chatwoot/chatwoot:latest
depends_on:
- postgres
- redis
environment:
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT}
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE}
- FORCE_SSL=false
- ENABLE_ACCOUNT_SIGNUP=false
- REDIS_URL=redis://default@redis:6379
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS
- REDIS_OPENSSL_VERIFY_MODE=none
- POSTGRES_DATABASE=chatwoot
- POSTGRES_HOST=postgres
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- RAILS_MAX_THREADS=5
- NODE_ENV=production
- RAILS_ENV=production
- INSTALLATION_ENV=docker
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL}
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS}
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION}
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN}
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO}
- SMTP_PORT=${CHATWOOT_SMTP_PORT}
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME}
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD}
- ACTIVE_STORAGE_SERVICE=local
command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"]
volumes:
- sidekiq-data:/app/storage
healthcheck:
test: ["CMD-SHELL", "bundle exec rails runner \'puts Sidekiq.redis(&:info)\' > /dev/null 2>&1"]
interval: 30s
timeout: 10s
retries: 3
postgres: postgres:
image: postgres:12 image: "postgres:latest"
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
environment: environment:
- POSTGRES_DB=chatwoot - POSTGRES_DB=activepieces
- POSTGRES_USER=$SERVICE_USER_POSTGRES_USER
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
healthcheck: - POSTGRES_USER=$SERVICE_USER_POSTGRES
test: ["CMD-SHELL", "pg_isready -U $SERVICE_USER_POSTGRES_USER -d chatwoot -h 127.0.0.1"]
interval: 30s
timeout: 10s
retries: 5
redis:
image: redis:alpine
restart: always
command: ["sh", "-c", "redis-server --requirepass \"$SERVICE_PASSWORD_REDIS\""]
volumes: volumes:
- redis-data:/data - "pg-data:/var/lib/postgresql/data"
healthcheck: healthcheck:
test: ["CMD", "redis-cli", "-a", "$SERVICE_PASSWORD_REDIS", "PING"] test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 30s interval: 5s
timeout: 10s timeout: 20s
retries: 5 retries: 10
redis:
image: "redis:latest"
volumes:
- "redis_data:/data"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 20s
retries: 10
'; ';

View File

@@ -1,13 +1,13 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.331"
},
"nightly": {
"version": "4.0.0-beta.332" "version": "4.0.0-beta.332"
}, },
"nightly": {
"version": "4.0.0-beta.333"
},
"helper": { "helper": {
"version": "1.0.0" "version": "1.0.1"
} }
} }
} }