From b60f8df17ae133d3b1c639e15bdd0b312a78ed74 Mon Sep 17 00:00:00 2001 From: buttercubz Date: Sun, 9 Jun 2024 12:14:55 -0400 Subject: [PATCH 001/212] feat: spanish translation --- lang/es.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/es.json diff --git a/lang/es.json b/lang/es.json new file mode 100644 index 000000000..866f5ae88 --- /dev/null +++ b/lang/es.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Iniciar Sesión", + "auth.login.azure": "Acceder con Microsoft", + "auth.login.bitbucket": "Acceder con Bitbucket", + "auth.login.github": "Acceder con GitHub", + "auth.login.gitlab": "Acceder con Gitlab", + "auth.login.google": "Acceder con Google", + "auth.already_registered": "¿Ya estás registrado?", + "auth.confirm_password": "Confirmar contraseña", + "auth.forgot_password": "¿Olvidaste tu contraseña?", + "auth.forgot_password_send_email": "Enviar correo de recuperación de contraseña", + "auth.register_now": "Registrar", + "auth.logout": "Cerrar sesión", + "auth.register": "Registrar", + "auth.registration_disabled": "El registro está desactivado. Por favor contacta con el administrador.", + "auth.reset_password": "Cambiar contraseña", + "auth.failed": "Las credenciales no coinciden con nuestro registro..", + "auth.failed.callback": "Fallo el proceso de inicio de sesión con el proveedor.", + "auth.failed.password": "La contraseña es incorrecta.", + "auth.failed.email": "No encontramos un usuario con ese correo.", + "auth.throttle": "Demasiados intentos. Por favor Inténtalo en :seconds segundos.", + "input.name": "Nombre", + "input.email": "Correo", + "input.password": "Contraseña", + "input.password.again": "Escribe la contraseña otra vez", + "input.code": "Código de único uso", + "input.recovery_code": "Código de recuperación", + "button.save": "Guardar", + "repository.url": "Examples
Para repositorios publicos, usar https://....
Para repositorios privados, usar git@....

https://github.com/coollabsio/coolify-examples main la rama 'main' será seleccionada.
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify la rama 'nodejs-fastify' será seleccionada.
https://gitea.com/sedlav/expressjs.git main la rama 'main' será seleccionada.
https://gitlab.com/andrasbacsai/nodejs-example.git main la rama 'main' será seleccionada." +} From 55e2e29696986b15168465d9b14bbd01c2a6d97c Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Mon, 10 Jun 2024 02:50:10 +0900 Subject: [PATCH 002/212] Add Japanese language support --- lang/ja.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/ja.json diff --git a/lang/ja.json b/lang/ja.json new file mode 100644 index 000000000..4652a3b17 --- /dev/null +++ b/lang/ja.json @@ -0,0 +1,30 @@ +{ + "auth.login": "ログイン", + "auth.login.azure": "Microsoftでログイン", + "auth.login.bitbucket": "Bitbucketでログイン", + "auth.login.github": "GitHubでログイン", + "auth.login.gitlab": "Gitlabでログイン", + "auth.login.google": "Googleでログイン", + "auth.already_registered": "すでに登録済みですか?", + "auth.confirm_password": "パスワードを確認", + "auth.forgot_password": "パスワードを忘れた", + "auth.forgot_password_send_email": "パスワードリセットメールを送信", + "auth.register_now": "今すぐ登録", + "auth.logout": "ログアウト", + "auth.register": "登録", + "auth.registration_disabled": "登録は無効です。管理者に連絡してください。", + "auth.reset_password": "パスワードをリセット", + "auth.failed": "これらの資格情報は記録と一致しません。", + "auth.failed.callback": "ログインプロバイダーからのコールバックの処理に失敗しました。", + "auth.failed.password": "提供されたパスワードが正しくありません。", + "auth.failed.email": "そのメールアドレスのユーザーが見つかりません。", + "auth.throttle": "ログイン試行回数が多すぎます。:seconds秒後にもう一度お試しください。", + "input.name": "名前", + "input.email": "メール", + "input.password": "パスワード", + "input.password.again": "パスワード再入力", + "input.code": "ワンタイムコード", + "input.recovery_code": "リカバリーコード", + "button.save": "保存", + "repository.url": "
公開リポジトリの場合はhttps://...を使用してください。
プライベートリポジトリの場合はgit@...を使用してください。

https://github.com/coollabsio/coolify-examples mainブランチが選択されます
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastifyブランチが選択されます。
https://gitea.com/sedlav/expressjs.git mainブランチが選択されます。
https://gitlab.com/andrasbacsai/nodejs-example.git mainブランチが選択されます。" +} From 665cd454efb32ec01f30be3de51e84ade9613a11 Mon Sep 17 00:00:00 2001 From: systematicRealm <119763173+systematicRealm@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:23:18 +0300 Subject: [PATCH 003/212] =?UTF-8?q?=F0=9F=8C=90=20ADD:=20Arabic=20Language?= =?UTF-8?q?=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lang/ar.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/ar.json diff --git a/lang/ar.json b/lang/ar.json new file mode 100644 index 000000000..c5ec96c8d --- /dev/null +++ b/lang/ar.json @@ -0,0 +1,30 @@ +{ + "auth.login": "تسجيل الدخول", + "auth.login.azure": "تسجيل الدخول باستخدام Microsoft", + "auth.login.bitbucket": "تسجيل الدخول باستخدام Bitbucket", + "auth.login.github": "تسجيل الدخول باستخدام GitHub", + "auth.login.gitlab": "تسجيل الدخول باستخدام Gitlab", + "auth.login.google": "تسجيل الدخول باستخدام Google", + "auth.already_registered": "هل سبق لك التسجيل؟", + "auth.confirm_password": "تأكيد كلمة المرور", + "auth.forgot_password": "نسيت كلمة المرور", + "auth.forgot_password_send_email": "إرسال بريد إلكتروني لإعادة تعيين كلمة المرور", + "auth.register_now": "تسجيل", + "auth.logout": "تسجيل الخروج", + "auth.register": "تسجيل", + "auth.registration_disabled": "تم تعطيل التسجيل. يرجى التواصل مع المسؤول.", + "auth.reset_password": "إعادة تعيين كلمة المرور", + "auth.failed": "هذه البيانات لا تتطابق مع سجلاتنا.", + "auth.failed.callback": "فشل في معالجة استدعاء من مزود تسجيل الدخول.", + "auth.failed.password": "كلمة المرور المقدمة غير صحيحة.", + "auth.failed.email": "لا يمكننا العثور على مستخدم بهذا البريد الإلكتروني.", + "auth.throttle": "عدد محاولات تسجيل الدخول كثيرة جدًا. يرجى المحاولة مرة أخرى في :seconds ثانية.", + "input.name": "الاسم", + "input.email": "البريد الإلكتروني", + "input.password": "كلمة المرور", + "input.password.again": "كلمة المرور مرة أخرى", + "input.code": "الرمز لمرة واحدة", + "input.recovery_code": "رمز الاسترداد", + "button.save": "حفظ", + "repository.url": "أمثلة
للمستودعات العامة، استخدم https://....
للمستودعات الخاصة، استخدم git@....

سيتم تحديد الفرع main لـ https://github.com/coollabsio/coolify-examples
سيتم تحديد الفرع nodejs-fastify لـ https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify
سيتم تحديد الفرع main لـ https://gitea.com/sedlav/expressjs.git
سيتم تحديد الفرع main لـ https://gitlab.com/andrasbacsai/nodejs-example.git." +} From f4904047b5d720462a5f08c23942f9906ab8ddb0 Mon Sep 17 00:00:00 2001 From: Ling <59746573+ndbiaw@users.noreply.github.com> Date: Tue, 11 Jun 2024 03:36:11 +0700 Subject: [PATCH 004/212] chore: add Vietnamese translate --- lang/vi.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/vi.json diff --git a/lang/vi.json b/lang/vi.json new file mode 100644 index 000000000..548dbe8b7 --- /dev/null +++ b/lang/vi.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Đăng Nhập", + "auth.login.azure": "Đăng Nhập Bằng Microsoft", + "auth.login.bitbucket": "Đăng Nhập Bằng Bitbucket", + "auth.login.github": "Đăng Nhập Bằng GitHub", + "auth.login.gitlab": "Đăng Nhập Bằng Gitlab", + "auth.login.google": "Đăng Nhập Bằng Google", + "auth.already_registered": "Đã đăng ký?", + "auth.confirm_password": "Nhập lại mật khẩu", + "auth.forgot_password": "Quên mật khẩu", + "auth.forgot_password_send_email": "Gửi email đặt lại mật khẩu", + "auth.register_now": "Đăng ký ngay", + "auth.logout": "Đăng xuất", + "auth.register": "Đăng ký", + "auth.registration_disabled": "Đăng ký không khả dụng. Vui lòng liên hệ quản trị viên.", + "auth.reset_password": "Đặt lại mật khẩu", + "auth.failed": "Thông tin đăng nhập không khớp với bất kỳ tài khoản nào.", + "auth.failed.callback": "Xử lý thông tin từ nhà cung cấp đăng nhập thất bại.", + "auth.failed.password": "Mật khẩu bạn cung cấp không chính xác.", + "auth.failed.email": "Không có người dùng nào đã đăng ký với email đó.", + "auth.throttle": "Quá nhiều lần đăng nhập thất bại. Vui lòng thử lại sau :seconds giây.", + "input.name": "Tên", + "input.email": "Email", + "input.password": "Mật khẩu", + "input.password.again": "Mật khẩu lần nữa", + "input.code": "One-time code", + "input.recovery_code": "Mã khôi phục", + "button.save": "Lưu", + "repository.url": "Ví dụ
Với repo công khai, sử dụng https://....
Với repo riêng tư, sử dụng git@....

https://github.com/coollabsio/coolify-examples nhánh chính sẽ được chọn
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nhánh nodejs-fastify sẽ được chọn.
https://gitea.com/sedlav/expressjs.git nhánh chính sẽ được chọn.
https://gitlab.com/andrasbacsai/nodejs-example.git nhánh chính sẽ được chọn." +} From e282686f97e8a4c0ea3242d3ce8b4e961955900a Mon Sep 17 00:00:00 2001 From: Flow Date: Tue, 11 Jun 2024 01:29:37 +0200 Subject: [PATCH 005/212] Add french translation --- lang/fr.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/fr.json diff --git a/lang/fr.json b/lang/fr.json new file mode 100644 index 000000000..ae7fa0a03 --- /dev/null +++ b/lang/fr.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Connexion", + "auth.login.azure": "Connexion avec Microsoft", + "auth.login.bitbucket": "Connexion avec Bitbucket", + "auth.login.github": "Connexion avec GitHub", + "auth.login.gitlab": "Connexion avec Gitlab", + "auth.login.google": "Connexion avec Google", + "auth.already_registered": "Déjà enregistré ?", + "auth.confirm_password": "Confirmer le mot de passe", + "auth.forgot_password": "Mot de passe oublié", + "auth.forgot_password_send_email": "Envoyer l'email de réinitialisation de mot de passe", + "auth.register_now": "S'enregistrer", + "auth.logout": "Déconnexion", + "auth.register": "S'enregistrer", + "auth.registration_disabled": "L'enregistrement est désactivé. Merci de contacter l'administateur.", + "auth.reset_password": "Réinitialiser le mot de passe", + "auth.failed": "Aucune correspondance n'a été trouvé pour les informations d'identification renseignées.", + "auth.failed.callback": "Erreur lors du processus de retour de la plateforme de connexion.", + "auth.failed.password": "Le mot de passe renseigné est incorrect.", + "auth.failed.email": "Aucun utilisateur avec cette adresse email n'a été trouvé.", + "auth.throttle": "Trop de tentatives de connexion. Merci de réessayer dans :seconds secondes.", + "input.name": "Nom", + "input.email": "Email", + "input.password": "Mot de passe", + "input.password.again": "Mot de passe identique", + "input.code": "Code à usage unique", + "input.recovery_code": "Code de récupération", + "button.save": "Sauvegarder", + "repository.url": "Exemples
Pour les dépôts publiques, utilisez https://....
Pour les dépôts privés, utilisez git@....

https://github.com/coollabsio/coolify-examples main sera la branche selectionnée
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify sera la branche selectionnée.
https://gitea.com/sedlav/expressjs.git main sera la branche selectionnée.
https://gitlab.com/andrasbacsai/nodejs-example.git main sera la branche selectionnée." +} From 85d313a7912483f96dec5969946505864b16f401 Mon Sep 17 00:00:00 2001 From: lopesboa Date: Tue, 11 Jun 2024 12:21:03 -0300 Subject: [PATCH 006/212] chore: add portuguese traslation --- lang/pt.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/pt.json diff --git a/lang/pt.json b/lang/pt.json new file mode 100644 index 000000000..b5dd5c434 --- /dev/null +++ b/lang/pt.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Entrar", + "auth.login.azure": "Entrar com Microsoft", + "auth.login.bitbucket": "Entrar com Bitbucket", + "auth.login.github": "Entrar com GitHub", + "auth.login.gitlab": "Entrar com Gitlab", + "auth.login.google": "Entrar com Google", + "auth.already_registered": "Já tem uma conta?", + "auth.confirm_password": "Confirmar senha", + "auth.forgot_password": "Esqueceu a senha?", + "auth.forgot_password_send_email": "Enviar e-mail de redefinição de senha", + "auth.register_now": "Cadastrar-se", + "auth.logout": "Sair", + "auth.register": "Cadastrar", + "auth.registration_disabled": "Cadastro desativado. Por favor, entre em contato com o administrador.", + "auth.reset_password": "Redefinir senha", + "auth.failed": "Essas credenciais não correspondem aos nossos registros.", + "auth.failed.callback": "Falha ao processar o callback do provedor de login.", + "auth.failed.password": "A senha fornecida está incorreta.", + "auth.failed.email": "Não encontramos um usuário com esse endereço de e-mail.", + "auth.throttle": "Muitas tentativas de login. Por favor, tente novamente em :seconds segundos.", + "input.name": "Nome", + "input.email": "E-mail", + "input.password": "Senha", + "input.password.again": "Repetir senha", + "input.code": "Código único", + "input.recovery_code": "Código de recuperação", + "button.save": "Salvar", + "repository.url": "Exemplos
Para repositórios públicos, use https://....
Para repositórios privados, use git@....

https://github.com/coollabsio/coolify-examples a branch main será selecionada
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify a branch nodejs-fastify será selecionada.
https://gitea.com/sedlav/expressjs.git a branch main será selecionada.
https://gitlab.com/andrasbacsai/nodejs-example.git a branch main será selecionada." +} From a138bb61bcf17fbad76ad2d0046c51038fa042e1 Mon Sep 17 00:00:00 2001 From: Flo Schuessel Date: Tue, 11 Jun 2024 17:35:26 +0200 Subject: [PATCH 007/212] Update logs.blade.php --- resources/views/livewire/project/shared/logs.blade.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/views/livewire/project/shared/logs.blade.php b/resources/views/livewire/project/shared/logs.blade.php index c0eaae105..6b20d716b 100644 --- a/resources/views/livewire/project/shared/logs.blade.php +++ b/resources/views/livewire/project/shared/logs.blade.php @@ -19,7 +19,7 @@ @forelse (data_get($server,'containers',[]) as $container) @empty -
No containers are not running on server: {{ $server->name }}
+
No containers are running on server: {{ $server->name }}
@endforelse @@ -41,7 +41,7 @@
No functional server found for the database.
@endif @empty -
No containers are not running.
+
No containers are running.
@endforelse @elseif ($type === 'service') @@ -56,7 +56,7 @@
No functional server found for the service.
@endif @empty -
No containers are not running.
+
No containers are running.
@endforelse @endif From 6d9454b3513e21a7591ea3dc3aaf36539b46e357 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 11 Jun 2024 19:05:37 +0200 Subject: [PATCH 008/212] chore: Update version numbers to 4.0.0-beta.298 --- config/sentry.php | 2 +- config/version.php | 2 +- versions.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/sentry.php b/config/sentry.php index 33a24edfb..caa659921 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.297', + 'release' => '4.0.0-beta.298', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 06c1e6c66..ddcd3f2d4 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ Date: Tue, 11 Jun 2024 22:46:02 +0300 Subject: [PATCH 009/212] chore: add Turkish translations --- lang/tr.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/tr.json diff --git a/lang/tr.json b/lang/tr.json new file mode 100644 index 000000000..255b0d15b --- /dev/null +++ b/lang/tr.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Giriş", + "auth.login.azure": "Microsoft ile Giriş Yap", + "auth.login.bitbucket": "Bitbucket ile Giriş Yap", + "auth.login.github": "GitHub ile Giriş Yap", + "auth.login.gitlab": "GitLab ile Giriş Yap", + "auth.login.google": "Google ile Giriş Yap", + "auth.already_registered": "Zaten kayıtlı mısınız?", + "auth.confirm_password": "Şifreyi Onayla", + "auth.forgot_password": "Şifremi Unuttum", + "auth.forgot_password_send_email": "Şifre sıfırlama e-postası gönder", + "auth.register_now": "Kayıt Ol", + "auth.logout": "Çıkış Yap", + "auth.register": "Kayıt Ol", + "auth.registration_disabled": "Kayıt devre dışı bırakıldı. Lütfen yöneticiyle iletişime geçin.", + "auth.reset_password": "Şifreyi Sıfırla", + "auth.failed": "Bu kimlik bilgileri kayıtlarımızla eşleşmiyor.", + "auth.failed.callback": "Giriş sağlayıcıdan gelen istek işlenemedi.", + "auth.failed.password": "Sağlanan şifre yanlış.", + "auth.failed.email": "Bu e-posta adresiyle bir kullanıcı bulamıyoruz.", + "auth.throttle": "Çok fazla giriş denemesi. Lütfen :seconds saniye sonra tekrar deneyin.", + "input.name": "İsim", + "input.email": "E-posta", + "input.password": "Şifre", + "input.password.again": "Şifreyi Tekrar Girin", + "input.code": "Tek Kullanımlık Kod", + "input.recovery_code": "Kurtarma Kodu", + "button.save": "Kaydet", + "repository.url": "Örnekler
Halka açık depolar için https://... kullanın.
Özel depolar için git@... kullanın.

