From 2d00214e74940036f0797b89471572d712ebc731 Mon Sep 17 00:00:00 2001 From: anthonyrawlins Date: Mon, 14 Jul 2025 20:52:26 +1000 Subject: [PATCH] Fix GitHub branch detection and complete task execution workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added dynamic branch detection that falls back to repository default branch - Fixed sandbox image reference to use local Docker registry - Completed full task execution pipeline: discovery → claim → sandbox → execution - Enhanced GitHub client to verify base branch existence and auto-correct - Successfully tested end-to-end task claiming and execution 🎉 MAJOR MILESTONE: Full Bzzz task execution workflow now functional\! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- github/client.go | 24 +++++++++++++++++++ github/hive_integration.go | 6 ++--- sandbox/sandbox.go | 49 +++++++++++++++++++++++++++++++++----- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/github/client.go b/github/client.go index fc4a71ea..ecf70ba6 100644 --- a/github/client.go +++ b/github/client.go @@ -76,6 +76,11 @@ func NewClient(ctx context.Context, config *Config) (*Client, error) { return nil, fmt.Errorf("failed to verify GitHub access: %w", err) } + // Verify base branch exists and update if needed + if err := client.verifyBaseBranch(); err != nil { + return nil, fmt.Errorf("failed to verify base branch: %w", err) + } + return client, nil } @@ -89,6 +94,25 @@ func (c *Client) verifyAccess() error { return nil } +// verifyBaseBranch checks if the base branch exists and updates to default if not +func (c *Client) verifyBaseBranch() error { + // Try to get the configured base branch + _, _, err := c.client.Git.GetRef(c.ctx, c.config.Owner, c.config.Repository, "heads/"+c.config.BaseBranch) + if err != nil { + // If the branch doesn't exist, get the repository's default branch + repo, _, err := c.client.Repositories.Get(c.ctx, c.config.Owner, c.config.Repository) + if err != nil { + return fmt.Errorf("failed to get repository info: %w", err) + } + + // Update config to use the default branch + if repo.DefaultBranch != nil { + c.config.BaseBranch = *repo.DefaultBranch + } + } + return nil +} + // Task represents a Bzzz task as a GitHub issue type Task struct { ID int64 `json:"id"` diff --git a/github/hive_integration.go b/github/hive_integration.go index c5b8839d..f13bd49e 100644 --- a/github/hive_integration.go +++ b/github/hive_integration.go @@ -12,7 +12,6 @@ import ( "github.com/anthonyrawlins/bzzz/pkg/hive" "github.com/anthonyrawlins/bzzz/pkg/types" "github.com/anthonyrawlins/bzzz/pubsub" - "github.com/anthonyrawlins/bzzz/reasoning" "github.com/libp2p/go-libp2p/core/peer" ) @@ -116,6 +115,7 @@ func (hi *HiveIntegration) syncRepositories() { AccessToken: hi.githubToken, Owner: repo.Owner, Repository: repo.Repository, + BaseBranch: repo.Branch, } client, err := NewClient(hi.ctx, githubConfig) @@ -304,7 +304,7 @@ func (hi *HiveIntegration) claimAndExecuteTask(task *types.EnhancedTask) { } // executeTask executes a claimed task with reasoning and coordination -func (hi *HiveIntegration) executeTask(task *EnhancedTask, repoClient *RepositoryClient) { +func (hi *HiveIntegration) executeTask(task *types.EnhancedTask, repoClient *RepositoryClient) { // Define the dynamic topic for this task taskTopic := fmt.Sprintf("bzzz/meta/issue/%d", task.Number) hi.pubsub.JoinDynamicTopic(taskTopic) @@ -344,7 +344,7 @@ func (hi *HiveIntegration) executeTask(task *EnhancedTask, repoClient *Repositor } // requestAssistance publishes a help request to the task-specific topic. -func (hi *HiveIntegration) requestAssistance(task *EnhancedTask, reason, topic string) { +func (hi *HiveIntegration) requestAssistance(task *types.EnhancedTask, reason, topic string) { fmt.Printf("🆘 Agent %s is requesting assistance for task #%d: %s\n", hi.config.AgentID, task.Number, reason) hi.hlog.Append(logging.TaskHelpRequested, map[string]interface{}{ "task_id": task.Number, diff --git a/sandbox/sandbox.go b/sandbox/sandbox.go index 035f51af..1ef3701b 100644 --- a/sandbox/sandbox.go +++ b/sandbox/sandbox.go @@ -1,18 +1,22 @@ package sandbox import ( + "archive/tar" + "bytes" "context" "fmt" + "io" "os" - "time" + "path/filepath" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" ) const ( // DefaultDockerImage is the image used if a task does not specify one. - DefaultDockerImage = "bzzz-sandbox:latest" + DefaultDockerImage = "registry.home.deepblack.cloud/tony/bzzz-sandbox:latest" ) // Sandbox represents a stateful, isolated execution environment for a single task. @@ -62,8 +66,8 @@ func CreateSandbox(ctx context.Context, taskImage string) (*Sandbox, error) { hostConfig := &container.HostConfig{ Binds: []string{fmt.Sprintf("%s:/home/agent/work", hostPath)}, Resources: container.Resources{ - CPUs: 2, - Memory: 2 * 1024 * 1024 * 1024, // 2GB + NanoCPUs: 2 * 1000000000, // 2 CPUs + Memory: 2 * 1024 * 1024 * 1024, // 2GB }, } @@ -98,7 +102,7 @@ func (s *Sandbox) DestroySandbox() error { } // Define a timeout for stopping the container - timeout := 30 * time.Second + timeout := 30 // seconds // Stop the container fmt.Printf("🛑 Stopping sandbox container %s...\n", s.ID[:12]) @@ -184,7 +188,40 @@ func (s *Sandbox) WriteFile(path string, content []byte) error { // Copy the file into the container dstPath := filepath.Join(s.Workspace, path) - return s.dockerCli.CopyToContainer(s.ctx, tmpfile.Name(), s.ID, dstPath) + + // Create tar archive of the file + tarBuf := new(bytes.Buffer) + tw := tar.NewWriter(tarBuf) + + fileInfo, err := os.Stat(tmpfile.Name()) + if err != nil { + return fmt.Errorf("failed to stat temp file: %w", err) + } + + header := &tar.Header{ + Name: filepath.Base(path), + Size: fileInfo.Size(), + Mode: 0644, + } + + if err := tw.WriteHeader(header); err != nil { + return fmt.Errorf("failed to write tar header: %w", err) + } + + fileContent, err := os.ReadFile(tmpfile.Name()) + if err != nil { + return fmt.Errorf("failed to read temp file: %w", err) + } + + if _, err := tw.Write(fileContent); err != nil { + return fmt.Errorf("failed to write to tar: %w", err) + } + + if err := tw.Close(); err != nil { + return fmt.Errorf("failed to close tar writer: %w", err) + } + + return s.dockerCli.CopyToContainer(s.ctx, s.ID, filepath.Dir(dstPath), tarBuf, container.CopyToContainerOptions{}) } // ReadFile reads the content of a file from the sandbox's workspace.