@@ -2,15 +2,19 @@ package main
22
33import (
44 "fmt"
5+ "io/fs"
56 "log"
67 "log/slog"
78 "os"
9+ "path"
10+ "plugin"
811 "strings"
912 "time"
1013
1114 "github.com/lmittmann/tint"
1215 "github.com/mattn/go-isatty"
1316 "github.com/spf13/cobra"
17+ "gitub.com/strangelove-ventures/spawn/plugins"
1418)
1519
1620// Set in the makefile ld_flags on compile
@@ -19,11 +23,14 @@ var SpawnVersion = ""
1923var LogLevelFlag = "log-level"
2024
2125func main () {
26+
2227 rootCmd .AddCommand (newChain )
2328 rootCmd .AddCommand (LocalICCmd )
2429 rootCmd .AddCommand (versionCmd )
2530 rootCmd .AddCommand (ModuleCmd ())
2631
32+ applyPluginCmds ()
33+
2734 rootCmd .PersistentFlags ().String (LogLevelFlag , "info" , "log level (debug, info, warn, error)" )
2835
2936 if err := rootCmd .Execute (); err != nil {
@@ -50,6 +57,83 @@ func GetLogger() *slog.Logger {
5057 return slog .Default ()
5158}
5259
60+ func applyPluginCmds () {
61+ plugins := & cobra.Command {
62+ Use : "plugins" ,
63+ Short : "Manage plugins" ,
64+ Run : func (cmd * cobra.Command , args []string ) {
65+ if err := cmd .Help (); err != nil {
66+ log .Fatal (err )
67+ }
68+ },
69+ }
70+
71+ for _ , plugin := range loadPlugins () {
72+ plugins .AddCommand (plugin .Cmd ())
73+ }
74+
75+ rootCmd .AddCommand (plugins )
76+ }
77+
78+ func loadPlugins () map [string ]* plugins.SpawnPluginBase {
79+ p := make (map [string ]* plugins.SpawnPluginBase )
80+
81+ homeDir , err := os .UserHomeDir ()
82+ if err != nil {
83+ panic (err )
84+ }
85+
86+ pluginsDir := path .Join (homeDir , ".spawn" , "plugins" )
87+
88+ d := os .DirFS (pluginsDir )
89+ if _ , err := d .Open ("." ); err != nil {
90+ if os .IsNotExist (err ) {
91+ if err := os .MkdirAll (pluginsDir , 0755 ); err != nil {
92+ panic (err )
93+ }
94+ } else {
95+ panic (err )
96+ }
97+ }
98+
99+ err = fs .WalkDir (d , "." , func (relPath string , d fs.DirEntry , e error ) error {
100+ if d .IsDir () {
101+ return nil
102+ }
103+
104+ if ! strings .Contains (relPath , ".so" ) {
105+ return nil
106+ }
107+
108+ absPath := path .Join (pluginsDir , relPath )
109+
110+ // read the absolute path
111+ plug , err := plugin .Open (absPath )
112+ if err != nil {
113+ return fmt .Errorf ("error opening plugin %s: %w" , absPath , err )
114+ }
115+
116+ base , err := plug .Lookup ("Plugin" )
117+ if err != nil {
118+ return fmt .Errorf ("error looking up symbol: %w" , err )
119+ }
120+
121+ pluginInstance , ok := base .(plugins.SpawnPlugin )
122+ if ! ok {
123+ log .Fatal ("Symbol 'Plugin' does not implement the SpawnPlugin interface" )
124+ }
125+
126+ p [relPath ] = plugins .NewSpawnPluginBase (pluginInstance .Cmd ())
127+
128+ return nil
129+ })
130+ if err != nil {
131+ panic (err )
132+ }
133+
134+ return p
135+ }
136+
53137var rootCmd = & cobra.Command {
54138 Use : "spawn" ,
55139 Short : "Entry into the Interchain" ,
0 commit comments