Skip to content

Commit e039f18

Browse files
authored
Merge pull request #16 from coollabsio/next
Next
2 parents 35857de + 7aab446 commit e039f18

File tree

135 files changed

+2733
-1584
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+2733
-1584
lines changed

.env.development.example

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ APP_KEY=
66
APP_URL=http://localhost
77
APP_PORT=8000
88
APP_DEBUG=true
9-
SSH_MUX_ENABLED=false
9+
SSH_MUX_ENABLED=true
1010

1111
# PostgreSQL Database Configuration
1212
DB_DATABASE=coolify
@@ -19,11 +19,7 @@ DB_PORT=5432
1919
# Set to true to enable Ray
2020
RAY_ENABLED=false
2121
# Set custom ray port
22-
RAY_PORT=
23-
24-
# Clockwork Configuration
25-
CLOCKWORK_ENABLED=false
26-
CLOCKWORK_QUEUE_COLLECT=true
22+
# RAY_PORT=
2723

2824
# Enable Laravel Telescope for debugging
2925
TELESCOPE_ENABLED=false

.github/pull_request_template.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
1-
> Always use `next` branch as destination branch for PRs, not `main`
1+
## Submit Checklist (REMOVE THIS SECTION BEFORE SUBMITTING)
2+
- [ ] I have selected the `next` branch as the destination for my PR, not `main`.
3+
- [ ] I have listed all changes in the `Changes` section.
4+
- [ ] I have filled out the `Issues` section with the issue/discussion link(s) (if applicable).
5+
- [ ] I have tested my changes.
6+
- [ ] I have considered backwards compatibility.
7+
- [ ] I have removed this checklist and any unused sections.
8+
9+
## Changes
10+
-
11+
12+
## Issues
13+
- fix #

