-
Notifications
You must be signed in to change notification settings - Fork 383
Add action SHA validation to compile --validate command
#3631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
30a86d5
504c497
756ef00
c07dcdc
2d7e4a2
fa9fa34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,194 @@ | ||||||||||
| package workflow | ||||||||||
|
|
||||||||||
| import ( | ||||||||||
| "fmt" | ||||||||||
| "os" | ||||||||||
| "regexp" | ||||||||||
|
|
||||||||||
| "github.com/githubnext/gh-aw/pkg/console" | ||||||||||
| "github.com/githubnext/gh-aw/pkg/logger" | ||||||||||
| "github.com/goccy/go-yaml" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| var actionSHACheckerLog = logger.New("workflow:action_sha_checker") | ||||||||||
|
|
||||||||||
| // ActionUsage represents an action used in a workflow with its SHA | ||||||||||
| type ActionUsage struct { | ||||||||||
| Repo string // e.g., "actions/checkout" | ||||||||||
| SHA string // The SHA currently used | ||||||||||
| Version string // The version tag if available (e.g., "v5") | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // ActionUpdateCheck represents the result of checking if an action needs updating | ||||||||||
| type ActionUpdateCheck struct { | ||||||||||
| Action ActionUsage | ||||||||||
| NeedsUpdate bool | ||||||||||
| LatestSHA string | ||||||||||
| Message string | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // ExtractActionsFromLockFile parses a lock.yml file and extracts all action usages | ||||||||||
| func ExtractActionsFromLockFile(lockFilePath string) ([]ActionUsage, error) { | ||||||||||
| actionSHACheckerLog.Printf("Extracting actions from lock file: %s", lockFilePath) | ||||||||||
|
|
||||||||||
| content, err := os.ReadFile(lockFilePath) | ||||||||||
| if err != nil { | ||||||||||
| return nil, fmt.Errorf("failed to read lock file: %w", err) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Parse YAML to extract actions from "uses" fields | ||||||||||
| var workflowData map[string]any | ||||||||||
| if err := yaml.Unmarshal(content, &workflowData); err != nil { | ||||||||||
| return nil, fmt.Errorf("failed to parse lock file YAML: %w", err) | ||||||||||
| } | ||||||||||
|
Comment on lines
+39
to
+43
|
||||||||||
|
|
||||||||||
| // Regular expression to match uses: owner/repo@sha | ||||||||||
| // This matches: owner/repo@40-char-hex-sha or owner/repo/subpath@40-char-hex-sha | ||||||||||
| usesPattern := regexp.MustCompile(`([a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+(?:/[a-zA-Z0-9_.-]+)*)@([0-9a-f]{40})`) | ||||||||||
|
|
||||||||||
| actions := make(map[string]ActionUsage) // Use map to deduplicate | ||||||||||
|
|
||||||||||
| // Convert to string and extract all uses fields | ||||||||||
| contentStr := string(content) | ||||||||||
| matches := usesPattern.FindAllStringSubmatch(contentStr, -1) | ||||||||||
|
|
||||||||||
| for _, match := range matches { | ||||||||||
| if len(match) >= 3 { | ||||||||||
| repo := match[1] | ||||||||||
| sha := match[2] | ||||||||||
|
|
||||||||||
| // Skip if we've already seen this action | ||||||||||
| if _, exists := actions[repo+"@"+sha]; exists { | ||||||||||
| continue | ||||||||||
| } | ||||||||||
|
|
||||||||||
| actionSHACheckerLog.Printf("Found action: %s@%s", repo, sha) | ||||||||||
|
|
||||||||||
| // Try to determine the version tag from action_pins.json | ||||||||||
| version := "" | ||||||||||
| if pin, found := GetActionPinByRepo(repo); found { | ||||||||||
| version = pin.Version | ||||||||||
| } | ||||||||||
|
|
||||||||||
| actions[repo+"@"+sha] = ActionUsage{ | ||||||||||
| Repo: repo, | ||||||||||
| SHA: sha, | ||||||||||
| Version: version, | ||||||||||
| } | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Convert map to slice | ||||||||||
| result := make([]ActionUsage, 0, len(actions)) | ||||||||||
| for _, action := range actions { | ||||||||||
| result = append(result, action) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| actionSHACheckerLog.Printf("Extracted %d unique actions", len(result)) | ||||||||||
| return result, nil | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // CheckActionSHAUpdates checks if actions need updating by comparing with latest SHAs | ||||||||||
| func CheckActionSHAUpdates(actions []ActionUsage, resolver *ActionResolver) []ActionUpdateCheck { | ||||||||||
| actionSHACheckerLog.Printf("Checking %d actions for updates", len(actions)) | ||||||||||
|
|
||||||||||
| results := make([]ActionUpdateCheck, 0, len(actions)) | ||||||||||
|
|
||||||||||
| for _, action := range actions { | ||||||||||
| check := ActionUpdateCheck{ | ||||||||||
| Action: action, | ||||||||||
| NeedsUpdate: false, | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Skip if we don't have a version to check against | ||||||||||
| if action.Version == "" { | ||||||||||
| actionSHACheckerLog.Printf("Skipping %s: no version tag available", action.Repo) | ||||||||||
|
||||||||||
| actionSHACheckerLog.Printf("Skipping %s: no version tag available", action.Repo) | |
| actionSHACheckerLog.Printf("Skipping %s: no version tag available", action.Repo) | |
| check.Message = "No version tag available; cannot check for updates" | |
| results = append(results, check) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on line 61 says 'Action SHA validation warnings are non-fatal,' but looking at
ValidateActionSHAsInLockFilein action_sha_checker.go (line 193), the function always returnsniland never returns an error. This means the error handling code on lines 60-63 can never execute. Either remove the error handling since it's unreachable, or updateValidateActionSHAsInLockFileto return an error in appropriate cases.