mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-18 12:33:06 +00:00
Compare commits
34 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e2c932f07 | ||
|
|
e4aed185a2 | ||
|
|
dddbe40bbe | ||
|
|
59d6818f70 | ||
|
|
7678cd47df | ||
|
|
b101fbacd4 | ||
|
|
a61a86dc3b | ||
|
|
0b3cde44c3 | ||
|
|
6b302ab786 | ||
|
|
3c623f13e2 | ||
|
|
da54c24e8d | ||
|
|
1e39c3d5ab | ||
|
|
6071412986 | ||
|
|
ba7148206a | ||
|
|
59c5b22e6c | ||
|
|
be7f2ad9c4 | ||
|
|
62295ef573 | ||
|
|
ceb9fcf3b6 | ||
|
|
60282f7b6c | ||
|
|
f14b0a3411 | ||
|
|
30af317bd9 | ||
|
|
95faa1c3ad | ||
|
|
fb280afe41 | ||
|
|
fd488a561a | ||
|
|
ab57a5d8ef | ||
|
|
f5ae222a6e | ||
|
|
5d95d8b79a | ||
|
|
fbb5f2ca2e | ||
|
|
16cbca36c1 | ||
|
|
24a578bedb | ||
|
|
36dc479772 | ||
|
|
83d6e488e4 | ||
|
|
a4d358d512 | ||
|
|
c0c197101d |
@@ -27,3 +27,5 @@ You can ask for guidance anytime on our
|
||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||
|
||||
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
||||
|
||||
Mails are caught by Mailpit: `localhost:8025`
|
||||
|
||||
@@ -87,7 +87,7 @@ class StartDatabaseProxy
|
||||
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||
"docker compose --project-directory {$configuration_dir} up --build -d >/dev/null",
|
||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||
], $database->destination->server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,41 +2,50 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Str;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
|
||||
class CheckProxy
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Server $server)
|
||||
public function handle(Server $server, $fromUI = false)
|
||||
{
|
||||
if (!$server->isProxyShouldRun()) {
|
||||
throw new \Exception("Proxy should not run");
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$status = getContainerStatus($server, 'coolify-proxy');
|
||||
if ($status === 'running') {
|
||||
$server->proxy->set('status', 'running');
|
||||
$server->save();
|
||||
return 'OK';
|
||||
return false;
|
||||
}
|
||||
$ip = $server->ip;
|
||||
if ($server->id === 0) {
|
||||
$ip = 'host.docker.internal';
|
||||
}
|
||||
|
||||
$connection = @fsockopen($ip, '80');
|
||||
$connection = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection) && fclose($connection);
|
||||
$port443 = is_resource($connection) && fclose($connection);
|
||||
ray($ip);
|
||||
$connection80 = @fsockopen($ip, '80');
|
||||
$connection443 = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection80) && fclose($connection80);
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($port443) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>>");
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ class StartProxy
|
||||
public function handle(Server $server, bool $async = true): string|Activity
|
||||
{
|
||||
try {
|
||||
CheckProxy::run($server);
|
||||
|
||||
$proxyType = $server->proxyType();
|
||||
$commands = collect([]);
|
||||
$proxy_path = get_proxy_path();
|
||||
|
||||
@@ -28,6 +28,7 @@ class Kernel extends ConsoleKernel
|
||||
// $this->check_scheduled_backups($schedule);
|
||||
$this->check_resources($schedule);
|
||||
$this->cleanup_servers($schedule);
|
||||
$this->check_scheduled_backups($schedule);
|
||||
} else {
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||
|
||||
@@ -60,12 +60,16 @@ class DeploymentNavbar extends Component
|
||||
$previous_logs[] = $new_log_entry;
|
||||
$this->application_deployment_queue->update([
|
||||
'logs' => json_encode($previous_logs, flags: JSON_THROW_ON_ERROR),
|
||||
'current_process_id' => null,
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->application_deployment_queue->update([
|
||||
'current_process_id' => null,
|
||||
'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value,
|
||||
]);
|
||||
queue_next_deployment($this->application);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
namespace App\Http\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
@@ -23,6 +20,10 @@ class General extends Component
|
||||
public ?string $git_commit_sha = null;
|
||||
public string $build_pack;
|
||||
|
||||
public $customLabels;
|
||||
public bool $labelsChanged = false;
|
||||
public bool $isConfigurationChanged = false;
|
||||
|
||||
public bool $is_static;
|
||||
public bool $is_git_submodules_enabled;
|
||||
public bool $is_git_lfs_enabled;
|
||||
@@ -52,6 +53,7 @@ class General extends Component
|
||||
'application.docker_registry_image_name' => 'nullable',
|
||||
'application.docker_registry_image_tag' => 'nullable',
|
||||
'application.dockerfile_location' => 'nullable',
|
||||
'application.custom_labels' => 'nullable',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'application.name' => 'name',
|
||||
@@ -73,16 +75,47 @@ class General extends Component
|
||||
'application.docker_registry_image_name' => 'Docker registry image name',
|
||||
'application.docker_registry_image_tag' => 'Docker registry image tag',
|
||||
'application.dockerfile_location' => 'Dockerfile location',
|
||||
|
||||
'application.custom_labels' => 'Custom labels',
|
||||
];
|
||||
|
||||
public function updatedApplicationBuildPack(){
|
||||
public function mount()
|
||||
{
|
||||
if (str($this->application->status)->startsWith('running') && is_null($this->application->config_hash)) {
|
||||
$this->application->isConfigurationChanged(true);
|
||||
}
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
if (is_null(data_get($this->application, 'custom_labels'))) {
|
||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||
} else {
|
||||
$this->customLabels = str($this->application->custom_labels)->replace(',', "\n");
|
||||
}
|
||||
if (data_get($this->application, 'settings')) {
|
||||
$this->is_static = $this->application->settings->is_static;
|
||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||
}
|
||||
$this->checkLabelUpdates();
|
||||
}
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
if ($this->application->build_pack !== 'nixpacks') {
|
||||
$this->application->settings->is_static = $this->is_static = false;
|
||||
$this->application->settings->save();
|
||||
}
|
||||
$this->submit();
|
||||
}
|
||||
public function checkLabelUpdates()
|
||||
{
|
||||
if (md5($this->application->custom_labels) !== md5(implode(",", generateLabelsApplication($this->application)))) {
|
||||
$this->labelsChanged = true;
|
||||
} else {
|
||||
$this->labelsChanged = false;
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
// @TODO: find another way - if possible
|
||||
@@ -102,37 +135,36 @@ class General extends Component
|
||||
$this->application->save();
|
||||
$this->application->refresh();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
$this->checkLabelUpdates();
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
}
|
||||
|
||||
public function getWildcardDomain() {
|
||||
public function getWildcardDomain()
|
||||
{
|
||||
$server = data_get($this->application, 'destination.server');
|
||||
if ($server) {
|
||||
$fqdn = generateFqdn($server, $this->application->uuid);
|
||||
ray($fqdn);
|
||||
$this->application->fqdn = $fqdn;
|
||||
$this->application->save();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
}
|
||||
|
||||
}
|
||||
public function mount()
|
||||
public function resetDefaultLabels($showToaster = true)
|
||||
{
|
||||
if (data_get($this->application,'settings')) {
|
||||
$this->is_static = $this->application->settings->is_static;
|
||||
$this->is_git_submodules_enabled = $this->application->settings->is_git_submodules_enabled;
|
||||
$this->is_git_lfs_enabled = $this->application->settings->is_git_lfs_enabled;
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
$this->is_preview_deployments_enabled = $this->application->settings->is_preview_deployments_enabled;
|
||||
$this->is_auto_deploy_enabled = $this->application->settings->is_auto_deploy_enabled;
|
||||
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
|
||||
}
|
||||
$this->customLabels = str(implode(",", generateLabelsApplication($this->application)))->replace(',', "\n");
|
||||
$this->submit($showToaster);
|
||||
}
|
||||
|
||||
public function submit()
|
||||
public function updatedApplicationFqdn()
|
||||
{
|
||||
$this->resetDefaultLabels(false);
|
||||
$this->emit('success', 'Labels reseted to default!');
|
||||
}
|
||||
public function submit($showToaster = true)
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if (data_get($this->application,'build_pack') === 'dockerimage') {
|
||||
if (data_get($this->application, 'build_pack') === 'dockerimage') {
|
||||
$this->validate([
|
||||
'application.docker_registry_image_name' => 'required',
|
||||
'application.docker_registry_image_tag' => 'required',
|
||||
@@ -156,10 +188,17 @@ class General extends Component
|
||||
if ($this->application->publish_directory && $this->application->publish_directory !== '/') {
|
||||
$this->application->publish_directory = rtrim($this->application->publish_directory, '/');
|
||||
}
|
||||
if (gettype($this->customLabels) === 'string') {
|
||||
$this->customLabels = str($this->customLabels)->replace(',', "\n");
|
||||
}
|
||||
$this->application->custom_labels = $this->customLabels->explode("\n")->implode(',');
|
||||
$this->application->save();
|
||||
$this->emit('success', 'Application settings updated!');
|
||||
$showToaster && $this->emit('success', 'Application settings updated!');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->checkLabelUpdates();
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,5 +34,6 @@ class Navbar extends Component
|
||||
StopService::run($this->service);
|
||||
$this->service->refresh();
|
||||
$this->emit('success', 'Service stopped successfully.');
|
||||
$this->checkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use Livewire\Component;
|
||||
|
||||
class StackForm extends Component
|
||||
{
|
||||
public $service;
|
||||
protected $listeners = ["saveCompose"];
|
||||
protected $rules = [
|
||||
'service.docker_compose_raw' => 'required',
|
||||
@@ -13,7 +14,6 @@ class StackForm extends Component
|
||||
'service.name' => 'required',
|
||||
'service.description' => 'nullable',
|
||||
];
|
||||
public $service;
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
$this->service->docker_compose_raw = $raw;
|
||||
|
||||
@@ -39,7 +39,7 @@ class Deploy extends Component
|
||||
public function checkProxy()
|
||||
{
|
||||
try {
|
||||
CheckProxy::run($this->server);
|
||||
CheckProxy::run($this->server, true);
|
||||
$this->emit('startProxyPolling');
|
||||
$this->emit('proxyChecked');
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -34,7 +34,7 @@ class Status extends Component
|
||||
}
|
||||
$this->numberOfPolls++;
|
||||
}
|
||||
CheckProxy::run($this->server);
|
||||
CheckProxy::run($this->server, true);
|
||||
$this->emit('proxyStatusUpdated');
|
||||
if ($this->server->proxy->status === 'running') {
|
||||
$this->polling = false;
|
||||
|
||||
@@ -54,7 +54,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private ApplicationPreview|null $preview = null;
|
||||
|
||||
private string $container_name;
|
||||
private string|null $currently_running_container_name = null;
|
||||
private ?string $currently_running_container_name = null;
|
||||
private string $basedir;
|
||||
private string $workdir;
|
||||
private ?string $build_pack = null;
|
||||
@@ -67,14 +67,18 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private $docker_compose;
|
||||
private $docker_compose_base64;
|
||||
private string $dockerfile_location = '/Dockerfile';
|
||||
|
||||
private ?string $addHosts = null;
|
||||
private $log_model;
|
||||
private Collection $saved_outputs;
|
||||
|
||||
private string $serverUser = 'root';
|
||||
private string $serverUserHomeDir = '/root';
|
||||
private string $dockerConfigFileExists = 'NOK';
|
||||
|
||||
public $tries = 1;
|
||||
public function __construct(int $application_deployment_queue_id)
|
||||
{
|
||||
ray()->clearScreen();
|
||||
// ray()->clearScreen();
|
||||
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
|
||||
$this->log_model = $this->application_deployment_queue;
|
||||
$this->application = Application::find($this->application_deployment_queue->application_id);
|
||||
@@ -92,13 +96,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
}
|
||||
$this->destination = $this->application->destination->getMorphClass()::where('id', $this->application->destination->id)->first();
|
||||
$this->server = $this->destination->server;
|
||||
|
||||
$this->serverUser = $this->server->user;
|
||||
$this->basedir = "/artifacts/{$this->deployment_uuid}";
|
||||
$this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/');
|
||||
$this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}";
|
||||
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
|
||||
|
||||
ray($this->basedir, $this->workdir);
|
||||
$this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id);
|
||||
savePrivateKeyToFs($this->server);
|
||||
$this->saved_outputs = collect();
|
||||
@@ -138,6 +141,32 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->application_deployment_queue->update([
|
||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
]);
|
||||
|
||||
// Generate custom host<->ip mapping
|
||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||
$allContainers = format_docker_command_output_to_json($allContainers);
|
||||
$ips = collect([]);
|
||||
if (count($allContainers) > 0) {
|
||||
$allContainers = $allContainers[0];
|
||||
foreach ($allContainers as $container) {
|
||||
$containerName = data_get($container, 'Name');
|
||||
if ($containerName === 'coolify-proxy') {
|
||||
continue;
|
||||
}
|
||||
$containerIp = data_get($container, 'IPv4Address');
|
||||
if ($containerName && $containerIp) {
|
||||
$containerIp = str($containerIp)->before('/');
|
||||
$ips->put($containerName, $containerIp->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addHosts = $ips->map(function ($ip, $name) {
|
||||
return "--add-host $name:$ip";
|
||||
})->implode(' ');
|
||||
|
||||
// Get user home directory
|
||||
$this->serverUserHomeDir = instant_remote_process(["echo \$HOME"], $this->server);
|
||||
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
|
||||
try {
|
||||
if ($this->application->dockerfile) {
|
||||
$this->deploy_simple_dockerfile();
|
||||
@@ -156,6 +185,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
dispatch(new ContainerStatusJob($this->server));
|
||||
}
|
||||
$this->next(ApplicationDeploymentStatus::FINISHED->value);
|
||||
$this->application->isConfigurationChanged(true);
|
||||
} catch (Exception $e) {
|
||||
ray($e);
|
||||
$this->fail($e);
|
||||
@@ -324,14 +354,19 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->execute_remote_command([
|
||||
"docker images -q {$this->production_image_name} 2>/dev/null", "hidden" => true, "save" => "local_image_found"
|
||||
]);
|
||||
if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||
if (Str::of($this->saved_outputs->get('local_image_found'))->isNotEmpty() && !$this->application->isConfigurationChanged()) {
|
||||
$this->execute_remote_command([
|
||||
"echo 'Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped...'"
|
||||
"echo 'No configuration changed & Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped.'",
|
||||
]);
|
||||
$this->generate_compose_file();
|
||||
$this->rolling_update();
|
||||
return;
|
||||
}
|
||||
if ($this->application->isConfigurationChanged()) {
|
||||
$this->execute_remote_command([
|
||||
"echo 'Configuration changed. Rebuilding image.'",
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->cleanup_git();
|
||||
$this->generate_nixpacks_confs();
|
||||
@@ -428,7 +463,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->stop_running_container();
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Starting preview deployment.'"],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -436,7 +471,11 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
$pull = "--pull=always";
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
if ($this->dockerConfigFileExists === 'OK') {
|
||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v {$this->serverUserHomeDir}/.docker/config.json:/root/.docker/config.json:ro -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
} else {
|
||||
$runCommand = "docker run {$pull} -d --network {$this->destination->network} -v /:/host --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock {$helperImage}";
|
||||
}
|
||||
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
@@ -616,6 +655,10 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables($ports);
|
||||
|
||||
$labels = generateLabelsApplication($this->application, $this->preview);
|
||||
if (data_get($this->application, 'custom_labels')) {
|
||||
$labels = str($this->application->custom_labels)->explode(',')->toArray();
|
||||
}
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
@@ -624,7 +667,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'container_name' => $this->container_name,
|
||||
'restart' => RESTART_MODE,
|
||||
'environment' => $environment_variables,
|
||||
'labels' => generateLabelsApplication($this->application, $this->preview, $ports),
|
||||
'labels' => $labels,
|
||||
'expose' => $ports,
|
||||
'networks' => [
|
||||
$this->destination->network,
|
||||
@@ -766,7 +809,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
|
||||
if ($this->application->settings->is_static) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->build_image_name {$this->workdir}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "docker build $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}
|
||||
@@ -799,12 +842,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d > {$this->workdir}/nginx.conf")
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "docker build --network host -f {$this->workdir}/Dockerfile-prod {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
executeInDocker($this->deployment_uuid, "docker build $this->addHosts --network host -f {$this->workdir}/Dockerfile-prod {$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 --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 $this->addHosts --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} --progress plain -t $this->production_image_name {$this->workdir}"), "hidden" => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -830,7 +873,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
{
|
||||
$this->execute_remote_command(
|
||||
["echo -n 'Starting application (could take a while).'"],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d >/dev/null"), "hidden" => true],
|
||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up --build -d"), "hidden" => true],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
@@ -47,7 +48,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
ray("checking server status for {$this->server->id}");
|
||||
// ray("checking server status for {$this->server->id}");
|
||||
// ray()->clearAll();
|
||||
$serverUptimeCheckNumber = $this->server->unreachable_count;
|
||||
$serverUptimeCheckNumberMax = 3;
|
||||
@@ -117,10 +118,18 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
})->first();
|
||||
if (!$foundProxyContainer) {
|
||||
if ($this->server->isProxyShouldRun()) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
try {
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
} else {
|
||||
ray('Proxy could not be started.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
$this->server->save();
|
||||
|
||||
@@ -277,4 +277,31 @@ class Application extends BaseModel
|
||||
}
|
||||
return 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;
|
||||
if ($this->pull_request_id === 0) {
|
||||
$newConfigHash .= json_encode($this->environment_variables->all());
|
||||
} else {
|
||||
$newConfigHash .= json_encode($this->environment_variables_preview->all());
|
||||
}
|
||||
$newConfigHash = md5($newConfigHash);
|
||||
$oldConfigHash = data_get($this, 'config_hash');
|
||||
if ($oldConfigHash === null) {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,23 +193,24 @@ class Server extends BaseModel
|
||||
}
|
||||
public function isProxyShouldRun()
|
||||
{
|
||||
$shouldRun = false;
|
||||
if ($this->proxyType() === ProxyTypes::NONE->value) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->applications() as $application) {
|
||||
if (data_get($application, 'fqdn')) {
|
||||
$shouldRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->id === 0) {
|
||||
$settings = InstanceSettings::get();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$shouldRun = true;
|
||||
}
|
||||
}
|
||||
return $shouldRun;
|
||||
// foreach ($this->applications() as $application) {
|
||||
// if (data_get($application, 'fqdn')) {
|
||||
// $shouldRun = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// ray($this->services()->get());
|
||||
|
||||
// if ($this->id === 0) {
|
||||
// $settings = InstanceSettings::get();
|
||||
// if (data_get($settings, 'fqdn')) {
|
||||
// $shouldRun = true;
|
||||
// }
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
public function isFunctional()
|
||||
{
|
||||
|
||||
@@ -382,7 +382,7 @@ class Service extends BaseModel
|
||||
$value = Str::of($variable);
|
||||
}
|
||||
if ($key->startsWith('SERVICE_FQDN')) {
|
||||
if ($isNew) {
|
||||
if ($isNew || $savedService->fqdn === null) {
|
||||
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||
$fqdn = generateFqdn($this->server, "{$name->value()}-{$this->uuid}");
|
||||
if (substr_count($key->value(), '_') === 3) {
|
||||
@@ -568,7 +568,7 @@ class Service extends BaseModel
|
||||
'networks' => $topLevelNetworks->toArray(),
|
||||
];
|
||||
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
|
||||
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||
$this->docker_compose = Yaml::dump($finalServices, 10, 2);
|
||||
$this->save();
|
||||
$this->saveComposeConfigs();
|
||||
return collect([]);
|
||||
|
||||
@@ -205,8 +205,9 @@ function fqdnLabelsForTraefik(Collection $domains, bool $is_force_https_enabled,
|
||||
|
||||
return $labels;
|
||||
}
|
||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null, $ports): array
|
||||
function generateLabelsApplication(Application $application, ?ApplicationPreview $preview = null): array
|
||||
{
|
||||
$ports = $application->settings->is_static ? [80] : $application->ports_exposes_array;
|
||||
$onlyPort = null;
|
||||
if (count($ports) === 1) {
|
||||
$onlyPort = $ports[0];
|
||||
@@ -226,7 +227,8 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
|
||||
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
|
||||
}
|
||||
// Add Traefik labels no matter which proxy is selected
|
||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled,$onlyPort));
|
||||
$labels = $labels->merge(fqdnLabelsForTraefik($domains, $application->settings->is_force_https_enabled, $onlyPort));
|
||||
}
|
||||
ray($labels);
|
||||
return $labels->all();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.86',
|
||||
'release' => '4.0.0-beta.94',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.86';
|
||||
return '4.0.0-beta.94';
|
||||
|
||||
@@ -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('applications', function (Blueprint $table) {
|
||||
$table->text('custom_labels')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->dropColumn('custom_labels');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="preconnect" href="https://api.fonts.coollabs.io" crossorigin>
|
||||
<link href="https://api.fonts.coollabs.io/css2?family=Inter&display=swap" rel="stylesheet">
|
||||
<meta name="robots" content="noindex">
|
||||
<title>Coolify</title>
|
||||
@env('local')
|
||||
<link rel="icon" href="{{ asset('favicon-dev.png') }}" type="image/x-icon" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
@if ($projects->count() === 1)
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
@else
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||
@endif
|
||||
@foreach ($projects as $project)
|
||||
<div class="gap-2 border border-transparent cursor-pointer box group" x-data
|
||||
@@ -43,7 +43,7 @@
|
||||
{{ $project->description }}</div>
|
||||
</a>
|
||||
@endif
|
||||
<a class="mx-4 rounded group-hover:text-white hover:no-underline "
|
||||
<a class="mx-4 rounded group-hover:text-white hover:no-underline"
|
||||
href="{{ route('project.resources.new', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($project, 'environments.0.name', 'production')]) }}">
|
||||
<span class="font-bold hover:text-warning">+ New Resource</span>
|
||||
</a>
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
@endif
|
||||
@if (data_get($application_deployment_queue, 'status') === 'in_progress' ||
|
||||
data_get($application_deployment_queue, 'status') === 'queued')
|
||||
<x-forms.button wire:click.prevent="cancel">Cancel deployment</x-forms.button>
|
||||
<x-forms.button wire:click.prevent="cancel">Cancel Deployment</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($isConfigurationChanged && !is_null($application->config_hash))
|
||||
<div class="font-bold text-warning">Configuration not applied to the running application. You need to
|
||||
redeploy.</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="">General configuration for your application.</div>
|
||||
<div>General configuration for your application.</div>
|
||||
<div class="flex flex-col gap-2 py-4">
|
||||
<div class="flex flex-col items-end gap-2 xl:flex-row">
|
||||
<x-forms.input id="application.name" label="Name" required />
|
||||
@@ -81,7 +85,6 @@
|
||||
@if ($application->dockerfile)
|
||||
<x-forms.textarea label="Dockerfile" id="application.dockerfile" rows="6"> </x-forms.textarea>
|
||||
@endif
|
||||
|
||||
<h3>Network</h3>
|
||||
<div class="flex flex-col gap-2 xl:flex-row">
|
||||
@if ($application->settings->is_static)
|
||||
@@ -93,6 +96,12 @@
|
||||
<x-forms.input placeholder="3000:3000" id="application.ports_mappings" label="Ports Mappings"
|
||||
helper="A comma separated list of ports you would like to map to the host system. Useful when you do not want to use domains.<br><br><span class='inline-block font-bold text-warning'>Example:</span><br>3000:3000,3002:3002<br><br>Rolling update is not supported if you have a port mapped to the host." />
|
||||
</div>
|
||||
@if ($labelsChanged)
|
||||
<x-forms.textarea label="Custom Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||
@else
|
||||
<x-forms.textarea label="Coolify Generated Labels" rows="15" id="customLabels"></x-forms.textarea>
|
||||
@endif
|
||||
<x-forms.button wire:click="resetDefaultLabels">Reset to Coolify Generated Labels</x-forms.button>
|
||||
</div>
|
||||
<h3>Advanced</h3>
|
||||
<div class="flex flex-col">
|
||||
|
||||
@@ -152,6 +152,9 @@
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
<div class="py-4 pb-10">Trademarks Policy: The respective trademarks mentioned here are owned by the
|
||||
respective
|
||||
companies, and use of them does not imply any affiliation or endorsement.</div>
|
||||
@endif
|
||||
@if ($current_step === 'servers')
|
||||
<ul class="pb-10 steps">
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
<div class="flex gap-2">
|
||||
@if ($application->required_fqdn)
|
||||
<x-forms.input required placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||
@else
|
||||
<x-forms.input placeholder="https://app.coolify.io" label="Domains"
|
||||
id="application.fqdn"></x-forms.input>
|
||||
id="application.fqdn" helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io, https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||
@endif
|
||||
<x-forms.input required
|
||||
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div x-init="$wire.checkStatus">
|
||||
<div x-init="$wire.checkStatus" wire:poll.2500ms='checkStatus'>
|
||||
<livewire:project.service.modal />
|
||||
<h1>Configuration</h1>
|
||||
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
||||
|
||||
@@ -46,9 +46,11 @@
|
||||
|
||||
</div>
|
||||
@if (Str::of(data_get($application, 'status'))->startsWith('running'))
|
||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
||||
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(data_get($application, 'status'))->startsWith('exited'))
|
||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(data_get($application, 'status'))->startsWith('restarting'))
|
||||
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
@@ -60,9 +62,11 @@
|
||||
<div class="text-xs text-gray-400 group-hover:text-white">{{ $database->description }}</div>
|
||||
</div>
|
||||
@if (Str::of(data_get($database, 'status'))->startsWith('running'))
|
||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
||||
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(data_get($database, 'status'))->startsWith('exited'))
|
||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(data_get($database, 'status'))->startsWith('restaring'))
|
||||
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
@@ -74,9 +78,9 @@
|
||||
<div class="text-xs text-gray-400 group-hover:text-white">{{ $service->description }}</div>
|
||||
</div>
|
||||
@if (Str::of(serviceStatus($service))->startsWith('running'))
|
||||
<div class="absolute bg-green-400 -top-1 -left-1 badge badge-xs"></div>
|
||||
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(serviceStatus($service))->startsWith('degraded'))
|
||||
<div class="absolute bg-yellow-400 -top-1 -left-1 badge badge-xs"></div>
|
||||
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
|
||||
@elseif (Str::of(serviceStatus($service))->startsWith('exited'))
|
||||
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
|
||||
@endif
|
||||
|
||||
@@ -18,7 +18,7 @@ if [ $EUID != 0 ]; then
|
||||
echo "Please run as root"
|
||||
exit
|
||||
fi
|
||||
if [ $OS_TYPE != "ubuntu" ] && [ $OS_TYPE != "debian" ]; then
|
||||
if [ $OS_TYPE != "ubuntu" ] && [ $OS_TYPE != "debian" ] && [ $OS_TYPE != "raspbian" ]; then
|
||||
echo "This script only supports Ubuntu and Debian for now."
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -21,9 +21,10 @@ function help {
|
||||
}
|
||||
|
||||
function setup:dev {
|
||||
docker exec coolify bash -c "php artisan key:generate"
|
||||
docker exec coolify bash -c "composer install"
|
||||
docker exec coolify bash -c "php artisan key:generate"
|
||||
docker exec coolify bash -c "php artisan migrate:fresh --seed"
|
||||
sudo chmod -R o+rwx .
|
||||
}
|
||||
function sync:v3 {
|
||||
if [ -z "$1" ]; then
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
"version": "3.12.36"
|
||||
},
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.86"
|
||||
"version": "4.0.0-beta.94"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user