feat: remove plugin

This commit is contained in:
Kevin Franklin Kim 2023-01-12 07:30:12 +01:00
parent 53d127ce6b
commit a8b2ae33ff
19 changed files with 165 additions and 278 deletions

View File

@ -18,8 +18,12 @@ linters-settings:
- ifElseChain
- singleCaseSwitch
- commentFormatting
importas:
no-unaliased: true
alias:
- pkg: github.com/foomo/posh/internal/(\w+)
alias: int$1
linters:
enable:
# Enabled by default linters:
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]

View File

@ -5,7 +5,7 @@ builds:
- binary: posh
main: ./main.go
env:
- CGO_ENABLED=1
- CGO_ENABLED=0
ldflags:
- -s -w
- -X github.com/foomo/posh/internal/version.Version={{.Version}}

View File

@ -2,7 +2,6 @@ package cmd
import (
intconfig "github.com/foomo/posh/internal/config"
intplugin "github.com/foomo/posh/internal/plugin"
"github.com/foomo/posh/pkg/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -31,7 +30,7 @@ var brewCmd = &cobra.Command{
}
cfg.Dry = brewCmdFlagDry
plg, err := intplugin.Load(cmd.Context(), l)
plg, err := pluginProvider(l)
if err != nil {
return err
}
@ -39,8 +38,3 @@ var brewCmd = &cobra.Command{
return plg.Brew(cmd.Context(), cfg)
},
}
func init() {
rootCmd.AddCommand(brewCmd)
brewCmd.Flags().BoolVar(&brewCmdFlagDry, "dry", false, "don't execute scripts")
}

68
cmd/cmd.go Normal file
View File

@ -0,0 +1,68 @@
package cmd
import (
"context"
"os"
"os/signal"
intenv "github.com/foomo/posh/internal/env"
intlog "github.com/foomo/posh/internal/log"
"github.com/foomo/posh/pkg/plugin"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
func Init(provider plugin.Provider) {
pluginProvider = provider
cobra.OnInitialize(func() {
l = intlog.Init(flagLevel, flagNoColor)
l.Must(intenv.Init())
})
rootCmd.PersistentFlags().BoolVar(&flagNoColor, "no-color", false, "disabled colors (default is false)")
rootCmd.PersistentFlags().StringVar(&flagLevel, "level", "info", "set log level (default is warn)")
rootCmd.AddCommand(
configCmd,
versionCmd,
)
if provider != nil {
rootCmd.AddCommand(
brewCmd,
execCmd,
promptCmd,
requireCmd,
)
brewCmd.Flags().BoolVar(&brewCmdFlagDry, "dry", false, "don't execute scripts")
}
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
code := 0
// handle interrupt
osInterrupt := make(chan os.Signal, 1)
signal.Notify(osInterrupt, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())
// handle defer
defer func() {
signal.Stop(osInterrupt)
cancel()
os.Exit(code)
}()
go func() {
<-osInterrupt
l.Debug("received interrupt")
cancel()
}()
if err := rootCmd.ExecuteContext(ctx); errors.Is(err, context.Canceled) {
l.Warn(err.Error())
} else if err != nil {
l.Error(err.Error())
code = 1
}
}

View File

@ -23,7 +23,3 @@ var configCmd = &cobra.Command{
return nil
},
}
func init() {
rootCmd.AddCommand(configCmd)
}

View File

