Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
201
backend/sdk/pluginstore/pluginstore.go
Normal file
201
backend/sdk/pluginstore/pluginstore.go
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
// Package pluginstore exposes plugin registry and artifact installation helpers
|
||||
// for embedders such as CLIProxyAPIHome.
|
||||
package pluginstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
internalpluginstore "github.com/router-for-me/CLIProxyAPI/v7/internal/pluginstore"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultRegistryURL = internalpluginstore.DefaultRegistryURL
|
||||
DefaultSourceID = internalpluginstore.DefaultSourceID
|
||||
DefaultSourceName = internalpluginstore.DefaultSourceName
|
||||
SchemaVersion = internalpluginstore.SchemaVersion
|
||||
SchemaVersionV2 = internalpluginstore.SchemaVersionV2
|
||||
|
||||
InstallTypeGitHubRelease = internalpluginstore.InstallTypeGitHubRelease
|
||||
InstallTypeDirect = internalpluginstore.InstallTypeDirect
|
||||
|
||||
RequestKindRegistry = internalpluginstore.RequestKindRegistry
|
||||
RequestKindMetadata = internalpluginstore.RequestKindMetadata
|
||||
RequestKindArtifact = internalpluginstore.RequestKindArtifact
|
||||
|
||||
AuthTypeNone = internalpluginstore.AuthTypeNone
|
||||
AuthTypeBearer = internalpluginstore.AuthTypeBearer
|
||||
AuthTypeBasic = internalpluginstore.AuthTypeBasic
|
||||
AuthTypeHeader = internalpluginstore.AuthTypeHeader
|
||||
AuthTypeGitHubToken = internalpluginstore.AuthTypeGitHubToken
|
||||
|
||||
PluginSyncSchemaVersion = internalpluginstore.PluginSyncSchemaVersion
|
||||
)
|
||||
|
||||
type Source = internalpluginstore.Source
|
||||
type Registry = internalpluginstore.Registry
|
||||
type Plugin = internalpluginstore.Plugin
|
||||
type Version = internalpluginstore.Version
|
||||
type Release = internalpluginstore.Release
|
||||
type ReleaseAsset = internalpluginstore.ReleaseAsset
|
||||
type InstallOptions = internalpluginstore.InstallOptions
|
||||
type InstallResult = internalpluginstore.InstallResult
|
||||
type InstallPlan = internalpluginstore.InstallPlan
|
||||
type Artifact = internalpluginstore.Artifact
|
||||
type Platform = internalpluginstore.Platform
|
||||
type Manifest = internalpluginstore.Manifest
|
||||
type AuthConfig = internalpluginstore.AuthConfig
|
||||
type Secret = internalpluginstore.Secret
|
||||
type ResolvedAuthConfig = internalpluginstore.ResolvedAuthConfig
|
||||
type PluginSyncRequest = internalpluginstore.PluginSyncRequest
|
||||
type PluginSyncItem = internalpluginstore.PluginSyncItem
|
||||
type PluginSyncResponse = internalpluginstore.PluginSyncResponse
|
||||
|
||||
type HTTPDoer interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
var ErrLoadedPluginLocked = internalpluginstore.ErrLoadedPluginLocked
|
||||
|
||||
type Client struct {
|
||||
inner internalpluginstore.Client
|
||||
}
|
||||
|
||||
func NewClient(httpClient HTTPDoer, registryURL string) Client {
|
||||
return Client{inner: internalpluginstore.Client{
|
||||
HTTPClient: httpClient,
|
||||
RegistryURL: strings.TrimSpace(registryURL),
|
||||
}}
|
||||
}
|
||||
|
||||
func NewClientWithAuth(httpClient HTTPDoer, registryURL string, auth []AuthConfig) Client {
|
||||
return Client{inner: internalpluginstore.Client{
|
||||
HTTPClient: httpClient,
|
||||
RegistryURL: strings.TrimSpace(registryURL),
|
||||
Auth: internalpluginstore.NormalizeAuthConfigs(auth),
|
||||
}}
|
||||
}
|
||||
|
||||
func NewClientWithResolvedAuth(httpClient HTTPDoer, registryURL string, auth []ResolvedAuthConfig) Client {
|
||||
return NewClientWithResolvedAuthExpiry(httpClient, registryURL, auth, time.Time{})
|
||||
}
|
||||
|
||||
func NewClientWithResolvedAuthExpiry(httpClient HTTPDoer, registryURL string, auth []ResolvedAuthConfig, expiresAt time.Time) Client {
|
||||
return Client{inner: internalpluginstore.Client{
|
||||
HTTPClient: httpClient,
|
||||
RegistryURL: strings.TrimSpace(registryURL),
|
||||
ResolvedAuth: auth,
|
||||
ResolvedAuthExpiresAt: expiresAt,
|
||||
}}
|
||||
}
|
||||
|
||||
func (c *Client) ClearAuth() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
internalpluginstore.ClearResolvedAuthConfigs(c.inner.ResolvedAuth)
|
||||
c.inner.ResolvedAuth = nil
|
||||
c.inner.ResolvedAuthExpiresAt = time.Time{}
|
||||
}
|
||||
|
||||
func DefaultSource() Source {
|
||||
return internalpluginstore.DefaultSource()
|
||||
}
|
||||
|
||||
func NormalizeSources(registryURLs []string) ([]Source, error) {
|
||||
return internalpluginstore.NormalizeSources(registryURLs)
|
||||
}
|
||||
|
||||
func SourceID(registryURL string) string {
|
||||
return internalpluginstore.SourceID(registryURL)
|
||||
}
|
||||
|
||||
func ValidatePlugin(plugin Plugin) error {
|
||||
return internalpluginstore.ValidatePlugin(plugin)
|
||||
}
|
||||
|
||||
func PluginInstallType(plugin Plugin) string {
|
||||
return internalpluginstore.PluginInstallType(plugin)
|
||||
}
|
||||
|
||||
func PluginPlatforms(plugin Plugin) []Platform {
|
||||
return internalpluginstore.PluginPlatforms(plugin)
|
||||
}
|
||||
|
||||
func PluginArtifacts(plugin Plugin) []Artifact {
|
||||
return internalpluginstore.PluginArtifacts(plugin)
|
||||
}
|
||||
|
||||
func SelectArtifact(plan InstallPlan, goos string, goarch string) (Artifact, error) {
|
||||
return internalpluginstore.SelectArtifact(plan, goos, goarch)
|
||||
}
|
||||
|
||||
func GitHubRepositoryParts(repository string) (string, string, error) {
|
||||
return internalpluginstore.GitHubRepositoryParts(repository)
|
||||
}
|
||||
|
||||
func NormalizeAuthConfigs(auth []AuthConfig) []AuthConfig {
|
||||
return internalpluginstore.NormalizeAuthConfigs(auth)
|
||||
}
|
||||
|
||||
func ClearResolvedAuthConfigs(auth []ResolvedAuthConfig) {
|
||||
internalpluginstore.ClearResolvedAuthConfigs(auth)
|
||||
}
|
||||
|
||||
func ResolvedAuthForRequest(auth []ResolvedAuthConfig, requestURL string, kind string) (ResolvedAuthConfig, bool) {
|
||||
return internalpluginstore.ResolvedAuthForRequest(auth, requestURL, kind)
|
||||
}
|
||||
|
||||
func ValidateResolvedAuthConfig(auth ResolvedAuthConfig) error {
|
||||
return internalpluginstore.ValidateResolvedAuthConfig(auth)
|
||||
}
|
||||
|
||||
func AuthConfigured(auth []AuthConfig, requestURL string, kind string) bool {
|
||||
return internalpluginstore.AuthConfigured(auth, requestURL, kind)
|
||||
}
|
||||
|
||||
func PluginAuthConfigured(source Source, plugin Plugin, auth []AuthConfig) bool {
|
||||
return internalpluginstore.PluginAuthConfigured(source, plugin, auth)
|
||||
}
|
||||
|
||||
func UpdateAvailable(installed, latest string) bool {
|
||||
return internalpluginstore.UpdateAvailable(installed, latest)
|
||||
}
|
||||
|
||||
func ReleaseVersion(release Release) (string, error) {
|
||||
return internalpluginstore.ReleaseVersion(release)
|
||||
}
|
||||
|
||||
func ManifestFromRelease(source Source, plugin Plugin, release Release) (Manifest, error) {
|
||||
return internalpluginstore.ManifestFromRelease(source, plugin, release)
|
||||
}
|
||||
|
||||
func ManifestFromPlugin(source Source, plugin Plugin) (Manifest, error) {
|
||||
return internalpluginstore.ManifestFromPlugin(source, plugin)
|
||||
}
|
||||
|
||||
func (c Client) FetchRegistry(ctx context.Context) (Registry, error) {
|
||||
return c.inner.FetchRegistry(ctx)
|
||||
}
|
||||
|
||||
func (c Client) FetchLatestRelease(ctx context.Context, plugin Plugin) (Release, error) {
|
||||
return c.inner.FetchLatestRelease(ctx, plugin)
|
||||
}
|
||||
|
||||
func (c Client) FetchReleaseByTag(ctx context.Context, plugin Plugin, tag string) (Release, error) {
|
||||
return c.inner.FetchReleaseByTag(ctx, plugin, tag)
|
||||
}
|
||||
|
||||
func (c Client) Install(ctx context.Context, plugin Plugin, options InstallOptions) (InstallResult, error) {
|
||||
return c.inner.Install(ctx, plugin, options)
|
||||
}
|
||||
|
||||
func (c Client) InstallVersion(ctx context.Context, plugin Plugin, releaseTag string, version string, options InstallOptions) (InstallResult, error) {
|
||||
return c.inner.InstallVersion(ctx, plugin, releaseTag, version, options)
|
||||
}
|
||||
|
||||
func (c Client) InstallManifest(ctx context.Context, manifest Manifest, options InstallOptions) (InstallResult, error) {
|
||||
return c.inner.InstallManifest(ctx, manifest, options)
|
||||
}
|
||||
159
backend/sdk/pluginstore/pluginstore_test.go
Normal file
159
backend/sdk/pluginstore/pluginstore_test.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package pluginstore
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestManifestValidateRequiresPinnedReleaseTag(t *testing.T) {
|
||||
manifest := validTestManifest()
|
||||
manifest.ReleaseTag = ""
|
||||
|
||||
errValidate := manifest.Validate()
|
||||
if errValidate == nil {
|
||||
t.Fatal("Validate() error = nil, want release-tag error")
|
||||
}
|
||||
if !strings.Contains(errValidate.Error(), "release-tag") {
|
||||
t.Fatalf("Validate() error = %v, want release-tag", errValidate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestValidateRejectsReleaseTagVersionMismatch(t *testing.T) {
|
||||
manifest := validTestManifest()
|
||||
manifest.ReleaseTag = "v0.3.0"
|
||||
|
||||
errValidate := manifest.Validate()
|
||||
if errValidate == nil {
|
||||
t.Fatal("Validate() error = nil, want version mismatch")
|
||||
}
|
||||
if !strings.Contains(errValidate.Error(), "resolves version") {
|
||||
t.Fatalf("Validate() error = %v, want version mismatch", errValidate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestFromReleaseBuildsPinnedManifest(t *testing.T) {
|
||||
manifest, errManifest := ManifestFromRelease(
|
||||
DefaultSource(),
|
||||
Plugin{
|
||||
ID: "sample-provider",
|
||||
Name: "Sample Provider",
|
||||
Description: "Adds sample provider support.",
|
||||
Author: "author-name",
|
||||
Repository: "https://github.com/author-name/sample-provider",
|
||||
},
|
||||
Release{TagName: "v0.2.0"},
|
||||
)
|
||||
if errManifest != nil {
|
||||
t.Fatalf("ManifestFromRelease() error = %v", errManifest)
|
||||
}
|
||||
if errValidate := manifest.Validate(); errValidate != nil {
|
||||
t.Fatalf("Validate() error = %v", errValidate)
|
||||
}
|
||||
if manifest.Version != "0.2.0" || manifest.ReleaseTag != "v0.2.0" {
|
||||
t.Fatalf("manifest version fields = %q/%q, want 0.2.0/v0.2.0", manifest.Version, manifest.ReleaseTag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestFromPluginBuildsDirectManifest(t *testing.T) {
|
||||
manifest, errManifest := ManifestFromPlugin(
|
||||
DefaultSource(),
|
||||
Plugin{
|
||||
ID: "sample-provider",
|
||||
Name: "Sample Provider",
|
||||
Description: "Adds sample provider support.",
|
||||
Author: "author-name",
|
||||
Version: "0.4.0",
|
||||
Install: InstallPlan{
|
||||
Type: InstallTypeDirect,
|
||||
Artifacts: []Artifact{{
|
||||
GOOS: "linux",
|
||||
GOARCH: "amd64",
|
||||
URL: "https://downloads.example/sample-provider.zip",
|
||||
SHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}},
|
||||
},
|
||||
},
|
||||
)
|
||||
if errManifest != nil {
|
||||
t.Fatalf("ManifestFromPlugin() error = %v", errManifest)
|
||||
}
|
||||
if errValidate := manifest.Validate(); errValidate != nil {
|
||||
t.Fatalf("Validate() error = %v", errValidate)
|
||||
}
|
||||
if manifest.SchemaVersion != SchemaVersionV2 || manifest.InstallType() != InstallTypeDirect || manifest.ReleaseTag != "" {
|
||||
t.Fatalf("manifest = %#v, want v2 direct without release tag", manifest)
|
||||
}
|
||||
if manifest.SourceURL != DefaultRegistryURL || len(manifest.Install.Artifacts) != 1 {
|
||||
t.Fatalf("manifest source/artifacts = %q/%d, want source URL and one pinned artifact", manifest.SourceURL, len(manifest.Install.Artifacts))
|
||||
}
|
||||
artifact := manifest.Install.Artifacts[0]
|
||||
if artifact.GOOS != "linux" || artifact.GOARCH != "amd64" || artifact.URL != "https://downloads.example/sample-provider.zip" {
|
||||
t.Fatalf("manifest artifact = %#v, want pinned linux/amd64 artifact", artifact)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestFromPluginRejectsArtifactQuery(t *testing.T) {
|
||||
_, errManifest := ManifestFromPlugin(DefaultSource(), Plugin{
|
||||
ID: "sample-provider", Name: "Sample Provider", Description: "Sample", Author: "tester", Version: "1.0.0",
|
||||
Install: InstallPlan{Type: InstallTypeDirect, Artifacts: []Artifact{{
|
||||
GOOS: "linux", GOARCH: "amd64", URL: "https://downloads.example/sample.zip?X-Amz-Signature=secret",
|
||||
SHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}}},
|
||||
})
|
||||
if errManifest == nil {
|
||||
t.Fatal("ManifestFromPlugin() error = nil, want query rejection")
|
||||
}
|
||||
if strings.Contains(errManifest.Error(), "secret") {
|
||||
t.Fatalf("ManifestFromPlugin() error leaked query value: %v", errManifest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginArtifactsIncludesVersionArtifacts(t *testing.T) {
|
||||
plugin := Plugin{
|
||||
ID: "sample-provider",
|
||||
Name: "Sample Provider",
|
||||
Description: "Adds sample provider support.",
|
||||
Author: "author-name",
|
||||
Version: "0.4.0",
|
||||
Install: InstallPlan{
|
||||
Type: InstallTypeDirect,
|
||||
Artifacts: []Artifact{{
|
||||
GOOS: "windows",
|
||||
GOARCH: "x64",
|
||||
URL: "https://downloads.example/sample-provider.zip",
|
||||
SHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}},
|
||||
},
|
||||
Versions: []Version{{
|
||||
Version: "0.3.0",
|
||||
Install: InstallPlan{
|
||||
Type: InstallTypeDirect,
|
||||
Artifacts: []Artifact{{
|
||||
GOOS: "linux",
|
||||
GOARCH: "aarch64",
|
||||
URL: "https://downloads.example/sample-provider-0.3.0.zip",
|
||||
SHA256: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
}},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
artifacts := PluginArtifacts(plugin)
|
||||
if len(artifacts) != 2 ||
|
||||
artifacts[0].GOARCH != "amd64" ||
|
||||
artifacts[1].GOARCH != "arm64" {
|
||||
t.Fatalf("PluginArtifacts() = %#v, want normalized top-level and version artifacts", artifacts)
|
||||
}
|
||||
}
|
||||
|
||||
func validTestManifest() Manifest {
|
||||
return Manifest{
|
||||
ID: "sample-provider",
|
||||
Name: "Sample Provider",
|
||||
Description: "Adds sample provider support.",
|
||||
Author: "author-name",
|
||||
Version: "0.2.0",
|
||||
ReleaseTag: "v0.2.0",
|
||||
Repository: "https://github.com/author-name/sample-provider",
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue