diff --git a/internal/pluginstore/github.go b/internal/pluginstore/github.go index fbb27f06d..836758871 100644 --- a/internal/pluginstore/github.go +++ b/internal/pluginstore/github.go @@ -33,6 +33,7 @@ type Release struct { } type ReleaseAsset struct { + APIURL string `json:"url"` Name string `json:"name"` BrowserDownloadURL string `json:"browser_download_url"` } @@ -114,10 +115,14 @@ func ReleaseVersion(release Release) (string, error) { } func (c Client) DownloadAsset(ctx context.Context, asset ReleaseAsset) ([]byte, error) { - if strings.TrimSpace(asset.BrowserDownloadURL) == "" { - return nil, fmt.Errorf("asset %q missing browser_download_url", asset.Name) + downloadURL := strings.TrimSpace(asset.APIURL) + if downloadURL == "" { + downloadURL = strings.TrimSpace(asset.BrowserDownloadURL) } - return c.get(ctx, asset.BrowserDownloadURL, "application/octet-stream", RequestKindArtifact, 0) + if downloadURL == "" { + return nil, fmt.Errorf("asset %q missing download url", asset.Name) + } + return c.get(ctx, downloadURL, "application/octet-stream", RequestKindArtifact, 0) } func (c Client) get(ctx context.Context, requestURL string, accept string, kind string, maxSize int64) ([]byte, error) { diff --git a/internal/pluginstore/install_test.go b/internal/pluginstore/install_test.go index 576c83ee3..9fcae5932 100644 --- a/internal/pluginstore/install_test.go +++ b/internal/pluginstore/install_test.go @@ -347,6 +347,53 @@ func TestInstallUsesLatestReleaseVersion(t *testing.T) { } } +func TestInstallDownloadsReleaseAssetsViaAPIURL(t *testing.T) { + t.Parallel() + + root := t.TempDir() + archiveData := makeZip(t, map[string]string{"sample-provider.dylib": "library-data"}) + archiveName := "sample-provider_0.2.0_darwin_arm64.zip" + checksum := sha256.Sum256(archiveData) + client := Client{HTTPClient: mapHTTPDoer{ + "https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/latest": []byte(`{ + "tag_name": "v0.2.0", + "assets": [ + { + "name": "` + archiveName + `", + "url": "https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/assets/1", + "browser_download_url": "https://downloads.example/missing.zip" + }, + { + "name": "checksums.txt", + "url": "https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/assets/2", + "browser_download_url": "https://downloads.example/missing-checksums.txt" + } + ] + }`), + "https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/assets/1": archiveData, + "https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/assets/2": []byte(hex.EncodeToString(checksum[:]) + " " + archiveName + "\n"), + }} + + result, errInstall := client.Install(context.Background(), testPlugin(), InstallOptions{ + PluginsDir: root, + GOOS: "darwin", + GOARCH: "arm64", + }) + if errInstall != nil { + t.Fatalf("Install() error = %v", errInstall) + } + if result.Version != "0.2.0" { + t.Fatalf("Version = %q, want 0.2.0 from latest release tag", result.Version) + } + data, errRead := os.ReadFile(filepath.Join(root, "darwin", "arm64", "sample-provider-v0.2.0.dylib")) + if errRead != nil { + t.Fatalf("ReadFile() error = %v", errRead) + } + if string(data) != "library-data" { + t.Fatalf("installed data = %q, want library-data", data) + } +} + func TestInstallVersionUsesPinnedReleaseTag(t *testing.T) { t.Parallel()