Add Co-authored-by trailers to Merge's squash commit
This commit is contained in:
@@ -407,11 +407,16 @@ func (e *env) openQuestions(sessionID string) []api.InboxItem {
|
||||
}
|
||||
|
||||
func (e *env) respond(m *agents.Manager, item api.InboxItem, typ api.InboxResponseType, option, text string, state api.InboxState) {
|
||||
e.t.Helper()
|
||||
e.respondAs(m, login, item, typ, option, text, state)
|
||||
}
|
||||
|
||||
func (e *env) respondAs(m *agents.Manager, user string, item api.InboxItem, typ api.InboxResponseType, option, text string, state api.InboxState) {
|
||||
e.t.Helper()
|
||||
resp := api.InboxResponse{
|
||||
Id: store.NewID(),
|
||||
ItemId: item.Id,
|
||||
User: login,
|
||||
User: user,
|
||||
Type: typ,
|
||||
Attachments: []api.Attachment{},
|
||||
CreatedAt: time.Now().UTC(),
|
||||
@@ -423,7 +428,7 @@ func (e *env) respond(m *agents.Manager, item api.InboxItem, typ api.InboxRespon
|
||||
resp.Text = &text
|
||||
}
|
||||
|
||||
err := e.store.Respond(e.ctx, login, []store.Resolution{{Response: resp, State: state}})
|
||||
err := e.store.Respond(e.ctx, user, []store.Resolution{{Response: resp, State: state}})
|
||||
if err != nil {
|
||||
e.t.Fatalf("respond: %v", err)
|
||||
}
|
||||
@@ -672,6 +677,105 @@ func TestMergeResolvesConflict(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeCoAuthors(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := newEnv(t)
|
||||
m := e.manager()
|
||||
|
||||
people := []struct{ login, name, gitName, gitEmail string }{
|
||||
{"carol@example.com", "Carol", "Carol Test", "carol@byre.invalid"},
|
||||
{"bob@example.com", "Bob", "Bob Test", "bob@byre.invalid"},
|
||||
{"dave@example.com", "Dave", "", ""},
|
||||
}
|
||||
const dismisser = "erin@example.com"
|
||||
_, err := e.store.EnsureUser(e.ctx, dismisser, "Erin")
|
||||
if err != nil {
|
||||
t.Fatalf("ensure user: %v", err)
|
||||
}
|
||||
_, err = e.store.UpdateUserGit(e.ctx, dismisser, "Erin Test", "erin@byre.invalid")
|
||||
if err != nil {
|
||||
t.Fatalf("update user: %v", err)
|
||||
}
|
||||
for _, p := range people {
|
||||
_, err := e.store.EnsureUser(e.ctx, p.login, p.name)
|
||||
if err != nil {
|
||||
t.Fatalf("ensure user: %v", err)
|
||||
}
|
||||
if p.gitName != "" {
|
||||
_, err = e.store.UpdateUserGit(e.ctx, p.login, p.gitName, p.gitEmail)
|
||||
if err != nil {
|
||||
t.Fatalf("update user: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sess, err := m.Create(e.ctx, e.task, e.user, colorPrompt, testModel)
|
||||
if err != nil {
|
||||
t.Fatalf("create session: %v", err)
|
||||
}
|
||||
e.waitIdle()
|
||||
questions := e.openQuestions(sess.Id)
|
||||
if len(questions) != 1 {
|
||||
t.Fatalf("want one open question, got %+v", questions)
|
||||
}
|
||||
options, _ := questions[0].Payload["options"].([]any)
|
||||
e.respondAs(m, people[0].login, questions[0], api.Option, fmt.Sprint(options[0]), "", api.Answered)
|
||||
e.waitIdle()
|
||||
|
||||
for _, p := range people[1:] {
|
||||
_, err = m.Send(e.ctx, sess.Id, p.login, "Reply with the single word ok.", "")
|
||||
if err != nil {
|
||||
t.Fatalf("send as %s: %v", p.login, err)
|
||||
}
|
||||
e.waitIdle()
|
||||
}
|
||||
|
||||
reports, err := e.store.ListInbox(e.ctx, store.InboxFilter{TaskID: e.task.Id, State: api.Open})
|
||||
if err != nil || len(reports) == 0 {
|
||||
t.Fatalf("want open reports to dismiss: %+v %v", reports, err)
|
||||
}
|
||||
e.respondAs(m, dismisser, reports[0], api.Dismiss, "", "", api.Dismissed)
|
||||
|
||||
coAuthors, err := m.CoAuthors(e.ctx, e.task.Id, login)
|
||||
if err != nil {
|
||||
t.Fatalf("co-authors: %v", err)
|
||||
}
|
||||
got := []string{}
|
||||
for _, p := range coAuthors {
|
||||
got = append(got, p.Login+"|"+p.GitName+"|"+p.GitEmail)
|
||||
}
|
||||
want := []string{}
|
||||
for _, p := range people {
|
||||
want = append(want, p.login+"|"+p.gitName+"|"+p.gitEmail)
|
||||
}
|
||||
if strings.Join(got, ",") != strings.Join(want, ",") {
|
||||
t.Fatalf("co-authors %v, want %v", got, want)
|
||||
}
|
||||
|
||||
_, err = m.Merge(e.ctx, e.task, e.project, e.user)
|
||||
if err != nil {
|
||||
t.Fatalf("merge: %v", err)
|
||||
}
|
||||
e.waitFor("task merged", func(ev api.Event) bool { return ev.Kind == "task.merged" })
|
||||
|
||||
message := gitOut(t, e.repo, "log", "-1", "--format=%B")
|
||||
t.Logf("merge commit message:\n%s", message)
|
||||
if author := gitOut(t, e.repo, "log", "-1", "--format=%an <%ae>|%cn <%ce>"); author != gitName+" <"+gitEmail+">|"+gitName+" <"+gitEmail+">" {
|
||||
t.Fatalf("merge commit author|committer %s, want %s", author, gitName)
|
||||
}
|
||||
cmd := exec.Command("git", "interpret-trailers", "--parse")
|
||||
cmd.Stdin = strings.NewReader(message)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
t.Fatalf("interpret-trailers: %v", err)
|
||||
}
|
||||
trailers := strings.TrimSpace(string(out))
|
||||
wantTrailers := "Co-authored-by: Carol Test <carol@byre.invalid>\nCo-authored-by: Bob Test <bob@byre.invalid>"
|
||||
if trailers != wantTrailers {
|
||||
t.Fatalf("trailers:\n%s\nwant:\n%s", trailers, wantTrailers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFetchesMovedRemote(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := newEnv(t)
|
||||
|
||||
@@ -100,7 +100,12 @@ func (m *Manager) squashAndClose(ctx context.Context, taskID, login string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
commit, err := git.SquashCommit(project.RepoPath, task.WorktreePath, project.DefaultBranch, task.MergeTitle, []string{
|
||||
coAuthors, err := m.CoAuthors(ctx, taskID, login)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commit, err := git.SquashCommit(project.RepoPath, task.WorktreePath, project.DefaultBranch, squashMessage(task.MergeTitle, coAuthors), []string{
|
||||
"GIT_AUTHOR_NAME=" + user.GitName,
|
||||
"GIT_AUTHOR_EMAIL=" + user.GitEmail,
|
||||
"GIT_COMMITTER_NAME=" + user.GitName,
|
||||
@@ -132,6 +137,35 @@ func (m *Manager) squashAndClose(ctx context.Context, taskID, login string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Manager) CoAuthors(ctx context.Context, taskID, login string) ([]api.Participant, error) {
|
||||
participants, err := m.store.Participants(ctx, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coAuthors := []api.Participant{}
|
||||
for _, p := range participants {
|
||||
if p.Login != login {
|
||||
coAuthors = append(coAuthors, p)
|
||||
}
|
||||
}
|
||||
return coAuthors, nil
|
||||
}
|
||||
|
||||
func squashMessage(title string, coAuthors []api.Participant) string {
|
||||
trailers := []string{}
|
||||
for _, p := range coAuthors {
|
||||
if p.GitName == "" || p.GitEmail == "" {
|
||||
continue
|
||||
}
|
||||
trailers = append(trailers, fmt.Sprintf("Co-authored-by: %s <%s>", p.GitName, p.GitEmail))
|
||||
}
|
||||
if len(trailers) == 0 {
|
||||
return title
|
||||
}
|
||||
return title + "\n\n" + strings.Join(trailers, "\n")
|
||||
}
|
||||
|
||||
func (m *Manager) RemoveTask(ctx context.Context, task api.Task, project api.Project, actor string) error {
|
||||
err := m.StopTask(ctx, task.Id, actor)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user