diff --git a/cmd/plugins/plugins.yaml b/cmd/plugins/plugins.yaml index a8b88e3..4b379e8 100644 --- a/cmd/plugins/plugins.yaml +++ b/cmd/plugins/plugins.yaml @@ -25,7 +25,8 @@ plugins: #args: ["../examples/plugins/typescript/dist/index.js"] command: "npm" args: ["run", "dev"] - work_dir: "../examples/plugins/typescript" + work_dir: + path: "../examples/plugins/typescript" env: NODE_ENV: production - id: example-php @@ -41,3 +42,15 @@ plugins: # - "--abort-on-container-exit" env: PHP_ENV: production + + - id: example-go + name: Example Go Plugin + command: "go" + args: ["run", "cmd/main.go"] + work_dir: + git: + enabled: true + persistent: false # persistent can be set to true if you don't + # want the plugin to be cloned at every startup + version: tags/v0.0.1 # can also specify commit hashes + path: https://github.com/secmc/plugin-go diff --git a/plugin/adapters/plugin/process.go b/plugin/adapters/plugin/process.go index 5611c98..783a2e2 100644 --- a/plugin/adapters/plugin/process.go +++ b/plugin/adapters/plugin/process.go @@ -110,8 +110,8 @@ func (p *pluginProcess) launchProcess(ctx context.Context, serverAddress string) } cmd := exec.CommandContext(ctx, p.cfg.Command, p.cfg.Args...) - if p.cfg.WorkDir != "" { - cmd.Dir = p.cfg.WorkDir + if p.cfg.WorkDir.Path != "" { + cmd.Dir = p.cfg.WorkDir.Path } env := os.Environ() env = append(env, fmt.Sprintf("DF_PLUGIN_ID=%s", p.id)) diff --git a/plugin/config/config.go b/plugin/config/config.go index 0cc61d5..d811be2 100644 --- a/plugin/config/config.go +++ b/plugin/config/config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "os/exec" "path/filepath" "gopkg.in/yaml.v2" @@ -20,11 +21,18 @@ type Config struct { } type PluginConfig struct { - ID string `yaml:"id"` - Name string `yaml:"name"` - Command string `yaml:"command"` - Args []string `yaml:"args"` - WorkDir string `yaml:"work_dir"` + ID string `yaml:"id"` + Name string `yaml:"name"` + Command string `yaml:"command"` + Args []string `yaml:"args"` + WorkDir struct { + Git struct { + Enabled bool `yaml:"enabled"` + Persistent bool `yaml:"persistent"` + Version string `yaml:"version"` + } `yaml:"git"` + Path string `yaml:"path"` + } `yaml:"work_dir"` Env map[string]string `yaml:"env"` Address string `yaml:"address"` } @@ -53,14 +61,61 @@ func LoadConfig(path string) (Config, error) { cfg.HelloTimeoutMs = 2000 } for i := range cfg.Plugins { - if cfg.Plugins[i].ID == "" { - cfg.Plugins[i].ID = fmt.Sprintf("plugin-%d", i+1) + pl := &cfg.Plugins[i] + if pl.ID == "" { + pl.ID = fmt.Sprintf("plugin-%d", i+1) } - if cfg.Plugins[i].Command != "" && cfg.Plugins[i].WorkDir != "" { - if !filepath.IsAbs(cfg.Plugins[i].WorkDir) { - cfg.Plugins[i].WorkDir = filepath.Clean(cfg.Plugins[i].WorkDir) + if pl.Command == "" || pl.WorkDir.Path == "" { + continue + } + + if pl.WorkDir.Git.Enabled { + path := filepath.Join(os.TempDir(), pl.ID) + remote := pl.WorkDir.Path + + needClone := true + if pl.WorkDir.Git.Persistent { + if _, err := os.Stat(path); err == nil { + needClone = false + } else if !errors.Is(err, os.ErrNotExist) { + return cfg, fmt.Errorf("stat remote plugin %q: %w", pl.ID, err) + } + } else { + if err := os.RemoveAll(path); err != nil { + return cfg, fmt.Errorf("reset remote plugin %q: %w", pl.ID, err) + } } + + if needClone { + if err := run("git", "", "clone", remote, path, "--depth=1"); err != nil { + return cfg, fmt.Errorf("clone remote plugin %q: %w", pl.ID, err) + } + + if pl.WorkDir.Git.Version != "" { + if err := run("git", path, "checkout", "--detach", pl.WorkDir.Git.Version); err != nil { + return cfg, err + } + } + } + + pl.WorkDir.Path = path + } + + if !filepath.IsAbs(pl.WorkDir.Path) { + pl.WorkDir.Path = filepath.Clean(pl.WorkDir.Path) } } return cfg, nil } + +func run(bin string, path string, args ...string) error { + cmd := exec.Command(bin, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if path != "" { + cmd.Dir = path + } + + return cmd.Run() +}