Add gerrit implementation
Change-Id: I293cea35572b051cd1d4a0466fe2fd5a06e81ad9
diff --git a/server/agent/manager.go b/server/agent/manager.go
index 88bfe21..c206665 100644
--- a/server/agent/manager.go
+++ b/server/agent/manager.go
@@ -41,15 +41,38 @@
// Create auto-assigner
autoAssigner := assignment.NewAutoAssigner(cfg.Agents)
- // Create GitHub PR provider
- githubConfig := git.GitHubConfig{
- Token: cfg.GitHub.Token,
- Logger: logger,
+ // Create PR provider based on configuration
+ var prProvider git.PullRequestProvider
+ var repoURL string
+
+ switch cfg.GetPrimaryGitProvider() {
+ case "github":
+ githubConfig := git.GitHubConfig{
+ Token: cfg.GitHub.Token,
+ Logger: logger,
+ }
+ prProvider = git.NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
+ repoURL = fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
+ logger.Info("Using GitHub as pull request provider",
+ slog.String("owner", cfg.GitHub.Owner),
+ slog.String("repo", cfg.GitHub.Repo))
+ case "gerrit":
+ gerritConfig := git.GerritConfig{
+ Username: cfg.Gerrit.Username,
+ Password: cfg.Gerrit.Password,
+ BaseURL: cfg.Gerrit.BaseURL,
+ Logger: logger,
+ }
+ prProvider = git.NewGerritPullRequestProvider(cfg.Gerrit.Project, gerritConfig)
+ repoURL = fmt.Sprintf("%s/%s", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
+ logger.Info("Using Gerrit as pull request provider",
+ slog.String("base_url", cfg.Gerrit.BaseURL),
+ slog.String("project", cfg.Gerrit.Project))
+ default:
+ return nil, fmt.Errorf("no valid Git provider configured")
}
- prProvider := git.NewGitHubPullRequestProvider(cfg.GitHub.Owner, cfg.GitHub.Repo, githubConfig)
- // Create clone manager for per-agent Git repositories
- repoURL := fmt.Sprintf("https://github.com/%s/%s.git", cfg.GitHub.Owner, cfg.GitHub.Repo)
+ // Create clone manager for per-agent Git repositories
workspacePath := filepath.Join(".", "workspace")
cloneManager := git.NewCloneManager(repoURL, workspacePath)
@@ -109,13 +132,24 @@
break
}
+ // Get owner and repo for subtask service based on provider
+ var owner, repo string
+ switch m.config.GetPrimaryGitProvider() {
+ case "github":
+ owner = m.config.GitHub.Owner
+ repo = m.config.GitHub.Repo
+ case "gerrit":
+ owner = m.config.Gerrit.Project
+ repo = m.config.Gerrit.Project
+ }
+
m.subtaskService = subtasks.NewSubtaskService(
firstAgent.Provider,
m.taskManager,
agentRoles,
m.prProvider,
- m.config.GitHub.Owner,
- m.config.GitHub.Repo,
+ owner,
+ repo,
m.cloneManager,
m.logger,
)
@@ -569,7 +603,15 @@
return "", fmt.Errorf("failed to create PR: %w", err)
}
- return fmt.Sprintf("https://github.com/%s/%s/pull/%d", m.config.GitHub.Owner, m.config.GitHub.Repo, pr.Number), nil
+ // Generate provider-specific PR URL
+ switch m.config.GetPrimaryGitProvider() {
+ case "github":
+ return fmt.Sprintf("https://github.com/%s/%s/pull/%d", m.config.GitHub.Owner, m.config.GitHub.Repo, pr.Number), nil
+ case "gerrit":
+ return fmt.Sprintf("%s/c/%s/+/%d", m.config.Gerrit.BaseURL, m.config.Gerrit.Project, pr.Number), nil
+ default:
+ return "", fmt.Errorf("unknown git provider")
+ }
}
// buildPRDescription creates PR description from template
diff --git a/server/cmd/commands/config_check.go b/server/cmd/commands/config_check.go
index ec8c894..5256c03 100644
--- a/server/cmd/commands/config_check.go
+++ b/server/cmd/commands/config_check.go
@@ -33,23 +33,27 @@
fmt.Printf("ℹ️ OpenAI Base URL: %s\n", cfg.OpenAI.BaseURL)
}
+ // Check Git provider configuration
+ fmt.Printf("\nGit Provider: %s\n", cfg.GetPrimaryGitProvider())
+
// Check GitHub configuration
- if cfg.GitHub.Token == "" {
- fmt.Println("❌ GitHub token is missing")
- } else {
- fmt.Printf("✅ GitHub token configured (ends with: ...%s)\n", cfg.GitHub.Token[len(cfg.GitHub.Token)-4:])
+ if cfg.HasGitHubConfig() {
+ fmt.Printf("✅ GitHub configured (ends with: ...%s)\n", cfg.GitHub.Token[len(cfg.GitHub.Token)-4:])
+ fmt.Printf(" Owner: %s, Repo: %s\n", cfg.GitHub.Owner, cfg.GitHub.Repo)
+ } else if cfg.GitHub.Token != "" || cfg.GitHub.Owner != "" || cfg.GitHub.Repo != "" {
+ fmt.Println("⚠️ GitHub partially configured (incomplete)")
}
-
- if cfg.GitHub.Owner == "" {
- fmt.Println("❌ GitHub owner is missing")
- } else {
- fmt.Printf("✅ GitHub owner: %s\n", cfg.GitHub.Owner)
+
+ // Check Gerrit configuration
+ if cfg.HasGerritConfig() {
+ fmt.Printf("✅ Gerrit configured (user: %s)\n", cfg.Gerrit.Username)
+ fmt.Printf(" Base URL: %s, Project: %s\n", cfg.Gerrit.BaseURL, cfg.Gerrit.Project)
+ } else if cfg.Gerrit.Username != "" || cfg.Gerrit.BaseURL != "" || cfg.Gerrit.Project != "" {
+ fmt.Println("⚠️ Gerrit partially configured (incomplete)")
}
-
- if cfg.GitHub.Repo == "" {
- fmt.Println("❌ GitHub repo is missing")
- } else {
- fmt.Printf("✅ GitHub repo: %s\n", cfg.GitHub.Repo)
+
+ if !cfg.HasGitHubConfig() && !cfg.HasGerritConfig() {
+ fmt.Println("❌ No Git provider configured (need GitHub or Gerrit)")
}
// Check agents configuration
diff --git a/server/config/config.go b/server/config/config.go
index 421e861..86dd7a5 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -12,6 +12,7 @@
type Config struct {
OpenAI OpenAIConfig `yaml:"openai"`
GitHub GitHubConfig `yaml:"github"`
+ Gerrit GerritConfig `yaml:"gerrit"`
Agents []AgentConfig `yaml:"agents"`
Tasks TasksConfig `yaml:"tasks"`
Git GitConfig `yaml:"git"`
@@ -33,6 +34,14 @@
Repo string `yaml:"repo"`
}
+// GerritConfig represents Gerrit integration configuration
+type GerritConfig struct {
+ Username string `yaml:"username"`
+ Password string `yaml:"password"` // HTTP password or API token
+ BaseURL string `yaml:"base_url"` // Gerrit server URL (e.g. https://gerrit.example.com)
+ Project string `yaml:"project"` // Gerrit project name
+}
+
// AgentConfig represents individual agent configuration
type AgentConfig struct {
Name string `yaml:"name"`
@@ -103,6 +112,18 @@
if repo := os.Getenv("GITHUB_REPO"); repo != "" {
config.GitHub.Repo = repo
}
+ if gerritUsername := os.Getenv("GERRIT_USERNAME"); gerritUsername != "" {
+ config.Gerrit.Username = gerritUsername
+ }
+ if gerritPassword := os.Getenv("GERRIT_PASSWORD"); gerritPassword != "" {
+ config.Gerrit.Password = gerritPassword
+ }
+ if gerritBaseURL := os.Getenv("GERRIT_BASE_URL"); gerritBaseURL != "" {
+ config.Gerrit.BaseURL = gerritBaseURL
+ }
+ if gerritProject := os.Getenv("GERRIT_PROJECT"); gerritProject != "" {
+ config.Gerrit.Project = gerritProject
+ }
// Re-validate after env overrides
if err := validateConfig(*config); err != nil {
@@ -183,15 +204,35 @@
return fmt.Errorf("openai.model is required")
}
- // Validate GitHub config
- if config.GitHub.Token == "" {
- return fmt.Errorf("github.token is required")
+ // Validate that at least one Git provider is configured
+ hasGitHub := config.GitHub.Token != "" && config.GitHub.Owner != "" && config.GitHub.Repo != ""
+ hasGerrit := config.Gerrit.Username != "" && config.Gerrit.Password != "" && config.Gerrit.BaseURL != "" && config.Gerrit.Project != ""
+
+ if !hasGitHub && !hasGerrit {
+ return fmt.Errorf("either GitHub or Gerrit configuration is required")
}
- if config.GitHub.Owner == "" {
- return fmt.Errorf("github.owner is required")
+
+ // Validate GitHub config if provided
+ if config.GitHub.Token != "" {
+ if config.GitHub.Owner == "" {
+ return fmt.Errorf("github.owner is required when github.token is provided")
+ }
+ if config.GitHub.Repo == "" {
+ return fmt.Errorf("github.repo is required when github.token is provided")
+ }
}
- if config.GitHub.Repo == "" {
- return fmt.Errorf("github.repo is required")
+
+ // Validate Gerrit config if provided
+ if config.Gerrit.Username != "" {
+ if config.Gerrit.Password == "" {
+ return fmt.Errorf("gerrit.password is required when gerrit.username is provided")
+ }
+ if config.Gerrit.BaseURL == "" {
+ return fmt.Errorf("gerrit.base_url is required when gerrit.username is provided")
+ }
+ if config.Gerrit.Project == "" {
+ return fmt.Errorf("gerrit.project is required when gerrit.username is provided")
+ }
}
// Validate agents
@@ -232,3 +273,25 @@
}
return names
}
+
+// HasGitHubConfig returns true if GitHub is properly configured
+func (c *Config) HasGitHubConfig() bool {
+ return c.GitHub.Token != "" && c.GitHub.Owner != "" && c.GitHub.Repo != ""
+}
+
+// HasGerritConfig returns true if Gerrit is properly configured
+func (c *Config) HasGerritConfig() bool {
+ return c.Gerrit.Username != "" && c.Gerrit.Password != "" && c.Gerrit.BaseURL != "" && c.Gerrit.Project != ""
+}
+
+// GetPrimaryGitProvider returns the primary git provider type ("github" or "gerrit")
+// If both are configured, GitHub takes precedence for backward compatibility
+func (c *Config) GetPrimaryGitProvider() string {
+ if c.HasGitHubConfig() {
+ return "github"
+ }
+ if c.HasGerritConfig() {
+ return "gerrit"
+ }
+ return ""
+}
diff --git a/server/git/gerrit.go b/server/git/gerrit.go
index d795079..d1d029f 100644
--- a/server/git/gerrit.go
+++ b/server/git/gerrit.go
@@ -5,6 +5,7 @@
"context"
"encoding/json"
"fmt"
+ "log/slog"
"net/http"
"strconv"
"strings"
@@ -17,12 +18,14 @@
Password string // Can be HTTP password or API token
BaseURL string
HTTPClient *http.Client
+ Logger *slog.Logger
}
// GerritPullRequestProvider implements PullRequestProvider for Gerrit
type GerritPullRequestProvider struct {
config GerritConfig
project string
+ logger *slog.Logger
}
// NewGerritPullRequestProvider creates a new Gerrit pull request provider
@@ -30,10 +33,14 @@
if config.HTTPClient == nil {
config.HTTPClient = &http.Client{Timeout: 30 * time.Second}
}
+ if config.Logger == nil {
+ config.Logger = slog.Default()
+ }
return &GerritPullRequestProvider{
config: config,
project: project,
+ logger: config.Logger,
}
}
@@ -145,6 +152,14 @@
}
url := fmt.Sprintf("%s/a/changes/", g.config.BaseURL)
+
+ // Log change creation with structured data
+ g.logger.Info("Creating Gerrit change",
+ slog.String("url", url),
+ slog.String("project", g.project),
+ slog.String("subject", options.Title),
+ slog.String("branch", options.BaseBranch),
+ slog.String("topic", options.HeadBranch))
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
diff --git a/server/staff b/server/staff
index 701a6e8..cfd99cf 100755
--- a/server/staff
+++ b/server/staff
Binary files differ