From 5799bd4338ac8b4b2c1ccba189fc9b7324d0916c Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 15 Mar 2026 14:50:08 +0000 Subject: [PATCH] refactor(marketplace): replace raw os calls with go-io Medium in Installer NewInstaller now takes an io.Medium parameter for filesystem operations, replacing direct os.MkdirAll and os.RemoveAll calls. Also fixes installer tests to use correct manifest path (.core/manifest.yaml) and force-add .core/ directory past .gitignore. Co-Authored-By: Virgil --- docs/architecture.md | 2 +- marketplace/installer.go | 11 ++++++----- marketplace/installer_test.go | 29 +++++++++++++++-------------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 9fe0250..7778996 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -621,7 +621,7 @@ byCategory := index.ByCategory("monitoring") mod, found := index.Find("my-module") // Installation -installer := marketplace.NewInstaller("/path/to/modules", store) +installer := marketplace.NewInstaller(medium, "/path/to/modules", store) installer.Install(ctx, mod) // Clone, verify manifest, register installer.Update(ctx, "code") // Pull, re-verify, update metadata installer.Remove("code") // Delete files and store entry diff --git a/marketplace/installer.go b/marketplace/installer.go index 93c65be..7e1dcca 100644 --- a/marketplace/installer.go +++ b/marketplace/installer.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "encoding/json" "fmt" - "os" "os/exec" "path/filepath" "strings" @@ -20,13 +19,15 @@ const storeGroup = "_modules" // Installer handles module installation from Git repos. type Installer struct { + medium io.Medium modulesDir string store *store.Store } // NewInstaller creates a new module installer. -func NewInstaller(modulesDir string, st *store.Store) *Installer { +func NewInstaller(m io.Medium, modulesDir string, st *store.Store) *Installer { return &Installer{ + medium: m, modulesDir: modulesDir, store: st, } @@ -52,7 +53,7 @@ func (i *Installer) Install(ctx context.Context, mod Module) error { } dest := filepath.Join(i.modulesDir, mod.Code) - if err := os.MkdirAll(i.modulesDir, 0755); err != nil { + if err := i.medium.EnsureDir(i.modulesDir); err != nil { return fmt.Errorf("marketplace: mkdir: %w", err) } if err := gitClone(ctx, mod.Repo, dest); err != nil { @@ -63,7 +64,7 @@ func (i *Installer) Install(ctx context.Context, mod Module) error { cleanup := true defer func() { if cleanup { - os.RemoveAll(dest) + _ = i.medium.DeleteAll(dest) } }() @@ -109,7 +110,7 @@ func (i *Installer) Remove(code string) error { } dest := filepath.Join(i.modulesDir, code) - os.RemoveAll(dest) + _ = i.medium.DeleteAll(dest) return i.store.Delete(storeGroup, code) } diff --git a/marketplace/installer_test.go b/marketplace/installer_test.go index f5118f9..772d18d 100644 --- a/marketplace/installer_test.go +++ b/marketplace/installer_test.go @@ -9,6 +9,7 @@ import ( "path/filepath" "testing" + "forge.lthn.ai/core/go-io" "forge.lthn.ai/core/go-scm/manifest" "forge.lthn.ai/core/go-io/store" "github.com/stretchr/testify/assert" @@ -24,7 +25,7 @@ func createTestRepo(t *testing.T, code, version string) string { manifestYAML := "code: " + code + "\nname: Test " + code + "\nversion: \"" + version + "\"\n" require.NoError(t, os.WriteFile( - filepath.Join(dir, ".core", "view.yml"), + filepath.Join(dir, ".core", "manifest.yaml"), []byte(manifestYAML), 0644, )) require.NoError(t, os.WriteFile( @@ -33,7 +34,7 @@ func createTestRepo(t *testing.T, code, version string) string { )) runGit(t, dir, "init") - runGit(t, dir, "add", ".") + runGit(t, dir, "add", "--force", ".") runGit(t, dir, "commit", "-m", "init") return dir } @@ -57,11 +58,11 @@ func createSignedTestRepo(t *testing.T, code, version string) (string, string) { data, err := manifest.MarshalYAML(m) require.NoError(t, err) - require.NoError(t, os.WriteFile(filepath.Join(dir, ".core", "view.yml"), data, 0644)) + require.NoError(t, os.WriteFile(filepath.Join(dir, ".core", "manifest.yaml"), data, 0644)) require.NoError(t, os.WriteFile(filepath.Join(dir, "main.ts"), []byte("export async function init(core: any) {}\n"), 0644)) runGit(t, dir, "init") - runGit(t, dir, "add", ".") + runGit(t, dir, "add", "--force", ".") runGit(t, dir, "commit", "-m", "init") return dir, hex.EncodeToString(pub) @@ -82,7 +83,7 @@ func TestInstall_Good(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) err = inst.Install(context.Background(), Module{ Code: "hello-mod", Repo: repo, @@ -108,7 +109,7 @@ func TestInstall_Good_Signed(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) err = inst.Install(context.Background(), Module{ Code: "signed-mod", Repo: repo, @@ -129,7 +130,7 @@ func TestInstall_Bad_AlreadyInstalled(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) mod := Module{Code: "dup-mod", Repo: repo} require.NoError(t, inst.Install(context.Background(), mod)) @@ -149,7 +150,7 @@ func TestInstall_Bad_InvalidSignature(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) err = inst.Install(context.Background(), Module{ Code: "bad-sig", Repo: repo, @@ -170,7 +171,7 @@ func TestRemove_Good(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) require.NoError(t, inst.Install(context.Background(), Module{Code: "rm-mod", Repo: repo})) err = inst.Remove("rm-mod") @@ -190,7 +191,7 @@ func TestRemove_Bad_NotInstalled(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(t.TempDir(), st) + inst := NewInstaller(io.Local, t.TempDir(), st) err = inst.Remove("nonexistent") assert.Error(t, err) assert.Contains(t, err.Error(), "not installed") @@ -203,7 +204,7 @@ func TestInstalled_Good(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) repo1 := createTestRepo(t, "mod-a", "1.0") repo2 := createTestRepo(t, "mod-b", "2.0") @@ -228,7 +229,7 @@ func TestInstalled_Good_Empty(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(t.TempDir(), st) + inst := NewInstaller(io.Local, t.TempDir(), st) installed, err := inst.Installed() require.NoError(t, err) assert.Empty(t, installed) @@ -242,12 +243,12 @@ func TestUpdate_Good(t *testing.T) { require.NoError(t, err) defer st.Close() - inst := NewInstaller(modulesDir, st) + inst := NewInstaller(io.Local, modulesDir, st) require.NoError(t, inst.Install(context.Background(), Module{Code: "upd-mod", Repo: repo})) // Update the origin repo newManifest := "code: upd-mod\nname: Updated Module\nversion: \"2.0\"\n" - require.NoError(t, os.WriteFile(filepath.Join(repo, ".core", "view.yml"), []byte(newManifest), 0644)) + require.NoError(t, os.WriteFile(filepath.Join(repo, ".core", "manifest.yaml"), []byte(newManifest), 0644)) runGit(t, repo, "add", ".") runGit(t, repo, "commit", "-m", "bump version")