@ -2,7 +2,6 @@ package cmd
import (
intconfig "github.com/foomo/posh/internal/config"
intplugin "github.com/foomo/posh/internal/plugin"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -25,7 +24,7 @@ var execCmd = &cobra.Command{
return errors.New("missing [cmd] argument")
}
plg, err := intplugin.Load(cmd.Context(), l)
plg, err := pluginProvider(l)
if err != nil {
return err
}
@ -33,17 +32,3 @@ var execCmd = &cobra.Command{
return plg.Execute(cmd.Context(), args)
},
}
func init() {
rootCmd.AddCommand(execCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// execCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// execCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@ -2,7 +2,6 @@ package cmd
import (
intconfig "github.com/foomo/posh/internal/config"
intplugin "github.com/foomo/posh/internal/plugin"
"github.com/foomo/posh/pkg/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -26,7 +25,7 @@ var promptCmd = &cobra.Command{
return err
}
plg, err := intplugin.Load(cmd.Context(), l)
plg, err := pluginProvider(l)
if err != nil {
return err
}
@ -34,7 +33,3 @@ var promptCmd = &cobra.Command{
return plg.Prompt(cmd.Context(), cfg)
},
}
func init() {
rootCmd.AddCommand(promptCmd)
}

View File

@ -2,7 +2,6 @@ package cmd
import (
intconfig "github.com/foomo/posh/internal/config"
intplugin "github.com/foomo/posh/internal/plugin"
"github.com/foomo/posh/pkg/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
@ -26,7 +25,7 @@ var requireCmd = &cobra.Command{
return err
}
plg, err := intplugin.Load(cmd.Context(), l)
plg, err := pluginProvider(l)
if err != nil {
return err
}
@ -34,7 +33,3 @@ var requireCmd = &cobra.Command{
return plg.Require(cmd.Context(), cfg)
},
}
func init() {
rootCmd.AddCommand(requireCmd)
}

View File

@ -1,14 +1,8 @@
package cmd
import (
"context"
"os"
"os/signal"
"github.com/foomo/posh/internal/env"
intlog "github.com/foomo/posh/internal/log"
"github.com/foomo/posh/pkg/log"
"github.com/pkg/errors"
"github.com/foomo/posh/pkg/plugin"
"github.com/spf13/cobra"
)
@ -16,6 +10,7 @@ var (
l log.Logger
flagLevel string
flagNoColor bool
pluginProvider plugin.Provider
)
// rootCmd represents the base command when called without any subcommands
@ -23,52 +18,3 @@ var rootCmd = &cobra.Command{
Use: "posh",
Short: "Project Oriented Shell (posh)",
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
code := 0
// handle interrupt
osInterrupt := make(chan os.Signal, 1)
signal.Notify(osInterrupt, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())
// handle defer
defer func() {
signal.Stop(osInterrupt)
cancel()
os.Exit(code)
}()
go func() {
<-osInterrupt
l.Debug("received interrupt")
cancel()
}()
if err := rootCmd.ExecuteContext(ctx); errors.Is(err, context.Canceled) {
l.Warn(err.Error())
} else if err != nil {
l.Error(err.Error())
code = 1
}
}
func init() {
cobra.OnInitialize(initialize)
rootCmd.PersistentFlags().BoolVar(&flagNoColor, "no-color", false, "disabled colors (default is false)")
rootCmd.PersistentFlags().StringVar(&flagLevel, "level", "info", "set log level (default is warn)")
}
// initialize reads in config file and ENV variables if set.
func initialize() {
var err error
// init logger
l, err = intlog.Init(flagLevel, flagNoColor)
cobra.CheckErr(err)
// init env
l.Must(env.Init())
}

View File

@ -20,13 +20,9 @@ var versionCmd = &cobra.Command{
buildTime = time.Unix(value, 0).String()
}
if l.IsLevel(log.LevelDebug) {
l.Printf("v%s, Commit: %s, BuildTime: %s", intversion.Version, intversion.CommitHash, buildTime)
l.Printf("Version: %s\nCommit: %s\nBuildTime: %s", intversion.Version, intversion.CommitHash, buildTime)
} else {
l.Printf("v%s", intversion.Version)
l.Printf("%s", intversion.Version)
}
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/foomo/posh-sandbox/posh/pkg"
"github.com/foomo/posh/cmd"
_ "github.com/foomo/posh/pkg/plugin"
)
func init() {
cmd.Init(pkg.New)
}
func main() {
cmd.Execute()
}

View File

