Merge branch 'next' into env-vars
This commit is contained in:
@@ -1,16 +1,31 @@
|
|||||||
APP_NAME=Coolify-localhost
|
# Coolify Configuration
|
||||||
APP_ID=development
|
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
|
APP_NAME="Coolify Development"
|
||||||
|
APP_ID=development
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
APP_PORT=8000
|
APP_PORT=8000
|
||||||
|
APP_DEBUG=true
|
||||||
MUX_ENABLED=false
|
MUX_ENABLED=false
|
||||||
|
|
||||||
|
# Enable Laravel Telescope for debugging
|
||||||
|
TELESCOPE_ENABLED=false
|
||||||
|
|
||||||
|
# Selenium Driver URL for Dusk
|
||||||
DUSK_DRIVER_URL=http://selenium:4444
|
DUSK_DRIVER_URL=http://selenium:4444
|
||||||
|
|
||||||
## For Andras only
|
# PostgreSQL Database Configuration
|
||||||
# To purge cache
|
DB_DATABASE=coolify
|
||||||
|
DB_USERNAME=coolify
|
||||||
|
DB_PASSWORD=password
|
||||||
|
DB_HOST=host.docker.internal
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
#Set custom ray port
|
||||||
|
RAY_PORT=
|
||||||
|
|
||||||
|
# Special Keys for Andras
|
||||||
|
# For cache purging
|
||||||
BUNNY_API_KEY=
|
BUNNY_API_KEY=
|
||||||
# To upload assets
|
# For asset uploads
|
||||||
BUNNY_STORAGE_API_KEY=
|
BUNNY_STORAGE_API_KEY=
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
|
# Coolify Configuration
|
||||||
APP_ID=
|
APP_ID=
|
||||||
APP_NAME=Coolify
|
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
|
|
||||||
|
# PostgreSQL Database Configuration
|
||||||
|
DB_DATABASE=coolify
|
||||||
|
DB_USERNAME=
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
# Redis Configuration
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
# Pusher Configuration
|
||||||
PUSHER_APP_ID=
|
PUSHER_APP_ID=
|
||||||
PUSHER_APP_KEY=
|
PUSHER_APP_KEY=
|
||||||
PUSHER_APP_SECRET=
|
PUSHER_APP_SECRET=
|
||||||
|
189
CONTRIBUTING.md
Normal file
189
CONTRIBUTING.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
> "First, thanks for considering contributing to my project. It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
||||||
|
|
||||||
|
You can ask for guidance anytime on our [Discord server](https://coollabs.io/discord) in the `#contribute` channel.
|
||||||
|
|
||||||
|
|
||||||
|
## Code Contribution
|
||||||
|
|
||||||
|
## 1. Setup your development environment
|
||||||
|
|
||||||
|
Follow the steps below for your operating system:
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
1. Install `docker-ce`, Docker Desktop (or similar):
|
||||||
|
- Docker CE (recommended):
|
||||||
|
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)
|
||||||
|
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/)
|
||||||
|
- Make sure to choose the appropriate Linux distribution (e.g., Ubuntu) when following the Docker installation guide
|
||||||
|
- Install Docker Desktop (easier):
|
||||||
|
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)
|
||||||
|
- Ensure WSL2 backend is enabled in Docker Desktop settings
|
||||||
|
|
||||||
|
2. Install Spin:
|
||||||
|
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2)
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
1. Install Orbstack, Docker Desktop (or similar):
|
||||||
|
- Orbstack (recommended, as it is a faster and lighter alternative to Docker Desktop):
|
||||||
|
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation)
|
||||||
|
- Docker Desktop:
|
||||||
|
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||||
|
|
||||||
|
2. Install Spin:
|
||||||
|
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin)
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
1. Install Docker Engine, Docker Desktop (or similar):
|
||||||
|
- Docker Engine (recommended, as there is no VM overhead):
|
||||||
|
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/) for your Linux distribution
|
||||||
|
- Docker Desktop:
|
||||||
|
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||||
|
|
||||||
|
2. Install Spin:
|
||||||
|
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions)
|
||||||
|
|
||||||
|
|
||||||
|
## 2. Verify installation (optional)
|
||||||
|
|
||||||
|
After installing Docker (or Orbstack) and Spin, verify the installation:
|
||||||
|
|
||||||
|
1. Open a terminal or command prompt
|
||||||
|
2. Run the following commands:
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
spin --version
|
||||||
|
```
|
||||||
|
You should see version information for both Docker and Spin.
|
||||||
|
|
||||||
|
|
||||||
|
## 3. Fork the Coolify repository and setup your local repository
|
||||||
|
|
||||||
|
1. Fork the [Coolify](https://github.com/coollabsio/coolify) repository to your GitHub account.
|
||||||
|
|
||||||
|
2. Install a code editor on your machine (below are some popular choices, choose one):
|
||||||
|
|
||||||
|
- Visual Studio Code (recommended free):
|
||||||
|
- Windows/macOS/Linux: Download and install from [https://code.visualstudio.com/download](https://code.visualstudio.com/download)
|
||||||
|
|
||||||
|
- Cursor (recommended but paid for getting the full benefits):
|
||||||
|
- Windows/macOS/Linux: Download and install from [https://www.cursor.com/](https://www.cursor.com/)
|
||||||
|
|
||||||
|
- Zed (very fast code editor):
|
||||||
|
- macOS/Linux: Download and install from [https://zed.dev/download](https://zed.dev/download)
|
||||||
|
- Windows: Not available yet
|
||||||
|
|
||||||
|
3. Clone the Coolify Repository from your fork to your local machine
|
||||||
|
- Use `git clone` in the command line
|
||||||
|
- Use GitHub Desktop (recommended):
|
||||||
|
- Download and install from [https://desktop.github.com/](https://desktop.github.com/)
|
||||||
|
- Open GitHub Desktop and login with your GitHub account
|
||||||
|
- Click on `File` -> `Clone Repository` select `github.com` as the repository location, then select your forked Coolify repository, choose the local path and then click `Clone`
|
||||||
|
|
||||||
|
4. Open the cloned Coolify Repository in your chosen code editor.
|
||||||
|
|
||||||
|
|
||||||
|
## 4. Set up Environment Variables
|
||||||
|
|
||||||
|
1. In the Code Editor, locate the `.env.development.example` file in the root directory of your local Coolify repository.
|
||||||
|
|
||||||
|
2. Duplicate the `.env.development.example` file and rename the copy to `.env`.
|
||||||
|
|
||||||
|
3. Open the new `.env` file and review its contents. Adjust any environment variables as needed for your development setup.
|
||||||
|
|
||||||
|
4. If you encounter errors during database migrations, update the database connection settings in your `.env` file. Use the IP address or hostname of your PostgreSQL database container. You can find this information by running `docker ps` after executing `spin up`.
|
||||||
|
|
||||||
|
5. Save the changes to your `.env` file.
|
||||||
|
|
||||||
|
|
||||||
|
## 5. Start Coolify
|
||||||
|
|
||||||
|
1. Open a terminal in the local Coolify directory.
|
||||||
|
|
||||||
|
2. Run the following command in the terminal (leave that terminal open):
|
||||||
|
```
|
||||||
|
spin up
|
||||||
|
```
|
||||||
|
Note: You may see some errors, but don't worry; this is expected.
|
||||||
|
|
||||||
|
3. If you encounter permission errors, especially on macOS, use:
|
||||||
|
```
|
||||||
|
sudo spin up
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: If you change environment variables afterwards or anything seems broken, press Ctrl + C to stop the process and run `spin up` again.
|
||||||
|
|
||||||
|
|
||||||
|
## 6. Start Development
|
||||||
|
|
||||||
|
1. Access your Coolify instance:
|
||||||
|
- URL: `http://localhost:8000`
|
||||||
|
- Login: `test@example.com`
|
||||||
|
- Password: `password`
|
||||||
|
|
||||||
|
2. Additional development tools:
|
||||||
|
- Laravel Horizon (scheduler): `http://localhost:8000/horizon`
|
||||||
|
Note: Only accessible when logged in as root user
|
||||||
|
- Mailpit (email catcher): `http://localhost:8025`
|
||||||
|
- Telescope (debugging tool): `http://localhost:8000/telescope`
|
||||||
|
Note: Disabled by default (so the database is not overloaded), enable by adding the following environment variable to your `.env` file:
|
||||||
|
```env
|
||||||
|
TELESCOPE_ENABLED=true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 7. Development Notes
|
||||||
|
|
||||||
|
When working on Coolify, keep the following in mind:
|
||||||
|
|
||||||
|
1. **Database Migrations**: After switching branches or making changes to the database structure, always run migrations:
|
||||||
|
```bash
|
||||||
|
docker exec -it coolify php artisan migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Resetting Development Setup**: To reset your development setup to a clean database with default values:
|
||||||
|
```bash
|
||||||
|
docker exec -it coolify php artisan migrate:fresh --seed
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Troubleshooting**: If you encounter unexpected behavior, ensure your database is up-to-date with the latest migrations and if possible reset the development setup to eliminate any envrionement specific issues.
|
||||||
|
|
||||||
|
Remember, forgetting to migrate the database can cause problems, so make it a habit to run migrations after pulling changes or switching branches.
|
||||||
|
|
||||||
|
|
||||||
|
## 8. Contributing a New Service
|
||||||
|
|
||||||
|
To add a new service to Coolify, please refer to our documentation:
|
||||||
|
[Adding a New Service](https://coolify.io/docs/knowledge-base/add-a-service)
|
||||||
|
|
||||||
|
|
||||||
|
## 9. Create a Pull Request
|
||||||
|
|
||||||
|
1. After making changes or adding a new service:
|
||||||
|
- Commit your changes to your forked repository.
|
||||||
|
- Push the changes to your GitHub account.
|
||||||
|
|
||||||
|
2. Creating the Pull Request (PR):
|
||||||
|
- Navigate to the main Coolify repository on GitHub.
|
||||||
|
- Click the "Pull requests" tab.
|
||||||
|
- Click the green "New pull request" button.
|
||||||
|
- Choose your fork and branch as the compare branch.
|
||||||
|
- Click "Create pull request".
|
||||||
|
|
||||||
|
3. Filling out the PR details:
|
||||||
|
- Give your PR a descriptive title.
|
||||||
|
- In the description, explain the changes you've made.
|
||||||
|
- Reference any related issues by using keywords like "Fixes #123" or "Closes #456".
|
||||||
|
|
||||||
|
4. Important note:
|
||||||
|
Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
|
||||||
|
|
||||||
|
5. Submit your PR:
|
||||||
|
- Review your changes one last time.
|
||||||
|
- Click "Create pull request" to submit.
|
||||||
|
|
||||||
|
After submission, maintainers will review your PR and may request changes or provide feedback.
|
@@ -1,34 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
> "First, thanks for considering to contribute to my project.
|
|
||||||
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
|
||||||
|
|
||||||
You can ask for guidance anytime on our
|
|
||||||
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
|
||||||
|
|
||||||
## Code Contribution
|
|
||||||
|
|
||||||
### 1) Setup your development environment
|
|
||||||
|
|
||||||
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
|
||||||
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
|
||||||
|
|
||||||
### 2) Set your environment variables
|
|
||||||
|
|
||||||
- Copy [.env.development.example](./.env.development.example) to .env.
|
|
||||||
|
|
||||||
## 3) Start & setup Coolify
|
|
||||||
|
|
||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
|
||||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
|
||||||
|
|
||||||
### 4) Start development
|
|
||||||
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`
|
|
||||||
|
|
||||||
## New Service Contribution
|
|
||||||
Check out the docs [here](https://coolify.io/docs/knowledge-base/add-a-service).
|
|
||||||
|
|
@@ -19,14 +19,19 @@ class StopService
|
|||||||
ray('Stopping service: '.$service->name);
|
ray('Stopping service: '.$service->name);
|
||||||
$applications = $service->applications()->get();
|
$applications = $service->applications()->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
instant_remote_process(command: ["docker stop --time=30 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
if ($applications->count() < 6) {
|
||||||
|
instant_remote_process(command: ["docker stop --time=10 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
|
}
|
||||||
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
$application->update(['status' => 'exited']);
|
$application->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
$dbs = $service->databases()->get();
|
$dbs = $service->databases()->get();
|
||||||
foreach ($dbs as $db) {
|
foreach ($dbs as $db) {
|
||||||
instant_remote_process(command: ["docker stop --time=30 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
if ($dbs->count() < 6) {
|
||||||
|
|
||||||
|
instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
|
}
|
||||||
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||||
$db->update(['status' => 'exited']);
|
$db->update(['status' => 'exited']);
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
@@ -42,6 +43,17 @@ class CleanupStuckedResources extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
$applicationsPreviews = ApplicationPreview::get();
|
||||||
|
foreach ($applicationsPreviews as $applicationPreview) {
|
||||||
|
if (! data_get($applicationPreview, 'application')) {
|
||||||
|
echo "Deleting stuck application preview: {$applicationPreview->uuid}\n";
|
||||||
|
$applicationPreview->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
|
@@ -443,7 +443,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$fileStorage->deleteStorageOnServer();
|
$fileStorage->deleteStorageOnServer();
|
||||||
$fileStorage->saveStorageOnServer();
|
$fileStorage->saveStorageOnServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,7 +454,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||||
$this->save_environment_variables();
|
$this->save_environment_variables();
|
||||||
} else {
|
} else {
|
||||||
$composeFile = $this->application->oldParser(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||||
$this->save_environment_variables();
|
$this->save_environment_variables();
|
||||||
if (! is_null($this->env_filename)) {
|
if (! is_null($this->env_filename)) {
|
||||||
$services = collect($composeFile['services']);
|
$services = collect($composeFile['services']);
|
||||||
@@ -476,7 +475,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
$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 | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
// Build new container to limit downtime.
|
// Build new container to limit downtime.
|
||||||
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
|
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
|
||||||
@@ -506,9 +506,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
// TODO
|
// TODO
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", 'hidden' => true, 'ignore_errors' => true,
|
"docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true",
|
||||||
|
'hidden' => true,
|
||||||
|
'ignore_errors' => true,
|
||||||
], [
|
], [
|
||||||
"docker network connect {$networkId} coolify-proxy || true", 'hidden' => true, 'ignore_errors' => true,
|
"docker network connect {$networkId} coolify-proxy || true",
|
||||||
|
'hidden' => true,
|
||||||
|
'ignore_errors' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,7 +565,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
);
|
);
|
||||||
$this->write_deployment_configurations();
|
$this->write_deployment_configurations();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -744,7 +747,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name}).");
|
$this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name}).");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if ($this->application->docker_registry_image_tag) {
|
if ($this->application->docker_registry_image_tag) {
|
||||||
@@ -752,10 +756,14 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->application_deployment_queue->addLogEntry("Tagging and pushing image with {$this->application->docker_registry_image_tag} tag.");
|
$this->application_deployment_queue->addLogEntry("Tagging and pushing image with {$this->application->docker_registry_image_tag} tag.");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"),
|
||||||
|
'ignore_errors' => true,
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"),
|
||||||
|
'ignore_errors' => true,
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -852,14 +860,20 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
private function check_image_locally_or_remotely()
|
private function check_image_locally_or_remotely()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found',
|
"docker images -q {$this->production_image_name} 2>/dev/null",
|
||||||
|
'hidden' => true,
|
||||||
|
'save' => 'local_image_found',
|
||||||
]);
|
]);
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isEmpty() && $this->application->docker_registry_image_name) {
|
if (str($this->saved_outputs->get('local_image_found'))->isEmpty() && $this->application->docker_registry_image_name) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker pull {$this->production_image_name} 2>/dev/null", 'ignore_errors' => true, 'hidden' => true,
|
"docker pull {$this->production_image_name} 2>/dev/null",
|
||||||
|
'ignore_errors' => true,
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found',
|
"docker images -q {$this->production_image_name} 2>/dev/null",
|
||||||
|
'hidden' => true,
|
||||||
|
'save' => 'local_image_found',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -892,12 +906,14 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
||||||
|
$envs->push("COOLIFY_DOMAIN_URL={$this->preview->fqdn}");
|
||||||
}
|
}
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||||
$envs->push("COOLIFY_URL={$url}");
|
$envs->push("COOLIFY_URL={$url}");
|
||||||
|
$envs->push("COOLIFY_DOMAIN_FQDN={$url}");
|
||||||
}
|
}
|
||||||
if ($this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||||
}
|
}
|
||||||
@@ -942,13 +958,21 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
if ($this->application->compose_parsing_version === '3') {
|
||||||
|
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
||||||
|
} else {
|
||||||
|
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||||
$envs->push("COOLIFY_URL={$url}");
|
if ($this->application->compose_parsing_version === '3') {
|
||||||
|
$envs->push("COOLIFY_FQDN={$url}");
|
||||||
|
} else {
|
||||||
|
$envs->push("COOLIFY_URL={$url}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||||
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||||
}
|
}
|
||||||
@@ -1417,7 +1441,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
$importCommands, 'hidden' => true,
|
$importCommands,
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->create_workdir();
|
$this->create_workdir();
|
||||||
@@ -1627,7 +1652,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
// Check for custom HEALTHCHECK
|
// Check for custom HEALTHCHECK
|
||||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile_from_repo', 'ignore_errors' => true,
|
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||||
|
'hidden' => true,
|
||||||
|
'save' => 'dockerfile_from_repo',
|
||||||
|
'ignore_errors' => true,
|
||||||
]);
|
]);
|
||||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
|
$dockerfile = collect(str($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
|
||||||
$this->application->parseHealthcheckFromDockerfile($dockerfile);
|
$this->application->parseHealthcheckFromDockerfile($dockerfile);
|
||||||
@@ -1893,7 +1921,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry.");
|
$this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry.");
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "docker pull {$image}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "docker pull {$image}"),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1943,12 +1972,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||||
}
|
}
|
||||||
@@ -1956,10 +1987,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$base64_build_command = base64_encode($build_command);
|
$base64_build_command = base64_encode($build_command);
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||||
@@ -1973,10 +2006,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2013,10 +2048,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"),
|
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -2030,10 +2067,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$base64_build_command = base64_encode($build_command);
|
$base64_build_command = base64_encode($build_command);
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -2042,22 +2081,26 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||||
if ($this->force_rebuild) {
|
if ($this->force_rebuild) {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||||
} else {
|
} else {
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||||
|
'hidden' => true,
|
||||||
]);
|
]);
|
||||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||||
}
|
}
|
||||||
$base64_build_command = base64_encode($build_command);
|
$base64_build_command = base64_encode($build_command);
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||||
@@ -2071,10 +2114,12 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||||
|
'hidden' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2100,7 +2145,6 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stop_running_container(bool $force = false)
|
private function stop_running_container(bool $force = false)
|
||||||
@@ -2178,7 +2222,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
private function add_build_env_variables_to_dockerfile()
|
private function add_build_env_variables_to_dockerfile()
|
||||||
{
|
{
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile',
|
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||||
|
'hidden' => true,
|
||||||
|
'save' => 'dockerfile',
|
||||||
]);
|
]);
|
||||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
@@ -2223,7 +2269,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$exec = "docker exec {$containerName} {$cmd}";
|
$exec = "docker exec {$containerName} {$cmd}";
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
'command' => $exec, 'hidden' => true,
|
'command' => $exec,
|
||||||
|
'hidden' => true,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2250,7 +2297,9 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
try {
|
try {
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
'command' => $exec, 'hidden' => true, 'save' => 'post-deployment-command-output',
|
'command' => $exec,
|
||||||
|
'hidden' => true,
|
||||||
|
'save' => 'post-deployment-command-output',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
@@ -50,15 +50,6 @@ class Dashboard extends Component
|
|||||||
])->sortBy('id')->groupBy('server_name')->toArray();
|
])->sortBy('id')->groupBy('server_name')->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// public function getIptables()
|
|
||||||
// {
|
|
||||||
// $servers = Server::ownedByCurrentTeam()->get();
|
|
||||||
// foreach ($servers as $server) {
|
|
||||||
// checkRequiredCommands($server);
|
|
||||||
// $iptables = instant_remote_process(['docker run --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c "iptables -L -n | jc --iptables"'], $server);
|
|
||||||
// ray($iptables);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.dashboard');
|
return view('livewire.dashboard');
|
||||||
|
@@ -68,7 +68,7 @@ class Advanced extends Component
|
|||||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
$this->application->oldRawParser();
|
$this->application->oldRawParser();
|
||||||
} else {
|
} else {
|
||||||
$this->application->oldParser();
|
$this->application->parse();
|
||||||
}
|
}
|
||||||
$this->application->settings->save();
|
$this->application->settings->save();
|
||||||
$this->dispatch('success', 'Settings saved.');
|
$this->dispatch('success', 'Settings saved.');
|
||||||
|
@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Application;
|
|||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
@@ -132,7 +131,7 @@ class General extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->parsedServices = $this->application->oldParser();
|
$this->parsedServices = $this->application->parse();
|
||||||
if (is_null($this->parsedServices) || empty($this->parsedServices)) {
|
if (is_null($this->parsedServices) || empty($this->parsedServices)) {
|
||||||
$this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.');
|
$this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.');
|
||||||
|
|
||||||
@@ -197,7 +196,7 @@ class General extends Component
|
|||||||
|
|
||||||
// Must reload the application to get the latest database changes
|
// Must reload the application to get the latest database changes
|
||||||
// Why? Not sure, but it works.
|
// Why? Not sure, but it works.
|
||||||
$this->application->refresh();
|
// $this->application->refresh();
|
||||||
|
|
||||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation] = $this->application->loadComposeFile($isInit);
|
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation] = $this->application->loadComposeFile($isInit);
|
||||||
if (is_null($this->parsedServices)) {
|
if (is_null($this->parsedServices)) {
|
||||||
@@ -205,7 +204,7 @@ class General extends Component
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->application->oldParser();
|
$this->application->parse();
|
||||||
$this->dispatch('success', 'Docker compose file loaded.');
|
$this->dispatch('success', 'Docker compose file loaded.');
|
||||||
$this->dispatch('compose_loaded');
|
$this->dispatch('compose_loaded');
|
||||||
$this->dispatch('refreshStorages');
|
$this->dispatch('refreshStorages');
|
||||||
|
@@ -79,8 +79,15 @@ class Previews extends Component
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
|
if ($this->application->build_pack === 'dockercompose') {
|
||||||
|
$preview->generate_preview_fqdn_compose();
|
||||||
|
$this->application->refresh();
|
||||||
|
$this->dispatch('success', 'Domain generated.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
|
||||||
$url = Url::fromString($fqdn);
|
$url = Url::fromString($fqdn);
|
||||||
$template = $this->application->preview_url_template;
|
$template = $this->application->preview_url_template;
|
||||||
$host = $url->getHost();
|
$host = $url->getHost();
|
||||||
|
@@ -91,18 +91,16 @@ class Create extends Component
|
|||||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||||
$key = str()->before($value, '=');
|
$key = str()->before($value, '=');
|
||||||
$value = str(str()->after($value, '='));
|
$value = str(str()->after($value, '='));
|
||||||
$generatedValue = $value;
|
if ($value) {
|
||||||
if ($value->contains('SERVICE_')) {
|
EnvironmentVariable::create([
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
'key' => $key,
|
||||||
$generatedValue = generateEnvValue($command->value(), $service);
|
'value' => $value,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_build_time' => false,
|
||||||
|
'is_preview' => false,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
EnvironmentVariable::create([
|
|
||||||
'key' => $key,
|
|
||||||
'value' => $generatedValue,
|
|
||||||
'service_id' => $service->id,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$service->parse(isNew: true);
|
$service->parse(isNew: true);
|
||||||
|
@@ -76,6 +76,12 @@ class Configuration extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
GetContainersStatus::run($this->service->server);
|
GetContainersStatus::run($this->service->server);
|
||||||
|
$this->service->applications->each(function ($application) {
|
||||||
|
$application->refresh();
|
||||||
|
});
|
||||||
|
$this->service->databases->each(function ($database) {
|
||||||
|
$database->refresh();
|
||||||
|
});
|
||||||
$this->dispatch('$refresh');
|
$this->dispatch('$refresh');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
@@ -26,7 +26,7 @@ class All extends Component
|
|||||||
$this->containerNames = $this->containerNames->merge($this->resource->databases()->pluck('name'));
|
$this->containerNames = $this->containerNames->merge($this->resource->databases()->pluck('name'));
|
||||||
} elseif ($this->resource->type() == 'application') {
|
} elseif ($this->resource->type() == 'application') {
|
||||||
if ($this->resource->build_pack === 'dockercompose') {
|
if ($this->resource->build_pack === 'dockercompose') {
|
||||||
$parsed = $this->resource->oldParser();
|
$parsed = $this->resource->parse();
|
||||||
$containers = collect(data_get($parsed, 'services'))->keys();
|
$containers = collect(data_get($parsed, 'services'))->keys();
|
||||||
$this->containerNames = $containers;
|
$this->containerNames = $containers;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -3,8 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Jobs\ServerFilesFromServerJob;
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
@@ -104,6 +102,8 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
|
private static $parserVersion = '3';
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $appends = ['server_status'];
|
protected $appends = ['server_status'];
|
||||||
@@ -127,7 +127,7 @@ class Application extends BaseModel
|
|||||||
ApplicationSetting::create([
|
ApplicationSetting::create([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
]);
|
]);
|
||||||
$application->compose_parsing_version = '2';
|
$application->compose_parsing_version = self::$parserVersion;
|
||||||
$application->save();
|
$application->save();
|
||||||
});
|
});
|
||||||
static::forceDeleting(function ($application) {
|
static::forceDeleting(function ($application) {
|
||||||
@@ -140,6 +140,7 @@ class Application extends BaseModel
|
|||||||
$task->delete();
|
$task->delete();
|
||||||
}
|
}
|
||||||
$application->tags()->detach();
|
$application->tags()->detach();
|
||||||
|
$application->previews()->delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1085,544 +1086,10 @@ class Application extends BaseModel
|
|||||||
instant_remote_process($commands, $this->destination->server, false);
|
instant_remote_process($commands, $this->destination->server, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newParser(int $pull_request_id = 0, ?int $preview_id = null)
|
public function parse(int $pull_request_id = 0, ?int $preview_id = null)
|
||||||
{
|
|
||||||
$pullRequestId = $pull_request_id;
|
|
||||||
$isPullRequest = $pullRequestId == 0 ? false : true;
|
|
||||||
|
|
||||||
$uuid = data_get($this, 'uuid');
|
|
||||||
$server = data_get($this, 'destination.server');
|
|
||||||
$compose = data_get($this, 'docker_compose_raw');
|
|
||||||
try {
|
|
||||||
$yaml = Yaml::parse($compose);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$services = data_get($yaml, 'services', collect([]));
|
|
||||||
$topLevel = collect([
|
|
||||||
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
|
||||||
'networks' => collect(data_get($yaml, 'networks', [])),
|
|
||||||
'configs' => collect(data_get($yaml, 'configs', [])),
|
|
||||||
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// If there are predefined volumes, make sure they are not null
|
|
||||||
if ($topLevel->get('volumes')->count() > 0) {
|
|
||||||
$temp = collect([]);
|
|
||||||
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
|
||||||
if (is_null($volume)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$temp->put($volumeName, $volume);
|
|
||||||
}
|
|
||||||
$topLevel['volumes'] = $temp;
|
|
||||||
}
|
|
||||||
// Get the base docker network
|
|
||||||
$baseNetwork = collect([$uuid]);
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$baseNetwork = collect(["{$uuid}-{$pullRequestId}"]);
|
|
||||||
}
|
|
||||||
$parsedServices = collect([]);
|
|
||||||
$fileStorages = $this->fileStorages();
|
|
||||||
// Let's loop through the services
|
|
||||||
foreach ($services as $serviceName => $service) {
|
|
||||||
$image = data_get_str($service, 'image');
|
|
||||||
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
|
||||||
$logging = data_get($service, 'logging');
|
|
||||||
|
|
||||||
if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
|
|
||||||
$logging = generate_fluentd_configuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
$volumes = collect(data_get($service, 'volumes', []));
|
|
||||||
$networks = collect(data_get($service, 'networks', []));
|
|
||||||
$depends_on = collect(data_get($service, 'depends_on', []));
|
|
||||||
$labels = collect(data_get($service, 'labels', []));
|
|
||||||
$environment = collect(data_get($service, 'environment', []));
|
|
||||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
|
||||||
$environment = $environment->merge($buildArgs);
|
|
||||||
|
|
||||||
$baseName = generateApplicationContainerName(
|
|
||||||
application: $this,
|
|
||||||
pull_request_id: $pullRequestId
|
|
||||||
);
|
|
||||||
$containerName = "$serviceName-$baseName";
|
|
||||||
$volumesParsed = collect([]);
|
|
||||||
|
|
||||||
if ($volumes->count() > 0) {
|
|
||||||
foreach ($volumes as $index => $volume) {
|
|
||||||
$type = null;
|
|
||||||
$source = null;
|
|
||||||
$target = null;
|
|
||||||
$content = null;
|
|
||||||
$isDirectory = false;
|
|
||||||
if (is_string($volume)) {
|
|
||||||
$source = str($volume)->before(':');
|
|
||||||
$target = str($volume)->after(':')->beforeLast(':');
|
|
||||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
|
||||||
if (sourceIsLocal($source)) {
|
|
||||||
$type = str('bind');
|
|
||||||
if ($foundConfig) {
|
|
||||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
|
||||||
if ($contentNotNull_temp) {
|
|
||||||
$content = $contentNotNull_temp;
|
|
||||||
}
|
|
||||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
|
||||||
} else {
|
|
||||||
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
|
||||||
$isDirectory = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$type = str('volume');
|
|
||||||
}
|
|
||||||
} elseif (is_array($volume)) {
|
|
||||||
$type = data_get_str($volume, 'type');
|
|
||||||
$source = data_get_str($volume, 'source');
|
|
||||||
$target = data_get_str($volume, 'target');
|
|
||||||
$content = data_get($volume, 'content');
|
|
||||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
|
||||||
|
|
||||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
|
||||||
if ($foundConfig) {
|
|
||||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
|
||||||
if ($contentNotNull_temp) {
|
|
||||||
$content = $contentNotNull_temp;
|
|
||||||
}
|
|
||||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
|
||||||
} else {
|
|
||||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
|
||||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
|
||||||
$isDirectory = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($type->value() === 'bind') {
|
|
||||||
if ($source->value() === '/var/run/docker.sock') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
|
||||||
$source = replaceLocalSource($source, $mainDirectory);
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$source = $source."-pr-$pullRequestId";
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
! $this?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git
|
|
||||||
) {
|
|
||||||
LocalFileVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $this->id,
|
|
||||||
'resource_type' => get_class($this),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'fs_path' => $source,
|
|
||||||
'mount_path' => $target,
|
|
||||||
'content' => $content,
|
|
||||||
'is_directory' => $isDirectory,
|
|
||||||
'resource_id' => $this->id,
|
|
||||||
'resource_type' => get_class($this),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$volume = "$source:$target";
|
|
||||||
} elseif ($type->value() === 'volume') {
|
|
||||||
if ($topLevel->get('volumes')->has($source->value())) {
|
|
||||||
$temp = $topLevel->get('volumes')->get($source->value());
|
|
||||||
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$slugWithoutUuid = Str::slug($source, '-');
|
|
||||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$name = "{$name}-pr-$pullRequestId";
|
|
||||||
}
|
|
||||||
if (is_string($volume)) {
|
|
||||||
$source = str($volume)->before(':');
|
|
||||||
$target = str($volume)->after(':')->beforeLast(':');
|
|
||||||
$source = $name;
|
|
||||||
$volume = "$source:$target";
|
|
||||||
} elseif (is_array($volume)) {
|
|
||||||
data_set($volume, 'source', $name);
|
|
||||||
}
|
|
||||||
$topLevel->get('volumes')->put($name, [
|
|
||||||
'name' => $name,
|
|
||||||
]);
|
|
||||||
|
|
||||||
LocalPersistentVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $this->id,
|
|
||||||
'resource_type' => get_class($this),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => $name,
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $this->id,
|
|
||||||
'resource_type' => get_class($this),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch(new ServerFilesFromServerJob($this));
|
|
||||||
$volumesParsed->put($index, $volume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($depends_on?->count() > 0) {
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$newDependsOn = collect([]);
|
|
||||||
$depends_on->each(function ($dependency, $condition) use ($pullRequestId, $newDependsOn) {
|
|
||||||
if (is_numeric($condition)) {
|
|
||||||
$dependency = "$dependency-pr-$pullRequestId";
|
|
||||||
|
|
||||||
$newDependsOn->put($condition, $dependency);
|
|
||||||
} else {
|
|
||||||
$condition = "$condition-pr-$pullRequestId";
|
|
||||||
$newDependsOn->put($condition, $dependency);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$depends_on = $newDependsOn;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if ($topLevel->get('networks')?->count() > 0) {
|
|
||||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
|
||||||
if ($networkName === 'default') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ignore aliases
|
|
||||||
if ($network['aliases'] ?? false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
|
||||||
return $value == $networkName || $key == $networkName;
|
|
||||||
});
|
|
||||||
if (! $networkExists) {
|
|
||||||
$networks->put($networkName, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
|
||||||
return $value == $baseNetwork;
|
|
||||||
});
|
|
||||||
if (! $baseNetworkExists) {
|
|
||||||
foreach ($baseNetwork as $network) {
|
|
||||||
$topLevel->get('networks')->put($network, [
|
|
||||||
'name' => $network,
|
|
||||||
'external' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$networks_temp = collect();
|
|
||||||
|
|
||||||
foreach ($networks as $key => $network) {
|
|
||||||
if (gettype($network) === 'string') {
|
|
||||||
// networks:
|
|
||||||
// - appwrite
|
|
||||||
$networks_temp->put($network, null);
|
|
||||||
} elseif (gettype($network) === 'array') {
|
|
||||||
// networks:
|
|
||||||
// default:
|
|
||||||
// ipv4_address: 192.168.203.254
|
|
||||||
$networks_temp->put($key, $network);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($baseNetwork as $key => $network) {
|
|
||||||
$networks_temp->put($network, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_get($this, 'settings.connect_to_docker_network')) {
|
|
||||||
$network = $this->destination->network;
|
|
||||||
$networks_temp->put($network, null);
|
|
||||||
$topLevel->get('networks')->put($network, [
|
|
||||||
'name' => $network,
|
|
||||||
'external' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($environment as $key => $value) {
|
|
||||||
if (is_numeric($key)) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
// - SESSION_SECRET: 123
|
|
||||||
// - SESSION_SECRET:
|
|
||||||
$key = str(collect($value)->keys()->first());
|
|
||||||
$value = str(collect($value)->values()->first());
|
|
||||||
} else {
|
|
||||||
$variable = str($value);
|
|
||||||
if ($variable->contains('=')) {
|
|
||||||
// - SESSION_SECRET=123
|
|
||||||
// - SESSION_SECRET=
|
|
||||||
$key = $variable->before('=');
|
|
||||||
$value = $variable->after('=');
|
|
||||||
} else {
|
|
||||||
// - SESSION_SECRET
|
|
||||||
$key = $variable;
|
|
||||||
$value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SESSION_SECRET: 123
|
|
||||||
// SESSION_SECRET:
|
|
||||||
$key = str($key);
|
|
||||||
$value = str($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto generate FQDN and URL
|
|
||||||
if ($key->startsWith('SERVICE_FQDN') || $value->startsWith('$SERVICE_FQDN') || $value->startsWith('${SERVICE_FQDN')) {
|
|
||||||
if ($value->contains('SERVICE_FQDN')) {
|
|
||||||
$key = str(replaceVariables($value));
|
|
||||||
$value = null;
|
|
||||||
}
|
|
||||||
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
|
||||||
$fqdn = generateFqdn($server, "{$name->value()}-{$uuid}");
|
|
||||||
|
|
||||||
$url = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
|
||||||
$keyUrl = $key->replace('SERVICE_FQDN', 'SERVICE_URL');
|
|
||||||
// TODO: is this needed?
|
|
||||||
// if (substr_count($key->value(), '_') === 3) {
|
|
||||||
// // SERVICE_FQDN_UMAMI_1000
|
|
||||||
// $port = $key->afterLast('_');
|
|
||||||
// } else {
|
|
||||||
// // SERVICE_FQDN_UMAMI
|
|
||||||
// $port = null;
|
|
||||||
// }
|
|
||||||
// if ($port) {
|
|
||||||
// $fqdn = "$fqdn:$port";
|
|
||||||
// }
|
|
||||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable') {
|
|
||||||
$path = $value->value();
|
|
||||||
$fqdn = "$fqdn$path";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ray([
|
|
||||||
// 'key' => $key,
|
|
||||||
// 'value' => $fqdn,
|
|
||||||
// ]);
|
|
||||||
// ray($this->environment_variables()->where('key', $key)->where('application_id', $this->id)->first());
|
|
||||||
$this->environment_variables()->where('key', $key)->where('application_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $key,
|
|
||||||
'application_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $fqdn,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
$this->environment_variables()->where('key', $keyUrl)->where('application_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $keyUrl,
|
|
||||||
'application_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $url,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
} elseif ($value->startsWith('$')) {
|
|
||||||
// If the value is a variable then we will add it to Coolify's DB
|
|
||||||
|
|
||||||
// ${VARIABLE} will be VARIABLE instead
|
|
||||||
$value = str(replaceVariables($value));
|
|
||||||
if ($value->contains(':-')) {
|
|
||||||
$key = $value->before(':');
|
|
||||||
$defaultValue = $value->after(':-');
|
|
||||||
} elseif ($value->contains('-')) {
|
|
||||||
$key = $value->before('-');
|
|
||||||
$defaultValue = $value->after('-');
|
|
||||||
} elseif ($value->contains(':?')) {
|
|
||||||
$key = $value->before(':');
|
|
||||||
$defaultValue = $value->after(':?');
|
|
||||||
} elseif ($value->contains('?')) {
|
|
||||||
$key = $value->before('?');
|
|
||||||
$defaultValue = $value->after('?');
|
|
||||||
} else {
|
|
||||||
$key = $value;
|
|
||||||
$defaultValue = null;
|
|
||||||
}
|
|
||||||
$this->environment_variables()->where('key', $key)->where('application_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $key,
|
|
||||||
'application_id' => $this->id,
|
|
||||||
'is_preview' => false,
|
|
||||||
], [
|
|
||||||
'value' => $defaultValue,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$branch = $this->git_branch;
|
|
||||||
if ($pullRequestId !== 0) {
|
|
||||||
$branch = "pull/{$pullRequestId}/head";
|
|
||||||
}
|
|
||||||
if ($this->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
|
||||||
$environment->put('COOLIFY_BRANCH', $branch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
|
||||||
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
|
||||||
}
|
|
||||||
add_coolify_default_environment_variables($this->database, $environment, $this->environment_variables);
|
|
||||||
|
|
||||||
// Remove SERVICE_FQDN and SERVICE_URL from environment
|
|
||||||
$environment = $environment->filter(function ($value, $key) {
|
|
||||||
return ! str($key)->startsWith('SERVICE_FQDN') && ! str($key)->startsWith('SERVICE_URL');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Labels
|
|
||||||
$fqdns = collect([]);
|
|
||||||
|
|
||||||
$domains = collect(json_decode($this->docker_compose_domains)) ?? collect([]);
|
|
||||||
if ($domains->count() !== 0) {
|
|
||||||
$fqdns = data_get($domains, "$serviceName.domain");
|
|
||||||
if (! $fqdns) {
|
|
||||||
$fqdns = collect([]);
|
|
||||||
} else {
|
|
||||||
$fqdns = str($fqdns)->explode(',');
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$preview = $this->previews()->find($preview_id);
|
|
||||||
$docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
|
|
||||||
if ($docker_compose_domains->count() > 0) {
|
|
||||||
$found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
|
|
||||||
if ($found_fqdn) {
|
|
||||||
$fqdns = collect($found_fqdn);
|
|
||||||
} else {
|
|
||||||
$fqdns = collect([]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$fqdns = $fqdns->map(function ($fqdn) use ($pullRequestId) {
|
|
||||||
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pullRequestId);
|
|
||||||
$url = Url::fromString($fqdn);
|
|
||||||
$template = $this->preview_url_template;
|
|
||||||
$host = $url->getHost();
|
|
||||||
$schema = $url->getScheme();
|
|
||||||
$random = new Cuid2;
|
|
||||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
|
||||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
|
||||||
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
|
|
||||||
$preview_fqdn = "$schema://$preview_fqdn";
|
|
||||||
$preview->fqdn = $preview_fqdn;
|
|
||||||
$preview->save();
|
|
||||||
|
|
||||||
return $preview_fqdn;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$shouldGenerateLabelsExactly = $server->settings->generate_exact_labels;
|
|
||||||
if ($shouldGenerateLabelsExactly) {
|
|
||||||
switch ($server->proxyType()) {
|
|
||||||
case ProxyTypes::TRAEFIK->value:
|
|
||||||
$labels = $labels->merge(
|
|
||||||
fqdnLabelsForTraefik(
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
serviceLabels: $labels,
|
|
||||||
generate_unique_uuid: $this->build_pack === 'dockercompose',
|
|
||||||
image: $image,
|
|
||||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
|
||||||
is_gzip_enabled: $this->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ProxyTypes::CADDY->value:
|
|
||||||
$labels = $labels->merge(
|
|
||||||
fqdnLabelsForCaddy(
|
|
||||||
network: $this->destination->network,
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
serviceLabels: $labels,
|
|
||||||
image: $image,
|
|
||||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
|
||||||
is_gzip_enabled: $this->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$labels = $labels->merge(
|
|
||||||
fqdnLabelsForTraefik(
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
serviceLabels: $labels,
|
|
||||||
generate_unique_uuid: $this->build_pack === 'dockercompose',
|
|
||||||
image: $image,
|
|
||||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
|
||||||
is_gzip_enabled: $this->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
$labels = $labels->merge(
|
|
||||||
fqdnLabelsForCaddy(
|
|
||||||
network: $this->destination->network,
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
serviceLabels: $labels,
|
|
||||||
image: $image,
|
|
||||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
|
||||||
is_gzip_enabled: $this->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$defaultLabels = defaultLabels(
|
|
||||||
id: $this->id,
|
|
||||||
name: $containerName,
|
|
||||||
pull_request_id: $pullRequestId,
|
|
||||||
type: 'application');
|
|
||||||
$labels = $labels->merge($defaultLabels);
|
|
||||||
|
|
||||||
if ($labels->count() > 0 && $this->settings->is_container_label_escape_enabled) {
|
|
||||||
$labels = $labels->map(function ($value, $key) {
|
|
||||||
return escapeDollarSign($value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$payload = collect($service)->merge([
|
|
||||||
'restart' => $restart->value(),
|
|
||||||
'container_name' => $containerName,
|
|
||||||
'volumes' => $volumesParsed,
|
|
||||||
'networks' => $networks_temp,
|
|
||||||
'labels' => $labels,
|
|
||||||
'environment' => $environment,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($logging) {
|
|
||||||
$payload['logging'] = $logging;
|
|
||||||
}
|
|
||||||
if ($depends_on->count() > 0) {
|
|
||||||
$payload['depends_on'] = $depends_on;
|
|
||||||
}
|
|
||||||
if ($isPullRequest) {
|
|
||||||
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
|
||||||
}
|
|
||||||
$parsedServices->put($serviceName, $payload);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$topLevel->put('services', $parsedServices);
|
|
||||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
|
||||||
|
|
||||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
|
||||||
return array_search($key, $customOrder);
|
|
||||||
});
|
|
||||||
$this->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
|
||||||
data_forget($this, 'environment_variables');
|
|
||||||
data_forget($this, 'environment_variables_preview');
|
|
||||||
$this->save();
|
|
||||||
|
|
||||||
return $topLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function oldParser(int $pull_request_id = 0, ?int $preview_id = null)
|
|
||||||
{
|
{
|
||||||
if ($this->compose_parsing_version === '3') {
|
if ($this->compose_parsing_version === '3') {
|
||||||
return $this->newParser($pull_request_id, $preview_id);
|
return newParser($this, $pull_request_id, $preview_id);
|
||||||
} elseif ($this->docker_compose_raw) {
|
} elseif ($this->docker_compose_raw) {
|
||||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||||
} else {
|
} else {
|
||||||
@@ -1675,7 +1142,7 @@ class Application extends BaseModel
|
|||||||
if ($composeFileContent) {
|
if ($composeFileContent) {
|
||||||
$this->docker_compose_raw = $composeFileContent;
|
$this->docker_compose_raw = $composeFileContent;
|
||||||
$this->save();
|
$this->save();
|
||||||
$parsedServices = $this->oldParser();
|
$parsedServices = $this->parse();
|
||||||
if ($this->docker_compose_domains) {
|
if ($this->docker_compose_domains) {
|
||||||
$json = collect(json_decode($this->docker_compose_domains));
|
$json = collect(json_decode($this->docker_compose_domains));
|
||||||
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
|
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
|
||||||
|
@@ -12,9 +12,9 @@ class ApplicationPreview extends BaseModel
|
|||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
static::deleting(function ($preview) {
|
static::deleting(function ($preview) {
|
||||||
if ($preview->application->build_pack === 'dockercompose') {
|
if (data_get($preview, 'application.build_pack') === 'dockercompose') {
|
||||||
$server = $preview->application->destination->server;
|
$server = $preview->application->destination->server;
|
||||||
$composeFile = $preview->application->oldParser(pull_request_id: $preview->pull_request_id);
|
$composeFile = $preview->application->parse(pull_request_id: $preview->pull_request_id);
|
||||||
$volumes = data_get($composeFile, 'volumes');
|
$volumes = data_get($composeFile, 'volumes');
|
||||||
$networks = data_get($composeFile, 'networks');
|
$networks = data_get($composeFile, 'networks');
|
||||||
$networkKeys = collect($networks)->keys();
|
$networkKeys = collect($networks)->keys();
|
||||||
|
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\ProxyTypes;
|
|
||||||
use App\Jobs\ServerFilesFromServerJob;
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
@@ -42,6 +39,8 @@ class Service extends BaseModel
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
private static $parserVersion = '3';
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
protected $appends = ['server_status'];
|
protected $appends = ['server_status'];
|
||||||
@@ -49,7 +48,7 @@ class Service extends BaseModel
|
|||||||
protected static function booted()
|
protected static function booted()
|
||||||
{
|
{
|
||||||
static::created(function ($service) {
|
static::created(function ($service) {
|
||||||
$service->compose_parsing_version = '2';
|
$service->compose_parsing_version = self::$parserVersion;
|
||||||
$service->save();
|
$service->save();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -676,6 +675,32 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
$fields->put('GitLab', $data->toArray());
|
$fields->put('GitLab', $data->toArray());
|
||||||
break;
|
break;
|
||||||
|
case str($image)->contains('code-server'):
|
||||||
|
$data = collect([]);
|
||||||
|
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_PASSWORDCODESERVER')->first();
|
||||||
|
if ($password) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Password' => [
|
||||||
|
'key' => data_get($password, 'key'),
|
||||||
|
'value' => data_get($password, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$sudoPassword = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_SUDOCODESERVER')->first();
|
||||||
|
if ($sudoPassword) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Sudo Password' => [
|
||||||
|
'key' => data_get($sudoPassword, 'key'),
|
||||||
|
'value' => data_get($sudoPassword, 'value'),
|
||||||
|
'rules' => 'required',
|
||||||
|
'isPassword' => true,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$fields->put('Code Server', $data->toArray());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$databases = $this->databases()->get();
|
$databases = $this->databases()->get();
|
||||||
@@ -722,8 +747,8 @@ class Service extends BaseModel
|
|||||||
$fields->put('PostgreSQL', $data->toArray());
|
$fields->put('PostgreSQL', $data->toArray());
|
||||||
break;
|
break;
|
||||||
case str($image)->contains('mysql'):
|
case str($image)->contains('mysql'):
|
||||||
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS'];
|
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS', 'MYSQL_USER'];
|
||||||
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS'];
|
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS', 'MYSQL_PASSWORD'];
|
||||||
$rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT'];
|
$rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT'];
|
||||||
$dbNameVariables = ['MYSQL_DATABASE'];
|
$dbNameVariables = ['MYSQL_DATABASE'];
|
||||||
$mysql_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
$mysql_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||||
@@ -772,10 +797,10 @@ class Service extends BaseModel
|
|||||||
$fields->put('MySQL', $data->toArray());
|
$fields->put('MySQL', $data->toArray());
|
||||||
break;
|
break;
|
||||||
case str($image)->contains('mariadb'):
|
case str($image)->contains('mariadb'):
|
||||||
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER'];
|
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER', 'SERVICE_USER_MYSQL', 'MYSQL_USER'];
|
||||||
$passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS'];
|
$passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS', 'MYSQL_PASSWORD'];
|
||||||
$rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS'];
|
$rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS', 'MYSQL_ROOT_PASSWORD'];
|
||||||
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA'];
|
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA', 'MYSQL_DATABASE'];
|
||||||
$mariadb_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
$mariadb_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||||
$mariadb_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
$mariadb_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
||||||
$mariadb_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
|
$mariadb_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
|
||||||
@@ -822,6 +847,7 @@ class Service extends BaseModel
|
|||||||
}
|
}
|
||||||
$fields->put('MariaDB', $data->toArray());
|
$fields->put('MariaDB', $data->toArray());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -956,7 +982,8 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
public function environment_variables(): HasMany
|
public function environment_variables(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
|
||||||
|
return $this->hasMany(EnvironmentVariable::class)->orderByRaw("key LIKE 'SERVICE%' DESC, value ASC");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function environment_variables_preview(): HasMany
|
public function environment_variables_preview(): HasMany
|
||||||
@@ -992,531 +1019,10 @@ class Service extends BaseModel
|
|||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newParser()
|
|
||||||
{
|
|
||||||
return newParser($this);
|
|
||||||
|
|
||||||
$uuid = data_get($this, 'uuid');
|
|
||||||
$server = data_get($this, 'destination.server');
|
|
||||||
$compose = data_get($this, 'docker_compose_raw');
|
|
||||||
try {
|
|
||||||
$yaml = Yaml::parse($compose);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$allServices = get_service_templates();
|
|
||||||
$services = data_get($yaml, 'services', collect([]));
|
|
||||||
$topLevel = collect([
|
|
||||||
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
|
||||||
'networks' => collect(data_get($yaml, 'networks', [])),
|
|
||||||
'configs' => collect(data_get($yaml, 'configs', [])),
|
|
||||||
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
|
||||||
]);
|
|
||||||
// If there are predefined volumes, make sure they are not null
|
|
||||||
if ($topLevel->get('volumes')->count() > 0) {
|
|
||||||
$temp = collect([]);
|
|
||||||
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
|
||||||
if (is_null($volume)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$temp->put($volumeName, $volume);
|
|
||||||
}
|
|
||||||
$topLevel['volumes'] = $temp;
|
|
||||||
}
|
|
||||||
// Get the base docker network
|
|
||||||
$baseNetwork = collect([$uuid]);
|
|
||||||
$parsedServices = collect([]);
|
|
||||||
|
|
||||||
// Let's loop through the services
|
|
||||||
foreach ($services as $serviceName => $service) {
|
|
||||||
if ($serviceName === 'registry') {
|
|
||||||
$tempServiceName = 'docker-registry';
|
|
||||||
} else {
|
|
||||||
$tempServiceName = $serviceName;
|
|
||||||
}
|
|
||||||
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
|
||||||
$tempServiceName = 'glitchtip';
|
|
||||||
}
|
|
||||||
if ($serviceName === 'supabase-kong') {
|
|
||||||
$tempServiceName = 'supabase';
|
|
||||||
}
|
|
||||||
$serviceDefinition = data_get($allServices, $tempServiceName);
|
|
||||||
$predefinedPort = data_get($serviceDefinition, 'port');
|
|
||||||
if ($serviceName === 'plausible') {
|
|
||||||
$predefinedPort = '8000';
|
|
||||||
}
|
|
||||||
$image = data_get_str($service, 'image');
|
|
||||||
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
|
||||||
$logging = data_get($service, 'logging');
|
|
||||||
|
|
||||||
if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
|
|
||||||
$logging = generate_fluentd_configuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
$volumes = collect(data_get($service, 'volumes', []));
|
|
||||||
$networks = collect(data_get($service, 'networks', []));
|
|
||||||
$labels = collect(data_get($service, 'labels', []));
|
|
||||||
$environment = collect(data_get($service, 'environment', []));
|
|
||||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
|
||||||
$environment = $environment->merge($buildArgs);
|
|
||||||
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
|
||||||
|
|
||||||
$containerName = "$serviceName-{$this->uuid}";
|
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
|
||||||
$volumesParsed = collect([]);
|
|
||||||
|
|
||||||
if ($isDatabase) {
|
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
|
||||||
'name' => $serviceName,
|
|
||||||
'image' => $image,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$savedService = ServiceApplication::firstOrCreate([
|
|
||||||
'name' => $serviceName,
|
|
||||||
'image' => $image,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$fileStorages = $savedService->fileStorages();
|
|
||||||
|
|
||||||
// Check if image changed
|
|
||||||
if ($savedService->image !== $image) {
|
|
||||||
$savedService->image = $image;
|
|
||||||
$savedService->save();
|
|
||||||
}
|
|
||||||
if ($volumes->count() > 0) {
|
|
||||||
foreach ($volumes as $index => $volume) {
|
|
||||||
$type = null;
|
|
||||||
$source = null;
|
|
||||||
$target = null;
|
|
||||||
$content = null;
|
|
||||||
$isDirectory = false;
|
|
||||||
if (is_string($volume)) {
|
|
||||||
$source = str($volume)->before(':');
|
|
||||||
$target = str($volume)->after(':')->beforeLast(':');
|
|
||||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
|
||||||
if (sourceIsLocal($source)) {
|
|
||||||
$type = str('bind');
|
|
||||||
if ($foundConfig) {
|
|
||||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
|
||||||
if ($contentNotNull_temp) {
|
|
||||||
$content = $contentNotNull_temp;
|
|
||||||
}
|
|
||||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
|
||||||
} else {
|
|
||||||
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
|
||||||
$isDirectory = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$type = str('volume');
|
|
||||||
}
|
|
||||||
} elseif (is_array($volume)) {
|
|
||||||
$type = data_get_str($volume, 'type');
|
|
||||||
$source = data_get_str($volume, 'source');
|
|
||||||
$target = data_get_str($volume, 'target');
|
|
||||||
$content = data_get($volume, 'content');
|
|
||||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
|
||||||
|
|
||||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
|
||||||
if ($foundConfig) {
|
|
||||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
|
||||||
if ($contentNotNull_temp) {
|
|
||||||
$content = $contentNotNull_temp;
|
|
||||||
}
|
|
||||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
|
||||||
} else {
|
|
||||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
|
||||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
|
||||||
$isDirectory = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($type->value() === 'bind') {
|
|
||||||
if ($source->value() === '/var/run/docker.sock') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
|
||||||
$source = replaceLocalSource($source, $mainDirectory);
|
|
||||||
|
|
||||||
LocalFileVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'fs_path' => $source,
|
|
||||||
'mount_path' => $target,
|
|
||||||
'content' => $content,
|
|
||||||
'is_directory' => $isDirectory,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
$volume = "$source:$target";
|
|
||||||
} elseif ($type->value() === 'volume') {
|
|
||||||
if ($topLevel->get('volumes')->has($source->value())) {
|
|
||||||
$temp = $topLevel->get('volumes')->get($source->value());
|
|
||||||
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
|
||||||
return $volume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$slugWithoutUuid = Str::slug($source, '-');
|
|
||||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
|
||||||
if (is_string($volume)) {
|
|
||||||
$source = str($volume)->before(':');
|
|
||||||
$target = str($volume)->after(':')->beforeLast(':');
|
|
||||||
$source = $name;
|
|
||||||
$volume = "$source:$target";
|
|
||||||
} elseif (is_array($volume)) {
|
|
||||||
data_set($volume, 'source', $name);
|
|
||||||
}
|
|
||||||
$topLevel->get('volumes')->put($name, [
|
|
||||||
'name' => $name,
|
|
||||||
]);
|
|
||||||
|
|
||||||
LocalPersistentVolume::updateOrCreate(
|
|
||||||
[
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => $name,
|
|
||||||
'mount_path' => $target,
|
|
||||||
'resource_id' => $savedService->id,
|
|
||||||
'resource_type' => get_class($savedService),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch(new ServerFilesFromServerJob($savedService));
|
|
||||||
$volumesParsed->put($index, $volume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($topLevel->get('networks')?->count() > 0) {
|
|
||||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
|
||||||
if ($networkName === 'default') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ignore aliases
|
|
||||||
if ($network['aliases'] ?? false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
|
||||||
return $value == $networkName || $key == $networkName;
|
|
||||||
});
|
|
||||||
if (! $networkExists) {
|
|
||||||
$networks->put($networkName, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
|
||||||
return $value == $baseNetwork;
|
|
||||||
});
|
|
||||||
if (! $baseNetworkExists) {
|
|
||||||
foreach ($baseNetwork as $network) {
|
|
||||||
$topLevel->get('networks')->put($network, [
|
|
||||||
'name' => $network,
|
|
||||||
'external' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$networks_temp = collect();
|
|
||||||
|
|
||||||
foreach ($networks as $key => $network) {
|
|
||||||
if (gettype($network) === 'string') {
|
|
||||||
// networks:
|
|
||||||
// - appwrite
|
|
||||||
$networks_temp->put($network, null);
|
|
||||||
} elseif (gettype($network) === 'array') {
|
|
||||||
// networks:
|
|
||||||
// default:
|
|
||||||
// ipv4_address: 192.168.203.254
|
|
||||||
$networks_temp->put($key, $network);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($baseNetwork as $key => $network) {
|
|
||||||
$networks_temp->put($network, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert
|
|
||||||
// - SESSION_SECRET: 123 to - SESSION_SECRET=123
|
|
||||||
$convertedServiceVariables = collect([]);
|
|
||||||
foreach ($environment as $variableName => $variable) {
|
|
||||||
if (is_numeric($variableName)) {
|
|
||||||
if (is_array($variable)) {
|
|
||||||
$key = str(collect($variable)->keys()->first());
|
|
||||||
$value = str(collect($variable)->values()->first());
|
|
||||||
$variable = "$key=$value";
|
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
|
||||||
} elseif (is_string($variable)) {
|
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
|
||||||
}
|
|
||||||
} elseif (is_string($variableName)) {
|
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$environment = $convertedServiceVariables;
|
|
||||||
|
|
||||||
// filter magic environments
|
|
||||||
$magicEnvironments = $environment->filter(function ($value, $key) {
|
|
||||||
return str($key)->startsWith('SERVICE_FQDN') || str($key)->startsWith('SERVICE_URL') || str($value)->startsWith('SERVICE_FQDN') || str($value)->startsWith('SERVICE_URL');
|
|
||||||
});
|
|
||||||
if ($magicEnvironments->count() > 0) {
|
|
||||||
foreach ($magicEnvironments as $key => $value) {
|
|
||||||
$key = str($key);
|
|
||||||
$value = str($value);
|
|
||||||
$command = $key->after('SERVICE_')->beforeLast('_');
|
|
||||||
if ($command->value() === 'FQDN') {
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
} elseif ($command->value() === 'URL') {
|
|
||||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
|
||||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
|
||||||
}
|
|
||||||
if (! $isDatabase && ! $this->environment_variables()->where('key', $key)->where('service_id', $this->id)->first()) {
|
|
||||||
$savedService->fqdn = $value;
|
|
||||||
$savedService->save();
|
|
||||||
}
|
|
||||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $key,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $value,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($environment as $key => $value) {
|
|
||||||
if (is_numeric($key)) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
// - SESSION_SECRET: 123
|
|
||||||
// - SESSION_SECRET:
|
|
||||||
$key = str(collect($value)->keys()->first());
|
|
||||||
$value = str(collect($value)->values()->first());
|
|
||||||
} else {
|
|
||||||
$variable = str($value);
|
|
||||||
if ($variable->contains('=')) {
|
|
||||||
// - SESSION_SECRET=123
|
|
||||||
// - SESSION_SECRET=
|
|
||||||
$key = $variable->before('=');
|
|
||||||
$value = $variable->after('=');
|
|
||||||
} else {
|
|
||||||
// - SESSION_SECRET
|
|
||||||
$key = $variable;
|
|
||||||
$value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SESSION_SECRET: 123
|
|
||||||
// SESSION_SECRET:
|
|
||||||
$key = str($key);
|
|
||||||
$value = str($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto generate FQDN and URL
|
|
||||||
// environment:
|
|
||||||
// - SERVICE_FQDN_UMAMI=/umami
|
|
||||||
// - FQDN=$SERVICE_FQDN_UMAMI
|
|
||||||
// - URL=$SERVICE_URL_UMAMI
|
|
||||||
// - TEST=${TEST:-initial}
|
|
||||||
// - HARDCODED=stuff
|
|
||||||
|
|
||||||
if ($value->startsWith('$')) {
|
|
||||||
$value = str(replaceVariables($value));
|
|
||||||
if ($value->startsWith('SERVICE_')) {
|
|
||||||
// $value = SERVICE_FQDN_UMAMI
|
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
|
||||||
if ($command->value() === 'FQDN') {
|
|
||||||
if ($magicEnvironments->has($value->value())) {
|
|
||||||
$found = $magicEnvironments->get($value->value());
|
|
||||||
if ($found) {
|
|
||||||
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
|
||||||
if ($found) {
|
|
||||||
$value = $found->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($command->value() === 'URL') {
|
|
||||||
if ($magicEnvironments->has($value->value())) {
|
|
||||||
$found = $magicEnvironments->get($value->value());
|
|
||||||
if ($found) {
|
|
||||||
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
|
||||||
if ($found) {
|
|
||||||
$value = str($found->value)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
|
||||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$value = generateEnvValue($command, $this);
|
|
||||||
}
|
|
||||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $key,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $value,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
|
||||||
'key' => $key,
|
|
||||||
'service_id' => $this->id,
|
|
||||||
], [
|
|
||||||
'value' => $value,
|
|
||||||
'is_build_time' => false,
|
|
||||||
'is_preview' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
|
||||||
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
|
||||||
}
|
|
||||||
// Remove SERVICE_FQDN and SERVICE_URL from environment
|
|
||||||
$environment = $environment->filter(function ($value, $key) {
|
|
||||||
return ! str($key)->startsWith('SERVICE_FQDN') && ! str($key)->startsWith('SERVICE_URL');
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
if ($savedService->serviceType()) {
|
|
||||||
$fqdns = generateServiceSpecificFqdns($savedService);
|
|
||||||
} else {
|
|
||||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
|
||||||
}
|
|
||||||
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
|
||||||
$serviceLabels = $labels->merge($defaultLabels);
|
|
||||||
if (! $isDatabase && $fqdns->count() > 0) {
|
|
||||||
if ($fqdns) {
|
|
||||||
$shouldGenerateLabelsExactly = $this->server->settings->generate_exact_labels;
|
|
||||||
if ($shouldGenerateLabelsExactly) {
|
|
||||||
switch ($this->server->proxyType()) {
|
|
||||||
case ProxyTypes::TRAEFIK->value:
|
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
is_force_https_enabled: true,
|
|
||||||
serviceLabels: $serviceLabels,
|
|
||||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
|
||||||
service_name: $serviceName,
|
|
||||||
image: data_get($service, 'image')
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
case ProxyTypes::CADDY->value:
|
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
|
||||||
network: $this->destination->network,
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
is_force_https_enabled: true,
|
|
||||||
serviceLabels: $serviceLabels,
|
|
||||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
|
||||||
service_name: $serviceName,
|
|
||||||
image: data_get($service, 'image')
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
is_force_https_enabled: true,
|
|
||||||
serviceLabels: $serviceLabels,
|
|
||||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
|
||||||
service_name: $serviceName,
|
|
||||||
image: data_get($service, 'image')
|
|
||||||
));
|
|
||||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
|
||||||
network: $this->destination->network,
|
|
||||||
uuid: $this->uuid,
|
|
||||||
domains: $fqdns,
|
|
||||||
is_force_https_enabled: true,
|
|
||||||
serviceLabels: $serviceLabels,
|
|
||||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
|
||||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
|
||||||
service_name: $serviceName,
|
|
||||||
image: data_get($service, 'image')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$payload = collect($service)->merge([
|
|
||||||
'restart' => $restart->value(),
|
|
||||||
'container_name' => $containerName,
|
|
||||||
'volumes' => $volumesParsed,
|
|
||||||
'networks' => $networks_temp,
|
|
||||||
'labels' => $serviceLabels,
|
|
||||||
'environment' => $environment,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($logging) {
|
|
||||||
$payload['logging'] = $logging;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parsedServices->put($serviceName, $payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
$topLevel->put('services', $parsedServices);
|
|
||||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
|
||||||
|
|
||||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
|
||||||
return array_search($key, $customOrder);
|
|
||||||
});
|
|
||||||
$this->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
|
||||||
data_forget($this, 'environment_variables');
|
|
||||||
data_forget($this, 'environment_variables_preview');
|
|
||||||
$this->save();
|
|
||||||
|
|
||||||
return $topLevel;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
if ($this->compose_parsing_version === '3') {
|
if ($this->compose_parsing_version === '3') {
|
||||||
return $this->newParser();
|
return newParser($this);
|
||||||
} elseif ($this->docker_compose_raw) {
|
} elseif ($this->docker_compose_raw) {
|
||||||
return parseDockerComposeFile($this, $isNew);
|
return parseDockerComposeFile($this, $isNew);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -253,7 +253,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
|
|||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both')
|
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null)
|
||||||
{
|
{
|
||||||
$labels = collect([]);
|
$labels = collect([]);
|
||||||
if ($serviceLabels) {
|
if ($serviceLabels) {
|
||||||
@@ -272,6 +272,9 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
|||||||
if (is_null($port) && ! is_null($onlyPort)) {
|
if (is_null($port) && ! is_null($onlyPort)) {
|
||||||
$port = $onlyPort;
|
$port = $onlyPort;
|
||||||
}
|
}
|
||||||
|
if (is_null($port) && $predefinedPort) {
|
||||||
|
$port = $predefinedPort;
|
||||||
|
}
|
||||||
$labels->push("caddy_{$loop}={$schema}://{$host}");
|
$labels->push("caddy_{$loop}={$schema}://{$host}");
|
||||||
$labels->push("caddy_{$loop}.header=-Server");
|
$labels->push("caddy_{$loop}.header=-Server");
|
||||||
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
|
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
|
||||||
|
@@ -146,7 +146,6 @@ function generate_default_proxy_configuration(Server $server)
|
|||||||
'coolify.managed=true',
|
'coolify.managed=true',
|
||||||
];
|
];
|
||||||
$config = [
|
$config = [
|
||||||
'version' => '3.8',
|
|
||||||
'networks' => $array_of_networks->toArray(),
|
'networks' => $array_of_networks->toArray(),
|
||||||
'services' => [
|
'services' => [
|
||||||
'traefik' => [
|
'traefik' => [
|
||||||
|
@@ -852,7 +852,7 @@ function parseEnvVariable(Str|string $value)
|
|||||||
'port' => $port,
|
'port' => $port,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
function generateEnvValue(string $command, ?Service $service = null)
|
function generateEnvValue(string $command, Service|Application|null $service = null)
|
||||||
{
|
{
|
||||||
switch ($command) {
|
switch ($command) {
|
||||||
case 'PASSWORD':
|
case 'PASSWORD':
|
||||||
@@ -927,7 +927,8 @@ function generateEnvValue(string $command, ?Service $service = null)
|
|||||||
$generatedValue = $token->toString();
|
$generatedValue = $token->toString();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$generatedValue = Str::random(16);
|
// $generatedValue = Str::random(16);
|
||||||
|
$generatedValue = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1341,7 +1342,6 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
|||||||
$isDirectory = true;
|
$isDirectory = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if ($type?->value() === 'bind') {
|
if ($type?->value() === 'bind') {
|
||||||
if ($source->value() === '/var/run/docker.sock') {
|
if ($source->value() === '/var/run/docker.sock') {
|
||||||
@@ -1990,7 +1990,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
} else {
|
} else {
|
||||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||||
}
|
}
|
||||||
ray($savedService);
|
|
||||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||||
if (! $isDatabase && $fqdns->count() > 0) {
|
if (! $isDatabase && $fqdns->count() > 0) {
|
||||||
@@ -2076,7 +2075,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
updateCompose($savedService);
|
updateCompose($savedService);
|
||||||
|
|
||||||
return $service;
|
return $service;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$envs_from_coolify = $resource->environment_variables()->get();
|
$envs_from_coolify = $resource->environment_variables()->get();
|
||||||
@@ -2514,7 +2512,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($collectedPorts->count() > 0) {
|
if ($collectedPorts->count() > 0) {
|
||||||
// ray($collectedPorts->implode(','));
|
ray($collectedPorts->implode(','));
|
||||||
}
|
}
|
||||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||||
return $value == $definedNetwork;
|
return $value == $definedNetwork;
|
||||||
@@ -2864,13 +2862,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
return collect($finalServices);
|
return collect($finalServices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function newParser(Application|Service $resource, int $pull_request_id = 0)
|
function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
|
||||||
{
|
{
|
||||||
$isApplication = $resource instanceof Application;
|
$isApplication = $resource instanceof Application;
|
||||||
$isService = $resource instanceof Service;
|
$isService = $resource instanceof Service;
|
||||||
|
|
||||||
$uuid = data_get($resource, 'uuid');
|
$uuid = data_get($resource, 'uuid');
|
||||||
$compose = data_get($resource, 'docker_compose_raw');
|
$compose = data_get($resource, 'docker_compose_raw');
|
||||||
|
if (! $compose) {
|
||||||
|
return collect([]);
|
||||||
|
}
|
||||||
|
|
||||||
if ($isApplication) {
|
if ($isApplication) {
|
||||||
$nameOfId = 'application_id';
|
$nameOfId = 'application_id';
|
||||||
@@ -2883,13 +2884,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
$server = data_get($resource, 'server');
|
$server = data_get($resource, 'server');
|
||||||
$allServices = get_service_templates();
|
$allServices = get_service_templates();
|
||||||
} else {
|
} else {
|
||||||
return;
|
return collect([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$yaml = Yaml::parse($compose);
|
$yaml = Yaml::parse($compose);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return;
|
return collect([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$services = data_get($yaml, 'services', collect([]));
|
$services = data_get($yaml, 'services', collect([]));
|
||||||
@@ -2930,6 +2931,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
$depends_on = collect(data_get($service, 'depends_on', []));
|
$depends_on = collect(data_get($service, 'depends_on', []));
|
||||||
$labels = collect(data_get($service, 'labels', []));
|
$labels = collect(data_get($service, 'labels', []));
|
||||||
$environment = collect(data_get($service, 'environment', []));
|
$environment = collect(data_get($service, 'environment', []));
|
||||||
|
$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);
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||||
@@ -2941,6 +2943,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
pull_request_id: $pullRequestId
|
pull_request_id: $pullRequestId
|
||||||
);
|
);
|
||||||
$containerName = "$serviceName-$baseName";
|
$containerName = "$serviceName-$baseName";
|
||||||
|
$predefinedPort = null;
|
||||||
} elseif ($isService) {
|
} elseif ($isService) {
|
||||||
$containerName = "$serviceName-{$resource->uuid}";
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
|
|
||||||
@@ -2979,6 +2982,9 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
$savedService->save();
|
$savedService->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$originalResource = $isApplication ? $resource : $savedService;
|
||||||
|
|
||||||
if ($volumes->count() > 0) {
|
if ($volumes->count() > 0) {
|
||||||
foreach ($volumes as $index => $volume) {
|
foreach ($volumes as $index => $volume) {
|
||||||
$type = null;
|
$type = null;
|
||||||
@@ -3028,30 +3034,33 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
}
|
}
|
||||||
if ($type->value() === 'bind') {
|
if ($type->value() === 'bind') {
|
||||||
if ($source->value() === '/var/run/docker.sock') {
|
if ($source->value() === '/var/run/docker.sock') {
|
||||||
return $volume;
|
$volume = $source->value().':'.$target->value();
|
||||||
}
|
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
$volume = $source->value().':'.$target->value();
|
||||||
return $volume;
|
} else {
|
||||||
}
|
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
$source = replaceLocalSource($source, $mainDirectory);
|
||||||
$source = replaceLocalSource($source, $mainDirectory);
|
if ($isApplication && $isPullRequest) {
|
||||||
|
$source = $source."-pr-$pullRequestId";
|
||||||
|
}
|
||||||
|
|
||||||
LocalFileVolume::updateOrCreate(
|
LocalFileVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $originalResource->id,
|
||||||
'resource_type' => get_class($savedService),
|
'resource_type' => get_class($originalResource),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'fs_path' => $source,
|
'fs_path' => $source,
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'is_directory' => $isDirectory,
|
'is_directory' => $isDirectory,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $originalResource->id,
|
||||||
'resource_type' => get_class($savedService),
|
'resource_type' => get_class($originalResource),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$volume = "$source:$target";
|
$volume = "$source:$target";
|
||||||
|
}
|
||||||
} elseif ($type->value() === 'volume') {
|
} elseif ($type->value() === 'volume') {
|
||||||
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());
|
||||||
@@ -3064,6 +3073,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
}
|
}
|
||||||
$slugWithoutUuid = Str::slug($source, '-');
|
$slugWithoutUuid = Str::slug($source, '-');
|
||||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||||
|
|
||||||
|
if ($isApplication && $isPullRequest) {
|
||||||
|
$name = "{$name}-pr-$pullRequestId";
|
||||||
|
}
|
||||||
if (is_string($volume)) {
|
if (is_string($volume)) {
|
||||||
$source = str($volume)->before(':');
|
$source = str($volume)->before(':');
|
||||||
$target = str($volume)->after(':')->beforeLast(':');
|
$target = str($volume)->after(':')->beforeLast(':');
|
||||||
@@ -3079,21 +3092,38 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
LocalPersistentVolume::updateOrCreate(
|
LocalPersistentVolume::updateOrCreate(
|
||||||
[
|
[
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $originalResource->id,
|
||||||
'resource_type' => get_class($savedService),
|
'resource_type' => get_class($originalResource),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'mount_path' => $target,
|
'mount_path' => $target,
|
||||||
'resource_id' => $savedService->id,
|
'resource_id' => $originalResource->id,
|
||||||
'resource_type' => get_class($savedService),
|
'resource_type' => get_class($originalResource),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
dispatch(new ServerFilesFromServerJob($savedService));
|
dispatch(new ServerFilesFromServerJob($originalResource));
|
||||||
$volumesParsed->put($index, $volume);
|
$volumesParsed->put($index, $volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($depends_on?->count() > 0) {
|
||||||
|
if ($isApplication && $isPullRequest) {
|
||||||
|
$newDependsOn = collect([]);
|
||||||
|
$depends_on->each(function ($dependency, $condition) use ($pullRequestId, $newDependsOn) {
|
||||||
|
if (is_numeric($condition)) {
|
||||||
|
$dependency = "$dependency-pr-$pullRequestId";
|
||||||
|
|
||||||
|
$newDependsOn->put($condition, $dependency);
|
||||||
|
} else {
|
||||||
|
$condition = "$condition-pr-$pullRequestId";
|
||||||
|
$newDependsOn->put($condition, $dependency);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$depends_on = $newDependsOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($topLevel->get('networks')?->count() > 0) {
|
if ($topLevel->get('networks')?->count() > 0) {
|
||||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||||
if ($networkName === 'default') {
|
if ($networkName === 'default') {
|
||||||
@@ -3122,6 +3152,27 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect/create/update ports
|
||||||
|
$collectedPorts = collect([]);
|
||||||
|
if ($ports->count() > 0) {
|
||||||
|
foreach ($ports as $sport) {
|
||||||
|
if (is_string($sport) || is_numeric($sport)) {
|
||||||
|
$collectedPorts->push($sport);
|
||||||
|
}
|
||||||
|
if (is_array($sport)) {
|
||||||
|
$target = data_get($sport, 'target');
|
||||||
|
$published = data_get($sport, 'published');
|
||||||
|
$protocol = data_get($sport, 'protocol');
|
||||||
|
$collectedPorts->push("$target:$published/$protocol");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($isService) {
|
||||||
|
$originalResource->ports = $collectedPorts->implode(',');
|
||||||
|
$originalResource->save();
|
||||||
|
}
|
||||||
|
|
||||||
$networks_temp = collect();
|
$networks_temp = collect();
|
||||||
|
|
||||||
foreach ($networks as $key => $network) {
|
foreach ($networks as $key => $network) {
|
||||||
@@ -3150,27 +3201,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// convert environment variables a different format
|
// convert environment variables to one format
|
||||||
$convertedServiceVariables = collect([]);
|
$environment = convertComposeEnvironmentToArray($environment);
|
||||||
foreach ($environment as $variableName => $variable) {
|
|
||||||
if (is_numeric($variableName)) {
|
// Add Coolify defined environments
|
||||||
if (is_array($variable)) {
|
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
|
||||||
$key = str(collect($variable)->keys()->first());
|
|
||||||
$value = str(collect($variable)->values()->first());
|
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
|
||||||
$variable = "$key=$value";
|
return [$item['key'] => $item['value']];
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
});
|
||||||
} elseif (is_string($variable)) {
|
|
||||||
if (is_numeric($variableName)) {
|
// remove $environment from $allEnvironments
|
||||||
$convertedServiceVariables->put($variable, null);
|
$coolifyDefinedEnvironments = $allEnvironments->diffKeys($environment);
|
||||||
} else {
|
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif (is_string($variableName)) {
|
|
||||||
$convertedServiceVariables->put($variableName, $variable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$environment = $convertedServiceVariables;
|
|
||||||
|
|
||||||
// filter magic environments
|
// filter magic environments
|
||||||
$magicEnvironments = $environment->filter(function ($value, $key) {
|
$magicEnvironments = $environment->filter(function ($value, $key) {
|
||||||
@@ -3178,17 +3220,64 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
|
|
||||||
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
|
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
|
||||||
});
|
});
|
||||||
ray($magicEnvironments);
|
$normalEnvironments = $environment->diffKeys($magicEnvironments);
|
||||||
// TODO: go through all the magic environments and handle all kinds of cases, FQDN, URL, PASSWORD, USER, etc.
|
|
||||||
if ($magicEnvironments->count() > 0) {
|
if ($magicEnvironments->count() > 0) {
|
||||||
foreach ($magicEnvironments as $key => $value) {
|
foreach ($magicEnvironments as $key => $value) {
|
||||||
$key = str($key);
|
$key = str($key);
|
||||||
$value = str($value);
|
$value = str(replaceVariables(str($value)));
|
||||||
$command = $key->after('SERVICE_')->before('_');
|
$originalValue = $value;
|
||||||
if ($command->value() === 'FQDN') {
|
$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 ($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) {
|
if ($isApplication) {
|
||||||
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||||
|
|
||||||
} elseif ($isService) {
|
} elseif ($isService) {
|
||||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||||
}
|
}
|
||||||
@@ -3198,6 +3287,64 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
} else {
|
} else {
|
||||||
$value = $fqdn;
|
$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) {
|
||||||
|
$key = str($key);
|
||||||
|
$value = str($value);
|
||||||
|
if ($value->startsWith('$')) {
|
||||||
|
$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,
|
||||||
@@ -3209,106 +3356,177 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($environment as $key => $value) {
|
if ($isApplication) {
|
||||||
if (is_numeric($key)) {
|
$branch = $originalResource->git_branch;
|
||||||
if (is_array($value)) {
|
if ($pullRequestId !== 0) {
|
||||||
// - SESSION_SECRET: 123
|
$branch = "pull/{$pullRequestId}/head";
|
||||||
// - SESSION_SECRET:
|
|
||||||
$key = str(collect($value)->keys()->first());
|
|
||||||
$value = str(collect($value)->values()->first());
|
|
||||||
} else {
|
|
||||||
$variable = str($value);
|
|
||||||
if ($variable->contains('=')) {
|
|
||||||
// - SESSION_SECRET=123
|
|
||||||
// - SESSION_SECRET=
|
|
||||||
$key = $variable->before('=');
|
|
||||||
$value = $variable->after('=');
|
|
||||||
} else {
|
|
||||||
// - SESSION_SECRET
|
|
||||||
$key = $variable;
|
|
||||||
$value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SESSION_SECRET: 123
|
|
||||||
// SESSION_SECRET:
|
|
||||||
$key = str($key);
|
|
||||||
$value = str($value);
|
|
||||||
}
|
}
|
||||||
// Auto generate FQDN and URL
|
if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||||
// environment:
|
$environment->put('COOLIFY_BRANCH', $branch);
|
||||||
// - SERVICE_FQDN_UMAMI=/umami
|
|
||||||
// - FQDN=$SERVICE_FQDN_UMAMI
|
|
||||||
// - URL=$SERVICE_URL_UMAMI
|
|
||||||
// - TEST=${TEST:-initial}
|
|
||||||
// - HARDCODED=stuff
|
|
||||||
if ($value->startsWith('$')) {
|
|
||||||
$value = str(replaceVariables($value));
|
|
||||||
if ($value->startsWith('SERVICE_')) {
|
|
||||||
$command = $value->after('SERVICE_')->before('_');
|
|
||||||
if ($command->value() === 'FQDN') {
|
|
||||||
if ($magicEnvironments->has($value->value())) {
|
|
||||||
$found = $magicEnvironments->get($value->value());
|
|
||||||
if ($found) {
|
|
||||||
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
|
||||||
if ($found) {
|
|
||||||
$value = $found->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($command->value() === 'URL') {
|
|
||||||
if ($magicEnvironments->has($value->value())) {
|
|
||||||
$found = $magicEnvironments->get($value->value());
|
|
||||||
if ($found) {
|
|
||||||
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
|
||||||
if ($found) {
|
|
||||||
$value = $found->value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($isApplication) {
|
|
||||||
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
|
||||||
} elseif ($isService) {
|
|
||||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
|
||||||
}
|
|
||||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$value = generateEnvValue($command, $resource);
|
|
||||||
}
|
|
||||||
$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,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$payload = collect($service)->merge([
|
|
||||||
'restart' => $restart->value(),
|
|
||||||
'container_name' => $containerName,
|
|
||||||
'volumes' => $volumesParsed,
|
|
||||||
'networks' => $networks_temp,
|
|
||||||
'labels' => $labels,
|
|
||||||
'environment' => $environment,
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
// Add COOLIFY_CONTAINER_NAME to environment
|
||||||
|
if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||||
|
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isApplication) {
|
||||||
|
$domains = collect(json_decode($resource->docker_compose_domains)) ?? collect([]);
|
||||||
|
$fqdns = data_get($domains, "$serviceName.domain");
|
||||||
|
if ($fqdns) {
|
||||||
|
$fqdns = str($fqdns)->explode(',');
|
||||||
|
if ($isPullRequest) {
|
||||||
|
$preview = $resource->previews()->find($preview_id);
|
||||||
|
$docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
|
||||||
|
if ($docker_compose_domains->count() > 0) {
|
||||||
|
$found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
|
||||||
|
if ($found_fqdn) {
|
||||||
|
$fqdns = collect($found_fqdn);
|
||||||
|
} else {
|
||||||
|
$fqdns = collect([]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fqdns = $fqdns->map(function ($fqdn) use ($pullRequestId, $resource) {
|
||||||
|
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pullRequestId);
|
||||||
|
$url = Url::fromString($fqdn);
|
||||||
|
$template = $resource->preview_url_template;
|
||||||
|
$host = $url->getHost();
|
||||||
|
$schema = $url->getScheme();
|
||||||
|
$random = new Cuid2;
|
||||||
|
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||||
|
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||||
|
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
|
||||||
|
$preview_fqdn = "$schema://$preview_fqdn";
|
||||||
|
$preview->fqdn = $preview_fqdn;
|
||||||
|
$preview->save();
|
||||||
|
|
||||||
|
return $preview_fqdn;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$defaultLabels = defaultLabels(
|
||||||
|
id: $resource->id,
|
||||||
|
name: $containerName,
|
||||||
|
pull_request_id: $pullRequestId,
|
||||||
|
type: 'application'
|
||||||
|
);
|
||||||
|
} elseif ($isService) {
|
||||||
|
if ($savedService->serviceType()) {
|
||||||
|
$fqdns = generateServiceSpecificFqdns($savedService);
|
||||||
|
} else {
|
||||||
|
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||||
|
}
|
||||||
|
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||||
|
}
|
||||||
|
// Add COOLIFY_FQDN & COOLIFY_URL to environment
|
||||||
|
if (! $isDatabase && $fqdns?->count() > 0) {
|
||||||
|
$environment->put('COOLIFY_URL', $fqdns->implode(','));
|
||||||
|
|
||||||
|
$urls = $fqdns->map(function ($fqdn) {
|
||||||
|
return str($fqdn)->replace('http://', '')->replace('https://', '');
|
||||||
|
});
|
||||||
|
$environment->put('COOLIFY_FQDN', $urls->implode(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
$serviceLabels = $labels->merge($defaultLabels);
|
||||||
|
if (! $isDatabase && $fqdns?->count() > 0) {
|
||||||
|
if ($isApplication) {
|
||||||
|
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
||||||
|
$uuid = $resource->uuid;
|
||||||
|
$network = $resource->destination->network;
|
||||||
|
if ($isPullRequest) {
|
||||||
|
$uuid = "{$resource->uuid}-{$pullRequestId}";
|
||||||
|
}
|
||||||
|
if ($isPullRequest) {
|
||||||
|
$network = "{$resource->destination->network}-{$pullRequestId}";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$shouldGenerateLabelsExactly = $resource->server->settings->generate_exact_labels;
|
||||||
|
$uuid = $resource->uuid;
|
||||||
|
$network = $resource->destination->network;
|
||||||
|
}
|
||||||
|
if ($shouldGenerateLabelsExactly) {
|
||||||
|
switch ($server->proxyType()) {
|
||||||
|
case ProxyTypes::TRAEFIK->value:
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||||
|
uuid: $uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: $image
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
case ProxyTypes::CADDY->value:
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||||
|
network: $network,
|
||||||
|
uuid: $uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: $image,
|
||||||
|
predefinedPort: $predefinedPort
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||||
|
uuid: $uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: $image
|
||||||
|
));
|
||||||
|
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||||
|
network: $network,
|
||||||
|
uuid: $uuid,
|
||||||
|
domains: $fqdns,
|
||||||
|
is_force_https_enabled: true,
|
||||||
|
serviceLabels: $serviceLabels,
|
||||||
|
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||||
|
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||||
|
service_name: $serviceName,
|
||||||
|
image: $image,
|
||||||
|
predefinedPort: $predefinedPort
|
||||||
|
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($isService) {
|
||||||
|
if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) {
|
||||||
|
$savedService->update(['exclude_from_status' => true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_forget($service, 'volumes.*.content');
|
||||||
|
data_forget($service, 'volumes.*.isDirectory');
|
||||||
|
data_forget($service, 'volumes.*.is_directory');
|
||||||
|
data_forget($service, 'exclude_from_hc');
|
||||||
|
|
||||||
|
$payload = collect($service)->merge([
|
||||||
|
'container_name' => $containerName,
|
||||||
|
'restart' => $restart->value(),
|
||||||
|
'networks' => $networks_temp,
|
||||||
|
'labels' => $serviceLabels,
|
||||||
|
]);
|
||||||
|
if ($ports->count() > 0) {
|
||||||
|
$payload['ports'] = $ports;
|
||||||
|
}
|
||||||
|
if ($volumesParsed->count() > 0) {
|
||||||
|
$payload['volumes'] = $volumesParsed;
|
||||||
|
}
|
||||||
|
if ($environment->count() > 0 || $coolifyDefinedEnvironments->count() > 0) {
|
||||||
|
$payload['environment'] = $environment->merge($coolifyDefinedEnvironments);
|
||||||
|
}
|
||||||
if ($logging) {
|
if ($logging) {
|
||||||
$payload['logging'] = $logging;
|
$payload['logging'] = $logging;
|
||||||
}
|
}
|
||||||
@@ -3318,14 +3536,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
|||||||
if ($isApplication && $isPullRequest) {
|
if ($isApplication && $isPullRequest) {
|
||||||
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
||||||
}
|
}
|
||||||
|
|
||||||
$parsedServices->put($serviceName, $payload);
|
$parsedServices->put($serviceName, $payload);
|
||||||
}
|
}
|
||||||
|
ray($parsedServices);
|
||||||
$topLevel->put('services', $parsedServices);
|
$topLevel->put('services', $parsedServices);
|
||||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||||
|
|
||||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||||
return array_search($key, $customOrder);
|
return array_search($key, $customOrder);
|
||||||
});
|
});
|
||||||
|
|
||||||
$resource->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
$resource->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||||
data_forget($resource, 'environment_variables');
|
data_forget($resource, 'environment_variables');
|
||||||
data_forget($resource, 'environment_variables_preview');
|
data_forget($resource, 'environment_variables_preview');
|
||||||
@@ -3374,4 +3595,35 @@ function add_coolify_default_environment_variables(StandaloneRedis|StandalonePos
|
|||||||
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_PROJECT_NAME')->isEmpty()) {
|
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_PROJECT_NAME')->isEmpty()) {
|
||||||
$where_to_add->push("COOLIFY_PROJECT_NAME={$resource->project()->name}");
|
$where_to_add->push("COOLIFY_PROJECT_NAME={$resource->project()->name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertComposeEnvironmentToArray($environment)
|
||||||
|
{
|
||||||
|
$convertedServiceVariables = collect([]);
|
||||||
|
foreach ($environment as $variableName => $variableValue) {
|
||||||
|
if (is_array($variableValue)) {
|
||||||
|
$key = str(collect($variableValue)->keys()->first());
|
||||||
|
$value = str(collect($variableValue)->values()->first());
|
||||||
|
} elseif (is_string($variableValue)) {
|
||||||
|
if (str($variableValue)->contains('=')) {
|
||||||
|
$key = str($variableValue)->before('=');
|
||||||
|
$value = str($variableValue)->after('=');
|
||||||
|
} else {
|
||||||
|
if (is_numeric($variableName)) {
|
||||||
|
$key = str($variableValue);
|
||||||
|
$value = null;
|
||||||
|
} else {
|
||||||
|
$key = str($variableName);
|
||||||
|
if ($variableValue) {
|
||||||
|
$value = str($variableValue);
|
||||||
|
} else {
|
||||||
|
$value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$convertedServiceVariables->put($key->value(), $value?->value() ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $convertedServiceVariables;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,19 +13,18 @@ services:
|
|||||||
- /data/coolify/backups:/var/www/html/storage/app/backups
|
- /data/coolify/backups:/var/www/html/storage/app/backups
|
||||||
- /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance
|
- /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance
|
||||||
environment:
|
environment:
|
||||||
- PHP_MEMORY_LIMIT
|
|
||||||
- APP_ID
|
|
||||||
- APP_ENV=production
|
- APP_ENV=production
|
||||||
- APP_DEBUG
|
- APP_NAME=Coolify
|
||||||
- APP_NAME
|
- APP_ID
|
||||||
- APP_KEY
|
- APP_KEY
|
||||||
- APP_URL
|
- APP_URL
|
||||||
- DB_CONNECTION
|
- APP_DEBUG
|
||||||
- DB_HOST
|
|
||||||
- DB_PORT
|
|
||||||
- DB_DATABASE
|
- DB_DATABASE
|
||||||
- DB_USERNAME
|
- DB_USERNAME
|
||||||
- DB_PASSWORD
|
- DB_PASSWORD
|
||||||
|
- DB_HOST
|
||||||
|
- DB_PORT
|
||||||
|
- DB_CONNECTION
|
||||||
- QUEUE_CONNECTION
|
- QUEUE_CONNECTION
|
||||||
- REDIS_HOST
|
- REDIS_HOST
|
||||||
- REDIS_PASSWORD
|
- REDIS_PASSWORD
|
||||||
@@ -34,6 +33,7 @@ services:
|
|||||||
- HORIZON_BALANCE_MAX_SHIFT
|
- HORIZON_BALANCE_MAX_SHIFT
|
||||||
- HORIZON_BALANCE_COOLDOWN
|
- HORIZON_BALANCE_COOLDOWN
|
||||||
- SSL_MODE=off
|
- SSL_MODE=off
|
||||||
|
- PHP_MEMORY_LIMIT
|
||||||
- PHP_PM_CONTROL=dynamic
|
- PHP_PM_CONTROL=dynamic
|
||||||
- PHP_PM_START_SERVERS=1
|
- PHP_PM_START_SERVERS=1
|
||||||
- PHP_PM_MIN_SPARE_SERVERS=1
|
- PHP_PM_MIN_SPARE_SERVERS=1
|
||||||
@@ -83,18 +83,20 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
soketi:
|
||||||
|
condition: service_healthy
|
||||||
postgres:
|
postgres:
|
||||||
volumes:
|
volumes:
|
||||||
- coolify-db:/var/lib/postgresql/data
|
- coolify-db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: "${DB_USERNAME:-coolify}"
|
POSTGRES_USER: "${DB_USERNAME}"
|
||||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
[
|
[
|
||||||
"CMD-SHELL",
|
"CMD-SHELL",
|
||||||
"pg_isready -U ${DB_USERNAME:-coolify}",
|
"pg_isready -U ${DB_USERNAME}",
|
||||||
"-d",
|
"-d",
|
||||||
"${DB_DATABASE:-coolify}"
|
"${DB_DATABASE:-coolify}"
|
||||||
]
|
]
|
||||||
|
@@ -71,14 +71,14 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- coolify-db:/var/lib/postgresql/data
|
- coolify-db:/var/lib/postgresql/data
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: "${DB_USERNAME:-coolify}"
|
POSTGRES_USER: "${DB_USERNAME}"
|
||||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
[
|
[
|
||||||
"CMD-SHELL",
|
"CMD-SHELL",
|
||||||
"pg_isready -U ${DB_USERNAME:-coolify}",
|
"pg_isready -U ${DB_USERNAME}",
|
||||||
"-d",
|
"-d",
|
||||||
"${DB_DATABASE:-coolify}"
|
"${DB_DATABASE:-coolify}"
|
||||||
]
|
]
|
||||||
|
@@ -10,6 +10,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
- soketi
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:15-alpine
|
image: postgres:15-alpine
|
||||||
container_name: coolify-db
|
container_name: coolify-db
|
||||||
@@ -32,4 +33,4 @@ networks:
|
|||||||
coolify:
|
coolify:
|
||||||
name: coolify
|
name: coolify
|
||||||
driver: bridge
|
driver: bridge
|
||||||
external: true
|
external: false
|
||||||
|
@@ -4,9 +4,9 @@
|
|||||||
<h4>{{ data_get($resource, 'name', 'unknown') }}</h4>
|
<h4>{{ data_get($resource, 'name', 'unknown') }}</h4>
|
||||||
@endif --}}
|
@endif --}}
|
||||||
@if ($fileStorage->is_directory)
|
@if ($fileStorage->is_directory)
|
||||||
<h4 class="dark:text-white">Directory Mount</h4>
|
<h4 class="dark:text-white pt-4 border-t dark:border-coolgray-200">Directory Mount</h4>
|
||||||
@else
|
@else
|
||||||
<h4 class="dark:text-white">File Mount</h4>
|
<h4 class="dark:text-white pt-4 border-t dark:border-coolgray-200">File Mount</h4>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<x-forms.input label="Source Path" :value="$fileStorage->fs_path" readonly />
|
<x-forms.input label="Source Path" :value="$fileStorage->fs_path" readonly />
|
||||||
|
@@ -35,8 +35,6 @@
|
|||||||
<livewire:project.shared.storages.all :resource="$resource" />
|
<livewire:project.shared.storages.all :resource="$resource" />
|
||||||
@endif
|
@endif
|
||||||
@if ($fileStorage->count() > 0)
|
@if ($fileStorage->count() > 0)
|
||||||
|
|
||||||
<h3 class="mt-4 pt-2 border-t dark:border-coolgray-200">Mounts</h3>
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
@foreach ($fileStorage->sort() as $fileStorage)
|
@foreach ($fileStorage->sort() as $fileStorage)
|
||||||
<livewire:project.service.file-storage :fileStorage="$fileStorage"
|
<livewire:project.service.file-storage :fileStorage="$fileStorage"
|
||||||
|
@@ -72,7 +72,7 @@
|
|||||||
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
|
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
|
||||||
</div>
|
</div>
|
||||||
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
||||||
helper="DNS servers for validation FQDNs againts. A comma separated list of DNS servers."
|
helper="DNS servers to validate FQDNs against. A comma separated list of DNS servers."
|
||||||
placeholder="1.1.1.1,8.8.8.8" />
|
placeholder="1.1.1.1,8.8.8.8" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -6,11 +6,12 @@ set -e # Exit immediately if a command exits with a non-zero status
|
|||||||
#set -u # Treat unset variables as an error and exit
|
#set -u # Treat unset variables as an error and exit
|
||||||
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
|
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
|
||||||
|
|
||||||
VERSION="1.3.3"
|
VERSION="1.3.4"
|
||||||
DOCKER_VERSION="26.0"
|
DOCKER_VERSION="26.0"
|
||||||
|
|
||||||
CDN="https://cdn.coollabs.io/coolify"
|
CDN="https://cdn.coollabs.io/coolify"
|
||||||
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||||
|
ENV_FILE="/data/coolify/source/.env"
|
||||||
|
|
||||||
# Check if the OS is manjaro, if so, change it to arch
|
# Check if the OS is manjaro, if so, change it to arch
|
||||||
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
|
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
|
||||||
@@ -93,7 +94,10 @@ centos | fedora | rhel | ol | rocky | almalinux | amzn)
|
|||||||
if ! command -v dnf >/dev/null; then
|
if ! command -v dnf >/dev/null; then
|
||||||
yum install -y dnf >/dev/null
|
yum install -y dnf >/dev/null
|
||||||
fi
|
fi
|
||||||
dnf install -y curl wget git jq >/dev/null
|
if ! command -v curl >/dev/null; then
|
||||||
|
dnf install -y curl >/dev/null
|
||||||
|
fi
|
||||||
|
dnf install -y wget git jq >/dev/null
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
sles | opensuse-leap | opensuse-tumbleweed)
|
sles | opensuse-leap | opensuse-tumbleweed)
|
||||||
@@ -285,15 +289,23 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
|
|||||||
curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh
|
curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh
|
||||||
|
|
||||||
# Copy .env.example if .env does not exist
|
# Copy .env.example if .env does not exist
|
||||||
if [ ! -f /data/coolify/source/.env ]; then
|
if [ ! -f $ENV_FILE ]; then
|
||||||
cp /data/coolify/source/.env.production /data/coolify/source/.env
|
cp /data/coolify/source/.env.production $ENV_FILE
|
||||||
sed -i "s|APP_ID=.*|APP_ID=$(openssl rand -hex 16)|g" /data/coolify/source/.env
|
# Generate a secure APP_ID and APP_KEY
|
||||||
sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE"
|
||||||
sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||||
sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
|
||||||
sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
# Generate a secure Postgres DB username and password
|
||||||
sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE"
|
||||||
sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||||
|
|
||||||
|
# Generate a secure Redis password
|
||||||
|
sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||||
|
|
||||||
|
# Generate secure Pusher credentials
|
||||||
|
sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||||
|
sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||||
|
sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Merge .env and .env.production. New values will be added to .env
|
# Merge .env and .env.production. New values will be added to .env
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
## Do not modify this file. You will lose the ability to autoupdate!
|
## Do not modify this file. You will lose the ability to autoupdate!
|
||||||
|
|
||||||
VERSION="1.0.5"
|
VERSION="1.0.6"
|
||||||
CDN="https://cdn.coollabs.io/coolify"
|
CDN="https://cdn.coollabs.io/coolify"
|
||||||
|
|
||||||
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
||||||
@@ -25,7 +25,8 @@ if grep -q "PUSHER_APP_SECRET=$" /data/coolify/source/.env; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Make sure coolify network exists
|
# Make sure coolify network exists
|
||||||
docker network create --attachable coolify 2>/dev/null
|
# It is created when starting Coolify with docker compose
|
||||||
|
# docker network create --attachable coolify 2>/dev/null
|
||||||
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
|
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
|
||||||
|
|
||||||
if [ -f /data/coolify/source/docker-compose.custom.yml ]; then
|
if [ -f /data/coolify/source/docker-compose.custom.yml ]; then
|
||||||
|
@@ -8,6 +8,7 @@ services:
|
|||||||
activepieces:
|
activepieces:
|
||||||
image: "ghcr.io/activepieces/activepieces:latest"
|
image: "ghcr.io/activepieces/activepieces:latest"
|
||||||
environment:
|
environment:
|
||||||
|
- SERVICE_FQDN_ACTIVEPIECES
|
||||||
- AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
|
- AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
|
||||||
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
|
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
|
||||||
- AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
|
- AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
|
||||||
|
@@ -3,9 +3,6 @@ _APP_LOCALE=en
|
|||||||
_APP_OPTIONS_ABUSE=enabled
|
_APP_OPTIONS_ABUSE=enabled
|
||||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||||
_APP_OPENSSL_KEY_V1=
|
_APP_OPENSSL_KEY_V1=
|
||||||
_APP_DOMAIN=
|
|
||||||
_APP_DOMAIN_TARGET=
|
|
||||||
_APP_DOMAIN_FUNCTIONS=
|
|
||||||
_APP_CONSOLE_WHITELIST_ROOT=enabled
|
_APP_CONSOLE_WHITELIST_ROOT=enabled
|
||||||
_APP_CONSOLE_WHITELIST_EMAILS=
|
_APP_CONSOLE_WHITELIST_EMAILS=
|
||||||
_APP_CONSOLE_WHITELIST_IPS=
|
_APP_CONSOLE_WHITELIST_IPS=
|
||||||
@@ -28,9 +25,6 @@ _APP_REDIS_PASS=
|
|||||||
_APP_DB_HOST=appwrite-mariadb
|
_APP_DB_HOST=appwrite-mariadb
|
||||||
_APP_DB_PORT=3306
|
_APP_DB_PORT=3306
|
||||||
_APP_DB_SCHEMA=appwrite
|
_APP_DB_SCHEMA=appwrite
|
||||||
_APP_DB_USER=$SERVICE_USER_MYSQL
|
|
||||||
_APP_DB_PASS=$SERVICE_PASSWORD_MYSQL
|
|
||||||
_APP_DB_ROOT_PASS=$SERVICE_PASSWORD_ROOTMYSQL
|
|
||||||
_APP_SMTP_HOST=
|
_APP_SMTP_HOST=
|
||||||
_APP_SMTP_PORT=
|
_APP_SMTP_PORT=
|
||||||
_APP_SMTP_SECURE=
|
_APP_SMTP_SECURE=
|
||||||
@@ -72,7 +66,6 @@ _APP_FUNCTIONS_CPUS=0
|
|||||||
_APP_FUNCTIONS_MEMORY=0
|
_APP_FUNCTIONS_MEMORY=0
|
||||||
_APP_FUNCTIONS_MEMORY_SWAP=0
|
_APP_FUNCTIONS_MEMORY_SWAP=0
|
||||||
_APP_FUNCTIONS_RUNTIMES=node-20.0,php-8.2,python-3.11,ruby-3.2
|
_APP_FUNCTIONS_RUNTIMES=node-20.0,php-8.2,python-3.11,ruby-3.2
|
||||||
_APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
|
|
||||||
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
||||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
||||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
||||||
|
@@ -52,8 +52,9 @@ services:
|
|||||||
- _APP_DB_HOST
|
- _APP_DB_HOST
|
||||||
- _APP_DB_PORT
|
- _APP_DB_PORT
|
||||||
- _APP_DB_SCHEMA
|
- _APP_DB_SCHEMA
|
||||||
- _APP_DB_USER
|
- _APP_DB_USER=$SERVICE_USER_MARIADB
|
||||||
- _APP_DB_PASS
|
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
|
||||||
|
- _APP_DB_ROOT_PASS=$SERVICE_PASSWORD_MARIADBROOT
|
||||||
- _APP_SMTP_HOST
|
- _APP_SMTP_HOST
|
||||||
- _APP_SMTP_PORT
|
- _APP_SMTP_PORT
|
||||||
- _APP_SMTP_SECURE
|
- _APP_SMTP_SECURE
|
||||||
@@ -92,7 +93,7 @@ services:
|
|||||||
- _APP_FUNCTIONS_CPUS
|
- _APP_FUNCTIONS_CPUS
|
||||||
- _APP_FUNCTIONS_MEMORY
|
- _APP_FUNCTIONS_MEMORY
|
||||||
- _APP_FUNCTIONS_RUNTIMES
|
- _APP_FUNCTIONS_RUNTIMES
|
||||||
- _APP_EXECUTOR_SECRET
|
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
|
||||||
- _APP_EXECUTOR_HOST
|
- _APP_EXECUTOR_HOST
|
||||||
- _APP_LOGGING_PROVIDER
|
- _APP_LOGGING_PROVIDER
|
||||||
- _APP_LOGGING_CONFIG
|
- _APP_LOGGING_CONFIG
|
||||||
@@ -500,7 +501,7 @@ services:
|
|||||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||||
|
|
||||||
appwrite-worker-usage:
|
appwrite-worker-usage:
|
||||||
image: appwrite/appwrite:1.5.1
|
image: appwrite/appwrite:1.5
|
||||||
entrypoint: worker-usage
|
entrypoint: worker-usage
|
||||||
container_name: appwrite-worker-usage
|
container_name: appwrite-worker-usage
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
@@ -527,7 +528,7 @@ services:
|
|||||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||||
|
|
||||||
appwrite-worker-usage-dump:
|
appwrite-worker-usage-dump:
|
||||||
image: appwrite/appwrite:1.5.1
|
image: appwrite/appwrite:1.5
|
||||||
entrypoint: worker-usage-dump
|
entrypoint: worker-usage-dump
|
||||||
<<: *x-logging
|
<<: *x-logging
|
||||||
container_name: appwrite-worker-usage-dump
|
container_name: appwrite-worker-usage-dump
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
@@ -9,74 +10,66 @@ use Illuminate\Support\Collection;
|
|||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
ray()->clearAll();
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$this->applicationComposeFile = [
|
$this->applicationYaml = '
|
||||||
'version' => '3.8',
|
version: "3.8"
|
||||||
'services' => [
|
services:
|
||||||
'app' => [
|
app:
|
||||||
'image' => 'nginx',
|
image: nginx
|
||||||
'environment' => [
|
environment:
|
||||||
'SERVICE_FQDN_APP' => '/app',
|
SERVICE_FQDN_APP: /app
|
||||||
'APP_KEY' => 'base64',
|
APP_KEY: base64
|
||||||
'APP_DEBUG' => '${APP_DEBUG:-false}',
|
APP_DEBUG: "${APP_DEBUG:-false}"
|
||||||
'APP_URL' => '$SERVICE_FQDN_APP',
|
APP_URL: $SERVICE_FQDN_APP
|
||||||
],
|
volumes:
|
||||||
'volumes' => [
|
- "./nginx:/etc/nginx"
|
||||||
'./nginx:/etc/nginx',
|
- "data:/var/www/html"
|
||||||
'data:/var/www/html',
|
depends_on:
|
||||||
],
|
- db
|
||||||
'depends_on' => [
|
db:
|
||||||
'db',
|
image: postgres
|
||||||
],
|
environment:
|
||||||
],
|
POSTGRES_USER: "${POSTGRES_USER:-postgres}"
|
||||||
'db' => [
|
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-postgres}"
|
||||||
'image' => 'postgres',
|
volumes:
|
||||||
'environment' => [
|
- "dbdata:/var/lib/postgresql/data"
|
||||||
'POSTGRES_USER' => '${POSTGRES_USER:-postgres}',
|
healthcheck:
|
||||||
'POSTGRES_PASSWORD' => '${POSTGRES_PASSWORD:-postgres}',
|
test:
|
||||||
],
|
- CMD
|
||||||
'volumes' => [
|
- pg_isready
|
||||||
'dbdata:/var/lib/postgresql/data',
|
- "-U"
|
||||||
],
|
- "postgres"
|
||||||
'healthcheck' => [
|
interval: 2s
|
||||||
'test' => ['CMD', 'pg_isready', '-U', 'postgres'],
|
timeout: 10s
|
||||||
'interval' => '2s',
|
retries: 10
|
||||||
'timeout' => '10s',
|
depends_on:
|
||||||
'retries' => 10,
|
app:
|
||||||
],
|
condition: service_healthy
|
||||||
'depends_on' => [
|
networks:
|
||||||
'app' => [
|
default:
|
||||||
'condition' => 'service_healthy',
|
name: something
|
||||||
],
|
external: true
|
||||||
],
|
noinet:
|
||||||
|
driver: bridge
|
||||||
|
internal: true';
|
||||||
|
|
||||||
],
|
$this->applicationComposeFileString = Yaml::parse($this->applicationYaml);
|
||||||
|
|
||||||
],
|
|
||||||
'networks' => [
|
|
||||||
'default' => [
|
|
||||||
'name' => 'something',
|
|
||||||
'external' => true,
|
|
||||||
],
|
|
||||||
'noinet' => [
|
|
||||||
'driver' => 'bridge',
|
|
||||||
'internal' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$this->applicationComposeFileString = Yaml::dump($this->applicationComposeFile, 10, 2);
|
|
||||||
$this->jsonapplicationComposeFile = json_encode($this->applicationComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
$this->application = Application::create([
|
$this->application = Application::create([
|
||||||
'name' => 'Application for tests',
|
'name' => 'Application for tests',
|
||||||
'uuid' => 'bcoowoookw0co4cok4sgc4k8',
|
'docker_compose_domains' => json_encode([
|
||||||
|
'app' => [
|
||||||
|
'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io',
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||||
|
'uuid' => 'bcoowoookw0co4cok4sgc4k8s',
|
||||||
'repository_project_id' => 603035348,
|
'repository_project_id' => 603035348,
|
||||||
'git_repository' => 'coollabsio/coolify-examples',
|
'git_repository' => 'coollabsio/coolify-examples',
|
||||||
'git_branch' => 'main',
|
'git_branch' => 'main',
|
||||||
'base_directory' => '/docker-compose-test',
|
'base_directory' => '/docker-compose-test',
|
||||||
'docker_compose_location' => 'docker-compose.yml',
|
'docker_compose_location' => 'docker-compose.yml',
|
||||||
'docker_compose_raw' => $this->applicationComposeFileString,
|
'docker_compose_raw' => $this->applicationYaml,
|
||||||
'build_pack' => 'dockercompose',
|
'build_pack' => 'dockercompose',
|
||||||
'ports_exposes' => '3000',
|
'ports_exposes' => '3000',
|
||||||
'environment_id' => 1,
|
'environment_id' => 1,
|
||||||
@@ -85,100 +78,84 @@ beforeEach(function () {
|
|||||||
'source_id' => 1,
|
'source_id' => 1,
|
||||||
'source_type' => GithubApp::class,
|
'source_type' => GithubApp::class,
|
||||||
]);
|
]);
|
||||||
|
$this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']);
|
||||||
|
$this->applicationPreview = ApplicationPreview::create([
|
||||||
|
'git_type' => 'github',
|
||||||
|
'application_id' => $this->application->id,
|
||||||
|
'pull_request_id' => 1,
|
||||||
|
'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1',
|
||||||
|
]);
|
||||||
|
$this->serviceYaml = '
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
activepieces:
|
||||||
|
image: ghcr.io/activepieces/activepieces:latest
|
||||||
|
environment:
|
||||||
|
- SERVICE_FQDN_ACTIVEPIECES
|
||||||
|
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
|
||||||
|
- AP_EXECUTION_MODE=UNSANDBOXED
|
||||||
|
- AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
|
||||||
|
- AP_TEST=${AP_TEST:-test}
|
||||||
|
volumes:
|
||||||
|
- "dbdata:/var/lib/postgresql/data"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
activepieces2:
|
||||||
|
image: ghcr.io/activepieces/activepieces:latest
|
||||||
|
environment:
|
||||||
|
TEST: $SERVICE_FQDN_ACTIVEPIECES
|
||||||
|
volumes:
|
||||||
|
- "dbdata:/var/lib/postgresql/data"
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: activepieces
|
||||||
|
POSTGRES_USER: $SERVICE_USER_POSTGRES
|
||||||
|
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
||||||
|
volumes:
|
||||||
|
- "dbdata:/var/lib/postgresql/data"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- pg_isready
|
||||||
|
- "-U"
|
||||||
|
- "postgres"
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
redis:
|
||||||
|
image: redis:latest
|
||||||
|
volumes:
|
||||||
|
- "redis_data:/data"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- redis-cli
|
||||||
|
- ping
|
||||||
|
interval: 2s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
volumes:
|
||||||
|
dbdata:
|
||||||
|
redis_data:
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: something
|
||||||
|
external: true
|
||||||
|
noinet:
|
||||||
|
driver: bridge
|
||||||
|
internal: true';
|
||||||
|
|
||||||
$this->serviceComposeFile = [
|
$this->serviceComposeFileString = Yaml::parse($this->serviceYaml);
|
||||||
'services' => [
|
|
||||||
'activepieces' => [
|
|
||||||
'image' => 'ghcr.io/activepieces/activepieces:latest',
|
|
||||||
'environment' => [
|
|
||||||
'SERVICE_FQDN_ACTIVEPIECES_80' => '/app',
|
|
||||||
'AP_API_KEY' => '$SERVICE_PASSWORD_64_APIKEY',
|
|
||||||
'AP_ENCRYPTION_KEY' => '$SERVICE_PASSWORD_ENCRYPTIONKEY',
|
|
||||||
'AP_ENGINE_EXECUTABLE_PATH' => 'dist/packages/engine/main.js',
|
|
||||||
'AP_ENVIRONMENT' => 'prod',
|
|
||||||
'AP_EXECUTION_MODE' => 'UNSANDBOXED',
|
|
||||||
'AP_FRONTEND_URL' => '$SERVICE_FQDN_ACTIVEPIECES',
|
|
||||||
'AP_JWT_SECRET' => '$SERVICE_PASSWORD_64_JWT',
|
|
||||||
'AP_POSTGRES_DATABASE' => 'activepieces',
|
|
||||||
'AP_POSTGRES_HOST' => 'postgres',
|
|
||||||
'AP_POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
|
||||||
'AP_POSTGRES_PORT' => '5432',
|
|
||||||
'AP_POSTGRES_USERNAME' => '$SERVICE_USER_POSTGRES',
|
|
||||||
'AP_REDIS_HOST' => 'redis',
|
|
||||||
'AP_REDIS_PORT' => '6379',
|
|
||||||
'AP_SANDBOX_RUN_TIME_SECONDS' => '600',
|
|
||||||
'AP_TELEMETRY_ENABLED' => 'true',
|
|
||||||
'AP_TEMPLATES_SOURCE_URL' => 'https://cloud.activepieces.com/api/v1/flow-templates',
|
|
||||||
'AP_TRIGGER_DEFAULT_POLL_INTERVAL' => '5',
|
|
||||||
'AP_WEBHOOK_TIMEOUT_SECONDS' => '30',
|
|
||||||
'AP_TEST' => '${AP_TEST:-test}',
|
|
||||||
|
|
||||||
],
|
|
||||||
'depends_on' => [
|
|
||||||
'postgres' => [
|
|
||||||
'condition' => 'service_healthy',
|
|
||||||
],
|
|
||||||
'redis' => [
|
|
||||||
'condition' => 'service_started',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => [
|
|
||||||
'CMD',
|
|
||||||
'curl',
|
|
||||||
'-f',
|
|
||||||
'http://127.0.0.1:80',
|
|
||||||
],
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '20s',
|
|
||||||
'retries' => 10,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'postgres' => [
|
|
||||||
'image' => 'postgres:latest',
|
|
||||||
'environment' => [
|
|
||||||
'POSTGRES_DB' => 'activepieces',
|
|
||||||
'POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
|
||||||
'POSTGRES_USER' => '$SERVICE_USER_POSTGRES',
|
|
||||||
],
|
|
||||||
'volumes' => [
|
|
||||||
'dbdata:/var/lib/postgresql/data',
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => [
|
|
||||||
'CMD-SHELL',
|
|
||||||
'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}',
|
|
||||||
],
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '20s',
|
|
||||||
'retries' => 10,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'redis' => [
|
|
||||||
'image' => 'redis:latest',
|
|
||||||
'volumes' => [
|
|
||||||
'redis_data:/data',
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => [
|
|
||||||
'CMD',
|
|
||||||
'redis-cli',
|
|
||||||
'ping',
|
|
||||||
],
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '20s',
|
|
||||||
'retries' => 10,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$this->serviceComposeFileString = Yaml::dump($this->serviceComposeFile, 10, 2);
|
|
||||||
$this->jsonServiceComposeFile = json_encode($this->serviceComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
$this->service = Service::create([
|
$this->service = Service::create([
|
||||||
'name' => 'Service for tests',
|
'name' => 'Service for tests',
|
||||||
'uuid' => (string) new Cuid2(),
|
'uuid' => (string) new Cuid2(),
|
||||||
'docker_compose_raw' => $this->serviceComposeFileString,
|
'docker_compose_raw' => $this->serviceYaml,
|
||||||
'environment_id' => 1,
|
'environment_id' => 1,
|
||||||
'server_id' => 0,
|
'server_id' => 0,
|
||||||
'destination_id' => 0,
|
'destination_id' => 0,
|
||||||
@@ -187,6 +164,7 @@ beforeEach(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
// $this->applicationPreview->forceDelete();
|
||||||
$this->application->forceDelete();
|
$this->application->forceDelete();
|
||||||
$this->service->forceDelete();
|
$this->service->forceDelete();
|
||||||
});
|
});
|
||||||
@@ -195,7 +173,7 @@ afterEach(function () {
|
|||||||
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||||
|
|
||||||
// $output = $this->application->newParser();
|
// $output = $this->application->newParser();
|
||||||
// $outputOld = $this->application->oldParser();
|
// $outputOld = $this->application->parse();
|
||||||
// expect($output)->toBeInstanceOf(Collection::class);
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||||
|
|
||||||
@@ -274,7 +252,7 @@ afterEach(function () {
|
|||||||
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||||
|
|
||||||
// $output = $this->application->newParser(pull_request_id: $pullRequestId, preview_id: $previewId);
|
// $output = $this->application->newParser(pull_request_id: $pullRequestId, preview_id: $previewId);
|
||||||
// $outputOld = $this->application->oldParser();
|
// $outputOld = $this->application->parse();
|
||||||
// expect($output)->toBeInstanceOf(Collection::class);
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||||
|
|
||||||
@@ -354,32 +332,25 @@ afterEach(function () {
|
|||||||
|
|
||||||
// });
|
// });
|
||||||
|
|
||||||
test('ServiceComposeParseNew', function () {
|
// test('ServiceComposeParseNew', function () {
|
||||||
ray()->clearAll();
|
// $output = newParser($this->application, pull_request_id: 1, preview_id: $this->applicationPreview->id);
|
||||||
$output = $this->service->newParser();
|
// // ray('New parser');
|
||||||
// ray('New parser');
|
// // ray($output->toArray());
|
||||||
// ray(data_get($output, 'services.activepieces.environment')->toArray());
|
// ray($this->service->environment_variables_preview->pluck('value', 'key')->toArray());
|
||||||
ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
// foreach ($this->service->applications as $application) {
|
// });
|
||||||
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
|
||||||
// }
|
|
||||||
// foreach ($this->service->databases as $database) {
|
|
||||||
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
|
||||||
// }
|
|
||||||
expect($output)->toBeInstanceOf(Collection::class);
|
|
||||||
});
|
|
||||||
|
|
||||||
// test('ServiceComposeParseOld', function () {
|
// test('ServiceComposeParseOld', function () {
|
||||||
// $output = parseDockerComposeFile($this->service);
|
// $output = parseDockerComposeFile($this->service);
|
||||||
// ray('Old parser');
|
// ray('Old parser');
|
||||||
// ray($output->toArray());
|
// // ray($output->toArray());
|
||||||
// ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
// // ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||||
// foreach ($this->service->applications as $application) {
|
// // foreach ($this->service->applications as $application) {
|
||||||
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
// // ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
// }
|
// // }
|
||||||
// foreach ($this->service->databases as $database) {
|
// // foreach ($this->service->databases as $database) {
|
||||||
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
// // ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||||
// }
|
// // }
|
||||||
// expect($output)->toBeInstanceOf(Collection::class);
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
@@ -388,3 +359,30 @@ test('ServiceComposeParseNew', function () {
|
|||||||
// $output = instant_remote_process(['docker --version'], $server);
|
// $output = instant_remote_process(['docker --version'], $server);
|
||||||
// expect($output)->toContain('Docker version');
|
// expect($output)->toContain('Docker version');
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
// test('ConvertComposeEnvironmentToArray', function () {
|
||||||
|
// ray()->clearAll();
|
||||||
|
// $yaml = '
|
||||||
|
// services:
|
||||||
|
// activepieces:
|
||||||
|
// environment:
|
||||||
|
// - SERVICE_FQDN_ACTIVEPIECES=/app
|
||||||
|
// - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
|
||||||
|
// activepieces2:
|
||||||
|
// environment:
|
||||||
|
// - SERVICE_FQDN_ACTIVEPIECES=/v1/realtime
|
||||||
|
// postgres:
|
||||||
|
// environment:
|
||||||
|
// - POSTGRES_DB: activepieces
|
||||||
|
// ';
|
||||||
|
// $parsedYaml = Yaml::parse($yaml);
|
||||||
|
// $output = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces']['environment']);
|
||||||
|
// $output2 = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces2']['environment']);
|
||||||
|
// $dboutput = convertComposeEnvironmentToArray($parsedYaml['services']['postgres']['environment']);
|
||||||
|
// ray($output);
|
||||||
|
// ray($output2);
|
||||||
|
// ray($dboutput);
|
||||||
|
// expect($output)->toBeInstanceOf(Collection::class);
|
||||||
|
// expect($output2)->toBeInstanceOf(Collection::class);
|
||||||
|
// expect($dboutput)->toBeInstanceOf(Collection::class);
|
||||||
|
// });
|
||||||
|
Reference in New Issue
Block a user