MCPcopy Create free account
hub / github.com/github/gh-aw / fetchAndSaveRemoteResources

Function fetchAndSaveRemoteResources

pkg/cli/resources.go:70–232  ·  view source on GitHub ↗

fetchAndSaveRemoteResources fetches files listed in the top-level "resources" frontmatter field from the same remote repository and saves them locally. Resources are resolved as relative paths from the same directory as the source workflow in the remote repo. GitHub Actions expression syntax (e.g.

(content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker)

Source from the content-addressed store, hash-verified

68// For non-Markdown resource files: if the target already exists and force is false, an error
69// is returned regardless of origin (non-markdown files have no source tracking).
70func fetchAndSaveRemoteResources(content string, spec *WorkflowSpec, targetDir string, verbose bool, force bool, tracker *FileTracker) error {
71 if spec.RepoSlug == "" {
72 return nil
73 }
74
75 parts := strings.SplitN(spec.RepoSlug, "/", 2)
76 if len(parts) != 2 {
77 return nil
78 }
79 owner, repo := parts[0], parts[1]
80 ref := spec.Version
81 if ref == "" {
82 defaultBranch, err := getRepoDefaultBranch(context.Background(), spec.RepoSlug)
83 if err != nil {
84 remoteWorkflowLog.Printf("Failed to resolve default branch for %s, falling back to 'main': %v", spec.RepoSlug, err)
85 ref = "main"
86 } else {
87 ref = defaultBranch
88 }
89 spec.Version = ref
90 }
91
92 resourcePaths, err := extractResources(content)
93 if err != nil {
94 return err
95 }
96 if len(resourcePaths) == 0 {
97 return nil
98 }
99
100 // Resources are resolved relative to the source workflow's directory in the remote repo.
101 workflowBaseDir := getParentDir(spec.WorkflowPath)
102
103 // Pre-compute the absolute target directory for path-traversal boundary checks.
104 absTargetDir, err := filepath.Abs(targetDir)
105 if err != nil {
106 remoteWorkflowLog.Printf("Failed to resolve absolute path for target directory %s: %v", targetDir, err)
107 return nil
108 }
109
110 for _, resourcePath := range resourcePaths {
111 // Early rejection of path traversal patterns. This is a fast first-pass check;
112 // the filepath.Rel boundary check below is the authoritative security control.
113 if strings.Contains(resourcePath, "..") {
114 if verbose {
115 fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Skipping resource with unsafe path: %q", resourcePath)))
116 }
117 continue
118 }
119
120 // Resolve the remote file path
121 var remoteFilePath string
122 if rest, ok := strings.CutPrefix(resourcePath, "/"); ok {
123 remoteFilePath = rest
124 } else if workflowBaseDir != "" {
125 remoteFilePath = path.Join(workflowBaseDir, resourcePath)
126 } else {
127 remoteFilePath = resourcePath

Calls 15

FormatWarningMessageFunction · 0.92
FileExistsFunction · 0.92
FormatInfoMessageFunction · 0.92
DownloadFileFromGitHubFunction · 0.92
FormatSuccessMessageFunction · 0.92
getRepoDefaultBranchFunction · 0.85
extractResourcesFunction · 0.85
getParentDirFunction · 0.85
readSourceRepoFromFileFunction · 0.85
sourceRepoLabelFunction · 0.85
addSourceToWorkflowFunction · 0.85
BackgroundMethod · 0.80