From 7e82ec4f42c7a46f3700cc3cf66455e8f20d4938 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:07:10 +0000 Subject: [PATCH 1/4] Initial plan From 7dbac389f95082b80cb579f84c84f89797bade20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:20:17 +0000 Subject: [PATCH 2/4] Fix expression errors by making secret_verification_result output conditional Only add secret_verification_result output when validate-secret step exists. Custom engine doesn't include this step, but Copilot/Claude/Codex do. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/issue-classifier.lock.yml | 2 -- .github/workflows/smoke-opencode.lock.yml | 2 -- pkg/workflow/compiler_activation_jobs.go | 29 +++++++++++++++++++-- pkg/workflow/notify_comment.go | 25 +++++++++++++++++- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index ed2d8ed7a4..0adf7c151d 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -119,7 +119,6 @@ jobs: model: ${{ steps.generate_aw_info.outputs.model }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Checkout actions folder uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 @@ -771,7 +770,6 @@ jobs: GH_AW_WORKFLOW_NAME: "Issue Classifier" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 9264f7bf37..18721a1861 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -121,7 +121,6 @@ jobs: model: ${{ steps.generate_aw_info.outputs.model }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Checkout actions folder uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 @@ -1494,7 +1493,6 @@ jobs: GH_AW_WORKFLOW_NAME: "Smoke OpenCode" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🚀 *[Liftoff Complete] — Powered by [{workflow_name}]({run_url})*\",\"runStarted\":\"🚀 **IGNITION!** [{workflow_name}]({run_url}) launching for this {event_type}! *[T-minus counting...]*\",\"runSuccess\":\"🎯 **MISSION SUCCESS** — [{workflow_name}]({run_url}) **TARGET ACQUIRED!** All systems nominal! ✨\",\"runFailure\":\"⚠️ **MISSION ABORT...** [{workflow_name}]({run_url}) {status}! Houston, we have a problem...\"}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/pkg/workflow/compiler_activation_jobs.go b/pkg/workflow/compiler_activation_jobs.go index 9a87da90a4..5cb9dcf640 100644 --- a/pkg/workflow/compiler_activation_jobs.go +++ b/pkg/workflow/compiler_activation_jobs.go @@ -711,8 +711,33 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) ( // Build job outputs // Always include model output for reuse in other jobs outputs := map[string]string{ - "model": "${{ steps.generate_aw_info.outputs.model }}", - "secret_verification_result": "${{ steps.validate-secret.outputs.verification_result }}", + "model": "${{ steps.generate_aw_info.outputs.model }}", + } + + // Only add secret_verification_result output if the engine adds the validate-secret step + // The validate-secret step is only added by engines that include it in GetInstallationSteps() + engine, err := c.getAgenticEngine(data.AI) + if err != nil { + return nil, fmt.Errorf("failed to get agentic engine: %w", err) + } + installSteps := engine.GetInstallationSteps(data) + hasValidateSecretStep := false + for _, step := range installSteps { + for _, line := range step { + if strings.Contains(line, "id: validate-secret") { + hasValidateSecretStep = true + break + } + } + if hasValidateSecretStep { + break + } + } + if hasValidateSecretStep { + outputs["secret_verification_result"] = "${{ steps.validate-secret.outputs.verification_result }}" + compilerActivationJobsLog.Printf("Added secret_verification_result output (engine includes validate-secret step)") + } else { + compilerActivationJobsLog.Printf("Skipped secret_verification_result output (engine does not include validate-secret step)") } // Add safe-output specific outputs if the workflow uses the safe-outputs feature diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index 9a3f5b2f19..c4c6be7e8e 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -3,6 +3,7 @@ package workflow import ( "encoding/json" "fmt" + "strings" "github.com/githubnext/gh-aw/pkg/constants" "github.com/githubnext/gh-aw/pkg/logger" @@ -151,7 +152,29 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa agentFailureEnvVars = append(agentFailureEnvVars, buildWorkflowMetadataEnvVarsWithTrackerID(data.Name, data.Source, data.TrackerID)...) agentFailureEnvVars = append(agentFailureEnvVars, " GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n") agentFailureEnvVars = append(agentFailureEnvVars, fmt.Sprintf(" GH_AW_AGENT_CONCLUSION: ${{ needs.%s.result }}\n", mainJobName)) - agentFailureEnvVars = append(agentFailureEnvVars, fmt.Sprintf(" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.%s.outputs.secret_verification_result }}\n", mainJobName)) + + // Only add secret_verification_result if the engine adds the validate-secret step + // The validate-secret step is only added by engines that include it in GetInstallationSteps() + engine, err := c.getAgenticEngine(data.AI) + if err != nil { + return nil, fmt.Errorf("failed to get agentic engine: %w", err) + } + installSteps := engine.GetInstallationSteps(data) + hasValidateSecretStep := false + for _, step := range installSteps { + for _, line := range step { + if strings.Contains(line, "id: validate-secret") { + hasValidateSecretStep = true + break + } + } + if hasValidateSecretStep { + break + } + } + if hasValidateSecretStep { + agentFailureEnvVars = append(agentFailureEnvVars, fmt.Sprintf(" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.%s.outputs.secret_verification_result }}\n", mainJobName)) + } // Pass assignment error outputs from safe_outputs job if assign-to-agent is configured if data.SafeOutputs != nil && data.SafeOutputs.AssignToAgent != nil { From 410db3987fd3979f648747f5281a74f6ceb4a25f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:25:59 +0000 Subject: [PATCH 3/4] Refactor: Extract EngineHasValidateSecretStep helper to reduce duplication Addresses code review feedback by extracting duplicate logic into a shared helper function. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_activation_jobs.go | 15 +------------ pkg/workflow/engine_helpers.go | 27 ++++++++++++++++++++++++ pkg/workflow/notify_comment.go | 16 +------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkg/workflow/compiler_activation_jobs.go b/pkg/workflow/compiler_activation_jobs.go index 5cb9dcf640..4fcb0e0cf4 100644 --- a/pkg/workflow/compiler_activation_jobs.go +++ b/pkg/workflow/compiler_activation_jobs.go @@ -720,20 +720,7 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) ( if err != nil { return nil, fmt.Errorf("failed to get agentic engine: %w", err) } - installSteps := engine.GetInstallationSteps(data) - hasValidateSecretStep := false - for _, step := range installSteps { - for _, line := range step { - if strings.Contains(line, "id: validate-secret") { - hasValidateSecretStep = true - break - } - } - if hasValidateSecretStep { - break - } - } - if hasValidateSecretStep { + if EngineHasValidateSecretStep(engine, data) { outputs["secret_verification_result"] = "${{ steps.validate-secret.outputs.verification_result }}" compilerActivationJobsLog.Printf("Added secret_verification_result output (engine includes validate-secret step)") } else { diff --git a/pkg/workflow/engine_helpers.go b/pkg/workflow/engine_helpers.go index a65c75013c..6ccc107e6f 100644 --- a/pkg/workflow/engine_helpers.go +++ b/pkg/workflow/engine_helpers.go @@ -441,3 +441,30 @@ func GetToolBinsEnvArg() []string { // Pre-wrap in double quotes so shellEscapeArg preserves them (allowing shell expansion) return []string{"--env", "\"GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS\""} } + +// EngineHasValidateSecretStep checks if the engine's installation steps include the validate-secret step. +// This is used to determine whether the secret_verification_result job output should be added. +// +// The validate-secret step is only added by engines that include it in GetInstallationSteps(): +// - Copilot engine: Adds step when GetRequiredSecretNames returns non-empty +// - Claude engine: Adds step when GetRequiredSecretNames returns non-empty +// - Codex engine: Adds step when GetRequiredSecretNames returns non-empty +// - Custom engine: Never adds this step (returns empty from GetInstallationSteps) +// +// Parameters: +// - engine: The agentic engine to check +// - data: The workflow data (needed for GetInstallationSteps) +// +// Returns: +// - bool: true if the engine includes the validate-secret step, false otherwise +func EngineHasValidateSecretStep(engine CodingAgentEngine, data *WorkflowData) bool { + installSteps := engine.GetInstallationSteps(data) + for _, step := range installSteps { + for _, line := range step { + if strings.Contains(line, "id: validate-secret") { + return true + } + } + } + return false +} diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index c4c6be7e8e..6ea2962999 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -3,7 +3,6 @@ package workflow import ( "encoding/json" "fmt" - "strings" "github.com/githubnext/gh-aw/pkg/constants" "github.com/githubnext/gh-aw/pkg/logger" @@ -159,20 +158,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa if err != nil { return nil, fmt.Errorf("failed to get agentic engine: %w", err) } - installSteps := engine.GetInstallationSteps(data) - hasValidateSecretStep := false - for _, step := range installSteps { - for _, line := range step { - if strings.Contains(line, "id: validate-secret") { - hasValidateSecretStep = true - break - } - } - if hasValidateSecretStep { - break - } - } - if hasValidateSecretStep { + if EngineHasValidateSecretStep(engine, data) { agentFailureEnvVars = append(agentFailureEnvVars, fmt.Sprintf(" GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.%s.outputs.secret_verification_result }}\n", mainJobName)) } From 33fe549c49fe918c0e6e34a32f33e7350c38eb8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:27:05 +0000 Subject: [PATCH 4/4] Add implementation note to EngineHasValidateSecretStep Addresses code review feedback by adding documentation explaining why string matching is safe. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/engine_helpers.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/workflow/engine_helpers.go b/pkg/workflow/engine_helpers.go index 6ccc107e6f..2936319310 100644 --- a/pkg/workflow/engine_helpers.go +++ b/pkg/workflow/engine_helpers.go @@ -451,6 +451,12 @@ func GetToolBinsEnvArg() []string { // - Codex engine: Adds step when GetRequiredSecretNames returns non-empty // - Custom engine: Never adds this step (returns empty from GetInstallationSteps) // +// Implementation Note: +// This uses simple string matching which is acceptable because: +// - Installation steps are generated by our code, not user input +// - The "id: validate-secret" format is controlled by GenerateMultiSecretValidationStep() +// - GitHubActionStep is already a string slice, not structured YAML +// // Parameters: // - engine: The agentic engine to check // - data: The workflow data (needed for GetInstallationSteps) @@ -461,6 +467,8 @@ func EngineHasValidateSecretStep(engine CodingAgentEngine, data *WorkflowData) b installSteps := engine.GetInstallationSteps(data) for _, step := range installSteps { for _, line := range step { + // String matching is safe here because installation steps are generated by our code + // and follow the format: " id: validate-secret" if strings.Contains(line, "id: validate-secret") { return true }