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
2 changes: 1 addition & 1 deletion pkg/actionpins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Resolution supports two modes:
| `GetActionPinsByRepo` | `func(repo string) []ActionPin` | Returns all pins for a repository (version-descending) |
| `GetActionPinByRepo` | `func(repo string) (ActionPin, bool)` | Returns the latest pin for a repository |
| `GetContainerPin` | `func(image string) (ContainerPin, bool)` | Returns a pinned container image by its original image reference |
| `FormatReference` | `func(repo, sha, version string) string` | Formats a pinned reference (`repo@sha # version`) |
| `FormatPinnedActionReference` | `func(repo, sha, version string) string` | Formats a pinned action reference string (`repo@sha # version`) |
| `FormatCacheKey` | `func(repo, version string) string` | Formats a cache key (`repo@version`) |
| `ExtractRepo` | `func(uses string) string` | Extracts the repository from a `uses` reference |
| `ExtractVersion` | `func(uses string) string` | Extracts the version from a `uses` reference |
Expand Down
25 changes: 14 additions & 11 deletions pkg/actionpins/actionpins.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,12 @@ func getLatestActionPinReference(repo string) string {
if len(pins) == 0 {
return ""
}
return FormatReference(repo, pins[0].SHA, pins[0].Version)
return FormatPinnedActionReference(repo, pins[0].SHA, pins[0].Version)
}

// FormatReference formats an action reference with repo, SHA, and version comment.
// FormatPinnedActionReference formats a pinned action reference with repo, SHA, and version comment.
// Example: "actions/checkout@abc123 # v4.1.0"
func FormatReference(repo, sha, version string) string {
func FormatPinnedActionReference(repo, sha, version string) string {
return fmt.Sprintf("%s@%s # %s", repo, sha, version)
}
Comment on lines +216 to 220

Expand Down Expand Up @@ -268,7 +268,10 @@ func initWarnings(ctx *PinContext) {
}
}

func notifyResolutionFailure(ctx *PinContext, actionRepo, version string, errorType ResolutionErrorType) {
// recordPinResolutionFailure silently records an unresolved action-ref pinning event
// to the audit callback (ctx.RecordResolutionFailure), if one is configured.
// If ctx is nil or ctx.RecordResolutionFailure is nil, the function returns early without recording.
func recordPinResolutionFailure(ctx *PinContext, actionRepo, version string, errorType ResolutionErrorType) {
if ctx == nil || ctx.RecordResolutionFailure == nil {
return
}
Expand All @@ -295,7 +298,7 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro
sha, err := ctx.Resolver.ResolveSHA(actionRepo, version)
if err == nil && sha != "" {
log.Printf("Dynamic resolution succeeded: %s@%s → %s", actionRepo, version, sha)
result := FormatReference(actionRepo, sha, version)
result := FormatPinnedActionReference(actionRepo, sha, version)
log.Printf("Returning pinned reference: %s", result)
return result, nil
}
Expand All @@ -319,19 +322,19 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro
for _, pin := range matchingPins {
if pin.Version == version {
log.Printf("Exact version match: requested=%s, found=%s", version, pin.Version)
return FormatReference(actionRepo, pin.SHA, pin.Version), nil
return FormatPinnedActionReference(actionRepo, pin.SHA, pin.Version), nil
}
}

if isAlreadySHA {
for _, pin := range matchingPins {
if pin.SHA == version {
log.Printf("Exact SHA match: requested=%s, found version=%s", version, pin.Version)
return FormatReference(actionRepo, pin.SHA, pin.Version), nil
return FormatPinnedActionReference(actionRepo, pin.SHA, pin.Version), nil
}
}
log.Printf("SHA %s not found in hardcoded pins, returning as-is", version)
return FormatReference(actionRepo, version, version), nil
return FormatPinnedActionReference(actionRepo, version, version), nil
}

if !ctx.StrictMode && len(matchingPins) > 0 {
Expand All @@ -355,13 +358,13 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro
}
log.Printf("Using version in non-strict mode: %s@%s (requested) → %s@%s (used)",
actionRepo, version, actionRepo, selectedPin.Version)
return FormatReference(actionRepo, selectedPin.SHA, version), nil
return FormatPinnedActionReference(actionRepo, selectedPin.SHA, version), nil
}
}

