fix: detect workspace root default branch

This commit is contained in:
Aditi Chauhan 2026-07-01 15:12:25 +05:30
parent 5ccaf3afd7
commit c1c1ff7fe2
4 changed files with 145 additions and 0 deletions

View File

@ -174,6 +174,60 @@ func TestOrchestratorManagedPath(t *testing.T) {
})
}
func TestCreateWorkspaceProjectOrchestratorCreatesRootAndChildWorktrees(t *testing.T) {
root := t.TempDir()
parentRepo := gitRepoWithInitialCommit(t, filepath.Join(t.TempDir(), "workspace-root"), "main")
childRepo := gitRepoWithInitialCommit(t, filepath.Join(t.TempDir(), "api"), "main")
ws, err := New(Options{ManagedRoot: root, RepoResolver: StaticRepoResolver{"proj": parentRepo}})
if err != nil {
t.Fatalf("new: %v", err)
}
info, err := ws.CreateWorkspaceProject(context.Background(), ports.WorkspaceProjectConfig{
ProjectID: "proj",
SessionID: "proj-1",
Kind: domain.KindOrchestrator,
SessionPrefix: "proj",
Branch: "ao/proj-1",
RootRepoPath: parentRepo,
BaseBranch: "main",
Repos: []ports.WorkspaceProjectRepoConfig{{
Name: "api",
RelativePath: "api",
RepoPath: childRepo,
BaseBranch: "main",
}},
})
if err != nil {
t.Fatalf("CreateWorkspaceProject: %v", err)
}
wantRoot := filepath.Join(ws.managedRoot, "proj", "orchestrator", "proj-orchestrator")
wantChild := filepath.Join(wantRoot, "api")
wantChildRepo, err := physicalAbs(childRepo)
if err != nil {
t.Fatalf("child physical path: %v", err)
}
if info.Root.Path != wantRoot || info.Root.Branch != "ao/proj-1" {
t.Fatalf("root = %#v, want path %q branch ao/proj-1", info.Root, wantRoot)
}
if len(info.Worktrees) != 2 {
t.Fatalf("worktrees = %d, want root and api: %#v", len(info.Worktrees), info.Worktrees)
}
if info.Worktrees[1].Path != wantChild || info.Worktrees[1].RepoPath != wantChildRepo {
t.Fatalf("child worktree = %#v, want path %q repo %q", info.Worktrees[1], wantChild, wantChildRepo)
}
if out, err := exec.Command("git", "-C", parentRepo, "worktree", "list", "--porcelain").CombinedOutput(); err != nil {
t.Fatalf("root worktree list: %v (%s)", err, out)
} else if !strings.Contains(string(out), wantRoot) {
t.Fatalf("root worktree list missing %q:\n%s", wantRoot, out)
}
if out, err := exec.Command("git", "-C", childRepo, "worktree", "list", "--porcelain").CombinedOutput(); err != nil {
t.Fatalf("child worktree list: %v (%s)", err, out)
} else if !strings.Contains(string(out), wantChild) {
t.Fatalf("child worktree list missing %q:\n%s", wantChild, out)
}
}
func TestCreateReusesRegisteredWorktreeAtExpectedPath(t *testing.T) {
root := t.TempDir()
repo := t.TempDir()
@ -532,3 +586,26 @@ func mkdirFile(dir, name string) error {
}
return os.WriteFile(filepath.Join(dir, name), []byte("data"), 0o644)
}
func gitRepoWithInitialCommit(t *testing.T, dir, branch string) string {
t.Helper()
if out, err := exec.Command("git", "init", "-b", branch, dir).CombinedOutput(); err != nil {
t.Fatalf("git init: %v (%s)", err, out)
}
if out, err := exec.Command("git", "-C", dir, "config", "user.email", "ao@example.com").CombinedOutput(); err != nil {
t.Fatalf("git config email: %v (%s)", err, out)
}
if out, err := exec.Command("git", "-C", dir, "config", "user.name", "AO Test").CombinedOutput(); err != nil {
t.Fatalf("git config name: %v (%s)", err, out)
}
if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("hello\n"), 0o644); err != nil {
t.Fatalf("write readme: %v", err)
}
if out, err := exec.Command("git", "-C", dir, "add", "README.md").CombinedOutput(); err != nil {
t.Fatalf("git add: %v (%s)", err, out)
}
if out, err := exec.Command("git", "-C", dir, "commit", "-m", "initial").CombinedOutput(); err != nil {
t.Fatalf("git commit: %v (%s)", err, out)
}
return dir
}

