Fetch before Merge and push the squash commit to origin
This commit is contained in:
@@ -32,6 +32,7 @@ type env struct {
|
||||
events chan api.Event
|
||||
user api.User
|
||||
repo string
|
||||
remote string
|
||||
project api.Project
|
||||
task api.Task
|
||||
|
||||
@@ -61,7 +62,11 @@ func newEnv(t *testing.T) *env {
|
||||
}
|
||||
|
||||
e.repo = scratchRepo(t)
|
||||
e.project = api.Project{Id: store.NewID(), Name: "demo", RepoPath: e.repo, DefaultBranch: "main", Remote: "none", CreatedBy: login, CreatedAt: time.Now().UTC()}
|
||||
e.remote = filepath.Join(t.TempDir(), "remote.git")
|
||||
gitOut(t, e.repo, "init", "--quiet", "--bare", "--initial-branch=main", e.remote)
|
||||
gitOut(t, e.repo, "remote", "add", git.Remote, e.remote)
|
||||
gitOut(t, e.repo, "push", "--quiet", git.Remote, "main")
|
||||
e.project = api.Project{Id: store.NewID(), Name: "demo", RepoPath: e.repo, DefaultBranch: "main", Remote: e.remote, CreatedBy: login, CreatedAt: time.Now().UTC()}
|
||||
err = e.store.CreateProject(ctx, e.project)
|
||||
if err != nil {
|
||||
t.Fatalf("create project: %v", err)
|
||||
@@ -597,7 +602,10 @@ func TestMergeResolvesConflict(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("merge: %v", err)
|
||||
}
|
||||
e.waitFor("task merged", func(ev api.Event) bool { return ev.Kind == "task.merged" })
|
||||
merged := e.waitFor("task merged", func(ev api.Event) bool { return ev.Kind == "task.merged" })
|
||||
if pushed := merged[len(merged)-1].Payload["pushed"]; pushed != "origin/main" {
|
||||
t.Fatalf("task.merged pushed %v, want origin/main", pushed)
|
||||
}
|
||||
|
||||
task, err := e.store.GetTask(e.ctx, e.task.Id)
|
||||
if err != nil {
|
||||
@@ -609,6 +617,9 @@ func TestMergeResolvesConflict(t *testing.T) {
|
||||
if head := gitOut(t, e.repo, "rev-parse", "main"); head != *task.MergeCommit {
|
||||
t.Fatalf("main at %s, want merge commit %s", head, *task.MergeCommit)
|
||||
}
|
||||
if head := gitOut(t, e.remote, "rev-parse", "main"); head != *task.MergeCommit {
|
||||
t.Fatalf("remote main at %s, want merge commit %s", head, *task.MergeCommit)
|
||||
}
|
||||
if parent := gitOut(t, e.repo, "rev-parse", "main^"); parent != moved {
|
||||
t.Fatalf("merge commit parent %s, want %s", parent, moved)
|
||||
}
|
||||
@@ -635,6 +646,63 @@ func TestMergeResolvesConflict(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeFetchesMovedRemote(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := newEnv(t)
|
||||
m := e.manager()
|
||||
|
||||
_, err := m.Create(e.ctx, e.task, e.user, `Create a file task.txt containing exactly the word task.`, testModel)
|
||||
if err != nil {
|
||||
t.Fatalf("create session: %v", err)
|
||||
}
|
||||
e.waitIdle()
|
||||
|
||||
other := t.TempDir()
|
||||
gitOut(t, other, "clone", "--quiet", e.remote, ".")
|
||||
err = os.WriteFile(filepath.Join(other, "elsewhere.txt"), []byte("elsewhere\n"), 0o644)
|
||||
if err != nil {
|
||||
t.Fatalf("write elsewhere.txt: %v", err)
|
||||
}
|
||||
gitOut(t, other, "add", "elsewhere.txt")
|
||||
gitOut(t, other, "commit", "--message", "pushed elsewhere")
|
||||
gitOut(t, other, "push", "--quiet", "origin", "main")
|
||||
moved := gitOut(t, other, "rev-parse", "HEAD")
|
||||
|
||||
_, err = m.Merge(e.ctx, e.task, e.project, e.user)
|
||||
if err != nil {
|
||||
t.Fatalf("merge: %v", err)
|
||||
}
|
||||
events := e.waitFor("task merged", func(ev api.Event) bool { return ev.Kind == "task.merged" })
|
||||
|
||||
for _, ev := range events {
|
||||
if ev.Kind == "inbox.created" && ev.Payload["kind"] == string(api.SessionError) {
|
||||
t.Fatalf("merge filed an error: %v", ev.Payload)
|
||||
}
|
||||
}
|
||||
|
||||
task, err := e.store.GetTask(e.ctx, e.task.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("get task: %v", err)
|
||||
}
|
||||
if task.Status != api.TaskStatusMerged || task.MergeCommit == nil {
|
||||
t.Fatalf("task after merge %+v", task)
|
||||
}
|
||||
for _, dir := range []string{e.repo, e.remote} {
|
||||
if head := gitOut(t, dir, "rev-parse", "main"); head != *task.MergeCommit {
|
||||
t.Fatalf("%s main at %s, want merge commit %s", dir, head, *task.MergeCommit)
|
||||
}
|
||||
}
|
||||
if parent := gitOut(t, e.remote, "rev-parse", "main^"); parent != moved {
|
||||
t.Fatalf("merge commit parent %s, want the moved remote head %s", parent, moved)
|
||||
}
|
||||
for _, f := range []string{"task.txt", "elsewhere.txt"} {
|
||||
_, err = os.Stat(filepath.Join(e.repo, f))
|
||||
if err != nil {
|
||||
t.Fatalf("%s missing from main after merge: %v", f, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateTaskTool(t *testing.T) {
|
||||
t.Parallel()
|
||||
e := newEnv(t)
|
||||
|
||||
@@ -45,6 +45,11 @@ func (m *Manager) Merge(ctx context.Context, task api.Task, project api.Project,
|
||||
return api.AgentSession{}, ErrNoGitIdentity
|
||||
}
|
||||
|
||||
err := git.FetchFastForward(project.RepoPath, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
m.merges[task.Id] = user.Login
|
||||
m.mu.Unlock()
|
||||
@@ -74,7 +79,7 @@ func (m *Manager) finishMerge(l *live, login string) {
|
||||
}
|
||||
|
||||
slog.Error("finish merge", "task", l.taskID, "err", err)
|
||||
m.fileError(l, "The agent merged the default branch in, but squashing onto it failed: "+err.Error())
|
||||
m.fileError(l, "Merging the task failed: "+err.Error())
|
||||
err = m.Deliver(ctx, l.id)
|
||||
if err != nil {
|
||||
slog.Error("deliver inbox responses", "session", l.id, "err", err)
|
||||
@@ -95,7 +100,7 @@ func (m *Manager) squashAndClose(ctx context.Context, taskID, login string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
commit, err := git.SquashMerge(project.RepoPath, task.WorktreePath, project.DefaultBranch, task.MergeTitle, []string{
|
||||
commit, err := git.SquashCommit(project.RepoPath, task.WorktreePath, project.DefaultBranch, task.MergeTitle, []string{
|
||||
"GIT_AUTHOR_NAME=" + user.GitName,
|
||||
"GIT_AUTHOR_EMAIL=" + user.GitEmail,
|
||||
"GIT_COMMITTER_NAME=" + user.GitName,
|
||||
@@ -105,12 +110,25 @@ func (m *Manager) squashAndClose(ctx context.Context, taskID, login string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
pushed := ""
|
||||
if commit != "" {
|
||||
err = git.Push(project.RepoPath, commit, project.DefaultBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pushed = git.Remote + "/" + project.DefaultBranch
|
||||
err = git.FastForward(project.RepoPath, commit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pushed %s to %s, but fast-forwarding the project checkout failed: %w", commit, pushed, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = m.RemoveTask(ctx, task, project, login)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = m.store.MergeTask(ctx, taskID, commit, login)
|
||||
_, err = m.store.MergeTask(ctx, taskID, commit, pushed, login)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user