Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions pkg/workflow/compiler_yaml_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent logger variable usage. This file defines and uses compilerYamlHelpersLog at line 12, and all other functions in this file use that logger (see lines 31, 35, 49, 62). The log statements on lines 178, 185, and 192 should use compilerYamlHelpersLog instead of compilerYamlLog to be consistent with the rest of the file.

This issue also appears in the following locations of the same file:

  • line 185
  • line 192

Copilot uses AI. Check for mistakes.
// 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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot this is implied by sparse checkout

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the redundant note. Sparse-checkout behavior is indeed implied. (9d6b778)

" persist-credentials: false\n",
}
Expand Down
51 changes: 51 additions & 0 deletions pkg/workflow/compiler_yaml_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
})
}
14 changes: 11 additions & 3 deletions pkg/workflow/github_folder_checkout_optimization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
})
}
Expand Down