refactor(marketplace): inject mediums into SCM helpers
Some checks failed
Security Scan / security (push) Failing after 14s
Test / test (push) Failing after 1m0s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 14:47:49 +00:00
parent 905889a9f8
commit 2f599eb6d5
4 changed files with 116 additions and 8 deletions

View file

@ -22,6 +22,10 @@ const IndexVersion = 1
// Builder constructs a marketplace Index by crawling directories for
// core.json (compiled manifests) or .core/manifest.yaml files.
type Builder struct {
// Medium is the filesystem abstraction used for manifest reads.
// If nil, io.Local is used.
Medium coreio.Medium
// BaseURL is the prefix for constructing repository URLs, e.g.
// "https://forge.lthn.ai". When set, module Repo is derived as
// BaseURL + "/" + org + "/" + code + ".git".
@ -147,9 +151,14 @@ func WriteIndex(m coreio.Medium, path string, idx *Index) error {
// loadFromDir tries core.json first, then falls back to .core/manifest.yaml.
func (b *Builder) loadFromDir(dir string) (*manifest.Manifest, error) {
medium := b.Medium
if medium == nil {
medium = coreio.Local
}
// Prefer compiled manifest (core.json).
coreJSON := filepath.Join(dir, "core.json")
if raw, err := coreio.Local.Read(coreJSON); err == nil {
if raw, err := medium.Read(coreJSON); err == nil {
cm, err := manifest.ParseCompiled([]byte(raw))
if err != nil {
return nil, coreerr.E("marketplace.Builder.loadFromDir", "parse core.json", err)
@ -159,7 +168,7 @@ func (b *Builder) loadFromDir(dir string) (*manifest.Manifest, error) {
// Fall back to source manifest.
manifestYAML := filepath.Join(dir, ".core", "manifest.yaml")
raw, err := coreio.Local.Read(manifestYAML)
raw, err := medium.Read(manifestYAML)
if err != nil {
return nil, nil // No manifest — skip silently.
}

View file

@ -108,6 +108,34 @@ func TestBuildFromDirs_Good_CoreJSON_Good(t *testing.T) {
assert.Equal(t, "Compiled Module", idx.Modules[0].Name)
}
func TestBuildFromDirs_Good_UsesInjectedMedium_Good(t *testing.T) {
root := t.TempDir()
modDir := filepath.Join(root, "virtual-mod")
require.NoError(t, os.MkdirAll(modDir, 0755))
medium := io.NewMockMedium()
cm := manifest.CompiledManifest{
Manifest: manifest.Manifest{
Code: "virtual-mod",
Name: "Virtual Module",
Version: "9.9.9",
Sign: "sig-virtual",
},
Commit: "commit-virtual",
}
data, err := json.Marshal(cm)
require.NoError(t, err)
require.NoError(t, medium.Write(filepath.Join(modDir, "core.json"), string(data)))
b := &Builder{Medium: medium}
idx, err := b.BuildFromDirs(root)
require.NoError(t, err)
require.Len(t, idx.Modules, 1)
assert.Equal(t, "virtual-mod", idx.Modules[0].Code)
assert.Equal(t, "sig-virtual", idx.Modules[0].SignKey)
}
func TestBuildFromDirs_Good_PrefersCompiledOverSource_Good(t *testing.T) {
root := t.TempDir()
modDir := filepath.Join(root, "dual-mod")

View file

@ -29,9 +29,16 @@ type DiscoveredProvider struct {
// Only manifests with provider fields (namespace + binary) are returned.
// Usage: DiscoverProviders(...)
func DiscoverProviders(dir string) ([]DiscoveredProvider, error) {
entries, err := os.ReadDir(dir)
return DiscoverProvidersWithMedium(coreio.Local, dir)
}
// DiscoverProvidersWithMedium scans the given directory for runtime provider
// manifests using the supplied filesystem medium.
// Usage: DiscoverProvidersWithMedium(...)
func DiscoverProvidersWithMedium(medium coreio.Medium, dir string) ([]DiscoveredProvider, error) {
entries, err := medium.List(dir)
if err != nil {
if os.IsNotExist(err) {
if !medium.Exists(dir) {
return nil, nil // No providers directory — not an error.
}
return nil, coreerr.E("marketplace.DiscoverProviders", "read directory", err)
@ -46,7 +53,7 @@ func DiscoverProviders(dir string) ([]DiscoveredProvider, error) {
providerDir := filepath.Join(dir, e.Name())
manifestPath := filepath.Join(providerDir, ".core", "manifest.yaml")
raw, err := coreio.Local.Read(manifestPath)
raw, err := medium.Read(manifestPath)
if err != nil {
core.Warn(core.Sprintf("marketplace: skipping %s: %v", e.Name(), err))
continue
@ -90,7 +97,14 @@ type ProviderRegistryFile struct {
// Returns an empty registry if the file does not exist.
// Usage: LoadProviderRegistry(...)
func LoadProviderRegistry(path string) (*ProviderRegistryFile, error) {
raw, err := coreio.Local.Read(path)
return LoadProviderRegistryWithMedium(coreio.Local, path)
}
// LoadProviderRegistryWithMedium reads a registry.yaml file using the supplied
// filesystem medium.
// Usage: LoadProviderRegistryWithMedium(...)
func LoadProviderRegistryWithMedium(medium coreio.Medium, path string) (*ProviderRegistryFile, error) {
raw, err := medium.Read(path)
if err != nil {
if os.IsNotExist(err) {
return &ProviderRegistryFile{
@ -116,7 +130,14 @@ func LoadProviderRegistry(path string) (*ProviderRegistryFile, error) {
// SaveProviderRegistry writes the registry to the given path.
// Usage: SaveProviderRegistry(...)
func SaveProviderRegistry(path string, reg *ProviderRegistryFile) error {
if err := coreio.Local.EnsureDir(filepath.Dir(path)); err != nil {
return SaveProviderRegistryWithMedium(coreio.Local, path, reg)
}
// SaveProviderRegistryWithMedium writes the registry using the supplied
// filesystem medium.
// Usage: SaveProviderRegistryWithMedium(...)
func SaveProviderRegistryWithMedium(medium coreio.Medium, path string, reg *ProviderRegistryFile) error {
if err := medium.EnsureDir(filepath.Dir(path)); err != nil {
return coreerr.E("marketplace.SaveProviderRegistry", "ensure directory", err)
}
@ -125,7 +146,7 @@ func SaveProviderRegistry(path string, reg *ProviderRegistryFile) error {
return coreerr.E("marketplace.SaveProviderRegistry", "marshal failed", err)
}
return coreio.Local.Write(path, string(data))
return medium.Write(path, string(data))
}
// Add adds or updates a provider entry in the registry.

View file

@ -7,6 +7,7 @@ import (
os "dappco.re/go/core/scm/internal/ax/osx"
"testing"
"dappco.re/go/core/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -163,6 +164,31 @@ binary: ./test-prov
assert.Equal(t, filepath.Join(dir, "test-prov"), providers[0].Dir)
}
func TestDiscoverProvidersWithMedium_Good(t *testing.T) {
medium := io.NewMockMedium()
medium.Dirs["/providers"] = true
medium.Dirs["/providers/cool-widget"] = true
medium.Dirs["/providers/data-viz"] = true
medium.Files["/providers/cool-widget/.core/manifest.yaml"] = `
code: cool-widget
name: Cool Widget
version: 1.0.0
namespace: /api/v1/cool-widget
binary: ./cool-widget
`
medium.Files["/providers/data-viz/.core/manifest.yaml"] = `
code: data-viz
name: Data Visualiser
version: 0.2.0
namespace: /api/v1/data-viz
binary: ./data-viz
`
providers, err := DiscoverProvidersWithMedium(medium, "/providers")
require.NoError(t, err)
assert.Len(t, providers, 2)
}
// -- ProviderRegistryFile tests -----------------------------------------------
func TestProviderRegistry_LoadSave_Good(t *testing.T) {
@ -201,6 +227,30 @@ func TestProviderRegistry_Load_Good_NonexistentFile_Good(t *testing.T) {
assert.Empty(t, reg.Providers)
}
func TestProviderRegistry_LoadSave_WithMedium_Good(t *testing.T) {
medium := io.NewMockMedium()
path := "/registry/registry.yaml"
reg := &ProviderRegistryFile{
Version: 1,
Providers: map[string]ProviderRegistryEntry{},
}
reg.Add("cool-widget", ProviderRegistryEntry{
Installed: "2026-03-14T12:00:00Z",
Version: "1.0.0",
Source: "forge.lthn.ai/someone/cool-widget",
AutoStart: true,
})
err := SaveProviderRegistryWithMedium(medium, path, reg)
require.NoError(t, err)
loaded, err := LoadProviderRegistryWithMedium(medium, path)
require.NoError(t, err)
assert.Equal(t, 1, loaded.Version)
assert.Len(t, loaded.Providers, 1)
}
func TestProviderRegistry_Add_Good(t *testing.T) {
reg := &ProviderRegistryFile{
Version: 1,