@ -1,4 +1,4 @@
package main
package pkg
import (
"context"
@ -29,7 +29,7 @@ type Plugin struct {
// ~ Constructor
// ------------------------------------------------------------------------------------------------
func New(l log.Logger) (plugin.Plugin, error) { //nolint:unparam
func New(l log.Logger) (plugin.Plugin, error) {
inst := &Plugin{
l: l,
commands: command.Commands{},

View File

@ -1,24 +1,65 @@
-include .makerc
.DEFAULT_GOAL:=help
## === Tasks ===
# --- .makerc -----------------------------------------------------------------
#
# level=debug
# --- Config -----------------------------------------------------------------
level?=info
# --- Helpers -----------------------------------------------------------------
.PHONY: bin/posh
# Builds posh and takes the git hash to detect changes
bin/posh: current=$(shell bin/posh version)
bin/posh: version=$(shell git ls-files -s .posh/pkg | git hash-object --stdin)
bin/posh: commitHash=$(shell git rev-parse HEAD)
bin/posh: buildTimestamp=$(shell date +%s)
bin/posh: ldflags=\
-X github.com/foomo/posh/internal/version.Version=${version} \
-X github.com/foomo/posh/internal/version.CommitHash=${commitHash} \
-X github.com/foomo/posh/internal/version.BuildTimestamp=${buildTimestamp}
bin/posh:
@if [ "${current}" != "${version}" ]; then \
cd .posh && go build -trimpath -ldflags="${ldflags}" -o ../bin/posh main.go; \
fi
# --- Targets -----------------------------------------------------------------
.PHONY: clean
## Remove built targets
clean:
@rm bin/*
.PHONY: config
## Print posh config
config: bin/posh
@bin/posh config --level ${level}
.PHONY: brew
## Install packages
brew:
@posh brew
## Install project specific packages
brew: bin/posh
@bin/posh brew --level ${level}
.PHONY: require
## Validate dependencies
require:
@posh require
require: bin/posh
@bin/posh require --level ${level}
.PHONY: shell
## Start the interactive shell
shell: require brew
@posh prompt
## Start the interactive
shell: bin/posh require brew
@bin/posh prompt --level ${level}
.PHONY: shell.rebuild
## Rebuild and start the interactive
shell.rebuild: clean shell
## === Utils ===
.PHONY: help
## Show help text
help:
@awk '{ \

View File

@ -4,13 +4,9 @@ import (
"github.com/foomo/posh/pkg/log"
)
func Init(level string, noColor bool) (log.Logger, error) {
if value, err := log.NewPTerm(
func Init(level string, noColor bool) log.Logger {
return log.NewPTerm(
log.PTermWithDisableColor(noColor),
log.PTermWithLevel(log.GetLevel(level)),
); err != nil {
return nil, err
} else {
return value, nil
}
)
}

View File

@ -1,23 +0,0 @@
package plugin
import (
"context"
"github.com/foomo/posh/pkg/config"
"github.com/foomo/posh/pkg/log"
"github.com/foomo/posh/pkg/plugin"
"github.com/spf13/viper"
)
func Load(ctx context.Context, l log.Logger) (plugin.Plugin, error) {
m, err := manager(l)
if err != nil {
return nil, err
}
var cfg config.Plugin
if err := viper.UnmarshalKey("plugin", &cfg); err != nil {
return nil, err
}
return m.BuildAndLoadPlugin(ctx, cfg.Source, cfg.Provider)
}

View File

@ -1,19 +0,0 @@
package plugin
import (
"github.com/foomo/posh/pkg/log"
"github.com/foomo/posh/pkg/plugin"
)
var m *plugin.Manager
func manager(l log.Logger) (*plugin.Manager, error) {
if m == nil {
if value, err := plugin.NewManager(l); err != nil {
return nil, err
} else {
m = value
}
}
return m, nil
}

View File

@ -4,6 +4,10 @@ import (
"github.com/foomo/posh/cmd"
)
func init() {
cmd.Init(nil)
}
func main() {
cmd.Execute()
}

View File

@ -11,7 +11,7 @@ type (
name string
level Level
}
PTermOption func(*PTerm) error
PTermOption func(*PTerm)
)
// ------------------------------------------------------------------------------------------------
@ -19,16 +19,15 @@ type (
// ------------------------------------------------------------------------------------------------
func PTermWithDisableColor(v bool) PTermOption {
return func(o *PTerm) error {
return func(o *PTerm) {
if v {
pterm.DisableColor()
}
return nil
}
}
func PTermWithLevel(v Level) PTermOption {
return func(o *PTerm) error {
return func(o *PTerm) {
o.level = v
switch {
case v <= LevelTrace:
@ -40,7 +39,6 @@ func PTermWithLevel(v Level) PTermOption {
default:
pterm.Debug.LineNumberOffset = 1
}
return nil
}
}
@ -48,18 +46,16 @@ func PTermWithLevel(v Level) PTermOption {
// ~ Constructor
// ------------------------------------------------------------------------------------------------
func NewPTerm(opts ...PTermOption) (*PTerm, error) {
func NewPTerm(opts ...PTermOption) *PTerm {
inst := &PTerm{
level: LevelError,
}
for _, opt := range opts {
if opt != nil {
if err := opt(inst); err != nil {
return nil, err
opt(inst)
}
}
}
return inst, nil
return inst
}
// ------------------------------------------------------------------------------------------------

View File

@ -1,102 +0,0 @@
package plugin
import (
"context"
"fmt"
"os/exec"
"path"
"path/filepath"
"plugin"
"strings"
"github.com/foomo/posh/pkg/log"
"github.com/pkg/errors"
)
type Manager struct {
l log.Logger
plugins map[string]*plugin.Plugin
}
// ------------------------------------------------------------------------------------------------
// ~ Constructor
// ------------------------------------------------------------------------------------------------
func NewManager(l log.Logger) (*Manager, error) {
inst := &Manager{
l: l.Named("plugin"),
plugins: map[string]*plugin.Plugin{},
}
return inst, nil
}
// ------------------------------------------------------------------------------------------------
// ~ Public methods
// ------------------------------------------------------------------------------------------------
func (m *Manager) BuildAndLoadPlugin(ctx context.Context, filename, provider string) (Plugin, error) {
if err := m.Tidy(ctx, filename); err != nil {
return nil, err
}
if err := m.Build(ctx, filename); err != nil {
return nil, err
}
return m.LoadPlugin(filename, provider)
}
func (m *Manager) Tidy(ctx context.Context, filename string) error {
m.l.Debug("tidying:", filename)
cmd := exec.CommandContext(ctx, "go", "mod", "tidy")
cmd.Dir = filepath.Dir(filename)
if output, err := cmd.CombinedOutput(); err != nil {
return errors.Wrap(err, string(output))
}
return nil
}
func (m *Manager) Build(ctx context.Context, filename string) error {
m.l.Debug("building:", filename)
base := path.Base(filename)
cmd := exec.CommandContext(ctx, "go", "build",
"-buildmode=plugin",
"-a",
"-o", strings.ReplaceAll(base, ".go", ".so"),
base,
)
cmd.Dir = filepath.Dir(filename)
if output, err := cmd.CombinedOutput(); err != nil {
return errors.Wrap(err, string(output))
}
return nil
}
func (m *Manager) LoadPlugin(filename, provider string) (Plugin, error) {
m.l.Debug("loading plugin:", filename, provider)
filename = strings.ReplaceAll(filename, ".go", ".so")
if plg, err := m.Load(filename); err != nil {
return nil, err
} else if sym, err := plg.Lookup(provider); err != nil {
return nil, errors.Wrapf(err, "failed to lookup provider (%s)", provider)
} else if fn, ok := sym.(func(l log.Logger) (Plugin, error)); !ok {
return nil, fmt.Errorf("invalid provider type (%T) ", sym)
} else if inst, err := fn(m.l.Named("")); err != nil {
return nil, errors.Wrap(err, "failed to create plugin instance")
} else if inst == nil {
return nil, errors.New("plugin can not be nil")
} else {
return inst, nil
}
}
func (m *Manager) Load(filename string) (*plugin.Plugin, error) {
if value, ok := m.plugins[filename]; ok {
return value, nil
}
m.l.Debug("loading:", filename)
if plg, err := plugin.Open(filename); err != nil {
return nil, errors.Wrapf(err, "failed to load plugin (%s)", filename)
} else {
m.plugins[filename] = plg
return plg, nil
}
}