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
6 changes: 5 additions & 1 deletion pkg/workflow/engine_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,14 @@ func GenerateMultiSecretValidationStep(secretNames []string, engineName, docsURL
// Returns:
// - GitHubActionStep: The validation step, or an empty step if a custom command is set
func BuildDefaultSecretValidationStep(workflowData *WorkflowData, secrets []string, name, docsURL string) GitHubActionStep {
if workflowData.EngineConfig != nil && workflowData.EngineConfig.Command != "" {
if workflowData != nil && workflowData.EngineConfig != nil && workflowData.EngineConfig.Command != "" {
engineHelpersLog.Printf("Skipping secret validation step: custom command specified (%s)", workflowData.EngineConfig.Command)
return GitHubActionStep{}
}
if workflowData != nil && strings.TrimSpace(workflowData.Environment) != "" {
engineHelpersLog.Print("Skipping secret validation step: top-level environment is configured")
return GitHubActionStep{}
}
return GenerateMultiSecretValidationStep(secrets, name, docsURL, getEngineEnvOverrides(workflowData))
}

Expand Down
54 changes: 54 additions & 0 deletions pkg/workflow/secret_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"fmt"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenerateMultiSecretValidationStep(t *testing.T) {
Expand Down Expand Up @@ -280,3 +283,54 @@ func TestValidationStepUsesEngineEnvOverride(t *testing.T) {
})
}
}

func TestEngineSecretValidationSkippedWhenEnvironmentConfigured(t *testing.T) {
tests := []struct {
name string
engine CodingAgentEngine
}{
{
name: "copilot engine skips validation with environment",
engine: NewCopilotEngine(),
},
{
name: "claude engine skips validation with environment",
engine: NewClaudeEngine(),
},
{
name: "codex engine skips validation with environment",
engine: NewCodexEngine(),
},
{
name: "gemini engine skips validation with environment",
engine: NewGeminiEngine(),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
workflowData := &WorkflowData{
Environment: "production",
}

steps := tt.engine.GetSecretValidationStep(workflowData)
if len(steps) != 0 {
t.Fatalf("expected secret validation step to be skipped when environment is configured, got:\n%s", strings.Join(steps, "\n"))
}
})
}
}

func TestBuildDefaultSecretValidationStepHandlesNilWorkflowData(t *testing.T) {
step := BuildDefaultSecretValidationStep(
nil,
[]string{"COPILOT_GITHUB_TOKEN"},
"GitHub Copilot CLI",
"https://github.github.com/gh-aw/reference/engines/#github-copilot-default",
)

require.NotEmpty(t, step, "expected non-empty validation step for nil workflowData")

stepContent := strings.Join(step, "\n")
assert.Contains(t, stepContent, "Validate COPILOT_GITHUB_TOKEN secret", "expected validation step to include COPILOT_GITHUB_TOKEN check")
}
39 changes: 39 additions & 0 deletions pkg/workflow/secret_verification_output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,42 @@ Test workflow`
t.Error("Expected conclusion job to receive secret_verification_result from activation job")
}
}

func TestSecretVerificationOutputSkippedWithEnvironment(t *testing.T) {
testDir := testutil.TempDir(t, "secret-verify-env-*")
workflowFile := filepath.Join(testDir, "test-workflow.md")

workflow := `---
on: workflow_dispatch
engine: copilot
environment: production
---

Test workflow`

if err := os.WriteFile(workflowFile, []byte(workflow), 0o644); err != nil {
t.Fatalf("Failed to write test workflow: %v", err)
}

compiler := NewCompiler()
if err := compiler.CompileWorkflow(workflowFile); err != nil {
t.Fatalf("Failed to compile workflow: %v", err)
}

lockFile := stringutil.MarkdownToLockFile(workflowFile)
lockContent, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("Failed to read lock file: %v", err)
}

lockStr := string(lockContent)
if !strings.Contains(lockStr, "environment: production") {
t.Error("Expected compiled workflow to include environment: production")
}
if strings.Contains(lockStr, "id: validate-secret") {
t.Error("Expected validate-secret step to be skipped when top-level environment is configured")
}
if strings.Contains(lockStr, "secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}") {
t.Error("Expected secret_verification_result activation output to be skipped when top-level environment is configured")
}
}