fix(railpack): serialize builder bootstrap before builds

This commit is contained in:
Andras Bacsai
2026-10-28 15:00:59 +01:00
parent 5de2456186
commit bc61f4860f
9 changed files with 117 additions and 7 deletions
@@ -268,11 +268,13 @@ it('stops the railpack builder after the build when no other build uses it', fun
$command = invokeRailpackMethod($job, $reflection, 'railpack_build_command', ['coollabsio/coolify:test', collect([])]);
$sharedLock = strpos($command, 'flock -s 9');
$bootstrap = strpos($command, railpackBuilderBootstrapScript());
$build = strpos($command, 'docker buildx build --builder coolify-railpack');
$stop = strpos($command, 'if flock -n -x 9; then DOCKER_CONFIG=/root/.docker docker buildx stop coolify-railpack');
expect($sharedLock)->not->toBeFalse()
->and($build)->toBeGreaterThan($sharedLock)
->and($bootstrap)->toBeGreaterThan($sharedLock)
->and($build)->toBeGreaterThan($bootstrap)
->and($stop)->toBeGreaterThan($build)
->and($command)->toEndWith('exit $status');
});
@@ -94,6 +94,7 @@ it('generates escaped railpack env args from resolved values and includes instal
it('uses preview railpack environment variables for preview deployments', function () {
$application = Mockery::mock(Application::class);
$application->shouldReceive('getAttribute')->with('install_command')->andReturn(null);
$application->shouldReceive('getAttribute')->with('settings')->andReturn((object) ['is_pr_deployments_public_enabled' => false]);
$previewValue = Mockery::mock(EnvironmentVariable::class)->makePartial();
$previewValue->forceFill([
@@ -104,12 +105,14 @@ it('uses preview railpack environment variables for preview deployments', functi
$previewValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('preview-value');
$previewQuery = Mockery::mock();
$previewQuery->shouldReceive('whereNotIn')->with('id', [])->once()->andReturnSelf();
$previewQuery->shouldReceive('withoutBuildpackControlVariables')->once()->andReturnSelf();
$previewQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf();
$previewQuery->shouldReceive('get')->once()->andReturn(collect([]));
$application->shouldReceive('environment_variables_preview')->once()->andReturn($previewQuery);
$railpackPreviewQuery = Mockery::mock();
$railpackPreviewQuery->shouldReceive('whereNotIn')->with('id', [])->once()->andReturnSelf();
$railpackPreviewQuery->shouldReceive('get')->once()->andReturn(collect([$previewValue]));
$application->shouldReceive('railpack_environment_variables_preview')->once()->andReturn($railpackPreviewQuery);
@@ -885,3 +885,14 @@ test('railpack builder helper command gets sudo only outside the helper script',
." exit \$status' 2>/dev/null || sudo true"
);
});
test('railpack builder bootstrap script gets no sudo inside the helper script', function () {
$command = railpackBuilderHelperCommand('$HOME/.docker/buildx', 'coolify-helper:1.0.0', railpackBuilderBootstrapScript().'; docker buildx prune --builder coolify-railpack -af');
$parsed = parseCommandsByLineForSudo(collect([$command]), $this->server)[0];
expect($parsed)
->toStartWith('sudo docker run --rm ')
->toContain(railpackBuilderBootstrapScript().'; docker buildx prune --builder coolify-railpack -af; status=$?;')
->toEndWith("exit \$status' 2>/dev/null || sudo true");
});
+29
View File
@@ -41,3 +41,32 @@ test('a held shared lock prevents stopping the builder', function () {
expect($process->getExitCode())->toBe(0);
expect($process->getOutput())->toBe('built');
});
test('concurrent builds bootstrap the builder one at a time', function () {
$directory = sys_get_temp_dir().'/railpack-bootstrap-'.bin2hex(random_bytes(8));
mkdir($directory);
try {
// The fake bootstrap takes time, like buildkitd that starts but does not listen yet.
$setup = "docker() { if [ \"\$2\" = inspect ]; then echo start >> {$directory}/log; sleep 0.3; echo end >> {$directory}/log; fi; }; ";
$script = str_replace(
['/root/.docker/buildx/coolify-railpack.lock', '/root/.docker/buildx/coolify-railpack-bootstrap.lock'],
[$directory.'/builder.lock', $directory.'/bootstrap.lock'],
$setup.railpackBuilderLockedScript(railpackBuilderBootstrapScript().'; true'),
);
$builds = [new Process(['bash', '-c', $script]), new Process(['bash', '-c', $script])];
foreach ($builds as $build) {
$build->start();
}
foreach ($builds as $build) {
$build->wait();
expect($build->getExitCode())->toBe(0);
}
expect(file($directory.'/log', FILE_IGNORE_NEW_LINES))->toBe(['start', 'end', 'start', 'end']);
} finally {
array_map('unlink', glob($directory.'/*'));
rmdir($directory);
}
});