fix(hydrator): split commit subject and body, make author one string (#23389)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
This commit is contained in:
Michael Crenshaw 2025-06-13 17:00:29 -04:00 committed by GitHub
parent 8c6f35bf5c
commit 96fd06165e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 966 additions and 1231 deletions

23
assets/swagger.json generated
View file

@ -8031,14 +8031,15 @@
"type": "object",
"properties": {
"author": {
"$ref": "#/definitions/v1alpha1CommitMetadataAuthor"
"description": "Author is the author of the commit, i.e. `git show -s --format=%an <%ae>`.\nMust be formatted according to RFC 5322 (mail.Address.String()).\nComes from the Argocd-reference-commit-author trailer.",
"type": "string"
},
"body": {
"description": "Body is the commit message body.\nComes from the Argocd-reference-commit-body trailer.",
"description": "Body is the commit message body minus the subject line, i.e. `git show -s --format=%b`.\nComes from the Argocd-reference-commit-body trailer.",
"type": "string"
},
"date": {
"description": "Date is the date of the commit, formatted as by `git show -s --format=%aI`.\nComes from the Argocd-reference-commit-date trailer.",
"description": "Date is the date of the commit, formatted as by `git show -s --format=%aI` (RFC 3339).\nIt can also be an empty string if the date is unknown.\nComes from the Argocd-reference-commit-date trailer.",
"type": "string"
},
"repoUrl": {
@ -8050,21 +8051,7 @@
"type": "string"
},
"subject": {
"description": "Subject is the commit message subject.\nComes from the Argocd-reference-commit-subject trailer.",
"type": "string"
}
}
},
"v1alpha1CommitMetadataAuthor": {
"description": "CommitMetadataAuthor contains information about the author of a commit.",
"type": "object",
"properties": {
"email": {
"description": "Email is the email of the author.\nComes from the Argocd-reference-commit-author-email trailer.",
"type": "string"
},
"name": {
"description": "Name is the name of the author.\nComes from the Argocd-reference-commit-author-name trailer.",
"description": "Subject is the commit message subject line, i.e. `git show -s --format=%s`.\nComes from the Argocd-reference-commit-subject trailer.",
"type": "string"
}
}

View file

@ -212,12 +212,15 @@ func (s *Service) initGitClient(logCtx *log.Entry, r *apiclient.CommitHydratedMa
}
type hydratorMetadataFile struct {
RepoURL string `json:"repoURL,omitempty"`
DrySHA string `json:"drySha,omitempty"`
Commands []string `json:"commands,omitempty"`
Author string `json:"author,omitempty"`
Date string `json:"date,omitempty"`
Message string `json:"message,omitempty"`
RepoURL string `json:"repoURL,omitempty"`
DrySHA string `json:"drySha,omitempty"`
Commands []string `json:"commands,omitempty"`
Author string `json:"author,omitempty"`
Date string `json:"date,omitempty"`
// Subject is the subject line of the DRY commit message, i.e. `git show --format=%s`.
Subject string `json:"message,omitempty"`
// Body is the body of the DRY commit message, excluding the subject line, i.e. `git show --format=%b`.
Body string `json:"body,omitempty"`
References []v1alpha1.RevisionReference `json:"references,omitempty"`
}
@ -239,7 +242,7 @@ git checkout {{ .DrySHA }}
{{ range $ref := .References -}}
{{ if $ref.Commit -}}
* [{{ $ref.Commit.SHA | mustRegexFind "[0-9a-f]+" | trunc 7 }}]({{ $ref.Commit.RepoURL }}): {{ $ref.Commit.Subject }} ({{ $ref.Commit.Author.Name }} <{{ $ref.Commit.Author.Email }}>)
* [{{ $ref.Commit.SHA | mustRegexFind "[0-9a-f]+" | trunc 7 }}]({{ $ref.Commit.RepoURL }}): {{ $ref.Commit.Subject }} ({{ $ref.Commit.Author }})
{{ end -}}
{{ end -}}
{{ end -}}`

View file

@ -46,8 +46,10 @@ func WriteForPaths(root *os.Root, repoUrl, drySha string, dryCommitMetadata *app
references = dryCommitMetadata.References
}
subject, body, _ := strings.Cut(message, "\n\n")
// Write the top-level readme.
err := writeMetadata(root, "", hydratorMetadataFile{DrySHA: drySha, RepoURL: repoUrl, Author: author, Message: message, Date: date, References: references})
err := writeMetadata(root, "", hydratorMetadataFile{DrySHA: drySha, RepoURL: repoUrl, Author: author, Subject: subject, Body: body, Date: date, References: references})
if err != nil {
return fmt.Errorf("failed to write top-level hydrator metadata: %w", err)
}

View file

@ -9,6 +9,7 @@ import (
"os"
"path"
"path/filepath"
"strings"
"testing"
"time"
@ -78,10 +79,7 @@ func TestWriteForPaths(t *testing.T) {
References: []appsv1.RevisionReference{
{
Commit: &appsv1.CommitMetadata{
Author: appsv1.CommitMetadataAuthor{
Name: "test-code-author",
Email: "test-email-author@example.com",
},
Author: "test-code-author <test-email-author@example.com>",
Date: now.Format(time.RFC3339),
Subject: "test-code-subject",
SHA: "test-code-sha",
@ -99,13 +97,16 @@ func TestWriteForPaths(t *testing.T) {
topMetadataBytes, err := os.ReadFile(topMetadataPath)
require.NoError(t, err)
expectedSubject, expectedBody, _ := strings.Cut(metadata.Message, "\n\n")
var topMetadata hydratorMetadataFile
err = json.Unmarshal(topMetadataBytes, &topMetadata)
require.NoError(t, err)
assert.Equal(t, repoURL, topMetadata.RepoURL)
assert.Equal(t, drySha, topMetadata.DrySHA)
assert.Equal(t, metadata.Author, topMetadata.Author)
assert.Equal(t, metadata.Message, topMetadata.Message)
assert.Equal(t, expectedSubject, topMetadata.Subject)
assert.Equal(t, expectedBody, topMetadata.Body)
assert.Equal(t, metadata.Date.Format(time.RFC3339), topMetadata.Date)
assert.Equal(t, metadata.References, topMetadata.References)
@ -174,10 +175,7 @@ func TestWriteReadme(t *testing.T) {
References: []appsv1.RevisionReference{
{
Commit: &appsv1.CommitMetadata{
Author: appsv1.CommitMetadataAuthor{
Name: "test-code-author",
Email: "test@example.com",
},
Author: "test-code-author <test@example.com>",
Date: time.Now().Format(time.RFC3339),
Subject: "test-code-subject",
SHA: sha,

View file

@ -158,7 +158,8 @@ code commit.
```shell
git commit -m "Bump image to v1.2.3" \
--trailer "Argocd-reference-commit-author-name: Author Name" \
# Must be an RFC 5322 name
--trailer "Argocd-reference-commit-author: Author Name <author@example.com>" \
# Must be a valid email address per RFC 5322
--trailer "Argocd-reference-commit-author-email: author@example.com" \
# Must be a hex string 5-40 characters long
@ -186,8 +187,7 @@ cd repo
# <cusom build logic here>
# Get the commit information
authorName=$(git show -s --format='%an')
authorEmail=$(git show -s --format='%ae')
author=$(git show -s --format="%an <%ae>")
sha=$(git rev-parse HEAD)
subject=$(git show -s --format='%s')
body=$(git show -s --format='%b')
@ -204,8 +204,7 @@ cd deployment-repo
# Commit the changes with the commit trailers
git commit -m "Bump image to v1.2.3" \
--trailer "Argocd-reference-commit-author-name: $authorName" \
--trailer "Argocd-reference-commit-author-email: $authorEmail" \
--trailer "Argocd-reference-commit-author: $author" \
--trailer "Argocd-reference-commit-sha: $sha" \
--trailer "Argocd-reference-commit-subject: $subject" \
--trailer "Argocd-reference-commit-body: $jsonbody" \

File diff suppressed because it is too large Load diff

View file

@ -937,18 +937,21 @@ message Command {
// CommitMetadata contains metadata about a commit that is related in some way to another commit.
message CommitMetadata {
// Author is the author of the commit.
optional CommitMetadataAuthor author = 1;
// Author is the author of the commit, i.e. `git show -s --format=%an <%ae>`.
// Must be formatted according to RFC 5322 (mail.Address.String()).
// Comes from the Argocd-reference-commit-author trailer.
optional string author = 1;
// Date is the date of the commit, formatted as by `git show -s --format=%aI`.
// Date is the date of the commit, formatted as by `git show -s --format=%aI` (RFC 3339).
// It can also be an empty string if the date is unknown.
// Comes from the Argocd-reference-commit-date trailer.
optional string date = 2;
// Subject is the commit message subject.
// Subject is the commit message subject line, i.e. `git show -s --format=%s`.
// Comes from the Argocd-reference-commit-subject trailer.
optional string subject = 3;
// Body is the commit message body.
// Body is the commit message body minus the subject line, i.e. `git show -s --format=%b`.
// Comes from the Argocd-reference-commit-body trailer.
optional string body = 4;
@ -963,17 +966,6 @@ message CommitMetadata {
optional string repoUrl = 6;
}
// CommitMetadataAuthor contains information about the author of a commit.
message CommitMetadataAuthor {
// Name is the name of the author.
// Comes from the Argocd-reference-commit-author-name trailer.
optional string name = 1;
// Email is the email of the author.
// Comes from the Argocd-reference-commit-author-email trailer.
optional string email = 2;
}
// ComparedTo contains application source and target which was used for resources comparison
message ComparedTo {
// Source is a reference to the application's source used for comparison

View file

@ -1569,27 +1569,20 @@ type SyncStrategyHook struct {
SyncStrategyApply `json:",inline" protobuf:"bytes,1,opt,name=syncStrategyApply"`
}
// CommitMetadataAuthor contains information about the author of a commit.
type CommitMetadataAuthor struct {
// Name is the name of the author.
// Comes from the Argocd-reference-commit-author-name trailer.
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
// Email is the email of the author.
// Comes from the Argocd-reference-commit-author-email trailer.
Email string `json:"email,omitempty" protobuf:"bytes,2,opt,name=email"`
}
// CommitMetadata contains metadata about a commit that is related in some way to another commit.
type CommitMetadata struct {
// Author is the author of the commit.
Author CommitMetadataAuthor `json:"author,omitempty" protobuf:"bytes,1,opt,name=author"`
// Date is the date of the commit, formatted as by `git show -s --format=%aI`.
// Author is the author of the commit, i.e. `git show -s --format=%an <%ae>`.
// Must be formatted according to RFC 5322 (mail.Address.String()).
// Comes from the Argocd-reference-commit-author trailer.
Author string `json:"author,omitempty" protobuf:"bytes,1,opt,name=author"`
// Date is the date of the commit, formatted as by `git show -s --format=%aI` (RFC 3339).
// It can also be an empty string if the date is unknown.
// Comes from the Argocd-reference-commit-date trailer.
Date string `json:"date,omitempty" protobuf:"bytes,2,opt,name=date"`
// Subject is the commit message subject.
// Subject is the commit message subject line, i.e. `git show -s --format=%s`.
// Comes from the Argocd-reference-commit-subject trailer.
Subject string `json:"subject,omitempty" protobuf:"bytes,3,opt,name=subject"`
// Body is the commit message body.
// Body is the commit message body minus the subject line, i.e. `git show -s --format=%b`.
// Comes from the Argocd-reference-commit-body trailer.
Body string `json:"body,omitempty" protobuf:"bytes,4,opt,name=body"`
// SHA is the commit hash.

View file

@ -1829,7 +1829,6 @@ func (in *Command) DeepCopy() *Command {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CommitMetadata) DeepCopyInto(out *CommitMetadata) {
*out = *in
out.Author = in.Author
return
}
@ -1843,22 +1842,6 @@ func (in *CommitMetadata) DeepCopy() *CommitMetadata {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CommitMetadataAuthor) DeepCopyInto(out *CommitMetadataAuthor) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommitMetadataAuthor.
func (in *CommitMetadataAuthor) DeepCopy() *CommitMetadataAuthor {
if in == nil {
return nil
}
out := new(CommitMetadataAuthor)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ComparedTo) DeepCopyInto(out *ComparedTo) {
*out = *in

View file

@ -2445,10 +2445,7 @@ func (s *Service) GetRevisionMetadata(_ context.Context, q *apiclient.RepoServer
relatedRevisions[i] = v1alpha1.RevisionReference{
Commit: &v1alpha1.CommitMetadata{
Author: v1alpha1.CommitMetadataAuthor{
Name: m.References[i].Commit.Author.Name,
Email: m.References[i].Commit.Author.Email,
},
Author: m.References[i].Commit.Author.String(),
Date: m.References[i].Commit.Date,
Subject: m.References[i].Commit.Subject,
Body: m.References[i].Commit.Body,

View file

@ -8,6 +8,7 @@ import (
"fmt"
goio "io"
"io/fs"
"net/mail"
"os"
"os/exec"
"path"
@ -1695,9 +1696,9 @@ func TestGetRevisionMetadata(t *testing.T) {
References: []git.RevisionReference{
{
Commit: &git.CommitMetadata{
Author: git.CommitMetadataAuthor{
Name: "test-name",
Email: "test-email",
Author: mail.Address{
Name: "test-name",
Address: "test-email@example.com",
},
Date: now.Format(time.RFC3339),
Subject: "test-subject",

View file

@ -45,28 +45,19 @@ import (
var ErrInvalidRepoURL = errors.New("repo URL is invalid")
// CommitMetadataAuthor contains information about the author of a commit.
type CommitMetadataAuthor struct {
// Name is the name of the author.
// Comes from the Argocd-reference-commit-author-name trailer.
Name string
// Email is the email of the author.
// Comes from the Argocd-reference-commit-author-email trailer.
Email string
}
// CommitMetadata contains metadata about a commit that is related in some way to another commit.
type CommitMetadata struct {
// Author is the author of the commit.
// Comes from the Argocd-reference-commit-author-* trailers.
Author CommitMetadataAuthor
// Comes from the Argocd-reference-commit-author trailer.
Author mail.Address
// Date is the date of the commit, formatted as by `git show -s --format=%aI`.
// May be an empty string if the date is unknown.
// Comes from the Argocd-reference-commit-date trailer.
Date string
// Subject is the commit message subject.
// Subject is the commit message subject, i.e. `git show -s --format=%s`.
// Comes from the Argocd-reference-commit-subject trailer.
Subject string
// Body is the full commit message body, formatted as a JSON string.
// Body is the commit message body, excluding the subject, i.e. `git show -s --format=%b`.
// Comes from the Argocd-reference-commit-body trailer.
Body string
// SHA is the commit hash.
@ -74,8 +65,8 @@ type CommitMetadata struct {
SHA string
// RepoURL is the URL of the repository where the commit is located.
// Comes from the Argocd-reference-commit-repourl trailer.
// This value is not validated and should not be used to construct UI links unless it is properly
// validated and/or sanitized first.
// This value is not validated beyond confirming that it's a URL, and it should not be used to construct UI links
// unless it is properly validated and/or sanitized first.
RepoURL string
}
@ -853,23 +844,21 @@ func getReferences(logCtx *log.Entry, commitMessageBody string) []RevisionRefere
continue
}
relatedCommit.RepoURL = trailerValue
case "Argocd-reference-commit-author-name":
relatedCommit.Author.Name = trailerValue
case "Argocd-reference-commit-author-email":
_, err := mail.ParseAddress(trailerValue)
if err != nil {
case "Argocd-reference-commit-author":
address, err := mail.ParseAddress(trailerValue)
if err != nil || address == nil {
logCtx.Errorf("failed to parse author email %q: %v", truncate(trailerValue), err)
continue
}
relatedCommit.Author.Email = trailerValue
relatedCommit.Author = *address
case "Argocd-reference-commit-date":
// Validate that it's the correct date format.
_, err := time.Parse(time.RFC3339, trailerValue)
t, err := time.Parse(time.RFC3339, trailerValue)
if err != nil {
logCtx.Errorf("failed to parse date %q with RFC3339 format: %v", truncate(trailerValue), err)
continue
}
relatedCommit.Date = trailerValue
relatedCommit.Date = t.Format(time.RFC3339)
case "Argocd-reference-commit-subject":
relatedCommit.Subject = trailerValue
case "Argocd-reference-commit-body":

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"net/mail"
"os"
"os/exec"
"path"
@ -502,8 +503,7 @@ func Test_nativeGitClient_RevisionMetadata(t *testing.T) {
(°°)
`, "-a",
"--trailer", "Argocd-reference-commit-author-name: test-author",
"--trailer", "Argocd-reference-commit-author-email: test@email.com",
"--trailer", "Argocd-reference-commit-author: test-author <test@email.com>",
"--trailer", "Argocd-reference-commit-date: "+now.Format(time.RFC3339),
"--trailer", "Argocd-reference-commit-subject: chore: make a change",
"--trailer", "Argocd-reference-commit-sha: abc123",
@ -521,8 +521,7 @@ func Test_nativeGitClient_RevisionMetadata(t *testing.T) {
(°°)
Argocd-reference-commit-author-name: test-author
Argocd-reference-commit-author-email: test@email.com
Argocd-reference-commit-author: test-author <test@email.com>
Argocd-reference-commit-date: %s
Argocd-reference-commit-subject: chore: make a change
Argocd-reference-commit-sha: abc123
@ -530,9 +529,9 @@ Argocd-reference-commit-repourl: https://git.example.com/test/repo.git`, now.For
References: []RevisionReference{
{
Commit: &CommitMetadata{
Author: CommitMetadataAuthor{
Name: "test-author",
Email: "test@email.com",
Author: mail.Address{
Name: "test-author",
Address: "test@email.com",
},
Date: now.Format(time.RFC3339),
Subject: "chore: make a change",
@ -1107,6 +1106,8 @@ func (m *mockCreds) GetUserInfo(_ context.Context) (string, string, error) {
func Test_getReferences(t *testing.T) {
t.Parallel()
now := time.Now()
tests := []struct {
name string
input string
@ -1123,7 +1124,7 @@ func Test_getReferences(t *testing.T) {
Argocd-reference-commit-date: invalid-date
Argocd-reference-commit-sha: xyz123
Argocd-reference-commit-body: this isn't json
Argocd-reference-commit-author-email: % not email %
Argocd-reference-commit-author: % not email %
Argocd-reference-commit-bogus:`,
expected: nil,
},
@ -1147,21 +1148,20 @@ Argocd-reference-commit-date: invalid-date`,
},
{
name: "Valid trailers",
input: `Argocd-reference-commit-repourl: https://github.com/org/repo.git
Argocd-reference-commit-author-name: John Doe
Argocd-reference-commit-author-email: john.doe@example.com
Argocd-reference-commit-date: 2023-10-01T12:00:00Z
input: fmt.Sprintf(`Argocd-reference-commit-repourl: https://github.com/org/repo.git
Argocd-reference-commit-author: John Doe <john.doe@example.com>
Argocd-reference-commit-date: %s
Argocd-reference-commit-subject: Fix bug
Argocd-reference-commit-body: "Fix bug\n\nSome: trailer"
Argocd-reference-commit-sha: abc123`,
Argocd-reference-commit-sha: abc123`, now.Format(time.RFC3339)),
expected: []RevisionReference{
{
Commit: &CommitMetadata{
Author: CommitMetadataAuthor{
Name: "John Doe",
Email: "john.doe@example.com",
Author: mail.Address{
Name: "John Doe",
Address: "john.doe@example.com",
},
Date: "2023-10-01T12:00:00Z",
Date: now.Format(time.RFC3339),
Body: "Fix bug\n\nSome: trailer",
Subject: "Fix bug",
SHA: "abc123",