.github/workflows/remove-labels-and-assignees-on-close.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
github-token: ${{ secrets.GITHUB_TOKEN }}
1919
script: |
2020
const { owner, repo } = context.repo;
21-
21+
2222
async function processIssue(issueNumber) {
2323
try {
2424
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
@@ -65,11 +65,14 @@ jobs:
6565
}
6666
6767
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
68-
const { data: closedIssues } = await github.rest.search.issuesAndPullRequests({
69-
q: `repo:${owner}/${repo} is:issue is:closed linked:${context.payload.pull_request.number}`,
70-
per_page: 100
71-
});
72-
for (const issue of closedIssues.items) {
73-
await processIssue(issue.number);
68+
const pr = context.payload.pull_request;
69+
if (pr.body) {
70+
const issueReferences = pr.body.match(/#(\d+)/g);
71+
if (issueReferences) {
72+
for (const reference of issueReferences) {
73+
const issueNumber = parseInt(reference.substring(1));
74+
await processIssue(issueNumber);
75+
}
76+
}
7477
}
7578
}

app/Actions/Application/StopApplication.php

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,43 @@
22

33
namespace App\Actions\Application;
44

5+
use App\Actions\Server\CleanupDocker;
56
use App\Models\Application;
67
use Lorisleiva\Actions\Concerns\AsAction;
78

89
class StopApplication
910
{
1011
use AsAction;
1112

12-
public function handle(Application $application, bool $previewDeployments = false)
13+
public function handle(Application $application, bool $previewDeployments = false, bool $dockerCleanup = true)
1314
{
14-
if ($application->destination->server->isSwarm()) {
15-
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
16-
17-
return;
18-
}
19-
20-
$servers = collect([]);
21-
$servers->push($application->destination->server);
22-
$application->additional_servers->map(function ($server) use ($servers) {
23-
$servers->push($server);
24-
});
25-
foreach ($servers as $server) {
15+
try {
16+
$server = $application->destination->server;
2617
if (! $server->isFunctional()) {
2718
return 'Server is not functional';
2819
}
29-
if ($previewDeployments) {
30-
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
31-
} else {
32-
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
33-
}
34-
if ($containers->count() > 0) {
35-
foreach ($containers as $container) {
36-
$containerName = data_get($container, 'Names');
37-
if ($containerName) {
38-
instant_remote_process(command: ["docker stop --time=30 $containerName"], server: $server, throwError: false);
39-
instant_remote_process(command: ["docker rm $containerName"], server: $server, throwError: false);
40-
instant_remote_process(command: ["docker rm -f {$containerName}"], server: $server, throwError: false);
41-
}
42-
}
20+
ray('Stopping application: '.$application->name);
21+
22+
if ($server->isSwarm()) {
23+
instant_remote_process(["docker stack rm {$application->uuid}"], $server);
24+
25+
return;
4326
}
27+
28+
$containersToStop = $application->getContainersToStop($previewDeployments);
29+
$application->stopContainers($containersToStop, $server);
30+
4431
if ($application->build_pack === 'dockercompose') {
45-
// remove network
46-
$uuid = $application->uuid;
47-
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
48-
instant_remote_process(["docker network rm {$uuid}"], $server, false);
32+
$application->delete_connected_networks($application->uuid);
4933
}
34+
35+
if ($dockerCleanup) {
36+
CleanupDocker::dispatch($server, true);
37+
}
38+
} catch (\Exception $e) {
39+
ray($e->getMessage());
40+
41+
return $e->getMessage();
5042
}
5143
}
5244
}

app/Actions/CoolifyTask/RunRemoteProcess.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Support\Facades\DB;
1111
use Illuminate\Support\Facades\Process;
1212
use Spatie\Activitylog\Models\Activity;
13+
use App\Helpers\SshMultiplexingHelper;
1314

1415
class RunRemoteProcess
1516
{
@@ -137,7 +138,7 @@ protected function getCommand(): string
137138
$command = $this->activity->getExtraProperty('command');
138139
$server = Server::whereUuid($server_uuid)->firstOrFail();
139140

140-
return generateSshCommand($server, $command);
141+
return SshMultiplexingHelper::generateSshCommand($server, $command);
141142
}
142143

143144
protected function handleOutput(string $type, string $output)

app/Actions/Database/StopDatabase.php

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Actions\Database;
44

5+
use App\Actions\Server\CleanupDocker;
56
use App\Models\StandaloneClickhouse;
67
use App\Models\StandaloneDragonfly;
78
use App\Models\StandaloneKeydb;
@@ -10,25 +11,65 @@
1011
use App\Models\StandaloneMysql;
1112
use App\Models\StandalonePostgresql;
1213
use App\Models\StandaloneRedis;
14+
use Illuminate\Support\Facades\Process;
1315
use Lorisleiva\Actions\Concerns\AsAction;
1416

1517
class StopDatabase
1618
{
1719
use AsAction;
1820

19-
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
21+
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, bool $isDeleteOperation = false, bool $dockerCleanup = true)
2022
{
2123
$server = $database->destination->server;
2224
if (! $server->isFunctional()) {
2325
return 'Server is not functional';
2426
}
2527

26-
instant_remote_process(command: ["docker stop --time=30 $database->uuid"], server: $server, throwError: false);
27-
instant_remote_process(command: ["docker rm $database->uuid"], server: $server, throwError: false);
28-
instant_remote_process(command: ["docker rm -f $database->uuid"], server: $server, throwError: false);
28+
$this->stopContainer($database, $database->uuid, 300);
29+
if (! $isDeleteOperation) {
30+
if ($dockerCleanup) {
31+
CleanupDocker::dispatch($server, true);
32+
}
33+
}
2934

3035
if ($database->is_public) {
3136
StopDatabaseProxy::run($database);
3237
}
38+
39+
return 'Database stopped successfully';
40+
}
41+
42+
private function stopContainer($database, string $containerName, int $timeout = 300): void
43+
{
44+
$server = $database->destination->server;
45+
46+
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
47+
48+
$startTime = time();
49+
while ($process->running()) {
50+
if (time() - $startTime >= $timeout) {
51+
$this->forceStopContainer($containerName, $server);
52+
break;
53+
}
54+
usleep(100000);
55+
}
56+
57+
$this->removeContainer($containerName, $server);
58+
}
59+
60+
private function forceStopContainer(string $containerName, $server): void
61+
{
62+
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
63+
}
64+
65+
private function removeContainer(string $containerName, $server): void
66+
{
67+
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
68+
}
69+
70+
private function deleteConnectedNetworks($uuid, $server)
71+
{
72+
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
73+
instant_remote_process(["docker network rm {$uuid}"], $server, false);
3374
}
3475
}

app/Actions/Proxy/CheckProxy.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public function handle(Server $server, $fromUI = false)
2626
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
2727
return false;
2828
}
29-
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
29+
['uptime' => $uptime, 'error' => $error] = $server->validateConnection(false);
3030
if (! $uptime) {
3131
throw new \Exception($error);
3232
}

app/Actions/Service/DeleteService.php

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22

33
namespace App\Actions\Service;
44

5+
use App\Actions\Server\CleanupDocker;
56
use App\Models\Service;
67
use Lorisleiva\Actions\Concerns\AsAction;
78

89
class DeleteService
910
{
1011
use AsAction;
1112

12-
public function handle(Service $service)
13+
public function handle(Service $service, bool $deleteConfigurations, bool $deleteVolumes, bool $dockerCleanup, bool $deleteConnectedNetworks)
1314
{
1415
try {
1516
$server = data_get($service, 'server');
16-
if ($server->isFunctional()) {
17+
if ($deleteVolumes && $server->isFunctional()) {
1718
$storagesToDelete = collect([]);
1819

1920
$service->environment_variables()->delete();
@@ -33,13 +34,29 @@ public function handle(Service $service)
3334
foreach ($storagesToDelete as $storage) {
3435
$commands[] = "docker volume rm -f $storage->name";
3536
}
36-
$commands[] = "docker rm -f $service->uuid";
3737

38-
instant_remote_process($commands, $server, false);
38+
// Execute volume deletion first, this must be done first otherwise volumes will not be deleted.
39+
if (! empty($commands)) {
40+
foreach ($commands as $command) {
41+
$result = instant_remote_process([$command], $server, false);
42+
if ($result !== 0) {
43+
ray("Failed to execute: $command");
44+
}
45+
}
46+
}
47+
}
48+
49+
if ($deleteConnectedNetworks) {
50+
$service->delete_connected_networks($service->uuid);
3951
}
52+
53+
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
4054
} catch (\Exception $e) {
4155
throw new \Exception($e->getMessage());
4256
} finally {
57+
if ($deleteConfigurations) {
58+
$service->delete_configurations();
59+
}
4360
foreach ($service->applications()->get() as $application) {
4461
$application->forceDelete();
4562
}
@@ -50,6 +67,11 @@ public function handle(Service $service)
5067
$task->delete();
5168
}
5269
$service->tags()->detach();
70+
$service->forceDelete();
71+
72+
if ($dockerCleanup) {
73+
CleanupDocker::dispatch($server, true);
74+
}
5375
}
5476
}
5577
}

app/Actions/Service/StopService.php

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,35 @@
22

33
namespace App\Actions\Service;
44

5+
use App\Actions\Server\CleanupDocker;
56
use App\Models\Service;
67
use Lorisleiva\Actions\Concerns\AsAction;
78

89
class StopService
910
{
1011
use AsAction;
1112

12-
public function handle(Service $service)
13+
public function handle(Service $service, bool $isDeleteOperation = false, bool $dockerCleanup = true)
1314
{
1415
try {
1516
$server = $service->destination->server;
1617
if (! $server->isFunctional()) {
1718
return 'Server is not functional';
1819
}
19-
ray('Stopping service: '.$service->name);
20-
$applications = $service->applications()->get();
21-
foreach ($applications as $application) {
22-
if ($applications->count() < 6) {
23-
instant_remote_process(command: ["docker stop --time=10 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
24-
}
25-
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
26-
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
27-
$application->update(['status' => 'exited']);
28-
}
29-
$dbs = $service->databases()->get();
30-
foreach ($dbs as $db) {
31-
if ($dbs->count() < 6) {
3220

33-
instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
21+
$containersToStop = $service->getContainersToStop();
22+
$service->stopContainers($containersToStop, $server);
23+
24+
if (! $isDeleteOperation) {
25+
$service->delete_connected_networks($service->uuid);
26+
if ($dockerCleanup) {
27+
CleanupDocker::dispatch($server, true);
3428
}
35-
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
36-
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
37-
$db->update(['status' => 'exited']);
3829
}
39-
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server);
40-
instant_remote_process(["docker network rm {$service->uuid}"], $service->server);
4130
} catch (\Exception $e) {
4231
ray($e->getMessage());
4332

4433
return $e->getMessage();
4534
}
46-
4735
}
4836
}

app/Console/Kernel.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ protected function schedule(Schedule $schedule): void
4343
$schedule->command('uploads:clear')->everyTwoMinutes();
4444

4545
$schedule->command('telescope:prune')->daily();
46+
47+
$schedule->job(new PullHelperImageJob)->everyFiveMinutes()->onOneServer();
4648
} else {
4749
// Instance Jobs
4850
$schedule->command('horizon:snapshot')->everyFiveMinutes();
@@ -77,11 +79,11 @@ private function pull_images($schedule)
7779
}
7880
})->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
7981
}
80-
$schedule->job(new PullHelperImageJob($server))
81-
->cron($settings->update_check_frequency)
82-
->timezone($settings->instance_timezone)
83-
->onOneServer();
8482
}
83+
$schedule->job(new PullHelperImageJob)
84+
->cron($settings->update_check_frequency)
85+
->timezone($settings->instance_timezone)
86+
->onOneServer();
8587
}
8688

8789
private function schedule_updates($schedule)

0 commit comments

Comments
 (0)