diff --git a/app/Models/Application.php b/app/Models/Application.php index 2a5fc629e..e79a5e702 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -906,50 +906,7 @@ class Application extends BaseModel public function customRepository() { - $repository = $this->git_repository; - - // Let's try and parse the string to detect if it's a valid SSH string or not - $sshMatches = []; - preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $this->git_repository, $sshMatches); - - if ($this->deploymentType() === 'deploy_key' && empty($sshMatches) && $this->source) { - // If this happens, the user may have provided an HTTP URL when they needed an SSH one - // Let's try and fix that for known Git providers - $providerInfo = [ - 'host' => null, - 'user' => 'git', - 'port' => 22, - 'repository' => $this->git_repository, - ]; - - switch ($this->source->getMorphClass()) { - case \App\Models\GithubApp::class: - $providerInfo['host'] = Url::fromString($this->source->html_url)->getHost(); - $providerInfo['port'] = $this->source->custom_port; - $providerInfo['user'] = $this->source->custom_user; - break; - } - - if (! empty($providerInfo['host'])) { - $repository = ($providerInfo['port'] === 22) - ? "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}" - : "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}"; - } - } - - 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"; - } - - return [ - 'repository' => $repository, - 'port' => $port, - ]; + return convertGitUrl($this->git_repository, $this->deploymentType(), $this->source); } public function generateBaseDir(string $uuid) diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 2f0a3ac2a..6e52dcde9 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -7,6 +7,7 @@ use App\Models\Application; use App\Models\ApplicationDeploymentQueue; use App\Models\ApplicationPreview; use App\Models\EnvironmentVariable; +use App\Models\GithubApp; use App\Models\InstanceSettings; use App\Models\LocalFileVolume; use App\Models\LocalPersistentVolume; @@ -4092,3 +4093,53 @@ function defaultNginxConfiguration(): string } }'; } + +function convertGitUrl(string $gitRepository, string $deploymentType, ?GithubApp $source = null): array +{ + $repository = $gitRepository; + $providerInfo = [ + 'host' => null, + 'user' => 'git', + 'port' => 22, + 'repository' => $gitRepository, + ]; + $sshMatches = []; + $matches = []; + + // Let's try and parse the string to detect if it's a valid SSH string or not + preg_match('/((.*?)\:\/\/)?(.*@.*:.*)/', $gitRepository, $sshMatches); + + if ($deploymentType === 'deploy_key' && empty($sshMatches) && $source) { + // If this happens, the user may have provided an HTTP URL when they needed an SSH one + // Let's try and fix that for known Git providers + switch ($source->getMorphClass()) { + case \App\Models\GithubApp::class: + $providerInfo['host'] = Url::fromString($source->html_url)->getHost(); + $providerInfo['port'] = $source->custom_port; + $providerInfo['user'] = $source->custom_user; + break; + } + if (! empty($providerInfo['host'])) { + // Until we do not support more providers with App (like GithubApp), this will be always true, port will be 22 + if ($providerInfo['port'] === 22) { + $repository = "{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['repository']}"; + } else { + $repository = "ssh://{$providerInfo['user']}@{$providerInfo['host']}:{$providerInfo['port']}/{$providerInfo['repository']}"; + } + } + } + + preg_match('/(?<=:)\d+(?=\/)/', $gitRepository, $matches); + + if (count($matches) === 1) { + $providerInfo['port'] = $matches[0]; + $gitHost = str($gitRepository)->before(':'); + $gitRepo = str($gitRepository)->after('/'); + $repository = "$gitHost:$gitRepo"; + } + + return [ + 'repository' => $repository, + 'port' => $providerInfo['port'], + ]; +} diff --git a/scripts/run b/scripts/run index f7e7b5264..9d3c4f1f4 100755 --- a/scripts/run +++ b/scripts/run @@ -24,7 +24,7 @@ function logs { docker exec -t coolify tail -f storage/logs/laravel.log } function test { - docker exec -t coolify php artisan test --testsuite=Feature + docker exec -t coolify php artisan test --testsuite=Feature -p } function sync:bunny { diff --git a/tests/Feature/ConvertingGitUrlsTest.php b/tests/Feature/ConvertingGitUrlsTest.php new file mode 100644 index 000000000..5bcdea1a1 --- /dev/null +++ b/tests/Feature/ConvertingGitUrlsTest.php @@ -0,0 +1,62 @@ +toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); + +}); + +test('convertGitUrlsForDeployKeyAndGithubAppAndSshUrl', function () { + $githubApp = GithubApp::find(0); + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'deploy_key', $githubApp); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForDeployKeyAndHttpUrl', function () { + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'deploy_key', null); + expect($result)->toBe([ + 'repository' => 'andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForDeployKeyAndSshUrl', function () { + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'deploy_key', null); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndSshUrl', function () { + $result = convertGitUrl('git@github.com:andrasbacsai/coolify-examples.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'git@github.com:andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndHttpUrl', function () { + $result = convertGitUrl('andrasbacsai/coolify-examples.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'andrasbacsai/coolify-examples.git', + 'port' => 22, + ]); +}); + +test('convertGitUrlsForSourceAndSshUrlWithCustomPort', function () { + $result = convertGitUrl('git@git.domain.com:766/group/project.git', 'source', null); + expect($result)->toBe([ + 'repository' => 'git@git.domain.com:group/project.git', + 'port' => '766', + ]); +}); diff --git a/tests/Feature/DockerComposeParseTest.php b/tests/Feature/DockerComposeParseTest.php index 8810280dc..d21adac8e 100644 --- a/tests/Feature/DockerComposeParseTest.php +++ b/tests/Feature/DockerComposeParseTest.php @@ -9,171 +9,171 @@ use App\Models\StandaloneDocker; use Illuminate\Support\Collection; use Symfony\Component\Yaml\Yaml; -beforeEach(function () { - $this->applicationYaml = ' -version: "3.8" -services: - app: - image: nginx - environment: - SERVICE_FQDN_APP: /app - APP_KEY: base64 - APP_DEBUG: "${APP_DEBUG:-false}" - APP_URL: $SERVICE_FQDN_APP - DB_URL: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/postgres?schema=public - volumes: - - "./nginx:/etc/nginx" - - "data:/var/www/html" - depends_on: - - db - db: - image: postgres - environment: - POSTGRES_USER: "${SERVICE_USER_POSTGRES}" - POSTGRES_PASSWORD: "${SERVICE_PASSWORD_POSTGRES}" - volumes: - - "dbdata:/var/lib/postgresql/data" - healthcheck: - test: - - CMD - - pg_isready - - "-U" - - "postgres" - interval: 2s - timeout: 10s - retries: 10 - depends_on: - app: - condition: service_healthy -networks: - default: - name: something - external: true - noinet: - driver: bridge - internal: true'; +// beforeEach(function () { +// $this->applicationYaml = ' +// version: "3.8" +// services: +// app: +// image: nginx +// environment: +// SERVICE_FQDN_APP: /app +// APP_KEY: base64 +// APP_DEBUG: "${APP_DEBUG:-false}" +// APP_URL: $SERVICE_FQDN_APP +// DB_URL: postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@db:5432/postgres?schema=public +// volumes: +// - "./nginx:/etc/nginx" +// - "data:/var/www/html" +// depends_on: +// - db +// db: +// image: postgres +// environment: +// POSTGRES_USER: "${SERVICE_USER_POSTGRES}" +// POSTGRES_PASSWORD: "${SERVICE_PASSWORD_POSTGRES}" +// volumes: +// - "dbdata:/var/lib/postgresql/data" +// healthcheck: +// test: +// - CMD +// - pg_isready +// - "-U" +// - "postgres" +// interval: 2s +// timeout: 10s +// retries: 10 +// depends_on: +// app: +// condition: service_healthy +// networks: +// default: +// name: something +// external: true +// noinet: +// driver: bridge +// internal: true'; - $this->applicationComposeFileString = Yaml::parse($this->applicationYaml); +// $this->applicationComposeFileString = Yaml::parse($this->applicationYaml); - $this->application = Application::create([ - 'name' => 'Application for tests', - 'docker_compose_domains' => json_encode([ - 'app' => [ - 'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io', - ], - ]), - 'preview_url_template' => '{{pr_id}}.{{domain}}', - 'uuid' => 'bcoowoookw0co4cok4sgc4k8s', - 'repository_project_id' => 603035348, - 'git_repository' => 'coollabsio/coolify-examples', - 'git_branch' => 'main', - 'base_directory' => '/docker-compose-test', - 'docker_compose_location' => 'docker-compose.yml', - 'docker_compose_raw' => $this->applicationYaml, - 'build_pack' => 'dockercompose', - 'ports_exposes' => '3000', - 'environment_id' => 1, - 'destination_id' => 0, - 'destination_type' => StandaloneDocker::class, - 'source_id' => 1, - 'source_type' => GithubApp::class, - ]); - $this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']); - $this->applicationPreview = ApplicationPreview::create([ - 'git_type' => 'github', - 'application_id' => $this->application->id, - 'pull_request_id' => 1, - 'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1', - ]); - $this->serviceYaml = ' -services: - activepieces: - image: "ghcr.io/activepieces/activepieces:latest" - environment: - - SERVICE_FQDN_ACTIVEPIECES - - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY - - AP_URL=$SERVICE_URL_ACTIVEPIECES - - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY - - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js - - AP_ENVIRONMENT=prod - - AP_EXECUTION_MODE=${AP_EXECUTION_MODE} - - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES - - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT - - AP_POSTGRES_DATABASE=activepieces - - AP_POSTGRES_HOST=postgres - - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - - AP_POSTGRES_PORT=5432 - - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES - - AP_REDIS_HOST=redis - - AP_REDIS_PORT=6379 - - AP_SANDBOX_RUN_TIME_SECONDS=600 - - AP_TELEMETRY_ENABLED=true - - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates" - - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 - - AP_WEBHOOK_TIMEOUT_SECONDS=30 - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_started - healthcheck: - test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] - interval: 5s - timeout: 20s - retries: 10 - postgres: - image: "nginx" - environment: - - SERVICE_FQDN_ACTIVEPIECES=/api - - POSTGRES_DB=activepieces - - PASSW=$AP_POSTGRES_PASSWORD - - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES - - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES - - POSTGRES_USER=$SERVICE_USER_POSTGRES - volumes: - - "pg-data:/var/lib/postgresql/data" - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval: 5s - timeout: 20s - retries: 10 - redis: - image: "redis:latest" - volumes: - - "redis_data:/data" - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 20s - retries: 10 +// $this->application = Application::create([ +// 'name' => 'Application for tests', +// 'docker_compose_domains' => json_encode([ +// 'app' => [ +// 'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io', +// ], +// ]), +// 'preview_url_template' => '{{pr_id}}.{{domain}}', +// 'uuid' => 'bcoowoookw0co4cok4sgc4k8s', +// 'repository_project_id' => 603035348, +// 'git_repository' => 'coollabsio/coolify-examples', +// 'git_branch' => 'main', +// 'base_directory' => '/docker-compose-test', +// 'docker_compose_location' => 'docker-compose.yml', +// 'docker_compose_raw' => $this->applicationYaml, +// 'build_pack' => 'dockercompose', +// 'ports_exposes' => '3000', +// 'environment_id' => 1, +// 'destination_id' => 0, +// 'destination_type' => StandaloneDocker::class, +// 'source_id' => 1, +// 'source_type' => GithubApp::class, +// ]); +// $this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']); +// $this->applicationPreview = ApplicationPreview::create([ +// 'git_type' => 'github', +// 'application_id' => $this->application->id, +// 'pull_request_id' => 1, +// 'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1', +// ]); +// $this->serviceYaml = ' +// services: +// activepieces: +// image: "ghcr.io/activepieces/activepieces:latest" +// environment: +// - SERVICE_FQDN_ACTIVEPIECES +// - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY +// - AP_URL=$SERVICE_URL_ACTIVEPIECES +// - AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY +// - AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js +// - AP_ENVIRONMENT=prod +// - AP_EXECUTION_MODE=${AP_EXECUTION_MODE} +// - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES +// - AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT +// - AP_POSTGRES_DATABASE=activepieces +// - AP_POSTGRES_HOST=postgres +// - AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES +// - AP_POSTGRES_PORT=5432 +// - AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES +// - AP_REDIS_HOST=redis +// - AP_REDIS_PORT=6379 +// - AP_SANDBOX_RUN_TIME_SECONDS=600 +// - AP_TELEMETRY_ENABLED=true +// - "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates" +// - AP_TRIGGER_DEFAULT_POLL_INTERVAL=5 +// - AP_WEBHOOK_TIMEOUT_SECONDS=30 +// depends_on: +// postgres: +// condition: service_healthy +// redis: +// condition: service_started +// healthcheck: +// test: ["CMD", "curl", "-f", "http://127.0.0.1:80"] +// interval: 5s +// timeout: 20s +// retries: 10 +// postgres: +// image: "nginx" +// environment: +// - SERVICE_FQDN_ACTIVEPIECES=/api +// - POSTGRES_DB=activepieces +// - PASSW=$AP_POSTGRES_PASSWORD +// - AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES +// - POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES +// - POSTGRES_USER=$SERVICE_USER_POSTGRES +// volumes: +// - "pg-data:/var/lib/postgresql/data" +// healthcheck: +// test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] +// interval: 5s +// timeout: 20s +// retries: 10 +// redis: +// image: "redis:latest" +// volumes: +// - "redis_data:/data" +// healthcheck: +// test: ["CMD", "redis-cli", "ping"] +// interval: 5s +// timeout: 20s +// retries: 10 -'; +// '; - $this->serviceComposeFileString = Yaml::parse($this->serviceYaml); +// $this->serviceComposeFileString = Yaml::parse($this->serviceYaml); - $this->service = Service::create([ - 'name' => 'Service for tests', - 'uuid' => 'tgwcg8w4s844wkog8kskw44g', - 'docker_compose_raw' => $this->serviceYaml, - 'environment_id' => 1, - 'server_id' => 0, - 'destination_id' => 0, - 'destination_type' => StandaloneDocker::class, - ]); -}); +// $this->service = Service::create([ +// 'name' => 'Service for tests', +// 'uuid' => 'tgwcg8w4s844wkog8kskw44g', +// 'docker_compose_raw' => $this->serviceYaml, +// 'environment_id' => 1, +// 'server_id' => 0, +// 'destination_id' => 0, +// 'destination_type' => StandaloneDocker::class, +// ]); +// }); -afterEach(function () { - // $this->applicationPreview->forceDelete(); - $this->application->forceDelete(); - DeleteResourceJob::dispatchSync($this->service); - $this->service->forceDelete(); -}); +// afterEach(function () { +// // $this->applicationPreview->forceDelete(); +// $this->application->forceDelete(); +// DeleteResourceJob::dispatchSync($this->service); +// $this->service->forceDelete(); +// }); -test('ServiceComposeParseNew', function () { - $output = newParser($this->service); - $this->service->saveComposeConfigs(); - expect($output)->toBeInstanceOf(Collection::class); -}); +// test('ServiceComposeParseNew', function () { +// $output = newParser($this->service); +// $this->service->saveComposeConfigs(); +// expect($output)->toBeInstanceOf(Collection::class); +// }); // test('ApplicationComposeParse', function () { // expect($this->jsonapplicationComposeFile)->toBeJson()->ray();