From 128be5fb6cc2e0f41e6b83a715657f1f32af1476 Mon Sep 17 00:00:00 2001 From: Michael Freeman Date: Fri, 10 Apr 2026 07:12:01 -0500 Subject: [PATCH] Skip workspace bootstrap for global hub starts --- cmd/common.go | 2 +- cmd/common_envgather_test.go | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/cmd/common.go b/cmd/common.go index 5096d557..a46c4625 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -692,7 +692,7 @@ func startAgentViaHub(hubCtx *HubContext, agentName, task string, resume bool, i // Detect non-git grove for workspace bootstrap var workspaceFiles []transfer.FileInfo - if hubCtx.GrovePath != "" { + if hubCtx.GrovePath != "" && !hubCtx.IsGlobal { groveDir := filepath.Dir(hubCtx.GrovePath) // parent of .scion if _, statErr := os.Stat(groveDir); statErr == nil && !util.IsGitRepoDir(groveDir) { files, err := transfer.CollectFiles(groveDir, transfer.DefaultExcludePatterns) diff --git a/cmd/common_envgather_test.go b/cmd/common_envgather_test.go index 3fb4c171..14baff71 100644 --- a/cmd/common_envgather_test.go +++ b/cmd/common_envgather_test.go @@ -20,7 +20,9 @@ import ( "net/http" "net/http/httptest" "os" + "path/filepath" "testing" + "time" "github.com/GoogleCloudPlatform/scion/pkg/hubclient" "github.com/stretchr/testify/assert" @@ -214,6 +216,79 @@ func TestStartAgentViaHub_EnvGatherFailureCleansUp(t *testing.T) { assert.True(t, deleteCalled, "expected provisioning agent to be deleted on env-gather failure") } +func TestStartAgentViaHub_GlobalGroveSkipsWorkspaceBootstrap(t *testing.T) { + origOutputFormat := outputFormat + origTemplateName := templateName + origHarnessConfigFlag := harnessConfigFlag + origRuntimeBrokerID := runtimeBrokerID + defer func() { + outputFormat = origOutputFormat + templateName = origTemplateName + harnessConfigFlag = origHarnessConfigFlag + runtimeBrokerID = origRuntimeBrokerID + }() + + outputFormat = "json" + templateName = "" + harnessConfigFlag = "codex" + runtimeBrokerID = "broker-1" + + globalDir := t.TempDir() + settingsPath := filepath.Join(globalDir, "settings.yaml") + require.NoError(t, os.WriteFile(settingsPath, []byte("hub:\n enabled: true\n"), 0644)) + require.NoError(t, os.WriteFile(filepath.Join(globalDir, "state.yaml"), []byte("syncedAgents: []\n"), 0644)) + require.NoError(t, os.WriteFile(filepath.Join(globalDir, "large-local-file.txt"), []byte("should-not-upload"), 0644)) + + groveID := "grove-global" + var captured *hubclient.CreateAgentRequest + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + switch { + case r.Method == http.MethodGet && r.URL.Path == "/api/v1/groves/"+groveID: + json.NewEncoder(w).Encode(map[string]interface{}{ + "id": groveID, + "name": "global", + }) + case r.Method == http.MethodPost && r.URL.Path == "/api/v1/groves/"+groveID+"/agents": + var req hubclient.CreateAgentRequest + require.NoError(t, json.NewDecoder(r.Body).Decode(&req)) + captured = &req + json.NewEncoder(w).Encode(&hubclient.CreateAgentResponse{ + Agent: &hubclient.Agent{ + ID: "agent-1", + Slug: "agent-1", + Name: "agent-1", + Status: "running", + Phase: "running", + RuntimeBrokerID: "broker-1", + RuntimeBrokerName: "scion", + Created: time.Now().UTC(), + }, + }) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + + client, err := hubclient.New(server.URL) + require.NoError(t, err) + + hubCtx := &HubContext{ + Client: client, + Endpoint: server.URL, + GroveID: groveID, + GrovePath: settingsPath, + IsGlobal: true, + } + + err = startAgentViaHub(hubCtx, "global-agent", "hello", false, nil) + require.NoError(t, err) + require.NotNil(t, captured) + assert.Empty(t, captured.WorkspaceFiles) +} + // newEnvGatherMockHubServer creates a mock Hub server that handles the SubmitEnv // endpoint and captures the submitted environment variables. func newEnvGatherMockHubServer(t *testing.T, groveID string) (*httptest.Server, *map[string]string) {