From 8292f3ae792f1df1c0abb890f57882d65a72d9e2 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 07:20:33 +0000 Subject: [PATCH] fix(cmd/scm): avoid masking invalid core.json Co-Authored-By: Virgil --- cmd/scm/cmd_export.go | 19 ++++++++--- cmd/scm/cmd_export_test.go | 64 ++++++++++++++++++++++++++++++++++++++ cmd/scm/cmd_index.go | 5 ++- cmd/scm/cmd_index_test.go | 37 ++++++++++++++++++++++ 4 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 cmd/scm/cmd_export_test.go create mode 100644 cmd/scm/cmd_index_test.go diff --git a/cmd/scm/cmd_export.go b/cmd/scm/cmd_export.go index 633ea0d..cc97b9b 100644 --- a/cmd/scm/cmd_export.go +++ b/cmd/scm/cmd_export.go @@ -4,6 +4,7 @@ package scm import ( fmt "dappco.re/go/core/scm/internal/ax/fmtx" + os "dappco.re/go/core/scm/internal/ax/osx" "dappco.re/go/core/io" "dappco.re/go/core/scm/manifest" @@ -16,7 +17,7 @@ func addExportCommand(parent *cli.Command) { cmd := &cli.Command{ Use: "export", Short: "Export compiled manifest as JSON", - Long: "Read core.json from the project root and print it to stdout. Falls back to compiling .core/manifest.yaml if core.json is not found.", + Long: "Read core.json from the project root and print it to stdout. Falls back to compiling .core/manifest.yaml only when core.json is missing.", RunE: func(cmd *cli.Command, args []string) error { return runExport(dir) }, @@ -33,10 +34,18 @@ func runExport(dir string) error { return cli.WrapVerb(err, "open", dir) } - // Try core.json first. - cm, err := manifest.LoadCompiled(medium, ".") - if err != nil { - // Fall back to compiling from source. + var cm *manifest.CompiledManifest + + // Prefer core.json if it exists and is valid. + if raw, readErr := medium.Read("core.json"); readErr == nil { + cm, err = manifest.ParseCompiled([]byte(raw)) + if err != nil { + return err + } + } else if !os.IsNotExist(readErr) { + return cli.WrapVerb(readErr, "read", "core.json") + } else { + // Fall back to compiling from source only when the compiled artifact is absent. m, loadErr := manifest.Load(medium, ".") if loadErr != nil { return cli.WrapVerb(loadErr, "load", "manifest") diff --git a/cmd/scm/cmd_export_test.go b/cmd/scm/cmd_export_test.go new file mode 100644 index 0000000..0d777fd --- /dev/null +++ b/cmd/scm/cmd_export_test.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package scm + +import ( + filepath "dappco.re/go/core/scm/internal/ax/filepathx" + os "dappco.re/go/core/scm/internal/ax/osx" + "testing" + + "dappco.re/go/core/scm/manifest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRunExport_Good_CompiledManifest_Good(t *testing.T) { + dir := t.TempDir() + + cm := &manifest.CompiledManifest{ + Manifest: manifest.Manifest{ + Code: "compiled-mod", + Name: "Compiled Module", + Version: "1.0.0", + }, + Commit: "abc123", + } + data, err := manifest.MarshalJSON(cm) + require.NoError(t, err) + require.NoError(t, os.WriteFile(filepath.Join(dir, "core.json"), data, 0644)) + + err = runExport(dir) + require.NoError(t, err) +} + +func TestRunExport_Good_FallsBackToSource_Good(t *testing.T) { + dir := t.TempDir() + + coreDir := filepath.Join(dir, ".core") + require.NoError(t, os.MkdirAll(coreDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(coreDir, "manifest.yaml"), []byte(` +code: source-mod +name: Source Module +version: 1.0.0 +`), 0644)) + + err := runExport(dir) + require.NoError(t, err) +} + +func TestRunExport_Bad_InvalidCompiledManifest_Good(t *testing.T) { + dir := t.TempDir() + + coreDir := filepath.Join(dir, ".core") + require.NoError(t, os.MkdirAll(coreDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(coreDir, "manifest.yaml"), []byte(` +code: source-mod +name: Source Module +version: 1.0.0 +`), 0644)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "core.json"), []byte("{not-json"), 0644)) + + err := runExport(dir) + require.Error(t, err) + assert.Contains(t, err.Error(), "manifest.ParseCompiled") +} diff --git a/cmd/scm/cmd_index.go b/cmd/scm/cmd_index.go index 29cce00..b1bccce 100644 --- a/cmd/scm/cmd_index.go +++ b/cmd/scm/cmd_index.go @@ -49,7 +49,10 @@ func runIndex(dirs []string, output, baseURL, org string) error { return cli.WrapVerb(err, "build", "index") } - absOutput, _ := filepath.Abs(output) + absOutput, err := filepath.Abs(output) + if err != nil { + return cli.WrapVerb(err, "resolve", output) + } if err := marketplace.WriteIndex(absOutput, idx); err != nil { return err } diff --git a/cmd/scm/cmd_index_test.go b/cmd/scm/cmd_index_test.go new file mode 100644 index 0000000..fd214fb --- /dev/null +++ b/cmd/scm/cmd_index_test.go @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package scm + +import ( + filepath "dappco.re/go/core/scm/internal/ax/filepathx" + os "dappco.re/go/core/scm/internal/ax/osx" + "testing" + + "dappco.re/go/core/io" + "dappco.re/go/core/scm/marketplace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRunIndex_Good_WritesIndex_Good(t *testing.T) { + root := t.TempDir() + + modDir := filepath.Join(root, "mod-a") + require.NoError(t, os.MkdirAll(filepath.Join(modDir, ".core"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(modDir, ".core", "manifest.yaml"), []byte(` +code: mod-a +name: Module A +version: 1.0.0 +sign: key-a +`), 0644)) + + output := filepath.Join(root, "index.json") + err := runIndex([]string{root}, output, "https://forge.example.com", "core") + require.NoError(t, err) + + idx, err := marketplace.LoadIndex(io.Local, output) + require.NoError(t, err) + require.Len(t, idx.Modules, 1) + assert.Equal(t, "mod-a", idx.Modules[0].Code) + assert.Equal(t, "https://forge.example.com/core/mod-a.git", idx.Modules[0].Repo) +}