mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Co-authored-by: Aditya Tripathi <aditya@climactic.co> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
5.0 KiB
PHP
121 lines
5.0 KiB
PHP
<?php
|
|
|
|
use App\Models\Application;
|
|
use App\Models\ApplicationSetting;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
/**
|
|
* Runs the generated import command in bash with a stub git that records its calls.
|
|
* The stub prints $lfsFiles for `git lfs ls-files -n`.
|
|
*
|
|
* @return array{0: int, 1: list<string>, 2: list<string>}
|
|
*/
|
|
function runImportCommandWithStubGit(string $lfsFiles, bool $hasSubmodules = false): array
|
|
{
|
|
$dir = sys_get_temp_dir().'/coolify-import-'.bin2hex(random_bytes(4));
|
|
mkdir("{$dir}/bin", 0755, true);
|
|
file_put_contents("{$dir}/bin/git", <<<SH
|
|
#!/bin/sh
|
|
echo "\$*" >> {$dir}/git.log
|
|
if [ "\$*" = "lfs ls-files -n" ]; then printf '%s' '{$lfsFiles}'; fi
|
|
SH);
|
|
chmod("{$dir}/bin/git", 0755);
|
|
if ($hasSubmodules) {
|
|
touch("{$dir}/.gitmodules");
|
|
}
|
|
|
|
$application = new Application;
|
|
$application->forceFill(['uuid' => 'test-app-uuid', 'git_commit_sha' => 'abc123']);
|
|
$settings = new ApplicationSetting;
|
|
$settings->is_git_shallow_clone_enabled = true;
|
|
$settings->is_git_submodules_enabled = true;
|
|
$settings->is_git_lfs_enabled = true;
|
|
$application->setRelation('settings', $settings);
|
|
|
|
$command = $application->setGitImportSettings(
|
|
deployment_uuid: 'test-uuid',
|
|
git_clone_command: 'true',
|
|
gitSshCommand: 'ssh -o ConnectTimeout=30 -i /root/.ssh/id_rsa',
|
|
baseDir: $dir,
|
|
);
|
|
|
|
$process = Process::fromShellCommandline($command, env: ['PATH' => "{$dir}/bin:".getenv('PATH')]);
|
|
$process->run();
|
|
$calls = file_exists("{$dir}/git.log") ? file("{$dir}/git.log", FILE_IGNORE_NEW_LINES) : [];
|
|
|
|
exec('rm -rf '.escapeshellarg($dir));
|
|
|
|
return [$process->getExitCode(), $calls, explode("\n", trim($process->getOutput()))];
|
|
}
|
|
|
|
test('git lfs pull is skipped when the checkout has no LFS files', function () {
|
|
[$exitCode, $calls, $output] = runImportCommandWithStubGit('');
|
|
|
|
expect($exitCode)->toBe(0)
|
|
->and($calls)->toBe([
|
|
'fetch --depth=1 origin abc123',
|
|
'-c advice.detachedHead=false checkout abc123',
|
|
'lfs ls-files -n',
|
|
])
|
|
->and($output)->toBe(['Fetching the commit.']);
|
|
});
|
|
|
|
test('each network step after the clone prints a log line before it runs', function () {
|
|
[$exitCode, $calls, $output] = runImportCommandWithStubGit('assets/logo.psd', hasSubmodules: true);
|
|
|
|
expect($exitCode)->toBe(0)
|
|
->and($calls)->toBe([
|
|
'fetch --depth=1 origin abc123',
|
|
'-c advice.detachedHead=false checkout abc123',
|
|
'submodule sync',
|
|
'submodule update --init --recursive --depth=1',
|
|
'lfs ls-files -n',
|
|
'lfs pull',
|
|
])
|
|
->and($output)->toBe(['Fetching the commit.', 'Updating submodules.', 'Downloading Git LFS objects.']);
|
|
});
|
|
|
|
test('git ssh command stops a stalled connection instead of waiting for the deployment timeout', function () {
|
|
expect(gitSshCommand(2222, '/root/.ssh/key'))->toBe(
|
|
'ssh -o ConnectTimeout=30 -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -p 2222 -o Port=2222 -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /root/.ssh/key -o IdentitiesOnly=yes'
|
|
)
|
|
->and(gitSshCommand(22))->not->toContain(' -i ')
|
|
->and(gitSshCommand(22, '/root/.ssh/id_rsa', identitiesOnly: false))->toEndWith('-i /root/.ssh/id_rsa');
|
|
});
|
|
|
|
test('a shallow clone checks out a short commit sha', function (string $commitName) {
|
|
$dir = sys_get_temp_dir().'/coolify-short-sha-'.bin2hex(random_bytes(4));
|
|
$git = 'git -c user.email=test@example.com -c user.name=test';
|
|
exec("git init -q -b main {$dir}/source && cd {$dir}/source && {$git} commit -q --allow-empty -m main"
|
|
." && git checkout -q -b feature && {$git} commit -q --allow-empty -m first && {$git} commit -q --allow-empty -m second"
|
|
." && git checkout -q main && git clone -q --bare {$dir}/source {$dir}/remote.git");
|
|
$commit = trim(shell_exec("git -C {$dir}/source rev-parse {$commitName}"));
|
|
|
|
$application = new Application;
|
|
$application->forceFill(['uuid' => 'test-app-uuid']);
|
|
$settings = new ApplicationSetting;
|
|
$settings->is_git_shallow_clone_enabled = true;
|
|
$settings->is_git_submodules_enabled = false;
|
|
$settings->is_git_lfs_enabled = false;
|
|
$application->setRelation('settings', $settings);
|
|
|
|
// Bitbucket sends the pull request commit as a 12-character hash.
|
|
$command = $application->setGitImportSettings(
|
|
deployment_uuid: 'test-uuid',
|
|
git_clone_command: "git clone -q --depth=1 -b main file://{$dir}/remote.git {$dir}/checkout",
|
|
commit: substr($commit, 0, 12),
|
|
baseDir: "{$dir}/checkout",
|
|
);
|
|
$process = Process::fromShellCommandline($command);
|
|
$process->run();
|
|
$checkedOutCommit = trim((string) shell_exec("git -C {$dir}/checkout rev-parse HEAD 2>/dev/null"));
|
|
|
|
exec('rm -rf '.escapeshellarg($dir));
|
|
|
|
expect($process->getExitCode())->toBe(0, $process->getErrorOutput())
|
|
->and($checkedOutCommit)->toBe($commit);
|
|
})->with([
|
|
'tip of a branch' => ['feature'],
|
|
'older commit of a branch' => ['feature~1'],
|
|
]);
|