https://github.com/coollabsio/coolify-examples main dalı seçilecek
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify dalı seçilecek.
https://gitea.com/sedlav/expressjs.git main dalı seçilecek.
https://gitlab.com/andrasbacsai/nodejs-example.git main dalı seçilecek." +} From b53bb44e42a017a50ed0f18fbd60851fd1f33e9c Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 09:56:13 +0200 Subject: [PATCH 010/212] chore: switch to database sessions from redis --- config/session.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/session.php b/config/session.php index c7b176a5a..44ca7ded9 100644 --- a/config/session.php +++ b/config/session.php @@ -18,7 +18,7 @@ | */ - 'driver' => env('SESSION_DRIVER', 'redis'), + 'driver' => env('SESSION_DRIVER', 'database'), /* |-------------------------------------------------------------------------- From 784cfb8fba146f12e5f85f620628e57af5dbe6e7 Mon Sep 17 00:00:00 2001 From: arthur <51604173+arthurauffray@users.noreply.github.com> Date: Wed, 12 Jun 2024 21:04:12 +1200 Subject: [PATCH 011/212] Updates to README: grammar, sentence structure, URL formats - Correct grammar mistakes - Updated URL markdown formatting to be uniform throughout (hidden protocol before URL on coolify.io domain) - Converted plain text links to markdown URLs --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 56bee004e..bfa849fa9 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ # About the Project Coolify is an open-source & self-hostable alternative to Heroku / Netlify / Vercel / etc. -It helps you to manage your servers, applications, databases on your own hardware, all you need is SSH connection. You can manage VPS, Bare Metal, Raspberry PI's anything. +It helps you manage your servers, applications, and databases on your own hardware; you only need an SSH connection. You can manage VPS, Bare Metal, Raspberry PIs, and anything else. -Imagine if you could have the ease of a cloud but with your own servers. That is **Coolify**. +Imagine having the ease of a cloud but with your own servers. That is **Coolify**. -No vendor lock-in, which means that all the configuration for your applications/databases/etc are saved to your server. So if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You just lose the automations and all the magic. 🪄️ +No vendor lock-in, which means that all the configurations for your applications/databases/etc are saved to your server. So, if you decide to stop using Coolify (oh nooo), you could still manage your running resources. You lose the automations and all the magic. 🪄️ -For more information, take a look at our landing page [here](https://coolify.io). +For more information, take a look at our landing page at [coolify.io](https://coolify.io). # Installation @@ -22,12 +22,12 @@ # Installation # Support -Contact us [here](https://coolify.io/docs/contact). +Contact us at [coolify.io/docs/contact](https://coolify.io/docs/contact). # Donations -To stay completely free, open-source, no feature behind paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the future development of the project. +To stay completely free and open-source, with no feature behind the paywall and evolve the project, we need your help. If you like Coolify, please consider donating to help us fund the project's future development. -https://coolify.io/sponsorships +[coolify.io/sponsorships](https://coolify.io/sponsorships) Thank you so much! @@ -83,9 +83,9 @@ ## Individuals # Cloud -If you do not want to self-host Coolify, there is a paid cloud version available: https://app.coolify.io +If you do not want to self-host Coolify, there is a paid cloud version available: [app.coolify.io](https://app.coolify.io) -For more information & pricing, take a look at our landing page [here](https://coolify.io). +For more information & pricing, take a look at our landing page [coolify.io](https://coolify.io). ## Why should I use the Cloud version? The recommended way to use Coolify is to have one server for Coolify and one (or more) for the resources you are deploying. A server is around 4-5$/month. From e922bc207a8ff31bc8857443ef84e2a84b48335f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 11:30:25 +0200 Subject: [PATCH 012/212] chore: Update dependencies and remove unused code --- bootstrap/helpers/shared.php | 2 +- scripts/cloud_upgrade.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 7994c10af..e84c30fad 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1,9 +1,9 @@ last_version docker compose logs -f From c58f468dc9487a78ac154e90c5a5f50357b1a5c6 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Wed, 12 Jun 2024 09:31:14 +0000 Subject: [PATCH 013/212] Fix styling --- bootstrap/helpers/shared.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index e84c30fad..7994c10af 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1,9 +1,9 @@ Date: Wed, 12 Jun 2024 11:35:07 +0200 Subject: [PATCH 014/212] fix: bitbucket link --- app/Models/Application.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/Models/Application.php b/app/Models/Application.php index 6e55f6626..bebf7c61c 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -235,11 +235,6 @@ public function gitCommitLink($link): string return "{$this->source->html_url}/{$this->git_repository}/commit/{$link}"; } - if (strpos($this->git_repository, 'git@') === 0) { - $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); - - return "https://{$git_repository}/commit/{$link}"; - } if (str($this->git_repository)->contains('bitbucket')) { $git_repository = str_replace('.git', '', $this->git_repository); $url = Url::fromString($git_repository); @@ -248,6 +243,10 @@ public function gitCommitLink($link): string return $url->__toString(); } + if (strpos($this->git_repository, 'git@') === 0) { + $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/commit/{$link}"; + } return $this->git_repository; } From 2335abac91818445ee3387d4bc8293dd734911d8 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Wed, 12 Jun 2024 09:35:55 +0000 Subject: [PATCH 015/212] Fix styling --- app/Models/Application.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Models/Application.php b/app/Models/Application.php index bebf7c61c..e536f8d69 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -245,6 +245,7 @@ public function gitCommitLink($link): string } if (strpos($this->git_repository, 'git@') === 0) { $git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository); + return "https://{$git_repository}/commit/{$link}"; } From f332a73122888573ca83f33f85a67c5e1c5f413b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 12:05:08 +0200 Subject: [PATCH 016/212] feat: cancelling a deployment will check if new could be started. --- .../Project/Application/DeploymentNavbar.php | 5 ++-- app/Models/Application.php | 2 +- bootstrap/helpers/applications.php | 29 +++++++++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php index cbbe98d99..b3e39d23d 100644 --- a/app/Livewire/Project/Application/DeploymentNavbar.php +++ b/app/Livewire/Project/Application/DeploymentNavbar.php @@ -54,9 +54,9 @@ public function force_start() public function cancel() { + $kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}"; + $server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id; try { - $kill_command = "docker rm -f {$this->application_deployment_queue->deployment_uuid}"; - $server_id = $this->application_deployment_queue->server_id ?? $this->application->destination->server_id; $server = Server::find($server_id); if ($this->application_deployment_queue->logs) { $previous_logs = json_decode($this->application_deployment_queue->logs, associative: true, flags: JSON_THROW_ON_ERROR); @@ -84,6 +84,7 @@ public function cancel() 'current_process_id' => null, 'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value, ]); + next_after_cancel($server); } } } diff --git a/app/Models/Application.php b/app/Models/Application.php index bebf7c61c..532fc5d4a 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -531,7 +531,7 @@ public function isDeploymentInprogress() public function get_last_successful_deployment() { - return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'finished')->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first(); + return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::FINISHED)->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first(); } public function get_last_days_deployments() diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index 376b0f2aa..8bfa4eac1 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -11,7 +11,7 @@ function queue_application_deployment(Application $application, string $deployment_uuid, ?int $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, ?Server $server = null, ?StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false) { $application_id = $application->id; - $deployment_link = Url::fromString($application->link()."/deployment/{$deployment_uuid}"); + $deployment_link = Url::fromString($application->link() . "/deployment/{$deployment_uuid}"); $deployment_url = $deployment_link->getPath(); $server_id = $application->destination->server->id; $server_name = $application->destination->server->name; @@ -65,7 +65,7 @@ function force_start_deployment(ApplicationDeploymentQueue $deployment) function queue_next_deployment(Application $application) { $server_id = $application->destination->server_id; - $next_found = ApplicationDeploymentQueue::where('server_id', $server_id)->where('status', 'queued')->get()->sortBy('created_at')->first(); + $next_found = ApplicationDeploymentQueue::where('server_id', $server_id)->where('status', ApplicationDeploymentStatus::QUEUED)->get()->sortBy('created_at')->first(); if ($next_found) { $next_found->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, @@ -79,7 +79,7 @@ function queue_next_deployment(Application $application) function next_queuable(string $server_id, string $application_id): bool { - $deployments = ApplicationDeploymentQueue::where('server_id', $server_id)->whereIn('status', ['in_progress', 'queued'])->get()->sortByDesc('created_at'); + $deployments = ApplicationDeploymentQueue::where('server_id', $server_id)->whereIn('status', ['in_progress', ApplicationDeploymentStatus::QUEUED])->get()->sortByDesc('created_at'); $same_application_deployments = $deployments->where('application_id', $application_id); $in_progress = $same_application_deployments->filter(function ($value, $key) { return $value->status === 'in_progress'; @@ -98,3 +98,26 @@ function next_queuable(string $server_id, string $application_id): bool return true; } +function next_after_cancel(?Server $server = null) +{ + if ($server) { + $next_found = ApplicationDeploymentQueue::where('server_id', data_get($server, 'id'))->where('status', ApplicationDeploymentStatus::QUEUED)->get()->sortBy('created_at'); + if ($next_found->count() > 0) { + foreach ($next_found as $next) { + $server = Server::find($next->server_id); + $concurrent_builds = $server->settings->concurrent_builds; + $inprogress_deployments = ApplicationDeploymentQueue::where('server_id', $next->server_id)->whereIn('status', [ApplicationDeploymentStatus::QUEUED])->get()->sortByDesc('created_at'); + if ($inprogress_deployments->count() < $concurrent_builds) { + $next->update([ + 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, + ]); + + dispatch(new ApplicationDeploymentJob( + application_deployment_queue_id: $next->id, + )); + } + break; + } + } + } +} From ce243529748a1c63a7ebfb930e1c209c6007b2da Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Wed, 12 Jun 2024 10:05:54 +0000 Subject: [PATCH 017/212] Fix styling --- bootstrap/helpers/applications.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index 8bfa4eac1..816a13853 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -11,7 +11,7 @@ function queue_application_deployment(Application $application, string $deployment_uuid, ?int $pull_request_id = 0, string $commit = 'HEAD', bool $force_rebuild = false, bool $is_webhook = false, bool $restart_only = false, ?string $git_type = null, bool $no_questions_asked = false, ?Server $server = null, ?StandaloneDocker $destination = null, bool $only_this_server = false, bool $rollback = false) { $application_id = $application->id; - $deployment_link = Url::fromString($application->link() . "/deployment/{$deployment_uuid}"); + $deployment_link = Url::fromString($application->link()."/deployment/{$deployment_uuid}"); $deployment_url = $deployment_link->getPath(); $server_id = $application->destination->server->id; $server_name = $application->destination->server->name; From 019cdd2b3a2e3a465e7260422236d7aa77024049 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 12:20:58 +0200 Subject: [PATCH 018/212] fix: compose generator --- app/Http/Controllers/Webhook/Bitbucket.php | 41 +++++++++------ app/Http/Controllers/Webhook/Gitea.php | 24 ++++++--- app/Http/Controllers/Webhook/Github.php | 51 +++++++++++-------- app/Http/Controllers/Webhook/Gitlab.php | 47 ++++++++++------- app/Livewire/Project/Application/Previews.php | 4 ++ .../project/application/previews.blade.php | 2 +- 6 files changed, 109 insertions(+), 60 deletions(-) diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php index b9035b755..f666d55cc 100644 --- a/app/Http/Controllers/Webhook/Bitbucket.php +++ b/app/Http/Controllers/Webhook/Bitbucket.php @@ -39,7 +39,7 @@ public function manual(Request $request) $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ''); $x_bitbucket_event = data_get($headers, 'x-event-key.0', ''); $handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); - if (! $handled_events->contains($x_bitbucket_event)) { + if (!$handled_events->contains($x_bitbucket_event)) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. Event not handled.', @@ -49,13 +49,13 @@ public function manual(Request $request) $branch = data_get($payload, 'push.changes.0.new.name'); $full_name = data_get($payload, 'repository.full_name'); $commit = data_get($payload, 'push.changes.0.new.target.hash'); - if (! $branch) { + if (!$branch) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); } - ray('Manual webhook bitbucket push event with branch: '.$branch); + ray('Manual webhook bitbucket push event with branch: ' . $branch); } if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { $branch = data_get($payload, 'pullrequest.destination.branch.name'); @@ -79,7 +79,7 @@ public function manual(Request $request) [$algo, $hash] = explode('=', $x_bitbucket_token, 2); $payloadHash = hash_hmac($algo, $payload, $webhook_secret); - if (! hash_equals($hash, $payloadHash) && ! isDev()) { + if (!hash_equals($hash, $payloadHash) && !isDev()) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', @@ -90,19 +90,19 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (! $isFunctional) { + if (!$isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional.', ]); - ray('Server is not functional: '.$application->destination->server->name); + ray('Server is not functional: ' . $application->destination->server->name); continue; } if ($x_bitbucket_event === 'repo:push') { if ($application->isDeployable()) { - ray('Deploying '.$application->name.' with branch '.$branch); + ray('Deploying ' . $application->name . ' with branch ' . $branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -126,16 +126,27 @@ public function manual(Request $request) } if ($x_bitbucket_event === 'pullrequest:created') { if ($application->isPRDeployable()) { - ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); + ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (! $found) { - ApplicationPreview::create([ - 'git_type' => 'bitbucket', - 'application_id' => $application->id, - 'pull_request_id' => $pull_request_id, - 'pull_request_html_url' => $pull_request_html_url, - ]); + if (!$found) { + if ($application->build_pack === 'dockercompose') { + $pr_app = ApplicationPreview::create([ + 'git_type' => 'bitbucket', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + 'docker_compose_domains' => $application->docker_compose_domains, + ]); + $pr_app->generate_preview_fqdn_compose(); + } else { + ApplicationPreview::create([ + 'git_type' => 'bitbucket', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + ]); + } } queue_application_deployment( application: $application, diff --git a/app/Http/Controllers/Webhook/Gitea.php b/app/Http/Controllers/Webhook/Gitea.php index 388481949..e6d91efd6 100644 --- a/app/Http/Controllers/Webhook/Gitea.php +++ b/app/Http/Controllers/Webhook/Gitea.php @@ -165,12 +165,24 @@ public function manual(Request $request) $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); if (! $found) { - ApplicationPreview::create([ - 'git_type' => 'gitea', - 'application_id' => $application->id, - 'pull_request_id' => $pull_request_id, - 'pull_request_html_url' => $pull_request_html_url, - ]); + if ($application->build_pack === 'dockercompose') { + $pr_app = ApplicationPreview::create([ + 'git_type' => 'gitea', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + 'docker_compose_domains' => $application->docker_compose_domains, + ]); + $pr_app->generate_preview_fqdn_compose(); + } else { + ApplicationPreview::create([ + 'git_type' => 'gitea', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + ]); + } + } queue_application_deployment( application: $application, diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php index 403438193..87c7e283a 100644 --- a/app/Http/Controllers/Webhook/Github.php +++ b/app/Http/Controllers/Webhook/Github.php @@ -73,7 +73,7 @@ public function manual(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitHub Push Event with branch: '.$branch); + ray('Manual Webhook GitHub Push Event with branch: ' . $branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -82,9 +82,9 @@ public function manual(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); } - if (! $branch) { + if (!$branch) { return response('Nothing to do. No branch found in the request.'); } $applications = Application::where('git_repository', 'like', "%$full_name%"); @@ -103,7 +103,7 @@ public function manual(Request $request) foreach ($applications as $application) { $webhook_secret = data_get($application, 'manual_webhook_secret_github'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); - if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) { + if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) { ray('Invalid signature'); $return_payloads->push([ 'application' => $application->name, @@ -114,7 +114,7 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (! $isFunctional) { + if (!$isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', @@ -127,7 +127,7 @@ public function manual(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying '.$application->name.' with branch '.$branch); + ray('Deploying ' . $application->name . ' with branch ' . $branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -169,13 +169,24 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (! $found) { - ApplicationPreview::create([ - 'git_type' => 'github', - 'application_id' => $application->id, - 'pull_request_id' => $pull_request_id, - 'pull_request_html_url' => $pull_request_html_url, - ]); + if (!$found) { + if ($application->build_pack === 'dockercompose') { + $pr_app = ApplicationPreview::create([ + 'git_type' => 'github', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + 'docker_compose_domains' => $application->docker_compose_domains, + ]); + $pr_app->generate_preview_fqdn_compose(); + } else { + ApplicationPreview::create([ + 'git_type' => 'github', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + ]); + } } queue_application_deployment( application: $application, @@ -279,7 +290,7 @@ public function normal(Request $request) $webhook_secret = data_get($github_app, 'webhook_secret'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); if (config('app.env') !== 'local') { - if (! hash_equals($x_hub_signature_256, $hmac)) { + if (!hash_equals($x_hub_signature_256, $hmac)) { return response('Invalid signature.'); } } @@ -302,7 +313,7 @@ public function normal(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Webhook GitHub Push Event: '.$id.' with branch: '.$branch); + ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -311,9 +322,9 @@ public function normal(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event: '.$id.' with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); + ray('Webhook GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); } - if (! $id || ! $branch) { + if (!$id || !$branch) { return response('Nothing to do. No id or branch found.'); } $applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false); @@ -332,7 +343,7 @@ public function normal(Request $request) foreach ($applications as $application) { $isFunctional = $application->destination->server->isFunctional(); - if (! $isFunctional) { + if (!$isFunctional) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Server is not functional.', @@ -346,7 +357,7 @@ public function normal(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying '.$application->name.' with branch '.$branch); + ray('Deploying ' . $application->name . ' with branch ' . $branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -388,7 +399,7 @@ public function normal(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (! $found) { + if (!$found) { ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index a3d7712eb..7ae376f1f 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -40,7 +40,7 @@ public function manual(Request $request) $x_gitlab_token = data_get($headers, 'x-gitlab-token.0'); $x_gitlab_event = data_get($payload, 'object_kind'); $allowed_events = ['push', 'merge_request']; - if (! in_array($x_gitlab_event, $allowed_events)) { + if (!in_array($x_gitlab_event, $allowed_events)) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Event not allowed. Only push and merge_request events are allowed.', @@ -55,7 +55,7 @@ public function manual(Request $request) if (Str::isMatch('/refs\/heads\/*/', $branch)) { $branch = Str::after($branch, 'refs/heads/'); } - if (! $branch) { + if (!$branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', @@ -67,7 +67,7 @@ public function manual(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitLab Push Event with branch: '.$branch); + ray('Manual Webhook GitLab Push Event with branch: ' . $branch); } if ($x_gitlab_event === 'merge_request') { $action = data_get($payload, 'object_attributes.action'); @@ -76,7 +76,7 @@ public function manual(Request $request) $full_name = data_get($payload, 'project.path_with_namespace'); $pull_request_id = data_get($payload, 'object_attributes.iid'); $pull_request_html_url = data_get($payload, 'object_attributes.url'); - if (! $branch) { + if (!$branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', @@ -84,7 +84,7 @@ public function manual(Request $request) return response($return_payloads); } - ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); } $applications = Application::where('git_repository', 'like', "%$full_name%"); if ($x_gitlab_event === 'push') { @@ -122,13 +122,13 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (! $isFunctional) { + if (!$isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional', ]); - ray('Server is not functional: '.$application->destination->server->name); + ray('Server is not functional: ' . $application->destination->server->name); continue; } @@ -136,7 +136,7 @@ public function manual(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying '.$application->name.' with branch '.$branch); + ray('Deploying ' . $application->name . ' with branch ' . $branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -171,7 +171,7 @@ public function manual(Request $request) 'application_uuid' => $application->uuid, 'application_name' => $application->name, ]); - ray('Deployments disabled for '.$application->name); + ray('Deployments disabled for ' . $application->name); } } if ($x_gitlab_event === 'merge_request') { @@ -179,13 +179,24 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (! $found) { - ApplicationPreview::create([ - 'git_type' => 'gitlab', - 'application_id' => $application->id, - 'pull_request_id' => $pull_request_id, - 'pull_request_html_url' => $pull_request_html_url, - ]); + if (!$found) { + if ($application->build_pack === 'dockercompose') { + $pr_app = ApplicationPreview::create([ + 'git_type' => 'gitlab', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + 'docker_compose_domains' => $application->docker_compose_domains, + ]); + $pr_app->generate_preview_fqdn_compose(); + } else { + ApplicationPreview::create([ + 'git_type' => 'gitlab', + 'application_id' => $application->id, + 'pull_request_id' => $pull_request_id, + 'pull_request_html_url' => $pull_request_html_url, + ]); + } } queue_application_deployment( application: $application, @@ -196,7 +207,7 @@ public function manual(Request $request) is_webhook: true, git_type: 'gitlab' ); - ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); + ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); $return_payloads->push([ 'application' => $application->name, 'status' => 'success', @@ -208,7 +219,7 @@ public function manual(Request $request) 'status' => 'failed', 'message' => 'Preview deployments disabled', ]); - ray('Preview deployments disabled for '.$application->name); + ray('Preview deployments disabled for ' . $application->name); } } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') { $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index ca911339e..ea8a19764 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -131,6 +131,10 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null) } } + public function add_and_deploy(int $pull_request_id, ?string $pull_request_html_url = null) { + $this->add($pull_request_id, $pull_request_html_url); + $this->deploy($pull_request_id, $pull_request_html_url); + } public function deploy(int $pull_request_id, ?string $pull_request_html_url = null) { try { diff --git a/resources/views/livewire/project/application/previews.blade.php b/resources/views/livewire/project/application/previews.blade.php index 79a7b88b0..2f142abbb 100644 --- a/resources/views/livewire/project/application/previews.blade.php +++ b/resources/views/livewire/project/application/previews.blade.php @@ -45,7 +45,7 @@ class="dark:text-warning">{{ $application->destination->server->name }}.< Configure + wire:click="add_and_deploy('{{ data_get($pull_request, 'number') }}', '{{ data_get($pull_request, 'html_url') }}')"> From 2e01665340bf8bd948ad7a2209fbe5fd7e42ad80 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Wed, 12 Jun 2024 10:21:47 +0000 Subject: [PATCH 019/212] Fix styling --- app/Http/Controllers/Webhook/Bitbucket.php | 18 ++++++------ app/Http/Controllers/Webhook/Github.php | 28 +++++++++---------- app/Http/Controllers/Webhook/Gitlab.php | 24 ++++++++-------- app/Livewire/Project/Application/Previews.php | 4 ++- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php index f666d55cc..059438ff4 100644 --- a/app/Http/Controllers/Webhook/Bitbucket.php +++ b/app/Http/Controllers/Webhook/Bitbucket.php @@ -39,7 +39,7 @@ public function manual(Request $request) $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ''); $x_bitbucket_event = data_get($headers, 'x-event-key.0', ''); $handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); - if (!$handled_events->contains($x_bitbucket_event)) { + if (! $handled_events->contains($x_bitbucket_event)) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. Event not handled.', @@ -49,13 +49,13 @@ public function manual(Request $request) $branch = data_get($payload, 'push.changes.0.new.name'); $full_name = data_get($payload, 'repository.full_name'); $commit = data_get($payload, 'push.changes.0.new.target.hash'); - if (!$branch) { + if (! $branch) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); } - ray('Manual webhook bitbucket push event with branch: ' . $branch); + ray('Manual webhook bitbucket push event with branch: '.$branch); } if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { $branch = data_get($payload, 'pullrequest.destination.branch.name'); @@ -79,7 +79,7 @@ public function manual(Request $request) [$algo, $hash] = explode('=', $x_bitbucket_token, 2); $payloadHash = hash_hmac($algo, $payload, $webhook_secret); - if (!hash_equals($hash, $payloadHash) && !isDev()) { + if (! hash_equals($hash, $payloadHash) && ! isDev()) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', @@ -90,19 +90,19 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional.', ]); - ray('Server is not functional: ' . $application->destination->server->name); + ray('Server is not functional: '.$application->destination->server->name); continue; } if ($x_bitbucket_event === 'repo:push') { if ($application->isDeployable()) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -126,10 +126,10 @@ public function manual(Request $request) } if ($x_bitbucket_event === 'pullrequest:created') { if ($application->isPRDeployable()) { - ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); + ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { if ($application->build_pack === 'dockercompose') { $pr_app = ApplicationPreview::create([ 'git_type' => 'bitbucket', diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php index 87c7e283a..a030e31ca 100644 --- a/app/Http/Controllers/Webhook/Github.php +++ b/app/Http/Controllers/Webhook/Github.php @@ -73,7 +73,7 @@ public function manual(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitHub Push Event with branch: ' . $branch); + ray('Manual Webhook GitHub Push Event with branch: '.$branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -82,9 +82,9 @@ public function manual(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } - if (!$branch) { + if (! $branch) { return response('Nothing to do. No branch found in the request.'); } $applications = Application::where('git_repository', 'like', "%$full_name%"); @@ -103,7 +103,7 @@ public function manual(Request $request) foreach ($applications as $application) { $webhook_secret = data_get($application, 'manual_webhook_secret_github'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); - if (!hash_equals($x_hub_signature_256, $hmac) && !isDev()) { + if (! hash_equals($x_hub_signature_256, $hmac) && ! isDev()) { ray('Invalid signature'); $return_payloads->push([ 'application' => $application->name, @@ -114,7 +114,7 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', @@ -127,7 +127,7 @@ public function manual(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -169,7 +169,7 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { if ($application->build_pack === 'dockercompose') { $pr_app = ApplicationPreview::create([ 'git_type' => 'github', @@ -290,7 +290,7 @@ public function normal(Request $request) $webhook_secret = data_get($github_app, 'webhook_secret'); $hmac = hash_hmac('sha256', $request->getContent(), $webhook_secret); if (config('app.env') !== 'local') { - if (!hash_equals($x_hub_signature_256, $hmac)) { + if (! hash_equals($x_hub_signature_256, $hmac)) { return response('Invalid signature.'); } } @@ -313,7 +313,7 @@ public function normal(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Webhook GitHub Push Event: ' . $id . ' with branch: ' . $branch); + ray('Webhook GitHub Push Event: '.$id.' with branch: '.$branch); } if ($x_github_event === 'pull_request') { $action = data_get($payload, 'action'); @@ -322,9 +322,9 @@ public function normal(Request $request) $pull_request_html_url = data_get($payload, 'pull_request.html_url'); $branch = data_get($payload, 'pull_request.head.ref'); $base_branch = data_get($payload, 'pull_request.base.ref'); - ray('Webhook GitHub Pull Request Event: ' . $id . ' with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event: '.$id.' with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } - if (!$id || !$branch) { + if (! $id || ! $branch) { return response('Nothing to do. No id or branch found.'); } $applications = Application::where('repository_project_id', $id)->whereRelation('source', 'is_public', false); @@ -343,7 +343,7 @@ public function normal(Request $request) foreach ($applications as $application) { $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Server is not functional.', @@ -357,7 +357,7 @@ public function normal(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -399,7 +399,7 @@ public function normal(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { ApplicationPreview::create([ 'git_type' => 'github', 'application_id' => $application->id, diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php index 7ae376f1f..f6e6cf7e7 100644 --- a/app/Http/Controllers/Webhook/Gitlab.php +++ b/app/Http/Controllers/Webhook/Gitlab.php @@ -40,7 +40,7 @@ public function manual(Request $request) $x_gitlab_token = data_get($headers, 'x-gitlab-token.0'); $x_gitlab_event = data_get($payload, 'object_kind'); $allowed_events = ['push', 'merge_request']; - if (!in_array($x_gitlab_event, $allowed_events)) { + if (! in_array($x_gitlab_event, $allowed_events)) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Event not allowed. Only push and merge_request events are allowed.', @@ -55,7 +55,7 @@ public function manual(Request $request) if (Str::isMatch('/refs\/heads\/*/', $branch)) { $branch = Str::after($branch, 'refs/heads/'); } - if (!$branch) { + if (! $branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', @@ -67,7 +67,7 @@ public function manual(Request $request) $removed_files = data_get($payload, 'commits.*.removed'); $modified_files = data_get($payload, 'commits.*.modified'); $changed_files = collect($added_files)->concat($removed_files)->concat($modified_files)->unique()->flatten(); - ray('Manual Webhook GitLab Push Event with branch: ' . $branch); + ray('Manual Webhook GitLab Push Event with branch: '.$branch); } if ($x_gitlab_event === 'merge_request') { $action = data_get($payload, 'object_attributes.action'); @@ -76,7 +76,7 @@ public function manual(Request $request) $full_name = data_get($payload, 'project.path_with_namespace'); $pull_request_id = data_get($payload, 'object_attributes.iid'); $pull_request_html_url = data_get($payload, 'object_attributes.url'); - if (!$branch) { + if (! $branch) { $return_payloads->push([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', @@ -84,7 +84,7 @@ public function manual(Request $request) return response($return_payloads); } - ray('Webhook GitHub Pull Request Event with branch: ' . $branch . ' and base branch: ' . $base_branch . ' and pull request id: ' . $pull_request_id); + ray('Webhook GitHub Pull Request Event with branch: '.$branch.' and base branch: '.$base_branch.' and pull request id: '.$pull_request_id); } $applications = Application::where('git_repository', 'like', "%$full_name%"); if ($x_gitlab_event === 'push') { @@ -122,13 +122,13 @@ public function manual(Request $request) continue; } $isFunctional = $application->destination->server->isFunctional(); - if (!$isFunctional) { + if (! $isFunctional) { $return_payloads->push([ 'application' => $application->name, 'status' => 'failed', 'message' => 'Server is not functional', ]); - ray('Server is not functional: ' . $application->destination->server->name); + ray('Server is not functional: '.$application->destination->server->name); continue; } @@ -136,7 +136,7 @@ public function manual(Request $request) if ($application->isDeployable()) { $is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files); if ($is_watch_path_triggered || is_null($application->watch_paths)) { - ray('Deploying ' . $application->name . ' with branch ' . $branch); + ray('Deploying '.$application->name.' with branch '.$branch); $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $application, @@ -171,7 +171,7 @@ public function manual(Request $request) 'application_uuid' => $application->uuid, 'application_name' => $application->name, ]); - ray('Deployments disabled for ' . $application->name); + ray('Deployments disabled for '.$application->name); } } if ($x_gitlab_event === 'merge_request') { @@ -179,7 +179,7 @@ public function manual(Request $request) if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2(7); $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); - if (!$found) { + if (! $found) { if ($application->build_pack === 'dockercompose') { $pr_app = ApplicationPreview::create([ 'git_type' => 'gitlab', @@ -207,7 +207,7 @@ public function manual(Request $request) is_webhook: true, git_type: 'gitlab' ); - ray('Deploying preview for ' . $application->name . ' with branch ' . $branch . ' and base branch ' . $base_branch . ' and pull request id ' . $pull_request_id); + ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id); $return_payloads->push([ 'application' => $application->name, 'status' => 'success', @@ -219,7 +219,7 @@ public function manual(Request $request) 'status' => 'failed', 'message' => 'Preview deployments disabled', ]); - ray('Preview deployments disabled for ' . $application->name); + ray('Preview deployments disabled for '.$application->name); } } elseif ($action === 'closed' || $action === 'close' || $action === 'merge') { $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index ea8a19764..f29cd43ce 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -131,10 +131,12 @@ public function add(int $pull_request_id, ?string $pull_request_html_url = null) } } - public function add_and_deploy(int $pull_request_id, ?string $pull_request_html_url = null) { + public function add_and_deploy(int $pull_request_id, ?string $pull_request_html_url = null) + { $this->add($pull_request_id, $pull_request_html_url); $this->deploy($pull_request_id, $pull_request_html_url); } + public function deploy(int $pull_request_id, ?string $pull_request_html_url = null) { try { From 9592076d45a5c71dc051bd1216a81d01f02b945f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 12:28:09 +0200 Subject: [PATCH 020/212] fix: do no truncate repositories wtih domain (git) in it --- .../Project/New/PublicGitRepository.php | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 739061f1f..5ba14e250 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -117,19 +117,19 @@ public function load_branch() if (str($this->repository_url)->startsWith('git@')) { $github_instance = str($this->repository_url)->after('git@')->before(':'); $repository = str($this->repository_url)->after(':')->before('.git'); - $this->repository_url = 'https://'.str($github_instance).'/'.$repository; + $this->repository_url = 'https://' . str($github_instance) . '/' . $repository; } if ( (str($this->repository_url)->startsWith('https://') || str($this->repository_url)->startsWith('http://')) && - ! str($this->repository_url)->endsWith('.git') && - (! str($this->repository_url)->contains('github.com') || - ! str($this->repository_url)->contains('git.sr.ht')) + !str($this->repository_url)->endsWith('.git') && + (!str($this->repository_url)->contains('github.com') || + !str($this->repository_url)->contains('git.sr.ht')) ) { - $this->repository_url = $this->repository_url.'.git'; + $this->repository_url = $this->repository_url . '.git'; } - if (str($this->repository_url)->contains('github.com')) { - $this->repository_url = str($this->repository_url)->before('.git')->value(); + if (str($this->repository_url)->contains('github.com') && str($this->repository_url)->endsWith('.git')) { + $this->repository_url = str($this->repository_url)->beforeLast('.git')->value(); } } catch (\Throwable $e) { return handleError($e, $this); @@ -140,8 +140,7 @@ public function load_branch() $this->get_branch(); $this->selected_branch = $this->git_branch; } catch (\Throwable $e) { - ray($e->getMessage()); - if (! $this->branch_found && $this->git_branch == 'main') { + if (!$this->branch_found && $this->git_branch == 'main') { try { $this->git_branch = 'master'; $this->get_branch(); @@ -158,7 +157,7 @@ private function get_git_source() { $this->repository_url_parsed = Url::fromString($this->repository_url); $this->git_host = $this->repository_url_parsed->getHost(); - $this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2); + $this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2); $this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main'; if ($this->git_host == 'github.com') { @@ -193,10 +192,10 @@ public function submit() $environment_name = $this->parameters['environment_name']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (! $destination) { + if (!$destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (! $destination) { + if (!$destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); @@ -207,7 +206,7 @@ public function submit() if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services) { $server = $destination->server; $new_service = [ - 'name' => 'service'.str()->random(10), + 'name' => 'service' . str()->random(10), 'docker_compose_raw' => 'coolify', 'environment_id' => $environment->id, 'server_id' => $server->id, From 5c8277ea1d0e56be5d0eabb4825ad56184e1183f Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Wed, 12 Jun 2024 10:28:52 +0000 Subject: [PATCH 021/212] Fix styling --- .../Project/New/PublicGitRepository.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index 5ba14e250..7ac7883dc 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -117,16 +117,16 @@ public function load_branch() if (str($this->repository_url)->startsWith('git@')) { $github_instance = str($this->repository_url)->after('git@')->before(':'); $repository = str($this->repository_url)->after(':')->before('.git'); - $this->repository_url = 'https://' . str($github_instance) . '/' . $repository; + $this->repository_url = 'https://'.str($github_instance).'/'.$repository; } if ( (str($this->repository_url)->startsWith('https://') || str($this->repository_url)->startsWith('http://')) && - !str($this->repository_url)->endsWith('.git') && - (!str($this->repository_url)->contains('github.com') || - !str($this->repository_url)->contains('git.sr.ht')) + ! str($this->repository_url)->endsWith('.git') && + (! str($this->repository_url)->contains('github.com') || + ! str($this->repository_url)->contains('git.sr.ht')) ) { - $this->repository_url = $this->repository_url . '.git'; + $this->repository_url = $this->repository_url.'.git'; } if (str($this->repository_url)->contains('github.com') && str($this->repository_url)->endsWith('.git')) { $this->repository_url = str($this->repository_url)->beforeLast('.git')->value(); @@ -140,7 +140,7 @@ public function load_branch() $this->get_branch(); $this->selected_branch = $this->git_branch; } catch (\Throwable $e) { - if (!$this->branch_found && $this->git_branch == 'main') { + if (! $this->branch_found && $this->git_branch == 'main') { try { $this->git_branch = 'master'; $this->get_branch(); @@ -157,7 +157,7 @@ private function get_git_source() { $this->repository_url_parsed = Url::fromString($this->repository_url); $this->git_host = $this->repository_url_parsed->getHost(); - $this->git_repository = $this->repository_url_parsed->getSegment(1) . '/' . $this->repository_url_parsed->getSegment(2); + $this->git_repository = $this->repository_url_parsed->getSegment(1).'/'.$this->repository_url_parsed->getSegment(2); $this->git_branch = $this->repository_url_parsed->getSegment(4) ?? 'main'; if ($this->git_host == 'github.com') { @@ -192,10 +192,10 @@ public function submit() $environment_name = $this->parameters['environment_name']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); - if (!$destination) { + if (! $destination) { $destination = SwarmDocker::where('uuid', $destination_uuid)->first(); } - if (!$destination) { + if (! $destination) { throw new \Exception('Destination not found. What?!'); } $destination_class = $destination->getMorphClass(); @@ -206,7 +206,7 @@ public function submit() if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services) { $server = $destination->server; $new_service = [ - 'name' => 'service' . str()->random(10), + 'name' => 'service'.str()->random(10), 'docker_compose_raw' => 'coolify', 'environment_id' => $environment->id, 'server_id' => $server->id, From 7a63a17b664beeee17a60261078b15a0aa1e76c5 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 12 Jun 2024 12:30:59 +0200 Subject: [PATCH 022/212] feat: add supaguide logo to donations section --- README.md | 2 +- other/logos/supaguide.png | Bin 0 -> 16916 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 other/logos/supaguide.png diff --git a/README.md b/README.md index bfa849fa9..129c07bf7 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ # Donations bc direct logo quantcdn logo arcjet logo - +supaguide logo ## Github Sponsors ($40+) SerpAPI diff --git a/other/logos/supaguide.png b/other/logos/supaguide.png new file mode 100644 index 0000000000000000000000000000000000000000..195f3ce92a11a2f87ae9482ccbaedbd1fa4e9c46 GIT binary patch literal 16916 zcmeIaWmsIx)-Z^>yK8W1+}+*Xp>cO7xNCsm1PB&1xVt8}yF+ld5F~t^mbOid*V4)*xsi+7>4=Tff!GdFeLH+^(eZas8z@UGZ!NAPG3IA2L1*iQB z1_A`b3I;lZp@Y7z;QYVKzi`4LZNQ-Z!i@o4e-#;jo&LUxn>u=$lF_PJIGS6yTDa1I zfk~KpT7V!R^TGcuK|aLaR3O;%A^$Fe<@};&sVTnS{F;o8ff_oPWdxC*M zBim@`xa%k?@By71m`%-{%q*C_9h`q*feCo?fr<_m?xtkk4)%_2eBOc-zajWQWL(TF%q$c_2xMer0f1!hZ2~t?QyF2p%0A5~R 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 LBvCD967qilD!3iZ literal 0 HcmV?d00001 From 25649f578d74b01f78bbdc6489130f84e11a2348 Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 12 Jun 2024 13:33:06 +0200 Subject: [PATCH 023/212] Create de.json --- lang/de.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/de.json diff --git a/lang/de.json b/lang/de.json new file mode 100644 index 000000000..8c9b41de1 --- /dev/null +++ b/lang/de.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Anmelden", + "auth.login.azure": "Mit Microsoft anmelden", + "auth.login.bitbucket": "Mit Bitbucket anmelden", + "auth.login.github": "Mit GitHub anmelden", + "auth.login.gitlab": "Mit GitLab anmelden", + "auth.login.google": "Mit Google anmelden", + "auth.already_registered": "Bereits registriert?", + "auth.confirm_password": "Passwort bestätigen", + "auth.forgot_password": "Passwort vergessen", + "auth.forgot_password_send_email": "Passwort zurücksetzen E-Mail senden", + "auth.register_now": "Registrieren", + "auth.logout": "Abmelden", + "auth.register": "Registrieren", + "auth.registration_disabled": "Registrierung ist deaktiviert. Bitte kontaktiere einen Administrator.", + "auth.reset_password": "Passwort zurücksetzen", + "auth.failed": "Diese Anmeldedaten wurden nicht gefunden.", + "auth.failed.callback": "Fehlerhafte Verarbeitung der Antwort des Anmeldeanbieters.", + "auth.failed.password": "Das angegebene Passwort ist inkorrekt.", + "auth.failed.email": "Wir können keinen Benutzer mit dieser E-Mail Adresse finden.", + "auth.throttle": "Zu viele anmeldeversuche. Bitte versuchen es in :seconds Sekunden erneut.", + "input.name": "Name", + "input.email": "E-Mail", + "input.password": "Passwort", + "input.password.again": "Passwort wiederholen", + "input.code": "Einmalcode", + "input.recovery_code": "Wiederherstellungscode", + "button.save": "Speichern", + "repository.url": "Beispiele
Für öffentliche Reposetories benutze https://....
Für private Repositories benutze git@....

