diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index 878ec718c8..d14d780fde 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -1287,6 +1287,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1}}" + GH_AW_ASSIGN_COPILOT: "true" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index a656a98d55..71be8e3471 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -1308,6 +1308,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1}}" + GH_AW_ASSIGN_COPILOT: "true" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/security-alert-burndown.lock.yml b/.github/workflows/security-alert-burndown.lock.yml index d5d1e81676..1adf066c01 100644 --- a/.github/workflows/security-alert-burndown.lock.yml +++ b/.github/workflows/security-alert-burndown.lock.yml @@ -2189,6 +2189,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_SAFE_OUTPUTS_PROJECT_HANDLER_CONFIG: "{\"create_project_status_update\":{\"max\":1},\"update_project\":{\"max\":100}}" + GH_AW_ASSIGN_COPILOT: "true" GH_AW_PROJECT_GITHUB_TOKEN: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_PROJECT_GITHUB_TOKEN }} @@ -2204,6 +2205,7 @@ jobs: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_TEMPORARY_PROJECT_MAP: ${{ steps.process_project_safe_outputs.outputs.temporary_project_map }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"assignees\":[\"copilot\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1}}" + GH_AW_ASSIGN_COPILOT: "true" with: github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -2211,17 +2213,4 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); - - name: Assign copilot to created issues - if: steps.process_safe_outputs.outputs.issues_to_assign_copilot != '' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - ISSUES_TO_ASSIGN_COPILOT: ${{ steps.process_safe_outputs.outputs.issues_to_assign_copilot }} - with: - github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/assign_copilot_to_created_issues.cjs'); - await main(); diff --git a/pkg/workflow/compiler_safe_outputs_env.go b/pkg/workflow/compiler_safe_outputs_env.go index 343dcb9e15..d44607f8b8 100644 --- a/pkg/workflow/compiler_safe_outputs_env.go +++ b/pkg/workflow/compiler_safe_outputs_env.go @@ -26,6 +26,11 @@ func (c *Compiler) addAllSafeOutputConfigEnvVars(steps *[]string, data *Workflow stagedFlagAdded = true compilerSafeOutputsEnvLog.Print("Added staged flag for create-issue") } + // Check if copilot is in assignees - if so, we'll output issues for assign_to_agent job + if hasCopilotAssignee(cfg.Assignees) { + *steps = append(*steps, " GH_AW_ASSIGN_COPILOT: \"true\"\n") + compilerSafeOutputsEnvLog.Print("Copilot assignment requested - will output issues_to_assign_copilot") + } } // Add Comment - all config now in handler config JSON diff --git a/pkg/workflow/compiler_safe_outputs_env_copilot_test.go b/pkg/workflow/compiler_safe_outputs_env_copilot_test.go new file mode 100644 index 0000000000..fb64d8a644 --- /dev/null +++ b/pkg/workflow/compiler_safe_outputs_env_copilot_test.go @@ -0,0 +1,132 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestCopilotAssignmentEnvVarIsSet verifies that GH_AW_ASSIGN_COPILOT is set +// when copilot is in the assignees list +func TestCopilotAssignmentEnvVarIsSet(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "Test", + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + Assignees: []string{"copilot"}, + }, + }, + } + + var steps []string + compiler.addAllSafeOutputConfigEnvVars(&steps, data) + + // Join steps to search for the env var + stepsStr := strings.Join(steps, "") + + assert.Contains(t, stepsStr, "GH_AW_ASSIGN_COPILOT", "Expected GH_AW_ASSIGN_COPILOT to be set when copilot is in assignees") + assert.Contains(t, stepsStr, `GH_AW_ASSIGN_COPILOT: "true"`, "Expected GH_AW_ASSIGN_COPILOT to be set to 'true'") +} + +// TestCopilotAssignmentEnvVarNotSetWithoutCopilot verifies that GH_AW_ASSIGN_COPILOT +// is not set when copilot is not in the assignees list +func TestCopilotAssignmentEnvVarNotSetWithoutCopilot(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "Test", + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + Assignees: []string{"user1"}, + }, + }, + } + + var steps []string + compiler.addAllSafeOutputConfigEnvVars(&steps, data) + + // Join steps to search for the env var + stepsStr := strings.Join(steps, "") + + assert.NotContains(t, stepsStr, "GH_AW_ASSIGN_COPILOT", "Expected GH_AW_ASSIGN_COPILOT not to be set when copilot is not in assignees") +} + +// TestCopilotAssignmentEnvVarWithMixedAssignees verifies that GH_AW_ASSIGN_COPILOT is set +// when copilot is in the assignees list along with other users +func TestCopilotAssignmentEnvVarWithMixedAssignees(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "Test", + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + Assignees: []string{"user1", "copilot", "user2"}, + }, + }, + } + + var steps []string + compiler.addAllSafeOutputConfigEnvVars(&steps, data) + + // Join steps to search for the env var + stepsStr := strings.Join(steps, "") + + assert.Contains(t, stepsStr, "GH_AW_ASSIGN_COPILOT", "Expected GH_AW_ASSIGN_COPILOT to be set when copilot is among multiple assignees") + assert.Contains(t, stepsStr, `GH_AW_ASSIGN_COPILOT: "true"`, "Expected GH_AW_ASSIGN_COPILOT to be set to 'true'") +} + +// TestCopilotAssignmentEnvVarWithNilAssignees verifies that GH_AW_ASSIGN_COPILOT +// is not set when assignees field is nil +func TestCopilotAssignmentEnvVarWithNilAssignees(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "Test", + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + Assignees: nil, + }, + }, + } + + var steps []string + compiler.addAllSafeOutputConfigEnvVars(&steps, data) + + // Join steps to search for the env var + stepsStr := strings.Join(steps, "") + + assert.NotContains(t, stepsStr, "GH_AW_ASSIGN_COPILOT", "Expected GH_AW_ASSIGN_COPILOT not to be set when assignees is nil") +} + +// TestCopilotAssignmentEnvVarWithEmptyAssignees verifies that GH_AW_ASSIGN_COPILOT +// is not set when assignees array is empty +func TestCopilotAssignmentEnvVarWithEmptyAssignees(t *testing.T) { + compiler := NewCompiler() + + data := &WorkflowData{ + Name: "Test", + SafeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + Assignees: []string{}, + }, + }, + } + + var steps []string + compiler.addAllSafeOutputConfigEnvVars(&steps, data) + + // Join steps to search for the env var + stepsStr := strings.Join(steps, "") + + assert.NotContains(t, stepsStr, "GH_AW_ASSIGN_COPILOT", "Expected GH_AW_ASSIGN_COPILOT not to be set when assignees is empty") +}