Skip to content
7 changes: 7 additions & 0 deletions .github/workflows/dev.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-add-issue-comment.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-add-issue-labels.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-cache-memory.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-command.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-create-issue.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-create-pull-request.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-markitdown-mcp.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-max-patch-size.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-mcp.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-missing-tool.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-patch-size-exceeded.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/cli/workflows/test-copilot-update-issue.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions pkg/workflow/copilot_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ copilot %s 2>&1 | tee %s`, strings.Join(copilotArgs, " "), logFile)
}
}

// Add upload config step before running copilot CLI
uploadConfigStep := e.generateUploadConfigStep()
steps = append(steps, uploadConfigStep)

// Generate the step for Copilot CLI execution
stepName := "Execute GitHub Copilot CLI"
var stepLines []string
Expand Down Expand Up @@ -200,6 +204,21 @@ func (e *CopilotEngine) convertStepToYAML(stepMap map[string]any) (string, error
return ConvertStepToYAML(stepMap)
}

// generateUploadConfigStep generates a step to upload the XDG_CONFIG_HOME folder content
func (e *CopilotEngine) generateUploadConfigStep() GitHubActionStep {
var stepLines []string

stepLines = append(stepLines, " - name: Upload config")
stepLines = append(stepLines, " if: always()")
stepLines = append(stepLines, " uses: actions/upload-artifact@v4")
stepLines = append(stepLines, " with:")
stepLines = append(stepLines, " name: config")
stepLines = append(stepLines, " path: /tmp/.copilot/")
stepLines = append(stepLines, " if-no-files-found: ignore")

return GitHubActionStep(stepLines)
}

func (e *CopilotEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) {
// Build the MCP configuration structure
config := CopilotMCPConfig{
Expand Down
81 changes: 69 additions & 12 deletions pkg/workflow/copilot_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ func TestCopilotEngineExecutionSteps(t *testing.T) {
}
steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log")

if len(steps) != 2 {
t.Fatalf("Expected 2 steps for Copilot CLI execution (execution + log capture), got %d", len(steps))
if len(steps) != 3 {
t.Fatalf("Expected 3 steps (upload config + copilot execution + log capture), got %d", len(steps))
}

// Check the execution step
stepContent := strings.Join([]string(steps[0]), "\n")
// Check the execution step (second step)
stepContent := strings.Join([]string(steps[1]), "\n")

if !strings.Contains(stepContent, "name: Execute GitHub Copilot CLI") {
t.Errorf("Expected step name 'Execute GitHub Copilot CLI' in step content:\n%s", stepContent)
Expand Down Expand Up @@ -98,12 +98,12 @@ func TestCopilotEngineExecutionStepsWithOutput(t *testing.T) {
}
steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log")

if len(steps) != 2 {
t.Fatalf("Expected 2 steps for Copilot CLI execution with output (execution + log capture), got %d", len(steps))
if len(steps) != 3 {
t.Fatalf("Expected 3 steps (upload config + copilot execution + log capture) with output, got %d", len(steps))
}

// Check the execution step
stepContent := strings.Join([]string(steps[0]), "\n")
// Check the execution step (second step)
stepContent := strings.Join([]string(steps[1]), "\n")

// Test that GITHUB_AW_SAFE_OUTPUTS is present when SafeOutputs is not nil
if !strings.Contains(stepContent, "GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}") {
Expand Down Expand Up @@ -301,12 +301,30 @@ func TestCopilotEngineExecutionStepsWithToolArguments(t *testing.T) {
}
steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log")

if len(steps) != 2 {
t.Fatalf("Expected 2 steps for Copilot CLI execution with tools (execution + log capture), got %d", len(steps))
if len(steps) != 3 {
t.Fatalf("Expected 3 steps (upload config + copilot execution + log capture), got %d", len(steps))
}

// Check the execution step contains tool arguments
stepContent := strings.Join([]string(steps[0]), "\n")
// Check the upload config step (first step)
uploadStepContent := strings.Join([]string(steps[0]), "\n")
if !strings.Contains(uploadStepContent, "name: Upload config") {
t.Errorf("Expected first step to be upload config step:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "uses: actions/upload-artifact@v4") {
t.Errorf("Expected upload step to use actions/upload-artifact@v4:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "name: config") {
t.Errorf("Expected artifact name to be 'config':\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "path: /tmp/.copilot/") {
t.Errorf("Expected artifact path to be '/tmp/.copilot/':\n%s", uploadStepContent)
}

// Check the execution step contains tool arguments (second step)
stepContent := strings.Join([]string(steps[1]), "\n")

// Should contain the tool arguments in the command line
if !strings.Contains(stepContent, "--allow-tool shell(echo)") {
Expand Down Expand Up @@ -334,3 +352,42 @@ func TestCopilotEngineExecutionStepsWithToolArguments(t *testing.T) {
t.Errorf("Expected step to contain comment for write:\n%s", stepContent)
}
}

func TestCopilotEngineUploadConfigStep(t *testing.T) {
engine := NewCopilotEngine()
workflowData := &WorkflowData{
Name: "test-workflow",
}
steps := engine.GetExecutionSteps(workflowData, "/tmp/test.log")

if len(steps) != 3 {
t.Fatalf("Expected 3 steps (upload config + copilot execution + log capture), got %d", len(steps))
}

// Check the upload config step is present and correct
uploadStepContent := strings.Join([]string(steps[0]), "\n")

if !strings.Contains(uploadStepContent, "name: Upload config") {
t.Errorf("Expected upload config step name in:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "if: always()") {
t.Errorf("Expected upload config step to have 'if: always()' condition in:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "uses: actions/upload-artifact@v4") {
t.Errorf("Expected upload config step to use 'actions/upload-artifact@v4' in:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "name: config") {
t.Errorf("Expected artifact name 'config' in:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "path: /tmp/.copilot/") {
t.Errorf("Expected artifact path '/tmp/.copilot/' in:\n%s", uploadStepContent)
}

if !strings.Contains(uploadStepContent, "if-no-files-found: ignore") {
t.Errorf("Expected 'if-no-files-found: ignore' in:\n%s", uploadStepContent)
}
}
Loading