View File

@ -196,6 +196,11 @@ func (m *Service) Add(ctx context.Context, in AddInput) (Project, error) {
if err != nil {
return Project{}, err
}
if row.Config.DefaultBranch == "" {
if branch := resolveDefaultBranch(path); branch != "" && branch != domain.DefaultBranchName {
row.Config.DefaultBranch = branch
}
}
row.Kind = domain.ProjectKindWorkspace
row.RepoOriginURL = resolveGitOriginURL(path)
if err := m.store.UpsertWorkspaceProject(ctx, row, repos); err != nil {

View File

@ -634,6 +634,30 @@ func TestManager_AddWorkspaceDetectsChildDefaultBranches(t *testing.T) {
}
}
func TestManager_AddWorkspaceDetectsParentDefaultBranch(t *testing.T) {
configureCommitter(t)
ctx := context.Background()
m := newManager(t)
parent := t.TempDir()
gitRepoWithCommitOnBranch(t, parent, "master")
gitRepoWithCommit(t, filepath.Join(parent, "api"))
proj, err := m.Add(ctx, project.AddInput{Path: parent, ProjectID: ptr("ws"), AsWorkspace: true})
if err != nil {
t.Fatalf("Add workspace: %v", err)
}
if proj.DefaultBranch != "master" {
t.Fatalf("workspace parent default branch = %q, want master", proj.DefaultBranch)
}
got, err := m.Get(ctx, "ws")
if err != nil {
t.Fatalf("Get workspace: %v", err)
}
if got.Project == nil || got.Project.DefaultBranch != "master" {
t.Fatalf("stored workspace default branch = %#v, want master", got.Project)
}
}
func TestManager_AddWorkspaceRejectsUncommittedChild(t *testing.T) {
configureCommitter(t)
ctx := context.Background()

View File

@ -679,6 +679,45 @@ func TestSpawn_WorkspaceProjectRecordsRootAndChildWorktrees(t *testing.T) {
}
}
func TestSpawn_WorkspaceProjectOrchestratorUsesOrchestratorPath(t *testing.T) {
st := newFakeStore()
st.projects["mer"] = domain.ProjectRecord{
ID: "mer",
Path: "/repo/mer",
Kind: domain.ProjectKindWorkspace,
Config: testRoleAgents(),
}
st.workspaceRepo["mer"] = []domain.WorkspaceRepoRecord{{Name: "api", RelativePath: "api", DefaultBranch: "main"}}
rt := &fakeRuntime{}
ws := &fakeWorkspace{path: "/managed/mer/orchestrator/mer-orchestrator"}
m := New(Deps{
Runtime: rt, Agents: fakeAgents{}, Workspace: ws, Store: st,
Messenger: &fakeMessenger{}, Lifecycle: &fakeLCM{store: st},
LookPath: func(string) (string, error) { return "/bin/true", nil },
})
rec, err := m.Spawn(ctx, ports.SpawnConfig{ProjectID: "mer", Kind: domain.KindOrchestrator})
if err != nil {
t.Fatal(err)
}
if rec.Kind != domain.KindOrchestrator {
t.Fatalf("kind = %q, want orchestrator", rec.Kind)
}
if rec.Metadata.WorkspacePath != "/managed/mer/orchestrator/mer-orchestrator" {
t.Fatalf("workspace path = %q", rec.Metadata.WorkspacePath)
}
if ws.lastProjectCfg.Kind != domain.KindOrchestrator {
t.Fatalf("workspace project kind = %q, want orchestrator", ws.lastProjectCfg.Kind)
}
if ws.lastProjectCfg.Branch != "ao/mer-1" {
t.Fatalf("workspace project branch = %q, want ao/mer-1", ws.lastProjectCfg.Branch)
}
rows := st.worktrees["mer-1"]
if len(rows) != 2 {
t.Fatalf("session worktree rows = %d, want root and api: %#v", len(rows), rows)
}
}
func TestSpawn_WorkspaceProjectRollsBackAllWorktreesOnRuntimeFailure(t *testing.T) {
m, st, _, ws := newManager()
st.projects["mer"] = domain.ProjectRecord{