From 004d3582563afb7209272a7fc7850a939b69c5ce Mon Sep 17 00:00:00 2001 From: ItsDeadlyProgrammer Date: Fri, 23 Oct 2026 18:57:25 +0530 Subject: [PATCH] fix: keep the repository path when an image digest replaces the tag ReplaceImageTagWithDigest cut the image at its first colon, so a registry with a port lost its repository path: registry.local:5000/team/app:v1 became registry.local@sha256:.... Look for the tag or digest only in the last path segment. Signed-off-by: ItsDeadlyProgrammer --- .../devtronApps/preStageHandlerCode.go | 8 +++- .../devtronApps/preStageHandlerCode_test.go | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 pkg/deployment/trigger/devtronApps/preStageHandlerCode_test.go diff --git a/pkg/deployment/trigger/devtronApps/preStageHandlerCode.go b/pkg/deployment/trigger/devtronApps/preStageHandlerCode.go index 8ca77e9e66..71b2d1448a 100644 --- a/pkg/deployment/trigger/devtronApps/preStageHandlerCode.go +++ b/pkg/deployment/trigger/devtronApps/preStageHandlerCode.go @@ -1092,8 +1092,12 @@ func (impl *HandlerServiceImpl) buildDefaultArtifactLocation(savedWf *pipelineCo } func ReplaceImageTagWithDigest(image, digest string) string { - imageWithoutTag := strings.Split(image, ":")[0] - imageWithDigest := fmt.Sprintf("%s@%s", imageWithoutTag, digest) + // the registry host can carry a port (registry.local:5000/app:v1), so a tag or a digest can only start inside the last path segment + nameStart := strings.LastIndex(image, "/") + 1 + if end := strings.IndexAny(image[nameStart:], ":@"); end != -1 { + image = image[:nameStart+end] + } + imageWithDigest := fmt.Sprintf("%s@%s", image, digest) return imageWithDigest } diff --git a/pkg/deployment/trigger/devtronApps/preStageHandlerCode_test.go b/pkg/deployment/trigger/devtronApps/preStageHandlerCode_test.go new file mode 100644 index 0000000000..1cc810b0c1 --- /dev/null +++ b/pkg/deployment/trigger/devtronApps/preStageHandlerCode_test.go @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024. Devtron Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package devtronApps + +import "testing" + +func TestReplaceImageTagWithDigest(t *testing.T) { + const digest = "sha256:0123456789abcdef" + tests := []struct { + name string + image string + want string + }{ + {"tag", "nginx:1.25", "nginx@" + digest}, + {"repository path and tag", "team/app:v1", "team/app@" + digest}, + {"registry and tag", "registry.example.com/team/app:v1", "registry.example.com/team/app@" + digest}, + {"registry with port and tag", "registry.local:5000/team/app:v1", "registry.local:5000/team/app@" + digest}, + {"registry with port and no tag", "registry.local:5000/team/app", "registry.local:5000/team/app@" + digest}, + {"no tag", "team/app", "team/app@" + digest}, + {"tag and old digest", "registry.local:5000/team/app:v1@sha256:fedcba", "registry.local:5000/team/app@" + digest}, + {"old digest only", "registry.local:5000/team/app@sha256:fedcba", "registry.local:5000/team/app@" + digest}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ReplaceImageTagWithDigest(tt.image, digest); got != tt.want { + t.Errorf("ReplaceImageTagWithDigest(%q) = %q, want %q", tt.image, got, tt.want) + } + }) + } +}