https://github.com/coollabsio/coolify-examples main Branch wird ausgewählt
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify Branch wird ausgewählt.
https://gitea.com/sedlav/expressjs.git main Branch wird ausgewählt.
https://gitlab.com/andrasbacsai/nodejs-example.git main Branch wird ausgewählt." +} From 19b38074a54933521d008392d7ba5921510dc371 Mon Sep 17 00:00:00 2001 From: Rishikesh S Date: Wed, 12 Jun 2024 19:04:00 +0530 Subject: [PATCH 024/212] add zorin as a os type --- scripts/install.sh | 5 +++++ 1 file changed, 5 insertions(+) mode change 100644 => 100755 scripts/install.sh diff --git a/scripts/install.sh b/scripts/install.sh old mode 100644 new mode 100755 index b55db4f7d..2aaaebaef --- a/scripts/install.sh +++ b/scripts/install.sh @@ -27,6 +27,11 @@ if [ "$OS_TYPE" = "linuxmint" ]; then OS_TYPE="ubuntu" fi +#Check if the OS is zorin, if so, change it to ubuntu +if [ "$OS_TYPE" = "zorin" ]; then + OS_TYPE="ubuntu" +fi + if [ "$OS_TYPE" = "arch" ] || [ "$OS_TYPE" = "archarm" ]; then OS_VERSION="rolling" else From 6bb565ee6768a8a0b2cd52e29c2c1d21e8bd5116 Mon Sep 17 00:00:00 2001 From: Julian Date: Thu, 13 Jun 2024 08:30:41 +0100 Subject: [PATCH 025/212] Create it.json chore: add Italian translations --- lang/it.json | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 lang/it.json diff --git a/lang/it.json b/lang/it.json new file mode 100644 index 000000000..6e4feb9cc --- /dev/null +++ b/lang/it.json @@ -0,0 +1,30 @@ +{ + "auth.login": "Accedi", + "auth.login.azure": "Accedi con Microsoft", + "auth.login.bitbucket": "Accedi con Bitbucket", + "auth.login.github": "Accedi con GitHub", + "auth.login.gitlab": "Accedi con Gitlab", + "auth.login.google": "Accedi con Google", + "auth.already_registered": "Già registrato?", + "auth.confirm_password": "Conferma password", + "auth.forgot_password": "Password dimenticata", + "auth.forgot_password_send_email": "Invia email per reimpostare la password", + "auth.register_now": "Registrati", + "auth.logout": "Esci", + "auth.register": "Registrati", + "auth.registration_disabled": "La registrazione è disabilitata. Si prega di contattare l'amministratore.", + "auth.reset_password": "Reimposta password", + "auth.failed": "Queste credenziali non corrispondono ai nostri record.", + "auth.failed.callback": "Errore durante l'elaborazione del callback dal provider di accesso.", + "auth.failed.password": "La password fornita non è corretta.", + "auth.failed.email": "Non possiamo trovare un utente con questo indirizzo email.", + "auth.throttle": "Troppi tentativi di accesso. Per favore riprova tra :seconds secondi.", + "input.name": "Nome", + "input.email": "Email", + "input.password": "Password", + "input.password.again": "Ripeti password", + "input.code": "Codice monouso", + "input.recovery_code": "Codice di recupero", + "button.save": "Salva", + "repository.url": "Esempi
Per i repository pubblici, utilizza https://....
Per i repository privati, utilizza git@....

