diff --git a/bob.yaml b/bob.yaml index e88afe2be..b7330572b 100644 --- a/bob.yaml +++ b/bob.yaml @@ -9,7 +9,6 @@ build: build: dependson: - binary - - docker # generate - is the default task execute when executing code generation. generate: @@ -27,7 +26,7 @@ build: # docker - is the default task execute when building docker image. docker: - cmd: task docker-build + cmd: task docker:build dependson: - binary target: diff --git a/cmd/sonrd/cmd/plugin.go b/cmd/sonrd/cmd/plugin.go index 3862cf6f7..548b5564b 100644 --- a/cmd/sonrd/cmd/plugin.go +++ b/cmd/sonrd/cmd/plugin.go @@ -358,7 +358,7 @@ func NewPluginList() *cobra.Command { Use: "list", Short: "List declared plugins and status", Long: "Prints status and information of declared plugins", - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, _ []string) error { s := cliui.New(cliui.WithStdout(os.Stdout)) return printPlugins(s) }, @@ -372,7 +372,7 @@ func NewPluginUpdate() *cobra.Command { Short: "Update plugins", Long: "Updates a plugin specified by path. If no path is specified all declared plugins are updated", Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { + RunE: func(_ *cobra.Command, args []string) error { if len(args) == 0 { // update all plugins err := plugin.Update(plugins...) @@ -573,7 +573,7 @@ func printPlugins(session *cliui.Session) error { func flagSetPluginsGlobal() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) fs.BoolP(flagPluginsGlobal, "g", false, "use global plugins configuration"+ - " ($HOME/.ignite/plugins/plugins.yml)") + " ($HOME/.sonr/plugins/plugins.yml)") return fs } diff --git a/cmd/sonrd/cmd/plugin_default.go b/cmd/sonrd/cmd/plugin_default.go index a74b9ed81..950d2cdbd 100644 --- a/cmd/sonrd/cmd/plugin_default.go +++ b/cmd/sonrd/cmd/plugin_default.go @@ -17,10 +17,13 @@ type defaultPlugin struct { path string } -const ( - PluginNetworkVersion = "v0.1.0" - PluginNetworkPath = "github.com/ignite/cli-plugin-network@" + PluginNetworkVersion -) +func defaultNetworkPlugin() string { + const ( + PluginNetworkVersion = "v0.2.0" + PluginNetworkPath = "github.com/sonr-eco/cli-plugin-network@" + PluginNetworkVersion + ) + return PluginNetworkPath +} // defaultPlugins holds the plugin that are considered trustable and for which // a command will added if the plugin is not already installed. @@ -30,7 +33,7 @@ var defaultPlugins = []defaultPlugin{ use: "network", short: "Launch a blockchain in production", aliases: []string{"n"}, - path: PluginNetworkPath, + path: defaultNetworkPlugin(), }, } diff --git a/cmd/sonrd/cmd/root.go b/cmd/sonrd/cmd/root.go index 6f1dd001f..b40613f93 100644 --- a/cmd/sonrd/cmd/root.go +++ b/cmd/sonrd/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "errors" "fmt" "io" @@ -159,6 +160,11 @@ func initRootCmd( // this line is used by starport scaffolding # root/commands ) + // Load plugins if any + if err := LoadPlugins(context.Background(), rootCmd); err != nil { + panic(err) + } + a := appCreator{ encodingConfig, } diff --git a/config/plugins/config.go b/config/plugins/config.go index 7143b9248..a003d185d 100644 --- a/config/plugins/config.go +++ b/config/plugins/config.go @@ -41,7 +41,7 @@ type Plugin struct { // With holds arguments passed to the plugin interface With map[string]string `yaml:"with,omitempty"` // Global holds whether the plugin is installed globally - // (default: $HOME/.ignite/plugins/plugins.yml) or locally for a chain. + // (default: $HOME/.sonr/plugins/plugins.yml) or locally for a chain. Global bool `yaml:"-"` } diff --git a/go.mod b/go.mod index 942e91e07..4a9fe5d07 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,10 @@ require ( github.com/charmbracelet/bubbletea v0.24.2 github.com/charmbracelet/lipgloss v0.9.1 github.com/go-git/go-git/v5 v5.10.0 + github.com/gobuffalo/genny/v2 v2.1.0 + github.com/gobuffalo/logger v1.0.7 + github.com/gobuffalo/packd v1.0.2 + github.com/gobuffalo/plush/v4 v4.1.19 github.com/goccy/go-yaml v1.11.2 github.com/hashicorp/go-hclog v1.2.0 github.com/hashicorp/go-plugin v1.5.2 @@ -90,6 +94,7 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go v1.44.295 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect @@ -134,6 +139,7 @@ require ( github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect @@ -153,11 +159,15 @@ require ( github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-webauthn/x v0.1.4 // indirect github.com/gobuffalo/envy v1.7.1 // indirect - github.com/gobuffalo/logger v1.0.1 // indirect - github.com/gobuffalo/packd v0.3.0 // indirect + github.com/gobuffalo/flect v0.3.0 // indirect + github.com/gobuffalo/github_flavored_markdown v1.1.4 // indirect + github.com/gobuffalo/helpers v0.6.7 // indirect github.com/gobuffalo/packr/v2 v2.7.1 // indirect + github.com/gobuffalo/tags/v3 v3.1.4 // indirect + github.com/gobuffalo/validate/v3 v3.3.3 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect + github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.0.0 // indirect @@ -173,6 +183,7 @@ require ( github.com/google/s2a-go v0.1.4 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -216,6 +227,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/microcosm-cc/bluemonday v1.0.23 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -248,9 +260,11 @@ require ( github.com/rs/cors v1.8.3 // indirect github.com/rs/zerolog v1.30.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/sergi/go-diff v1.1.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.2.0 // indirect + github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect + github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect diff --git a/go.sum b/go.sum index 3edbf7791..d4a4a13ef 100644 --- a/go.sum +++ b/go.sum @@ -289,6 +289,8 @@ github.com/aws/aws-sdk-go v1.44.295/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8 github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca/go.mod h1:TWe0N2hv5qvpLHT+K16gYcGBllld4h65dQ/5CNuirmk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -533,6 +535,8 @@ github.com/ethereum/go-ethereum v1.12.1/go.mod h1:zKetLweqBR8ZS+1O9iJWI8DvmmD2Nz github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -629,15 +633,33 @@ github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/flect v0.3.0 h1:erfPWM+K1rFNIQeRPdeEXxo8yFr/PO17lhRnS8FUrtk= +github.com/gobuffalo/flect v0.3.0/go.mod h1:5pf3aGnsvqvCj50AVni7mJJF8ICxGZ8HomberC3pXLE= +github.com/gobuffalo/genny/v2 v2.1.0 h1:cCRBbqzo3GfNvj3UetD16zRgUvWFEyyl0qTqquuIqOM= +github.com/gobuffalo/genny/v2 v2.1.0/go.mod h1:4yoTNk4bYuP3BMM6uQKYPvtP6WsXFGm2w2EFYZdRls8= +github.com/gobuffalo/github_flavored_markdown v1.1.3/go.mod h1:IzgO5xS6hqkDmUh91BW/+Qxo/qYnvfzoz3A7uLkg77I= +github.com/gobuffalo/github_flavored_markdown v1.1.4 h1:WacrEGPXUDX+BpU1GM/Y0ADgMzESKNWls9hOTG1MHVs= +github.com/gobuffalo/github_flavored_markdown v1.1.4/go.mod h1:Vl9686qrVVQou4GrHRK/KOG3jCZOKLUqV8MMOAYtlso= +github.com/gobuffalo/helpers v0.6.7 h1:C9CedoRSfgWg2ZoIkVXgjI5kgmSpL34Z3qdnzpfNVd8= +github.com/gobuffalo/helpers v0.6.7/go.mod h1:j0u1iC1VqlCaJEEVkZN8Ia3TEzfj/zoXANqyJExTMTA= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/logger v1.0.7 h1:LTLwWelETXDYyqF/ASf0nxaIcdEOIJNxRokPcfI/xbU= +github.com/gobuffalo/logger v1.0.7/go.mod h1:u40u6Bq3VVvaMcy5sRBclD8SXhBYPS0Qk95ubt+1xJM= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= +github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= +github.com/gobuffalo/plush/v4 v4.1.16/go.mod h1:6t7swVsarJ8qSLw1qyAH/KbrcSTwdun2ASEQkOznakg= +github.com/gobuffalo/plush/v4 v4.1.19 h1:o0E5gEJw+ozkAwQoCeiaWC6VOU2lEmX+GhtGkwpqZ8o= +github.com/gobuffalo/plush/v4 v4.1.19/go.mod h1:WiKHJx3qBvfaDVlrv8zT7NCd3dEMaVR/fVxW4wqV17M= +github.com/gobuffalo/tags/v3 v3.1.4 h1:X/ydLLPhgXV4h04Hp2xlbI2oc5MDaa7eub6zw8oHjsM= +github.com/gobuffalo/tags/v3 v3.1.4/go.mod h1:ArRNo3ErlHO8BtdA0REaZxijuWnWzF6PUXngmMXd2I0= +github.com/gobuffalo/validate/v3 v3.3.3 h1:o7wkIGSvZBYBd6ChQoLxkz2y1pfmhbI4jNJYh6PuNJ4= +github.com/gobuffalo/validate/v3 v3.3.3/go.mod h1:YC7FsbJ/9hW/VjQdmXPvFqvRis4vrRYFxr69WiNZw6g= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= @@ -651,6 +673,9 @@ github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHv github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= +github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1-0.20201022092350-68b0159b7869/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= @@ -795,6 +820,8 @@ github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORR github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127/go.mod h1:7X1acUyFRf+oVFTU6SWw9mnb57Vxn+Nbh8iPbKg95hs= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -1029,6 +1056,10 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/microcosm-cc/bluemonday v1.0.22/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/microcosm-cc/bluemonday v1.0.23 h1:SMZe2IGa0NuHvnVNAZ+6B38gsTbi5e4sViiWJyDDqFY= +github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgStuPzlK76QuruE/z4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= @@ -1235,8 +1266,10 @@ github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71e github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shengdoushi/base58 v1.0.0 h1:tGe4o6TmdXFJWoI31VoSWvuaKxf0Px3gqa3sUWhAxBs= github.com/shengdoushi/base58 v1.0.0/go.mod h1:m5uIILfzcKMw6238iWAhP4l3s5+uXyF3+bJKUNhAL9I= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1246,6 +1279,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= @@ -1257,6 +1291,10 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/sonr-io/kryptology v1.10.0 h1:vZ3S1Ct29SKDH6VwYPa9xUeFfwZ0O20mgPIXknCuPM0= github.com/sonr-io/kryptology v1.10.0/go.mod h1:INzpheY1ROZyPxoLGQenlaxM5uRAoz67XNuScsr39Ys= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1530,11 +1568,14 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -1697,6 +1738,7 @@ golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/pkg/crypto/credential.go b/internal/crypto/credential.go similarity index 100% rename from pkg/crypto/credential.go rename to internal/crypto/credential.go diff --git a/pkg/crypto/crypto.go b/internal/crypto/crypto.go similarity index 100% rename from pkg/crypto/crypto.go rename to internal/crypto/crypto.go diff --git a/pkg/crypto/keys.go b/internal/crypto/keys.go similarity index 100% rename from pkg/crypto/keys.go rename to internal/crypto/keys.go diff --git a/internal/highway/handler/wallet.go b/internal/highway/handler/wallet.go index 5ebac81e5..71d632cdb 100644 --- a/internal/highway/handler/wallet.go +++ b/internal/highway/handler/wallet.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/cosmos/cosmos-sdk/client" + "github.com/sonr-io/core/internal/crypto" mdw "github.com/sonr-io/core/internal/highway/middleware" - "github.com/sonr-io/core/pkg/crypto" walletpb "github.com/sonr-io/core/types/highway/wallet/v1" ) diff --git a/internal/highway/middleware/verifier.go b/internal/highway/middleware/verifier.go index f51176a1d..dca688623 100644 --- a/internal/highway/middleware/verifier.go +++ b/internal/highway/middleware/verifier.go @@ -6,8 +6,8 @@ import ( "github.com/gin-gonic/gin" "github.com/go-webauthn/webauthn/protocol" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/internal/highway/types" - "github.com/sonr-io/core/pkg/crypto" "github.com/sonr-io/core/services/did/controller" "github.com/sonr-io/core/types/webauthn" domaintypes "github.com/sonr-io/core/x/domain/types" diff --git a/internal/highway/types/claims.go b/internal/highway/types/claims.go index 7b31fd466..23e2dfdd3 100644 --- a/internal/highway/types/claims.go +++ b/internal/highway/types/claims.go @@ -9,7 +9,7 @@ import ( "github.com/kataras/jwt" "github.com/spf13/viper" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" identitytypes "github.com/sonr-io/core/x/identity/types" ) diff --git a/internal/highway/types/response.go b/internal/highway/types/response.go index 8be3efbf3..3d354253a 100644 --- a/internal/highway/types/response.go +++ b/internal/highway/types/response.go @@ -3,7 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" identitytypes "github.com/sonr-io/core/x/identity/types" ) diff --git a/pkg/zk/codec.go b/internal/zk/codec.go similarity index 100% rename from pkg/zk/codec.go rename to internal/zk/codec.go diff --git a/pkg/zk/zkset.go b/internal/zk/zkset.go similarity index 99% rename from pkg/zk/zkset.go rename to internal/zk/zkset.go index 8576bb9ec..b067a2b37 100644 --- a/pkg/zk/zkset.go +++ b/internal/zk/zkset.go @@ -7,7 +7,7 @@ import ( "errors" "io" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/kryptology/pkg/accumulator" "github.com/sonr-io/kryptology/pkg/core/curves" ) diff --git a/pkg/zk/zkset_test.go b/internal/zk/zkset_test.go similarity index 100% rename from pkg/zk/zkset_test.go rename to internal/zk/zkset_test.go diff --git a/pkg/env/env.go b/pkg/env/env.go index 390fd12d1..bbbc479f0 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -25,7 +25,7 @@ func ConfigDir() xfilepath.PathRetriever { } return dir, nil } - return xfilepath.JoinFromHome(xfilepath.Path(".ignite"))() + return xfilepath.JoinFromHome(xfilepath.Path(".sonr"))() } } diff --git a/pkg/placeholder/error.go b/pkg/placeholder/error.go new file mode 100755 index 000000000..6afbbd795 --- /dev/null +++ b/pkg/placeholder/error.go @@ -0,0 +1,87 @@ +package placeholder + +import ( + "fmt" + "strings" + + "github.com/sonr-io/core/pkg/validation" +) + +var _ validation.Error = (*MissingPlaceholdersError)(nil) + +// MissingPlaceholdersError is used as an error when a source file is missing placeholder. +type MissingPlaceholdersError struct { + missing iterableStringSet + additionalInfo string + additionalErrors error +} + +// Is true if both errors have the same list of missing placeholders. +func (e *MissingPlaceholdersError) Is(err error) bool { + other, ok := err.(*MissingPlaceholdersError) //nolint:errorlint + if !ok { + return false + } + if len(other.missing) != len(e.missing) { + return false + } + for i := range e.missing { + if e.missing[i] != other.missing[i] { + return false + } + } + return true +} + +// Error implements error interface. +func (e *MissingPlaceholdersError) Error() string { + var b strings.Builder + b.WriteString("missing placeholders: ") + e.missing.Iterate(func(i int, element string) bool { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(element) + return true + }) + return b.String() +} + +// ValidationInfo implements validation.Error interface. +func (e *MissingPlaceholdersError) ValidationInfo() string { + var b strings.Builder + b.WriteString("Missing placeholders:\n\n") + e.missing.Iterate(func(i int, element string) bool { + if i > 0 { + b.WriteString("\n") + } + b.WriteString(element) + return true + }) + if e.additionalInfo != "" { + b.WriteString("\n\n") + b.WriteString(e.additionalInfo) + } + if e.additionalErrors != nil { + b.WriteString("\n\nAdditional errors: ") + b.WriteString(e.additionalErrors.Error()) + } + return b.String() +} + +var _ validation.Error = (*ValidationMiscError)(nil) + +// ValidationMiscError is used as a miscellaneous error related to validation. +type ValidationMiscError struct { + errors []string +} + +// Error implements error interface. +func (e *ValidationMiscError) Error() string { + return fmt.Sprintf("validation errors: %v", e.errors) +} + +// ValidationInfo implements validation.Error interface. +func (e *ValidationMiscError) ValidationInfo() string { + return fmt.Sprintf("Validation errors:\n\n%v", strings.Join(e.errors, "\n")) +} diff --git a/pkg/placeholder/tracer.go b/pkg/placeholder/tracer.go new file mode 100755 index 000000000..c5f2e6924 --- /dev/null +++ b/pkg/placeholder/tracer.go @@ -0,0 +1,113 @@ +package placeholder + +import ( + "strings" +) + +type iterableStringSet map[string]struct{} + +func (set iterableStringSet) Iterate(f func(i int, element string) bool) { + i := 0 + for key := range set { + if !f(i, key) { + return + } + i++ + } +} + +func (set iterableStringSet) Add(item string) { + set[item] = struct{}{} +} + +// Option for configuring session. +type Option func(*Tracer) + +// WithAdditionalInfo will append info to the validation error. +func WithAdditionalInfo(info string) Option { + return func(s *Tracer) { + s.additionalInfo = info + } +} + +// New instantiates Session with provided options. +func New(opts ...Option) *Tracer { + s := &Tracer{missing: iterableStringSet{}} + for _, opt := range opts { + opt(s) + } + return s +} + +type Replacer interface { + Replace(content, placeholder, replacement string) string + ReplaceAll(content, placeholder, replacement string) string + ReplaceOnce(content, placeholder, replacement string) string + AppendMiscError(miscError string) +} + +// Tracer keeps track of missing placeholders or other issues related to file modification. +type Tracer struct { + missing iterableStringSet + miscErrors []string + additionalInfo string +} + +// ReplaceAll replace all placeholders in content with replacement string. +func (t *Tracer) ReplaceAll(content, placeholder, replacement string) string { + if strings.Count(content, placeholder) == 0 { + t.missing.Add(placeholder) + return content + } + return strings.ReplaceAll(content, placeholder, replacement) +} + +// Replace placeholder in content with replacement string once. +func (t *Tracer) Replace(content, placeholder, replacement string) string { + // NOTE(dshulyak) we will count twice. once here and second time in strings.Replace + // if it turns out to be an issue, copy the code from strings.Replace. + if strings.Count(content, placeholder) == 0 { + t.missing.Add(placeholder) + return content + } + return strings.Replace(content, placeholder, replacement, 1) +} + +// ReplaceOnce will replace placeholder in content only if replacement is not already found in content. +func (t *Tracer) ReplaceOnce(content, placeholder, replacement string) string { + if !strings.Contains(content, replacement) { + return t.Replace(content, placeholder, replacement) + } + return content +} + +// AppendMiscError allows to track errors not related to missing placeholders during file modification. +func (t *Tracer) AppendMiscError(miscError string) { + t.miscErrors = append(t.miscErrors, miscError) +} + +// Err if any of the placeholders were missing during execution. +func (t *Tracer) Err() error { + // miscellaneous errors represent errors preventing source modification not related to missing placeholder + var miscErrors error + if len(t.miscErrors) > 0 { + miscErrors = &ValidationMiscError{ + errors: t.miscErrors, + } + } + + if len(t.missing) > 0 { + missing := iterableStringSet{} + for key := range t.missing { + missing.Add(key) + } + return &MissingPlaceholdersError{ + missing: missing, + additionalInfo: t.additionalInfo, + additionalErrors: miscErrors, + } + } + + // if not missing placeholder but still miscellaneous errors, return them + return miscErrors +} diff --git a/pkg/placeholder/tracer_test.go b/pkg/placeholder/tracer_test.go new file mode 100755 index 000000000..69daef1f3 --- /dev/null +++ b/pkg/placeholder/tracer_test.go @@ -0,0 +1,99 @@ +package placeholder + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func newErrMissingPlaceholder(missing []string) *MissingPlaceholdersError { + err := &MissingPlaceholdersError{missing: iterableStringSet{}} + for _, placeholder := range missing { + err.missing[placeholder] = struct{}{} + } + return err +} + +func TestReplace(t *testing.T) { + tests := []struct { + desc string + content string + replace []string + missing []string + }{ + { + desc: "FoundAll", + content: "#one #two", + replace: []string{"#one", "#two"}, + }, + { + desc: "MissingAll", + content: "", + replace: []string{"#one", "#two"}, + missing: []string{"#one", "#two"}, + }, + { + desc: "MissingOne", + content: "#two", + replace: []string{"#one", "#two"}, + missing: []string{"#one"}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + tr := New() + content := tc.content + for _, placeholder := range tc.replace { + content = tr.Replace(content, placeholder, "") + } + err := tr.Err() + if err != nil { + require.ErrorIs(t, err, newErrMissingPlaceholder(tc.missing)) + } else { + require.Empty(t, tc.missing) + } + }) + } +} + +func TestReplaceAll(t *testing.T) { + tests := []struct { + desc string + content string + replace []string + missing []string + }{ + { + desc: "FoundAll", + content: "#one #one #two", + replace: []string{"#one", "#two"}, + }, + { + desc: "MissingAll", + content: "", + replace: []string{"#one", "#two"}, + missing: []string{"#one", "#two"}, + }, + { + desc: "MissingOne", + content: "#two #two", + replace: []string{"#one", "#two"}, + missing: []string{"#one"}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + tr := New() + content := tc.content + for _, placeholder := range tc.replace { + content = tr.ReplaceAll(content, placeholder, "") + } + err := tr.Err() + if err != nil { + require.ErrorIs(t, err, newErrMissingPlaceholder(tc.missing)) + } else { + require.Empty(t, tc.missing) + } + }) + } +} diff --git a/pkg/tarball/tarball.go b/pkg/tarball/tarball.go new file mode 100755 index 000000000..02f15abe7 --- /dev/null +++ b/pkg/tarball/tarball.go @@ -0,0 +1,57 @@ +package tarball + +import ( + "archive/tar" + "compress/gzip" + "errors" + "io" + "path/filepath" +) + +var ( + // ErrGzipFileNotFound the file not found in the gzip. + ErrGzipFileNotFound = errors.New("file not found in the gzip") + // ErrNotGzipType the file is not a gzip. + ErrNotGzipType = errors.New("file is not a gzip type") + // ErrInvalidFileName the file name is invalid. + ErrInvalidFileName = errors.New("invalid file name") +) + +// ExtractFile founds and reads a specific file into a gzip file and folders recursively. +func ExtractFile(reader io.Reader, out io.Writer, fileName string) (string, error) { + if fileName == "" { + return "", ErrInvalidFileName + } + archive, err := gzip.NewReader(reader) + // Verify if is a GZIP file + if errors.Is(err, io.EOF) || errors.Is(err, gzip.ErrHeader) { + return "", ErrNotGzipType + } else if err != nil { + return "", err + } + defer archive.Close() + + tarReader := tar.NewReader(archive) + // Read the tarball files and find only the necessary file + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + return "", ErrGzipFileNotFound + } else if err != nil { + return header.Name, err + } + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + name := filepath.Base(header.Name) + if fileName == name { + _, err := io.Copy(out, tarReader) + return header.Name, err + } + default: + continue + } + } +} diff --git a/pkg/validation/errors.go b/pkg/validation/errors.go new file mode 100755 index 000000000..345ad9de5 --- /dev/null +++ b/pkg/validation/errors.go @@ -0,0 +1,7 @@ +package validation + +// Error must be implemented by errors that provide validation info. +type Error interface { + error + ValidationInfo() string +} diff --git a/pkg/xgenny/run.go b/pkg/xgenny/run.go new file mode 100755 index 000000000..c34153df8 --- /dev/null +++ b/pkg/xgenny/run.go @@ -0,0 +1,104 @@ +package xgenny + +import ( + "context" + "errors" + "os" + "strings" + + "github.com/gobuffalo/genny/v2" + "github.com/gobuffalo/logger" + "github.com/gobuffalo/packd" + + "github.com/sonr-io/core/pkg/placeholder" + "github.com/sonr-io/core/pkg/validation" +) + +var _ validation.Error = (*dryRunError)(nil) + +type dryRunError struct { + error +} + +// ValidationInfo returns validation info. +func (d *dryRunError) ValidationInfo() string { + return d.Error() +} + +// DryRunner is a genny DryRunner with a logger. +func DryRunner(ctx context.Context) *genny.Runner { + runner := genny.DryRunner(ctx) + runner.Logger = logger.New(genny.DefaultLogLvl) + return runner +} + +// RunWithValidation checks the generators with a dry run and then execute the wet runner to the generators. +func RunWithValidation( + tracer *placeholder.Tracer, + gens ...*genny.Generator, +) (sm SourceModification, err error) { + // run executes the provided runner with the provided generator + run := func(runner *genny.Runner, gen *genny.Generator) error { + err := runner.With(gen) + if err != nil { + return err + } + return runner.Run() + } + for _, gen := range gens { + // check with a dry runner the generators + dryRunner := DryRunner(context.Background()) + if err := run(dryRunner, gen); err != nil { + if errors.Is(err, os.ErrNotExist) { + return sm, &dryRunError{err} + } + return sm, err + } + if err := tracer.Err(); err != nil { + return sm, err + } + + // fetch the source modification + sm = NewSourceModification() + for _, file := range dryRunner.Results().Files { + fileName := file.Name() + _, err := os.Stat(fileName) + + //nolint:gocritic + if os.IsNotExist(err) { + // if the file doesn't exist in the source, it means it has been created by the runner + sm.AppendCreatedFiles(fileName) + } else if err != nil { + return sm, err + } else { + // the file has been modified by the runner + sm.AppendModifiedFiles(fileName) + } + } + + // execute the modification with a wet runner + if err := run(genny.WetRunner(context.Background()), gen); err != nil { + return sm, err + } + } + return sm, nil +} + +// Box will mount each file in the Box and wrap it, already existing files are ignored. +func Box(g *genny.Generator, box packd.Walker) error { + return box.Walk(func(path string, bf packd.File) error { + f := genny.NewFile(path, bf) + f, err := g.Transform(f) + if err != nil { + return err + } + filePath := strings.TrimSuffix(f.Name(), ".plush") + _, err = os.Stat(filePath) + if os.IsNotExist(err) { + // path doesn't exist. move on. + g.File(f) + return nil + } + return err + }) +} diff --git a/pkg/xgenny/sourcemodification.go b/pkg/xgenny/sourcemodification.go new file mode 100755 index 000000000..f74bb90fd --- /dev/null +++ b/pkg/xgenny/sourcemodification.go @@ -0,0 +1,58 @@ +package xgenny + +// SourceModification describes modified and created files in the source code after a run. +type SourceModification struct { + modified map[string]struct{} + created map[string]struct{} +} + +func NewSourceModification() SourceModification { + return SourceModification{ + make(map[string]struct{}), + make(map[string]struct{}), + } +} + +// ModifiedFiles returns the modified files of the source modification. +func (sm SourceModification) ModifiedFiles() (modifiedFiles []string) { + for modified := range sm.modified { + modifiedFiles = append(modifiedFiles, modified) + } + return +} + +// CreatedFiles returns the created files of the source modification. +func (sm SourceModification) CreatedFiles() (createdFiles []string) { + for created := range sm.created { + createdFiles = append(createdFiles, created) + } + return +} + +// AppendModifiedFiles appends modified files in the source modification that are not already documented. +func (sm *SourceModification) AppendModifiedFiles(modifiedFiles ...string) { + for _, modifiedFile := range modifiedFiles { + _, alreadyModified := sm.modified[modifiedFile] + _, alreadyCreated := sm.created[modifiedFile] + if !alreadyModified && !alreadyCreated { + sm.modified[modifiedFile] = struct{}{} + } + } +} + +// AppendCreatedFiles appends a created files in the source modification that are not already documented. +func (sm *SourceModification) AppendCreatedFiles(createdFiles ...string) { + for _, createdFile := range createdFiles { + _, alreadyModified := sm.modified[createdFile] + _, alreadyCreated := sm.created[createdFile] + if !alreadyModified && !alreadyCreated { + sm.created[createdFile] = struct{}{} + } + } +} + +// Merge merges new source modification to an existing one. +func (sm *SourceModification) Merge(newSm SourceModification) { + sm.AppendModifiedFiles(newSm.ModifiedFiles()...) + sm.AppendCreatedFiles(newSm.CreatedFiles()...) +} diff --git a/pkg/xgenny/sourcemodification_test.go b/pkg/xgenny/sourcemodification_test.go new file mode 100755 index 000000000..18d8887ac --- /dev/null +++ b/pkg/xgenny/sourcemodification_test.go @@ -0,0 +1,83 @@ +package xgenny_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/sonr-io/core/pkg/xgenny" +) + +var ( + modifiedExample = []string{"mfoo", "mbar", "mfoobar"} + createdExample = []string{"cfoo", "cbar", "cfoobar"} +) + +func sourceModificationExample() xgenny.SourceModification { + sourceModification := xgenny.NewSourceModification() + sourceModification.AppendModifiedFiles(modifiedExample...) + sourceModification.AppendCreatedFiles(createdExample...) + return sourceModification +} + +func TestNewSourceModification(t *testing.T) { + sm := xgenny.NewSourceModification() + require.Empty(t, sm.ModifiedFiles()) + require.Empty(t, sm.CreatedFiles()) +} + +func TestModifiedFiles(t *testing.T) { + sm := sourceModificationExample() + require.Len(t, sm.ModifiedFiles(), len(modifiedExample)) + require.Subset(t, sm.ModifiedFiles(), modifiedExample) +} + +func TestCreatedFiles(t *testing.T) { + sm := sourceModificationExample() + require.Len(t, sm.CreatedFiles(), len(createdExample)) + require.Subset(t, sm.CreatedFiles(), createdExample) +} + +func TestAppendModifiedFiles(t *testing.T) { + sm := sourceModificationExample() + sm.AppendModifiedFiles("foo1") + require.Len(t, sm.ModifiedFiles(), len(modifiedExample)+1) + require.Contains(t, sm.ModifiedFiles(), "foo1") + + // Do not append a existing element + sm.AppendModifiedFiles("foo1") + require.Len(t, sm.ModifiedFiles(), len(modifiedExample)+1) + sm.AppendCreatedFiles("foo2") + sm.AppendModifiedFiles("foo2") + require.Len(t, sm.ModifiedFiles(), len(modifiedExample)+1) +} + +func TestAppendCreatedFiles(t *testing.T) { + sm := sourceModificationExample() + sm.AppendCreatedFiles("foo1") + require.Len(t, sm.CreatedFiles(), len(createdExample)+1) + require.Contains(t, sm.CreatedFiles(), "foo1") + + // Do not append a existing element + sm.AppendCreatedFiles("foo1") + require.Len(t, sm.CreatedFiles(), len(createdExample)+1) + sm.AppendModifiedFiles("foo2") + sm.AppendCreatedFiles("foo2") + require.Len(t, sm.ModifiedFiles(), len(modifiedExample)+1) +} + +func TestMerge(t *testing.T) { + sm1 := xgenny.NewSourceModification() + sm2 := xgenny.NewSourceModification() + + sm1.AppendModifiedFiles("foo1", "foo2", "foo3") + sm2.AppendModifiedFiles("foo3", "foo4", "foo5") + sm1.AppendCreatedFiles("bar1", "bar2", "bar3") + sm2.AppendCreatedFiles("foo1", "bar2", "bar3") + + sm1.Merge(sm2) + require.Len(t, sm1.ModifiedFiles(), 5) + require.Len(t, sm1.CreatedFiles(), 3) + require.Subset(t, sm1.ModifiedFiles(), []string{"foo1", "foo2", "foo3", "foo4", "foo5"}) + require.Subset(t, sm1.CreatedFiles(), []string{"bar1", "bar2", "bar3"}) +} diff --git a/pkg/xgenny/xgenny.go b/pkg/xgenny/xgenny.go new file mode 100755 index 000000000..4338a58c7 --- /dev/null +++ b/pkg/xgenny/xgenny.go @@ -0,0 +1,77 @@ +package xgenny + +import ( + "bytes" + "embed" + "path/filepath" + "strings" + + "github.com/gobuffalo/genny/v2" + "github.com/gobuffalo/plush/v4" + "github.com/pkg/errors" + + "github.com/gobuffalo/packd" +) + +// Walker implements packd.Walker for Go embed's fs.FS. +type Walker struct { + fs embed.FS + trimPrefix string + path string +} + +// NewEmbedWalker returns a new Walker for fs. +// trimPrefix is used to trim parent paths from the paths of found files. +func NewEmbedWalker(fs embed.FS, trimPrefix, path string) Walker { + return Walker{fs: fs, trimPrefix: trimPrefix, path: path} +} + +// Walk implements packd.Walker. +func (w Walker) Walk(wl packd.WalkFunc) error { + return w.walkDir(wl, ".") +} + +func (w Walker) walkDir(wl packd.WalkFunc, path string) error { + entries, err := w.fs.ReadDir(path) + if err != nil { + return err + } + + for _, entry := range entries { + if entry.IsDir() { + w.walkDir(wl, filepath.Join(path, entry.Name())) + continue + } + + entryPath := filepath.Join(path, entry.Name()) + + data, err := w.fs.ReadFile(entryPath) + if err != nil { + return err + } + + trimPath := strings.TrimPrefix(entryPath, w.trimPrefix) + trimPath = filepath.Join(w.path, trimPath) + f, err := packd.NewFile(trimPath, bytes.NewReader(data)) + if err != nil { + return err + } + + wl(trimPath, f) + } + + return nil +} + +// Transformer will plush-ify any file that has a ".plush" extension. +func Transformer(ctx *plush.Context) genny.Transformer { + t := genny.NewTransformer(".plush", func(f genny.File) (genny.File, error) { + s, err := plush.RenderR(f, ctx) + if err != nil { + return f, errors.Wrap(err, f.Name()) + } + return genny.NewFileS(f.Name(), s), nil + }) + t.StripExt = true + return t +} diff --git a/pkg/xgenny/xgenny_test.go b/pkg/xgenny/xgenny_test.go new file mode 100755 index 000000000..153101922 --- /dev/null +++ b/pkg/xgenny/xgenny_test.go @@ -0,0 +1,47 @@ +package xgenny_test + +import ( + "io" + "strings" + "testing" + + "github.com/gobuffalo/genny/v2" + "github.com/gobuffalo/plush/v4" + "github.com/stretchr/testify/require" + + "github.com/sonr-io/core/pkg/xgenny" +) + +func Test_Transformer(t *testing.T) { + r := require.New(t) + + ctx := plush.NewContext() + ctx.Set("name", "mark") + f := genny.NewFile("foo.plush.txt", strings.NewReader("Hello <%= name %>")) + + tr := xgenny.Transformer(ctx) + f, err := tr.Transform(f) + r.NoError(err) + r.Equal("foo.txt", f.Name()) + + b, err := io.ReadAll(f) + r.NoError(err) + r.Equal("Hello mark", string(b)) +} + +func Test_Transformer_No_Ext(t *testing.T) { + r := require.New(t) + + ctx := plush.NewContext() + ctx.Set("name", "mark") + f := genny.NewFile("foo.txt", strings.NewReader("Hello <%= name %>")) + + tr := xgenny.Transformer(ctx) + f, err := tr.Transform(f) + r.NoError(err) + r.Equal("foo.txt", f.Name()) + + b, err := io.ReadAll(f) + r.NoError(err) + r.Equal("Hello <%= name %>", string(b)) +} diff --git a/services/did/controller/controller.go b/services/did/controller/controller.go index a30c087a2..976095e3a 100644 --- a/services/did/controller/controller.go +++ b/services/did/controller/controller.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/highlight/highlight/sdk/highlight-go" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/method/btcr" "github.com/sonr-io/core/services/did/method/ethr" "github.com/sonr-io/core/services/did/method/sonr" diff --git a/services/did/method/authr/authr.go b/services/did/method/authr/authr.go index e641282b4..d9d050a14 100644 --- a/services/did/method/authr/authr.go +++ b/services/did/method/authr/authr.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/go-webauthn/webauthn/protocol" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/types" "github.com/sonr-io/kryptology/pkg/core/curves" ) diff --git a/services/did/method/authr/secret.go b/services/did/method/authr/secret.go index 217dcfc8c..02f7918c5 100644 --- a/services/did/method/authr/secret.go +++ b/services/did/method/authr/secret.go @@ -9,7 +9,7 @@ import ( "io" "github.com/google/uuid" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/kryptology/pkg/accumulator" "github.com/sonr-io/kryptology/pkg/core/curves" "golang.org/x/crypto/hkdf" diff --git a/services/did/method/btcr/account.go b/services/did/method/btcr/account.go index 796006f97..6b453fd4b 100644 --- a/services/did/method/btcr/account.go +++ b/services/did/method/btcr/account.go @@ -1,7 +1,7 @@ package btcr import ( - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/types" "github.com/sonr-io/core/services/mpc" ) diff --git a/services/did/method/ethr/account.go b/services/did/method/ethr/account.go index 0f52c07ff..da95db058 100644 --- a/services/did/method/ethr/account.go +++ b/services/did/method/ethr/account.go @@ -1,7 +1,7 @@ package ethr import ( - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/types" "github.com/sonr-io/core/services/mpc" ) diff --git a/services/did/method/sonr/account.go b/services/did/method/sonr/account.go index 141b26af7..7d57e97dd 100644 --- a/services/did/method/sonr/account.go +++ b/services/did/method/sonr/account.go @@ -3,7 +3,7 @@ package sonr import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/types" "github.com/sonr-io/core/services/mpc" ) diff --git a/services/did/method/sonr/signer.go b/services/did/method/sonr/signer.go index 788d3c26c..782166537 100644 --- a/services/did/method/sonr/signer.go +++ b/services/did/method/sonr/signer.go @@ -7,7 +7,7 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/did/types" identitytypes "github.com/sonr-io/core/x/identity/types" ) diff --git a/services/did/types/accumulator.go b/services/did/types/accumulator.go index 67b23087a..cdc789802 100644 --- a/services/did/types/accumulator.go +++ b/services/did/types/accumulator.go @@ -7,7 +7,7 @@ import ( "github.com/sonr-io/kryptology/pkg/accumulator" "github.com/sonr-io/kryptology/pkg/core/curves" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" ) // DIDAccumulator is a ZKSet accumulator for a DID diff --git a/services/did/types/identifier.go b/services/did/types/identifier.go index fbf301cbb..7ca91fa83 100644 --- a/services/did/types/identifier.go +++ b/services/did/types/identifier.go @@ -3,7 +3,7 @@ package types import ( "github.com/sonr-io/kryptology/pkg/accumulator" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" identitytypes "github.com/sonr-io/core/x/identity/types" ) diff --git a/services/did/types/method.go b/services/did/types/method.go index d3a08ee65..9efb3b5f5 100644 --- a/services/did/types/method.go +++ b/services/did/types/method.go @@ -1,6 +1,6 @@ package types -import "github.com/sonr-io/core/pkg/crypto" +import "github.com/sonr-io/core/internal/crypto" // DIDMethod is a DID method type DIDMethod string diff --git a/services/did/types/resource.go b/services/did/types/resource.go index ad0c6c453..6e9d72d35 100644 --- a/services/did/types/resource.go +++ b/services/did/types/resource.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" ) // TxResponse is a type alias for sdk.TxResponse diff --git a/services/mpc/base/account.go b/services/mpc/base/account.go index e56ce3fba..fe343107e 100644 --- a/services/mpc/base/account.go +++ b/services/mpc/base/account.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" algo "github.com/sonr-io/core/services/mpc/protocol/dkls" diff --git a/services/mpc/mpc.go b/services/mpc/mpc.go index 1bd1e0d5b..5e6665172 100644 --- a/services/mpc/mpc.go +++ b/services/mpc/mpc.go @@ -1,7 +1,7 @@ package mpc import ( - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/mpc/base" models "github.com/sonr-io/core/services/mpc/base" v1algo "github.com/sonr-io/core/services/mpc/protocol/dkls" diff --git a/services/mpc/mpc_test.go b/services/mpc/mpc_test.go index 54e08adbb..acc97b775 100644 --- a/services/mpc/mpc_test.go +++ b/services/mpc/mpc_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" v1 "github.com/sonr-io/core/services/mpc/base" "github.com/stretchr/testify/assert" ) diff --git a/services/mpc/protocol/dkls/dkls_test.go b/services/mpc/protocol/dkls/dkls_test.go index 937da37e3..21e4af40d 100644 --- a/services/mpc/protocol/dkls/dkls_test.go +++ b/services/mpc/protocol/dkls/dkls_test.go @@ -3,7 +3,7 @@ package dkls_test import ( "testing" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/services/mpc/protocol/dkls" "github.com/stretchr/testify/assert" ) diff --git a/services/mpc/types/keyshare.go b/services/mpc/types/keyshare.go index fba1db75f..4bfa823bc 100644 --- a/services/mpc/types/keyshare.go +++ b/services/mpc/types/keyshare.go @@ -5,7 +5,7 @@ import ( "fmt" "math/big" - sonrcrypto "github.com/sonr-io/core/pkg/crypto" + sonrcrypto "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/types/crypto" "github.com/sonr-io/kryptology/pkg/core/curves" "github.com/sonr-io/kryptology/pkg/core/protocol" diff --git a/services/mpc/types/kss.go b/services/mpc/types/kss.go index 3de991b92..3a4e62c88 100644 --- a/services/mpc/types/kss.go +++ b/services/mpc/types/kss.go @@ -8,7 +8,7 @@ import ( dklsv1 "github.com/sonr-io/kryptology/pkg/tecdsa/dkls/v1" secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "golang.org/x/crypto/sha3" ) diff --git a/services/plugin/cache.go b/services/plugin/cache.go index 2c829ff2b..8708c8dd6 100644 --- a/services/plugin/cache.go +++ b/services/plugin/cache.go @@ -12,10 +12,11 @@ import ( ) const ( - cacheFileName = "ignite_plugin_cache.db" + cacheFileName = "sonr_plugin_cache.db" cacheNamespace = "plugin.rpc.context" ) + var storageCache *cache.Cache[hplugin.ReattachConfig] func init() { @@ -28,7 +29,7 @@ func writeConfigCache(pluginPath string, conf hplugin.ReattachConfig) error { return fmt.Errorf("provided path is invalid: %s", pluginPath) } if conf.Addr == nil { - return fmt.Errorf("app Address info cannot be empty") + return fmt.Errorf("plugin Address info cannot be empty") } cache, err := newCache() if err != nil { diff --git a/services/plugin/cache_test.go b/services/plugin/cache_test.go new file mode 100755 index 000000000..c11ca8b03 --- /dev/null +++ b/services/plugin/cache_test.go @@ -0,0 +1,109 @@ +package plugin + +import ( + "net" + "testing" + + hplugin "github.com/hashicorp/go-plugin" + "github.com/stretchr/testify/require" +) + +func TestReadWriteConfigCache(t *testing.T) { + t.Run("Should cache plugin config and read from cache", func(t *testing.T) { + const path = "/path/to/awesome/plugin" + unixFD, _ := net.ResolveUnixAddr("unix", "/var/folders/5k/sv4bxrs102n_6rr7430jc7j80000gn/T/plugin193424090") + + rc := hplugin.ReattachConfig{ + Protocol: hplugin.ProtocolNetRPC, + ProtocolVersion: hplugin.CoreProtocolVersion, + Addr: unixFD, + Pid: 24464, + } + + err := writeConfigCache(path, rc) + require.NoError(t, err) + + c, err := readConfigCache(path) + require.NoError(t, err) + require.Equal(t, rc, c) + }) + + t.Run("Should error writing bad plugin config to cache", func(t *testing.T) { + const path = "/path/to/awesome/plugin" + rc := hplugin.ReattachConfig{ + Protocol: hplugin.ProtocolNetRPC, + ProtocolVersion: hplugin.CoreProtocolVersion, + Addr: nil, + Pid: 24464, + } + + err := writeConfigCache(path, rc) + require.Error(t, err) + }) + + t.Run("Should error with invalid plugin path", func(t *testing.T) { + const path = "" + rc := hplugin.ReattachConfig{ + Protocol: hplugin.ProtocolNetRPC, + ProtocolVersion: hplugin.CoreProtocolVersion, + Addr: nil, + Pid: 24464, + } + + err := writeConfigCache(path, rc) + require.Error(t, err) + }) +} + +func TestDeleteConfCache(t *testing.T) { + t.Run("Delete plugin config after write to cache should remove from cache", func(t *testing.T) { + const path = "/path/to/awesome/plugin" + unixFD, _ := net.ResolveUnixAddr("unix", "/var/folders/5k/sv4bxrs102n_6rr7430jc7j80000gn/T/plugin193424090") + + rc := hplugin.ReattachConfig{ + Protocol: hplugin.ProtocolNetRPC, + ProtocolVersion: hplugin.CoreProtocolVersion, + Addr: unixFD, + Pid: 24464, + } + + err := writeConfigCache(path, rc) + require.NoError(t, err) + + err = deleteConfCache(path) + require.NoError(t, err) + + // there should be an error after deleting the config from the cache + _, err = readConfigCache(path) + require.Error(t, err) + }) + + t.Run("Delete plugin config should return error given empty path", func(t *testing.T) { + const path = "" + err := deleteConfCache(path) + require.Error(t, err) + }) +} + +func TestCheckConfCache(t *testing.T) { + const path = "/path/to/awesome/plugin" + unixFD, _ := net.ResolveUnixAddr("unix", "/var/folders/5k/sv4bxrs102n_6rr7430jc7j80000gn/T/plugin193424090") + + rc := hplugin.ReattachConfig{ + Protocol: hplugin.ProtocolNetRPC, + ProtocolVersion: hplugin.CoreProtocolVersion, + Addr: unixFD, + Pid: 24464, + } + + t.Run("Cache should be hydrated", func(t *testing.T) { + err := writeConfigCache(path, rc) + require.NoError(t, err) + require.Equal(t, true, checkConfCache(path)) + }) + + t.Run("Cache should be empty", func(t *testing.T) { + _ = deleteConfCache(path) + require.Equal(t, false, checkConfCache(path)) + }) +} diff --git a/services/plugin/interface_test.go b/services/plugin/interface_test.go new file mode 100755 index 000000000..f18b290a1 --- /dev/null +++ b/services/plugin/interface_test.go @@ -0,0 +1,169 @@ +package plugin_test + +import ( + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/sonr-io/core/services/plugin" +) + +func TestCommandToCobraCommand(t *testing.T) { + var ( + require = require.New(t) + assert = assert.New(t) + pcmd = plugin.Command{ + Use: "new", + Aliases: []string{"n"}, + Short: "short", + Long: "long", + Hidden: true, + Flags: []plugin.Flag{ + { + Name: "bool", + Shorthand: "b", + DefValue: "true", + Value: "true", + Usage: "a bool", + Type: plugin.FlagTypeBool, + }, + { + Name: "string", + DefValue: "hello", + Value: "hello", + Usage: "a string", + Type: plugin.FlagTypeString, + Persistent: true, + }, + }, + Commands: []plugin.Command{ + { + Use: "sub", + Aliases: []string{"s"}, + Short: "sub short", + Long: "sub long", + }, + }, + } + ) + + cmd, err := pcmd.ToCobraCommand() + + require.NoError(err) + require.NotNil(cmd) + assert.Empty(cmd.Commands()) // subcommands aren't converted + assert.Equal(pcmd.Use, cmd.Use) + assert.Equal(pcmd.Short, cmd.Short) + assert.Equal(pcmd.Long, cmd.Long) + assert.Equal(pcmd.Aliases, cmd.Aliases) + assert.Equal(pcmd.Hidden, cmd.Hidden) + for _, f := range pcmd.Flags { + if f.Persistent { + assert.NotNil(cmd.PersistentFlags().Lookup(f.Name), "missing pflag %s", f.Name) + } else { + assert.NotNil(cmd.Flags().Lookup(f.Name), "missing flag %s", f.Name) + } + } +} + +func TestManifestImportCobraCommand(t *testing.T) { + manifest := plugin.Manifest{ + Name: "hey", + Commands: []plugin.Command{ + {Use: "existing"}, + }, + } + cmd := &cobra.Command{ + Use: "new", + Aliases: []string{"n"}, + Short: "short", + Long: "long", + Hidden: true, + } + cmd.Flags().BoolP("bool", "b", true, "a bool") + cmd.Flags().String("string", "hello", "a string") + cmd.PersistentFlags().String("persistent", "hello", "a persistent string") + subcmd := &cobra.Command{ + Use: "sub", + Aliases: []string{"s"}, + Short: "sub short", + Long: "sub long", + } + subcmd.Flags().BoolP("subbool", "b", true, "a bool") + subcmd.Flags().String("substring", "hello", "a string") + subcmd.AddCommand(&cobra.Command{ + Use: "subsub", + }) + cmd.AddCommand(subcmd) + + manifest.ImportCobraCommand(cmd, "under") + + expectedManifest := plugin.Manifest{ + Name: "hey", + Commands: []plugin.Command{ + {Use: "existing"}, + { + Use: "new", + Aliases: []string{"n"}, + Short: "short", + Long: "long", + Hidden: true, + PlaceCommandUnder: "under", + Flags: []plugin.Flag{ + { + Name: "bool", + Shorthand: "b", + DefValue: "true", + Value: "true", + Usage: "a bool", + Type: plugin.FlagTypeBool, + }, + { + Name: "string", + DefValue: "hello", + Value: "hello", + Usage: "a string", + Type: plugin.FlagTypeString, + }, + { + Name: "persistent", + DefValue: "hello", + Value: "hello", + Usage: "a persistent string", + Type: plugin.FlagTypeString, + Persistent: true, + }, + }, + Commands: []plugin.Command{ + { + Use: "sub", + Aliases: []string{"s"}, + Short: "sub short", + Long: "sub long", + Flags: []plugin.Flag{ + { + Name: "subbool", + Shorthand: "b", + DefValue: "true", + Value: "true", + Usage: "a bool", + Type: plugin.FlagTypeBool, + }, + { + Name: "substring", + DefValue: "hello", + Value: "hello", + Usage: "a string", + Type: plugin.FlagTypeString, + }, + }, + Commands: []plugin.Command{{Use: "subsub"}}, + }, + }, + }, + }, + } + assert.Equal(t, expectedManifest, manifest) +} diff --git a/services/plugin/plugin.go b/services/plugin/plugin.go index 3b7b8083e..839ca96de 100644 --- a/services/plugin/plugin.go +++ b/services/plugin/plugin.go @@ -31,7 +31,7 @@ import ( // PluginsPath holds the plugin cache directory. var PluginsPath = xfilepath.Mkdir(xfilepath.Join( config.DirPath, - xfilepath.Path("apps"), + xfilepath.Path("plugins"), )) // Plugin represents a ignite plugin. @@ -43,12 +43,12 @@ type Plugin struct { // If any error occurred during the plugin load, it's stored here Error error - name string - repoPath string - cloneURL string - cloneDir string - reference string - srcPath string + repoPath string + cloneURL string + cloneDir string + reference string + srcPath string + binaryName string client *hplugin.Client @@ -74,13 +74,15 @@ func CollectEvents(ev events.Bus) Option { // Load loads the plugins found in the chain config. // // There's 2 kinds of plugins, local or remote. -// Local plugins have their path starting with a `/`, while remote plugins don't. +// Local plugins have their path starting with a `/`, while remote plugins +// don't. // Local plugins are useful for development purpose. -// Remote plugins require to be fetched first, in $HOME/.ignite/apps folder, -// then they are loaded from there. +// Remote plugins require to be fetched first, in $HOME/.sonr/plugins +// folder, then they are loaded from there. // -// If an error occurs during a plugin load, it's not returned but rather stored in -// the `Plugin.Error` field. This prevents the loading of other plugins to be interrupted. +// If an error occurs during a plugin load, it's not returned but rather stored +// in the Plugin.Error field. This prevents the loading of other plugins to be +// interrupted. func Load(ctx context.Context, plugins []pluginsconfig.Plugin, options ...Option) ([]*Plugin, error) { pluginsDir, err := PluginsPath() if err != nil { @@ -117,7 +119,7 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P pluginPath = cp.Path ) if pluginPath == "" { - p.Error = errors.Errorf(`missing app property "path"`) + p.Error = errors.Errorf(`missing plugin property "path"`) return p } @@ -130,15 +132,15 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P // This is a local plugin, check if the file exists st, err := os.Stat(pluginPath) if err != nil { - p.Error = errors.Wrapf(err, "local app path %q not found", pluginPath) + p.Error = errors.Wrapf(err, "local plugin path %q not found", pluginPath) return p } if !st.IsDir() { - p.Error = errors.Errorf("local app path %q is not a directory", pluginPath) + p.Error = errors.Errorf("local plugin path %q is not a dir", pluginPath) return p } p.srcPath = pluginPath - p.name = path.Base(pluginPath) + p.binaryName = path.Base(pluginPath) return p } // This is a remote plugin, parse the URL @@ -149,7 +151,7 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P } parts := strings.Split(pluginPath, "/") if len(parts) < 3 { - p.Error = errors.Errorf("app path %q is not a valid repository URL", pluginPath) + p.Error = errors.Errorf("plugin path %q is not a valid repository URL", pluginPath) return p } p.repoPath = path.Join(parts[:3]...) @@ -163,12 +165,12 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P p.cloneDir = path.Join(pluginsDir, p.repoPath) } - // Plugin can have a subpath within its repository. - // For example, "github.com/ignite/apps/app1" where "app1" is the subpath. + // Plugin can have a subpath within its repository. For example, + // "github.com/ignite/plugins/plugin1" where "plugin1" is the subpath. repoSubPath := path.Join(parts[3:]...) p.srcPath = path.Join(p.cloneDir, repoSubPath) - p.name = path.Base(pluginPath) + p.binaryName = path.Base(pluginPath) return p } @@ -186,17 +188,13 @@ func (p *Plugin) KillClient() { } if p.isHost { - _ = deleteConfCache(p.Path) + deleteConfCache(p.Path) p.isHost = false } } -func (p Plugin) binaryName() string { - return fmt.Sprintf("%s.app", p.name) -} - -func (p Plugin) binaryPath() string { - return path.Join(p.srcPath, p.binaryName()) +func (p *Plugin) binaryPath() string { + return path.Join(p.srcPath, p.binaryName) } // load tries to fill p.Interface, ensuring the plugin is usable. @@ -231,7 +229,7 @@ func (p *Plugin) load(ctx context.Context) { } // pluginMap is the map of plugins we can dispense. pluginMap := map[string]hplugin.Plugin{ - p.name: &InterfacePlugin{}, + p.binaryName: &InterfacePlugin{}, } // Create an hclog.Logger logLevel := hclog.Error @@ -239,7 +237,7 @@ func (p *Plugin) load(ctx context.Context) { logLevel = hclog.Trace } logger := hclog.New(&hclog.LoggerOptions{ - Name: fmt.Sprintf("app %s", p.Path), + Name: fmt.Sprintf("plugin %s", p.Path), Output: os.Stderr, Level: logLevel, }) @@ -281,7 +279,7 @@ func (p *Plugin) load(ctx context.Context) { } // Request the plugin - raw, err := rpcClient.Dispense(p.name) + raw, err := rpcClient.Dispense(p.binaryName) if err != nil { p.Error = errors.Wrapf(err, "dispensing") return @@ -321,8 +319,8 @@ func (p *Plugin) fetch() { if p.Error != nil { return } - p.ev.Send(fmt.Sprintf("Fetching app %q", p.cloneURL), events.ProgressStart()) - defer p.ev.Send(fmt.Sprintf("%s App fetched ✅", p.cloneURL), events.ProgressFinish()) + p.ev.Send(fmt.Sprintf("Fetching plugin %q", p.cloneURL), events.ProgressStart()) + defer p.ev.Send(fmt.Sprintf("Plugin fetched %q", p.cloneURL), events.ProgressFinish()) urlref := strings.Join([]string{p.cloneURL, p.reference}, "@") err := xgit.Clone(context.Background(), urlref, p.cloneDir) @@ -336,14 +334,14 @@ func (p *Plugin) build(ctx context.Context) { if p.Error != nil { return } - p.ev.Send(fmt.Sprintf("Building app %q", p.Path), events.ProgressStart()) - defer p.ev.Send(fmt.Sprintf("%s App built ✅", p.Path), events.ProgressFinish()) + p.ev.Send(fmt.Sprintf("Building plugin %q", p.Path), events.ProgressStart()) + defer p.ev.Send(fmt.Sprintf("Plugin built %q", p.Path), events.ProgressFinish()) if err := gocmd.ModTidy(ctx, p.srcPath); err != nil { p.Error = errors.Wrapf(err, "go mod tidy") return } - if err := gocmd.Build(ctx, p.binaryName(), p.srcPath, nil); err != nil { + if err := gocmd.Build(ctx, p.binaryName, p.srcPath, nil); err != nil { p.Error = errors.Wrapf(err, "go build") return } @@ -391,7 +389,7 @@ func (p *Plugin) outdatedBinary() bool { return nil }) if err != nil { - fmt.Printf("error while walking app source path %q\n", p.srcPath) + fmt.Printf("error while walking plugin source path %q\n", p.srcPath) return false } return mostRecent.After(binaryTime) diff --git a/services/plugin/plugin_test.go b/services/plugin/plugin_test.go new file mode 100644 index 000000000..62cb96430 --- /dev/null +++ b/services/plugin/plugin_test.go @@ -0,0 +1,519 @@ +package plugin + +import ( + "context" + "fmt" + "os" + "path" + "path/filepath" + "testing" + "time" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" + hplugin "github.com/hashicorp/go-plugin" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + pluginsconfig "github.com/sonr-io/core/config/plugins" + "github.com/sonr-io/core/pkg/gocmd" + "github.com/sonr-io/core/pkg/gomodule" +) + +func TestNewPlugin(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + tests := []struct { + name string + pluginCfg pluginsconfig.Plugin + expectedPlugin Plugin + }{ + { + name: "fail: empty path", + expectedPlugin: Plugin{ + Error: errors.Errorf(`missing plugin property "path"`), + }, + }, + { + name: "fail: local plugin doesnt exists", + pluginCfg: pluginsconfig.Plugin{Path: "/xxx/yyy/plugin"}, + expectedPlugin: Plugin{ + Error: errors.Errorf(`local plugin path "/xxx/yyy/plugin" not found`), + }, + }, + { + name: "fail: local plugin is not a dir", + pluginCfg: pluginsconfig.Plugin{Path: path.Join(wd, "testdata/fakebin")}, + expectedPlugin: Plugin{ + Error: errors.Errorf(fmt.Sprintf("local plugin path %q is not a dir", path.Join(wd, "testdata/fakebin"))), + }, + }, + { + name: "ok: local plugin", + pluginCfg: pluginsconfig.Plugin{Path: path.Join(wd, "testdata")}, + expectedPlugin: Plugin{ + srcPath: path.Join(wd, "testdata"), + binaryName: "testdata", + }, + }, + { + name: "fail: remote plugin with only domain", + pluginCfg: pluginsconfig.Plugin{Path: "github.com"}, + expectedPlugin: Plugin{ + Error: errors.Errorf(`plugin path "github.com" is not a valid repository URL`), + }, + }, + { + name: "fail: remote plugin with incomplete URL", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite"}, + expectedPlugin: Plugin{ + Error: errors.Errorf(`plugin path "github.com/ignite" is not a valid repository URL`), + }, + }, + { + name: "ok: remote plugin", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin", + reference: "", + srcPath: ".sonr/plugins/github.com/ignite/plugin", + binaryName: "plugin", + }, + }, + { + name: "ok: remote plugin with @ref", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin@develop"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin@develop", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin-develop", + reference: "develop", + srcPath: ".sonr/plugins/github.com/ignite/plugin-develop", + binaryName: "plugin", + }, + }, + { + name: "ok: remote plugin with @ref containing slash", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin@package/v1.0.0"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin@package/v1.0.0", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin-package-v1.0.0", + reference: "package/v1.0.0", + srcPath: ".sonr/plugins/github.com/ignite/plugin-package-v1.0.0", + binaryName: "plugin", + }, + }, + { + name: "ok: remote plugin with subpath", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin", + reference: "", + srcPath: ".sonr/plugins/github.com/ignite/plugin/plugin1", + binaryName: "plugin1", + }, + }, + { + name: "ok: remote plugin with subpath and @ref", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1@develop"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin@develop", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin-develop", + reference: "develop", + srcPath: ".sonr/plugins/github.com/ignite/plugin-develop/plugin1", + binaryName: "plugin1", + }, + }, + { + name: "ok: remote plugin with subpath and @ref containing slash", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1@package/v1.0.0"}, + expectedPlugin: Plugin{ + repoPath: "github.com/ignite/plugin@package/v1.0.0", + cloneURL: "https://github.com/ignite/plugin", + cloneDir: ".sonr/plugins/github.com/ignite/plugin-package-v1.0.0", + reference: "package/v1.0.0", + srcPath: ".sonr/plugins/github.com/ignite/plugin-package-v1.0.0/plugin1", + binaryName: "plugin1", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.expectedPlugin.Plugin = tt.pluginCfg + + p := newPlugin(".sonr/plugins", tt.pluginCfg) + + assertPlugin(t, tt.expectedPlugin, *p) + }) + } +} + +func TestPluginLoad(t *testing.T) { + wd, err := os.Getwd() + require.NoError(t, err) + + // Helper to make a local git repository with gofile committed. + // Returns the repo directory and the git.Repository + makeGitRepo := func(t *testing.T, name string) (string, *git.Repository) { + require := require.New(t) + repoDir := t.TempDir() + scaffoldPlugin(t, repoDir, "github.com/ignite/"+name, false) + require.NoError(err) + repo, err := git.PlainInit(repoDir, false) + require.NoError(err) + w, err := repo.Worktree() + require.NoError(err) + _, err = w.Add(".") + require.NoError(err) + _, err = w.Commit("msg", &git.CommitOptions{ + Author: &object.Signature{ + Name: "bob", + Email: "bob@example.com", + When: time.Now(), + }, + }) + require.NoError(err) + return repoDir, repo + } + tests := []struct { + name string + buildPlugin func(t *testing.T) Plugin + expectedError string + }{ + { + name: "fail: plugin is already in error", + buildPlugin: func(t *testing.T) Plugin { + return Plugin{ + Error: errors.New("oups"), + } + }, + expectedError: `oups`, + }, + { + name: "fail: no go files in srcPath", + buildPlugin: func(t *testing.T) Plugin { + return Plugin{ + srcPath: path.Join(wd, "testdata"), + binaryName: "testdata", + } + }, + expectedError: `no Go files in`, + }, + + { + name: "ok: from git repo", + buildPlugin: func(t *testing.T) Plugin { + repoDir, _ := makeGitRepo(t, "remote") + cloneDir := t.TempDir() + + return Plugin{ + cloneURL: repoDir, + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "remote"), + binaryName: "remote", + } + }, + }, + { + name: "fail: git repo doesnt exists", + buildPlugin: func(t *testing.T) Plugin { + cloneDir := t.TempDir() + + return Plugin{ + repoPath: "/xxxx/yyyy", + cloneURL: "/xxxx/yyyy", + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "plugin"), + } + }, + expectedError: `cloning "/xxxx/yyyy": repository not found`, + }, + { + name: "ok: from git repo with tag", + buildPlugin: func(t *testing.T) Plugin { + repoDir, repo := makeGitRepo(t, "remote-tag") + h, err := repo.Head() + require.NoError(t, err) + _, err = repo.CreateTag("v1", h.Hash(), &git.CreateTagOptions{ + Tagger: &object.Signature{Name: "me"}, + Message: "v1", + }) + require.NoError(t, err) + + cloneDir := t.TempDir() + + return Plugin{ + cloneURL: repoDir, + reference: "v1", + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "remote-tag"), + binaryName: "remote-tag", + } + }, + }, + { + name: "ok: from git repo with branch", + buildPlugin: func(t *testing.T) Plugin { + repoDir, repo := makeGitRepo(t, "remote-branch") + w, err := repo.Worktree() + require.NoError(t, err) + err = w.Checkout(&git.CheckoutOptions{ + Branch: plumbing.NewBranchReferenceName("branch1"), + Create: true, + }) + require.NoError(t, err) + + cloneDir := t.TempDir() + + return Plugin{ + cloneURL: repoDir, + reference: "branch1", + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "remote-branch"), + binaryName: "remote-branch", + } + }, + }, + { + name: "ok: from git repo with hash", + buildPlugin: func(t *testing.T) Plugin { + repoDir, repo := makeGitRepo(t, "remote-hash") + h, err := repo.Head() + require.NoError(t, err) + + cloneDir := t.TempDir() + + return Plugin{ + cloneURL: repoDir, + reference: h.Hash().String(), + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "remote-hash"), + binaryName: "remote-hash", + } + }, + }, + { + name: "fail: git ref not found", + buildPlugin: func(t *testing.T) Plugin { + repoDir, _ := makeGitRepo(t, "remote-no-ref") + + cloneDir := t.TempDir() + + return Plugin{ + cloneURL: repoDir, + reference: "doesnt_exists", + cloneDir: cloneDir, + srcPath: path.Join(cloneDir, "remote-no-ref"), + binaryName: "remote-no-ref", + } + }, + expectedError: `cloning ".*": reference not found`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require := require.New(t) + assert := assert.New(t) + p := tt.buildPlugin(t) + defer p.KillClient() + + p.load(context.Background()) + + if tt.expectedError != "" { + require.Error(p.Error, "expected error %q", tt.expectedError) + require.Regexp(tt.expectedError, p.Error.Error()) + return + } + + require.NoError(p.Error) + require.NotNil(p.Interface) + manifest, err := p.Interface.Manifest() + require.NoError(err) + assert.Equal(p.binaryName, manifest.Name) + assert.NoError(p.Interface.Execute(ExecutedCommand{})) + assert.NoError(p.Interface.ExecuteHookPre(ExecutedHook{})) + assert.NoError(p.Interface.ExecuteHookPost(ExecutedHook{})) + assert.NoError(p.Interface.ExecuteHookCleanUp(ExecutedHook{})) + }) + } +} + +func TestPluginLoadSharedHost(t *testing.T) { + tests := []struct { + name string + instances int + sharesHost bool + }{ + { + name: "ok: from local sharedhost is on 1 instance", + instances: 1, + sharesHost: true, + }, + { + name: "ok: from local sharedhost is on 2 instances", + instances: 2, + sharesHost: true, + }, + { + name: "ok: from local sharedhost is on 4 instances", + instances: 4, + sharesHost: true, + }, + { + name: "ok: from local sharedhost is off 4 instances", + instances: 4, + sharesHost: false, + }, + } + + for i, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + require = require.New(t) + assert = assert.New(t) + // scaffold an unique plugin for all instances + path = scaffoldPlugin(t, t.TempDir(), + fmt.Sprintf("github.com/foo/bar-%d", i), tt.sharesHost) + plugins []*Plugin + ) + // Load one plugin per instance + for i := 0; i < tt.instances; i++ { + p := Plugin{ + Plugin: pluginsconfig.Plugin{Path: path}, + srcPath: path, + binaryName: filepath.Base(path), + } + p.load(context.Background()) + require.NoError(p.Error) + plugins = append(plugins, &p) + } + // Ensure all plugins are killed at the end of test case + defer func() { + for i := len(plugins) - 1; i >= 0; i-- { + plugins[i].KillClient() + if tt.sharesHost && i > 0 { + assert.False(plugins[i].client.Exited(), "non host plugin can't kill host plugin") + assert.True(checkConfCache(plugins[i].Path), "non host plugin doesn't remove config cache when killed") + } else { + assert.True(plugins[i].client.Exited(), "plugin should be killed") + } + assert.False(plugins[i].isHost, "killed plugins are no longer host") + } + assert.False(checkConfCache(plugins[0].Path), "once host is killed the cache should be cleared") + }() + + var hostConf *hplugin.ReattachConfig + for i := 0; i < len(plugins); i++ { + if tt.sharesHost { + assert.True(checkConfCache(plugins[i].Path), "sharedHost must have a cache entry") + if i == 0 { + // first plugin is the host + assert.True(plugins[i].isHost, "first plugin is the host") + // Assert reattach config has been saved + hostConf = plugins[i].client.ReattachConfig() + ref, err := readConfigCache(plugins[i].Path) + if assert.NoError(err) { + assert.Equal(hostConf, &ref, "wrong cache entry for plugin host") + } + } else { + // plugins after first aren't host + assert.False(plugins[i].isHost, "plugin %d can't be host", i) + assert.Equal(hostConf, plugins[i].client.ReattachConfig(), "ReattachConfig different from host plugin") + } + } else { + assert.False(plugins[i].isHost, "plugin %d can't be host if sharedHost is disabled", i) + assert.False(checkConfCache(plugins[i].Path), "plugin %d can't have a cache entry if sharedHost is disabled", i) + } + } + }) + } +} + +func TestPluginClean(t *testing.T) { + tests := []struct { + name string + plugin *Plugin + expectRemove bool + }{ + { + name: "dont clean local plugin", + plugin: &Plugin{ + Plugin: pluginsconfig.Plugin{Path: "/local"}, + }, + }, + { + name: "dont clean plugin with errors", + plugin: &Plugin{Error: errors.New("oups")}, + }, + { + name: "ok", + plugin: &Plugin{ + cloneURL: "https://github.com/ignite/plugin", + }, + expectRemove: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmp, err := os.MkdirTemp("", "cloneDir") + require.NoError(t, err) + tt.plugin.cloneDir = tmp + + err = tt.plugin.clean() + + require.NoError(t, err) + if tt.expectRemove { + _, err := os.Stat(tmp) + assert.True(t, os.IsNotExist(err), "cloneDir not removed") + } + }) + } +} + + +// scaffoldPlugin runs Scaffold and updates the go.mod so it uses the +// current ignite/cli sources. +func scaffoldPlugin(t *testing.T, dir, name string, sharedHost bool) string { + require := require.New(t) + path, err := Scaffold(dir, name, sharedHost) + require.NoError(err) + // We want the scaffolded plugin to use the current version of ignite/cli, + // for that we need to update the plugin go.mod and add a replace to target + // current ignite/cli + gomod, err := gomodule.ParseAt(path) + require.NoError(err) + // use GOMOD env to get current directory module path + modpath, err := gocmd.Env(gocmd.EnvGOMOD) + require.NoError(err) + modpath = filepath.Dir(modpath) + gomod.AddReplace("github.com/ignite/cli", "", modpath, "") + // Save go.mod + data, err := gomod.Format() + require.NoError(err) + err = os.WriteFile(filepath.Join(path, "go.mod"), data, 0o644) + require.NoError(err) + return path +} + +func assertPlugin(t *testing.T, want, have Plugin) { + t.Helper() + if want.Error != nil { + require.Error(t, have.Error) + assert.Regexp(t, want.Error.Error(), have.Error.Error()) + } else { + require.NoError(t, have.Error) + } + // Errors aren't comparable with assert.Equal, because of the different stacks + want.Error = nil + have.Error = nil + assert.Equal(t, want, have) +} diff --git a/services/plugin/scaffold.go b/services/plugin/scaffold.go new file mode 100644 index 000000000..1ff9c5cfb --- /dev/null +++ b/services/plugin/scaffold.go @@ -0,0 +1,58 @@ +package plugin + +import ( + "context" + "embed" + "os" + "path" + "path/filepath" + + "github.com/gobuffalo/genny/v2" + "github.com/gobuffalo/plush/v4" + "github.com/pkg/errors" + + "github.com/sonr-io/core/pkg/gocmd" + "github.com/sonr-io/core/pkg/xgenny" +) + +//go:embed template/* +var fsPluginSource embed.FS + +// Scaffold generates a plugin structure under dir/path.Base(moduleName). +func Scaffold(dir, moduleName string, sharedHost bool) (string, error) { + var ( + name = filepath.Base(moduleName) + finalDir = path.Join(dir, name) + g = genny.New() + template = xgenny.NewEmbedWalker( + fsPluginSource, + "template", + finalDir, + ) + ) + if _, err := os.Stat(finalDir); err == nil { + // finalDir already exists, don't overwrite stuff + return "", errors.Errorf("dir %q already exists, abort scaffolding", finalDir) + } + if err := g.Box(template); err != nil { + return "", errors.WithStack(err) + } + ctx := plush.NewContext() + ctx.Set("ModuleName", moduleName) + ctx.Set("Name", name) + ctx.Set("SharedHost", sharedHost) + + g.Transformer(xgenny.Transformer(ctx)) + r := genny.WetRunner(ctx) + err := r.With(g) + if err != nil { + return "", errors.WithStack(err) + } + if err := r.Run(); err != nil { + return "", errors.WithStack(err) + } + if err := gocmd.ModTidy(context.TODO(), finalDir); err != nil { + return "", errors.WithStack(err) + } + return finalDir, nil +} diff --git a/services/plugin/scaffold_test.go b/services/plugin/scaffold_test.go new file mode 100755 index 000000000..8d7f122df --- /dev/null +++ b/services/plugin/scaffold_test.go @@ -0,0 +1,16 @@ +package plugin + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestScaffold(t *testing.T) { + tmp := t.TempDir() + + path, err := Scaffold(tmp, "github.com/foo/bar", false) + + require.NoError(t, err) + require.DirExists(t, path) +} diff --git a/services/plugin/template/.gitignore.plush b/services/plugin/template/.gitignore.plush new file mode 100755 index 000000000..3c921bddc --- /dev/null +++ b/services/plugin/template/.gitignore.plush @@ -0,0 +1 @@ +<%= Name %> diff --git a/services/plugin/template/go.mod.plush b/services/plugin/template/go.mod.plush new file mode 100755 index 000000000..fe8fffd05 --- /dev/null +++ b/services/plugin/template/go.mod.plush @@ -0,0 +1,8 @@ +module <%= ModuleName %> + +go 1.21 + +require ( + github.com/hashicorp/go-plugin v1.4.9 + github.com/ignite/cli v0.26.2-0.20230504112712-4324e2ff958f +) \ No newline at end of file diff --git a/services/plugin/template/main.go.plush b/services/plugin/template/main.go.plush new file mode 100755 index 000000000..dbdbcd3e6 --- /dev/null +++ b/services/plugin/template/main.go.plush @@ -0,0 +1,117 @@ +package main + +import ( + "encoding/gob" + "fmt" + "path/filepath" + + hplugin "github.com/hashicorp/go-plugin" + + "github.com/ignite/cli/ignite/services/chain" + "github.com/ignite/cli/ignite/services/plugin" +) + +func init() { + gob.Register(plugin.Manifest{}) + gob.Register(plugin.ExecutedCommand{}) + gob.Register(plugin.ExecutedHook{}) +} + +type p struct{} + +func (p) Manifest() (plugin.Manifest, error) { + return plugin.Manifest{ + Name: "<%= Name %>", + // Add commands here + Commands: []plugin.Command{ + // Example of a command + { + Use: "<%= Name %>", + Short: "Explain what the command is doing...", + Long: "Long description goes here...", + Flags: []plugin.Flag{ + {Name: "my-flag", Type: plugin.FlagTypeString, Usage: "my flag description"}, + }, + PlaceCommandUnder: "ignite", + // Examples of adding subcommands: + /* + Commands: []plugin.Command{ + {Use: "add"}, + {Use: "list"}, + {Use: "delete"}, + }, + */ + }, + }, + // Add hooks here + Hooks: []plugin.Hook{}, + SharedHost: <%= SharedHost %>, + }, nil +} + +func (p) Execute(cmd plugin.ExecutedCommand) error { + // TODO: write command execution here + fmt.Printf("Hello I'm the <%= Name %> plugin\n") + fmt.Printf("My executed command: %q\n", cmd.Path) + fmt.Printf("My args: %v\n", cmd.Args) + myFlag, _ := cmd.Flags().GetString("my-flag") + fmt.Printf("My flags: my-flag=%q\n", myFlag) + fmt.Printf("My config parameters: %v\n", cmd.With) + + // This is how the plugin can access the chain: + // c, err := getChain(cmd) + + // According to the number of declared commands, you may need a switch: + /* + switch cmd.Use { + case "add": + fmt.Println("Adding stuff...") + case "list": + fmt.Println("Listing stuff...") + case "delete": + fmt.Println("Deleting stuff...") + } + */ + return nil +} + +func (p) ExecuteHookPre(hook plugin.ExecutedHook) error { + fmt.Printf("Executing hook pre %q\n", hook.Name) + return nil +} + +func (p) ExecuteHookPost(hook plugin.ExecutedHook) error { + fmt.Printf("Executing hook post %q\n", hook.Name) + return nil +} + +func (p) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { + fmt.Printf("Executing hook cleanup %q\n", hook.Name) + return nil +} + +func getChain(cmd plugin.ExecutedCommand, chainOption ...chain.Option) (*chain.Chain, error) { + var ( + home, _ = cmd.Flags().GetString("home") + path, _ = cmd.Flags().GetString("path") + ) + if home != "" { + chainOption = append(chainOption, chain.HomePath(home)) + } + absPath, err := filepath.Abs(path) + if err != nil { + return nil, err + } + return chain.New(absPath, chainOption...) +} + +func main() { + pluginMap := map[string]hplugin.Plugin{ + "<%= Name %>": &plugin.InterfacePlugin{Impl: &p{}}, + } + + hplugin.Serve(&hplugin.ServeConfig{ + HandshakeConfig: plugin.HandshakeConfig(), + Plugins: pluginMap, + }) +} diff --git a/services/plugin/testdata/fakebin b/services/plugin/testdata/fakebin new file mode 100755 index 000000000..e69de29bb diff --git a/types/webauthn/credential.go b/types/webauthn/credential.go index 1109e8874..85d7b80fb 100644 --- a/types/webauthn/credential.go +++ b/types/webauthn/credential.go @@ -11,7 +11,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/protocol/webauthncose" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" idtypes "github.com/sonr-io/core/x/identity/types" "github.com/yoseplee/vrf" ) diff --git a/types/webauthn/webauthn.go b/types/webauthn/webauthn.go index cbe89160d..68bc13e0a 100644 --- a/types/webauthn/webauthn.go +++ b/types/webauthn/webauthn.go @@ -7,7 +7,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/shengdoushi/base58" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" ) // PublicKeyMultibase returns the public key in multibase format diff --git a/x/domain/types/keys.go b/x/domain/types/keys.go index 2e2f3f22a..ed14b783b 100644 --- a/x/domain/types/keys.go +++ b/x/domain/types/keys.go @@ -1,7 +1,7 @@ package types import ( - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "lukechampine.com/blake3" ) diff --git a/x/identity/keeper/msg_server.go b/x/identity/keeper/msg_server.go index 13b4d7c61..af352a935 100644 --- a/x/identity/keeper/msg_server.go +++ b/x/identity/keeper/msg_server.go @@ -8,7 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "github.com/sonr-io/core/x/identity/types" ) diff --git a/x/identity/types/did_document.go b/x/identity/types/did_document.go index 986e4822b..9fa987fc6 100644 --- a/x/identity/types/did_document.go +++ b/x/identity/types/did_document.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/go-webauthn/webauthn/protocol" - crypto "github.com/sonr-io/core/pkg/crypto" + crypto "github.com/sonr-io/core/internal/crypto" ) // NewDIDDocument creates a new DIDDocument from an Identification and optional VerificationRelationships diff --git a/x/identity/types/msg_identity.go b/x/identity/types/msg_identity.go index 712423d6e..aa7aba8f1 100644 --- a/x/identity/types/msg_identity.go +++ b/x/identity/types/msg_identity.go @@ -3,7 +3,7 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" ) const TypeMsgRegisterIdentity = "register_identity" diff --git a/x/service/types/params.go b/x/service/types/params.go index 144e10d3c..f42799435 100644 --- a/x/service/types/params.go +++ b/x/service/types/params.go @@ -4,7 +4,7 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/protocol/webauthncose" - "github.com/sonr-io/core/pkg/crypto" + "github.com/sonr-io/core/internal/crypto" "gopkg.in/yaml.v2" )