This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new abb3ab1cf fix(traits): gitops, idempotent push
abb3ab1cf is described below

commit abb3ab1cf432928379eb43dff539daaff9212df8
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sun Jan 25 09:24:39 2026 +0100

    fix(traits): gitops, idempotent push
    
    Closes #6412
---
 docs/modules/ROOT/pages/running/gitops.adoc    |  3 +++
 pkg/controller/integrationplatform/kamelets.go |  6 +++---
 pkg/trait/gitops.go                            | 25 +++++++++++++++++++++++--
 pkg/trait/gitops_test.go                       | 13 ++++++++++---
 4 files changed, 39 insertions(+), 8 deletions(-)

diff --git a/docs/modules/ROOT/pages/running/gitops.adoc 
b/docs/modules/ROOT/pages/running/gitops.adoc
index 1dd75b93f..28792e400 100644
--- a/docs/modules/ROOT/pages/running/gitops.adoc
+++ b/docs/modules/ROOT/pages/running/gitops.adoc
@@ -32,6 +32,7 @@ spec:
     container:
       requestMemory: 256Mi
     gitops:
+      enabled: true
       url: https://github.com/my-org/my-camel-apps.git
       secret: my-gh-token
       branchPush: cicd-listener
@@ -154,6 +155,7 @@ spec:
       - my-env=dev
 ...
     gitops:
+      enabled: true
       url: https://github.com/my-org/my-camel-apps.git
       secret: my-gh-token
       branchPush: cicd-listener-test
@@ -174,6 +176,7 @@ spec:
       properties:
       - my-env=test
     gitops:
+      enabled: true
       url: https://github.com/my-org/my-camel-apps.git
       secret: my-gh-token
       branchPush: cicd-listener-prod
diff --git a/pkg/controller/integrationplatform/kamelets.go 
b/pkg/controller/integrationplatform/kamelets.go
index 87b8f8de7..dbe7151e9 100644
--- a/pkg/controller/integrationplatform/kamelets.go
+++ b/pkg/controller/integrationplatform/kamelets.go
@@ -29,7 +29,7 @@ import (
        "strings"
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
-       "knative.dev/pkg/ptr"
+       "k8s.io/utils/ptr"
 
        "github.com/apache/camel-k/v2/pkg/client"
        "github.com/apache/camel-k/v2/pkg/util"
@@ -234,8 +234,8 @@ func loadKamelet(path string, platform 
*v1.IntegrationPlatform) (*v1.Kamelet, er
                        Kind:               platform.Kind,
                        Name:               platform.Name,
                        UID:                platform.UID,
-                       Controller:         ptr.Bool(true),
-                       BlockOwnerDeletion: ptr.Bool(true),
+                       Controller:         ptr.To(true),
+                       BlockOwnerDeletion: ptr.To(true),
                },
        }
        kamelet.SetOwnerReferences(references)
diff --git a/pkg/trait/gitops.go b/pkg/trait/gitops.go
index 9f54b2f45..58a4eab5b 100644
--- a/pkg/trait/gitops.go
+++ b/pkg/trait/gitops.go
@@ -30,6 +30,7 @@ import (
        "github.com/apache/camel-k/v2/pkg/util/io"
        "github.com/go-git/go-git/v5/config"
        "github.com/go-git/go-git/v5/plumbing"
+       corev1 "k8s.io/api/core/v1"
        "k8s.io/utils/ptr"
 
        git "github.com/go-git/go-git/v5"
@@ -117,7 +118,10 @@ func (t *gitOpsTrait) pushGitOpsItInGitRepo(ctx 
context.Context, it *v1.Integrat
        nowDate := time.Now().Format("20060102-150405")
        branchName := t.BranchPush
        if branchName == "" {
-               branchName = "cicd/candidate-release-" + nowDate
+               // NOTE: this is important to guarantee idempotency. We make 
sure not to create
+               // more than one branch from different reconciliation cycles.
+               branchNameDate := 
it.Status.DeploymentTimestamp.Format("20060102-150405")
+               branchName = "cicd/candidate-release-" + branchNameDate
        }
        commitMessage := "feat(ci): build completed on " + nowDate
        branchRef := plumbing.NewBranchReferenceName(branchName)
@@ -175,9 +179,26 @@ func (t *gitOpsTrait) pushGitOpsItInGitRepo(ctx 
context.Context, it *v1.Integrat
                RefSpecs: []config.RefSpec{
                        config.RefSpec(branchRef + ":" + branchRef),
                },
+               // Note: this is needed to make the task idempotent without 
returning any error.
+               // Even if more parallel reconciliations are kicked off, we 
always push the same content,
+               // possibly more than once, without risking to raise an error.
+               Force: true,
        }
 
-       return repo.Push(gitPushOptions)
+       err = repo.Push(gitPushOptions)
+       if err != nil {
+               return err
+       }
+
+       // Publish a condition to notify the change was pushed to the branch
+       it.Status.SetCondition(
+               v1.IntegrationConditionType("GitPushed"),
+               corev1.ConditionTrue,
+               "PushedToGit",
+               "Integration changes pushed to branch "+branchName,
+       )
+
+       return nil
 }
 
 // gitConf returns the git repo configuration where to pull the project from. 
If no value is provided, then, it takes
diff --git a/pkg/trait/gitops_test.go b/pkg/trait/gitops_test.go
index 4acda7c8e..ebc40a046 100644
--- a/pkg/trait/gitops_test.go
+++ b/pkg/trait/gitops_test.go
@@ -26,18 +26,19 @@ import (
        "time"
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
-       "knative.dev/pkg/ptr"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
        "github.com/go-git/go-git/v5/plumbing/object"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
+       "k8s.io/utils/ptr"
 
        git "github.com/go-git/go-git/v5"
 )
 
 func TestGitOpsAddAction(t *testing.T) {
        trait, _ := newGitOpsTrait().(*gitOpsTrait)
-       trait.Enabled = ptr.Bool(true)
+       trait.Enabled = ptr.To(true)
        env := &Environment{
                Integration: &v1.Integration{
                        Status: v1.IntegrationStatus{
@@ -72,12 +73,18 @@ func TestGitOpsPushRepoDefault(t *testing.T) {
                Git:     conf,
                Sources: []v1.SourceSpec{v1.NewSourceSpec("Test.java", "bogus, 
irrelevant for test", v1.LanguageJavaSource)},
        }
+       now := metav1.Now().Rfc3339Copy()
        it.Status = v1.IntegrationStatus{
-               Image: "my-img-recently-baked",
+               Image:               "my-img-recently-baked",
+               DeploymentTimestamp: &now,
        }
 
        err = trait.pushGitOpsItInGitRepo(context.TODO(), &it, tmpGitDir, 
"fake")
        require.NoError(t, err)
+       assert.Contains(t,
+               
it.Status.GetCondition(v1.IntegrationConditionType("GitPushed")).Message,
+               "Integration changes pushed to branch cicd/candidate-release",
+       )
 
        lastCommitMessage, err := getLastCommitMessage(tmpGitDir)
        require.NoError(t, err)

Reply via email to