https://github.com/coollabsio/coolify-examples verrà selezionato il branch main
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify verrà selezionato il branch nodejs-fastify.
https://gitea.com/sedlav/expressjs.git verrà selezionato il branch main.
https://gitlab.com/andrasbacsai/nodejs-example.git verrà selezionato il branch main." +} From 95d3ebdc2dd8e026afcd0107d58011bbf847e73b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 10:18:35 +0200 Subject: [PATCH 026/212] fix: in services should edit compose file for volumes and envs --- .../project/service/configuration.blade.php | 3 ++- .../shared/environment-variable/all.blade.php | 8 +++++--- .../livewire/project/shared/storages/show.blade.php | 13 ++++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/resources/views/livewire/project/service/configuration.blade.php b/resources/views/livewire/project/service/configuration.blade.php index d1ff54a5c..272de2f4f 100644 --- a/resources/views/livewire/project/service/configuration.blade.php +++ b/resources/views/livewire/project/service/configuration.blade.php @@ -172,7 +172,8 @@ class="w-4 h-4 dark:text-warning text-coollabs"

Storages

Persistent storage to preserve data between deployments.
- Please modify storage layout in your Docker Compose file. +
If you would like to add a volume, you must add it to + your compose file (General tab).
@foreach ($applications as $application) diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index d2693b983..42a2b48a7 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -6,19 +6,21 @@ + {{ $view === 'normal' ? 'Developer view' : 'Normal view' }} @endif - {{ $view === 'normal' ? 'Developer view' : 'Normal view' }}
Environment variables (secrets) for this resource.
@if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose')
+ helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order." + instantSave>
@endif @if ($resource->type() === 'service' || $resource?->build_pack === 'dockercompose')
Hardcoded variables are not shown here.
+
If you would like to add a variable, you must add it to your compose file (General tab).
@endif @if ($view === 'normal') diff --git a/resources/views/livewire/project/shared/storages/show.blade.php b/resources/views/livewire/project/shared/storages/show.blade.php index 935d0a43b..6b429a535 100644 --- a/resources/views/livewire/project/shared/storages/show.blade.php +++ b/resources/views/livewire/project/shared/storages/show.blade.php @@ -2,8 +2,15 @@
@if ($isReadOnly) @if ($isFirst) - + @if ( + $storage->resource_type === 'App\Models\ServiceApplication' || + $storage->resource_type === 'App\Models\ServiceDatabase') + + @else + + @endif @if ($isService || $startedAt) @else - + @endif @else From 7485c1240bcc275ce90ae5d00801ea357c800e8b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 12:02:52 +0200 Subject: [PATCH 027/212] feat: nixpacks now could reach local dbs internally --- app/Jobs/ApplicationDeploymentJob.php | 140 +++++++++++++++----------- 1 file changed, 80 insertions(+), 60 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 72d8c0ad1..05fdf5c21 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -188,8 +188,8 @@ public function __construct(int $application_deployment_queue_id) $this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0; $this->basedir = $this->application->generateBaseDir($this->deployment_uuid); - $this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/'); - $this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}"; + $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/'); + $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}"; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); @@ -217,7 +217,7 @@ public function handle(): void $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); - if (! $this->server->isFunctional()) { + if (!$this->server->isFunctional()) { $this->application_deployment_queue->addLogEntry('Server is not functional.'); $this->fail('Server is not functional.'); @@ -227,7 +227,7 @@ public function handle(): void // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (! is_null($allContainers)) { + if (!is_null($allContainers)) { $allContainers = format_docker_command_output_to_json($allContainers); $ips = collect([]); if (count($allContainers) > 0) { @@ -397,14 +397,14 @@ private function deploy_docker_compose_buildpack() } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; - if (! str($this->docker_compose_custom_start_command)->contains('--project-directory')) { - $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); + if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) { + $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); } } if (data_get($this->application, 'docker_compose_custom_build_command')) { $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command; - if (! str($this->docker_compose_custom_build_command)->contains('--project-directory')) { - $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); + if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) { + $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); } } if ($this->pull_request_id === 0) { @@ -425,7 +425,7 @@ private function deploy_docker_compose_buildpack() } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id')); $this->save_environment_variables(); - if (! is_null($this->env_filename)) { + if (!is_null($this->env_filename)) { $services = collect($composeFile['services']); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; @@ -536,7 +536,7 @@ private function deploy_dockerfile_buildpack() $this->check_git_if_build_needed(); $this->generate_image_names(); $this->clone_repository(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -560,7 +560,7 @@ private function deploy_nixpacks_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -585,7 +585,7 @@ private function deploy_static_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -664,7 +664,7 @@ private function push_to_docker_registry() return; } - ray('push_to_docker_registry noww: '.$this->production_image_name); + ray('push_to_docker_registry noww: ' . $this->production_image_name); try { instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); $this->application_deployment_queue->addLogEntry('----------------------------------------'); @@ -755,7 +755,7 @@ private function should_skip_build() return true; } - if (! $this->application->isConfigurationChanged()) { + if (!$this->application->isConfigurationChanged()) { $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped."); $this->generate_compose_file(); $this->push_to_docker_registry(); @@ -811,7 +811,7 @@ private function save_environment_variables() $this->env_filename = ".env-pr-$this->pull_request_id"; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (! is_null($this->commit)) { + if (!is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -833,12 +833,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\''.$real_value.'\''; + $real_value = '\'' . $real_value . '\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key.'='.$real_value); + $envs->push($env->key . '=' . $real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { @@ -852,7 +852,7 @@ private function save_environment_variables() $this->env_filename = '.env'; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (! is_null($this->commit)) { + if (!is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -874,13 +874,13 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\''.$real_value.'\''; + $real_value = '\'' . $real_value . '\''; } else { $real_value = escapeEnvVariables($env->real_value); ray($real_value); } } - $envs->push($env->key.'='.$real_value); + $envs->push($env->key . '=' . $real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) { @@ -1233,7 +1233,7 @@ private function deploy_to_additional_destinations() destination: $destination, no_questions_asked: true, ); - $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: ".route('project.application.deployment.show', [ + $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [ 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, @@ -1295,7 +1295,7 @@ private function check_git_if_build_needed() ], ); } - if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) { + if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) { $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t"); $this->application_deployment_queue->commit = $this->commit; $this->application_deployment_queue->save(); @@ -1379,10 +1379,10 @@ private function generate_nixpacks_confs() if (count($aptPkgs) === 0) { data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { - if (! in_array('curl', $aptPkgs)) { + if (!in_array('curl', $aptPkgs)) { $aptPkgs[] = 'curl'; } - if (! in_array('wget', $aptPkgs)) { + if (!in_array('wget', $aptPkgs)) { $aptPkgs[] = 'wget'; } data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs); @@ -1417,13 +1417,13 @@ private function generate_nixpacks_env_variables() $this->env_nixpacks_args = collect([]); if ($this->pull_request_id === 0) { foreach ($this->application->nixpacks_environment_variables as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } } else { foreach ($this->application->nixpacks_environment_variables_preview as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } @@ -1438,13 +1438,13 @@ private function generate_env_variables() $this->env_args->put('SOURCE_COMMIT', $this->commit); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } } else { foreach ($this->application->build_environment_variables_preview as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } @@ -1468,7 +1468,7 @@ private function generate_compose_file() $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { - return ! Str::startsWith($value, 'coolify.'); + return !Str::startsWith($value, 'coolify.'); }); $found_caddy_labels = $labels->filter(function ($value, $key) { return Str::startsWith($value, 'caddy_'); @@ -1559,7 +1559,7 @@ private function generate_compose_file() // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; // } // } - if (! is_null($this->env_filename)) { + if (!is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } $docker_compose['services'][$this->container_name]['healthcheck'] = [ @@ -1567,27 +1567,27 @@ private function generate_compose_file() 'CMD-SHELL', $this->generate_healthcheck_commands(), ], - 'interval' => $this->application->health_check_interval.'s', - 'timeout' => $this->application->health_check_timeout.'s', + 'interval' => $this->application->health_check_interval . 's', + 'timeout' => $this->application->health_check_timeout . 's', 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period.'s', + 'start_period' => $this->application->health_check_start_period . 's', ]; - if (! is_null($this->application->limits_cpuset)) { - data_set($docker_compose, 'services.'.$this->container_name.'.cpuset', $this->application->limits_cpuset); + if (!is_null($this->application->limits_cpuset)) { + data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); } if ($this->server->isSwarm()) { - data_forget($docker_compose, 'services.'.$this->container_name.'.container_name'); - data_forget($docker_compose, 'services.'.$this->container_name.'.expose'); - data_forget($docker_compose, 'services.'.$this->container_name.'.restart'); + data_forget($docker_compose, 'services.' . $this->container_name . '.container_name'); + data_forget($docker_compose, 'services.' . $this->container_name . '.expose'); + data_forget($docker_compose, 'services.' . $this->container_name . '.restart'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_limit'); - data_forget($docker_compose, 'services.'.$this->container_name.'.memswap_limit'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_swappiness'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_reservation'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpus'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpuset'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpu_shares'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit'); + data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpus'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares'); $docker_compose['services'][$this->container_name]['deploy'] = [ 'mode' => 'replicated', @@ -1653,7 +1653,7 @@ private function generate_compose_file() } } if ($this->application->isHealthcheckDisabled()) { - data_forget($docker_compose, 'services.'.$this->container_name.'.healthcheck'); + data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); } if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) { $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array; @@ -1731,9 +1731,9 @@ private function generate_local_persistent_volumes() $volume_name = $persistentStorage->name; } if ($this->pull_request_id !== 0) { - $volume_name = $volume_name.'-pr-'.$this->pull_request_id; + $volume_name = $volume_name . '-pr-' . $this->pull_request_id; } - $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; } return $local_persistent_volumes; @@ -1749,7 +1749,7 @@ private function generate_local_persistent_volumes_only_volume_names() $name = $persistentStorage->name; if ($this->pull_request_id !== 0) { - $name = $name.'-pr-'.$this->pull_request_id; + $name = $name . '-pr-' . $this->pull_request_id; } $local_persistent_volumes_names[$name] = [ @@ -1763,7 +1763,7 @@ private function generate_local_persistent_volumes_only_volume_names() private function generate_healthcheck_commands() { - if (! $this->application->health_check_port) { + if (!$this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { $health_check_port = $this->application->health_check_port; @@ -1841,13 +1841,23 @@ private function build_image() $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) { $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}"), '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, ]); } else { $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}"), '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->production_image_name} {$this->workdir}"; + $base64_build_command = base64_encode($build_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, 'bash /artifacts/build.sh'), 'hidden' => true, + ] + ); $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); } else { if ($this->force_rebuild) { @@ -1929,13 +1939,23 @@ private function build_image() $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) { $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}"), '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, ]); } else { $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}"), '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}"; + $base64_build_command = base64_encode($build_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, 'bash /artifacts/build.sh'), 'hidden' => true, + ] + ); $this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]); } else { if ($this->force_rebuild) { @@ -1966,7 +1986,7 @@ private function stop_running_container(bool $force = false) $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; }); } $containers->each(function ($container) { @@ -2098,8 +2118,8 @@ private function run_pre_deployment_command() foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container.'-'.$this->application->uuid)) { - $cmd = "sh -c '".str_replace("'", "'\''", $this->application->pre_deployment_command)."'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) { + $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'"; $exec = "docker exec {$containerName} {$cmd}"; $this->execute_remote_command( [ @@ -2124,8 +2144,8 @@ private function run_post_deployment_command() $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container.'-'.$this->application->uuid)) { - $cmd = "sh -c '".str_replace("'", "'\''", $this->application->post_deployment_command)."'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) { + $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'"; $exec = "docker exec {$containerName} {$cmd}"; try { $this->execute_remote_command( @@ -2164,7 +2184,7 @@ private function next(string $status) return; } if ($status === ApplicationDeploymentStatus::FINISHED->value) { - if (! $this->only_this_server) { + if (!$this->only_this_server) { $this->deploy_to_additional_destinations(); } $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); From dffcee6cf1b261d68708d81ec25cd23eb2892896 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Thu, 13 Jun 2024 10:03:36 +0000 Subject: [PATCH 028/212] Fix styling --- app/Jobs/ApplicationDeploymentJob.php | 112 +++++++++++++------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 05fdf5c21..ddcf5c2ff 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -188,8 +188,8 @@ public function __construct(int $application_deployment_queue_id) $this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0; $this->basedir = $this->application->generateBaseDir($this->deployment_uuid); - $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/'); - $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}"; + $this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/'); + $this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}"; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); @@ -217,7 +217,7 @@ public function handle(): void $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { $this->application_deployment_queue->addLogEntry('Server is not functional.'); $this->fail('Server is not functional.'); @@ -227,7 +227,7 @@ public function handle(): void // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (!is_null($allContainers)) { + if (! is_null($allContainers)) { $allContainers = format_docker_command_output_to_json($allContainers); $ips = collect([]); if (count($allContainers) > 0) { @@ -397,14 +397,14 @@ private function deploy_docker_compose_buildpack() } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; - if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) { - $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_start_command)->contains('--project-directory')) { + $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if (data_get($this->application, 'docker_compose_custom_build_command')) { $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command; - if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) { - $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_build_command)->contains('--project-directory')) { + $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if ($this->pull_request_id === 0) { @@ -425,7 +425,7 @@ private function deploy_docker_compose_buildpack() } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id')); $this->save_environment_variables(); - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $services = collect($composeFile['services']); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; @@ -536,7 +536,7 @@ private function deploy_dockerfile_buildpack() $this->check_git_if_build_needed(); $this->generate_image_names(); $this->clone_repository(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -560,7 +560,7 @@ private function deploy_nixpacks_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -585,7 +585,7 @@ private function deploy_static_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -664,7 +664,7 @@ private function push_to_docker_registry() return; } - ray('push_to_docker_registry noww: ' . $this->production_image_name); + ray('push_to_docker_registry noww: '.$this->production_image_name); try { instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); $this->application_deployment_queue->addLogEntry('----------------------------------------'); @@ -755,7 +755,7 @@ private function should_skip_build() return true; } - if (!$this->application->isConfigurationChanged()) { + if (! $this->application->isConfigurationChanged()) { $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped."); $this->generate_compose_file(); $this->push_to_docker_registry(); @@ -811,7 +811,7 @@ private function save_environment_variables() $this->env_filename = ".env-pr-$this->pull_request_id"; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -833,12 +833,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\'' . $real_value . '\''; + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { @@ -852,7 +852,7 @@ private function save_environment_variables() $this->env_filename = '.env'; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -874,13 +874,13 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\'' . $real_value . '\''; + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); ray($real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) { @@ -1233,7 +1233,7 @@ private function deploy_to_additional_destinations() destination: $destination, no_questions_asked: true, ); - $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [ + $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: ".route('project.application.deployment.show', [ 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, @@ -1295,7 +1295,7 @@ private function check_git_if_build_needed() ], ); } - if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) { + if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) { $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t"); $this->application_deployment_queue->commit = $this->commit; $this->application_deployment_queue->save(); @@ -1379,10 +1379,10 @@ private function generate_nixpacks_confs() if (count($aptPkgs) === 0) { data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { - if (!in_array('curl', $aptPkgs)) { + if (! in_array('curl', $aptPkgs)) { $aptPkgs[] = 'curl'; } - if (!in_array('wget', $aptPkgs)) { + if (! in_array('wget', $aptPkgs)) { $aptPkgs[] = 'wget'; } data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs); @@ -1417,13 +1417,13 @@ private function generate_nixpacks_env_variables() $this->env_nixpacks_args = collect([]); if ($this->pull_request_id === 0) { foreach ($this->application->nixpacks_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } } else { foreach ($this->application->nixpacks_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } @@ -1438,13 +1438,13 @@ private function generate_env_variables() $this->env_args->put('SOURCE_COMMIT', $this->commit); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } } else { foreach ($this->application->build_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } @@ -1468,7 +1468,7 @@ private function generate_compose_file() $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { - return !Str::startsWith($value, 'coolify.'); + return ! Str::startsWith($value, 'coolify.'); }); $found_caddy_labels = $labels->filter(function ($value, $key) { return Str::startsWith($value, 'caddy_'); @@ -1559,7 +1559,7 @@ private function generate_compose_file() // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; // } // } - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } $docker_compose['services'][$this->container_name]['healthcheck'] = [ @@ -1567,27 +1567,27 @@ private function generate_compose_file() 'CMD-SHELL', $this->generate_healthcheck_commands(), ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', + 'interval' => $this->application->health_check_interval.'s', + 'timeout' => $this->application->health_check_timeout.'s', 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's', + 'start_period' => $this->application->health_check_start_period.'s', ]; - if (!is_null($this->application->limits_cpuset)) { - data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); + if (! is_null($this->application->limits_cpuset)) { + data_set($docker_compose, 'services.'.$this->container_name.'.cpuset', $this->application->limits_cpuset); } if ($this->server->isSwarm()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.container_name'); - data_forget($docker_compose, 'services.' . $this->container_name . '.expose'); - data_forget($docker_compose, 'services.' . $this->container_name . '.restart'); + data_forget($docker_compose, 'services.'.$this->container_name.'.container_name'); + data_forget($docker_compose, 'services.'.$this->container_name.'.expose'); + data_forget($docker_compose, 'services.'.$this->container_name.'.restart'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpus'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.memswap_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_swappiness'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_reservation'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpus'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpuset'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpu_shares'); $docker_compose['services'][$this->container_name]['deploy'] = [ 'mode' => 'replicated', @@ -1653,7 +1653,7 @@ private function generate_compose_file() } } if ($this->application->isHealthcheckDisabled()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); + data_forget($docker_compose, 'services.'.$this->container_name.'.healthcheck'); } if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) { $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array; @@ -1731,9 +1731,9 @@ private function generate_local_persistent_volumes() $volume_name = $persistentStorage->name; } if ($this->pull_request_id !== 0) { - $volume_name = $volume_name . '-pr-' . $this->pull_request_id; + $volume_name = $volume_name.'-pr-'.$this->pull_request_id; } - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } return $local_persistent_volumes; @@ -1749,7 +1749,7 @@ private function generate_local_persistent_volumes_only_volume_names() $name = $persistentStorage->name; if ($this->pull_request_id !== 0) { - $name = $name . '-pr-' . $this->pull_request_id; + $name = $name.'-pr-'.$this->pull_request_id; } $local_persistent_volumes_names[$name] = [ @@ -1763,7 +1763,7 @@ private function generate_local_persistent_volumes_only_volume_names() private function generate_healthcheck_commands() { - if (!$this->application->health_check_port) { + if (! $this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { $health_check_port = $this->application->health_check_port; @@ -1986,7 +1986,7 @@ private function stop_running_container(bool $force = false) $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; }); } $containers->each(function ($container) { @@ -2118,8 +2118,8 @@ private function run_pre_deployment_command() foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->pre_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; $this->execute_remote_command( [ @@ -2144,8 +2144,8 @@ private function run_post_deployment_command() $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->post_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; try { $this->execute_remote_command( @@ -2184,7 +2184,7 @@ private function next(string $status) return; } if ($status === ApplicationDeploymentStatus::FINISHED->value) { - if (!$this->only_this_server) { + if (! $this->only_this_server) { $this->deploy_to_additional_destinations(); } $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); From c35f6e926d1f2c42732363115708c37bb5cf0fee Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 12:25:10 +0200 Subject: [PATCH 029/212] refactor: update text color for stderr output in deployment show view --- .../livewire/project/application/deployment/show.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index b65b463f0..32cc15e54 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -90,7 +90,7 @@ class="fixed top-4 right-16" x-on:click="toggleScroll"> $line['hidden'], - 'text-red-500 font-bold whitespace-pre-line' => $line['type'] == 'stderr', + 'text-coollabs font-bold whitespace-pre-line' => $line['type'] == 'stderr', ])>[{{ $line['timestamp'] }}] @if ($line['hidden'])

[COMMAND] {{ $line['command'] }}
[OUTPUT] @endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://')) From 07ae971ae15386d39a56cc2c407287ba0ed44ca7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 12:29:57 +0200 Subject: [PATCH 030/212] feat: Add Tigris logo to other/logos directory --- README.md | 29 +++++++++++++++-------------- other/logos/tigris.svg | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 other/logos/tigris.svg diff --git a/README.md b/README.md index 129c07bf7..2f61468a1 100644 --- a/README.md +++ b/README.md @@ -36,22 +36,23 @@ # Donations cccareers logo hetzner logo logto logo -bc direct logo -quantcdn logo -arcjet logo -supaguide logo +bc direct logo +quantcdn logo +arcjet logo +supaguide logo +tigris logo ## Github Sponsors ($40+) -SerpAPI -typebot - +SerpAPI +typebot + -Lightspeed.run - FlintCompany -American Cloud -CryptoJobsList -Thompson Edolo -UXWizz +Lightspeed.run + FlintCompany +American Cloud +CryptoJobsList +Thompson Edolo +UXWizz Younes Barrad Automaze Corentin Clichy @@ -109,7 +110,7 @@ # Recognitions

-Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt +Coolify - An open-source & self-hostable Heroku, Netlify alternative | Product Hunt coollabsio%2Fcoolify | Trendshift diff --git a/other/logos/tigris.svg b/other/logos/tigris.svg new file mode 100644 index 000000000..367c59f2d --- /dev/null +++ b/other/logos/tigris.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + From e4e9de0a537b267c028aaf05a1785da689288320 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 12:51:55 +0200 Subject: [PATCH 031/212] refactor: Update text color for stderr output in deployment show view --- .../livewire/project/application/deployment/show.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index 32cc15e54..f97914ec2 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -89,8 +89,8 @@ class="fixed top-4 right-16" x-on:click="toggleScroll">count() > 0) @foreach (decode_remote_command_output($application_deployment_queue) as $line) $line['hidden'], - 'text-coollabs font-bold whitespace-pre-line' => $line['type'] == 'stderr', + 'text-coollabs dark:text-warning whitespace-pre-line' => $line['hidden'], + 'text-red-500 font-bold whitespace-pre-line' => $line['type'] == 'stderr', ])>[{{ $line['timestamp'] }}] @if ($line['hidden'])

[COMMAND] {{ $line['command'] }}
[OUTPUT] @endif @if (str($line['output'])->contains('http://') || str($line['output'])->contains('https://')) From 566faba6e349150c2769cc4c3f58f41c03d495dd Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 13:14:24 +0200 Subject: [PATCH 032/212] fix: handle laravel deployment better --- app/Jobs/ApplicationDeploymentJob.php | 142 +++++++++++++++----------- 1 file changed, 80 insertions(+), 62 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index ddcf5c2ff..423487cb1 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -9,6 +9,7 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationPreview; +use App\Models\EnvironmentVariable; use App\Models\GithubApp; use App\Models\GitlabApp; use App\Models\Server; @@ -188,8 +189,8 @@ public function __construct(int $application_deployment_queue_id) $this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0; $this->basedir = $this->application->generateBaseDir($this->deployment_uuid); - $this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/'); - $this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}"; + $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/'); + $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}"; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); @@ -217,7 +218,7 @@ public function handle(): void $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); - if (! $this->server->isFunctional()) { + if (!$this->server->isFunctional()) { $this->application_deployment_queue->addLogEntry('Server is not functional.'); $this->fail('Server is not functional.'); @@ -227,7 +228,7 @@ public function handle(): void // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (! is_null($allContainers)) { + if (!is_null($allContainers)) { $allContainers = format_docker_command_output_to_json($allContainers); $ips = collect([]); if (count($allContainers) > 0) { @@ -397,14 +398,14 @@ private function deploy_docker_compose_buildpack() } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; - if (! str($this->docker_compose_custom_start_command)->contains('--project-directory')) { - $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); + if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) { + $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); } } if (data_get($this->application, 'docker_compose_custom_build_command')) { $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command; - if (! str($this->docker_compose_custom_build_command)->contains('--project-directory')) { - $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); + if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) { + $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); } } if ($this->pull_request_id === 0) { @@ -425,7 +426,7 @@ private function deploy_docker_compose_buildpack() } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id')); $this->save_environment_variables(); - if (! is_null($this->env_filename)) { + if (!is_null($this->env_filename)) { $services = collect($composeFile['services']); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; @@ -536,7 +537,7 @@ private function deploy_dockerfile_buildpack() $this->check_git_if_build_needed(); $this->generate_image_names(); $this->clone_repository(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -560,7 +561,7 @@ private function deploy_nixpacks_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -585,7 +586,7 @@ private function deploy_static_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (! $this->force_rebuild) { + if (!$this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -664,7 +665,7 @@ private function push_to_docker_registry() return; } - ray('push_to_docker_registry noww: '.$this->production_image_name); + ray('push_to_docker_registry noww: ' . $this->production_image_name); try { instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); $this->application_deployment_queue->addLogEntry('----------------------------------------'); @@ -755,7 +756,7 @@ private function should_skip_build() return true; } - if (! $this->application->isConfigurationChanged()) { + if (!$this->application->isConfigurationChanged()) { $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped."); $this->generate_compose_file(); $this->push_to_docker_registry(); @@ -811,7 +812,7 @@ private function save_environment_variables() $this->env_filename = ".env-pr-$this->pull_request_id"; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (! is_null($this->commit)) { + if (!is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -833,12 +834,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\''.$real_value.'\''; + $real_value = '\'' . $real_value . '\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key.'='.$real_value); + $envs->push($env->key . '=' . $real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { @@ -852,7 +853,7 @@ private function save_environment_variables() $this->env_filename = '.env'; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (! is_null($this->commit)) { + if (!is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -874,13 +875,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\''.$real_value.'\''; + $real_value = '\'' . $real_value . '\''; } else { $real_value = escapeEnvVariables($env->real_value); - ray($real_value); } } - $envs->push($env->key.'='.$real_value); + $envs->push($env->key . '=' . $real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) { @@ -946,9 +946,8 @@ private function save_environment_variables() } } - private function framework_based_notification() + private function laravel_finetunes() { - // Laravel old env variables if ($this->pull_request_id === 0) { $nixpacks_php_fallback_path = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $nixpacks_php_root_dir = $this->application->environment_variables->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); @@ -956,9 +955,21 @@ private function framework_based_notification() $nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); } - if ($nixpacks_php_fallback_path?->value === '/index.php' && $nixpacks_php_root_dir?->value === '/app/public' && $this->newVersionIsHealthy === false) { - $this->application_deployment_queue->addLogEntry('There was a change in how Laravel is deployed. Please update your environment variables to match the new deployment method. More details here: https://coolify.io/docs/resources/laravel', 'stderr'); + if (!$nixpacks_php_fallback_path) { + $nixpacks_php_fallback_path = new EnvironmentVariable(); + $nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH'; + $nixpacks_php_fallback_path->value = '/index.php'; + $nixpacks_php_fallback_path->application_id = $this->application->id; + $nixpacks_php_fallback_path->save(); } + if (!$nixpacks_php_root_dir) { + $nixpacks_php_root_dir = new EnvironmentVariable(); + $nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR'; + $nixpacks_php_root_dir->value = '/app/public'; + $nixpacks_php_root_dir->application_id = $this->application->id; + $nixpacks_php_root_dir->save(); + } + return [$nixpacks_php_fallback_path, $nixpacks_php_root_dir]; } private function rolling_update() @@ -1005,7 +1016,6 @@ private function rolling_update() $this->application_deployment_queue->addLogEntry('Rolling update completed.'); } } - $this->framework_based_notification(); } private function health_check() @@ -1233,7 +1243,7 @@ private function deploy_to_additional_destinations() destination: $destination, no_questions_asked: true, ); - $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: ".route('project.application.deployment.show', [ + $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [ 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, @@ -1295,7 +1305,7 @@ private function check_git_if_build_needed() ], ); } - if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) { + if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) { $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t"); $this->application_deployment_queue->commit = $this->commit; $this->application_deployment_queue->save(); @@ -1366,12 +1376,14 @@ private function generate_nixpacks_confs() throw new RuntimeException('Nixpacks failed to detect the application type. Please check the documentation of Nixpacks: https://nixpacks.com/docs/providers'); } } + if ($this->saved_outputs->get('nixpacks_plan')) { $this->nixpacks_plan = $this->saved_outputs->get('nixpacks_plan'); if ($this->nixpacks_plan) { $this->application_deployment_queue->addLogEntry("Found application type: {$this->nixpacks_type}."); $this->application_deployment_queue->addLogEntry("If you need further customization, please check the documentation of Nixpacks: https://nixpacks.com/docs/providers/{$this->nixpacks_type}"); $parsed = Toml::Parse($this->nixpacks_plan); + // Do any modifications here $this->generate_env_variables(); $merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', []))); @@ -1379,15 +1391,21 @@ private function generate_nixpacks_confs() if (count($aptPkgs) === 0) { data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { - if (! in_array('curl', $aptPkgs)) { + if (!in_array('curl', $aptPkgs)) { $aptPkgs[] = 'curl'; } - if (! in_array('wget', $aptPkgs)) { + if (!in_array('wget', $aptPkgs)) { $aptPkgs[] = 'wget'; } data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs); } data_set($parsed, 'variables', $merged_envs->toArray()); + $is_laravel = data_get($parsed, 'variables.IS_LARAVEL', false); + if ($is_laravel) { + $variables = $this->laravel_finetunes(); + data_set($parsed, 'variables.NIXPACKS_PHP_FALLBACK_PATH', $variables[0]->value); + data_set($parsed, 'variables.NIXPACKS_PHP_ROOT_DIR', $variables[1]->value); + } $this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT); $this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true); } @@ -1417,13 +1435,13 @@ private function generate_nixpacks_env_variables() $this->env_nixpacks_args = collect([]); if ($this->pull_request_id === 0) { foreach ($this->application->nixpacks_environment_variables as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } } else { foreach ($this->application->nixpacks_environment_variables_preview as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } @@ -1438,13 +1456,13 @@ private function generate_env_variables() $this->env_args->put('SOURCE_COMMIT', $this->commit); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } } else { foreach ($this->application->build_environment_variables_preview as $env) { - if (! is_null($env->real_value)) { + if (!is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } @@ -1468,7 +1486,7 @@ private function generate_compose_file() $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { - return ! Str::startsWith($value, 'coolify.'); + return !Str::startsWith($value, 'coolify.'); }); $found_caddy_labels = $labels->filter(function ($value, $key) { return Str::startsWith($value, 'caddy_'); @@ -1559,7 +1577,7 @@ private function generate_compose_file() // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; // } // } - if (! is_null($this->env_filename)) { + if (!is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } $docker_compose['services'][$this->container_name]['healthcheck'] = [ @@ -1567,27 +1585,27 @@ private function generate_compose_file() 'CMD-SHELL', $this->generate_healthcheck_commands(), ], - 'interval' => $this->application->health_check_interval.'s', - 'timeout' => $this->application->health_check_timeout.'s', + 'interval' => $this->application->health_check_interval . 's', + 'timeout' => $this->application->health_check_timeout . 's', 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period.'s', + 'start_period' => $this->application->health_check_start_period . 's', ]; - if (! is_null($this->application->limits_cpuset)) { - data_set($docker_compose, 'services.'.$this->container_name.'.cpuset', $this->application->limits_cpuset); + if (!is_null($this->application->limits_cpuset)) { + data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); } if ($this->server->isSwarm()) { - data_forget($docker_compose, 'services.'.$this->container_name.'.container_name'); - data_forget($docker_compose, 'services.'.$this->container_name.'.expose'); - data_forget($docker_compose, 'services.'.$this->container_name.'.restart'); + data_forget($docker_compose, 'services.' . $this->container_name . '.container_name'); + data_forget($docker_compose, 'services.' . $this->container_name . '.expose'); + data_forget($docker_compose, 'services.' . $this->container_name . '.restart'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_limit'); - data_forget($docker_compose, 'services.'.$this->container_name.'.memswap_limit'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_swappiness'); - data_forget($docker_compose, 'services.'.$this->container_name.'.mem_reservation'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpus'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpuset'); - data_forget($docker_compose, 'services.'.$this->container_name.'.cpu_shares'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit'); + data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness'); + data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpus'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset'); + data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares'); $docker_compose['services'][$this->container_name]['deploy'] = [ 'mode' => 'replicated', @@ -1653,7 +1671,7 @@ private function generate_compose_file() } } if ($this->application->isHealthcheckDisabled()) { - data_forget($docker_compose, 'services.'.$this->container_name.'.healthcheck'); + data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); } if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) { $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array; @@ -1731,9 +1749,9 @@ private function generate_local_persistent_volumes() $volume_name = $persistentStorage->name; } if ($this->pull_request_id !== 0) { - $volume_name = $volume_name.'-pr-'.$this->pull_request_id; + $volume_name = $volume_name . '-pr-' . $this->pull_request_id; } - $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; } return $local_persistent_volumes; @@ -1749,7 +1767,7 @@ private function generate_local_persistent_volumes_only_volume_names() $name = $persistentStorage->name; if ($this->pull_request_id !== 0) { - $name = $name.'-pr-'.$this->pull_request_id; + $name = $name . '-pr-' . $this->pull_request_id; } $local_persistent_volumes_names[$name] = [ @@ -1763,7 +1781,7 @@ private function generate_local_persistent_volumes_only_volume_names() private function generate_healthcheck_commands() { - if (! $this->application->health_check_port) { + if (!$this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { $health_check_port = $this->application->health_check_port; @@ -1986,7 +2004,7 @@ private function stop_running_container(bool $force = false) $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; }); } $containers->each(function ($container) { @@ -2118,8 +2136,8 @@ private function run_pre_deployment_command() foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container.'-'.$this->application->uuid)) { - $cmd = "sh -c '".str_replace("'", "'\''", $this->application->pre_deployment_command)."'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) { + $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'"; $exec = "docker exec {$containerName} {$cmd}"; $this->execute_remote_command( [ @@ -2144,8 +2162,8 @@ private function run_post_deployment_command() $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container.'-'.$this->application->uuid)) { - $cmd = "sh -c '".str_replace("'", "'\''", $this->application->post_deployment_command)."'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) { + $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'"; $exec = "docker exec {$containerName} {$cmd}"; try { $this->execute_remote_command( @@ -2184,7 +2202,7 @@ private function next(string $status) return; } if ($status === ApplicationDeploymentStatus::FINISHED->value) { - if (! $this->only_this_server) { + if (!$this->only_this_server) { $this->deploy_to_additional_destinations(); } $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); From a521d8549a227da5a6adfe7c013eaa4dc2c78658 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Thu, 13 Jun 2024 11:15:09 +0000 Subject: [PATCH 033/212] Fix styling --- app/Jobs/ApplicationDeploymentJob.php | 117 +++++++++++++------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 423487cb1..449734c7a 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -189,8 +189,8 @@ public function __construct(int $application_deployment_queue_id) $this->is_this_additional_server = $this->application->additional_servers()->wherePivot('server_id', $this->server->id)->count() > 0; $this->basedir = $this->application->generateBaseDir($this->deployment_uuid); - $this->workdir = "{$this->basedir}" . rtrim($this->application->base_directory, '/'); - $this->configuration_dir = application_configuration_dir() . "/{$this->application->uuid}"; + $this->workdir = "{$this->basedir}".rtrim($this->application->base_directory, '/'); + $this->configuration_dir = application_configuration_dir()."/{$this->application->uuid}"; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; $this->container_name = generateApplicationContainerName($this->application, $this->pull_request_id); @@ -218,7 +218,7 @@ public function handle(): void $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); - if (!$this->server->isFunctional()) { + if (! $this->server->isFunctional()) { $this->application_deployment_queue->addLogEntry('Server is not functional.'); $this->fail('Server is not functional.'); @@ -228,7 +228,7 @@ public function handle(): void // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (!is_null($allContainers)) { + if (! is_null($allContainers)) { $allContainers = format_docker_command_output_to_json($allContainers); $ips = collect([]); if (count($allContainers) > 0) { @@ -398,14 +398,14 @@ private function deploy_docker_compose_buildpack() } if (data_get($this->application, 'docker_compose_custom_start_command')) { $this->docker_compose_custom_start_command = $this->application->docker_compose_custom_start_command; - if (!str($this->docker_compose_custom_start_command)->contains('--project-directory')) { - $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_start_command)->contains('--project-directory')) { + $this->docker_compose_custom_start_command = str($this->docker_compose_custom_start_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if (data_get($this->application, 'docker_compose_custom_build_command')) { $this->docker_compose_custom_build_command = $this->application->docker_compose_custom_build_command; - if (!str($this->docker_compose_custom_build_command)->contains('--project-directory')) { - $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory ' . $this->workdir)->value(); + if (! str($this->docker_compose_custom_build_command)->contains('--project-directory')) { + $this->docker_compose_custom_build_command = str($this->docker_compose_custom_build_command)->replaceFirst('compose', 'compose --project-directory '.$this->workdir)->value(); } } if ($this->pull_request_id === 0) { @@ -426,7 +426,7 @@ private function deploy_docker_compose_buildpack() } else { $composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id, preview_id: data_get($this, 'preview.id')); $this->save_environment_variables(); - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $services = collect($composeFile['services']); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; @@ -537,7 +537,7 @@ private function deploy_dockerfile_buildpack() $this->check_git_if_build_needed(); $this->generate_image_names(); $this->clone_repository(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -561,7 +561,7 @@ private function deploy_nixpacks_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -586,7 +586,7 @@ private function deploy_static_buildpack() $this->prepare_builder_image(); $this->check_git_if_build_needed(); $this->generate_image_names(); - if (!$this->force_rebuild) { + if (! $this->force_rebuild) { $this->check_image_locally_or_remotely(); if ($this->should_skip_build()) { return; @@ -665,7 +665,7 @@ private function push_to_docker_registry() return; } - ray('push_to_docker_registry noww: ' . $this->production_image_name); + ray('push_to_docker_registry noww: '.$this->production_image_name); try { instant_remote_process(["docker images --format '{{json .}}' {$this->production_image_name}"], $this->server); $this->application_deployment_queue->addLogEntry('----------------------------------------'); @@ -756,7 +756,7 @@ private function should_skip_build() return true; } - if (!$this->application->isConfigurationChanged()) { + if (! $this->application->isConfigurationChanged()) { $this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped."); $this->generate_compose_file(); $this->push_to_docker_registry(); @@ -812,7 +812,7 @@ private function save_environment_variables() $this->env_filename = ".env-pr-$this->pull_request_id"; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -834,12 +834,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\'' . $real_value . '\''; + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) { @@ -853,7 +853,7 @@ private function save_environment_variables() $this->env_filename = '.env'; // Add SOURCE_COMMIT if not exists if ($this->application->environment_variables->where('key', 'SOURCE_COMMIT')->isEmpty()) { - if (!is_null($this->commit)) { + if (! is_null($this->commit)) { $envs->push("SOURCE_COMMIT={$this->commit}"); } else { $envs->push('SOURCE_COMMIT=unknown'); @@ -875,12 +875,12 @@ private function save_environment_variables() $real_value = $env->real_value; } else { if ($env->is_literal || $env->is_multiline) { - $real_value = '\'' . $real_value . '\''; + $real_value = '\''.$real_value.'\''; } else { $real_value = escapeEnvVariables($env->real_value); } } - $envs->push($env->key . '=' . $real_value); + $envs->push($env->key.'='.$real_value); } // Add PORT if not exists, use the first port as default if ($this->application->environment_variables->where('key', 'PORT')->isEmpty()) { @@ -955,20 +955,21 @@ private function laravel_finetunes() $nixpacks_php_fallback_path = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_FALLBACK_PATH')->first(); $nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first(); } - if (!$nixpacks_php_fallback_path) { + if (! $nixpacks_php_fallback_path) { $nixpacks_php_fallback_path = new EnvironmentVariable(); $nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH'; $nixpacks_php_fallback_path->value = '/index.php'; $nixpacks_php_fallback_path->application_id = $this->application->id; $nixpacks_php_fallback_path->save(); } - if (!$nixpacks_php_root_dir) { + if (! $nixpacks_php_root_dir) { $nixpacks_php_root_dir = new EnvironmentVariable(); $nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR'; $nixpacks_php_root_dir->value = '/app/public'; $nixpacks_php_root_dir->application_id = $this->application->id; $nixpacks_php_root_dir->save(); } + return [$nixpacks_php_fallback_path, $nixpacks_php_root_dir]; } @@ -1243,7 +1244,7 @@ private function deploy_to_additional_destinations() destination: $destination, no_questions_asked: true, ); - $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: " . route('project.application.deployment.show', [ + $this->application_deployment_queue->addLogEntry("Deployment to {$server->name}. Logs: ".route('project.application.deployment.show', [ 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, @@ -1305,7 +1306,7 @@ private function check_git_if_build_needed() ], ); } - if ($this->saved_outputs->get('git_commit_sha') && !$this->rollback) { + if ($this->saved_outputs->get('git_commit_sha') && ! $this->rollback) { $this->commit = $this->saved_outputs->get('git_commit_sha')->before("\t"); $this->application_deployment_queue->commit = $this->commit; $this->application_deployment_queue->save(); @@ -1391,10 +1392,10 @@ private function generate_nixpacks_confs() if (count($aptPkgs) === 0) { data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { - if (!in_array('curl', $aptPkgs)) { + if (! in_array('curl', $aptPkgs)) { $aptPkgs[] = 'curl'; } - if (!in_array('wget', $aptPkgs)) { + if (! in_array('wget', $aptPkgs)) { $aptPkgs[] = 'wget'; } data_set($parsed, 'phases.setup.aptPkgs', $aptPkgs); @@ -1435,13 +1436,13 @@ private function generate_nixpacks_env_variables() $this->env_nixpacks_args = collect([]); if ($this->pull_request_id === 0) { foreach ($this->application->nixpacks_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } } else { foreach ($this->application->nixpacks_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}"); } } @@ -1456,13 +1457,13 @@ private function generate_env_variables() $this->env_args->put('SOURCE_COMMIT', $this->commit); if ($this->pull_request_id === 0) { foreach ($this->application->build_environment_variables as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } } else { foreach ($this->application->build_environment_variables_preview as $env) { - if (!is_null($env->real_value)) { + if (! is_null($env->real_value)) { $this->env_args->put($env->key, $env->real_value); } } @@ -1486,7 +1487,7 @@ private function generate_compose_file() $this->application->parseContainerLabels(); $labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels))); $labels = $labels->filter(function ($value, $key) { - return !Str::startsWith($value, 'coolify.'); + return ! Str::startsWith($value, 'coolify.'); }); $found_caddy_labels = $labels->filter(function ($value, $key) { return Str::startsWith($value, 'caddy_'); @@ -1577,7 +1578,7 @@ private function generate_compose_file() // $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; // } // } - if (!is_null($this->env_filename)) { + if (! is_null($this->env_filename)) { $docker_compose['services'][$this->container_name]['env_file'] = [$this->env_filename]; } $docker_compose['services'][$this->container_name]['healthcheck'] = [ @@ -1585,27 +1586,27 @@ private function generate_compose_file() 'CMD-SHELL', $this->generate_healthcheck_commands(), ], - 'interval' => $this->application->health_check_interval . 's', - 'timeout' => $this->application->health_check_timeout . 's', + 'interval' => $this->application->health_check_interval.'s', + 'timeout' => $this->application->health_check_timeout.'s', 'retries' => $this->application->health_check_retries, - 'start_period' => $this->application->health_check_start_period . 's', + 'start_period' => $this->application->health_check_start_period.'s', ]; - if (!is_null($this->application->limits_cpuset)) { - data_set($docker_compose, 'services.' . $this->container_name . '.cpuset', $this->application->limits_cpuset); + if (! is_null($this->application->limits_cpuset)) { + data_set($docker_compose, 'services.'.$this->container_name.'.cpuset', $this->application->limits_cpuset); } if ($this->server->isSwarm()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.container_name'); - data_forget($docker_compose, 'services.' . $this->container_name . '.expose'); - data_forget($docker_compose, 'services.' . $this->container_name . '.restart'); + data_forget($docker_compose, 'services.'.$this->container_name.'.container_name'); + data_forget($docker_compose, 'services.'.$this->container_name.'.expose'); + data_forget($docker_compose, 'services.'.$this->container_name.'.restart'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.memswap_limit'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_swappiness'); - data_forget($docker_compose, 'services.' . $this->container_name . '.mem_reservation'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpus'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpuset'); - data_forget($docker_compose, 'services.' . $this->container_name . '.cpu_shares'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.memswap_limit'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_swappiness'); + data_forget($docker_compose, 'services.'.$this->container_name.'.mem_reservation'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpus'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpuset'); + data_forget($docker_compose, 'services.'.$this->container_name.'.cpu_shares'); $docker_compose['services'][$this->container_name]['deploy'] = [ 'mode' => 'replicated', @@ -1671,7 +1672,7 @@ private function generate_compose_file() } } if ($this->application->isHealthcheckDisabled()) { - data_forget($docker_compose, 'services.' . $this->container_name . '.healthcheck'); + data_forget($docker_compose, 'services.'.$this->container_name.'.healthcheck'); } if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) { $docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array; @@ -1749,9 +1750,9 @@ private function generate_local_persistent_volumes() $volume_name = $persistentStorage->name; } if ($this->pull_request_id !== 0) { - $volume_name = $volume_name . '-pr-' . $this->pull_request_id; + $volume_name = $volume_name.'-pr-'.$this->pull_request_id; } - $local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path; + $local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path; } return $local_persistent_volumes; @@ -1767,7 +1768,7 @@ private function generate_local_persistent_volumes_only_volume_names() $name = $persistentStorage->name; if ($this->pull_request_id !== 0) { - $name = $name . '-pr-' . $this->pull_request_id; + $name = $name.'-pr-'.$this->pull_request_id; } $local_persistent_volumes_names[$name] = [ @@ -1781,7 +1782,7 @@ private function generate_local_persistent_volumes_only_volume_names() private function generate_healthcheck_commands() { - if (!$this->application->health_check_port) { + if (! $this->application->health_check_port) { $health_check_port = $this->application->ports_exposes_array[0]; } else { $health_check_port = $this->application->health_check_port; @@ -2004,7 +2005,7 @@ private function stop_running_container(bool $force = false) $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); if ($this->pull_request_id === 0) { $containers = $containers->filter(function ($container) { - return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name . '-pr-' . $this->pull_request_id; + return data_get($container, 'Names') !== $this->container_name && data_get($container, 'Names') !== $this->container_name.'-pr-'.$this->pull_request_id; }); } $containers->each(function ($container) { @@ -2136,8 +2137,8 @@ private function run_pre_deployment_command() foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->pre_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->pre_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->pre_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; $this->execute_remote_command( [ @@ -2162,8 +2163,8 @@ private function run_post_deployment_command() $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id); foreach ($containers as $container) { $containerName = data_get($container, 'Names'); - if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container . '-' . $this->application->uuid)) { - $cmd = "sh -c '" . str_replace("'", "'\''", $this->application->post_deployment_command) . "'"; + if ($containers->count() == 1 || str_starts_with($containerName, $this->application->post_deployment_command_container.'-'.$this->application->uuid)) { + $cmd = "sh -c '".str_replace("'", "'\''", $this->application->post_deployment_command)."'"; $exec = "docker exec {$containerName} {$cmd}"; try { $this->execute_remote_command( @@ -2202,7 +2203,7 @@ private function next(string $status) return; } if ($status === ApplicationDeploymentStatus::FINISHED->value) { - if (!$this->only_this_server) { + if (! $this->only_this_server) { $this->deploy_to_additional_destinations(); } $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview)); From e4ee149085169b4e0991c4f6d6a3947f53156db3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 13:37:19 +0200 Subject: [PATCH 034/212] refactor: Remove debug code for saving environment variables --- app/Livewire/Project/Shared/EnvironmentVariable/All.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 4c06bfe23..d67dae19e 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -112,7 +112,6 @@ public function saveVariables($isPreview) $this->resource->environment_variables_preview()->whereNotIn('key', array_keys($variables))->delete(); } else { $variables = parseEnvFormatToArray($this->variables); - ray($variables, $this->variables); $this->resource->environment_variables()->whereNotIn('key', array_keys($variables))->delete(); } foreach ($variables as $key => $variable) { From 30ac392af48b30ed3a9fdf034328078064ffa87d Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 13 Jun 2024 13:56:19 +0200 Subject: [PATCH 035/212] Update lang/de.json Co-authored-by: Marco <59606979+marcomaiermm@users.noreply.github.com> --- lang/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/de.json b/lang/de.json index 8c9b41de1..f2050283f 100644 --- a/lang/de.json +++ b/lang/de.json @@ -26,5 +26,5 @@ "input.code": "Einmalcode", "input.recovery_code": "Wiederherstellungscode", "button.save": "Speichern", - "repository.url": "Beispiele
Für öffentliche Reposetories benutze https://....
Für private Repositories benutze git@....

https://github.com/coollabsio/coolify-examples main Branch wird ausgewählt
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify Branch wird ausgewählt.
https://gitea.com/sedlav/expressjs.git main Branch wird ausgewählt.
https://gitlab.com/andrasbacsai/nodejs-example.git main Branch wird ausgewählt." + "repository.url": "Beispiele
Für öffentliche Repositories benutze https://....
Für private Repositories benutze git@....

https://github.com/coollabsio/coolify-examples main Branch wird ausgewählt
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify Branch wird ausgewählt.
https://gitea.com/sedlav/expressjs.git main Branch wird ausgewählt.
https://gitlab.com/andrasbacsai/nodejs-example.git main Branch wird ausgewählt." } From a47816f45df635fcc81bedc731dbcf920b9480d5 Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 13 Jun 2024 13:56:27 +0200 Subject: [PATCH 036/212] Update lang/de.json Co-authored-by: Marco <59606979+marcomaiermm@users.noreply.github.com> --- lang/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/de.json b/lang/de.json index f2050283f..29fec629f 100644 --- a/lang/de.json +++ b/lang/de.json @@ -18,7 +18,7 @@ "auth.failed.callback": "Fehlerhafte Verarbeitung der Antwort des Anmeldeanbieters.", "auth.failed.password": "Das angegebene Passwort ist inkorrekt.", "auth.failed.email": "Wir können keinen Benutzer mit dieser E-Mail Adresse finden.", - "auth.throttle": "Zu viele anmeldeversuche. Bitte versuchen es in :seconds Sekunden erneut.", + "auth.throttle": "Zu viele Anmeldeversuche. Bitte versuche es in :seconds Sekunden erneut.", "input.name": "Name", "input.email": "E-Mail", "input.password": "Passwort", From bfe2fb6da8965bd2080c154ed30742cd0981f4fe Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 13 Jun 2024 14:48:23 +0200 Subject: [PATCH 037/212] refactor: Update Docker build commands for better performance and flexibility --- app/Jobs/ApplicationDeploymentJob.php | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 423487cb1..395f58e19 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1389,6 +1389,7 @@ private function generate_nixpacks_confs() $merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', []))); $aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []); if (count($aptPkgs) === 0) { + $aptPkgs = ['curl', 'wget']; data_set($parsed, 'phases.setup.aptPkgs', ['curl', 'wget']); } else { if (!in_array('curl', $aptPkgs)) { @@ -1861,12 +1862,14 @@ private function build_image() $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, ]); + $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 { $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, ]); + $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); $this->execute_remote_command( [ @@ -1959,12 +1962,13 @@ private function build_image() $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, ]); + $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 { $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, ]); + $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); $this->execute_remote_command( [ @@ -2222,10 +2226,14 @@ public function failed(Throwable $exception): void ray($code); if ($code !== 69420) { // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one - $this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr'); - $this->execute_remote_command( - ["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true] - ); + if ($this->application->settings->is_consistent_container_name_enabled || isset($this->application->settings->custom_internal_name)) { + // do not remove already running container + } else { + $this->application_deployment_queue->addLogEntry('Deployment failed. Removing the new version of your application.', 'stderr'); + $this->execute_remote_command( + ["docker rm -f $this->container_name >/dev/null 2>&1", 'hidden' => true, 'ignore_errors' => true] + ); + } } } } From af464c2af7c4a510c2611ef72fdca53a35afed36 Mon Sep 17 00:00:00 2001 From: Leonardo Cabeza Date: Thu, 13 Jun 2024 20:39:37 -0500 Subject: [PATCH 038/212] add: glances service --- public/svgs/glances.png | Bin 0 -> 50295 bytes templates/compose/glances.yaml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 public/svgs/glances.png create mode 100644 templates/compose/glances.yaml diff --git a/public/svgs/glances.png b/public/svgs/glances.png new file mode 100644 index 0000000000000000000000000000000000000000..31e1a09fbab1988e5b34c053f497603ea3224ac5 GIT binary patch literal 50295 zcmcG#g*soGQyIAy(ITx8Imq@FAu74>i+oUI+JH9!&P|j$0Bkhf8z5cn{s@k5df@hJB-Edo=t# zb8n9b<{PWWTiau_q%A?g{;-Sa2CKQAACGL&PVFIzbMwSW7Z33iMv4paMyzUk1&7!j zv*deK^Ca|E>{V&?j|r0B^#po))%+erHB%@Tna;mH(y~d67_vgU=0}P`S-X_jrL~Pe zP8=_wH-*K22pk-+qsZC3rWdEP2WDck`d<>n6U0%B(z_DPO$jeAUw|D=t8Vf#y zcW@kJb>4v!9KZWT`HwZ#9lVL<{7O+8YYmMa5B&k&CGQsqVuW5vz0h!<+?w|CrgfEf*|2H`YYnI9| zEd>Tfe7%b2tD1WO!}+!;kNzer3REk)Rz4rxjiFo`9)9%q(s!ox9=G+U^mLDv=g%_F z2m0I~=KsHbB4~_p!on6-qaG&+HJXW1bQbC}5o#1pewI=}k;agE^fv;RM5D0hVE`rS zYsQb>kV-6S;m(Di3Zul$&M(Tj*;_&s?A@85(5(7fg7WYND%lR;OmRvwI~(ZVg2SZ*@Ax4t9#WO!`zM%jl0^O*%4Gy5R;&>lc-imt{uuZ zlKzO{X&2KAc#s7{$IOrDA;(~U*C3(~xo+L;Dh4+gV+(wM>Dh}oT=l=GqGvXdU|~-2 zKOZ!7@u!QXv&1H>g1?QJlf`(8%ac5MKhZ$K5G^X2`GgXgFrM~1`gHW0#o{)aRJ*^N z)Df4tNO_-7?H|84eF9d}+poj^puk8an2JA>U;8(q#!^m97)4g`jhDE8&W;bGQVC}@ z-`cqU!yLt_BFqPuvFlk>emaVTp=P`$$#-h@Z^au4&7HsH?yM2Fzac0VU>NG%{JKsSVu5lii}rIR6>Xa(gNvI2fbK^aGPq1nOfnC+aLa z%|{&481BC`J;=)#p3qYJh6Kcip~Q!=E^mx`*FrmcoLEP1;6gMlMbLqEavC50+99Iz zng8{gMDSrI?McMH&kAKbw=J~Mvacf6{7^e-Pd*MC;G?+XG82V=BbpYkFHdJ-x-(yd zGNZo!ZcFP-OvIB+O0?Dgj%782JAL^+kN@cmvRadr88|4ZH7KdpA!o+aV9vQF%CqLm zd{Z-R{=$)qF%wgz6`?|_<1dEa8Z=ONXn&gFY%MPT-#-hZ+>TZ_92C~Apm>V;e@206 zQ^*C9K+4#AUJsOS9cG_$c$>c!`ENDe7NOLHmG~3s!JwZ1Aah6Y|8#mV_h3?52}~y~J(Kad zoH%zLoR@q-5J^PFf1@kKjpZfJ7Rve?C*DhAIkEro%vfi{zdOJ!uZ~+2ohX*Ovrv)R zY=uKgb_q9%=Y4-C&G&1KpMKH3{CA<8G_B^9gI9!FGo$$V zQuv5};V|(a-Rz0#D4x_31>8qR$^bXCjmFx_>OaU~=2>Dq`N}GPVNZ2Sc23wZM`6T` z4R*OM!&m+LCPH^XR}o^FVu-nSczQjdIa)Pf;a2FfHYzzoJ)>DBT0xfEv&t7X?D&jw zh{iPb%8+lc)-q|C5jb^^dx9B%;l_c_wZ4Ms1j+i(v2Mce(OWGEd#*=PL7a@L7ZYsE zf=BW9o4wsTzZoY40iR(tJ-@!`m0tR=?_DJ>;NL&L?e61_CqUR7ZWLGDMv@oc3XSuR z3fxelnhD*x81DzmlzS*OZaE{Wtim$a?xyZ|1`IP%f-;lt@{p<=QD24Fql?j%$xd@^ z0!$~}gpQm=uRs3YtsH%+`-P4>bf!gKva~hg#EFHN<&=j` zbrku4>$C#vLvylV4B4Ga#=O&|q@-*u*MOH`sNh0atI8c(h*H@NZ|9d++TusNV+2k9 zH&ljEl5y>AE-_)(pS1&A)DQbsus2`ar82ccK|VbrG(jAmiqHRi@-kE?mi=McOTmQSL1D-Br-Gz-g8oD^TN<($? zyKpi=y0hu;DW^NCc-B~k?I{78_xunx zSiZ&n&Hcfw{>o3iIYdq7)oX5KLaeLE%q^z@ZX@CH#G+LAb8Zd34^l5nE)Ut7S0)_p zBAG1fqsJ2hJZK8L<=I$_-==>@(n85Vm^6V@kbBOp(AWDG(o!B(6vKb8!)vnZ-az|uQ>_lX^o19;-?|T~HKs0!I^nh~x#0zJ-oYIw z^QgeNi(VrKJ*7E&K2TfX1~p57WMuH!6s-1*rsinh6FXYCJF60_FfLj-lXG;6jjWX8 zgRVNg0X%cAuU;ODJp{TsL~_W49#Y~C!|TovlLzmYA+A?Tr{wp8Ym6rayRK@7hE&h4 zeaY|L%dM%QB9ytvC~kV%fZ?}wP2(z6H&GV0(4`rlc@ZiyF6yyztoqOADn!#t8V_XK z<}FYiGl525l$5;|y7lGY19+qTa3ec!lF9I3&C;YL3VoWFK zjUPOS$Rwz;?Xwo!uM$WI9LC|^C+iq$ARtH#Grq!+P^@UGtU9BLgGF>+)vB<*mwkw8 zq!qLA`!}(tm)BDm3>zMd+zv6OT6cW(BoOT|f9}h|;!}&zvS-8noiE85_?bP|Rkgtwxj5MRdT;Yk?>)M#FFn!^%f;&wOS57m_MhFI-P|Q;lQ1@B z>gnlGQBy;WJ^HI}L;W`*zeTb^Jf5{F)+kSZ&2UnrQt0Pv6+}5!yhY&34^E-hWd||b zxg!nl+ZP_{Jfij+6GYO?*qA|H*Nq5f(r_aqBdFDgxXShcH#heaPdG>Va+10eD*n^o zlrtza-jkWEgm3TXN@oh2s7^1Iew%7}svZuKvU>k)nLCsJE!v#Cnf7zUUfEN>fpzDc zf&wf=-C*+%TbgVY?8e5%Fezq-T0IBhbhdAeOqg$%l)Ih=hniZuV_ldA*?g2DbMt5A zO0O-mLv|Y4y2mePY0R_u<~W{h;f=?wNTQk5;L({+`lp3Pn`Q4#?B!7C_-JLrm=L&vHu^4rH-@Ndci`S8#!hm z)aDN?5$BwgsT#GgRk7dSv$nQYgSN4@#(Hsid3R=sz45~Zw*(~K4{s%C)tzCJKex9O zSt4R48%VDI#A?ysc3X4kUTPD9x*?~2D8MNuL$F8!sgIatFbhYF$eFi2F(nFP4s2=) zr*q30`nM_{-}{CJks7^{xY3+;)u_)AFiJ&sLnJ`ir1viA=vOmn{p&}xuZqBFGfR7+BhlXY4Nzt-R!)>$S%JR&PeIUY~6w+C>7Rn6EM}_Zpyi5goT+#pr^Mzn=pS`7ae#;N$ixdQH zDqH2vH#AD)2(N=0F>4)4g?HYi1W$nddYAvRx983o@(Eo6Ifd4t1%-_!fgPdxmGBC~ z8Z%S5a~uO@3o0X%D1{n{*WuEo5?&fZ#h9L>FH7+ziJ+>HrzSAdHi$zb?fO|W;D`cqv}=8Qm>6 zxUB+k8(x1`nh8SL`mmj{+;X~e3>KuX*Vf?h1%0DFcN$ePGcy*Vhg>y1hj|1_&;Qtl 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 Date: Thu, 13 Jun 2024 20:46:13 -0500 Subject: [PATCH 039/212] fix: image logo --- templates/compose/glances.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/compose/glances.yaml b/templates/compose/glances.yaml index 68894ee34..fdf27e5f4 100644 --- a/templates/compose/glances.yaml +++ b/templates/compose/glances.yaml @@ -1,7 +1,7 @@ # documentation: https://nicolargo.github.io/glances/ # slogan: An Eye on your system # tags: monitoring tool python cross platform -# logo: svgs/glances.svg +# logo: svgs/glances.png # port: 61208 services: From 49d91c498eef858149c92317ce0ba66d7d9c7f02 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 14 Jun 2024 09:50:16 +0200 Subject: [PATCH 040/212] refactor: Update image sizes and add new logos to README.md --- README.md | 9 +++++---- other/logos/fractal.png | Bin 0 -> 5643 bytes 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 other/logos/fractal.png diff --git a/README.md b/README.md index 2f61468a1..d22ec90d1 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,14 @@ # Donations Special thanks to our biggest sponsors! cccareers logo -hetzner logo -logto logo +hetzner logo +logto logo bc direct logo -quantcdn logo +quantcdn logo arcjet logo supaguide logo -tigris logo +tigris logo +fractal logo ## Github Sponsors ($40+) SerpAPI diff --git a/other/logos/fractal.png b/other/logos/fractal.png new file mode 100644 index 0000000000000000000000000000000000000000..c4d39c1f1dbcb8861479cf169ceed9e796c6e351 GIT binary patch literal 5643 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 literal 0 HcmV?d00001 From b17be37aee22ae807da978d77cd98dafa4e40738 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 14 Jun 2024 14:09:56 +0200 Subject: [PATCH 041/212] fix: db proxy status shown better in the UI --- app/Actions/Database/StopDatabaseProxy.php | 2 ++ app/Livewire/Project/Database/Heading.php | 1 - .../Project/Database/Postgresql/General.php | 27 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/Actions/Database/StopDatabaseProxy.php b/app/Actions/Database/StopDatabaseProxy.php index 984225435..1b262c898 100644 --- a/app/Actions/Database/StopDatabaseProxy.php +++ b/app/Actions/Database/StopDatabaseProxy.php @@ -2,6 +2,7 @@ namespace App\Actions\Database; +use App\Events\DatabaseStatusChanged; use App\Models\ServiceDatabase; use App\Models\StandaloneClickhouse; use App\Models\StandaloneDragonfly; @@ -28,5 +29,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St instant_remote_process(["docker rm -f {$uuid}-proxy"], $server); $database->is_public = false; $database->save(); + DatabaseStatusChanged::dispatch(); } } diff --git a/app/Livewire/Project/Database/Heading.php b/app/Livewire/Project/Database/Heading.php index 61dafa76f..ae88ac12b 100644 --- a/app/Livewire/Project/Database/Heading.php +++ b/app/Livewire/Project/Database/Heading.php @@ -12,7 +12,6 @@ use App\Actions\Database\StartRedis; use App\Actions\Database\StopDatabase; use App\Actions\Docker\GetContainersStatus; -use App\Jobs\ContainerStatusJob; use Livewire\Component; class Heading extends Component diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index 38cac2e5c..a950e5ef9 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -25,7 +25,17 @@ class General extends Component public ?string $db_url_public = null; - protected $listeners = ['refresh', 'save_init_script', 'delete_init_script']; + public function getListeners() + { + $userId = auth()->user()->id; + + return [ + "echo-private:user.{$userId},DatabaseStatusChanged" => 'database_stopped', + "refresh", + "save_init_script", + "delete_init_script", + ]; + } protected $rules = [ 'database.name' => 'required', @@ -68,11 +78,14 @@ public function mount() } $this->server = data_get($this->database, 'destination.server'); } - + public function database_stopped() + { + $this->dispatch('success', 'Database proxy stopped. Database is no longer publicly accessible.'); + } public function instantSaveAdvanced() { try { - if (! $this->server->isLogDrainEnabled()) { + if (!$this->server->isLogDrainEnabled()) { $this->database->is_log_drain_enabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); @@ -89,14 +102,14 @@ public function instantSaveAdvanced() public function instantSave() { try { - if ($this->database->is_public && ! $this->database->public_port) { + if ($this->database->is_public && !$this->database->public_port) { $this->dispatch('error', 'Public port is required.'); $this->database->is_public = false; return; } if ($this->database->is_public) { - if (! str($this->database->status)->startsWith('running')) { + if (!str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); $this->database->is_public = false; @@ -112,7 +125,7 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = ! $this->database->is_public; + $this->database->is_public = !$this->database->is_public; return handleError($e, $this); } @@ -157,7 +170,7 @@ public function save_new_init_script() return; } - if (! isset($this->database->init_scripts)) { + if (!isset($this->database->init_scripts)) { $this->database->init_scripts = []; } $this->database->init_scripts = array_merge($this->database->init_scripts, [ From f10f3456d7bfc6f6d4a3ed7b0d8cc3eeaa9a4a97 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Fri, 14 Jun 2024 12:10:40 +0000 Subject: [PATCH 042/212] Fix styling --- .../Project/Database/Postgresql/General.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index a950e5ef9..1c5d39055 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -31,9 +31,9 @@ public function getListeners() return [ "echo-private:user.{$userId},DatabaseStatusChanged" => 'database_stopped', - "refresh", - "save_init_script", - "delete_init_script", + 'refresh', + 'save_init_script', + 'delete_init_script', ]; } @@ -78,14 +78,16 @@ public function mount() } $this->server = data_get($this->database, 'destination.server'); } + public function database_stopped() { $this->dispatch('success', 'Database proxy stopped. Database is no longer publicly accessible.'); } + public function instantSaveAdvanced() { try { - if (!$this->server->isLogDrainEnabled()) { + if (! $this->server->isLogDrainEnabled()) { $this->database->is_log_drain_enabled = false; $this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.'); @@ -102,14 +104,14 @@ public function instantSaveAdvanced() public function instantSave() { try { - if ($this->database->is_public && !$this->database->public_port) { + if ($this->database->is_public && ! $this->database->public_port) { $this->dispatch('error', 'Public port is required.'); $this->database->is_public = false; return; } if ($this->database->is_public) { - if (!str($this->database->status)->startsWith('running')) { + if (! str($this->database->status)->startsWith('running')) { $this->dispatch('error', 'Database must be started to be publicly accessible.'); $this->database->is_public = false; @@ -125,7 +127,7 @@ public function instantSave() } $this->database->save(); } catch (\Throwable $e) { - $this->database->is_public = !$this->database->is_public; + $this->database->is_public = ! $this->database->is_public; return handleError($e, $this); } @@ -170,7 +172,7 @@ public function save_new_init_script() return; } - if (!isset($this->database->init_scripts)) { + if (! isset($this->database->init_scripts)) { $this->database->init_scripts = []; } $this->database->init_scripts = array_merge($this->database->init_scripts, [ From 1ecd0307edc96e7d04c2c4cc44d2e551470c563b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 14 Jun 2024 14:23:40 +0200 Subject: [PATCH 043/212] feat: COOLIFY_CONTAINER_NAME predefined variable --- app/Jobs/ApplicationDeploymentJob.php | 6 ++++++ app/Models/Service.php | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index fb577b895..8f89da2d2 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -828,6 +828,9 @@ private function save_environment_variables() if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) { $envs->push("COOLIFY_BRANCH={$local_branch}"); } + if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) { + $envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}"); + } foreach ($sorted_environment_variables_preview as $env) { $real_value = $env->real_value; if ($env->version === '4.0.0-beta.239') { @@ -869,6 +872,9 @@ private function save_environment_variables() if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) { $envs->push("COOLIFY_BRANCH={$local_branch}"); } + if ($this->application->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) { + $envs->push("COOLIFY_CONTAINER_NAME={$this->container_name}"); + } foreach ($sorted_environment_variables as $env) { $real_value = $env->real_value; if ($env->version === '4.0.0-beta.239') { diff --git a/app/Models/Service.php b/app/Models/Service.php index 7851eb58a..6e8ba25a8 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; +use Symfony\Component\Yaml\Yaml; class Service extends BaseModel { @@ -837,6 +838,13 @@ public function saveComposeConfigs() $commands[] = "mkdir -p $workdir"; $commands[] = "cd $workdir"; + $json = Yaml::parse($this->docker_compose); + foreach($json['services'] as $service => $config) { + $envs = collect($config['environment']); + $envs->push("COOLIFY_CONTAINER_NAME=$service-{$this->uuid}"); + data_set($json, "services.$service.environment", $envs->toArray()); + } + $this->docker_compose = Yaml::dump($json); $docker_compose_base64 = base64_encode($this->docker_compose); $commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null"; $envs = $this->environment_variables()->get(); From 2b6fc16637f488048412f402327a764c886d9911 Mon Sep 17 00:00:00 2001 From: andrasbacsai Date: Fri, 14 Jun 2024 12:24:28 +0000 Subject: [PATCH 044/212] Fix styling --- app/Models/Service.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Service.php b/app/Models/Service.php index 6e8ba25a8..8adca3424 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -839,7 +839,7 @@ public function saveComposeConfigs() $commands[] = "cd $workdir"; $json = Yaml::parse($this->docker_compose); - foreach($json['services'] as $service => $config) { + foreach ($json['services'] as $service => $config) { $envs = collect($config['environment']); $envs->push("COOLIFY_CONTAINER_NAME=$service-{$this->uuid}"); data_set($json, "services.$service.environment", $envs->toArray()); From f6f44d8e8f5c36649af3327f09d617fe862797da Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Fri, 14 Jun 2024 14:48:12 +0200 Subject: [PATCH 045/212] fix: show commit message on webhooks + prs --- .../application/deployment/index.blade.php | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/resources/views/livewire/project/application/deployment/index.blade.php b/resources/views/livewire/project/application/deployment/index.blade.php index 4282700f8..241530fe0 100644 --- a/resources/views/livewire/project/application/deployment/index.blade.php +++ b/resources/views/livewire/project/application/deployment/index.blade.php @@ -46,7 +46,7 @@ class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> {{ $deployment->status }} @if (data_get($deployment, 'is_webhook') || data_get($deployment, 'pull_request_id')) -
+
@if (data_get($deployment, 'is_webhook')) Webhook @endif @@ -55,12 +55,19 @@ class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | @endif Pull Request #{{ data_get($deployment, 'pull_request_id') }} - (SHA - @if (data_get($deployment, 'commit')) - {{ data_get($deployment, 'commit') }}) - @else - HEAD) - @endif + @endif + @if (data_get($deployment, 'commit')) +
+
+ @if ($deployment->commitMessage()) + ({{ data_get_str($deployment, 'commit')->limit(7) }} - + {{ $deployment->commitMessage() }}) + @else + {{ data_get_str($deployment, 'commit')->limit(7) }} + @endif +
+
@endif
@else @@ -98,7 +105,7 @@ class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> Finished 0s in 0s @else - Running for 0s + Running for 0s @endif
From af38d0cc078d76ee83e5ecca9ae69a723ca2dd3c Mon Sep 17 00:00:00 2001 From: Alexzvn Date: Sat, 15 Jun 2024 00:01:58 +0000 Subject: [PATCH 046/212] fix: application custom labels reset after saving --- app/Livewire/Project/Application/General.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index 60cdee48e..06ff7b1de 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -347,7 +347,9 @@ public function set_redirect() public function submit($showToaster = true) { try { - $this->set_redirect(); + if ($this->application->isDirty('redirect')) { + $this->set_redirect(); + } $this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim(); $this->application->fqdn = str($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { From 1ae6106782ec0451ca15ac3659c77927fd4beca3 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 17 Jun 2024 10:20:02 +0200 Subject: [PATCH 047/212] refactor: Update README.md with new logos and fix styling --- README.md | 4 +++- other/logos/advin.png | Bin 0 -> 33880 bytes other/logos/codext.jpg | Bin 0 -> 105902 bytes other/logos/fractal.svg | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 other/logos/advin.png create mode 100644 other/logos/codext.jpg create mode 100644 other/logos/fractal.svg diff --git a/README.md b/README.md index d22ec90d1..34c40acf5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ # Donations arcjet logo supaguide logo tigris logo -fractal logo +fractal logo +advin logo ## Github Sponsors ($40+) SerpAPI @@ -52,6 +53,7 @@ ## Github Sponsors ($40+) FlintCompany American Cloud CryptoJobsList +Codext Thompson Edolo UXWizz Younes Barrad diff --git a/other/logos/advin.png b/other/logos/advin.png new file mode 100644 index 0000000000000000000000000000000000000000..155408b9cb1914a35c86e99d8b3a85b06def62d6 GIT binary patch literal 33880 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 z z(ma%Hl` z$p#GhwU88BNU}UOhMZOWMsa{CtylSyo<(=EwHviY$*bQLYubbuj7U!|MOEW0yV=P9 zvSkZ%UhB~Ota*7g`CQZXi*Eguor_kAV>=ENBDj{SoRGimTbUIw;vCLyNl?Va-cP5n z_Y#!`Jr&1jd@uuN<=Pyy-bQbGe7D@y3@t)PW!)AODBb(^(BS*oOfOgs)DKx`h)>(L z%&bx$!0tS9IltG_6T}*7N*C z`55LJ4G3)D`GV`jgXmh|^7+AQ?{^AW^@n;qgeiOOn2pJv{43qpy1?Ctj&$SS7U}+K zY;T92LQ6uIc((g=Ks;Y*)F`L#RNk4#7&ms?-ZeWV1}*Mv9TvkJ2&Fh zM0m;*NuL(u5EL_-xp%>!zYlA$B?HE?9&dY0s(wa5gF%wNei*O9Ua+!%3aXp181O)# z%=_=X644V)X!7r=x)K{t4C5NtL;Woc4W*z*9DAi63!a7WCioLRv?AK^-J?RhBlWk! zVGLVl@8@}C7Gd~jJVfoT*ur4A4?jGU&URAPu(prsQEz|Nee)6Eao~

*p?!IAt}F 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