From 41e89c48a2dd75b1226c765dd77b7dba60910684 Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:26:22 +0200 Subject: [PATCH 01/11] feat(app push): add --image-tag flag to appPushConfig --- internal/cmd/app/push.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index bf0d0fa35..6bd49817a 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -43,6 +43,7 @@ type appPushConfig struct { mountServiceBindingSecrets types.ServiceBindingSecretArray quiet bool insecure bool + imageTag string } func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { @@ -110,6 +111,7 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { flags.MarkExactlyOneRequired("image", "dockerfile", "code-path"), flags.MarkExclusive("dockerfile-context", "image", "code-path"), flags.MarkExclusive("dockerfile-build-arg", "image", "code-path"), + flags.MarkExclusive("image-tag", "image"), flags.MarkPrerequisites("expose", "container-port"), flags.MarkPrerequisites("image-pull-secret", "image"), )) @@ -132,6 +134,7 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { // image flags cmd.Flags().StringVar(&config.image, "image", "", "Name of the image to deploy") cmd.Flags().StringVar(&config.imagePullSecretName, "image-pull-secret", "", "Name of the Kubernetes Secret with credentials to pull the image") + cmd.Flags().StringVar(&config.imageTag, "image-tag", "", "Custom tag for the built image (e.g. a Git commit SHA). Applies only to --code-path and --dockerfile builds.") // dockerfile flags cmd.Flags().StringVar(&config.dockerfilePath, "dockerfile", "", "Path to the Dockerfile") From a34b858c0e55c323aba729e2924200826c24fb27 Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:28:45 +0200 Subject: [PATCH 02/11] refactor(app push): move imageTag field next to imagePullSecretName in struct --- internal/cmd/app/push.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 6bd49817a..71a38e759 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -27,6 +27,7 @@ type appPushConfig struct { namespace string image string imagePullSecretName string + imageTag string dockerfilePath string dockerfileSrcContext string dockerfileArgs types.Map @@ -43,7 +44,6 @@ type appPushConfig struct { mountServiceBindingSecrets types.ServiceBindingSecretArray quiet bool insecure bool - imageTag string } func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { From f3cbb68ceba83ea89b99f47395f29d8aa21dc9ca Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:31:26 +0200 Subject: [PATCH 03/11] feat(app push): validate --image-tag format in complete() --- internal/cmd/app/push.go | 13 +++++ internal/cmd/app/push_test.go | 95 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 internal/cmd/app/push_test.go diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 71a38e759..40dd1cccb 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -3,6 +3,7 @@ package app import ( "fmt" "os" + "regexp" "time" "github.com/kyma-project/cli.v3/internal/clierror" @@ -181,6 +182,18 @@ func (apc *appPushConfig) complete() clierror.Error { } } + if apc.imageTag != "" { + validTag := regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_.\-]{0,127}$`) + if !validTag.MatchString(apc.imageTag) { + return clierror.New( + fmt.Sprintf("invalid image tag %q", apc.imageTag), + "tag must start with a letter, digit, or underscore", + "tag may only contain letters, digits, underscores, dots, and hyphens", + "tag must be at most 128 characters", + ) + } + } + return nil } diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go new file mode 100644 index 000000000..481f28be6 --- /dev/null +++ b/internal/cmd/app/push_test.go @@ -0,0 +1,95 @@ +package app + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_appPushConfig_complete_imageTag(t *testing.T) { + tests := []struct { + name string + imageTag string + wantError bool + }{ + { + name: "empty tag uses timestamp fallback - no error", + imageTag: "", + wantError: false, + }, + { + name: "valid commit sha", + imageTag: "abc1234def5678", + wantError: false, + }, + { + name: "valid semver", + imageTag: "1.0.0", + wantError: false, + }, + { + name: "valid underscore prefix", + imageTag: "_build", + wantError: false, + }, + { + name: "valid single char", + imageTag: "1", + wantError: false, + }, + { + name: "valid with dots and dashes", + imageTag: "v1.2.3-beta.1", + wantError: false, + }, + { + name: "invalid: contains space", + imageTag: "my tag", + wantError: true, + }, + { + name: "invalid: contains colon", + imageTag: "my:tag", + wantError: true, + }, + { + name: "invalid: contains slash", + imageTag: "my/tag", + wantError: true, + }, + { + name: "invalid: contains @", + imageTag: "my@tag", + wantError: true, + }, + { + name: "invalid: starts with dot", + imageTag: ".mytag", + wantError: true, + }, + { + name: "invalid: starts with dash", + imageTag: "-mytag", + wantError: true, + }, + { + name: "invalid: exceeds 128 chars", + imageTag: "a123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := &appPushConfig{ + imageTag: tt.imageTag, + } + err := cfg.complete() + if tt.wantError { + require.NotNil(t, err, "expected validation error for imageTag=%q", tt.imageTag) + } else { + require.Nil(t, err, "expected no error for imageTag=%q", tt.imageTag) + } + }) + } +} From d24417fe577851049daca67fe721e0fbc8fd64f9 Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:43:28 +0200 Subject: [PATCH 04/11] fix(app push): hoist imageTagRegexp to package level, fix test conventions --- internal/cmd/app/push.go | 5 +- internal/cmd/app/push_test.go | 86 +++++++++++++++++------------------ 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 40dd1cccb..43e34ca72 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -21,6 +21,8 @@ import ( "github.com/spf13/cobra" ) +var imageTagRegexp = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$`) + type appPushConfig struct { *cmdcommon.KymaConfig @@ -183,8 +185,7 @@ func (apc *appPushConfig) complete() clierror.Error { } if apc.imageTag != "" { - validTag := regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_.\-]{0,127}$`) - if !validTag.MatchString(apc.imageTag) { + if !imageTagRegexp.MatchString(apc.imageTag) { return clierror.New( fmt.Sprintf("invalid image tag %q", apc.imageTag), "tag must start with a letter, digit, or underscore", diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index 481f28be6..85ae7bcc7 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -8,74 +8,74 @@ import ( func Test_appPushConfig_complete_imageTag(t *testing.T) { tests := []struct { - name string - imageTag string - wantError bool + name string + imageTag string + wantErr bool }{ { - name: "empty tag uses timestamp fallback - no error", - imageTag: "", - wantError: false, + name: "empty tag skips validation - no error", + imageTag: "", + wantErr: false, }, { - name: "valid commit sha", - imageTag: "abc1234def5678", - wantError: false, + name: "valid commit sha", + imageTag: "abc1234def5678", + wantErr: false, }, { - name: "valid semver", - imageTag: "1.0.0", - wantError: false, + name: "valid semver", + imageTag: "1.0.0", + wantErr: false, }, { - name: "valid underscore prefix", - imageTag: "_build", - wantError: false, + name: "valid underscore prefix", + imageTag: "_build", + wantErr: false, }, { - name: "valid single char", - imageTag: "1", - wantError: false, + name: "valid single char", + imageTag: "1", + wantErr: false, }, { - name: "valid with dots and dashes", - imageTag: "v1.2.3-beta.1", - wantError: false, + name: "valid with dots and dashes", + imageTag: "v1.2.3-beta.1", + wantErr: false, }, { - name: "invalid: contains space", - imageTag: "my tag", - wantError: true, + name: "invalid: contains space", + imageTag: "my tag", + wantErr: true, }, { - name: "invalid: contains colon", - imageTag: "my:tag", - wantError: true, + name: "invalid: contains colon", + imageTag: "my:tag", + wantErr: true, }, { - name: "invalid: contains slash", - imageTag: "my/tag", - wantError: true, + name: "invalid: contains slash", + imageTag: "my/tag", + wantErr: true, }, { - name: "invalid: contains @", - imageTag: "my@tag", - wantError: true, + name: "invalid: contains @", + imageTag: "my@tag", + wantErr: true, }, { - name: "invalid: starts with dot", - imageTag: ".mytag", - wantError: true, + name: "invalid: starts with dot", + imageTag: ".mytag", + wantErr: true, }, { - name: "invalid: starts with dash", - imageTag: "-mytag", - wantError: true, + name: "invalid: starts with dash", + imageTag: "-mytag", + wantErr: true, }, { - name: "invalid: exceeds 128 chars", - imageTag: "a123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", - wantError: true, + name: "invalid: exceeds 128 chars", + imageTag: "abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + wantErr: true, }, } @@ -85,7 +85,7 @@ func Test_appPushConfig_complete_imageTag(t *testing.T) { imageTag: tt.imageTag, } err := cfg.complete() - if tt.wantError { + if tt.wantErr { require.NotNil(t, err, "expected validation error for imageTag=%q", tt.imageTag) } else { require.Nil(t, err, "expected no error for imageTag=%q", tt.imageTag) From 6bea28673be6f239e635290deff94f7a96053b94 Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:49:50 +0200 Subject: [PATCH 05/11] fix(app push): correct exceeds-128-chars test string to exactly 129 chars --- internal/cmd/app/push_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index 85ae7bcc7..eb1740e30 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -74,7 +74,7 @@ func Test_appPushConfig_complete_imageTag(t *testing.T) { }, { name: "invalid: exceeds 128 chars", - imageTag: "abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + imageTag: "abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", wantErr: true, }, } From 47bf0049920f2f75813bec4b882de9887c4644eb Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:52:34 +0200 Subject: [PATCH 06/11] feat(app push): wire --image-tag through buildImage() via resolveImageTag helper --- internal/cmd/app/push.go | 15 +++++++++++---- internal/cmd/app/push_test.go | 13 +++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 43e34ca72..1c6106672 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -332,7 +332,7 @@ func createDeployment(cfg *appPushConfig, client kube.Client, image, imagePullSe func buildAndImportImage(client kube.Client, cfg *appPushConfig, registryConfig *registry.InternalRegistryConfig) (string, clierror.Error) { out.Msgln("Building image\n") - imageName, err := buildImage(cfg) + imageName, err := buildImage(cfg, cfg.imageTag) if err != nil { return "", clierror.Wrap(err, clierror.New("failed to build image from Dockerfile")) } @@ -369,9 +369,16 @@ func buildAndImportImage(client kube.Client, cfg *appPushConfig, registryConfig return pushedImage, nil } -func buildImage(cfg *appPushConfig) (string, error) { - imageTag := time.Now().Format("2006-01-02_15-04-05") - imageName := fmt.Sprintf("%s:%s", cfg.name, imageTag) +// resolveImageTag returns imageTag if non-empty, otherwise a timestamp-based tag. +func resolveImageTag(imageTag string) string { + if imageTag != "" { + return imageTag + } + return time.Now().Format("2006-01-02_15-04-05") +} + +func buildImage(cfg *appPushConfig, imageTag string) (string, error) { + imageName := fmt.Sprintf("%s:%s", cfg.name, resolveImageTag(imageTag)) var err error if cfg.packAppPath != "" { diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index eb1740e30..8b26e6488 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -93,3 +93,16 @@ func Test_appPushConfig_complete_imageTag(t *testing.T) { }) } } + +func Test_buildImage_tagSelection(t *testing.T) { + t.Run("provided tag is used in image name", func(t *testing.T) { + imageTag := "abc1234" + resolvedTag := resolveImageTag(imageTag) + require.Equal(t, "abc1234", resolvedTag) + }) + + t.Run("empty tag resolves to timestamp format", func(t *testing.T) { + resolvedTag := resolveImageTag("") + require.Regexp(t, `^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$`, resolvedTag) + }) +} From c126464554b6816212bc7e26d50eab8889bb061d Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Wed, 6 May 2026 17:54:36 +0200 Subject: [PATCH 07/11] fix(app push): remove redundant imageTag param, rename test, fix error message --- internal/cmd/app/push.go | 8 ++++---- internal/cmd/app/push_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 1c6106672..69f719405 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -332,9 +332,9 @@ func createDeployment(cfg *appPushConfig, client kube.Client, image, imagePullSe func buildAndImportImage(client kube.Client, cfg *appPushConfig, registryConfig *registry.InternalRegistryConfig) (string, clierror.Error) { out.Msgln("Building image\n") - imageName, err := buildImage(cfg, cfg.imageTag) + imageName, err := buildImage(cfg) if err != nil { - return "", clierror.Wrap(err, clierror.New("failed to build image from Dockerfile")) + return "", clierror.Wrap(err, clierror.New("failed to build image")) } pushFunc := registry.NewPushWithPortforwardFunc( @@ -377,8 +377,8 @@ func resolveImageTag(imageTag string) string { return time.Now().Format("2006-01-02_15-04-05") } -func buildImage(cfg *appPushConfig, imageTag string) (string, error) { - imageName := fmt.Sprintf("%s:%s", cfg.name, resolveImageTag(imageTag)) +func buildImage(cfg *appPushConfig) (string, error) { + imageName := fmt.Sprintf("%s:%s", cfg.name, resolveImageTag(cfg.imageTag)) var err error if cfg.packAppPath != "" { diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index 8b26e6488..51ddb0e7a 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -94,7 +94,7 @@ func Test_appPushConfig_complete_imageTag(t *testing.T) { } } -func Test_buildImage_tagSelection(t *testing.T) { +func Test_resolveImageTag(t *testing.T) { t.Run("provided tag is used in image name", func(t *testing.T) { imageTag := "abc1234" resolvedTag := resolveImageTag(imageTag) From 897abe6d4d8312067764baf33edbf3a613fe7dcd Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Thu, 7 May 2026 06:55:58 +0200 Subject: [PATCH 08/11] docs(app push): add --image-tag usage examples to help text --- internal/cmd/app/push.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 69f719405..4df2c495a 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -63,6 +63,10 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { # The application will be built using Cloud Native Buildpacks: kyma app push --name my-app --code-path . + # Push with a custom image tag (e.g. a Git commit SHA for CI/CD traceability): + kyma app push --name my-app --code-path . --image-tag abc1234 + kyma app push --name my-app --dockerfile ./Dockerfile --image-tag $GITHUB_SHA + # Push an application based on a Dockerfile located in the current directory: kyma app push --name my-app --dockerfile ./Dockerfile --dockerfile-context . From a7ae9769a51fa3249a15be397034cf98204dca6a Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Thu, 7 May 2026 07:46:51 +0200 Subject: [PATCH 09/11] fix(app push): fix gofmt alignment in push_test.go --- internal/cmd/app/push_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index 51ddb0e7a..64543f3e4 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -8,9 +8,9 @@ import ( func Test_appPushConfig_complete_imageTag(t *testing.T) { tests := []struct { - name string + name string imageTag string - wantErr bool + wantErr bool }{ { name: "empty tag skips validation - no error", From c4fb5252ba7443453f5505f6b8322b2293f6f8d3 Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Thu, 7 May 2026 07:57:49 +0200 Subject: [PATCH 10/11] refactor(app push): rename --image-tag to --build-tag for clarity --- internal/cmd/app/push.go | 20 +++++++++---------- internal/cmd/app/push_test.go | 36 +++++++++++++++++------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/internal/cmd/app/push.go b/internal/cmd/app/push.go index 4df2c495a..3b96bbffa 100644 --- a/internal/cmd/app/push.go +++ b/internal/cmd/app/push.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" ) -var imageTagRegexp = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$`) +var buildTagRegexp = regexp.MustCompile(`^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$`) type appPushConfig struct { *cmdcommon.KymaConfig @@ -30,7 +30,7 @@ type appPushConfig struct { namespace string image string imagePullSecretName string - imageTag string + buildTag string dockerfilePath string dockerfileSrcContext string dockerfileArgs types.Map @@ -64,8 +64,8 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { kyma app push --name my-app --code-path . # Push with a custom image tag (e.g. a Git commit SHA for CI/CD traceability): - kyma app push --name my-app --code-path . --image-tag abc1234 - kyma app push --name my-app --dockerfile ./Dockerfile --image-tag $GITHUB_SHA + kyma app push --name my-app --code-path . --build-tag abc1234 + kyma app push --name my-app --dockerfile ./Dockerfile --build-tag $GITHUB_SHA # Push an application based on a Dockerfile located in the current directory: kyma app push --name my-app --dockerfile ./Dockerfile --dockerfile-context . @@ -118,7 +118,7 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { flags.MarkExactlyOneRequired("image", "dockerfile", "code-path"), flags.MarkExclusive("dockerfile-context", "image", "code-path"), flags.MarkExclusive("dockerfile-build-arg", "image", "code-path"), - flags.MarkExclusive("image-tag", "image"), + flags.MarkExclusive("build-tag", "image"), flags.MarkPrerequisites("expose", "container-port"), flags.MarkPrerequisites("image-pull-secret", "image"), )) @@ -141,7 +141,7 @@ func NewAppPushCMD(kymaConfig *cmdcommon.KymaConfig) *cobra.Command { // image flags cmd.Flags().StringVar(&config.image, "image", "", "Name of the image to deploy") cmd.Flags().StringVar(&config.imagePullSecretName, "image-pull-secret", "", "Name of the Kubernetes Secret with credentials to pull the image") - cmd.Flags().StringVar(&config.imageTag, "image-tag", "", "Custom tag for the built image (e.g. a Git commit SHA). Applies only to --code-path and --dockerfile builds.") + cmd.Flags().StringVar(&config.buildTag, "build-tag", "", "Custom tag for the built image (e.g. a Git commit SHA). Applies only to --code-path and --dockerfile builds.") // dockerfile flags cmd.Flags().StringVar(&config.dockerfilePath, "dockerfile", "", "Path to the Dockerfile") @@ -188,10 +188,10 @@ func (apc *appPushConfig) complete() clierror.Error { } } - if apc.imageTag != "" { - if !imageTagRegexp.MatchString(apc.imageTag) { + if apc.buildTag != "" { + if !buildTagRegexp.MatchString(apc.buildTag) { return clierror.New( - fmt.Sprintf("invalid image tag %q", apc.imageTag), + fmt.Sprintf("invalid image tag %q", apc.buildTag), "tag must start with a letter, digit, or underscore", "tag may only contain letters, digits, underscores, dots, and hyphens", "tag must be at most 128 characters", @@ -382,7 +382,7 @@ func resolveImageTag(imageTag string) string { } func buildImage(cfg *appPushConfig) (string, error) { - imageName := fmt.Sprintf("%s:%s", cfg.name, resolveImageTag(cfg.imageTag)) + imageName := fmt.Sprintf("%s:%s", cfg.name, resolveImageTag(cfg.buildTag)) var err error if cfg.packAppPath != "" { diff --git a/internal/cmd/app/push_test.go b/internal/cmd/app/push_test.go index 64543f3e4..4946b7697 100644 --- a/internal/cmd/app/push_test.go +++ b/internal/cmd/app/push_test.go @@ -6,75 +6,75 @@ import ( "github.com/stretchr/testify/require" ) -func Test_appPushConfig_complete_imageTag(t *testing.T) { +func Test_appPushConfig_complete_buildTag(t *testing.T) { tests := []struct { name string - imageTag string + buildTag string wantErr bool }{ { name: "empty tag skips validation - no error", - imageTag: "", + buildTag: "", wantErr: false, }, { name: "valid commit sha", - imageTag: "abc1234def5678", + buildTag: "abc1234def5678", wantErr: false, }, { name: "valid semver", - imageTag: "1.0.0", + buildTag: "1.0.0", wantErr: false, }, { name: "valid underscore prefix", - imageTag: "_build", + buildTag: "_build", wantErr: false, }, { name: "valid single char", - imageTag: "1", + buildTag: "1", wantErr: false, }, { name: "valid with dots and dashes", - imageTag: "v1.2.3-beta.1", + buildTag: "v1.2.3-beta.1", wantErr: false, }, { name: "invalid: contains space", - imageTag: "my tag", + buildTag: "my tag", wantErr: true, }, { name: "invalid: contains colon", - imageTag: "my:tag", + buildTag: "my:tag", wantErr: true, }, { name: "invalid: contains slash", - imageTag: "my/tag", + buildTag: "my/tag", wantErr: true, }, { name: "invalid: contains @", - imageTag: "my@tag", + buildTag: "my@tag", wantErr: true, }, { name: "invalid: starts with dot", - imageTag: ".mytag", + buildTag: ".mytag", wantErr: true, }, { name: "invalid: starts with dash", - imageTag: "-mytag", + buildTag: "-mytag", wantErr: true, }, { name: "invalid: exceeds 128 chars", - imageTag: "abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + buildTag: "abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", wantErr: true, }, } @@ -82,13 +82,13 @@ func Test_appPushConfig_complete_imageTag(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := &appPushConfig{ - imageTag: tt.imageTag, + buildTag: tt.buildTag, } err := cfg.complete() if tt.wantErr { - require.NotNil(t, err, "expected validation error for imageTag=%q", tt.imageTag) + require.NotNil(t, err, "expected validation error for buildTag=%q", tt.buildTag) } else { - require.Nil(t, err, "expected no error for imageTag=%q", tt.imageTag) + require.Nil(t, err, "expected no error for buildTag=%q", tt.buildTag) } }) } From a6e662ee2ac0c0c1208134754cb73b4a527f402a Mon Sep 17 00:00:00 2001 From: kwiatekus Date: Thu, 7 May 2026 08:03:43 +0200 Subject: [PATCH 11/11] docs: regenerate kyma_app_push.md with --build-tag flag --- docs/user/gen-docs/kyma_app_push.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/user/gen-docs/kyma_app_push.md b/docs/user/gen-docs/kyma_app_push.md index 42b9e8f0f..fcb2eb018 100644 --- a/docs/user/gen-docs/kyma_app_push.md +++ b/docs/user/gen-docs/kyma_app_push.md @@ -17,6 +17,10 @@ kyma app push [flags] # The application will be built using Cloud Native Buildpacks: kyma app push --name my-app --code-path . + # Push with a custom image tag (e.g. a Git commit SHA for CI/CD traceability): + kyma app push --name my-app --code-path . --build-tag abc1234 + kyma app push --name my-app --dockerfile ./Dockerfile --build-tag $GITHUB_SHA + # Push an application based on a Dockerfile located in the current directory: kyma app push --name my-app --dockerfile ./Dockerfile --dockerfile-context . @@ -65,6 +69,7 @@ kyma app push [flags] ## Flags ```text + --build-tag string Custom tag for the built image (e.g. a Git commit SHA). Applies only to --code-path and --dockerfile builds. --code-path string Path to the application source code directory --container-port int Port on which the application is exposed --dockerfile string Path to the Dockerfile