{{ message }}
Repository navigation
Expand file tree
/
Copy pathaction_reference.go
More file actions
317 lines (284 loc) · 12.7 KB
/
Copy pathaction_reference.go
File metadata and controls
317 lines (284 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
package workflow
import (
"context"
"fmt"
"strings"
"github.com/github/gh-aw/pkg/logger"
)
var actionRefLog = logger.New("workflow:action_reference")
const (
// GitHubOrgRepo is the organization and repository name for custom action references
GitHubOrgRepo = "github/gh-aw"
// GitHubActionsOrgRepo is the organization and repository name for the external gh-aw-actions repository
GitHubActionsOrgRepo = "github/gh-aw-actions"
)
// ResolveSetupActionReference resolves the actions/setup action reference based on action mode and version.
// This is a standalone helper function that can be used by both Compiler methods and standalone
// workflow generators (like maintenance workflow) that don't have access to WorkflowData.
//
// Parameters:
// - actionMode: The action mode (dev, release, or action)
// - version: The version string to use for release/action mode
// - actionTag: Optional override tag/SHA (takes precedence over version when in release mode)
// - resolver: Optional SHAResolver for dynamic SHA resolution (can be nil for standalone use)
//
// Returns:
// - For dev mode: "./actions/setup" (local path)
// - For release mode with resolver: "github/gh-aw/actions/setup@<sha> # <version>" (SHA-pinned)
// - For release mode without resolver: "github/gh-aw/actions/setup@<version>" (tag-based, SHA resolved later)
// - For action mode with resolver: "github/gh-aw-actions/setup@<sha> # <version>" (SHA-pinned)
// - For action mode without resolver: "github/gh-aw-actions/setup@<version>" (tag-based, SHA resolved later)
// - Falls back to local path if version is invalid in release/action mode
func ResolveSetupActionReference(ctx context.Context, actionMode ActionMode, version string, actionTag string, resolver SHAResolver) string {
return resolveSetupActionRef(ctx, actionMode, version, actionTag, resolver, "")
}
// resolveSetupActionRef is the internal implementation of ResolveSetupActionReference
// that accepts an optional actionsOrgRepo override. When actionsOrgRepo is empty,
// GitHubActionsOrgRepo is used.
func resolveSetupActionRef(ctx context.Context, actionMode ActionMode, version string, actionTag string, resolver SHAResolver, actionsOrgRepo string) string {
if actionsOrgRepo == "" {
actionsOrgRepo = GitHubActionsOrgRepo
}
localPath := "./actions/setup"
if actionMode == ActionModeDev {
actionRefLog.Printf("Dev mode: using local action path: %s", localPath)
return localPath
}
if actionMode == ActionModeAction {
return resolveSetupActionModeRef(ctx, actionTag, version, resolver, actionsOrgRepo, localPath)
}
if actionMode == ActionModeRelease {
return resolveSetupReleaseModeRef(ctx, actionTag, version, resolver, localPath)
}
actionRefLog.Printf("WARNING: Unknown action mode %s, defaulting to local path", actionMode)
return localPath
}
func resolveSetupActionModeRef(ctx context.Context, actionTag string, version string, resolver SHAResolver, actionsOrgRepo string, localPath string) string {
tag, ok := resolveSetupTag(actionTag, version, localPath)
if !ok {
return localPath
}
actionRepo := actionsOrgRepo + "/setup"
remoteRef := fmt.Sprintf("%s@%s", actionRepo, tag)
ref := tryResolveSetupSHA(ctx, resolver, actionRepo, tag, remoteRef, "Action mode")
if ref != "" {
return ref
}
actionRefLog.Printf("Action mode: using tag-based external actions repo reference: %s (SHA will be resolved later)", remoteRef)
return remoteRef
}
func resolveSetupReleaseModeRef(ctx context.Context, actionTag string, version string, resolver SHAResolver, localPath string) string {
tag, ok := resolveSetupTag(actionTag, version, localPath)
if !ok {
return localPath
}
actionPath := strings.TrimPrefix(localPath, "./")
actionRepo := fmt.Sprintf("%s/%s", GitHubOrgRepo, actionPath)
remoteRef := fmt.Sprintf("%s@%s", actionRepo, tag)
ref := tryResolveSetupSHA(ctx, resolver, actionRepo, tag, remoteRef, "Release mode")
if ref != "" {
return ref
}
actionRefLog.Printf("Release mode: using tag-based remote action reference: %s (SHA will be resolved later)", remoteRef)
return remoteRef
}
func resolveSetupTag(actionTag string, version string, localPath string) (string, bool) {
tag := actionTag
if tag == "" {
tag = version
}
if tag == "" || tag == "dev" {
actionRefLog.Printf("WARNING: No release tag available in binary version (version is 'dev' or empty), falling back to local path: %s", localPath)
return "", false
}
return tag, true
}
func tryResolveSetupSHA(ctx context.Context, resolver SHAResolver, actionRepo, tag, remoteRef, modeLabel string) string {
if resolver == nil {
return ""
}
sha, err := resolver.ResolveSHA(ctx, actionRepo, tag)
if err == nil && sha != "" {
pinnedRef := formatActionReference(actionRepo, sha, tag)
actionRefLog.Printf("%s: resolved %s to SHA-pinned reference: %s", modeLabel, remoteRef, pinnedRef)
return pinnedRef
}
if err != nil {
actionRefLog.Printf("Failed to resolve SHA for %s@%s: %v", actionRepo, tag, err)
}
return ""
}
// resolveActionReference converts a local action path to the appropriate reference
// based on the current action mode (dev vs release vs action).
// If action-tag is specified in features, it overrides the mode check and enables action mode behavior
// (using the github/gh-aw-actions external repository).
// For dev mode: returns the local path as-is (e.g., "./actions/create-issue")
// For release mode: converts to SHA-pinned remote reference (e.g., "github/gh-aw/actions/create-issue@SHA # tag")
// For action mode: converts to SHA-pinned reference in external repo if possible (e.g., "github/gh-aw-actions/create-issue@SHA # version")
func (c *Compiler) resolveActionReference(localActionPath string, data *WorkflowData) string {
hasActionTag, frontmatterActionTag := getFrontmatterActionTag(data)
// For ./actions/setup, check for compiler-level actionTag override first
if localActionPath == "./actions/setup" {
return c.resolveSetupReference(data, hasActionTag, frontmatterActionTag)
}
if c.actionMode == ActionModeAction || hasActionTag {
return c.convertToExternalActionsRef(localActionPath, data)
}
if c.actionMode == ActionModeRelease {
return c.resolveReleaseActionReference(localActionPath, data)
}
if c.actionMode == ActionModeDev {
actionRefLog.Printf("Dev mode: using local action path: %s", localActionPath)
return localActionPath
}
// Default to dev mode for unknown modes
actionRefLog.Printf("WARNING: Unknown action mode %s, defaulting to dev mode", c.actionMode)
return localActionPath
}
func getFrontmatterActionTag(data *WorkflowData) (bool, string) {
if data == nil || data.Features == nil {
return false, ""
}
actionTagVal, exists := data.Features["action-tag"]
if !exists {
return false, ""
}
actionTagStr, ok := actionTagVal.(string)
if !ok || actionTagStr == "" {
return false, ""
}
actionRefLog.Printf("action-tag feature detected: %s - using action mode behavior", actionTagStr)
return true, actionTagStr
}
func (c *Compiler) resolveSetupReference(data *WorkflowData, hasActionTag bool, frontmatterActionTag string) string {
var resolver SHAResolver
if data != nil && data.ActionResolver != nil {
resolver = data.ActionResolver
}
if c.actionTag != "" {
return resolveSetupActionRef(c.ctx, c.actionMode, c.version, c.actionTag, resolver, c.effectiveActionsRepo())
}
if !hasActionTag {
return resolveSetupActionRef(c.ctx, c.actionMode, c.version, "", resolver, c.effectiveActionsRepo())
}
return resolveSetupActionRef(c.ctx, ActionModeAction, c.version, frontmatterActionTag, resolver, c.effectiveActionsRepo())
}
func (c *Compiler) resolveReleaseActionReference(localActionPath string, data *WorkflowData) string {
remoteRef := c.convertToRemoteActionRef(localActionPath, data)
if remoteRef == "" {
actionRefLog.Printf("WARNING: Could not resolve remote reference for %s", localActionPath)
return ""
}
actionRepo := extractActionRepo(remoteRef)
version := extractActionVersion(remoteRef)
if actionRepo == "" || version == "" {
actionRefLog.Printf("Release mode: using tag-based remote action reference: %s", remoteRef)
return remoteRef
}
pinnedRef, err := getActionPinWithData(actionRepo, version, data)
if err != nil {
actionRefLog.Printf("Failed to pin action %s@%s: %v", actionRepo, version, err)
return ""
}
if pinnedRef != "" {
actionRefLog.Printf("Release mode: resolved %s to SHA-pinned reference: %s", remoteRef, pinnedRef)
return pinnedRef
}
actionRefLog.Printf("Release mode: using tag-based remote action reference: %s", remoteRef)
return remoteRef
}
// convertToRemoteActionRef converts a local action path to a tag-based remote reference
// that will be resolved to a SHA later in the release pipeline using action pins.
// Uses the action-tag from WorkflowData.Features if specified (for testing), otherwise uses the version stored in the compiler binary.
// If compiler has actionTag set, it takes priority over both.
// Example: "./actions/create-issue" -> "github/gh-aw/actions/create-issue@v1.0.0"
func (c *Compiler) convertToRemoteActionRef(localPath string, data *WorkflowData) string {
// Strip the leading "./" if present
actionPath := strings.TrimPrefix(localPath, "./")
// Priority order for tag selection:
// 1. Compiler actionTag (from --action-tag flag)
// 2. WorkflowData.Features["action-tag"] (from frontmatter)
// 3. Compiler version
var tag string
// Check compiler actionTag first (highest priority)
if c.actionTag != "" {
tag = c.actionTag
actionRefLog.Printf("Using action-tag from compiler: %s", tag)
} else if data != nil && data.Features != nil {
// Check WorkflowData.Features for action-tag
if actionTagVal, exists := data.Features["action-tag"]; exists {
if actionTagStr, ok := actionTagVal.(string); ok && actionTagStr != "" {
tag = actionTagStr
actionRefLog.Printf("Using action-tag from features: %s", tag)
}
}
}
// Fall back to compiler version if no tag specified
if tag == "" {
tag = c.version
if tag == "" || tag == "dev" {
actionRefLog.Print("WARNING: No release tag available in binary version (version is 'dev' or empty)")
return ""
}
actionRefLog.Printf("Using tag from binary version: %s", tag)
}
// Construct the remote reference with tag: github/gh-aw/actions/name@tag
// The SHA will be resolved later by action pinning infrastructure
remoteRef := fmt.Sprintf("%s/%s@%s", GitHubOrgRepo, actionPath, tag)
actionRefLog.Printf("Remote reference: %s (SHA will be resolved via action pins)", remoteRef)
return remoteRef
}
// convertToExternalActionsRef converts a local action path to a SHA-pinned (if possible) reference
// in the external github/gh-aw-actions repository.
// Example: "./actions/create-issue" -> "github/gh-aw-actions/create-issue@<sha> # v1.0.0"
//
// If SHA resolution fails (no resolver or pin not available), falls back to version-tagged reference:
// Example: "./actions/create-issue" -> "github/gh-aw-actions/create-issue@v1.0.0"
func (c *Compiler) convertToExternalActionsRef(localPath string, data *WorkflowData) string {
// Strip the leading "./" prefix
actionPath := strings.TrimPrefix(localPath, "./")
// Strip the "actions/" prefix to get just the action name
// e.g., "actions/create-issue" -> "create-issue"
actionName := strings.TrimPrefix(actionPath, "actions/")
// Determine tag: use compiler actionTag or version
tag := c.actionTag
if tag == "" {
if data != nil && data.Features != nil {
if actionTagVal, exists := data.Features["action-tag"]; exists {
if actionTagStr, ok := actionTagVal.(string); ok && actionTagStr != "" {
tag = actionTagStr
}
}
}
}
if tag == "" {
tag = c.version
if tag == "" || tag == "dev" {
actionRefLog.Print("WARNING: No release tag available in binary version (version is 'dev' or empty)")
return ""
}
}
// Construct the external actions reference: <actionsRepo>/action-name@tag
actionRepo := fmt.Sprintf("%s/%s", c.effectiveActionsRepo(), actionName)
remoteRef := fmt.Sprintf("%s@%s", actionRepo, tag)
// Try to resolve the SHA using action pins
if data != nil {
pinnedRef, err := getActionPinWithData(actionRepo, tag, data)
if err != nil {
if hasExactActionPinMapping(actionRepo, tag, data) {
actionRefLog.Printf("Failed to resolve exact action pin mapping for %s@%s: %v", actionRepo, tag, err)
return ""
}
// Log and fall through to tag-based reference (action mode is not strict)
actionRefLog.Printf("Failed to pin action %s@%s: %v, falling back to tag-based reference", actionRepo, tag, err)
} else if pinnedRef != "" {
actionRefLog.Printf("Action mode: resolved %s to SHA-pinned reference: %s", remoteRef, pinnedRef)
return pinnedRef
} else if hasExactActionPinMapping(actionRepo, tag, data) {
return ""
}
}
// If SHA resolution unavailable or pin not found, return tag-based reference
actionRefLog.Printf("Action mode: using tag-based external actions repo reference: %s (SHA will be resolved later)", remoteRef)
return remoteRef
}
You can’t perform that action at this time.
