Add PRs to agent
Change-Id: If623cf29d160a0ff4dcbbb2923f681a2d9f17c1b
diff --git a/server/agent/agent.go b/server/agent/agent.go
index d1c64a4..42a7024 100644
--- a/server/agent/agent.go
+++ b/server/agent/agent.go
@@ -37,6 +37,18 @@
GitRepoPath string
GitRemote string
GitBranch string
+
+ // Gerrit Configuration
+ GerritEnabled bool
+ GerritConfig GerritConfig
+}
+
+// GerritConfig holds configuration for Gerrit operations
+type GerritConfig struct {
+ Username string
+ Password string // Can be HTTP password or API token
+ BaseURL string
+ Project string
}
// Agent represents an AI agent that can process tasks
@@ -62,7 +74,26 @@
}
// Create git interface
- gitInterface := git.DefaultGit(config.GitRepoPath)
+ var gitInterface git.GitInterface
+ if config.GerritEnabled {
+ // Create Gerrit pull request provider
+ gerritPRProvider := git.NewGerritPullRequestProvider(config.GerritConfig.Project, git.GerritConfig{
+ Username: config.GerritConfig.Username,
+ Password: config.GerritConfig.Password,
+ BaseURL: config.GerritConfig.BaseURL,
+ HTTPClient: nil, // Will use default client
+ })
+
+ // Create git interface with Gerrit pull request provider
+ gitConfig := git.GitConfig{
+ Timeout: 30 * time.Second,
+ PullRequestProvider: gerritPRProvider,
+ }
+ gitInterface = git.NewGitWithPullRequests(config.GitRepoPath, gitConfig, gerritPRProvider)
+ } else {
+ // Use default git interface (GitHub)
+ gitInterface = git.DefaultGit(config.GitRepoPath)
+ }
// Create context with cancellation
ctx, cancel := context.WithCancel(context.Background())
@@ -152,6 +183,52 @@
}
}
+ // Set up git user configuration
+ userConfig := git.UserConfig{
+ Name: a.Config.GitUsername,
+ Email: a.Config.GitEmail,
+ }
+ if err := a.gitInterface.SetUserConfig(ctx, userConfig); err != nil {
+ return fmt.Errorf("failed to set git user config: %w", err)
+ }
+
+ // Check if remote origin exists, if not add it
+ remotes, err := a.gitInterface.ListRemotes(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to list remotes: %w", err)
+ }
+
+ originExists := false
+ for _, remote := range remotes {
+ if remote.Name == "origin" {
+ originExists = true
+ break
+ }
+ }
+
+ if !originExists {
+ // Add remote origin - use Gerrit URL if enabled, otherwise use the configured remote
+ remoteURL := a.Config.GitRemote
+ if a.Config.GerritEnabled {
+ // For Gerrit, the remote URL should be the Gerrit SSH or HTTP URL
+ // Format: ssh://username@gerrit-host:29418/project-name.git
+ // or: https://gerrit-host/project-name.git
+ if strings.HasPrefix(a.Config.GerritConfig.BaseURL, "https://") {
+ remoteURL = fmt.Sprintf("%s/%s.git", a.Config.GerritConfig.BaseURL, a.Config.GerritConfig.Project)
+ } else {
+ // Assume SSH format
+ remoteURL = fmt.Sprintf("ssh://%s@%s:29418/%s.git",
+ a.Config.GerritConfig.Username,
+ strings.TrimPrefix(a.Config.GerritConfig.BaseURL, "https://"),
+ a.Config.GerritConfig.Project)
+ }
+ }
+
+ if err := a.gitInterface.AddRemote(ctx, "origin", remoteURL); err != nil {
+ return fmt.Errorf("failed to add remote origin: %w", err)
+ }
+ }
+
// Checkout to the specified branch
if a.Config.GitBranch != "" {
if err := a.gitInterface.Checkout(ctx, a.Config.GitBranch); err != nil {
@@ -309,7 +386,7 @@
return fmt.Errorf("failed to add solution file: %w", err)
}
- commitMessage := fmt.Sprintf("feat: Complete task %s - %s", task.ID, task.Title)
+ commitMessage := fmt.Sprintf("feat: Complete task %s - %s\n\n%s", task.ID, task.Title, a.formatPullRequestDescription(task, solution))
if err := a.gitInterface.Commit(ctx, commitMessage, git.CommitOptions{
Author: &git.Author{
Name: a.Config.GitUsername,
@@ -320,12 +397,37 @@
return fmt.Errorf("failed to commit solution: %w", err)
}
- // Push the branch
- if err := a.gitInterface.Push(ctx, "origin", branchName, git.PushOptions{SetUpstream: true}); err != nil {
- return fmt.Errorf("failed to push branch: %w", err)
+ if a.Config.GerritEnabled {
+ // For Gerrit: Push to refs/for/BRANCH to create a change
+ gerritRef := fmt.Sprintf("refs/for/%s", a.Config.GitBranch)
+ if err := a.gitInterface.Push(ctx, "origin", gerritRef, git.PushOptions{}); err != nil {
+ return fmt.Errorf("failed to push to Gerrit: %w", err)
+ }
+ log.Printf("Created Gerrit change for task %s by pushing to %s", task.ID, gerritRef)
+ } else {
+ // For GitHub: Push branch and create PR
+ if err := a.gitInterface.Push(ctx, "origin", branchName, git.PushOptions{SetUpstream: true}); err != nil {
+ return fmt.Errorf("failed to push branch: %w", err)
+ }
+
+ // Create pull request using the git interface
+ prOptions := git.PullRequestOptions{
+ Title: fmt.Sprintf("Complete task %s: %s", task.ID, task.Title),
+ Description: a.formatPullRequestDescription(task, solution),
+ BaseBranch: a.Config.GitBranch,
+ HeadBranch: branchName,
+ BaseRepo: a.Config.GerritConfig.Project,
+ HeadRepo: a.Config.GerritConfig.Project,
+ }
+
+ pr, err := a.gitInterface.CreatePullRequest(ctx, prOptions)
+ if err != nil {
+ return fmt.Errorf("failed to create pull request: %w", err)
+ }
+
+ log.Printf("Created pull request for task %s: %s (ID: %s)", task.ID, pr.Title, pr.ID)
}
- log.Printf("Created pull request for task %s on branch %s", task.ID, branchName)
return nil
}
@@ -386,6 +488,28 @@
return content.String()
}
+// formatPullRequestDescription formats the description for the pull request
+func (a *Agent) formatPullRequestDescription(task *tm.Task, solution string) string {
+ var content strings.Builder
+
+ content.WriteString(fmt.Sprintf("**Task ID:** %s\n", task.ID))
+ content.WriteString(fmt.Sprintf("**Title:** %s\n", task.Title))
+ content.WriteString(fmt.Sprintf("**Priority:** %s\n", task.Priority))
+
+ if task.Description != "" {
+ content.WriteString(fmt.Sprintf("**Description:** %s\n", task.Description))
+ }
+
+ if task.DueDate != nil {
+ content.WriteString(fmt.Sprintf("**Due Date:** %s\n", task.DueDate.Format("2006-01-02")))
+ }
+
+ content.WriteString("\n**Solution:**\n\n")
+ content.WriteString(solution)
+
+ return content.String()
+}
+
// ptr helpers for cleaner code
func intPtr(i int) *int {
return &i