checkUpstreamProvenance fetches the skill's SKILL.md via the contents API to check if it contains github-repo metadata pointing to a different repository, indicating the skill was re-published from an upstream source. In interactive mode, the user is asked whether to install from the re-publisher or
(opts *InstallOptions, client *api.Client, hostname string, skill discovery.Skill, commitSHA string)
| 1297 | // installs from the re-publisher. |
| 1298 | // Returns (repo to redirect to, whether upstream was detected, error). |
| 1299 | func checkUpstreamProvenance(opts *InstallOptions, client *api.Client, hostname string, skill discovery.Skill, commitSHA string) (ghrepo.Interface, bool, error) { |
| 1300 | apiPath := fmt.Sprintf("repos/%s/%s/contents/%s?ref=%s", |
| 1301 | opts.repo.RepoOwner(), opts.repo.RepoName(), |
| 1302 | skill.Path+"/SKILL.md", commitSHA) |
| 1303 | var fileResp struct { |
| 1304 | Content string `json:"content"` |
| 1305 | Encoding string `json:"encoding"` |
| 1306 | } |
| 1307 | if err := client.REST(hostname, "GET", apiPath, nil, &fileResp); err != nil { |
| 1308 | return nil, false, nil //nolint:nilerr // best-effort check; failing to fetch is not fatal |
| 1309 | } |
| 1310 | if fileResp.Encoding != "base64" { |
| 1311 | return nil, false, nil |
| 1312 | } |
| 1313 | decoded, decodeErr := io.ReadAll(base64.NewDecoder(base64.StdEncoding, strings.NewReader(fileResp.Content))) |
| 1314 | if decodeErr != nil { |
| 1315 | return nil, false, nil //nolint:nilerr // best-effort; decode failure is not fatal |
| 1316 | } |
| 1317 | content := string(decoded) |
| 1318 | |
| 1319 | result, parseErr := frontmatter.Parse(content) |
| 1320 | if parseErr != nil || result.Metadata.Meta == nil { |
| 1321 | //nolint:nilerr // unparseable frontmatter means no upstream to detect |
| 1322 | return nil, false, nil |
| 1323 | } |
| 1324 | |
| 1325 | existingRepo, _ := result.Metadata.Meta["github-repo"].(string) |
| 1326 | if existingRepo == "" { |
| 1327 | return nil, false, nil |
| 1328 | } |
| 1329 | |
| 1330 | currentRepoURL := source.BuildRepoURL(hostname, opts.repo.RepoOwner(), opts.repo.RepoName()) |
| 1331 | if existingRepo == currentRepoURL { |
| 1332 | return nil, false, nil |
| 1333 | } |
| 1334 | |
| 1335 | upstreamRepo, parseErr := source.ParseRepoURL(existingRepo) |
| 1336 | if parseErr != nil { |
| 1337 | //nolint:nilerr // invalid repo URL means we can't redirect; install normally |
| 1338 | return nil, false, nil |
| 1339 | } |
| 1340 | |
| 1341 | cs := opts.IO.ColorScheme() |
| 1342 | upstreamLabel := ghrepo.FullName(upstreamRepo) |
| 1343 | repoSource := ghrepo.FullName(opts.repo) |
| 1344 | |
| 1345 | fmt.Fprintf(opts.IO.ErrOut, "%s This skill was originally published in %s\n", cs.WarningIcon(), upstreamLabel) |
| 1346 | |
| 1347 | if opts.Upstream { |
| 1348 | fmt.Fprintf(opts.IO.ErrOut, "Redirecting install to %s...\n", upstreamLabel) |
| 1349 | return upstreamRepo, true, nil |
| 1350 | } |
| 1351 | |
| 1352 | if !opts.IO.CanPrompt() { |
| 1353 | fmt.Fprintf(opts.IO.ErrOut, " Installing from %s (use --upstream or interactive mode to choose upstream)\n", repoSource) |
| 1354 | return nil, true, nil |
| 1355 | } |
| 1356 |
no test coverage detected