2023-03-27 08:44:31 +00:00
< ? php
namespace App\Models ;
2023-12-13 11:13:20 +00:00
use App\Enums\ApplicationDeploymentStatus ;
2023-03-30 07:47:04 +00:00
use Illuminate\Database\Eloquent\Casts\Attribute ;
2023-05-04 20:29:14 +00:00
use Illuminate\Database\Eloquent\Relations\HasMany ;
2023-12-13 11:08:12 +00:00
use Illuminate\Database\Eloquent\SoftDeletes ;
2024-03-28 14:05:12 +00:00
use Illuminate\Support\Collection ;
2023-10-09 09:10:04 +00:00
use Illuminate\Support\Str ;
2023-11-24 14:48:23 +00:00
use RuntimeException ;
2024-06-10 20:43:34 +00:00
use Spatie\Activitylog\Models\Activity ;
2024-05-23 12:28:03 +00:00
use Spatie\Url\Url ;
2024-03-04 09:13:40 +00:00
use Symfony\Component\Yaml\Yaml ;
2023-11-27 10:54:55 +00:00
use Visus\Cuid2\Cuid2 ;
2023-03-29 10:27:02 +00:00
2023-03-27 08:44:31 +00:00
class Application extends BaseModel
{
2023-12-13 11:08:12 +00:00
use SoftDeletes ;
2024-06-10 20:43:34 +00:00
2023-08-11 14:13:53 +00:00
protected $guarded = [];
2024-06-10 20:43:34 +00:00
2023-08-08 09:51:36 +00:00
protected static function booted ()
{
2023-10-09 09:10:04 +00:00
static :: saving ( function ( $application ) {
if ( $application -> fqdn == '' ) {
$application -> fqdn = null ;
}
$application -> forceFill ([
'fqdn' => $application -> fqdn ,
2024-06-25 08:37:10 +00:00
'install_command' => str ( $application -> install_command ) -> trim (),
'build_command' => str ( $application -> build_command ) -> trim (),
'start_command' => str ( $application -> start_command ) -> trim (),
'base_directory' => str ( $application -> base_directory ) -> trim (),
'publish_directory' => str ( $application -> publish_directory ) -> trim (),
2023-10-09 09:10:04 +00:00
]);
});
2023-08-08 09:51:36 +00:00
static :: created ( function ( $application ) {
ApplicationSetting :: create ([
'application_id' => $application -> id ,
]);
});
static :: deleting ( function ( $application ) {
2024-01-30 08:48:51 +00:00
$application -> update ([ 'fqdn' => null ]);
2023-08-08 09:51:36 +00:00
$application -> settings () -> delete ();
2023-10-14 12:22:07 +00:00
$storages = $application -> persistentStorages () -> get ();
2023-11-06 17:04:18 +00:00
$server = data_get ( $application , 'destination.server' );
if ( $server ) {
foreach ( $storages as $storage ) {
instant_remote_process ([ " docker volume rm -f $storage->name " ], $server , false );
}
2023-09-22 09:34:27 +00:00
}
2023-08-08 09:51:36 +00:00
$application -> persistentStorages () -> delete ();
2023-09-19 12:08:20 +00:00
$application -> environment_variables () -> delete ();
$application -> environment_variables_preview () -> delete ();
2024-02-14 14:14:06 +00:00
foreach ( $application -> scheduled_tasks as $task ) {
$task -> delete ();
}
2024-02-02 10:50:28 +00:00
$application -> tags () -> detach ();
2023-08-08 09:51:36 +00:00
});
}
2023-12-04 14:08:24 +00:00
2024-04-12 08:54:25 +00:00
public function delete_configurations ()
{
$server = data_get ( $this , 'destination.server' );
$workdir = $this -> workdir ();
if ( str ( $workdir ) -> endsWith ( $this -> uuid )) {
ray ( 'Deleting workdir' );
2024-06-19 07:07:49 +00:00
instant_remote_process ([ 'rm -rf ' . $this -> workdir ()], $server , false );
2024-04-12 08:54:25 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-02-06 14:05:11 +00:00
public function additional_servers ()
{
return $this -> belongsToMany ( Server :: class , 'additional_destinations' )
2024-02-07 13:55:06 +00:00
-> withPivot ( 'standalone_docker_id' , 'status' );
2024-02-06 14:05:11 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-06 14:05:11 +00:00
public function additional_networks ()
{
return $this -> belongsToMany ( StandaloneDocker :: class , 'additional_destinations' )
2024-02-07 13:55:06 +00:00
-> withPivot ( 'server_id' , 'status' );
2024-02-06 14:05:11 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-01 17:24:14 +00:00
public function is_public_repository () : bool
{
2024-03-01 10:41:22 +00:00
if ( data_get ( $this , 'source.is_public' )) {
return true ;
}
2024-06-10 20:43:34 +00:00
2024-03-01 10:41:22 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2024-01-29 09:43:18 +00:00
public function is_github_based () : bool
{
if ( data_get ( $this , 'source' )) {
return true ;
}
2024-06-10 20:43:34 +00:00
2024-01-29 09:43:18 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2024-03-04 10:01:14 +00:00
public function isForceHttpsEnabled ()
2024-03-04 09:46:13 +00:00
{
return data_get ( $this , 'settings.is_force_https_enabled' , false );
}
2024-06-10 20:43:34 +00:00
2024-03-04 10:01:14 +00:00
public function isStripprefixEnabled ()
2024-03-04 09:46:13 +00:00
{
return data_get ( $this , 'settings.is_stripprefix_enabled' , true );
}
2024-06-10 20:43:34 +00:00
2024-03-04 10:01:14 +00:00
public function isGzipEnabled ()
2024-03-04 09:46:13 +00:00
{
return data_get ( $this , 'settings.is_gzip_enabled' , true );
}
2024-06-10 20:43:34 +00:00
2023-11-27 08:39:43 +00:00
public function link ()
{
2023-11-30 11:21:53 +00:00
if ( data_get ( $this , 'environment.project.uuid' )) {
return route ( 'project.application.configuration' , [
'project_uuid' => data_get ( $this , 'environment.project.uuid' ),
'environment_name' => data_get ( $this , 'environment.name' ),
2024-06-10 20:43:34 +00:00
'application_uuid' => data_get ( $this , 'uuid' ),
2023-11-30 11:21:53 +00:00
]);
}
2024-06-10 20:43:34 +00:00
2023-11-30 11:21:53 +00:00
return null ;
2023-11-27 08:39:43 +00:00
}
2024-06-10 20:43:34 +00:00
2024-05-21 13:36:26 +00:00
public function failedTaskLink ( $task_uuid )
{
if ( data_get ( $this , 'environment.project.uuid' )) {
return route ( 'project.application.scheduled-tasks' , [
'project_uuid' => data_get ( $this , 'environment.project.uuid' ),
'environment_name' => data_get ( $this , 'environment.name' ),
'application_uuid' => data_get ( $this , 'uuid' ),
2024-06-10 20:43:34 +00:00
'task_uuid' => $task_uuid ,
2024-05-21 13:36:26 +00:00
]);
}
2024-06-10 20:43:34 +00:00
2024-05-21 13:36:26 +00:00
return null ;
}
2024-06-10 20:43:34 +00:00
2023-08-08 09:51:36 +00:00
public function settings ()
{
return $this -> hasOne ( ApplicationSetting :: class );
}
public function persistentStorages ()
{
return $this -> morphMany ( LocalPersistentVolume :: class , 'resource' );
}
2024-06-10 20:43:34 +00:00
2023-10-04 07:58:39 +00:00
public function fileStorages ()
{
return $this -> morphMany ( LocalFileVolume :: class , 'resource' );
}
2023-08-08 09:51:36 +00:00
public function type ()
{
2023-08-07 20:14:21 +00:00
return 'application' ;
}
2023-08-08 09:51:36 +00:00
2023-04-26 11:01:09 +00:00
public function publishDirectory () : Attribute
{
return Attribute :: make (
2024-06-19 07:07:49 +00:00
set : fn ( $value ) => $value ? '/' . ltrim ( $value , '/' ) : null ,
2023-04-26 11:01:09 +00:00
);
}
2023-08-08 09:51:36 +00:00
2023-05-10 10:22:27 +00:00
public function gitBranchLocation () : Attribute
2023-05-08 10:22:45 +00:00
{
return Attribute :: make (
2023-05-10 09:06:54 +00:00
get : function () {
2024-06-19 07:07:49 +00:00
if ( ! is_null ( $this -> source ? -> html_url ) && ! is_null ( $this -> git_repository ) && ! is_null ( $this -> git_branch )) {
2023-05-10 10:22:27 +00:00
return " { $this -> source -> html_url } / { $this -> git_repository } /tree/ { $this -> git_branch } " ;
}
2024-05-10 14:28:14 +00:00
// Convert the SSH URL to HTTPS URL
if ( strpos ( $this -> git_repository , 'git@' ) === 0 ) {
$git_repository = str_replace ([ 'git@' , ':' , '.git' ], [ '' , '/' , '' ], $this -> git_repository );
2024-06-10 20:43:34 +00:00
2024-05-10 14:28:14 +00:00
return " https:// { $git_repository } /tree/ { $this -> git_branch } " ;
}
2024-06-10 20:43:34 +00:00
2023-10-06 11:46:42 +00:00
return $this -> git_repository ;
2023-05-10 10:22:27 +00:00
}
);
}
2023-08-08 09:51:36 +00:00
2023-11-14 13:07:42 +00:00
public function gitWebhook () : Attribute
{
return Attribute :: make (
get : function () {
2024-06-19 07:07:49 +00:00
if ( ! is_null ( $this -> source ? -> html_url ) && ! is_null ( $this -> git_repository ) && ! is_null ( $this -> git_branch )) {
2023-11-14 13:07:42 +00:00
return " { $this -> source -> html_url } / { $this -> git_repository } /settings/hooks " ;
}
2024-05-10 14:28:14 +00:00
// Convert the SSH URL to HTTPS URL
if ( strpos ( $this -> git_repository , 'git@' ) === 0 ) {
$git_repository = str_replace ([ 'git@' , ':' , '.git' ], [ '' , '/' , '' ], $this -> git_repository );
2024-06-10 20:43:34 +00:00
2024-05-10 14:28:14 +00:00
return " https:// { $git_repository } /settings/hooks " ;
}
2024-06-10 20:43:34 +00:00
2023-11-14 13:07:42 +00:00
return $this -> git_repository ;
}
);
}
2023-05-10 10:22:27 +00:00
public function gitCommits () : Attribute
{
return Attribute :: make (
get : function () {
2024-06-19 07:07:49 +00:00
if ( ! is_null ( $this -> source ? -> html_url ) && ! is_null ( $this -> git_repository ) && ! is_null ( $this -> git_branch )) {
2023-05-10 10:22:27 +00:00
return " { $this -> source -> html_url } / { $this -> git_repository } /commits/ { $this -> git_branch } " ;
2023-05-10 09:06:54 +00:00
}
2024-05-10 14:28:14 +00:00
// Convert the SSH URL to HTTPS URL
if ( strpos ( $this -> git_repository , 'git@' ) === 0 ) {
$git_repository = str_replace ([ 'git@' , ':' , '.git' ], [ '' , '/' , '' ], $this -> git_repository );
2024-06-10 20:43:34 +00:00
2024-05-10 14:28:14 +00:00
return " https:// { $git_repository } /commits/ { $this -> git_branch } " ;
}
2024-06-10 20:43:34 +00:00
2023-10-06 11:46:42 +00:00
return $this -> git_repository ;
2023-05-10 09:06:54 +00:00
}
2023-05-08 10:22:45 +00:00
);
}
2024-06-10 20:43:34 +00:00
2024-05-15 08:45:08 +00:00
public function gitCommitLink ( $link ) : string
{
2024-06-21 12:39:22 +00:00
if ( ! is_null ( data_get ( $this , 'source.html_url' )) && ! is_null ( data_get ( $this , 'git_repository' )) && ! is_null ( data_get ( $this , 'git_branch' ))) {
2024-05-17 08:12:05 +00:00
if ( str ( $this -> source -> html_url ) -> contains ( 'bitbucket' )) {
return " { $this -> source -> html_url } / { $this -> git_repository } /commits/ { $link } " ;
}
2024-06-10 20:43:34 +00:00
2024-05-15 08:45:08 +00:00
return " { $this -> source -> html_url } / { $this -> git_repository } /commit/ { $link } " ;
}
2024-05-23 12:28:03 +00:00
if ( str ( $this -> git_repository ) -> contains ( 'bitbucket' )) {
$git_repository = str_replace ( '.git' , '' , $this -> git_repository );
$url = Url :: fromString ( $git_repository );
$url = $url -> withUserInfo ( '' );
2024-06-19 07:07:49 +00:00
$url = $url -> withPath ( $url -> getPath () . '/commits/' . $link );
2024-06-10 20:43:34 +00:00
2024-05-23 12:28:03 +00:00
return $url -> __toString ();
}
2024-06-12 09:35:07 +00:00
if ( strpos ( $this -> git_repository , 'git@' ) === 0 ) {
$git_repository = str_replace ([ 'git@' , ':' , '.git' ], [ '' , '/' , '' ], $this -> git_repository );
2024-06-21 12:39:22 +00:00
if ( data_get ( $this , 'source.html_url' )) {
return " { $this -> source -> html_url } / { $git_repository } /commit/ { $link } " ;
}
2024-06-12 09:35:55 +00:00
2024-06-21 12:39:22 +00:00
return " { $git_repository } /commit/ { $link } " ;
2024-06-12 09:35:07 +00:00
}
2024-06-10 20:43:34 +00:00
2024-05-15 08:45:08 +00:00
return $this -> git_repository ;
}
2024-06-10 20:43:34 +00:00
2023-10-10 12:02:43 +00:00
public function dockerfileLocation () : Attribute
{
return Attribute :: make (
set : function ( $value ) {
if ( is_null ( $value ) || $value === '' ) {
return '/Dockerfile' ;
} else {
if ( $value !== '/' ) {
return Str :: start ( Str :: replaceEnd ( '/' , '' , $value ), '/' );
}
2024-06-10 20:43:34 +00:00
2023-10-10 12:02:43 +00:00
return Str :: start ( $value , '/' );
}
}
);
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
public function dockerComposeLocation () : Attribute
{
return Attribute :: make (
set : function ( $value ) {
if ( is_null ( $value ) || $value === '' ) {
2023-11-27 14:50:22 +00:00
return '/docker-compose.yaml' ;
} else {
if ( $value !== '/' ) {
return Str :: start ( Str :: replaceEnd ( '/' , '' , $value ), '/' );
}
2024-06-10 20:43:34 +00:00
2023-11-27 14:50:22 +00:00
return Str :: start ( $value , '/' );
}
}
);
}
2024-06-10 20:43:34 +00:00
2023-11-27 14:50:22 +00:00
public function dockerComposePrLocation () : Attribute
{
return Attribute :: make (
set : function ( $value ) {
if ( is_null ( $value ) || $value === '' ) {
2023-11-28 09:11:53 +00:00
return '/docker-compose.yaml' ;
2023-11-24 14:48:23 +00:00
} else {
if ( $value !== '/' ) {
return Str :: start ( Str :: replaceEnd ( '/' , '' , $value ), '/' );
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return Str :: start ( $value , '/' );
}
}
);
}
2024-06-10 20:43:34 +00:00
2023-04-26 11:01:09 +00:00
public function baseDirectory () : Attribute
{
return Attribute :: make (
2024-06-19 07:07:49 +00:00
set : fn ( $value ) => '/' . ltrim ( $value , '/' ),
2023-04-26 11:01:09 +00:00
);
}
2023-08-08 09:51:36 +00:00
2023-04-26 12:29:33 +00:00
public function portsMappings () : Attribute
{
return Attribute :: make (
2024-06-10 20:43:34 +00:00
set : fn ( $value ) => $value === '' ? null : $value ,
2023-04-26 12:29:33 +00:00
);
}
2024-06-10 20:43:34 +00:00
2023-04-24 11:25:02 +00:00
public function portsMappingsArray () : Attribute
2023-03-30 07:47:04 +00:00
{
return Attribute :: make (
2023-08-11 18:48:52 +00:00
get : fn () => is_null ( $this -> ports_mappings )
2023-03-30 07:47:04 +00:00
? []
2023-04-26 12:29:33 +00:00
: explode ( ',' , $this -> ports_mappings ),
2023-03-30 07:47:04 +00:00
);
}
2024-06-10 20:43:34 +00:00
2024-02-19 12:22:09 +00:00
public function isExited ()
{
2024-02-16 07:34:30 +00:00
return ( bool ) str ( $this -> status ) -> startsWith ( 'exited' );
}
2024-06-10 20:43:34 +00:00
2024-02-07 13:55:06 +00:00
public function realStatus ()
{
2024-02-12 10:46:36 +00:00
return $this -> getRawOriginal ( 'status' );
2024-02-07 13:55:06 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-06 16:37:07 +00:00
public function status () : Attribute
{
return Attribute :: make (
set : function ( $value ) {
2024-02-07 13:55:06 +00:00
if ( $this -> additional_servers -> count () === 0 ) {
if ( str ( $value ) -> contains ( '(' )) {
$status = str ( $value ) -> before ( '(' ) -> trim () -> value ();
$health = str ( $value ) -> after ( '(' ) -> before ( ')' ) -> trim () -> value () ? ? 'unhealthy' ;
2024-06-10 20:43:34 +00:00
} elseif ( str ( $value ) -> contains ( ':' )) {
2024-02-07 13:55:06 +00:00
$status = str ( $value ) -> before ( ':' ) -> trim () -> value ();
$health = str ( $value ) -> after ( ':' ) -> trim () -> value () ? ? 'unhealthy' ;
} else {
$status = $value ;
$health = 'unhealthy' ;
}
2024-06-10 20:43:34 +00:00
2024-02-07 13:55:06 +00:00
return " $status : $health " ;
2024-02-06 16:37:07 +00:00
} else {
2024-02-07 13:55:06 +00:00
if ( str ( $value ) -> contains ( '(' )) {
$status = str ( $value ) -> before ( '(' ) -> trim () -> value ();
$health = str ( $value ) -> after ( '(' ) -> before ( ')' ) -> trim () -> value () ? ? 'unhealthy' ;
2024-06-10 20:43:34 +00:00
} elseif ( str ( $value ) -> contains ( ':' )) {
2024-02-07 13:55:06 +00:00
$status = str ( $value ) -> before ( ':' ) -> trim () -> value ();
$health = str ( $value ) -> after ( ':' ) -> trim () -> value () ? ? 'unhealthy' ;
} else {
$status = $value ;
$health = 'unhealthy' ;
}
2024-06-10 20:43:34 +00:00
2024-02-07 13:55:06 +00:00
return " $status : $health " ;
2024-02-06 16:37:07 +00:00
}
},
get : function ( $value ) {
2024-02-07 13:55:06 +00:00
if ( $this -> additional_servers -> count () === 0 ) {
//running (healthy)
if ( str ( $value ) -> contains ( '(' )) {
$status = str ( $value ) -> before ( '(' ) -> trim () -> value ();
$health = str ( $value ) -> after ( '(' ) -> before ( ')' ) -> trim () -> value () ? ? 'unhealthy' ;
2024-06-10 20:43:34 +00:00
} elseif ( str ( $value ) -> contains ( ':' )) {
2024-02-07 13:55:06 +00:00
$status = str ( $value ) -> before ( ':' ) -> trim () -> value ();
$health = str ( $value ) -> after ( ':' ) -> trim () -> value () ? ? 'unhealthy' ;
} else {
$status = $value ;
$health = 'unhealthy' ;
}
2024-06-10 20:43:34 +00:00
2024-02-07 13:55:06 +00:00
return " $status : $health " ;
2024-02-06 16:37:07 +00:00
} else {
2024-02-07 13:55:06 +00:00
$complex_status = null ;
$complex_health = null ;
$complex_status = $main_server_status = str ( $value ) -> before ( ':' ) -> value ();
$complex_health = $main_server_health = str ( $value ) -> after ( ':' ) -> value () ? ? 'unhealthy' ;
$additional_servers_status = $this -> additional_servers -> pluck ( 'pivot.status' );
foreach ( $additional_servers_status as $status ) {
$server_status = str ( $status ) -> before ( ':' ) -> value ();
$server_health = str ( $status ) -> after ( ':' ) -> value () ? ? 'unhealthy' ;
2024-02-22 09:57:05 +00:00
if ( $main_server_status !== $server_status ) {
$complex_status = 'degraded' ;
2024-02-07 13:55:06 +00:00
}
2024-02-22 09:57:05 +00:00
if ( $main_server_health !== $server_health ) {
$complex_health = 'unhealthy' ;
2024-02-07 13:55:06 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-02-07 13:55:06 +00:00
return " $complex_status : $complex_health " ;
2024-02-06 16:37:07 +00:00
}
},
);
}
2023-08-08 09:51:36 +00:00
2023-04-24 11:25:02 +00:00
public function portsExposesArray () : Attribute
2023-03-30 07:47:04 +00:00
{
return Attribute :: make (
2023-08-11 18:48:52 +00:00
get : fn () => is_null ( $this -> ports_exposes )
2023-03-30 07:47:04 +00:00
? []
2023-04-24 11:25:02 +00:00
: explode ( ',' , $this -> ports_exposes )
2023-03-30 07:47:04 +00:00
);
}
2024-06-10 20:43:34 +00:00
2024-02-01 14:38:12 +00:00
public function tags ()
{
return $this -> morphToMany ( Tag :: class , 'taggable' );
}
2024-06-10 20:43:34 +00:00
2024-02-06 14:05:11 +00:00
public function project ()
{
2024-02-03 11:39:07 +00:00
return data_get ( $this , 'environment.project' );
}
2024-06-10 20:43:34 +00:00
2024-01-23 16:13:23 +00:00
public function team ()
{
return data_get ( $this , 'environment.project.team' );
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
public function serviceType ()
{
$found = str ( collect ( SPECIFIC_SERVICES ) -> filter ( function ( $service ) {
return str ( $this -> image ) -> before ( ':' ) -> value () === $service ;
}) -> first ());
if ( $found -> isNotEmpty ()) {
return $found ;
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return null ;
}
2024-06-10 20:43:34 +00:00
2024-04-26 10:59:51 +00:00
public function main_port ()
{
return $this -> settings -> is_static ? [ 80 ] : $this -> ports_exposes_array ;
}
2024-06-10 20:43:34 +00:00
2023-05-04 20:29:14 +00:00
public function environment_variables () : HasMany
{
2023-09-08 14:16:59 +00:00
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , false ) -> orderBy ( 'key' , 'asc' );
2023-05-04 20:29:14 +00:00
}
2023-08-08 09:51:36 +00:00
2023-05-05 12:48:40 +00:00
public function runtime_environment_variables () : HasMany
{
2023-06-05 10:07:55 +00:00
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , false ) -> where ( 'key' , 'not like' , 'NIXPACKS_%' );
2023-05-05 12:48:40 +00:00
}
2023-08-08 09:51:36 +00:00
// Preview Deployments
2023-05-05 07:02:50 +00:00
public function build_environment_variables () : HasMany
{
2023-06-05 10:07:55 +00:00
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , false ) -> where ( 'is_build_time' , true ) -> where ( 'key' , 'not like' , 'NIXPACKS_%' );
2023-05-05 07:02:50 +00:00
}
2023-08-08 09:51:36 +00:00
2023-05-05 08:51:58 +00:00
public function nixpacks_environment_variables () : HasMany
{
2023-06-05 10:07:55 +00:00
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , false ) -> where ( 'key' , 'like' , 'NIXPACKS_%' );
}
2023-08-08 09:51:36 +00:00
2023-06-05 10:07:55 +00:00
public function environment_variables_preview () : HasMany
{
2023-09-08 14:16:59 +00:00
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , true ) -> orderBy ( 'key' , 'asc' );
2023-06-05 10:07:55 +00:00
}
2023-08-08 09:51:36 +00:00
2023-06-05 10:07:55 +00:00
public function runtime_environment_variables_preview () : HasMany
{
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , true ) -> where ( 'key' , 'not like' , 'NIXPACKS_%' );
}
2023-08-08 09:51:36 +00:00
2023-06-05 10:07:55 +00:00
public function build_environment_variables_preview () : HasMany
{
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , true ) -> where ( 'is_build_time' , true ) -> where ( 'key' , 'not like' , 'NIXPACKS_%' );
}
2023-08-08 09:51:36 +00:00
2023-06-05 10:07:55 +00:00
public function nixpacks_environment_variables_preview () : HasMany
{
return $this -> hasMany ( EnvironmentVariable :: class ) -> where ( 'is_preview' , true ) -> where ( 'key' , 'like' , 'NIXPACKS_%' );
2023-05-05 08:51:58 +00:00
}
2023-08-08 09:51:36 +00:00
2024-01-01 18:33:16 +00:00
public function scheduled_tasks () : HasMany
{
return $this -> hasMany ( ScheduledTask :: class ) -> orderBy ( 'name' , 'asc' );
}
2023-05-10 11:05:32 +00:00
public function private_key ()
{
return $this -> belongsTo ( PrivateKey :: class );
}
2023-08-08 09:51:36 +00:00
2023-04-26 12:29:33 +00:00
public function environment ()
{
return $this -> belongsTo ( Environment :: class );
}
2023-08-08 09:51:36 +00:00
2023-05-30 13:52:17 +00:00
public function previews ()
{
return $this -> hasMany ( ApplicationPreview :: class );
}
2023-08-08 09:51:36 +00:00
2023-04-26 12:29:33 +00:00
public function destination ()
{
return $this -> morphTo ();
}
2023-08-08 09:51:36 +00:00
2023-04-26 12:29:33 +00:00
public function source ()
{
return $this -> morphTo ();
}
2024-06-10 20:43:34 +00:00
2023-11-21 14:31:46 +00:00
public function isDeploymentInprogress ()
{
2024-06-09 20:12:13 +00:00
$deployments = ApplicationDeploymentQueue :: where ( 'application_id' , $this -> id ) -> whereIn ( 'status' , [ ApplicationDeploymentStatus :: IN_PROGRESS , ApplicationDeploymentStatus :: QUEUED ]) -> count ();
2023-11-10 09:34:28 +00:00
if ( $deployments > 0 ) {
return true ;
}
2024-06-10 20:43:34 +00:00
2023-11-10 09:34:28 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2024-05-17 06:53:25 +00:00
public function get_last_successful_deployment ()
{
2024-06-12 10:05:08 +00:00
return ApplicationDeploymentQueue :: where ( 'application_id' , $this -> id ) -> where ( 'status' , ApplicationDeploymentStatus :: FINISHED ) -> where ( 'pull_request_id' , 0 ) -> orderBy ( 'created_at' , 'desc' ) -> first ();
2024-05-17 06:53:25 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-01 17:24:14 +00:00
public function get_last_days_deployments ()
{
return ApplicationDeploymentQueue :: where ( 'application_id' , $this -> id ) -> where ( 'created_at' , '>=' , now () -> subDays ( 7 )) -> orderBy ( 'created_at' , 'desc' ) -> get ();
}
2024-06-10 20:43:34 +00:00
2023-05-31 12:24:20 +00:00
public function deployments ( int $skip = 0 , int $take = 10 )
2023-03-29 10:52:22 +00:00
{
2023-05-31 12:57:42 +00:00
$deployments = ApplicationDeploymentQueue :: where ( 'application_id' , $this -> id ) -> orderBy ( 'created_at' , 'desc' );
$count = $deployments -> count ();
$deployments = $deployments -> skip ( $skip ) -> take ( $take ) -> get ();
2024-06-10 20:43:34 +00:00
2023-05-31 12:57:42 +00:00
return [
'count' => $count ,
2024-06-10 20:43:34 +00:00
'deployments' => $deployments ,
2023-05-31 12:57:42 +00:00
];
2023-03-29 10:52:22 +00:00
}
2023-08-08 09:51:36 +00:00
2023-03-29 10:27:02 +00:00
public function get_deployment ( string $deployment_uuid )
2023-03-28 13:47:37 +00:00
{
2023-05-03 06:51:03 +00:00
return Activity :: where ( 'subject_id' , $this -> id ) -> where ( 'properties->type_uuid' , '=' , $deployment_uuid ) -> first ();
2023-03-28 13:47:37 +00:00
}
2023-08-08 09:51:36 +00:00
2023-05-10 09:43:49 +00:00
public function isDeployable () : bool
{
2023-05-31 09:24:02 +00:00
if ( $this -> settings -> is_auto_deploy_enabled ) {
return true ;
}
2024-06-10 20:43:34 +00:00
2023-05-31 09:24:02 +00:00
return false ;
}
2023-08-08 09:51:36 +00:00
2023-05-31 09:24:02 +00:00
public function isPRDeployable () : bool
{
if ( $this -> settings -> is_preview_deployments_enabled ) {
2023-05-10 09:43:49 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-05-10 09:43:49 +00:00
return false ;
}
2023-08-08 09:51:36 +00:00
2023-05-10 11:05:32 +00:00
public function deploymentType ()
{
2023-12-30 13:20:02 +00:00
if ( isDev () && data_get ( $this , 'private_key_id' ) === 0 ) {
return 'deploy_key' ;
}
2023-07-05 20:10:10 +00:00
if ( data_get ( $this , 'private_key_id' )) {
return 'deploy_key' ;
2024-06-10 20:43:34 +00:00
} elseif ( data_get ( $this , 'source' )) {
2023-05-10 11:05:32 +00:00
return 'source' ;
2023-10-06 11:46:42 +00:00
} else {
return 'other' ;
2023-05-10 11:05:32 +00:00
}
throw new \Exception ( 'No deployment type found' );
}
2024-06-10 20:43:34 +00:00
2023-08-11 20:41:47 +00:00
public function could_set_build_commands () : bool
{
if ( $this -> build_pack === 'nixpacks' ) {
return true ;
}
2024-06-10 20:43:34 +00:00
2023-08-11 20:41:47 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2023-08-11 20:41:47 +00:00
public function git_based () : bool
{
2023-09-29 12:26:31 +00:00
if ( $this -> dockerfile ) {
2023-08-11 20:41:47 +00:00
return false ;
}
2023-10-10 12:02:43 +00:00
if ( $this -> build_pack === 'dockerimage' ) {
2023-10-10 09:16:38 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2023-08-11 20:41:47 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-10-01 16:14:13 +00:00
public function isHealthcheckDisabled () : bool
{
2023-10-10 12:02:43 +00:00
if ( data_get ( $this , 'health_check_enabled' ) === false ) {
2023-10-01 16:14:13 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-10-01 16:14:13 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2024-03-04 09:13:40 +00:00
public function workdir ()
{
2024-06-19 07:07:49 +00:00
return application_configuration_dir () . " / { $this -> uuid } " ;
2024-03-04 09:13:40 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-04 10:01:14 +00:00
public function isLogDrainEnabled ()
2023-11-21 14:31:46 +00:00
{
return data_get ( $this , 'settings.is_log_drain_enabled' , false );
2023-11-17 19:08:21 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-15 10:55:43 +00:00
public function isConfigurationChanged ( bool $save = false )
2023-10-18 09:20:40 +00:00
{
2024-06-19 07:07:49 +00:00
$newConfigHash = $this -> fqdn . $this -> git_repository . $this -> git_branch . $this -> git_commit_sha . $this -> build_pack . $this -> static_image . $this -> install_command . $this -> build_command . $this -> start_command . $this -> ports_exposes . $this -> ports_mappings . $this -> base_directory . $this -> publish_directory . $this -> dockerfile . $this -> dockerfile_location . $this -> custom_labels . $this -> custom_docker_run_options . $this -> dockerfile_target_build . $this -> redirect ;
2024-01-03 12:20:24 +00:00
if ( $this -> pull_request_id === 0 || $this -> pull_request_id === null ) {
2024-04-15 13:45:50 +00:00
$newConfigHash .= json_encode ( $this -> environment_variables () -> get ( 'value' ) -> sort ());
2023-10-18 09:20:40 +00:00
} else {
2024-04-15 13:45:50 +00:00
$newConfigHash .= json_encode ( $this -> environment_variables_preview -> get ( 'value' ) -> sort ());
2023-10-18 09:20:40 +00:00
}
$newConfigHash = md5 ( $newConfigHash );
$oldConfigHash = data_get ( $this , 'config_hash' );
if ( $oldConfigHash === null ) {
if ( $save ) {
$this -> config_hash = $newConfigHash ;
$this -> save ();
}
2024-06-10 20:43:34 +00:00
2023-10-18 09:20:40 +00:00
return true ;
}
if ( $oldConfigHash === $newConfigHash ) {
return false ;
} else {
if ( $save ) {
$this -> config_hash = $newConfigHash ;
$this -> save ();
}
2024-06-10 20:43:34 +00:00
2023-10-18 09:20:40 +00:00
return true ;
}
}
2024-06-10 20:43:34 +00:00
public function customRepository ()
2023-11-24 14:48:23 +00:00
{
preg_match ( '/(?<=:)\d+(?=\/)/' , $this -> git_repository , $matches );
$port = 22 ;
if ( count ( $matches ) === 1 ) {
$port = $matches [ 0 ];
$gitHost = str ( $this -> git_repository ) -> before ( ':' );
$gitRepo = str ( $this -> git_repository ) -> after ( '/' );
$repository = " $gitHost : $gitRepo " ;
} else {
$repository = $this -> git_repository ;
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return [
'repository' => $repository ,
2024-06-10 20:43:34 +00:00
'port' => $port ,
2023-11-24 14:48:23 +00:00
];
}
2024-06-10 20:43:34 +00:00
public function generateBaseDir ( string $uuid )
2023-11-24 14:48:23 +00:00
{
return " /artifacts/ { $uuid } " ;
}
2024-06-10 20:43:34 +00:00
public function setGitImportSettings ( string $deployment_uuid , string $git_clone_command , bool $public = false )
2023-11-24 14:48:23 +00:00
{
$baseDir = $this -> generateBaseDir ( $deployment_uuid );
2024-02-19 12:22:09 +00:00
2023-11-24 14:48:23 +00:00
if ( $this -> git_commit_sha !== 'HEAD' ) {
2024-02-19 12:22:09 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \" git -c advice.detachedHead=false checkout { $this -> git_commit_sha } >/dev/null 2>&1 " ;
2023-11-24 14:48:23 +00:00
}
if ( $this -> settings -> is_git_submodules_enabled ) {
2024-02-19 12:22:09 +00:00
if ( $public ) {
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && sed -i \" s#git@ \ (.* \ ):#https:// \\ 1/#g \" { $baseDir } /.gitmodules || true " ;
}
2024-04-08 12:07:07 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \" git submodule update --init --recursive " ;
2023-11-24 14:48:23 +00:00
}
if ( $this -> settings -> is_git_lfs_enabled ) {
2024-02-19 12:22:09 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \" git lfs pull " ;
2023-11-24 14:48:23 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return $git_clone_command ;
}
2024-06-10 20:43:34 +00:00
public function generateGitImportCommands ( string $deployment_uuid , int $pull_request_id = 0 , ? string $git_type = null , bool $exec_in_docker = true , bool $only_checkout = false , ? string $custom_base_dir = null , ? string $commit = null )
2023-11-24 14:48:23 +00:00
{
$branch = $this -> git_branch ;
[ 'repository' => $customRepository , 'port' => $customPort ] = $this -> customRepository ();
$baseDir = $custom_base_dir ? ? $this -> generateBaseDir ( $deployment_uuid );
$commands = collect ([]);
2024-04-19 22:10:50 +00:00
$git_clone_command = " git clone -b \" { $this -> git_branch } \" " ;
2023-11-24 14:48:23 +00:00
if ( $only_checkout ) {
2024-04-19 22:10:50 +00:00
$git_clone_command = " git clone --no-checkout -b \" { $this -> git_branch } \" " ;
2023-11-24 14:48:23 +00:00
}
if ( $pull_request_id !== 0 ) {
$pr_branch_name = " pr- { $pull_request_id } -coolify " ;
}
if ( $this -> deploymentType () === 'source' ) {
$source_html_url = data_get ( $this , 'source.html_url' );
$url = parse_url ( filter_var ( $source_html_url , FILTER_SANITIZE_URL ));
$source_html_url_host = $url [ 'host' ];
$source_html_url_scheme = $url [ 'scheme' ];
if ( $this -> source -> getMorphClass () == 'App\Models\GithubApp' ) {
if ( $this -> source -> is_public ) {
$fullRepoUrl = " { $this -> source -> html_url } / { $customRepository } " ;
$git_clone_command = " { $git_clone_command } { $this -> source -> html_url } / { $customRepository } { $baseDir } " ;
2024-06-19 07:07:49 +00:00
if ( ! $only_checkout ) {
2024-02-19 12:22:09 +00:00
$git_clone_command = $this -> setGitImportSettings ( $deployment_uuid , $git_clone_command , public : true );
2023-11-24 14:48:23 +00:00
}
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , $git_clone_command ));
} else {
$commands -> push ( $git_clone_command );
}
} else {
$github_access_token = generate_github_installation_token ( $this -> source );
if ( $exec_in_docker ) {
2024-04-08 12:07:07 +00:00
$git_clone_command = " { $git_clone_command } $source_html_url_scheme ://x-access-token: $github_access_token @ $source_html_url_host / { $customRepository } .git { $baseDir } " ;
2023-11-24 14:48:23 +00:00
$fullRepoUrl = " $source_html_url_scheme ://x-access-token: $github_access_token @ $source_html_url_host / { $customRepository } .git " ;
} else {
2024-04-08 12:07:07 +00:00
$git_clone_command = " { $git_clone_command } $source_html_url_scheme ://x-access-token: $github_access_token @ $source_html_url_host / { $customRepository } { $baseDir } " ;
2023-11-24 14:48:23 +00:00
$fullRepoUrl = " $source_html_url_scheme ://x-access-token: $github_access_token @ $source_html_url_host / { $customRepository } " ;
}
2024-06-19 07:07:49 +00:00
if ( ! $only_checkout ) {
2024-05-29 16:01:10 +00:00
$git_clone_command = $this -> setGitImportSettings ( $deployment_uuid , $git_clone_command , public : false );
}
2024-04-08 12:07:07 +00:00
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , $git_clone_command ));
} else {
$commands -> push ( $git_clone_command );
}
2023-11-24 14:48:23 +00:00
}
if ( $pull_request_id !== 0 ) {
$branch = " pull/ { $pull_request_id } /head: $pr_branch_name " ;
2024-03-28 08:53:09 +00:00
$git_checkout_command = $this -> buildGitCheckoutCommand ( $pr_branch_name );
2023-11-24 14:48:23 +00:00
if ( $exec_in_docker ) {
2024-03-28 08:53:09 +00:00
$commands -> push ( executeInDocker ( $deployment_uuid , " cd { $baseDir } && git fetch origin { $branch } && $git_checkout_command " ));
2023-11-24 14:48:23 +00:00
} else {
2024-03-28 08:53:09 +00:00
$commands -> push ( " cd { $baseDir } && git fetch origin { $branch } && $git_checkout_command " );
2023-11-24 14:48:23 +00:00
}
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return [
'commands' => $commands -> implode ( ' && ' ),
'branch' => $branch ,
2024-06-10 20:43:34 +00:00
'fullRepoUrl' => $fullRepoUrl ,
2023-11-24 14:48:23 +00:00
];
}
}
if ( $this -> deploymentType () === 'deploy_key' ) {
$fullRepoUrl = $customRepository ;
$private_key = data_get ( $this , 'private_key.private_key' );
if ( is_null ( $private_key )) {
throw new RuntimeException ( 'Private key not found. Please add a private key to the application and try again.' );
}
$private_key = base64_encode ( $private_key );
$git_clone_command_base = " GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" { $git_clone_command } { $customRepository } { $baseDir } " ;
2023-12-30 13:20:02 +00:00
if ( $only_checkout ) {
$git_clone_command = $git_clone_command_base ;
} else {
2023-11-24 14:48:23 +00:00
$git_clone_command = $this -> setGitImportSettings ( $deployment_uuid , $git_clone_command_base );
}
if ( $exec_in_docker ) {
$commands = collect ([
2024-06-10 20:43:34 +00:00
executeInDocker ( $deployment_uuid , 'mkdir -p /root/.ssh' ),
2024-04-17 08:49:34 +00:00
executeInDocker ( $deployment_uuid , " echo ' { $private_key } ' | base64 -d | tee /root/.ssh/id_rsa > /dev/null " ),
2024-06-10 20:43:34 +00:00
executeInDocker ( $deployment_uuid , 'chmod 600 /root/.ssh/id_rsa' ),
2023-11-24 14:48:23 +00:00
]);
} else {
$commands = collect ([
2024-06-10 20:43:34 +00:00
'mkdir -p /root/.ssh' ,
2024-04-17 08:49:34 +00:00
" echo ' { $private_key } ' | base64 -d | tee /root/.ssh/id_rsa > /dev/null " ,
2024-06-10 20:43:34 +00:00
'chmod 600 /root/.ssh/id_rsa' ,
2023-11-24 14:48:23 +00:00
]);
}
if ( $pull_request_id !== 0 ) {
if ( $git_type === 'gitlab' ) {
$branch = " merge-requests/ { $pull_request_id } /head: $pr_branch_name " ;
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" git fetch origin $branch && " . $this -> buildGitCheckoutCommand ( $pr_branch_name );
2024-06-10 20:43:34 +00:00
} elseif ( $git_type === 'github' ) {
2023-11-24 14:48:23 +00:00
$branch = " pull/ { $pull_request_id } /head: $pr_branch_name " ;
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" git fetch origin $branch && " . $this -> buildGitCheckoutCommand ( $pr_branch_name );
2024-06-10 20:43:34 +00:00
} elseif ( $git_type === 'bitbucket' ) {
2024-01-29 15:33:06 +00:00
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" " . $this -> buildGitCheckoutCommand ( $commit );
2023-11-24 14:48:23 +00:00
}
}
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , $git_clone_command ));
} else {
$commands -> push ( $git_clone_command );
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return [
'commands' => $commands -> implode ( ' && ' ),
'branch' => $branch ,
2024-06-10 20:43:34 +00:00
'fullRepoUrl' => $fullRepoUrl ,
2023-11-24 14:48:23 +00:00
];
}
if ( $this -> deploymentType () === 'other' ) {
$fullRepoUrl = $customRepository ;
$git_clone_command = " { $git_clone_command } { $customRepository } { $baseDir } " ;
2024-02-19 12:22:09 +00:00
$git_clone_command = $this -> setGitImportSettings ( $deployment_uuid , $git_clone_command , public : true );
2024-01-29 09:43:18 +00:00
if ( $pull_request_id !== 0 ) {
if ( $git_type === 'gitlab' ) {
$branch = " merge-requests/ { $pull_request_id } /head: $pr_branch_name " ;
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" git fetch origin $branch && " . $this -> buildGitCheckoutCommand ( $pr_branch_name );
2024-06-10 20:43:34 +00:00
} elseif ( $git_type === 'github' ) {
2024-01-29 09:43:18 +00:00
$branch = " pull/ { $pull_request_id } /head: $pr_branch_name " ;
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" git fetch origin $branch && " . $this -> buildGitCheckoutCommand ( $pr_branch_name );
2024-06-10 20:43:34 +00:00
} elseif ( $git_type === 'bitbucket' ) {
2024-01-29 09:43:18 +00:00
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , " echo 'Checking out $branch ' " ));
} else {
$commands -> push ( " echo 'Checking out $branch ' " );
}
2024-06-19 07:07:49 +00:00
$git_clone_command = " { $git_clone_command } && cd { $baseDir } && GIT_SSH_COMMAND= \" ssh -o ConnectTimeout=30 -p { $customPort } -o Port= { $customPort } -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/id_rsa \" " . $this -> buildGitCheckoutCommand ( $commit );
2024-01-29 09:43:18 +00:00
}
}
2023-11-24 14:48:23 +00:00
if ( $exec_in_docker ) {
$commands -> push ( executeInDocker ( $deployment_uuid , $git_clone_command ));
} else {
$commands -> push ( $git_clone_command );
}
2024-06-10 20:43:34 +00:00
2023-11-24 14:48:23 +00:00
return [
'commands' => $commands -> implode ( ' && ' ),
'branch' => $branch ,
2024-06-10 20:43:34 +00:00
'fullRepoUrl' => $fullRepoUrl ,
2023-11-24 14:48:23 +00:00
];
}
}
2024-06-10 20:43:34 +00:00
public function parseRawCompose ()
2024-03-04 09:13:40 +00:00
{
try {
$yaml = Yaml :: parse ( $this -> docker_compose_raw );
} catch ( \Exception $e ) {
throw new \Exception ( $e -> getMessage ());
}
$services = data_get ( $yaml , 'services' );
$commands = collect ([]);
$services = collect ( $services ) -> map ( function ( $service ) use ( $commands ) {
$serviceVolumes = collect ( data_get ( $service , 'volumes' , []));
if ( $serviceVolumes -> count () > 0 ) {
foreach ( $serviceVolumes as $volume ) {
$workdir = $this -> workdir ();
$type = null ;
$source = null ;
if ( is_string ( $volume )) {
2024-06-25 08:37:10 +00:00
$source = str ( $volume ) -> before ( ':' );
2024-03-04 09:13:40 +00:00
if ( $source -> startsWith ( './' ) || $source -> startsWith ( '/' ) || $source -> startsWith ( '~' )) {
2024-06-25 08:37:10 +00:00
$type = str ( 'bind' );
2024-03-04 09:13:40 +00:00
}
2024-06-10 20:43:34 +00:00
} elseif ( is_array ( $volume )) {
2024-03-04 09:13:40 +00:00
$type = data_get_str ( $volume , 'type' );
$source = data_get_str ( $volume , 'source' );
}
2024-03-18 11:40:58 +00:00
if ( $type ? -> value () === 'bind' ) {
2024-06-10 20:43:34 +00:00
if ( $source -> value () === '/var/run/docker.sock' ) {
2024-03-04 09:13:40 +00:00
continue ;
}
if ( $source -> value () === '/tmp' || $source -> value () === '/tmp/' ) {
continue ;
}
if ( $source -> startsWith ( '.' )) {
$source = $source -> after ( '.' );
2024-06-19 07:07:49 +00:00
$source = $workdir . $source ;
2024-03-04 09:13:40 +00:00
}
$commands -> push ( " mkdir -p $source > /dev/null 2>&1 || true " );
}
}
}
$labels = collect ( data_get ( $service , 'labels' , []));
2024-06-19 07:07:49 +00:00
if ( ! $labels -> contains ( 'coolify.managed' )) {
2024-03-04 09:13:40 +00:00
$labels -> push ( 'coolify.managed=true' );
}
2024-06-19 07:07:49 +00:00
if ( ! $labels -> contains ( 'coolify.applicationId' )) {
$labels -> push ( 'coolify.applicationId=' . $this -> id );
2024-03-04 09:13:40 +00:00
}
2024-06-19 07:07:49 +00:00
if ( ! $labels -> contains ( 'coolify.type' )) {
2024-03-04 09:13:40 +00:00
$labels -> push ( 'coolify.type=application' );
}
data_set ( $service , 'labels' , $labels -> toArray ());
2024-06-10 20:43:34 +00:00
2024-03-04 09:13:40 +00:00
return $service ;
});
data_set ( $yaml , 'services' , $services -> toArray ());
$this -> docker_compose_raw = Yaml :: dump ( $yaml , 10 , 2 );
instant_remote_process ( $commands , $this -> destination -> server , false );
}
2024-06-10 20:43:34 +00:00
public function parseCompose ( int $pull_request_id = 0 , ? int $preview_id = null )
2023-11-24 14:48:23 +00:00
{
if ( $this -> docker_compose_raw ) {
2024-06-05 13:14:44 +00:00
return parseDockerComposeFile ( resource : $this , isNew : false , pull_request_id : $pull_request_id , preview_id : $preview_id );
2023-11-24 14:48:23 +00:00
} else {
return collect ([]);
}
}
2024-06-10 20:43:34 +00:00
public function loadComposeFile ( $isInit = false )
2023-11-27 10:54:55 +00:00
{
$initialDockerComposeLocation = $this -> docker_compose_location ;
2023-12-15 10:00:51 +00:00
if ( $isInit && $this -> docker_compose_raw ) {
return ;
}
$uuid = new Cuid2 ();
[ 'commands' => $cloneCommand ] = $this -> generateGitImportCommands ( deployment_uuid : $uuid , only_checkout : true , exec_in_docker : false , custom_base_dir : '.' );
$workdir = rtrim ( $this -> base_directory , '/' );
$composeFile = $this -> docker_compose_location ;
// $prComposeFile = $this->docker_compose_pr_location;
$fileList = collect ([ " . $workdir $composeFile " ]);
// if ($composeFile !== $prComposeFile) {
// $fileList->push(".$prComposeFile");
// }
$commands = collect ([
2023-12-21 19:16:03 +00:00
" rm -rf /tmp/ { $uuid } " ,
2024-05-29 16:01:10 +00:00
" mkdir -p /tmp/ { $uuid } " ,
" cd /tmp/ { $uuid } " ,
2023-12-15 10:00:51 +00:00
$cloneCommand ,
2024-06-10 20:43:34 +00:00
'git sparse-checkout init --cone' ,
2023-12-15 10:00:51 +00:00
" git sparse-checkout set { $fileList -> implode ( ' ' ) } " ,
2024-06-10 20:43:34 +00:00
'git read-tree -mu HEAD' ,
2023-12-15 10:00:51 +00:00
" cat . $workdir $composeFile " ,
]);
$composeFileContent = instant_remote_process ( $commands , $this -> destination -> server , false );
2024-06-19 07:07:49 +00:00
if ( ! $composeFileContent ) {
2023-12-15 10:00:51 +00:00
$this -> docker_compose_location = $initialDockerComposeLocation ;
$this -> save ();
2024-05-29 16:01:10 +00:00
$commands = collect ([
" rm -rf /tmp/ { $uuid } " ,
]);
instant_remote_process ( $commands , $this -> destination -> server , false );
2024-05-21 10:02:04 +00:00
throw new \RuntimeException ( " Docker Compose file not found at: $workdir $composeFile <br><br>Check if you used the right extension (.yaml or .yml) in the compose file name. " );
2023-12-15 10:00:51 +00:00
} else {
$this -> docker_compose_raw = $composeFileContent ;
$this -> save ();
}
2023-11-28 09:11:53 +00:00
2023-12-15 10:00:51 +00:00
$commands = collect ([
" rm -rf /tmp/ { $uuid } " ,
]);
instant_remote_process ( $commands , $this -> destination -> server , false );
$parsedServices = $this -> parseCompose ();
if ( $this -> docker_compose_domains ) {
$json = collect ( json_decode ( $this -> docker_compose_domains ));
$names = collect ( data_get ( $parsedServices , 'services' )) -> keys () -> toArray ();
$jsonNames = $json -> keys () -> toArray ();
$diff = array_diff ( $jsonNames , $names );
$json = $json -> filter ( function ( $value , $key ) use ( $diff ) {
2024-06-19 07:07:49 +00:00
return ! in_array ( $key , $diff );
2023-12-15 10:00:51 +00:00
});
if ( $json ) {
$this -> docker_compose_domains = json_encode ( $json );
} else {
2023-12-15 09:37:45 +00:00
$this -> docker_compose_domains = null ;
}
2023-12-15 10:00:51 +00:00
$this -> save ();
2023-11-27 10:54:55 +00:00
}
2024-06-10 20:43:34 +00:00
2023-12-15 10:00:51 +00:00
return [
'parsedServices' => $parsedServices ,
'initialDockerComposeLocation' => $this -> docker_compose_location ,
'initialDockerComposePrLocation' => $this -> docker_compose_pr_location ,
];
2023-11-27 10:54:55 +00:00
}
2024-06-10 20:43:34 +00:00
public function parseContainerLabels ( ? ApplicationPreview $preview = null )
2023-12-13 08:23:27 +00:00
{
$customLabels = data_get ( $this , 'custom_labels' );
2024-06-19 07:07:49 +00:00
if ( ! $customLabels ) {
2023-12-13 08:23:27 +00:00
return ;
}
if ( base64_encode ( base64_decode ( $customLabels , true )) !== $customLabels ) {
ray ( 'custom_labels is not base64 encoded' );
$this -> custom_labels = str ( $customLabels ) -> replace ( ',' , " \n " );
$this -> custom_labels = base64_encode ( $customLabels );
}
$customLabels = base64_decode ( $this -> custom_labels );
if ( mb_detect_encoding ( $customLabels , 'ASCII' , true ) === false ) {
ray ( 'custom_labels contains non-ascii characters' );
2024-06-11 09:36:42 +00:00
$customLabels = str ( implode ( '|coolify|' , generateLabelsApplication ( $this , $preview ))) -> replace ( '|coolify|' , " \n " );
2023-12-13 08:23:27 +00:00
}
$this -> custom_labels = base64_encode ( $customLabels );
$this -> save ();
2024-06-10 20:43:34 +00:00
2023-12-13 08:23:27 +00:00
return $customLabels ;
}
2024-06-10 20:43:34 +00:00
2024-01-30 08:22:34 +00:00
public function fqdns () : Attribute
{
return Attribute :: make (
get : fn () => is_null ( $this -> fqdn )
? []
: explode ( ',' , $this -> fqdn ),
);
}
2024-06-10 20:43:34 +00:00
2024-04-08 12:07:07 +00:00
protected function buildGitCheckoutCommand ( $target ) : string
{
2024-03-28 08:53:09 +00:00
$command = " git checkout $target " ;
if ( $this -> settings -> is_git_submodules_enabled ) {
2024-06-10 20:43:34 +00:00
$command .= ' && git submodule update --init --recursive' ;
2024-03-28 08:53:09 +00:00
}
return $command ;
2024-04-08 12:07:07 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-28 14:05:12 +00:00
public function watchPaths () : Attribute
{
return Attribute :: make (
set : function ( $value ) {
if ( $value ) {
return trim ( $value );
}
}
);
}
2024-06-10 20:43:34 +00:00
2024-04-03 12:05:35 +00:00
public function isWatchPathsTriggered ( Collection $modified_files ) : bool
2024-03-28 14:05:12 +00:00
{
2024-04-03 12:05:35 +00:00
if ( is_null ( $this -> watch_paths )) {
return false ;
}
2024-03-28 14:05:12 +00:00
$watch_paths = collect ( explode ( " \n " , $this -> watch_paths ));
$matches = $modified_files -> filter ( function ( $file ) use ( $watch_paths ) {
return $watch_paths -> contains ( function ( $glob ) use ( $file ) {
return fnmatch ( $glob , $file );
});
});
2024-06-10 20:43:34 +00:00
2024-03-28 14:05:12 +00:00
return $matches -> count () > 0 ;
2024-03-28 08:53:09 +00:00
}
2024-04-15 17:47:17 +00:00
public function getFilesFromServer ( bool $isInit = false )
{
getFilesystemVolumesFromServer ( $this , $isInit );
}
2024-04-29 11:33:28 +00:00
2024-05-17 08:12:05 +00:00
public function parseHealthcheckFromDockerfile ( $dockerfile , bool $isInit = false )
{
2024-04-29 11:33:28 +00:00
if ( str ( $dockerfile ) -> contains ( 'HEALTHCHECK' ) && ( $this -> isHealthcheckDisabled () || $isInit )) {
$healthcheckCommand = null ;
$lines = $dockerfile -> toArray ();
foreach ( $lines as $line ) {
$trimmedLine = trim ( $line );
if ( str_starts_with ( $trimmedLine , 'HEALTHCHECK' )) {
$healthcheckCommand .= trim ( $trimmedLine , '\\ ' );
2024-06-10 20:43:34 +00:00
2024-04-29 11:33:28 +00:00
continue ;
}
if ( isset ( $healthcheckCommand ) && str_contains ( $trimmedLine , '\\' )) {
2024-06-19 07:07:49 +00:00
$healthcheckCommand .= ' ' . trim ( $trimmedLine , '\\ ' );
2024-04-29 11:33:28 +00:00
}
2024-06-19 07:07:49 +00:00
if ( isset ( $healthcheckCommand ) && ! str_contains ( $trimmedLine , '\\' ) && ! empty ( $healthcheckCommand )) {
$healthcheckCommand .= ' ' . $trimmedLine ;
2024-04-29 11:33:28 +00:00
break ;
}
}
if ( str ( $healthcheckCommand ) -> isNotEmpty ()) {
$interval = str ( $healthcheckCommand ) -> match ( '/--interval=(\d+)/' );
$timeout = str ( $healthcheckCommand ) -> match ( '/--timeout=(\d+)/' );
$start_period = str ( $healthcheckCommand ) -> match ( '/--start-period=(\d+)/' );
$start_interval = str ( $healthcheckCommand ) -> match ( '/--start-interval=(\d+)/' );
$retries = str ( $healthcheckCommand ) -> match ( '/--retries=(\d+)/' );
if ( $interval -> isNotEmpty ()) {
$this -> health_check_interval = $interval -> toInteger ();
}
if ( $timeout -> isNotEmpty ()) {
$this -> health_check_timeout = $timeout -> toInteger ();
}
if ( $start_period -> isNotEmpty ()) {
$this -> health_check_start_period = $start_period -> toInteger ();
}
// if ($start_interval) {
// $this->health_check_start_interval = $start_interval->value();
// }
if ( $retries -> isNotEmpty ()) {
$this -> health_check_retries = $retries -> toInteger ();
}
if ( $interval || $timeout || $start_period || $start_interval || $retries ) {
$this -> custom_healthcheck_found = true ;
$this -> save ();
}
}
}
}
2024-06-10 20:43:34 +00:00
public function generate_preview_fqdn ( int $pull_request_id )
2024-06-05 13:14:44 +00:00
{
2024-05-30 10:28:29 +00:00
$preview = ApplicationPreview :: findPreviewByApplicationAndPullId ( $this -> id , $pull_request_id );
if ( is_null ( data_get ( $preview , 'fqdn' )) && $this -> fqdn ) {
if ( str ( $this -> fqdn ) -> contains ( ',' )) {
$url = Url :: fromString ( str ( $this -> fqdn ) -> explode ( ',' )[ 0 ]);
$preview_fqdn = getFqdnWithoutPort ( str ( $this -> fqdn ) -> explode ( ',' )[ 0 ]);
} else {
$url = Url :: fromString ( $this -> fqdn );
if ( data_get ( $preview , 'fqdn' )) {
$preview_fqdn = getFqdnWithoutPort ( data_get ( $preview , 'fqdn' ));
}
}
$template = $this -> preview_url_template ;
$host = $url -> getHost ();
$schema = $url -> getScheme ();
$random = new Cuid2 ( 7 );
$preview_fqdn = str_replace ( '{{random}}' , $random , $template );
$preview_fqdn = str_replace ( '{{domain}}' , $host , $preview_fqdn );
$preview_fqdn = str_replace ( '{{pr_id}}' , $pull_request_id , $preview_fqdn );
$preview_fqdn = " $schema :// $preview_fqdn " ;
$preview -> fqdn = $preview_fqdn ;
$preview -> save ();
}
2024-06-10 20:43:34 +00:00
2024-05-30 10:28:29 +00:00
return $preview ;
}
2024-06-18 18:59:39 +00:00
public static function getDomainsByUuid ( string $uuid ) : array
{
$application = self :: where ( 'uuid' , $uuid ) -> first ();
if ( $application ) {
return $application -> fqdns ;
}
return [];
2024-06-20 12:07:24 +00:00
}
2024-06-20 11:17:06 +00:00
public function getMetrics ( int $mins = 5 )
{
$server = $this -> destination -> server ;
2024-06-20 11:30:17 +00:00
$container_name = $this -> uuid ;
2024-06-20 11:17:06 +00:00
if ( $server -> isMetricsEnabled ()) {
$from = now () -> subMinutes ( $mins ) -> toIso8601ZuluString ();
$metrics = instant_remote_process ([ " docker exec coolify-sentinel sh -c 'curl -H \" Authorization: Bearer { $server -> settings -> metrics_token } \" http://localhost:8888/api/container/ { $container_name } /metrics/history?from= $from ' " ], $server , false );
if ( str ( $metrics ) -> contains ( 'error' )) {
$error = json_decode ( $metrics , true );
$error = data_get ( $error , 'error' , 'Something is not okay, are you okay?' );
if ( $error == 'Unauthorized' ) {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.' ;
}
throw new \Exception ( $error );
}
$metrics = str ( $metrics ) -> explode ( " \n " ) -> skip ( 1 ) -> all ();
$parsedCollection = collect ( $metrics ) -> flatMap ( function ( $item ) {
return collect ( explode ( " \n " , trim ( $item ))) -> map ( function ( $line ) {
[ $time , $cpu_usage_percent , $memory_usage , $memory_usage_percent ] = explode ( ',' , trim ( $line ));
$cpu_usage_percent = number_format ( $cpu_usage_percent , 2 );
return [( int ) $time , ( float ) $cpu_usage_percent , ( int ) $memory_usage ];
});
});
return $parsedCollection -> toArray ();
}
2024-06-18 18:59:39 +00:00
}
2023-08-07 20:14:21 +00:00
}