mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 09:34:36 +08:00
fix(clone): replace the clone suffix instead of adding one for each clone
Cloning a clone added another "-clone-<id>" to the name, so after about seven clones the name was longer than 255 characters and the insert failed. Closes #9976
This commit is contained in:
1 parent
1440ea83d3
commit
bb1fada600
9 files changed
+58
-5
No files matched your search
@@ -34,6 +34,7 @@
|
||||
- Call `visit()` directly in each `tests/v4/Browser` test body; Pest does not mark a test that only uses helper-wrapped `visit()` as a browser test, so it fails with `sendText() on null`.
|
||||
- The root team (id 0) has `personal_team = true`, so `personal_team` does not identify a user's own team. To clean up test users, detach them from shared teams and then delete the user; never delete a team selected by a filter. A Team delete revokes the team's API tokens and deletes its private keys before any guard can stop it.
|
||||
- SQLite returns `0`/`1` for boolean columns without a `boolean` cast, while Postgres returns real booleans. A strict `=== false` check on such a column never matches in tests, so tests can pass on behavior that Postgres blocks. Add the cast or compare loosely.
|
||||
- SQLite does not enforce `varchar(255)` lengths, while Postgres rejects a longer value (`SQLSTATE[22001]`). Tests cannot show a too-long name, so assert the length or the exact value of generated strings.
|
||||
|
||||
- The Compose parsers (`parse()`, `applicationParser`, `serviceParser`) run on page mounts, refreshes, and deployments. Do not dispatch jobs or run server commands from them; let the caller that changes data start the work. Code that only needs the service names reads `docker_compose_raw` (`Application::composeServicesWithoutParse()`).
|
||||
|
||||
|
||||
@@ -5282,7 +5282,7 @@ class DatabasesController extends Controller
|
||||
$uuid = new_public_id();
|
||||
$name = $request->filled('name')
|
||||
? $request->string('name')->toString()
|
||||
: $database->name.'-clone-'.$uuid;
|
||||
: clone_resource_name($database->name, $uuid);
|
||||
$cloneVolumeData = $request->boolean('clone_volumes', false);
|
||||
|
||||
$newDatabase = $database->replicate([
|
||||
|
||||
@@ -3337,7 +3337,7 @@ class ServicesController extends Controller
|
||||
$uuid = new_public_id();
|
||||
$name = $request->filled('name')
|
||||
? $request->string('name')->toString()
|
||||
: $service->name.'-clone-'.$uuid;
|
||||
: clone_resource_name($service->name, $uuid);
|
||||
$cloneVolumeData = $request->boolean('clone_volumes', false);
|
||||
|
||||
$newService = clone_service($service, $destination, [
|
||||
|
||||
@@ -63,7 +63,7 @@ class CloneMe extends Component
|
||||
->where('team_id', $this->project->team_id)
|
||||
->get()
|
||||
->reject(fn ($server) => $server->isBuildServer());
|
||||
$this->newName = str($this->project->name.'-clone-'.new_public_id())->slug();
|
||||
$this->newName = str(clone_resource_name($this->project->name, new_public_id()))->slug();
|
||||
}
|
||||
|
||||
public function toggleVolumeCloning(bool $value)
|
||||
|
||||
@@ -132,7 +132,7 @@ class ResourceOperations extends Component
|
||||
'updated_at',
|
||||
])->fill([
|
||||
'uuid' => $uuid,
|
||||
'name' => $this->resource->name.'-clone-'.$uuid,
|
||||
'name' => clone_resource_name($this->resource->name, $uuid),
|
||||
'status' => 'exited',
|
||||
'started_at' => null,
|
||||
'environment_id' => $new_environment->id,
|
||||
|
||||
@@ -695,7 +695,7 @@ function clone_service(Service $source, StandaloneDocker|SwarmDocker $destinatio
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])->fill([
|
||||
'name' => $source->name.'-clone-'.$uuid,
|
||||
'name' => clone_resource_name($source->name, $uuid),
|
||||
...$overrides,
|
||||
'uuid' => $uuid,
|
||||
'destination_id' => $destination->id,
|
||||
|
||||
@@ -134,6 +134,18 @@ function new_public_id(int $length = 24): string
|
||||
return Str::lower(Str::random($length));
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of a clone: the source name without the "-clone-<id>" suffixes of earlier clones, then a new suffix.
|
||||
* Without this, each clone of a clone adds a suffix, and the name soon becomes longer than the 255 characters of the column.
|
||||
*/
|
||||
function clone_resource_name(string $name, string $uuid): string
|
||||
{
|
||||
$suffix = '-clone-'.$uuid;
|
||||
$baseName = preg_replace('/(-clone-[a-z0-9]{7,})+$/i', '', $name);
|
||||
|
||||
return Str::limit($baseName, 255 - strlen($suffix), '').$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a path or identifier is safe for use in shell commands.
|
||||
*
|
||||
|
||||
@@ -179,3 +179,18 @@ test('cloning a service without volume data dispatches no volume copy', function
|
||||
Bus::assertNotDispatched(VolumeCloneJob::class);
|
||||
expect(serviceVolumeNames($clone))->toHaveCount(2);
|
||||
});
|
||||
|
||||
test('cloning a cloned service replaces the clone suffix instead of adding another one', function () {
|
||||
$this->service->update(['name' => 'Gitea Runner']);
|
||||
|
||||
$source = $this->service;
|
||||
foreach (range(1, 10) as $ignored) {
|
||||
Livewire::test(ResourceOperations::class, ['resource' => $source])
|
||||
->call('cloneTo', $this->destination->uuid)
|
||||
->assertHasNoErrors();
|
||||
|
||||
$source = Service::query()->latest('id')->firstOrFail();
|
||||
}
|
||||
|
||||
expect($source->name)->toBe("Gitea Runner-clone-{$source->uuid}");
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
it('adds a clone suffix to the source name', function () {
|
||||
expect(clone_resource_name('Gitea Runner', 'abc123def456ghi789jkl012'))
|
||||
->toBe('Gitea Runner-clone-abc123def456ghi789jkl012');
|
||||
});
|
||||
|
||||
it('replaces the clone suffixes of earlier clones', function () {
|
||||
$name = 'Gitea Runner-clone-sy2cojnseswr8szrpj1omsgw-clone-yvj9kdc2xuff8td0f0mps7eq-clone-ab12cd3';
|
||||
|
||||
expect(clone_resource_name($name, 'newid000000000000000000a'))
|
||||
->toBe('Gitea Runner-clone-newid000000000000000000a');
|
||||
});
|
||||
|
||||
it('keeps a name that only contains the word clone', function () {
|
||||
expect(clone_resource_name('my-clone-db', 'abc123def456ghi789jkl012'))
|
||||
->toBe('my-clone-db-clone-abc123def456ghi789jkl012');
|
||||
});
|
||||
|
||||
it('keeps the clone name within the 255 characters of the name column', function () {
|
||||
$name = clone_resource_name(str_repeat('a', 255), 'abc123def456ghi789jkl012');
|
||||
|
||||
expect($name)->toHaveLength(255)
|
||||
->toEndWith('-clone-abc123def456ghi789jkl012');
|
||||
});
|
||||
Reference in new issue
Block a user