From 1af9fbf1f6ea8917019b2a1066d1a5fa131b8f7f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:40:09 +0000 Subject: [PATCH 1/5] Initial plan From 58bdd39ca94c1fff6c4d09d95f4467080c5bf695 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:54:32 +0000 Subject: [PATCH 2/5] Add .agents folder to sparse-checkout alongside .github Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_yaml_helpers.go | 23 +++++---- pkg/workflow/compiler_yaml_helpers_test.go | 51 +++++++++++++++++++ ...ithub_folder_checkout_optimization_test.go | 14 +++-- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/pkg/workflow/compiler_yaml_helpers.go b/pkg/workflow/compiler_yaml_helpers.go index 0baa723d23..ce5f49466b 100644 --- a/pkg/workflow/compiler_yaml_helpers.go +++ b/pkg/workflow/compiler_yaml_helpers.go @@ -145,9 +145,9 @@ func (c *Compiler) generateCheckoutActionsFolder(data *WorkflowData) []string { return nil } -// generateCheckoutGitHubFolder generates the checkout step for the .github folder -// for the agent job. This ensures workflows have access to workflow configurations -// and runtime imports even when they don't do a full repository checkout. +// generateCheckoutGitHubFolder generates the checkout step for the .github and .agents folders +// for the agent job. This ensures workflows have access to workflow configurations, +// runtime imports, and skills even when they don't do a full repository checkout. // // This checkout works in all modes (dev, script, release) and uses shallow clone // for minimal overhead. It should only be called in the main agent job. @@ -171,32 +171,33 @@ func (c *Compiler) generateCheckoutGitHubFolder(data *WorkflowData) []string { // Check if we have contents permission - without it, checkout is not possible permParser := NewPermissionsParser(data.Permissions) if !permParser.HasContentsReadAccess() { - compilerYamlLog.Print("Skipping .github checkout: no contents read access") + compilerYamlLog.Print("Skipping .github and .agents checkout: no contents read access") return nil } - // Skip .github checkout if custom steps already contain a full repository checkout - // The full checkout already includes the .github folder, making sparse checkout redundant + // Skip .github and .agents checkout if custom steps already contain a full repository checkout + // The full checkout already includes these folders, making sparse checkout redundant if data.CustomSteps != "" && ContainsCheckout(data.CustomSteps) { - compilerYamlLog.Print("Skipping .github sparse checkout: custom steps contain full repository checkout") + compilerYamlLog.Print("Skipping .github and .agents sparse checkout: custom steps contain full repository checkout") return nil } - // Skip .github checkout if an automatic full repository checkout will be added + // Skip .github and .agents checkout if an automatic full repository checkout will be added // The shouldAddCheckoutStep function returns true when a checkout step will be automatically added if c.shouldAddCheckoutStep(data) { - compilerYamlLog.Print("Skipping .github sparse checkout: full repository checkout will be added automatically") + compilerYamlLog.Print("Skipping .github and .agents sparse checkout: full repository checkout will be added automatically") return nil } - // For all modes (dev, script, release), checkout .github folder + // For all modes (dev, script, release), checkout .github and .agents folders // This works in release mode where actions aren't checked out return []string{ - " - name: Checkout .github folder\n", + " - name: Checkout .github and .agents folders\n", fmt.Sprintf(" uses: %s\n", GetActionPin("actions/checkout")), " with:\n", " sparse-checkout: |\n", " .github\n", + " .agents\n", " depth: 1\n", " persist-credentials: false\n", } diff --git a/pkg/workflow/compiler_yaml_helpers_test.go b/pkg/workflow/compiler_yaml_helpers_test.go index 7dee1f656a..6ea38effdf 100644 --- a/pkg/workflow/compiler_yaml_helpers_test.go +++ b/pkg/workflow/compiler_yaml_helpers_test.go @@ -340,3 +340,54 @@ func TestGenerateYAMLRefactored(t *testing.T) { }) } } + +// TestGenerateCheckoutGitHubFolder verifies that when sparse checkout is generated, +// it includes both .github and .agents folders +func TestGenerateCheckoutGitHubFolder(t *testing.T) { +compiler := NewCompiler() + +// Test that when checkout is generated, it includes both folders +// Note: Due to complex logic in shouldAddCheckoutStep, the sparse checkout +// may not be generated in simple test scenarios. This test verifies the +// output format when it is generated. +workflowData := &WorkflowData{ +Permissions: "permissions:\n contents: read", +} + +result := compiler.generateCheckoutGitHubFolder(workflowData) + +// If result is generated, verify it includes both .github and .agents +if result != nil { +checkoutStr := strings.Join(result, "") + +if !strings.Contains(checkoutStr, "Checkout .github and .agents folders") { +t.Errorf("Step name should mention both .github and .agents folders, got: %s", checkoutStr) +} + +if !strings.Contains(checkoutStr, ".github") || !strings.Contains(checkoutStr, ".agents") { +t.Errorf("Sparse checkout should include both .github and .agents folders, got: %s", checkoutStr) +} + +t.Log("✓ Sparse checkout includes both .github and .agents folders") +} else { +t.Log("Sparse checkout not generated (expected with default logic)") +} + +// Test negative cases +t.Run("without_contents_permission", func(t *testing.T) { +data := &WorkflowData{Permissions: "permissions: {}"} +if compiler.generateCheckoutGitHubFolder(data) != nil { +t.Error("Should not generate checkout without contents permission") +} +}) + +t.Run("with_action_tag", func(t *testing.T) { +data := &WorkflowData{ +Permissions: "permissions:\n contents: read", +Features: map[string]any{"action-tag": "abc123"}, +} +if compiler.generateCheckoutGitHubFolder(data) != nil { +t.Error("Should not generate checkout with action-tag") +} +}) +} diff --git a/pkg/workflow/github_folder_checkout_optimization_test.go b/pkg/workflow/github_folder_checkout_optimization_test.go index 45dd7aaf49..b4d42e27b7 100644 --- a/pkg/workflow/github_folder_checkout_optimization_test.go +++ b/pkg/workflow/github_folder_checkout_optimization_test.go @@ -145,10 +145,10 @@ This workflow uses runtime imports: {{runtime-import:shared/example.md}} agentJobSection = lockStr[agentJobStart : agentJobStart+10+nextJobStart] } - // Check for .github folder checkout - hasGitHubCheckout := strings.Contains(agentJobSection, "Checkout .github folder") + // Check for .github and .agents folders checkout + hasGitHubCheckout := strings.Contains(agentJobSection, "Checkout .github and .agents folders") assert.Equal(t, tt.expectGitHubCheckout, hasGitHubCheckout, - "Test case: %s - Expected .github checkout: %t, got: %t\nDescription: %s", + "Test case: %s - Expected .github and .agents checkout: %t, got: %t\nDescription: %s", tt.name, tt.expectGitHubCheckout, hasGitHubCheckout, tt.description) // Check for full repository checkout @@ -163,6 +163,14 @@ This workflow uses runtime imports: {{runtime-import:shared/example.md}} tt.name, tt.description) } + // If .github checkout is expected, verify that .agents folder is also included + if tt.expectGitHubCheckout { + assert.Contains(t, agentJobSection, ".github", + "Test case: %s - Sparse checkout should include .github folder", tt.name) + assert.Contains(t, agentJobSection, ".agents", + "Test case: %s - Sparse checkout should include .agents folder", tt.name) + } + t.Logf("✓ Test case passed: %s", tt.description) }) } From 2b673e42d31486250b6d451e5ba1ea1c7b442d20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:56:46 +0000 Subject: [PATCH 3/5] Fix formatting in test file Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_yaml_helpers_test.go | 80 +++++++++++----------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/pkg/workflow/compiler_yaml_helpers_test.go b/pkg/workflow/compiler_yaml_helpers_test.go index 6ea38effdf..33e036b063 100644 --- a/pkg/workflow/compiler_yaml_helpers_test.go +++ b/pkg/workflow/compiler_yaml_helpers_test.go @@ -344,50 +344,50 @@ func TestGenerateYAMLRefactored(t *testing.T) { // TestGenerateCheckoutGitHubFolder verifies that when sparse checkout is generated, // it includes both .github and .agents folders func TestGenerateCheckoutGitHubFolder(t *testing.T) { -compiler := NewCompiler() - -// Test that when checkout is generated, it includes both folders -// Note: Due to complex logic in shouldAddCheckoutStep, the sparse checkout -// may not be generated in simple test scenarios. This test verifies the -// output format when it is generated. -workflowData := &WorkflowData{ -Permissions: "permissions:\n contents: read", -} + compiler := NewCompiler() + + // Test that when checkout is generated, it includes both folders + // Note: Due to complex logic in shouldAddCheckoutStep, the sparse checkout + // may not be generated in simple test scenarios. This test verifies the + // output format when it is generated. + workflowData := &WorkflowData{ + Permissions: "permissions:\n contents: read", + } -result := compiler.generateCheckoutGitHubFolder(workflowData) + result := compiler.generateCheckoutGitHubFolder(workflowData) -// If result is generated, verify it includes both .github and .agents -if result != nil { -checkoutStr := strings.Join(result, "") + // If result is generated, verify it includes both .github and .agents + if result != nil { + checkoutStr := strings.Join(result, "") -if !strings.Contains(checkoutStr, "Checkout .github and .agents folders") { -t.Errorf("Step name should mention both .github and .agents folders, got: %s", checkoutStr) -} - -if !strings.Contains(checkoutStr, ".github") || !strings.Contains(checkoutStr, ".agents") { -t.Errorf("Sparse checkout should include both .github and .agents folders, got: %s", checkoutStr) -} + if !strings.Contains(checkoutStr, "Checkout .github and .agents folders") { + t.Errorf("Step name should mention both .github and .agents folders, got: %s", checkoutStr) + } -t.Log("✓ Sparse checkout includes both .github and .agents folders") -} else { -t.Log("Sparse checkout not generated (expected with default logic)") -} + if !strings.Contains(checkoutStr, ".github") || !strings.Contains(checkoutStr, ".agents") { + t.Errorf("Sparse checkout should include both .github and .agents folders, got: %s", checkoutStr) + } -// Test negative cases -t.Run("without_contents_permission", func(t *testing.T) { -data := &WorkflowData{Permissions: "permissions: {}"} -if compiler.generateCheckoutGitHubFolder(data) != nil { -t.Error("Should not generate checkout without contents permission") -} -}) + t.Log("✓ Sparse checkout includes both .github and .agents folders") + } else { + t.Log("Sparse checkout not generated (expected with default logic)") + } -t.Run("with_action_tag", func(t *testing.T) { -data := &WorkflowData{ -Permissions: "permissions:\n contents: read", -Features: map[string]any{"action-tag": "abc123"}, -} -if compiler.generateCheckoutGitHubFolder(data) != nil { -t.Error("Should not generate checkout with action-tag") -} -}) + // Test negative cases + t.Run("without_contents_permission", func(t *testing.T) { + data := &WorkflowData{Permissions: "permissions: {}"} + if compiler.generateCheckoutGitHubFolder(data) != nil { + t.Error("Should not generate checkout without contents permission") + } + }) + + t.Run("with_action_tag", func(t *testing.T) { + data := &WorkflowData{ + Permissions: "permissions:\n contents: read", + Features: map[string]any{"action-tag": "abc123"}, + } + if compiler.generateCheckoutGitHubFolder(data) != nil { + t.Error("Should not generate checkout with action-tag") + } + }) } From ce6aff4a1d902218e36af7a8cb07cd639087554e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 13:33:15 +0000 Subject: [PATCH 4/5] Document that .agents folder is optional and handled gracefully Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_yaml_helpers.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/workflow/compiler_yaml_helpers.go b/pkg/workflow/compiler_yaml_helpers.go index ce5f49466b..7986d45af6 100644 --- a/pkg/workflow/compiler_yaml_helpers.go +++ b/pkg/workflow/compiler_yaml_helpers.go @@ -152,6 +152,10 @@ func (c *Compiler) generateCheckoutActionsFolder(data *WorkflowData) []string { // This checkout works in all modes (dev, script, release) and uses shallow clone // for minimal overhead. It should only be called in the main agent job. // +// Note: The sparse-checkout includes .agents for the new skills location, but this folder +// may not exist in all repositories. The actions/checkout action handles non-existent paths +// gracefully - it simply won't check them out without failing. +// // Returns a slice of strings that can be appended to a steps array, where each // string represents a line of YAML for the checkout step. Returns nil if: // - action-tag feature is specified (uses remote actions instead) From 9d6b77872abb3041e4e00a7346570e9628938309 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 5 Feb 2026 13:38:43 +0000 Subject: [PATCH 5/5] Remove redundant documentation about sparse-checkout behavior Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_yaml_helpers.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/workflow/compiler_yaml_helpers.go b/pkg/workflow/compiler_yaml_helpers.go index 7986d45af6..ce5f49466b 100644 --- a/pkg/workflow/compiler_yaml_helpers.go +++ b/pkg/workflow/compiler_yaml_helpers.go @@ -152,10 +152,6 @@ func (c *Compiler) generateCheckoutActionsFolder(data *WorkflowData) []string { // This checkout works in all modes (dev, script, release) and uses shallow clone // for minimal overhead. It should only be called in the main agent job. // -// Note: The sparse-checkout includes .agents for the new skills location, but this folder -// may not exist in all repositories. The actions/checkout action handles non-existent paths -// gracefully - it simply won't check them out without failing. -// // Returns a slice of strings that can be appended to a steps array, where each // string represents a line of YAML for the checkout step. Returns nil if: // - action-tag feature is specified (uses remote actions instead)