Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a20a48e
initial commit.. wip
pbhatnagar-oss Aug 14, 2025
c2ba18c
adding unit tests
pbhatnagar-oss Aug 14, 2025
de7f06f
more unit tests and comments
pbhatnagar-oss Aug 15, 2025
c8c3bd0
clean up, adding more comments
pbhatnagar-oss Aug 19, 2025
94e0b10
Merge branch 'master' of github.com:pbhatnagar-oss/argo-cd into commi…
pbhatnagar-oss Aug 19, 2025
4d143a9
updating the template to strongly typed. reverting changes to generat…
pbhatnagar-oss Aug 20, 2025
2d1a17d
Update controller/hydrator/hydrator.go
pbhatnagar-oss Aug 22, 2025
328ebfd
Update controller/hydrator/hydrator.go
pbhatnagar-oss Aug 22, 2025
9f2bbb9
addressing review-comments and codegen
pbhatnagar-oss Aug 22, 2025
991c0c6
rename symbol and adjust comments
pbhatnagar-oss Aug 22, 2025
4b2fb40
please lint
pbhatnagar-oss Aug 22, 2025
06125ce
adjust template and test (#1)
crenshaw-dev Aug 27, 2025
71a85b0
review changes, fix e2e test and update documentation
pbhatnagar-oss Aug 28, 2025
b7f6e2e
Update docs/user-guide/source-hydrator.md
pbhatnagar-oss Aug 28, 2025
3fdb975
Merge branch 'master' of github.com:pbhatnagar-oss/argo-cd into commi…
pbhatnagar-oss Aug 28, 2025
b566ce6
Merge branch 'commit-message-templating' of github.com:pbhatnagar-oss…
pbhatnagar-oss Aug 28, 2025
4840a39
review comments
pbhatnagar-oss Aug 28, 2025
eedfa33
fix unit test
pbhatnagar-oss Aug 28, 2025
2420e4b
Update docs/operator-manual/argocd-cm.yaml
pbhatnagar-oss Aug 29, 2025
66965ca
Update util/settings/settings.go
pbhatnagar-oss Aug 29, 2025
d655a91
remove the template from argocd-cm in the favor of default
pbhatnagar-oss Aug 29, 2025
d7697ff
remove the template from argocd-cm in the favor of default
pbhatnagar-oss Aug 29, 2025
46d5a10
Merge branch 'commit-message-templating' of github.com:pbhatnagar-oss…
pbhatnagar-oss Aug 29, 2025
267d9ab
Merge branch 'commit-message-templating' of github.com:pbhatnagar-oss…
pbhatnagar-oss Aug 29, 2025
1793160
Merge branch 'commit-message-templating' of github.com:pbhatnagar-oss…
pbhatnagar-oss Aug 29, 2025
b2b9cbc
fix unit test to accommodate the change to template
pbhatnagar-oss Aug 29, 2025
abac470
Update docs/user-guide/source-hydrator.md
pbhatnagar-oss Sep 2, 2025
c31eeed
Update docs/operator-manual/argocd-cm.yaml
pbhatnagar-oss Sep 2, 2025
7d60fe4
Update manifests/base/config/argocd-cm.yaml
pbhatnagar-oss Sep 2, 2025
343d40f
codegen
pbhatnagar-oss Sep 3, 2025
416fca1
Merge branch 'master' of github.com:pbhatnagar-oss/argo-cd into commi…
pbhatnagar-oss Sep 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 8 additions & 22 deletions commitserver/commit/hydratorhelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"time"

"github.com/Masterminds/sprig/v3"
log "github.com/sirupsen/logrus"
Expand All @@ -17,7 +15,7 @@ import (
"github.com/argoproj/argo-cd/v3/commitserver/apiclient"
"github.com/argoproj/argo-cd/v3/common"
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/git"
"github.com/argoproj/argo-cd/v3/util/hydrator"
"github.com/argoproj/argo-cd/v3/util/io"
)

Expand All @@ -36,25 +34,13 @@ func init() {
// WriteForPaths writes the manifests, hydrator.metadata, and README.md files for each path in the provided paths. It
// also writes a root-level hydrator.metadata file containing the repo URL and dry SHA.
func WriteForPaths(root *os.Root, repoUrl, drySha string, dryCommitMetadata *appv1.RevisionMetadata, paths []*apiclient.PathDetails) error { //nolint:revive //FIXME(var-naming)
author := ""
message := ""
date := ""
var references []appv1.RevisionReference
if dryCommitMetadata != nil {
author = dryCommitMetadata.Author
message = dryCommitMetadata.Message
if dryCommitMetadata.Date != nil {
date = dryCommitMetadata.Date.Format(time.RFC3339)
}
references = dryCommitMetadata.References
hydratorMetadata, err := hydrator.GetCommitMetadata(repoUrl, drySha, dryCommitMetadata)
if err != nil {
return fmt.Errorf("failed to retrieve hydrator metadata: %w", err)
}

subject, body, _ := strings.Cut(message, "\n\n")

_, bodyMinusTrailers := git.GetReferences(log.WithFields(log.Fields{"repo": repoUrl, "revision": drySha}), body)

// Write the top-level readme.
err := writeMetadata(root, "", hydratorMetadataFile{DrySHA: drySha, RepoURL: repoUrl, Author: author, Subject: subject, Body: bodyMinusTrailers, Date: date, References: references})
err = writeMetadata(root, "", hydratorMetadata)
if err != nil {
return fmt.Errorf("failed to write top-level hydrator metadata: %w", err)
}
Expand Down Expand Up @@ -86,7 +72,7 @@ func WriteForPaths(root *os.Root, repoUrl, drySha string, dryCommitMetadata *app
}

// Write hydrator.metadata containing information about the hydration process.
hydratorMetadata := hydratorMetadataFile{
hydratorMetadata := hydrator.HydratorCommitMetadata{
Commands: p.Commands,
DrySHA: drySha,
RepoURL: repoUrl,
Expand All @@ -106,7 +92,7 @@ func WriteForPaths(root *os.Root, repoUrl, drySha string, dryCommitMetadata *app
}

// writeMetadata writes the metadata to the hydrator.metadata file.
func writeMetadata(root *os.Root, dirPath string, metadata hydratorMetadataFile) error {
func writeMetadata(root *os.Root, dirPath string, metadata hydrator.HydratorCommitMetadata) error {
hydratorMetadataPath := filepath.Join(dirPath, "hydrator.metadata")
f, err := root.Create(hydratorMetadataPath)
if err != nil {
Expand All @@ -125,7 +111,7 @@ func writeMetadata(root *os.Root, dirPath string, metadata hydratorMetadataFile)
}

// writeReadme writes the readme to the README.md file.
func writeReadme(root *os.Root, dirPath string, metadata hydratorMetadataFile) error {
func writeReadme(root *os.Root, dirPath string, metadata hydrator.HydratorCommitMetadata) error {
readmeTemplate, err := template.New("readme").Funcs(sprigFuncMap).Parse(manifestHydrationReadmeTemplate)
if err != nil {
return fmt.Errorf("failed to parse readme template: %w", err)
Expand Down
7 changes: 4 additions & 3 deletions commitserver/commit/hydratorhelper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/argoproj/argo-cd/v3/commitserver/apiclient"
appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/hydrator"
)

// tempRoot creates a temporary directory and returns an os.Root object for it.
Expand Down Expand Up @@ -144,7 +145,7 @@ Argocd-reference-commit-sha: abc123
func TestWriteMetadata(t *testing.T) {
root := tempRoot(t)

metadata := hydratorMetadataFile{
metadata := hydrator.HydratorCommitMetadata{
RepoURL: "https://github.com/example/repo",
DrySHA: "abc123",
}
Expand All @@ -156,7 +157,7 @@ func TestWriteMetadata(t *testing.T) {
metadataBytes, err := os.ReadFile(metadataPath)
require.NoError(t, err)

var readMetadata hydratorMetadataFile
var readMetadata hydrator.HydratorCommitMetadata
err = json.Unmarshal(metadataBytes, &readMetadata)
require.NoError(t, err)
assert.Equal(t, metadata, readMetadata)
Expand All @@ -171,7 +172,7 @@ func TestWriteReadme(t *testing.T) {
hash := sha256.Sum256(randomData)
sha := hex.EncodeToString(hash[:])

metadata := hydratorMetadataFile{
metadata := hydrator.HydratorCommitMetadata{
RepoURL: "https://github.com/example/repo",
DrySHA: "abc123",
References: []appsv1.RevisionReference{
Expand Down
30 changes: 29 additions & 1 deletion controller/hydrator/hydrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
applog "github.com/argoproj/argo-cd/v3/util/app/log"
"github.com/argoproj/argo-cd/v3/util/git"
"github.com/argoproj/argo-cd/v3/util/hydrator"
utilio "github.com/argoproj/argo-cd/v3/util/io"
)

Expand Down Expand Up @@ -59,6 +60,9 @@ type Dependencies interface {
// AddHydrationQueueItem adds a hydration queue item to the queue. This is used to trigger the hydration process for
// a group of applications which are hydrating to the same repo and target branch.
AddHydrationQueueItem(key types.HydrationQueueKey)

// GetHydratorCommitMessageTemplate gets the configured template for rendering commit messages.
GetHydratorCommitMessageTemplate() (string, error)
}

// Hydrator is the main struct that implements the hydration logic. It uses the Dependencies interface to access the
Expand Down Expand Up @@ -340,13 +344,22 @@ func (h *Hydrator) hydrate(logCtx *log.Entry, apps []*appv1.Application) (string
}
logCtx.Warn("no credentials found for repo, continuing without credentials")
}
// get the commit message template
commitMessageTemplate, err := h.dependencies.GetHydratorCommitMessageTemplate()
if err != nil {
return "", "", fmt.Errorf("failed to get hydrated commit message template: %w", err)
}
commitMessage, errMsg := getTemplatedCommitMessage(repoURL, targetRevision, commitMessageTemplate, revisionMetadata)
if errMsg != nil {
return "", "", fmt.Errorf("failed to get hydrator commit templated message: %w", errMsg)
}

manifestsRequest := commitclient.CommitHydratedManifestsRequest{
Repo: repo,
SyncBranch: syncBranch,
TargetBranch: targetBranch,
DrySha: targetRevision,
CommitMessage: "[Argo CD Bot] hydrate " + targetRevision,
CommitMessage: commitMessage,
Paths: paths,
DryCommitMetadata: revisionMetadata,
}
Expand Down Expand Up @@ -411,3 +424,18 @@ func appNeedsHydration(app *appv1.Application, statusHydrateTimeout time.Duratio

return false, ""
}

// Gets the multi-line commit message based on the template defined in the configmap. It is a two step process:
// 1. Get the metadata template engine would use to render the template
// 2. Pass the output of Step 1 and Step 2 to template Render
func getTemplatedCommitMessage(repoURL, revision, commitMessageTemplate string, dryCommitMetadata *appv1.RevisionMetadata) (string, error) {
hydratorCommitMetadata, err := hydrator.GetCommitMetadata(repoURL, revision, dryCommitMetadata)
if err != nil {
return "", fmt.Errorf("failed to get hydrated commit message: %w", err)
}
templatedCommitMsg, err := hydrator.Render(commitMessageTemplate, hydratorCommitMetadata)
if err != nil {
return "", fmt.Errorf("failed to parse template %s: %w", commitMessageTemplate, err)
}
return templatedCommitMsg, nil
}
84 changes: 84 additions & 0 deletions controller/hydrator/hydrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ import (
"github.com/argoproj/argo-cd/v3/controller/hydrator/mocks"
"github.com/argoproj/argo-cd/v3/controller/hydrator/types"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/settings"
)

var message = `testn
Argocd-reference-commit-repourl: https://github.com/test/argocd-example-apps
Argocd-reference-commit-author: Argocd-reference-commit-author
Argocd-reference-commit-subject: testhydratormd
Signed-off-by: testUser <[email protected]>`

func Test_appNeedsHydration(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -167,3 +174,80 @@ func Test_getRelevantAppsForHydration_RepoURLNormalization(t *testing.T) {
require.NoError(t, err)
assert.Len(t, relevantApps, 2, "Expected both apps to be considered relevant despite URL differences")
}

func TestHydrator_getTemplatedCommitMessage(t *testing.T) {
references := make([]v1alpha1.RevisionReference, 0)
revReference := v1alpha1.RevisionReference{
Commit: &v1alpha1.CommitMetadata{
Author: "testAuthor",
Subject: "test",
RepoURL: "https://github.com/test/argocd-example-apps",
SHA: "3ff41cc5247197a6caf50216c4c76cc29d78a97c",
},
}
references = append(references, revReference)
type args struct {
repoURL string
revision string
dryCommitMetadata *v1alpha1.RevisionMetadata
template string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "test template",
args: args{
repoURL: "https://github.com/test/argocd-example-apps",
revision: "3ff41cc5247197a6caf50216c4c76cc29d78a97d",
dryCommitMetadata: &v1alpha1.RevisionMetadata{
Author: "test [email protected]",
Date: &metav1.Time{
Time: metav1.Now().Time,
},
Message: message,
References: references,
},
template: settings.CommitMessageTemplate,
},
want: `3ff41cc: testn
Argocd-reference-commit-repourl: https://github.com/test/argocd-example-apps
Argocd-reference-commit-author: Argocd-reference-commit-author
Argocd-reference-commit-subject: testhydratormd
Signed-off-by: testUser <[email protected]>

Co-authored-by: testAuthor
Co-authored-by: test [email protected]
`,
},
{
name: "test empty template",
args: args{
repoURL: "https://github.com/test/argocd-example-apps",
revision: "3ff41cc5247197a6caf50216c4c76cc29d78a97d",
dryCommitMetadata: &v1alpha1.RevisionMetadata{
Author: "test [email protected]",
Date: &metav1.Time{
Time: metav1.Now().Time,
},
Message: message,
References: references,
},
},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getTemplatedCommitMessage(tt.args.repoURL, tt.args.revision, tt.args.template, tt.args.dryCommitMetadata)
if (err != nil) != tt.wantErr {
t.Errorf("Hydrator.getHydratorCommitMessage() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got)
})
}
}
53 changes: 53 additions & 0 deletions controller/hydrator/mocks/Dependencies.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions controller/hydrator_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,12 @@ func (ctrl *ApplicationController) PersistAppHydratorStatus(orig *appv1.Applicat
func (ctrl *ApplicationController) AddHydrationQueueItem(key types.HydrationQueueKey) {
ctrl.hydrationQueue.AddRateLimited(key)
}

func (ctrl *ApplicationController) GetHydratorCommitMessageTemplate() (string, error) {
sourceHydratorCommitMessageKey, err := ctrl.settingsMgr.GetSourceHydratorCommitMessageTemplate()
if err != nil {
return "", fmt.Errorf("failed to get sourceHydrator commit message template key: %w", err)
}

return sourceHydratorCommitMessageKey, nil
}
44 changes: 44 additions & 0 deletions controller/hydrator_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
"github.com/argoproj/argo-cd/v3/test"
"github.com/argoproj/argo-cd/v3/util/settings"
)

func TestGetRepoObjs(t *testing.T) {
Expand Down Expand Up @@ -77,3 +78,46 @@ func TestGetRepoObjs(t *testing.T) {

assert.Equal(t, "ConfigMap", objs[0].GetKind())
}

func TestGetHydratorCommitMessageTemplate_WhenTemplateisNotDefined_FallbackToDefault(t *testing.T) {
cm := test.NewConfigMap()
cmBytes, _ := json.Marshal(cm)

data := fakeData{
manifestResponse: &apiclient.ManifestResponse{
Manifests: []string{string(cmBytes)},
Namespace: test.FakeDestNamespace,
Server: test.FakeClusterURL,
Revision: "abc123",
},
}

ctrl := newFakeControllerWithResync(&data, time.Minute, nil, errors.New("this should not be called"))

tmpl, err := ctrl.GetHydratorCommitMessageTemplate()
require.NoError(t, err)
assert.NotEmpty(t, tmpl) // should fallback to default
assert.Equal(t, settings.CommitMessageTemplate, tmpl)
}

func TestGetHydratorCommitMessageTemplate(t *testing.T) {
cm := test.NewFakeConfigMap()
cm.Data["sourceHydrator.commitMessageTemplate"] = settings.CommitMessageTemplate
cmBytes, _ := json.Marshal(cm)

data := fakeData{
manifestResponse: &apiclient.ManifestResponse{
Manifests: []string{string(cmBytes)},
Namespace: test.FakeDestNamespace,
Server: test.FakeClusterURL,
Revision: "abc123",
},
configMapData: cm.Data,
}

ctrl := newFakeControllerWithResync(&data, time.Minute, nil, errors.New("this should not be called"))

tmpl, err := ctrl.GetHydratorCommitMessageTemplate()
require.NoError(t, err)
assert.NotEmpty(t, tmpl)
}
Loading
Loading