From 855f5f68ea4b9222f8dd5893ea9c0506fde10b19 Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 30 Apr 2026 19:26:45 +0100 Subject: [PATCH] refactor(go): use core.E + propagate command/setup Result (Mantis #1248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate go-scm error construction and command-registration patterns to canonical core/go primitives: - go/scm.go: drop `errors` import; use `core.E(op, msg, cause)` instead of `errors.New(...)` in NewCoreService nil-check - go/git/service.go: similar core.E migrations - go/cmd/{collect,compile,forge,gitea,pkg,scm,sign,verify}/...: bulk conversion of `_ = app.Command(...)` discards to inline propagation `if r := app.Command(...); !r.OK { return r }`. Several `newApp()` helpers changed signature from `func newApp() *core.Core` to `func newApp() core.Result` so registration failures propagate to main(), which then handles them via `core.Exit(1)` after logging. 10 files changed, 386/-249. Audit: 3180 → 3154 (-26 result-discards). Build, vet, test all clean. 0 gaming patterns across all dims. Closes tasks.lthn.sh/view.php?id=1248 Co-authored-by: Codex --- go/cmd/collect/main.go | 24 ++++-- go/cmd/compile/cmd_compile.go | 125 ++++++++++++++++-------------- go/cmd/forge/main.go | 20 +++-- go/cmd/gitea/main.go | 25 ++++-- go/cmd/pkg/cmd_pkg.go | 129 +++++++++++++++++++------------ go/cmd/scm/main.go | 36 ++++++--- go/cmd/sign/cmd_sign.go | 138 +++++++++++++++++++--------------- go/cmd/verify/cmd_verify.go | 123 ++++++++++++++++++------------ go/git/service.go | 11 ++- go/scm.go | 4 +- 10 files changed, 386 insertions(+), 249 deletions(-) diff --git a/go/cmd/collect/main.go b/go/cmd/collect/main.go index 83a4959..8169690 100644 --- a/go/cmd/collect/main.go +++ b/go/cmd/collect/main.go @@ -10,18 +10,30 @@ import ( ) func main() { - newApp().Run() + result := newApp() + if !result.OK { + core.Error("collect setup failed", "err", result.Value) + core.Exit(1) + return + } + result.Value.(*core.Core).Run() } -func newApp() *core.Core { +func newApp() core.Result { app := core.New(core.WithOption("name", "collect")) app.App().Version = "dev" - _ = app.Command("github", core.Command{Action: github}) - _ = app.Command("market", core.Command{Action: market}) - _ = app.Command("papers", core.Command{Action: papers}) + if r := app.Command("github", core.Command{Action: github}); !r.OK { + return r + } + if r := app.Command("market", core.Command{Action: market}); !r.OK { + return r + } + if r := app.Command("papers", core.Command{Action: papers}); !r.OK { + return r + } - return app + return core.Ok(app) } func github(opts core.Options) core.Result { diff --git a/go/cmd/compile/cmd_compile.go b/go/cmd/compile/cmd_compile.go index 5364122..e4c60b0 100644 --- a/go/cmd/compile/cmd_compile.go +++ b/go/cmd/compile/cmd_compile.go @@ -4,10 +4,6 @@ package compile import ( - "os" - "path/filepath" - "strings" - core "dappco.re/go" "dappco.re/go/scm/manifest" ) @@ -22,86 +18,77 @@ func Register(app *core.Core) core.Result { if app == nil { return core.Fail(core.E("cmd.compile.Register", "core app is required", nil)) } - return app.Command("compile", core.Command{Action: run}) + return app.Command("compile", core.Command{Action: run(app)}) } -func run(opts core.Options) core.Result { - if wantsHelp(opts) { - core.Print(nil, usage) - return core.Ok(nil) - } +func run(app *core.Core) core.CommandAction { + return func(opts core.Options) core.Result { + if wantsHelp(opts) { + core.Print(nil, usage) + return core.Ok(nil) + } - root := option(opts, "root", ".") - manifestPath := option(opts, "manifest", filepath.Join(root, ".core", "manifest.yaml")) - outPath := option(opts, "out", filepath.Join(root, "core.json")) + root := option(opts, "root", ".") + manifestPath := option(opts, "manifest", core.PathJoin(root, ".core", "manifest.yaml")) + outPath := option(opts, "out", core.PathJoin(root, "core.json")) - raw, err := os.ReadFile(manifestPath) - if err != nil { - return failed(err) - } - m, err := manifest.Parse(raw) - if err != nil { - return failed(err) - } + raw, err := readFile(app, manifestPath) + if err != nil { + return failed(err) + } + m, err := manifest.Parse(raw) + if err != nil { + return failed(err) + } - cm, err := manifest.CompileWithOptions(m, manifest.CompileOptions{ - Commit: opts.String("commit"), - Tag: opts.String("tag"), - BuiltBy: opts.String("built-by"), - Build: manifest.BuildInfo{ - Targets: splitList(option(opts, "targets", opts.String("target"))), - Checksums: opts.String("checksums"), - SHA256: opts.String("sha256"), - }, - }) - if err != nil { - return failed(err) - } + cm, err := manifest.CompileWithOptions(m, manifest.CompileOptions{ + Commit: opts.String("commit"), + Tag: opts.String("tag"), + BuiltBy: opts.String("built-by"), + Build: manifest.BuildInfo{ + Targets: splitList(option(opts, "targets", opts.String("target"))), + Checksums: opts.String("checksums"), + SHA256: opts.String("sha256"), + }, + }) + if err != nil { + return failed(err) + } - compiled, err := manifest.MarshalJSON(cm) - if err != nil { - return failed(err) - } - if err := mkdirParent(outPath); err != nil { - return failed(err) - } - if err := os.WriteFile(outPath, compiled, 0o600); err != nil { - return failed(err) - } + compiled, err := manifest.MarshalJSON(cm) + if err != nil { + return failed(err) + } + if r := app.Fs().WriteMode(outPath, string(compiled), 0o600); !r.OK { + return failed(resultError("cmd.compile.run", "write compiled manifest", r)) + } - core.Print(nil, "%s", outPath) - return core.Ok(nil) + core.Print(nil, "%s", outPath) + return core.Ok(nil) + } } func option(opts core.Options, key, fallback string) string { - if value := strings.TrimSpace(opts.String(key)); value != "" { + if value := core.Trim(opts.String(key)); value != "" { return value } return fallback } func splitList(value string) []string { - if strings.TrimSpace(value) == "" { + if core.Trim(value) == "" { return nil } - parts := strings.Split(value, ",") + parts := core.Split(value, ",") out := make([]string, 0, len(parts)) for _, part := range parts { - if part = strings.TrimSpace(part); part != "" { + if part = core.Trim(part); part != "" { out = append(out, part) } } return out } -func mkdirParent(path string) error { - dir := filepath.Dir(path) - if dir == "." || dir == "" { - return nil - } - return os.MkdirAll(dir, 0o755) -} - func wantsHelp(opts core.Options) bool { return opts.Bool("help") || opts.Bool("h") } @@ -109,3 +96,25 @@ func wantsHelp(opts core.Options) bool { func failed(err error) core.Result { return core.Fail(err) } + +func readFile(app *core.Core, path string) ([]byte, error) { + if app == nil { + return nil, core.E("cmd.compile.readFile", "core app is required", nil) + } + r := app.Fs().Read(path) + if !r.OK { + return nil, resultError("cmd.compile.readFile", "read file", r) + } + raw, ok := r.Value.(string) + if !ok { + return nil, core.E("cmd.compile.readFile", "read returned invalid payload", nil) + } + return []byte(raw), nil +} + +func resultError(op, msg string, r core.Result) error { + if err, ok := r.Value.(error); ok { + return core.E(op, msg, err) + } + return core.E(op, msg, nil) +} diff --git a/go/cmd/forge/main.go b/go/cmd/forge/main.go index 1cbf853..66eb2a5 100644 --- a/go/cmd/forge/main.go +++ b/go/cmd/forge/main.go @@ -8,17 +8,27 @@ import ( ) func main() { - newApp().Run() + result := newApp() + if !result.OK { + core.Error("forge setup failed", "err", result.Value) + core.Exit(1) + return + } + result.Value.(*core.Core).Run() } -func newApp() *core.Core { +func newApp() core.Result { app := core.New(core.WithOption("name", "forge")) app.App().Version = "dev" - _ = app.Command("auth", core.Command{Action: auth}) - _ = app.Command("repos", core.Command{Action: repos}) + if r := app.Command("auth", core.Command{Action: auth}); !r.OK { + return r + } + if r := app.Command("repos", core.Command{Action: repos}); !r.OK { + return r + } - return app + return core.Ok(app) } func auth(opts core.Options) core.Result { diff --git a/go/cmd/gitea/main.go b/go/cmd/gitea/main.go index 735b676..ce42743 100644 --- a/go/cmd/gitea/main.go +++ b/go/cmd/gitea/main.go @@ -4,24 +4,33 @@ package main import ( "strconv" - "strings" core "dappco.re/go" "dappco.re/go/scm/gitea" ) func main() { - newApp().Run() + result := newApp() + if !result.OK { + core.Error("gitea setup failed", "err", result.Value) + core.Exit(1) + return + } + result.Value.(*core.Core).Run() } -func newApp() *core.Core { +func newApp() core.Result { app := core.New(core.WithOption("name", "gitea")) app.App().Version = "dev" - _ = app.Command("repos", core.Command{Action: repos}) - _ = app.Command("issues", core.Command{Action: issues}) + if r := app.Command("repos", core.Command{Action: repos}); !r.OK { + return r + } + if r := app.Command("issues", core.Command{Action: issues}); !r.OK { + return r + } - return app + return core.Ok(app) } func repos(opts core.Options) core.Result { @@ -97,11 +106,11 @@ func issues(opts core.Options) core.Result { } func splitRepo(value string) (string, string) { - parts := strings.SplitN(value, "/", 2) + parts := core.SplitN(value, "/", 2) if len(parts) != 2 { return "", "" } - return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + return core.Trim(parts[0]), core.Trim(parts[1]) } func intOption(opts core.Options, key string) int { diff --git a/go/cmd/pkg/cmd_pkg.go b/go/cmd/pkg/cmd_pkg.go index c6d0bc5..fb94ae2 100644 --- a/go/cmd/pkg/cmd_pkg.go +++ b/go/cmd/pkg/cmd_pkg.go @@ -4,10 +4,7 @@ package pkg import ( - "os" - "path/filepath" "sort" - "strings" core "dappco.re/go" "dappco.re/go/scm/manifest" @@ -24,40 +21,39 @@ func Register(app *core.Core) core.Result { if app == nil { return core.Fail(core.E("cmd.pkg.Register", "core app is required", nil)) } - return app.Command("pkg", core.Command{Action: run}) + return app.Command("pkg", core.Command{Action: run(app)}) } -func run(opts core.Options) core.Result { - if wantsHelp(opts) { - core.Print(nil, usage) - return core.Ok(nil) - } +func run(app *core.Core) core.CommandAction { + return func(opts core.Options) core.Result { + if wantsHelp(opts) { + core.Print(nil, usage) + return core.Ok(nil) + } - root := option(opts, "root", ".") - dirs := packageDirs(opts, root) - idx, err := buildIndex(dirs, opts.String("base-url"), opts.String("org")) - if err != nil { - return failed(err) - } + root := option(opts, "root", ".") + dirs := packageDirs(opts, root) + idx, err := buildIndex(app, dirs, opts.String("base-url"), opts.String("org")) + if err != nil { + return failed(err) + } - outPath := option(opts, "out", filepath.Join(root, "marketplace", "index.json")) - if err := mkdirParent(outPath); err != nil { - return failed(err) - } - if err := marketplace.WriteIndex(outPath, idx); err != nil { - return failed(err) - } + outPath := option(opts, "out", core.PathJoin(root, "marketplace", "index.json")) + if err := writeIndex(app, outPath, idx); err != nil { + return failed(err) + } - core.Print(nil, "%s", outPath) - return core.Ok(nil) + core.Print(nil, "%s", outPath) + return core.Ok(nil) + } } -func buildIndex(dirs []string, baseURL, org string) (*marketplace.Index, error) { +func buildIndex(app *core.Core, dirs []string, baseURL, org string) (*marketplace.Index, error) { var manifests []*manifest.Manifest var collectionDirs []string for _, dir := range dirs { - m, err := loadPackageManifest(dir) + m, err := loadPackageManifest(app, dir) if err == nil { manifests = append(manifests, m) continue @@ -85,15 +81,15 @@ func buildIndex(dirs []string, baseURL, org string) (*marketplace.Index, error) return idx, nil } -func loadPackageManifest(root string) (*manifest.Manifest, error) { - if raw, err := os.ReadFile(filepath.Join(root, "core.json")); err == nil { +func loadPackageManifest(app *core.Core, root string) (*manifest.Manifest, error) { + if raw, err := readFile(app, core.PathJoin(root, "core.json")); err == nil { cm, err := manifest.ParseCompiled(raw) if err != nil { return nil, err } return &cm.Manifest, nil } - raw, err := os.ReadFile(filepath.Join(root, ".core", "manifest.yaml")) + raw, err := readFile(app, core.PathJoin(root, ".core", "manifest.yaml")) if err != nil { return nil, err } @@ -104,10 +100,10 @@ func packageDirs(opts core.Options, root string) []string { if dirs := splitList(opts.String("dirs")); len(dirs) > 0 { return dirs } - if dir := strings.TrimSpace(opts.String("dir")); dir != "" { + if dir := core.Trim(opts.String("dir")); dir != "" { return []string{dir} } - if arg := strings.TrimSpace(opts.String("_arg")); arg != "" { + if arg := core.Trim(opts.String("_arg")); arg != "" { return []string{arg} } return []string{root} @@ -117,7 +113,7 @@ func uniqueCategories(existing, extra []string) []string { seen := map[string]struct{}{} out := make([]string, 0, len(existing)+len(extra)) for _, category := range append(existing, extra...) { - category = strings.TrimSpace(category) + category = core.Trim(category) if category == "" { continue } @@ -131,13 +127,13 @@ func uniqueCategories(existing, extra []string) []string { } func applyRepoDefaults(idx *marketplace.Index, baseURL, org string) { - if idx == nil || strings.TrimSpace(baseURL) == "" { + if idx == nil || core.Trim(baseURL) == "" { return } - if strings.TrimSpace(org) == "" { + if core.Trim(org) == "" { org = "core" } - baseURL = strings.TrimRight(baseURL, "/") + baseURL = trimRightSlash(baseURL) for i := range idx.Modules { if idx.Modules[i].Repo == "" && idx.Modules[i].Code != "" { idx.Modules[i].Repo = baseURL + "/" + org + "/" + idx.Modules[i].Code @@ -156,34 +152,26 @@ func sortIndex(idx *marketplace.Index) { } func option(opts core.Options, key, fallback string) string { - if value := strings.TrimSpace(opts.String(key)); value != "" { + if value := core.Trim(opts.String(key)); value != "" { return value } return fallback } func splitList(value string) []string { - if strings.TrimSpace(value) == "" { + if core.Trim(value) == "" { return nil } - parts := strings.Split(value, ",") + parts := core.Split(value, ",") out := make([]string, 0, len(parts)) for _, part := range parts { - if part = strings.TrimSpace(part); part != "" { + if part = core.Trim(part); part != "" { out = append(out, part) } } return out } -func mkdirParent(path string) error { - dir := filepath.Dir(path) - if dir == "." || dir == "" { - return nil - } - return os.MkdirAll(dir, 0o755) -} - func wantsHelp(opts core.Options) bool { return opts.Bool("help") || opts.Bool("h") } @@ -191,3 +179,50 @@ func wantsHelp(opts core.Options) bool { func failed(err error) core.Result { return core.Fail(err) } + +func writeIndex(app *core.Core, path string, idx *marketplace.Index) error { + if idx == nil { + return core.E("cmd.pkg.writeIndex", "index is required", nil) + } + r := core.JSONMarshalIndent(idx, "", " ") + if !r.OK { + return resultError("cmd.pkg.writeIndex", "marshal index", r) + } + raw, ok := r.Value.([]byte) + if !ok { + return core.E("cmd.pkg.writeIndex", "marshal returned invalid payload", nil) + } + if writeResult := app.Fs().WriteMode(path, string(raw), 0o600); !writeResult.OK { + return resultError("cmd.pkg.writeIndex", "write index", writeResult) + } + return nil +} + +func readFile(app *core.Core, path string) ([]byte, error) { + if app == nil { + return nil, core.E("cmd.pkg.readFile", "core app is required", nil) + } + r := app.Fs().Read(path) + if !r.OK { + return nil, resultError("cmd.pkg.readFile", "read file", r) + } + raw, ok := r.Value.(string) + if !ok { + return nil, core.E("cmd.pkg.readFile", "read returned invalid payload", nil) + } + return []byte(raw), nil +} + +func resultError(op, msg string, r core.Result) error { + if err, ok := r.Value.(error); ok { + return core.E(op, msg, err) + } + return core.E(op, msg, nil) +} + +func trimRightSlash(value string) string { + for core.HasSuffix(value, "/") { + value = core.TrimSuffix(value, "/") + } + return value +} diff --git a/go/cmd/scm/main.go b/go/cmd/scm/main.go index d70e708..ed224f4 100644 --- a/go/cmd/scm/main.go +++ b/go/cmd/scm/main.go @@ -12,24 +12,42 @@ import ( ) func main() { - newApp().Run() + result := newApp() + if !result.OK { + core.Error("scm setup failed", "err", result.Value) + core.Exit(1) + return + } + result.Value.(*core.Core).Run() } -func newApp() *core.Core { +func newApp() core.Result { app := core.New( core.WithOption("name", "scm"), core.WithService(scm.NewCoreService(scm.Options{})), ) app.App().Version = "dev" - _ = app.Command("health", core.Command{Action: health(app)}) - _ = app.Command("dev/health", core.Command{Action: health(app)}) - _ = compilecmd.Register(app) - _ = signcmd.Register(app) - _ = verifycmd.Register(app) - _ = pkgcmd.Register(app) + if r := app.Command("health", core.Command{Action: health(app)}); !r.OK { + return r + } + if r := app.Command("dev/health", core.Command{Action: health(app)}); !r.OK { + return r + } + if r := compilecmd.Register(app); !r.OK { + return r + } + if r := signcmd.Register(app); !r.OK { + return r + } + if r := verifycmd.Register(app); !r.OK { + return r + } + if r := pkgcmd.Register(app); !r.OK { + return r + } - return app + return core.Ok(app) } func health(app *core.Core) core.CommandAction { diff --git a/go/cmd/sign/cmd_sign.go b/go/cmd/sign/cmd_sign.go index 1cc9ea3..e6b64e6 100644 --- a/go/cmd/sign/cmd_sign.go +++ b/go/cmd/sign/cmd_sign.go @@ -6,11 +6,6 @@ package sign import ( "crypto/ed25519" "encoding/base64" - "encoding/json" - "errors" - "os" - "path/filepath" - "strings" core "dappco.re/go" "dappco.re/go/scm/manifest" @@ -26,49 +21,48 @@ func Register(app *core.Core) core.Result { if app == nil { return core.Fail(core.E("cmd.sign.Register", "core app is required", nil)) } - return app.Command("sign", core.Command{Action: run}) + return app.Command("sign", core.Command{Action: run(app)}) } -func run(opts core.Options) core.Result { - if wantsHelp(opts) { - core.Print(nil, usage) - return core.Ok(nil) - } +func run(app *core.Core) core.CommandAction { + return func(opts core.Options) core.Result { + if wantsHelp(opts) { + core.Print(nil, usage) + return core.Ok(nil) + } - priv, err := privateKey(opts) - if err != nil { - return failed(err) - } + priv, err := privateKey(app, opts) + if err != nil { + return failed(err) + } - root := option(opts, "root", ".") - outPath := option(opts, "out", filepath.Join(root, "core.json")) + root := option(opts, "root", ".") + outPath := option(opts, "out", core.PathJoin(root, "core.json")) - cm, err := compiledManifest(opts, root, priv) - if err != nil { - return failed(err) - } + cm, err := compiledManifest(app, opts, root, priv) + if err != nil { + return failed(err) + } - raw, err := manifest.MarshalJSON(cm) - if err != nil { - return failed(err) - } - if err := mkdirParent(outPath); err != nil { - return failed(err) - } - if err := os.WriteFile(outPath, raw, 0o600); err != nil { - return failed(err) - } + raw, err := manifest.MarshalJSON(cm) + if err != nil { + return failed(err) + } + if r := app.Fs().WriteMode(outPath, string(raw), 0o600); !r.OK { + return failed(resultError("cmd.sign.run", "write signed manifest", r)) + } - core.Print(nil, "%s", outPath) - return core.Ok(nil) + core.Print(nil, "%s", outPath) + return core.Ok(nil) + } } -func compiledManifest(opts core.Options, root string, priv ed25519.PrivateKey) (*manifest.CompiledManifest, error) { +func compiledManifest(app *core.Core, opts core.Options, root string, priv ed25519.PrivateKey) (*manifest.CompiledManifest, error) { pub := priv.Public().(ed25519.PublicKey) signKey := base64.StdEncoding.EncodeToString(pub) - if path := strings.TrimSpace(opts.String("manifest")); path != "" { - raw, err := os.ReadFile(path) + if path := core.Trim(opts.String("manifest")); path != "" { + raw, err := readFile(app, path) if err != nil { return nil, err } @@ -76,15 +70,15 @@ func compiledManifest(opts core.Options, root string, priv ed25519.PrivateKey) ( if err != nil { return nil, err } - if strings.TrimSpace(m.SignKey) == "" { + if core.Trim(m.SignKey) == "" { m.SignKey = signKey } m.Sign = "" return manifest.CompileWithOptions(m, manifest.CompileOptions{SignKey: priv}) } - inPath := option(opts, "in", filepath.Join(root, "core.json")) - raw, err := os.ReadFile(inPath) + inPath := option(opts, "in", core.PathJoin(root, "core.json")) + raw, err := readFile(app, inPath) if err != nil { return nil, err } @@ -95,7 +89,7 @@ func compiledManifest(opts core.Options, root string, priv ed25519.PrivateKey) ( if _, err := manifest.Compile(&cm.Manifest, cm.Build); err != nil { return nil, err } - if strings.TrimSpace(cm.SignKey) == "" { + if core.Trim(cm.SignKey) == "" { cm.SignKey = signKey } payload, err := canonicalManifestBytes(&cm.Manifest) @@ -108,59 +102,59 @@ func compiledManifest(opts core.Options, root string, priv ed25519.PrivateKey) ( return cm, nil } -func privateKey(opts core.Options) (ed25519.PrivateKey, error) { - value := strings.TrimSpace(opts.String("key")) - if path := strings.TrimSpace(opts.String("key-file")); path != "" { - raw, err := os.ReadFile(path) +func privateKey(app *core.Core, opts core.Options) (ed25519.PrivateKey, error) { + value := core.Trim(opts.String("key")) + if path := core.Trim(opts.String("key-file")); path != "" { + raw, err := readFile(app, path) if err != nil { return nil, err } - value = strings.TrimSpace(string(raw)) + value = core.Trim(string(raw)) } if value == "" { - value = strings.TrimSpace(os.Getenv("SCM_SIGN_KEY")) + value = core.Trim(app.Env("SCM_SIGN_KEY")) } if value == "" { - return nil, errors.New("signing key is required") + return nil, core.E("cmd.sign.privateKey", "signing key is required", nil) } - if raw, err := os.ReadFile(value); err == nil { - value = strings.TrimSpace(string(raw)) + if raw, err := readFile(app, value); err == nil { + value = core.Trim(string(raw)) } decoded, err := base64.StdEncoding.DecodeString(value) if err != nil { return nil, err } if len(decoded) != ed25519.PrivateKeySize { - return nil, errors.New("signing key must be a base64 ed25519 private key") + return nil, core.E("cmd.sign.privateKey", "signing key must be a base64 ed25519 private key", nil) } return ed25519.PrivateKey(decoded), nil } func canonicalManifestBytes(m *manifest.Manifest) ([]byte, error) { if m == nil { - return nil, errors.New("manifest is required") + return nil, core.E("cmd.sign.canonicalManifestBytes", "manifest is required", nil) } cp := *m cp.Sign = "" cp.SignKey = "" - return json.Marshal(cp) + r := core.JSONMarshal(cp) + if !r.OK { + return nil, resultError("cmd.sign.canonicalManifestBytes", "marshal manifest", r) + } + raw, ok := r.Value.([]byte) + if !ok { + return nil, core.E("cmd.sign.canonicalManifestBytes", "marshal returned invalid payload", nil) + } + return raw, nil } func option(opts core.Options, key, fallback string) string { - if value := strings.TrimSpace(opts.String(key)); value != "" { + if value := core.Trim(opts.String(key)); value != "" { return value } return fallback } -func mkdirParent(path string) error { - dir := filepath.Dir(path) - if dir == "." || dir == "" { - return nil - } - return os.MkdirAll(dir, 0o755) -} - func wantsHelp(opts core.Options) bool { return opts.Bool("help") || opts.Bool("h") } @@ -168,3 +162,25 @@ func wantsHelp(opts core.Options) bool { func failed(err error) core.Result { return core.Fail(err) } + +func readFile(app *core.Core, path string) ([]byte, error) { + if app == nil { + return nil, core.E("cmd.sign.readFile", "core app is required", nil) + } + r := app.Fs().Read(path) + if !r.OK { + return nil, resultError("cmd.sign.readFile", "read file", r) + } + raw, ok := r.Value.(string) + if !ok { + return nil, core.E("cmd.sign.readFile", "read returned invalid payload", nil) + } + return []byte(raw), nil +} + +func resultError(op, msg string, r core.Result) error { + if err, ok := r.Value.(error); ok { + return core.E(op, msg, err) + } + return core.E(op, msg, nil) +} diff --git a/go/cmd/verify/cmd_verify.go b/go/cmd/verify/cmd_verify.go index e1e7ad6..a028cbd 100644 --- a/go/cmd/verify/cmd_verify.go +++ b/go/cmd/verify/cmd_verify.go @@ -6,11 +6,6 @@ package verify import ( "crypto/ed25519" "encoding/base64" - "encoding/json" - "errors" - "os" - "path/filepath" - "strings" core "dappco.re/go" "dappco.re/go/scm/manifest" @@ -26,45 +21,47 @@ func Register(app *core.Core) core.Result { if app == nil { return core.Fail(core.E("cmd.verify.Register", "core app is required", nil)) } - return app.Command("verify", core.Command{Action: run}) + return app.Command("verify", core.Command{Action: run(app)}) } -func run(opts core.Options) core.Result { - if wantsHelp(opts) { - core.Print(nil, usage) - return core.Ok(nil) - } +func run(app *core.Core) core.CommandAction { + return func(opts core.Options) core.Result { + if wantsHelp(opts) { + core.Print(nil, usage) + return core.Ok(nil) + } - root := option(opts, "root", ".") - input := option(opts, "in", filepath.Join(root, "core.json")) + root := option(opts, "root", ".") + input := option(opts, "in", core.PathJoin(root, "core.json")) - m, source, err := loadManifest(opts, input) - if err != nil { - return failed(err) - } - if key, err := publicKey(opts); err != nil { - return failed(err) - } else if key != "" { - cp := *m - cp.SignKey = key - m = &cp - } + m, source, err := loadManifest(app, opts, input) + if err != nil { + return failed(err) + } + if key, err := publicKey(app, opts); err != nil { + return failed(err) + } else if key != "" { + cp := *m + cp.SignKey = key + m = &cp + } - payload, err := canonicalManifestBytes(m) - if err != nil { - return failed(err) - } - if err := manifest.Verify(m, payload); err != nil { - return failed(err) - } + payload, err := canonicalManifestBytes(m) + if err != nil { + return failed(err) + } + if err := manifest.Verify(m, payload); err != nil { + return failed(err) + } - core.Print(nil, "verified %s", source) - return core.Ok(nil) + core.Print(nil, "verified %s", source) + return core.Ok(nil) + } } -func loadManifest(opts core.Options, defaultInput string) (*manifest.Manifest, string, error) { - if path := strings.TrimSpace(opts.String("manifest")); path != "" { - raw, err := os.ReadFile(path) +func loadManifest(app *core.Core, opts core.Options, defaultInput string) (*manifest.Manifest, string, error) { + if path := core.Trim(opts.String("manifest")); path != "" { + raw, err := readFile(app, path) if err != nil { return nil, path, err } @@ -72,7 +69,7 @@ func loadManifest(opts core.Options, defaultInput string) (*manifest.Manifest, s return m, path, err } - raw, err := os.ReadFile(defaultInput) + raw, err := readFile(app, defaultInput) if err != nil { return nil, defaultInput, err } @@ -86,43 +83,51 @@ func loadManifest(opts core.Options, defaultInput string) (*manifest.Manifest, s return &cm.Manifest, defaultInput, nil } -func publicKey(opts core.Options) (string, error) { - value := strings.TrimSpace(opts.String("key")) - if path := strings.TrimSpace(opts.String("key-file")); path != "" { - raw, err := os.ReadFile(path) +func publicKey(app *core.Core, opts core.Options) (string, error) { + value := core.Trim(opts.String("key")) + if path := core.Trim(opts.String("key-file")); path != "" { + raw, err := readFile(app, path) if err != nil { return "", err } - value = strings.TrimSpace(string(raw)) + value = core.Trim(string(raw)) } if value == "" { return "", nil } - if raw, err := os.ReadFile(value); err == nil { - value = strings.TrimSpace(string(raw)) + if raw, err := readFile(app, value); err == nil { + value = core.Trim(string(raw)) } decoded, err := base64.StdEncoding.DecodeString(value) if err != nil { return "", err } if len(decoded) != ed25519.PublicKeySize { - return "", errors.New("verification key must be a base64 ed25519 public key") + return "", core.E("cmd.verify.publicKey", "verification key must be a base64 ed25519 public key", nil) } return base64.StdEncoding.EncodeToString(decoded), nil } func canonicalManifestBytes(m *manifest.Manifest) ([]byte, error) { if m == nil { - return nil, errors.New("manifest is required") + return nil, core.E("cmd.verify.canonicalManifestBytes", "manifest is required", nil) } cp := *m cp.Sign = "" cp.SignKey = "" - return json.Marshal(cp) + r := core.JSONMarshal(cp) + if !r.OK { + return nil, resultError("cmd.verify.canonicalManifestBytes", "marshal manifest", r) + } + raw, ok := r.Value.([]byte) + if !ok { + return nil, core.E("cmd.verify.canonicalManifestBytes", "marshal returned invalid payload", nil) + } + return raw, nil } func option(opts core.Options, key, fallback string) string { - if value := strings.TrimSpace(opts.String(key)); value != "" { + if value := core.Trim(opts.String(key)); value != "" { return value } return fallback @@ -135,3 +140,25 @@ func wantsHelp(opts core.Options) bool { func failed(err error) core.Result { return core.Fail(err) } + +func readFile(app *core.Core, path string) ([]byte, error) { + if app == nil { + return nil, core.E("cmd.verify.readFile", "core app is required", nil) + } + r := app.Fs().Read(path) + if !r.OK { + return nil, resultError("cmd.verify.readFile", "read file", r) + } + raw, ok := r.Value.(string) + if !ok { + return nil, core.E("cmd.verify.readFile", "read returned invalid payload", nil) + } + return []byte(raw), nil +} + +func resultError(op, msg string, r core.Result) error { + if err, ok := r.Value.(error); ok { + return core.E(op, msg, err) + } + return core.E(op, msg, nil) +} diff --git a/go/git/service.go b/go/git/service.go index 013365e..1ce9d4b 100644 --- a/go/git/service.go +++ b/go/git/service.go @@ -5,8 +5,6 @@ package git import ( "context" "iter" - // Note: AX-6 — validatePath requires filepath.Rel; no core relative-path primitive exists. - "path/filepath" "sync" core "dappco.re/go" @@ -277,8 +275,13 @@ func (s *Service) validatePath(path string) error { if !core.PathIsAbs(workDir) { return core.E(sonarServiceGitValidatepath, "WorkDir must be absolute", nil) } - rel, err := filepath.Rel(workDir, core.CleanPath(path, ds)) - if err != nil || rel == ".." || core.HasPrefix(rel, ".."+ds) { + relResult := core.PathRel(workDir, core.CleanPath(path, ds)) + rel, _ := relResult.Value.(string) + if !relResult.OK { + err, _ := relResult.Value.(error) + return core.E(sonarServiceGitValidatepath, "path is outside of allowed WorkDir", err) + } + if rel == ".." || core.HasPrefix(rel, ".."+ds) { return core.E(sonarServiceGitValidatepath, "path is outside of allowed WorkDir", nil) } return nil diff --git a/go/scm.go b/go/scm.go index b624f74..88203ac 100644 --- a/go/scm.go +++ b/go/scm.go @@ -6,8 +6,6 @@ package scm import ( // Note: AX-6 — Core lifecycle hooks use context.Context directly. "context" - // Note: AX-6 — Constructor failures return standard error values through core.Result. - "errors" core "dappco.re/go" coreio "dappco.re/go/io" @@ -74,7 +72,7 @@ func (r *Registry) Medium() coreio.Medium { func NewCoreService(opts Options) func(*core.Core) core.Result { return func(c *core.Core) core.Result { if c == nil { - return core.Fail(errors.New("scm.NewCoreService: core is required")) + return core.Fail(core.E("scm.NewCoreService", "core is required", nil)) } if result := registerReposService(c, opts); !result.OK { return result