fix: improve Docker image digest handling and add auto-parse feature

- Replace manual regex parsing with DockerImageParser in ApplicationsController
- Fix double-decoration bug where image names like nginx@sha256:hash would
  become nginx:hash@sha256 causing malformed references
- Add auto-parse feature in Livewire DockerImage component
- Users can now paste complete references like nginx:stable@sha256:abc123...
  and fields auto-populate
- Update UI placeholder with examples: nginx, docker.io/nginx:latest,
  ghcr.io/user/app:v1.2.3, nginx:stable@sha256:abc123...
- Add comprehensive unit tests for auto-parse functionality
- All tests passing (20 tests, 73 assertions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2025-10-15 10:19:01 +02:00
parent 6d3c996ef3
commit 20b4288916
5 changed files with 236 additions and 27 deletions

View File

@@ -17,6 +17,7 @@ use App\Models\Server;
use App\Models\Service;
use App\Rules\ValidGitBranch;
use App\Rules\ValidGitRepositoryUrl;
use App\Services\DockerImageParser;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use OpenApi\Attributes as OA;
@@ -1512,31 +1513,32 @@ class ApplicationsController extends Controller
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
// Process docker image name and tag for SHA256 digests
// Process docker image name and tag using DockerImageParser
$dockerImageName = $request->docker_registry_image_name;
$dockerImageTag = $request->docker_registry_image_tag;
// Strip 'sha256:' prefix if user provided it in the tag
// Build the full Docker image string for parsing
if ($dockerImageTag) {
$dockerImageTag = preg_replace('/^sha256:/i', '', trim($dockerImageTag));
$dockerImageString = $dockerImageName.':'.$dockerImageTag;
} else {
$dockerImageString = $dockerImageName;
}
// Remove @sha256 from image name if user added it
if ($dockerImageName) {
$dockerImageName = preg_replace('/@sha256$/i', '', trim($dockerImageName));
}
// Parse using DockerImageParser to normalize the image reference
$parser = new DockerImageParser;
$parser->parse($dockerImageString);
// Check if tag is a valid SHA256 hash (64 hex characters)
$isSha256Hash = $dockerImageTag && preg_match('/^[a-f0-9]{64}$/i', $dockerImageTag);
// Get normalized image name and tag
$normalizedImageName = $parser->getFullImageNameWithoutTag();
// Append @sha256 to image name if using digest and not already present
if ($isSha256Hash && ! str_ends_with($dockerImageName, '@sha256')) {
$dockerImageName .= '@sha256';
// Append @sha256 to image name if using digest
if ($parser->isImageHash() && ! str_ends_with($normalizedImageName, '@sha256')) {
$normalizedImageName .= '@sha256';
}
// Set processed values back to request
$request->offsetSet('docker_registry_image_name', $dockerImageName);
$request->offsetSet('docker_registry_image_tag', $dockerImageTag ?: 'latest');
$request->offsetSet('docker_registry_image_name', $normalizedImageName);
$request->offsetSet('docker_registry_image_tag', $parser->getTag());
$application = new Application;
removeUnnecessaryFieldsFromRequest($request);