fetchAndSaveRemoteFrontmatterImports fetches and saves files referenced in the frontmatter 'imports:' field of a remote workflow. These relative-path imports are resolved against the workflow's location in the source repository and saved locally so compilation can find them. This is analogous to fet
(content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker)
| 123 | // markdown body; this function handles the YAML frontmatter 'imports:' field. |
| 124 | // Import failures are non-fatal (best-effort); the compiler will report any still-missing files. |
| 125 | func fetchAndSaveRemoteFrontmatterImports(content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker) error { |
| 126 | if spec.RepoSlug == "" { |
| 127 | return nil |
| 128 | } |
| 129 | |
| 130 | remoteWorkflowLog.Printf("Fetching frontmatter imports for workflow: repo=%s, path=%s", spec.RepoSlug, spec.WorkflowPath) |
| 131 | |
| 132 | parts := strings.SplitN(spec.RepoSlug, "/", 2) |
| 133 | if len(parts) != 2 { |
| 134 | return nil |
| 135 | } |
| 136 | owner, repo := parts[0], parts[1] |
| 137 | ref := spec.Version |
| 138 | if ref == "" { |
| 139 | // Resolve the actual default branch of the source repo rather than assuming "main" |
| 140 | defaultBranch, err := getRepoDefaultBranch(context.Background(), spec.RepoSlug) |
| 141 | if err != nil { |
| 142 | remoteWorkflowLog.Printf("Failed to resolve default branch for %s, falling back to 'main': %v", spec.RepoSlug, err) |
| 143 | ref = "main" |
| 144 | } else { |
| 145 | ref = defaultBranch |
| 146 | } |
| 147 | // Persist the resolved default ref so other callers do not need to re-resolve it |
| 148 | spec.Version = ref |
| 149 | } |
| 150 | |
| 151 | // workflowBaseDir is the directory of the top-level workflow in the source repo |
| 152 | // (e.g. ".github/workflows"). It serves as both the starting point for resolving |
| 153 | // relative imports and as the prefix to strip when computing local target paths. |
| 154 | workflowBaseDir := getParentDir(spec.WorkflowPath) |
| 155 | |
| 156 | // seen is keyed by fully-resolved remote file path. It is shared across all recursion |
| 157 | // levels so that every import (at any depth) is downloaded at most once and import |
| 158 | // cycles (A imports B, B imports A) are broken without infinite recursion. |
| 159 | seen := make(map[string]struct { |
| 160 | }) |
| 161 | fetchFrontmatterImportsRecursive(content, workflowBaseDir, frontmatterImportsOpts{ |
| 162 | owner: owner, |
| 163 | repo: repo, |
| 164 | ref: ref, |
| 165 | originalBaseDir: workflowBaseDir, |
| 166 | targetDir: targetDir, |
| 167 | verbose: verbose, |
| 168 | force: force, |
| 169 | tracker: tracker, |
| 170 | seen: seen, |
| 171 | }) |
| 172 | return nil |
| 173 | } |
| 174 | |
| 175 | // frontmatterImportsOpts holds the constant parameters for fetchFrontmatterImportsRecursive. |
| 176 | // Only `content` and `currentBaseDir` change per recursion level; everything else is constant. |