if isAlreadySHA {
log.Printf("SHA %s not found in hardcoded pins, returning as-is", version)
return FormatReference(actionRepo, version, version), nil
return FormatPinnedActionReference(actionRepo, version, version), nil
}

initWarnings(ctx)
Expand All @@ -370,7 +373,7 @@ func ResolveActionPin(actionRepo, version string, ctx *PinContext) (string, erro
if ctx.Resolver != nil {
errorType = ResolutionErrorTypeDynamicResolutionFailed
}
notifyResolutionFailure(ctx, actionRepo, version, errorType)
recordPinResolutionFailure(ctx, actionRepo, version, errorType)
if ctx.EnforcePinned && !ctx.AllowActionRefs {
if ctx.Resolver != nil {
return "", fmt.Errorf("unable to pin action %s@%s: resolution failed", actionRepo, version)
Expand Down
14 changes: 7 additions & 7 deletions pkg/actionpins/spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"github.com/github/gh-aw/pkg/actionpins"
)

// TestSpec_PublicAPI_FormatReference validates the documented format "repo@sha # version".
func TestSpec_PublicAPI_FormatReference(t *testing.T) {
// TestSpec_PublicAPI_FormatPinnedActionReference validates the documented format "repo@sha # version".
func TestSpec_PublicAPI_FormatPinnedActionReference(t *testing.T) {
tests := []struct {
name string
repo string
Expand All @@ -39,8 +39,8 @@ func TestSpec_PublicAPI_FormatReference(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := actionpins.FormatReference(tt.repo, tt.sha, tt.version)
assert.Equal(t, tt.expected, result, "FormatReference(%q, %q, %q) should match spec format", tt.repo, tt.sha, tt.version)
result := actionpins.FormatPinnedActionReference(tt.repo, tt.sha, tt.version)
assert.Equal(t, tt.expected, result, "FormatPinnedActionReference(%q, %q, %q) should match spec format", tt.repo, tt.sha, tt.version)
})
}
}
Expand Down Expand Up @@ -180,7 +180,7 @@ func TestSpec_PublicAPI_ResolveLatestActionPin(t *testing.T) {
require.True(t, ok, "expected latest pin for known repository")

result := actionpins.ResolveLatestActionPin(known, nil)
expected := actionpins.FormatReference(known, latestPin.SHA, latestPin.Version)
expected := actionpins.FormatPinnedActionReference(known, latestPin.SHA, latestPin.Version)
assert.Equal(t, expected, result, "should resolve latest pinned reference")
})
}
Expand All @@ -199,15 +199,15 @@ func TestSpec_Types_PinContext(t *testing.T) {
})
}

// TestSpec_DesignDecision_FormatConsistency validates that FormatReference and FormatCacheKey
// TestSpec_DesignDecision_FormatConsistency validates that FormatPinnedActionReference and FormatCacheKey
// produce outputs consistent with the spec: cacheKey = "repo@version", ref = "repo@sha # version".
func TestSpec_DesignDecision_FormatConsistency(t *testing.T) {
repo := "actions/checkout"
version := "v4"
sha := "deadbeef"

cacheKey := actionpins.FormatCacheKey(repo, version)
reference := actionpins.FormatReference(repo, sha, version)
reference := actionpins.FormatPinnedActionReference(repo, sha, version)

assert.True(t, strings.HasPrefix(cacheKey, repo+"@"), "cache key should be repo@version")
assert.True(t, strings.HasPrefix(reference, repo+"@"), "reference should start with repo@sha")
Expand Down
4 changes: 2 additions & 2 deletions pkg/workflow/action_pins.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ActionPinsData = actionpins.ActionPinsData

// formatActionReference formats an action reference with repo, SHA, and version.
func formatActionReference(repo, sha, version string) string {
return actionpins.FormatReference(repo, sha, version)
return actionpins.FormatPinnedActionReference(repo, sha, version)
}

// formatActionCacheKey generates a cache key for action resolution.
Expand All @@ -54,7 +54,7 @@ func getActionPin(repo string) string {
actionPinsLog.Printf("No embedded pins found for repo: %s", repo)
return ""
}
return actionpins.FormatReference(repo, pins[0].SHA, pins[0].Version)
return actionpins.FormatPinnedActionReference(repo, pins[0].SHA, pins[0].Version)
}

// getCachedActionPinFromResolver returns the pinned action reference for repo,
Expand Down
Loading