getWorkflowInputs extracts workflow_dispatch inputs from the compiled lock file This function checks the .lock.yml file because that's what GitHub Actions uses.
(markdownPath string)
| 85 | // getWorkflowInputs extracts workflow_dispatch inputs from the compiled lock file |
| 86 | // This function checks the .lock.yml file because that's what GitHub Actions uses. |
| 87 | func getWorkflowInputs(markdownPath string) (map[string]*workflow.InputDefinition, error) { |
| 88 | // Convert markdown path to lock file path |
| 89 | lockPath := getLockFilePath(markdownPath) |
| 90 | cleanLockPath := filepath.Clean(lockPath) |
| 91 | |
| 92 | validationLog.Printf("Extracting workflow inputs from lock file: %s", lockPath) |
| 93 | |
| 94 | // Check if the lock file exists |
| 95 | if _, err := os.Stat(cleanLockPath); os.IsNotExist(err) { |
| 96 | validationLog.Printf("Lock file does not exist: %s", cleanLockPath) |
| 97 | return nil, errors.New("workflow has not been compiled yet - run 'gh aw compile' first") |
| 98 | } |
| 99 | |
| 100 | // Read the lock file - path is sanitized using filepath.Clean() to prevent path traversal attacks. |
| 101 | // The lockPath is derived from markdownPath which comes from trusted sources (CLI arguments, validated workflow paths). |
| 102 | contentBytes, err := os.ReadFile(cleanLockPath) // #nosec G304 -- path is sanitized with filepath.Clean() and derived from trusted CLI argument |
| 103 | if err != nil { |
| 104 | return nil, fmt.Errorf("failed to read lock file: %w", err) |
| 105 | } |
| 106 | |
| 107 | // Parse the YAML content |
| 108 | var workflowYAML map[string]any |
| 109 | if err := yaml.Unmarshal(contentBytes, &workflowYAML); err != nil { |
| 110 | return nil, fmt.Errorf("failed to parse lock file YAML: %w", err) |
| 111 | } |
| 112 | |
| 113 | // Check if 'on' section is present |
| 114 | onSection, exists := workflowYAML["on"] |
| 115 | if !exists { |
| 116 | return nil, nil |
| 117 | } |
| 118 | |
| 119 | // Convert to map if possible |
| 120 | onMap, ok := onSection.(map[string]any) |
| 121 | if !ok { |
| 122 | return nil, nil |
| 123 | } |
| 124 | |
| 125 | // Get workflow_dispatch section |
| 126 | workflowDispatch, exists := onMap["workflow_dispatch"] |
| 127 | if !exists { |
| 128 | return nil, nil |
| 129 | } |
| 130 | |
| 131 | // Convert to map |
| 132 | workflowDispatchMap, ok := workflowDispatch.(map[string]any) |
| 133 | if !ok { |
| 134 | // workflow_dispatch might be null/empty |
| 135 | return nil, nil |
| 136 | } |
| 137 | |
| 138 | // Get inputs section |
| 139 | inputsSection, exists := workflowDispatchMap["inputs"] |
| 140 | if !exists { |
| 141 | return nil, nil |
| 142 | } |
| 143 | |
| 144 | // Convert to map |