fetchFrontmatterImportsRecursive is the internal worker for fetchAndSaveRemoteFrontmatterImports. Parameters that change per recursion level: - content: the text of the file whose imports are being processed - currentBaseDir: directory of that file inside the source repo (used to resolve relative p
(content, currentBaseDir string, opts frontmatterImportsOpts)
| 198 | // - targetDir: the `.github/workflows` directory in the user's repo |
| 199 | // - seen: shared visited set (keyed by fully-resolved remote path) — prevents cycles & duplicates |
| 200 | func fetchFrontmatterImportsRecursive(content, currentBaseDir string, opts frontmatterImportsOpts) { |
| 201 | result, err := parser.ExtractFrontmatterFromContent(content) |
| 202 | if err != nil || result.Frontmatter == nil { |
| 203 | return |
| 204 | } |
| 205 | |
| 206 | importsField, exists := result.Frontmatter["imports"] |
| 207 | if !exists { |
| 208 | return |
| 209 | } |
| 210 | |
| 211 | var importPaths []string |
| 212 | switch v := importsField.(type) { |
| 213 | case []any: |
| 214 | for _, item := range v { |
| 215 | switch importItem := item.(type) { |
| 216 | case string: |
| 217 | importPaths = append(importPaths, importItem) |
| 218 | case map[string]any: |
| 219 | // Handle uses: and path: forms (mirrors GitHub Actions reusable workflow syntax) |
| 220 | if usesVal, ok := importItem["uses"]; ok { |
| 221 | if p, ok := usesVal.(string); ok { |
| 222 | importPaths = append(importPaths, p) |
| 223 | } |
| 224 | } else if pathVal, ok := importItem["path"]; ok { |
| 225 | if p, ok := pathVal.(string); ok { |
| 226 | importPaths = append(importPaths, p) |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | case []string: |
| 232 | importPaths = v |
| 233 | } |
| 234 | |
| 235 | if len(importPaths) == 0 { |
| 236 | return |
| 237 | } |
| 238 | |
| 239 | remoteWorkflowLog.Printf("Processing %d frontmatter imports recursively: owner=%s, repo=%s, ref=%s", len(importPaths), opts.owner, opts.repo, opts.ref) |
| 240 | |
| 241 | // Pre-compute the absolute target directory once for path-traversal boundary checks. |
| 242 | absTargetDir, err := filepath.Abs(opts.targetDir) |
| 243 | if err != nil { |
| 244 | return |
| 245 | } |
| 246 | |
| 247 | for _, importPath := range importPaths { |
| 248 | // Skip workflowspec-format imports (already pinned to a remote ref) |
| 249 | if isWorkflowSpecFormat(importPath) { |
| 250 | continue |
| 251 | } |
| 252 | |
| 253 | // Strip any section reference (file.md#Section → file.md) |
| 254 | filePath := importPath |
| 255 | if before, _, hasSec := strings.Cut(importPath, "#"); hasSec { |
| 256 | filePath = before |
| 257 | } |
no test coverage detected