feat(jobrunner): resolve dispatch target branch
Some checks failed
Security Scan / security (push) Failing after 10s
Test / test (push) Successful in 2m12s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 04:45:40 +00:00
parent a0fac1341b
commit 976d20c02f
3 changed files with 44 additions and 4 deletions

View file

@ -82,7 +82,8 @@ func newForgejoMux() *http.ServeMux {
}
jsonResponse(w, map[string]any{
"id": 10, "name": "org-repo", "full_name": "test-org/org-repo",
"owner": map[string]any{"login": "test-org"},
"owner": map[string]any{"login": "test-org"},
"default_branch": "main",
})
})

View file

@ -157,7 +157,7 @@ func (h *DispatchHandler) Execute(ctx context.Context, signal *jobrunner.Pipelin
}
// Build ticket.
targetBranch := "new" // TODO: resolve from epic or repo default
targetBranch := h.resolveTargetBranch(safeOwner, safeRepo)
ticketID := fmt.Sprintf("%s-%s-%d-%d", safeOwner, safeRepo, signal.ChildNumber, time.Now().Unix())
ticket := DispatchTicket{
@ -263,6 +263,21 @@ func (h *DispatchHandler) Execute(ctx context.Context, signal *jobrunner.Pipelin
}, nil
}
func (h *DispatchHandler) resolveTargetBranch(owner, repo string) string {
const fallbackBranch = "main"
if h.forge == nil {
return fallbackBranch
}
repoInfo, err := h.forge.GetRepo(owner, repo)
if err != nil || repoInfo == nil || repoInfo.DefaultBranch == "" {
return fallbackBranch
}
return repoInfo.DefaultBranch
}
// failDispatch handles cleanup when dispatch fails (adds failed label, removes in-progress).
func (h *DispatchHandler) failDispatch(signal *jobrunner.PipelineSignal, reason string) {
if failedLabel, err := h.forge.EnsureLabel(signal.RepoOwner, signal.RepoName, LabelAgentFailed, ColorAgentFailed); err == nil {

View file

@ -175,7 +175,7 @@ func TestDispatch_TicketJSON_Good(t *testing.T) {
IssueNumber: 5,
IssueTitle: "Fix the thing",
IssueBody: "Please fix this bug",
TargetBranch: "new",
TargetBranch: "main",
EpicNumber: 3,
ForgeURL: "https://forge.lthn.ai",
ForgeUser: "darbs-claude",
@ -198,7 +198,7 @@ func TestDispatch_TicketJSON_Good(t *testing.T) {
assert.Equal(t, float64(5), decoded["issue_number"])
assert.Equal(t, "Fix the thing", decoded["issue_title"])
assert.Equal(t, "Please fix this bug", decoded["issue_body"])
assert.Equal(t, "new", decoded["target_branch"])
assert.Equal(t, "main", decoded["target_branch"])
assert.Equal(t, float64(3), decoded["epic_number"])
assert.Equal(t, "https://forge.lthn.ai", decoded["forge_url"])
assert.Equal(t, "darbs-claude", decoded["forgejo_user"])
@ -254,6 +254,30 @@ func TestDispatch_TicketJSON_Good_OmitsEmptyModelRunner_Good(t *testing.T) {
assert.False(t, hasRunner, "runner should be omitted when empty")
}
func TestDispatch_Execute_Good_UsesRepoDefaultBranch_Good(t *testing.T) {
srv := httptest.NewServer(withVersion(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})))
defer srv.Close()
client := newTestForgeClient(t, srv.URL)
spinner := newTestSpinner(map[string]agentci.AgentConfig{
"darbs-claude": {Host: "claude@192.168.0.201", QueueDir: "~/ai-work/queue", Active: true},
})
h := NewDispatchHandler(client, srv.URL, "test-token", spinner)
sig := &jobrunner.PipelineSignal{
NeedsCoding: true,
Assignee: "darbs-claude",
RepoOwner: "test-org",
RepoName: "org-repo",
ChildNumber: 1,
}
branch := h.resolveTargetBranch(sig.RepoOwner, sig.RepoName)
assert.Equal(t, "main", branch)
}
func TestDispatch_runRemote_Good_EscapesPath_Good(t *testing.T) {
outputPath := filepath.Join(t.TempDir(), "ssh-output.txt")
toolPath := writeFakeSSHCommand(t, outputPath)