blob: 7858d2bcbe573f19170e7dfa15ff25487c8b04b1 [file] [log] [blame]
package app
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/iomodo/staff/git"
)
func (a *App) ProposalApproval(body []byte, signature string) error {
// Set reasonable timeout for webhook processing
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Validate required dependencies
if a.git == nil {
return fmt.Errorf("git interface not initialized")
}
if a.taskManager == nil {
return fmt.Errorf("task manager not initialized")
}
// Process the webhook payload (includes signature validation)
taskID, err := git.ProcessMergeWebhook(body, signature, a.config.GitHub.WebhookSecret)
if err != nil {
return fmt.Errorf("failed to process webhook: %w", err)
}
// Complete the task for the agent that was performing it
if err := a.manager.CompleteTaskForAgent(taskID); err != nil {
a.logger.Warn("Failed to complete task for agent",
slog.String("task_id", taskID),
slog.String("error", err.Error()))
// Don't fail the webhook if agent completion fails
}
a.logger.Info("Proposal approved via webhook",
slog.String("task_id", taskID),
slog.String("repository", fmt.Sprintf("%s/%s", a.config.GitHub.Owner, a.config.GitHub.Repo)),
)
// Synchronize repository to get any new tasks that were added
a.syncRepository(ctx)
a.logger.Info("Webhook processing completed successfully",
slog.String("task_id", taskID),
)
return nil
}
// syncRepository pulls the remote repository to fetch new tasks
// This is non-critical for webhook success, so failures are logged but don't fail the webhook
func (a *App) syncRepository(ctx context.Context) {
a.logger.Info("Synchronizing repository to fetch new tasks")
// Get current branch to determine the upstream branch to pull from
currentBranch, err := a.git.GetCurrentBranch(ctx)
if err != nil {
a.logger.Warn("Failed to get current branch, using default pull",
slog.String("error", err.Error()),
)
// Fallback to simple pull without specifying branch
if err := a.git.Pull(ctx, "origin", ""); err != nil {
a.logger.Warn("Failed to synchronize repository",
slog.String("remote", "origin"),
slog.String("error", err.Error()),
)
} else {
a.logger.Info("Successfully synchronized repository",
slog.String("remote", "origin"),
)
}
return
}
// Get the upstream branch for the current branch
upstreamBranch, err := a.git.GetUpstreamBranch(ctx, currentBranch)
if err != nil {
a.logger.Warn("Failed to get upstream branch, trying simple pull",
slog.String("current_branch", currentBranch),
slog.String("error", err.Error()),
)
// Fallback to simple pull without specifying branch
if err := a.git.Pull(ctx, "origin", ""); err != nil {
a.logger.Warn("Failed to synchronize repository",
slog.String("remote", "origin"),
slog.String("error", err.Error()),
)
} else {
a.logger.Info("Successfully synchronized repository",
slog.String("remote", "origin"),
)
}
return
}
// Use the upstream branch for pulling
if err := a.git.Pull(ctx, "origin", upstreamBranch); err != nil {
// Don't fail the entire webhook if git pull fails - agents can still work with existing tasks
a.logger.Warn("Failed to synchronize repository",
slog.String("remote", "origin"),
slog.String("branch", upstreamBranch),
slog.String("error", err.Error()),
)
} else {
a.logger.Info("Successfully synchronized repository",
slog.String("remote", "origin"),
slog.String("branch", upstreamBranch),
)
}
}