validateActionYml validates that an action.yml file exists and contains required fields. This validation function is co-located with the actions build command because: - It's specific to GitHub Actions custom action structure - It's only called during the actions build process - It validates action
(actionPath string)
| 165 | // |
| 166 | // This follows the principle that domain-specific validation belongs in domain files. |
| 167 | func validateActionYml(actionPath string) error { |
| 168 | actionsBuildLog.Printf("Validating action.yml: path=%s", actionPath) |
| 169 | ymlPath := filepath.Join(actionPath, "action.yml") |
| 170 | |
| 171 | if _, err := os.Stat(ymlPath); os.IsNotExist(err) { |
| 172 | return errors.New("action.yml not found") |
| 173 | } |
| 174 | |
| 175 | content, err := os.ReadFile(ymlPath) |
| 176 | if err != nil { |
| 177 | return fmt.Errorf("failed to read action.yml: %w", err) |
| 178 | } |
| 179 | |
| 180 | contentStr := string(content) |
| 181 | |
| 182 | // Check required fields |
| 183 | requiredFields := []string{"name:", "description:", "runs:"} |
| 184 | for _, field := range requiredFields { |
| 185 | if !strings.Contains(contentStr, field) { |
| 186 | return fmt.Errorf("missing required field '%s'", strings.TrimSuffix(field, ":")) |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // Check that it's a supported action runtime (nodeXX or composite) |
| 191 | isNodeJS := strings.Contains(contentStr, "using: 'node") || strings.Contains(contentStr, "using: \"node") |
| 192 | isComposite := strings.Contains(contentStr, "using: 'composite'") || strings.Contains(contentStr, "using: \"composite\"") |
| 193 | |
| 194 | if !isNodeJS && !isComposite { |
| 195 | return errors.New("action must use either a 'nodeXX' or 'composite' runtime") |
| 196 | } |
| 197 | |
| 198 | return nil |
| 199 | } |
| 200 | |
| 201 | // buildAction builds a single action by bundling its dependencies |
| 202 | func buildAction(actionsDir, actionName string) error { |