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
16 changes: 5 additions & 11 deletions pkg/workflow/action_pins.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func GetActionPin(actionRepo string) string {

// GetActionPinWithData returns the pinned action reference for a given action@version
// It tries dynamic resolution first, then falls back to hardcoded pins
// If strictMode is true and resolution fails, it returns an error
// If resolution fails, emits a warning and returns empty string (in both strict and non-strict modes)
// The returned reference includes a comment with the version tag (e.g., "repo@sha # v1")
func GetActionPinWithData(actionRepo, version string, data *WorkflowData) (string, error) {
actionPinsLog.Printf("Resolving action pin: repo=%s, version=%s, strict_mode=%t", actionRepo, version, data.StrictMode)
Expand Down Expand Up @@ -272,17 +272,11 @@ func GetActionPinWithData(actionRepo, version string, data *WorkflowData) (strin
}
}

// No pin available
if data.StrictMode {
errMsg := fmt.Sprintf("Unable to pin action %s@%s", actionRepo, version)
if data.ActionResolver != nil {
errMsg = fmt.Sprintf("Unable to pin action %s@%s: resolution failed", actionRepo, version)
}
fmt.Fprintln(os.Stderr, console.FormatErrorMessage(errMsg))
return "", fmt.Errorf("%s", errMsg)
}
// No pin available - emit warning in both strict and non-strict modes
// Note: This used to fail in strict mode, but it's not always possible to resolve pins
// so we now emit a warning and continue compilation in both modes

// In non-strict mode, emit warning and return empty string (unless it's already a SHA)
// In both strict and non-strict mode, emit warning and return empty string (unless it's already a SHA)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Grammar: "In both strict and non-strict mode" should be "In both strict and non-strict modes".

Suggested change
// In both strict and non-strict mode, emit warning and return empty string (unless it's already a SHA)
// In both strict and non-strict modes, emit warning and return empty string (unless it's already a SHA)

Copilot uses AI. Check for mistakes.
if isAlreadySHA {
// If version is already a SHA and we couldn't find it in pins, return it as-is without warnings
actionPinsLog.Printf("SHA %s not found in hardcoded pins, returning as-is", version)
Expand Down
53 changes: 34 additions & 19 deletions pkg/workflow/action_pins_logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,33 +117,38 @@ func TestActionPinResolutionWithMismatchedVersions(t *testing.T) {
}
}

// TestActionPinResolutionWithStrictMode tests that strict mode prevents version mismatches
// TestActionPinResolutionWithStrictMode tests action pin resolution in strict mode
// Note: Strict mode now emits warnings instead of errors when SHA resolution fails,
// as it's not always possible to resolve pins
func TestActionPinResolutionWithStrictMode(t *testing.T) {
tests := []struct {
name string
repo string
requestedVer string
expectError bool
name string
repo string
requestedVer string
expectWarning bool
expectSuccess bool
}{
{
name: "ai-inference v1 fails in strict mode",
repo: "actions/ai-inference",
requestedVer: "v1",
expectError: true,
name: "ai-inference v1 emits warning in strict mode",
repo: "actions/ai-inference",
requestedVer: "v1",
expectWarning: true,
expectSuccess: false,
},
{
name: "checkout v5.0.1 succeeds in strict mode",
repo: "actions/checkout",
requestedVer: "v5.0.1",
expectError: false,
name: "checkout v5.0.1 succeeds in strict mode",
repo: "actions/checkout",
requestedVer: "v5.0.1",
expectWarning: false,
expectSuccess: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a WorkflowData in strict mode without a resolver
data := &WorkflowData{
StrictMode: true, // Strict mode should error on version mismatch
StrictMode: true,
ActionResolver: nil,
}

Expand All @@ -159,15 +164,25 @@ func TestActionPinResolutionWithStrictMode(t *testing.T) {

var buf bytes.Buffer
buf.ReadFrom(r)
stderrOutput := buf.String()

if tt.expectError {
if err == nil {
t.Errorf("Expected error in strict mode for %s@%s, got nil", tt.repo, tt.requestedVer)
// Strict mode should never return an error for resolution failures
if err != nil {
t.Errorf("Unexpected error in strict mode for %s@%s: %v", tt.repo, tt.requestedVer, err)
}

if tt.expectWarning {
// Should emit warning and return empty result
if !strings.Contains(stderrOutput, "Unable to pin action") {
t.Errorf("Expected warning message for %s@%s, got: %s", tt.repo, tt.requestedVer, stderrOutput)
}
if result != "" {
t.Errorf("Expected empty result on error, got: %s", result)
t.Errorf("Expected empty result on warning, got: %s", result)
}
} else {
}

if tt.expectSuccess {
// Should not emit warning and return non-empty result
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
Expand Down
Loading