feat(hydrator): making configurable authorName/Email used for hydration commit (#22847) (#25746)

Signed-off-by: Aditya Raj <adityaraj10600@gmail.com>
This commit is contained in:
Aditya Raj
2026-01-21 19:14:32 +05:30
committed by GitHub
parent fc6379b90e
commit 507289925b
11 changed files with 416 additions and 39 deletions

View File

@@ -43,10 +43,14 @@ type CommitHydratedManifestsRequest struct {
// Paths contains the paths to write hydrated manifests to, along with the manifests and commands to execute.
Paths []*PathDetails `protobuf:"bytes,6,rep,name=paths,proto3" json:"paths,omitempty"`
// DryCommitMetadata contains metadata about the DRY commit, such as the author and committer.
DryCommitMetadata *v1alpha1.RevisionMetadata `protobuf:"bytes,7,opt,name=dryCommitMetadata,proto3" json:"dryCommitMetadata,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
DryCommitMetadata *v1alpha1.RevisionMetadata `protobuf:"bytes,7,opt,name=dryCommitMetadata,proto3" json:"dryCommitMetadata,omitempty"`
// AuthorName is the author name to use for the commit. If empty, defaults to "Argo CD".
AuthorName string `protobuf:"bytes,8,opt,name=authorName,proto3" json:"authorName,omitempty"`
// AuthorEmail is the author email to use for the commit. If empty, defaults to "argo-cd@example.com".
AuthorEmail string `protobuf:"bytes,9,opt,name=authorEmail,proto3" json:"authorEmail,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommitHydratedManifestsRequest) Reset() { *m = CommitHydratedManifestsRequest{} }
@@ -131,6 +135,20 @@ func (m *CommitHydratedManifestsRequest) GetDryCommitMetadata() *v1alpha1.Revisi
return nil
}
func (m *CommitHydratedManifestsRequest) GetAuthorName() string {
if m != nil {
return m.AuthorName
}
return ""
}
func (m *CommitHydratedManifestsRequest) GetAuthorEmail() string {
if m != nil {
return m.AuthorEmail
}
return ""
}
// PathDetails holds information about hydrated manifests to be written to a particular path in the hydrated manifests
// commit.
type PathDetails struct {
@@ -307,37 +325,39 @@ func init() {
func init() { proto.RegisterFile("commitserver/commit/commit.proto", fileDescriptor_cf3a3abbc35e3069) }
var fileDescriptor_cf3a3abbc35e3069 = []byte{
// 479 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x95, 0xeb, 0x34, 0x90, 0x49, 0x7b, 0x60, 0x0f, 0xd4, 0xca, 0xc1, 0xb5, 0x2c, 0x0e, 0xb9,
0xb0, 0x56, 0x13, 0xc1, 0x8d, 0x4b, 0xc3, 0xa1, 0x42, 0xb4, 0x20, 0xe7, 0x86, 0x2a, 0xa1, 0xa9,
0xbd, 0xd8, 0x4b, 0x63, 0xef, 0xb2, 0xbb, 0xb1, 0x64, 0x89, 0xaf, 0xe1, 0x6b, 0x38, 0xf2, 0x09,
0x28, 0x5f, 0x82, 0xbc, 0xb6, 0x69, 0x02, 0x0a, 0x39, 0x70, 0xf2, 0xce, 0xbc, 0xf1, 0x7b, 0xb3,
0xf3, 0x76, 0x20, 0x48, 0x44, 0x51, 0x70, 0xa3, 0x99, 0xaa, 0x98, 0x8a, 0xda, 0xa0, 0xfb, 0x50,
0xa9, 0x84, 0x11, 0x93, 0xb7, 0x19, 0x37, 0xf9, 0xfa, 0x8e, 0x26, 0xa2, 0x88, 0x50, 0x65, 0x42,
0x2a, 0xf1, 0xd9, 0x1e, 0x9e, 0x27, 0x69, 0x54, 0xcd, 0x23, 0x79, 0x9f, 0x45, 0x28, 0xb9, 0x8e,
0x50, 0xca, 0x15, 0x4f, 0xd0, 0x70, 0x51, 0x46, 0xd5, 0x05, 0xae, 0x64, 0x8e, 0x17, 0x51, 0xc6,
0x4a, 0xa6, 0xd0, 0xb0, 0xb4, 0x65, 0x0b, 0xbf, 0xb9, 0xe0, 0x2f, 0x2c, 0xfd, 0x55, 0x9d, 0x5a,
0xe0, 0x1a, 0x4b, 0xfe, 0x89, 0x69, 0xa3, 0x63, 0xf6, 0x65, 0xcd, 0xb4, 0x21, 0xb7, 0x30, 0x50,
0x4c, 0x0a, 0xcf, 0x09, 0x9c, 0xe9, 0x78, 0x76, 0x45, 0x1f, 0xf4, 0x69, 0xaf, 0x6f, 0x0f, 0x1f,
0x93, 0x94, 0x56, 0x73, 0x2a, 0xef, 0x33, 0xda, 0xe8, 0xd3, 0x2d, 0x7d, 0xda, 0xeb, 0xd3, 0x98,
0x49, 0xa1, 0xb9, 0x11, 0xaa, 0x8e, 0x2d, 0x2b, 0xf1, 0x01, 0x74, 0x5d, 0x26, 0x97, 0x0a, 0xcb,
0x24, 0xf7, 0x8e, 0x02, 0x67, 0x3a, 0x8a, 0xb7, 0x32, 0x24, 0x84, 0x13, 0x83, 0x2a, 0x63, 0xa6,
0xab, 0x70, 0x6d, 0xc5, 0x4e, 0x8e, 0x3c, 0x85, 0x61, 0xaa, 0xea, 0x65, 0x8e, 0xde, 0xc0, 0xa2,
0x5d, 0x44, 0x9e, 0xc1, 0x69, 0x3b, 0xba, 0x6b, 0xa6, 0x35, 0x66, 0xcc, 0x3b, 0xb6, 0xf0, 0x6e,
0x92, 0x84, 0x70, 0x2c, 0xd1, 0xe4, 0xda, 0x1b, 0x06, 0xee, 0x74, 0x3c, 0x3b, 0xa1, 0xef, 0xd1,
0xe4, 0xaf, 0x99, 0x41, 0xbe, 0xd2, 0x71, 0x0b, 0x91, 0xaf, 0xf0, 0x24, 0x55, 0xf5, 0xa2, 0xfb,
0xcf, 0x60, 0x8a, 0x06, 0xbd, 0x47, 0x76, 0x20, 0x37, 0xff, 0x3b, 0x90, 0x8a, 0x6b, 0x2e, 0xca,
0x9e, 0x35, 0xfe, 0x5b, 0x28, 0x5c, 0xc3, 0x78, 0xab, 0x27, 0x42, 0x60, 0xd0, 0x74, 0x65, 0x0d,
0x19, 0xc5, 0xf6, 0x4c, 0x5e, 0xc2, 0xa8, 0xe8, 0x8d, 0xf3, 0x8e, 0xec, 0x45, 0x3c, 0xfa, 0xa7,
0xa5, 0xfd, 0xa5, 0x1e, 0x4a, 0xc9, 0x04, 0x1e, 0x37, 0xd3, 0xc0, 0x32, 0xd5, 0x9e, 0x1b, 0xb8,
0xd3, 0x51, 0xfc, 0x3b, 0x0e, 0x5f, 0xc1, 0xd9, 0x1e, 0x86, 0xc6, 0x95, 0x9e, 0xe3, 0xcd, 0xf2,
0xdd, 0x4d, 0xd7, 0xca, 0x4e, 0x2e, 0x5c, 0xc0, 0xf9, 0xde, 0x97, 0xa5, 0xa5, 0x28, 0x35, 0x23,
0x01, 0x8c, 0xf3, 0x0e, 0x6c, 0xdc, 0x6b, 0x59, 0xb6, 0x53, 0xb3, 0x02, 0x4e, 0x5b, 0x92, 0x25,
0x53, 0x15, 0x4f, 0x18, 0xb9, 0x85, 0xb3, 0x3d, 0xac, 0xe4, 0x9c, 0xfe, 0xfb, 0x25, 0x4f, 0x02,
0x7a, 0xa0, 0xa1, 0xcb, 0xc5, 0xf7, 0x8d, 0xef, 0xfc, 0xd8, 0xf8, 0xce, 0xcf, 0x8d, 0xef, 0x7c,
0x78, 0x71, 0x60, 0xd5, 0x76, 0x76, 0x15, 0x25, 0x4f, 0x56, 0x9c, 0x95, 0xe6, 0x6e, 0x68, 0x57,
0x6b, 0xfe, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x21, 0x3b, 0xa8, 0x3c, 0xcc, 0x03, 0x00, 0x00,
// 505 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x41, 0x6f, 0xd3, 0x30,
0x14, 0x56, 0xd6, 0xae, 0xac, 0xee, 0x76, 0xc0, 0x07, 0x66, 0xf5, 0x90, 0x45, 0x11, 0x87, 0x5e,
0x70, 0xb4, 0x56, 0x70, 0xe3, 0xb2, 0x82, 0x34, 0x21, 0x56, 0x50, 0x7a, 0x43, 0x93, 0xd0, 0x5b,
0x62, 0x12, 0xb3, 0x24, 0x36, 0xb6, 0x1b, 0x29, 0x12, 0x3f, 0x90, 0x23, 0x3f, 0x01, 0x55, 0xfc,
0x10, 0x14, 0x27, 0xa1, 0x29, 0xa8, 0xec, 0xc0, 0x29, 0x7e, 0xdf, 0x7b, 0xf9, 0x3e, 0xbf, 0xef,
0xf9, 0x21, 0x2f, 0x12, 0x79, 0xce, 0x8d, 0x66, 0xaa, 0x64, 0x2a, 0x68, 0x82, 0xf6, 0x43, 0xa5,
0x12, 0x46, 0x4c, 0xdf, 0x26, 0xdc, 0xa4, 0x9b, 0x3b, 0x1a, 0x89, 0x3c, 0x00, 0x95, 0x08, 0xa9,
0xc4, 0x67, 0x7b, 0x78, 0x16, 0xc5, 0x41, 0xb9, 0x08, 0xe4, 0x7d, 0x12, 0x80, 0xe4, 0x3a, 0x00,
0x29, 0x33, 0x1e, 0x81, 0xe1, 0xa2, 0x08, 0xca, 0x4b, 0xc8, 0x64, 0x0a, 0x97, 0x41, 0xc2, 0x0a,
0xa6, 0xc0, 0xb0, 0xb8, 0x61, 0xf3, 0x7f, 0x0e, 0x90, 0xbb, 0xb4, 0xf4, 0xd7, 0x55, 0x6c, 0x13,
0x37, 0x50, 0xf0, 0x4f, 0x4c, 0x1b, 0x1d, 0xb2, 0x2f, 0x1b, 0xa6, 0x0d, 0xbe, 0x45, 0x43, 0xc5,
0xa4, 0x20, 0x8e, 0xe7, 0xcc, 0x26, 0xf3, 0x6b, 0xba, 0xd3, 0xa7, 0x9d, 0xbe, 0x3d, 0x7c, 0x8c,
0x62, 0x5a, 0x2e, 0xa8, 0xbc, 0x4f, 0x68, 0xad, 0x4f, 0x7b, 0xfa, 0xb4, 0xd3, 0xa7, 0x21, 0x93,
0x42, 0x73, 0x23, 0x54, 0x15, 0x5a, 0x56, 0xec, 0x22, 0xa4, 0xab, 0x22, 0xba, 0x52, 0x50, 0x44,
0x29, 0x39, 0xf2, 0x9c, 0xd9, 0x38, 0xec, 0x21, 0xd8, 0x47, 0xa7, 0x06, 0x54, 0xc2, 0x4c, 0x5b,
0x31, 0xb0, 0x15, 0x7b, 0x18, 0x7e, 0x82, 0x46, 0xb1, 0xaa, 0xd6, 0x29, 0x90, 0xa1, 0xcd, 0xb6,
0x11, 0x7e, 0x8a, 0xce, 0x1a, 0xeb, 0x6e, 0x98, 0xd6, 0x90, 0x30, 0x72, 0x6c, 0xd3, 0xfb, 0x20,
0xf6, 0xd1, 0xb1, 0x04, 0x93, 0x6a, 0x32, 0xf2, 0x06, 0xb3, 0xc9, 0xfc, 0x94, 0xbe, 0x07, 0x93,
0xbe, 0x62, 0x06, 0x78, 0xa6, 0xc3, 0x26, 0x85, 0xbf, 0xa2, 0xc7, 0xb1, 0xaa, 0x96, 0xed, 0x7f,
0x06, 0x62, 0x30, 0x40, 0x1e, 0x59, 0x43, 0x56, 0xff, 0x6b, 0x48, 0xc9, 0x35, 0x17, 0x45, 0xc7,
0x1a, 0xfe, 0x2d, 0x54, 0x7b, 0x04, 0x1b, 0x93, 0x0a, 0xb5, 0x82, 0x9c, 0x91, 0x93, 0xc6, 0xa3,
0x1d, 0x82, 0x3d, 0x34, 0x69, 0xa2, 0xd7, 0x39, 0xf0, 0x8c, 0x8c, 0x6d, 0x41, 0x1f, 0xf2, 0x37,
0x68, 0xd2, 0xeb, 0x0a, 0x63, 0x34, 0xac, 0xfb, 0xb2, 0x23, 0x1d, 0x87, 0xf6, 0x8c, 0x5f, 0xa0,
0x71, 0xde, 0x8d, 0x9e, 0x1c, 0x59, 0x2b, 0x08, 0xfd, 0xf3, 0x51, 0x74, 0xb6, 0xec, 0x4a, 0xf1,
0x14, 0x9d, 0xd4, 0x7e, 0x42, 0x11, 0x6b, 0x32, 0xf0, 0x06, 0xb3, 0x71, 0xf8, 0x3b, 0xf6, 0x5f,
0xa2, 0xf3, 0x03, 0x0c, 0xf5, 0x5c, 0x3b, 0x8e, 0x37, 0xeb, 0x77, 0xab, 0xf6, 0x2a, 0x7b, 0x98,
0xbf, 0x44, 0x17, 0x07, 0xdf, 0xa6, 0x96, 0xa2, 0xd0, 0xb6, 0xf5, 0xb4, 0x4d, 0xd6, 0xf3, 0x6f,
0x58, 0xfa, 0xd0, 0x3c, 0x47, 0x67, 0x0d, 0xc9, 0x9a, 0xa9, 0x92, 0x47, 0x0c, 0xdf, 0xa2, 0xf3,
0x03, 0xac, 0xf8, 0x82, 0xfe, 0x7b, 0x17, 0xa6, 0x1e, 0x7d, 0xe0, 0x42, 0x57, 0xcb, 0x6f, 0x5b,
0xd7, 0xf9, 0xbe, 0x75, 0x9d, 0x1f, 0x5b, 0xd7, 0xf9, 0xf0, 0xfc, 0x81, 0x65, 0xdd, 0xdb, 0x76,
0x90, 0x3c, 0xca, 0x38, 0x2b, 0xcc, 0xdd, 0xc8, 0x2e, 0xe7, 0xe2, 0x57, 0x00, 0x00, 0x00, 0xff,
0xff, 0x58, 0x7e, 0x26, 0x9f, 0x0e, 0x04, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -446,6 +466,20 @@ func (m *CommitHydratedManifestsRequest) MarshalToSizedBuffer(dAtA []byte) (int,
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.AuthorEmail) > 0 {
i -= len(m.AuthorEmail)
copy(dAtA[i:], m.AuthorEmail)
i = encodeVarintCommit(dAtA, i, uint64(len(m.AuthorEmail)))
i--
dAtA[i] = 0x4a
}
if len(m.AuthorName) > 0 {
i -= len(m.AuthorName)
copy(dAtA[i:], m.AuthorName)
i = encodeVarintCommit(dAtA, i, uint64(len(m.AuthorName)))
i--
dAtA[i] = 0x42
}
if m.DryCommitMetadata != nil {
{
size, err := m.DryCommitMetadata.MarshalToSizedBuffer(dAtA[:i])
@@ -687,6 +721,14 @@ func (m *CommitHydratedManifestsRequest) Size() (n int) {
l = m.DryCommitMetadata.Size()
n += 1 + l + sovCommit(uint64(l))
}
l = len(m.AuthorName)
if l > 0 {
n += 1 + l + sovCommit(uint64(l))
}
l = len(m.AuthorEmail)
if l > 0 {
n += 1 + l + sovCommit(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
@@ -1022,6 +1064,70 @@ func (m *CommitHydratedManifestsRequest) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 8:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthorName", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCommit
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCommit
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthCommit
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.AuthorName = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AuthorEmail", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCommit
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCommit
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthCommit
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.AuthorEmail = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipCommit(dAtA[iNdEx:])

View File

@@ -272,16 +272,19 @@ func (s *Service) initGitClient(logCtx *log.Entry, r *apiclient.CommitHydratedMa
// cleanupOrLog()
// return nil, "", nil, fmt.Errorf("failed to get github app info: %w", err)
// }
var authorName, authorEmail string
// Use author name and email from request, defaulting to "Argo CD" if not provided
authorName := r.AuthorName
if authorName == "" {
authorName = "Argo CD"
}
authorEmail := r.AuthorEmail
if authorEmail == "" {
logCtx.Warnf("Author email not available, using 'argo-cd@example.com'.")
authorEmail = "argo-cd@example.com"
}
logCtx.Debugf("Author config: request name='%s', request email='%s', final name='%s', final email='%s'",
r.AuthorName, r.AuthorEmail, authorName, authorEmail)
logCtx.Debugf("Setting author %s <%s>", authorName, authorEmail)
_, err = gitClient.SetAuthor(authorName, authorEmail)
if err != nil {

View File

@@ -20,6 +20,10 @@ message CommitHydratedManifestsRequest {
repeated PathDetails paths = 6;
// DryCommitMetadata contains metadata about the DRY commit, such as the author and committer.
github.com.argoproj.argo_cd.v3.pkg.apis.application.v1alpha1.RevisionMetadata dryCommitMetadata = 7;
// AuthorName is the author name to use for the commit. If empty, defaults to "Argo CD".
string authorName = 8;
// AuthorEmail is the author email to use for the commit. If empty, defaults to "argo-cd@example.com".
string authorEmail = 9;
}
// PathDetails holds information about hydrated manifests to be written to a particular path in the hydrated manifests

View File

@@ -90,6 +90,73 @@ func Test_CommitHydratedManifests(t *testing.T) {
assert.ErrorIs(t, err, assert.AnError)
})
t.Run("custom author name and email configured", func(t *testing.T) {
t.Parallel()
service, mockRepoClientFactory := newServiceWithMocks(t)
mockGitClient := gitmocks.NewClient(t)
mockGitClient.On("Init").Return(nil).Once()
mockGitClient.On("Fetch", mock.Anything, mock.Anything).Return(nil).Once()
mockGitClient.On("SetAuthor", "Custom Author", "custom@example.com").Return("", nil).Once()
mockGitClient.On("CheckoutOrOrphan", "env/test", false).Return("", nil).Once()
mockGitClient.On("CheckoutOrNew", "main", "env/test", false).Return("", nil).Once()
mockGitClient.On("GetCommitNote", mock.Anything, mock.Anything).Return("", fmt.Errorf("test %w", git.ErrNoNoteFound)).Once()
mockGitClient.On("AddAndPushNote", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
mockGitClient.On("CommitSHA").Return("custom-author-sha", nil).Once()
mockRepoClientFactory.On("NewClient", mock.Anything, mock.Anything).Return(mockGitClient, nil).Once()
requestWithCustomAuthor := &apiclient.CommitHydratedManifestsRequest{
Repo: validRequest.Repo,
SyncBranch: validRequest.SyncBranch,
TargetBranch: validRequest.TargetBranch,
DrySha: validRequest.DrySha,
CommitMessage: validRequest.CommitMessage,
Paths: validRequest.Paths,
AuthorName: "Custom Author",
AuthorEmail: "custom@example.com",
}
resp, err := service.CommitHydratedManifests(t.Context(), requestWithCustomAuthor)
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "custom-author-sha", resp.HydratedSha)
})
t.Run("custom author email only configured", func(t *testing.T) {
t.Parallel()
service, mockRepoClientFactory := newServiceWithMocks(t)
mockGitClient := gitmocks.NewClient(t)
mockGitClient.On("Init").Return(nil).Once()
mockGitClient.On("Fetch", mock.Anything, mock.Anything).Return(nil).Once()
// When only email is provided, name defaults to "Argo CD"
mockGitClient.On("SetAuthor", "Argo CD", "custom@example.com").Return("", nil).Once()
mockGitClient.On("CheckoutOrOrphan", "env/test", false).Return("", nil).Once()
mockGitClient.On("CheckoutOrNew", "main", "env/test", false).Return("", nil).Once()
mockGitClient.On("GetCommitNote", mock.Anything, mock.Anything).Return("", fmt.Errorf("test %w", git.ErrNoNoteFound)).Once()
mockGitClient.On("AddAndPushNote", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
mockGitClient.On("CommitSHA").Return("custom-email-only-sha", nil).Once()
mockRepoClientFactory.On("NewClient", mock.Anything, mock.Anything).Return(mockGitClient, nil).Once()
requestWithEmailOnly := &apiclient.CommitHydratedManifestsRequest{
Repo: validRequest.Repo,
SyncBranch: validRequest.SyncBranch,
TargetBranch: validRequest.TargetBranch,
DrySha: validRequest.DrySha,
CommitMessage: validRequest.CommitMessage,
Paths: validRequest.Paths,
AuthorName: "",
AuthorEmail: "custom@example.com",
}
resp, err := service.CommitHydratedManifests(t.Context(), requestWithEmailOnly)
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "custom-email-only-sha", resp.HydratedSha)
})
t.Run("happy path", func(t *testing.T) {
t.Parallel()

View File

@@ -69,6 +69,12 @@ type Dependencies interface {
// GetHydratorCommitMessageTemplate gets the configured template for rendering commit messages.
GetHydratorCommitMessageTemplate() (string, error)
// GetCommitAuthorName gets the configured commit author name from argocd-cm ConfigMap.
GetCommitAuthorName() (string, error)
// GetCommitAuthorEmail gets the configured commit author email from argocd-cm ConfigMap.
GetCommitAuthorEmail() (string, error)
}
// Hydrator is the main struct that implements the hydration logic. It uses the Dependencies interface to access the
@@ -432,6 +438,16 @@ func (h *Hydrator) hydrate(logCtx *log.Entry, apps []*appv1.Application, project
return targetRevision, "", errors, fmt.Errorf("failed to get hydrator commit templated message: %w", errMsg)
}
// get commit author configuration from argocd-cm
authorName, err := h.dependencies.GetCommitAuthorName()
if err != nil {
return targetRevision, "", errors, fmt.Errorf("failed to get commit author name: %w", err)
}
authorEmail, err := h.dependencies.GetCommitAuthorEmail()
if err != nil {
return targetRevision, "", errors, fmt.Errorf("failed to get commit author email: %w", err)
}
manifestsRequest := commitclient.CommitHydratedManifestsRequest{
Repo: repo,
SyncBranch: syncBranch,
@@ -440,6 +456,8 @@ func (h *Hydrator) hydrate(logCtx *log.Entry, apps []*appv1.Application, project
CommitMessage: commitMessage,
Paths: paths,
DryCommitMetadata: revisionMetadata,
AuthorName: authorName,
AuthorEmail: authorEmail,
}
closer, commitService, err := h.commitClientset.NewCommitServerClient()

View File

@@ -645,6 +645,8 @@ func TestProcessHydrationQueueItem_SuccessfulHydration(t *testing.T) {
rc.EXPECT().GetRevisionMetadata(mock.Anything, mock.Anything).Return(nil, nil).Once()
d.EXPECT().GetWriteCredentials(mock.Anything, "https://example.com/repo", "test-project").Return(nil, nil).Once()
d.EXPECT().GetHydratorCommitMessageTemplate().Return("commit message", nil).Once()
d.EXPECT().GetCommitAuthorName().Return("", nil).Once()
d.EXPECT().GetCommitAuthorEmail().Return("", nil).Once()
cc.EXPECT().CommitHydratedManifests(mock.Anything, mock.Anything).Return(&commitclient.CommitHydratedManifestsResponse{HydratedSha: "def456"}, nil).Once()
h.ProcessHydrationQueueItem(hydrationKey)
@@ -804,6 +806,8 @@ func TestHydrator_hydrate_Success(t *testing.T) {
})
d.EXPECT().GetWriteCredentials(mock.Anything, readRepo.Repo, proj.Name).Return(writeRepo, nil)
d.EXPECT().GetHydratorCommitMessageTemplate().Return("commit message", nil)
d.EXPECT().GetCommitAuthorName().Return("", nil)
d.EXPECT().GetCommitAuthorEmail().Return("", nil)
cc.EXPECT().CommitHydratedManifests(mock.Anything, mock.Anything).Return(&commitclient.CommitHydratedManifestsResponse{HydratedSha: "hydrated123"}, nil).Run(func(_ context.Context, in *commitclient.CommitHydratedManifestsRequest, _ ...grpc.CallOption) {
assert.Equal(t, "commit message", in.CommitMessage)
assert.Equal(t, "hydrated", in.SyncBranch)
@@ -1011,6 +1015,8 @@ func TestHydrator_hydrate_CommitHydratedManifestsError(t *testing.T) {
rc.EXPECT().GetRevisionMetadata(mock.Anything, mock.Anything).Return(&v1alpha1.RevisionMetadata{}, nil)
d.EXPECT().GetWriteCredentials(mock.Anything, mock.Anything, mock.Anything).Return(&v1alpha1.Repository{Repo: "https://example.com/repo"}, nil)
d.EXPECT().GetHydratorCommitMessageTemplate().Return("commit message", nil)
d.EXPECT().GetCommitAuthorName().Return("", nil)
d.EXPECT().GetCommitAuthorEmail().Return("", nil)
cc.EXPECT().CommitHydratedManifests(mock.Anything, mock.Anything).Return(nil, errors.New("commit error"))
logCtx := log.NewEntry(log.StandardLogger())

View File

@@ -81,6 +81,112 @@ func (_c *Dependencies_AddHydrationQueueItem_Call) RunAndReturn(run func(key typ
return _c
}
// GetCommitAuthorEmail provides a mock function for the type Dependencies
func (_mock *Dependencies) GetCommitAuthorEmail() (string, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetCommitAuthorEmail")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func() (string, error)); ok {
return returnFunc()
}
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Dependencies_GetCommitAuthorEmail_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCommitAuthorEmail'
type Dependencies_GetCommitAuthorEmail_Call struct {
*mock.Call
}
// GetCommitAuthorEmail is a helper method to define mock.On call
func (_e *Dependencies_Expecter) GetCommitAuthorEmail() *Dependencies_GetCommitAuthorEmail_Call {
return &Dependencies_GetCommitAuthorEmail_Call{Call: _e.mock.On("GetCommitAuthorEmail")}
}
func (_c *Dependencies_GetCommitAuthorEmail_Call) Run(run func()) *Dependencies_GetCommitAuthorEmail_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Dependencies_GetCommitAuthorEmail_Call) Return(s string, err error) *Dependencies_GetCommitAuthorEmail_Call {
_c.Call.Return(s, err)
return _c
}
func (_c *Dependencies_GetCommitAuthorEmail_Call) RunAndReturn(run func() (string, error)) *Dependencies_GetCommitAuthorEmail_Call {
_c.Call.Return(run)
return _c
}
// GetCommitAuthorName provides a mock function for the type Dependencies
func (_mock *Dependencies) GetCommitAuthorName() (string, error) {
ret := _mock.Called()
if len(ret) == 0 {
panic("no return value specified for GetCommitAuthorName")
}
var r0 string
var r1 error
if returnFunc, ok := ret.Get(0).(func() (string, error)); ok {
return returnFunc()
}
if returnFunc, ok := ret.Get(0).(func() string); ok {
r0 = returnFunc()
} else {
r0 = ret.Get(0).(string)
}
if returnFunc, ok := ret.Get(1).(func() error); ok {
r1 = returnFunc()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Dependencies_GetCommitAuthorName_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCommitAuthorName'
type Dependencies_GetCommitAuthorName_Call struct {
*mock.Call
}
// GetCommitAuthorName is a helper method to define mock.On call
func (_e *Dependencies_Expecter) GetCommitAuthorName() *Dependencies_GetCommitAuthorName_Call {
return &Dependencies_GetCommitAuthorName_Call{Call: _e.mock.On("GetCommitAuthorName")}
}
func (_c *Dependencies_GetCommitAuthorName_Call) Run(run func()) *Dependencies_GetCommitAuthorName_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Dependencies_GetCommitAuthorName_Call) Return(s string, err error) *Dependencies_GetCommitAuthorName_Call {
_c.Call.Return(s, err)
return _c
}
func (_c *Dependencies_GetCommitAuthorName_Call) RunAndReturn(run func() (string, error)) *Dependencies_GetCommitAuthorName_Call {
_c.Call.Return(run)
return _c
}
// GetHydratorCommitMessageTemplate provides a mock function for the type Dependencies
func (_mock *Dependencies) GetHydratorCommitMessageTemplate() (string, error) {
ret := _mock.Called()

View File

@@ -106,3 +106,19 @@ func (ctrl *ApplicationController) GetHydratorCommitMessageTemplate() (string, e
return sourceHydratorCommitMessageKey, nil
}
func (ctrl *ApplicationController) GetCommitAuthorName() (string, error) {
authorName, err := ctrl.settingsMgr.GetCommitAuthorName()
if err != nil {
return "", fmt.Errorf("failed to get commit author name: %w", err)
}
return authorName, nil
}
func (ctrl *ApplicationController) GetCommitAuthorEmail() (string, error) {
authorEmail, err := ctrl.settingsMgr.GetCommitAuthorEmail()
if err != nil {
return "", fmt.Errorf("failed to get commit author email: %w", err)
}
return authorEmail, nil
}

View File

@@ -445,6 +445,16 @@ data:
# We highly recommend that this be set to `true`. The next major release will set the default to be `true`.
application.sync.requireOverridePrivilegeForRevisionSync: "true"
### SourceHydrator commit author name (optional).
# Configures the author name for commits created by the Source Hydrator.
# If not specified, defaults to "Argo CD".
commit.author.name: "Argo CD"
### SourceHydrator commit author email (optional).
# Configures the author email for commits created by the Source Hydrator.
# If not specified, defaults to "argo-cd@example.com".
commit.author.email: "argo-cd@example.com"
### SourceHydrator commit message template.
# This template iterates through the fields in the `.metadata` object,
# and formats them based on their type (map, array, or primitive values).

View File

@@ -416,6 +416,27 @@ data:
{{- end }}
```
## Commit Author Configuration
You can customize the git commit author name and email used by the source hydrator when committing hydrated manifests. This is configured via the `argocd-cm` ConfigMap.
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
commit.author.name: "GitOps Bot"
commit.author.email: "gitops@company.com"
```
**Configuration Keys:**
- `commit.author.name`: The git commit author name (defaults to `"Argo CD"` if not set)
- `commit.author.email`: The git commit author email (defaults to `"argo-cd@example.com"` if not set)
Both values are optional. If only one is configured, the configured value will be used and the other will use its default.
### Credential Templates
Credential templates allow a single credential to be used for multiple repositories. The source hydrator supports credential templates. For example, if you setup credential templates for the URL prefix `https://github.com/argoproj`, these credentials will be used for all repositories with this URL as prefix (e.g. `https://github.com/argoproj/argocd-example-apps`) that do not have their own credentials configured.

View File

@@ -507,8 +507,12 @@ const (
settingUIBannerPositionKey = "ui.bannerposition"
// settingsBinaryUrlsKey designates the key for the argocd binary URLs
settingsBinaryUrlsKey = "help.download"
// settingsApplicationInstanceLabelKey is the key to configure injected app instance label key
// settingsSourceHydratorCommitMessageTemplateKey is the key for the hydrator commit message template
settingsSourceHydratorCommitMessageTemplateKey = "sourceHydrator.commitMessageTemplate"
// settingsCommitAuthorNameKey is the key for the commit author name
settingsCommitAuthorNameKey = "commit.author.name"
// settingsCommitAuthorEmailKey is the key for the commit author email
settingsCommitAuthorEmailKey = "commit.author.email"
// globalProjectsKey designates the key for global project settings
globalProjectsKey = "globalProjects"
// initialPasswordSecretName is the name of the secret that will hold the initial admin password
@@ -1042,6 +1046,22 @@ func (mgr *SettingsManager) GetSourceHydratorCommitMessageTemplate() (string, er
return argoCDCM.Data[settingsSourceHydratorCommitMessageTemplateKey], nil
}
func (mgr *SettingsManager) GetCommitAuthorName() (string, error) {
argoCDCM, err := mgr.getConfigMap()
if err != nil {
return "", err
}
return argoCDCM.Data[settingsCommitAuthorNameKey], nil
}
func (mgr *SettingsManager) GetCommitAuthorEmail() (string, error) {
argoCDCM, err := mgr.getConfigMap()
if err != nil {
return "", err
}
return argoCDCM.Data[settingsCommitAuthorEmailKey], nil
}
func addStatusOverrideToGK(resourceOverrides map[string]v1alpha1.ResourceOverride, groupKind string) {
if val, ok := resourceOverrides[groupKind]; ok {
val.IgnoreDifferences.JSONPointers = append(val.IgnoreDifferences.JSONPointers, "/status")