From 089f65bfbfd74e98408328cef7aa638fde62e7b5 Mon Sep 17 00:00:00 2001 From: tyru Date: Sat, 27 Oct 2018 23:36:38 +0900 Subject: [PATCH 01/11] refactor: pass lock.json & config value to subcommands --- _docs/layer.md | 22 ++++++++++++++++++++++ subcmd/build.go | 4 ++-- subcmd/cmd.go | 37 ++++++++++++++++++++++++++++++------- subcmd/disable.go | 4 ++-- subcmd/enable.go | 4 ++-- subcmd/get.go | 14 ++++---------- subcmd/help.go | 7 ++++--- subcmd/list.go | 4 ++-- subcmd/migrate.go | 4 ++-- subcmd/profile.go | 4 ++-- subcmd/rm.go | 4 ++-- subcmd/self_upgrade.go | 4 ++-- subcmd/version.go | 4 ++-- 13 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 _docs/layer.md diff --git a/_docs/layer.md b/_docs/layer.md new file mode 100644 index 00000000..8c363236 --- /dev/null +++ b/_docs/layer.md @@ -0,0 +1,22 @@ + +## Layered architecture + +The volt commands like `volt get` which may modify lock.json, config.toml([#221](https://github.com/vim-volt/volt/issues/221)), +filesystem, are executed in several steps: + +1. (Subcmd layer): pass subcommand arguments, lock.json & config.toml structure + to Gateway layer +2. (Gateway layer): Create an AST according to given information + * This layer cannot touch filesystem, because it makes unit testing difficult +3. (Usecase layer): Execute the AST. This note mainly describes this layer's design + +Below is the dependency graph: + +``` +Subcmd --> Gateway --> Usecase +``` + +* Subcmd only depends Gateway +* Gateway doesn't know Subcmd +* Gateway only depends Usecase +* Usecase doesn't know Gateway diff --git a/subcmd/build.go b/subcmd/build.go index 0b871d60..139113c0 100644 --- a/subcmd/build.go +++ b/subcmd/build.go @@ -53,10 +53,10 @@ Description return fs } -func (cmd *buildCmd) Run(args []string) *Error { +func (cmd *buildCmd) Run(cmdctx *CmdContext) *Error { // Parse args fs := cmd.FlagSet() - fs.Parse(args) + fs.Parse(cmdctx.Args) if cmd.helped { return nil } diff --git a/subcmd/cmd.go b/subcmd/cmd.go index 67a88f74..0b16ed23 100644 --- a/subcmd/cmd.go +++ b/subcmd/cmd.go @@ -2,12 +2,14 @@ package subcmd import ( "flag" - "github.com/pkg/errors" "os" "os/user" "runtime" + "github.com/pkg/errors" + "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" ) @@ -17,13 +19,20 @@ var cmdMap = make(map[string]Cmd) // All subcommands must implement this. type Cmd interface { ProhibitRootExecution(args []string) bool - Run(args []string) *Error + Run(cmdctx *CmdContext) *Error FlagSet() *flag.FlagSet } +// CmdContext is a data transfer object between Subcmd and Gateway layer. +type CmdContext struct { + Args []string + LockJSON *lockjson.LockJSON + Config *config.Config +} + // RunnerFunc invokes c with args. // On unit testing, a mock function was given. -type RunnerFunc func(c Cmd, args []string) *Error +type RunnerFunc func(c Cmd, cmdctx *CmdContext) *Error // Error is a command error. // It also has a exit code. @@ -37,8 +46,8 @@ func (e *Error) Error() string { } // DefaultRunner simply runs command with args -func DefaultRunner(c Cmd, args []string) *Error { - return c.Run(args) +func DefaultRunner(c Cmd, cmdctx *CmdContext) *Error { + return c.Run(cmdctx) } // Run is invoked by main(), each argument means 'volt {subcmd} {args}'. @@ -61,7 +70,7 @@ func Run(args []string, cont RunnerFunc) *Error { c, exists := cmdMap[subCmd] if !exists { - return &Error{Code: 3, Msg: "unknown command '" + subCmd + "'"} + return &Error{Code: 3, Msg: "Unknown command '" + subCmd + "'"} } // Disallow executing the commands which may modify files in root priviledge @@ -72,7 +81,21 @@ func Run(args []string, cont RunnerFunc) *Error { } } - return cont(c, args) + lockJSON, err := lockjson.Read() + if err != nil { + return &Error{Code: 20, Msg: errors.Wrap(err, "failed to read lock.json").Error()} + } + + cfg, err := config.Read() + if err != nil { + return &Error{Code: 30, Msg: errors.Wrap(err, "failed to read config.toml").Error()} + } + + return cont(c, &CmdContext{ + Args: args, + LockJSON: lockJSON, + Config: cfg, + }) } func expandAlias(subCmd string, args []string) (string, []string, error) { diff --git a/subcmd/disable.go b/subcmd/disable.go index 8ed15114..79f2d2d1 100644 --- a/subcmd/disable.go +++ b/subcmd/disable.go @@ -41,8 +41,8 @@ Description return fs } -func (cmd *disableCmd) Run(args []string) *Error { - reposPathList, err := cmd.parseArgs(args) +func (cmd *disableCmd) Run(cmdctx *CmdContext) *Error { + reposPathList, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/enable.go b/subcmd/enable.go index bea7586a..3d7bceac 100644 --- a/subcmd/enable.go +++ b/subcmd/enable.go @@ -41,8 +41,8 @@ Description return fs } -func (cmd *enableCmd) Run(args []string) *Error { - reposPathList, err := cmd.parseArgs(args) +func (cmd *enableCmd) Run(cmdctx *CmdContext) *Error { + reposPathList, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/get.go b/subcmd/get.go index 48ca6c80..bd7561d0 100644 --- a/subcmd/get.go +++ b/subcmd/get.go @@ -116,9 +116,9 @@ Options`) return fs } -func (cmd *getCmd) Run(args []string) *Error { +func (cmd *getCmd) Run(cmdctx *CmdContext) *Error { // Parse args - args, err := cmd.parseArgs(args) + args, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } @@ -126,13 +126,7 @@ func (cmd *getCmd) Run(args []string) *Error { return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()} } - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return &Error{Code: 11, Msg: "Could not read lock.json: " + err.Error()} - } - - reposPathList, err := cmd.getReposPathList(args, lockJSON) + reposPathList, err := cmd.getReposPathList(args, cmdctx.LockJSON) if err != nil { return &Error{Code: 12, Msg: "Could not get repos list: " + err.Error()} } @@ -140,7 +134,7 @@ func (cmd *getCmd) Run(args []string) *Error { return &Error{Code: 13, Msg: "No repositories are specified"} } - err = cmd.doGet(reposPathList, lockJSON) + err = cmd.doGet(reposPathList, cmdctx.LockJSON) if err != nil { return &Error{Code: 20, Msg: err.Error()} } diff --git a/subcmd/help.go b/subcmd/help.go index 6c94bb45..6d7dfa45 100644 --- a/subcmd/help.go +++ b/subcmd/help.go @@ -99,7 +99,8 @@ Command return fs } -func (cmd *helpCmd) Run(args []string) *Error { +func (cmd *helpCmd) Run(cmdctx *CmdContext) *Error { + args := cmdctx.Args if len(args) == 0 { cmd.FlagSet().Usage() return nil @@ -112,7 +113,7 @@ func (cmd *helpCmd) Run(args []string) *Error { if !exists { return &Error{Code: 1, Msg: fmt.Sprintf("Unknown command '%s'", args[0])} } - args = append([]string{"-help"}, args[1:]...) - fs.Run(args) + cmdctx.Args = append([]string{"-help"}, args[1:]...) + fs.Run(cmdctx) return nil } diff --git a/subcmd/list.go b/subcmd/list.go index af734e5e..bd997fc5 100644 --- a/subcmd/list.go +++ b/subcmd/list.go @@ -125,9 +125,9 @@ repos path: ` } -func (cmd *listCmd) Run(args []string) *Error { +func (cmd *listCmd) Run(cmdctx *CmdContext) *Error { fs := cmd.FlagSet() - fs.Parse(args) + fs.Parse(cmdctx.Args) if cmd.helped { return nil } diff --git a/subcmd/migrate.go b/subcmd/migrate.go index 8d22b00d..707f46d5 100644 --- a/subcmd/migrate.go +++ b/subcmd/migrate.go @@ -55,8 +55,8 @@ Available operations`) return fs } -func (cmd *migrateCmd) Run(args []string) *Error { - op, err := cmd.parseArgs(args) +func (cmd *migrateCmd) Run(cmdctx *CmdContext) *Error { + op, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/profile.go b/subcmd/profile.go index 6912cb21..38be0b28 100644 --- a/subcmd/profile.go +++ b/subcmd/profile.go @@ -100,9 +100,9 @@ Quick example return fs } -func (cmd *profileCmd) Run(args []string) *Error { +func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { // Parse args - args, err := cmd.parseArgs(args) + args, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/rm.go b/subcmd/rm.go index b2e4d309..e09d635a 100644 --- a/subcmd/rm.go +++ b/subcmd/rm.go @@ -63,8 +63,8 @@ Description return fs } -func (cmd *rmCmd) Run(args []string) *Error { - reposPathList, err := cmd.parseArgs(args) +func (cmd *rmCmd) Run(cmdctx *CmdContext) *Error { + reposPathList, err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/self_upgrade.go b/subcmd/self_upgrade.go index 00d86445..fad61bbf 100644 --- a/subcmd/self_upgrade.go +++ b/subcmd/self_upgrade.go @@ -50,8 +50,8 @@ Description return fs } -func (cmd *selfUpgradeCmd) Run(args []string) *Error { - err := cmd.parseArgs(args) +func (cmd *selfUpgradeCmd) Run(cmdctx *CmdContext) *Error { + err := cmd.parseArgs(cmdctx.Args) if err == ErrShowedHelp { return nil } diff --git a/subcmd/version.go b/subcmd/version.go index 2d8cde46..49962e48 100644 --- a/subcmd/version.go +++ b/subcmd/version.go @@ -40,9 +40,9 @@ Description return fs } -func (cmd *versionCmd) Run(args []string) *Error { +func (cmd *versionCmd) Run(cmdctx *CmdContext) *Error { fs := cmd.FlagSet() - fs.Parse(args) + fs.Parse(cmdctx.Args) if cmd.helped { return nil } From cba0aa08bfe642d599790db0d36e4fc174da03f1 Mon Sep 17 00:00:00 2001 From: tyru Date: Sat, 27 Oct 2018 23:51:23 +0900 Subject: [PATCH 02/11] doc: rename ambigous 'Subcmd layer' to 'UI layer' --- _docs/layer.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/_docs/layer.md b/_docs/layer.md index 8c363236..6f3dc5f7 100644 --- a/_docs/layer.md +++ b/_docs/layer.md @@ -1,10 +1,10 @@ ## Layered architecture -The volt commands like `volt get` which may modify lock.json, config.toml([#221](https://github.com/vim-volt/volt/issues/221)), +The volt commands like `volt get` which may modify lock.json, config.toml, filesystem, are executed in several steps: -1. (Subcmd layer): pass subcommand arguments, lock.json & config.toml structure +1. (UI layer): pass subcommand arguments, lock.json & config.toml structure to Gateway layer 2. (Gateway layer): Create an AST according to given information * This layer cannot touch filesystem, because it makes unit testing difficult @@ -13,10 +13,10 @@ filesystem, are executed in several steps: Below is the dependency graph: ``` -Subcmd --> Gateway --> Usecase +UI --> Gateway --> Usecase ``` -* Subcmd only depends Gateway -* Gateway doesn't know Subcmd +* UI only depends Gateway +* Gateway doesn't know UI * Gateway only depends Usecase * Usecase doesn't know Gateway From b9e97c12c064a55a649062797e2883645fc5e38c Mon Sep 17 00:00:00 2001 From: tyru Date: Sat, 27 Oct 2018 23:52:11 +0900 Subject: [PATCH 03/11] refactor: move UI layer code to main.go --- main.go | 94 +++++++++++++++++++++++++++++++++++++++++++++-- subcmd/cmd.go | 100 +++----------------------------------------------- 2 files changed, 96 insertions(+), 98 deletions(-) diff --git a/main.go b/main.go index 70334e02..666564e5 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,103 @@ package main import ( "os" + "os/user" + "runtime" + "github.com/pkg/errors" + "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/subcmd" ) func main() { - err := subcmd.Run(os.Args, subcmd.DefaultRunner) + code, msg := run(os.Args) + if code != 0 { + logger.Error(msg) + os.Exit(code) + } +} + +func run(args []string) (int, string) { + if os.Getenv("VOLT_DEBUG") != "" { + logger.SetLevel(logger.DebugLevel) + } + + if len(args) <= 1 { + args = append(args, "help") + } + subCmd := args[1] + args = args[2:] + + // Expand subcommand alias + subCmd, args, err := expandAlias(subCmd, args) + if err != nil { + return 1, err.Error() + } + + c := subcmd.LookUpCmd(subCmd) + if c == nil { + return 3, "Unknown command '" + subCmd + "'" + } + + // Disallow executing the commands which may modify files in root priviledge + if c.ProhibitRootExecution(args) { + err := detectPriviledgedUser() + if err != nil { + return 4, err.Error() + } + } + + lockJSON, err := lockjson.Read() + if err != nil { + return 20, errors.Wrap(err, "failed to read lock.json").Error() + } + + cfg, err := config.Read() + if err != nil { + return 30, errors.Wrap(err, "failed to read config.toml").Error() + } + + result := c.Run(&subcmd.CmdContext{ + Args: args, + LockJSON: lockJSON, + Config: cfg, + }) + if result != nil { + return result.Code, result.Msg + } + return 0, "" +} + +func expandAlias(subCmd string, args []string) (string, []string, error) { + cfg, err := config.Read() if err != nil { - logger.Error(err.Msg) - os.Exit(err.Code) + return "", nil, errors.Wrap(err, "could not read config.toml") + } + if newArgs, exists := cfg.Alias[subCmd]; exists && len(newArgs) > 0 { + subCmd = newArgs[0] + args = append(newArgs[1:], args...) + } + return subCmd, args, nil +} + +// On Windows, this function always returns nil. +// Because if even administrator user creates a file, the file can be +// overwritten by normal user. +// On Linux, if current user's uid == 0, returns non-nil error. +func detectPriviledgedUser() error { + if runtime.GOOS == "windows" { + return nil + } + u, err := user.Current() + if err != nil { + return errors.Wrap(err, "cannot get current user") + } + if u.Uid == "0" { + return errors.New( + "cannot run this sub command with root priviledge. " + + "Please run as normal user") } + return nil } diff --git a/subcmd/cmd.go b/subcmd/cmd.go index 0b16ed23..62b48ecd 100644 --- a/subcmd/cmd.go +++ b/subcmd/cmd.go @@ -2,19 +2,18 @@ package subcmd import ( "flag" - "os" - "os/user" - "runtime" - - "github.com/pkg/errors" "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/lockjson" - "github.com/vim-volt/volt/logger" ) var cmdMap = make(map[string]Cmd) +// LookUpCmd looks up subcommand by name. +func LookUpCmd(cmd string) Cmd { + return cmdMap[cmd] +} + // Cmd represents volt's subcommand interface. // All subcommands must implement this. type Cmd interface { @@ -30,10 +29,6 @@ type CmdContext struct { Config *config.Config } -// RunnerFunc invokes c with args. -// On unit testing, a mock function was given. -type RunnerFunc func(c Cmd, cmdctx *CmdContext) *Error - // Error is a command error. // It also has a exit code. type Error struct { @@ -44,88 +39,3 @@ type Error struct { func (e *Error) Error() string { return e.Msg } - -// DefaultRunner simply runs command with args -func DefaultRunner(c Cmd, cmdctx *CmdContext) *Error { - return c.Run(cmdctx) -} - -// Run is invoked by main(), each argument means 'volt {subcmd} {args}'. -func Run(args []string, cont RunnerFunc) *Error { - if os.Getenv("VOLT_DEBUG") != "" { - logger.SetLevel(logger.DebugLevel) - } - - if len(args) <= 1 { - args = append(args, "help") - } - subCmd := args[1] - args = args[2:] - - // Expand subcommand alias - subCmd, args, err := expandAlias(subCmd, args) - if err != nil { - return &Error{Code: 1, Msg: err.Error()} - } - - c, exists := cmdMap[subCmd] - if !exists { - return &Error{Code: 3, Msg: "Unknown command '" + subCmd + "'"} - } - - // Disallow executing the commands which may modify files in root priviledge - if c.ProhibitRootExecution(args) { - err := detectPriviledgedUser() - if err != nil { - return &Error{Code: 4, Msg: err.Error()} - } - } - - lockJSON, err := lockjson.Read() - if err != nil { - return &Error{Code: 20, Msg: errors.Wrap(err, "failed to read lock.json").Error()} - } - - cfg, err := config.Read() - if err != nil { - return &Error{Code: 30, Msg: errors.Wrap(err, "failed to read config.toml").Error()} - } - - return cont(c, &CmdContext{ - Args: args, - LockJSON: lockJSON, - Config: cfg, - }) -} - -func expandAlias(subCmd string, args []string) (string, []string, error) { - cfg, err := config.Read() - if err != nil { - return "", nil, errors.Wrap(err, "could not read config.toml") - } - if newArgs, exists := cfg.Alias[subCmd]; exists && len(newArgs) > 0 { - subCmd = newArgs[0] - args = append(newArgs[1:], args...) - } - return subCmd, args, nil -} - -// On Windows, this function always returns nil. -// Because if even administrator user creates a file, the file can be -// overwritten by normal user. -// On Linux, if current user's uid == 0, returns non-nil error. -func detectPriviledgedUser() error { - if runtime.GOOS == "windows" { - return nil - } - u, err := user.Current() - if err != nil { - return errors.Wrap(err, "cannot get current user") - } - if u.Uid == "0" { - return errors.New( - "cannot run this sub command with root priviledge. " + - "Please run as normal user") - } - return nil -} From 6b5c5816024993dfd5185a323d9bf3d444ee97ef Mon Sep 17 00:00:00 2001 From: tyru Date: Sat, 27 Oct 2018 23:58:59 +0900 Subject: [PATCH 04/11] refactor: rename package 'subcmd' to 'gateway' --- Makefile | 2 +- {subcmd => gateway}/build.go | 4 ++-- {subcmd => gateway}/build_test.go | 4 ++-- {subcmd => gateway}/builder/base.go | 2 +- {subcmd => gateway}/builder/builder.go | 2 +- {subcmd => gateway}/builder/copy.go | 2 +- {subcmd => gateway}/builder/symlink.go | 2 +- {subcmd => gateway}/buildinfo/buildinfo.go | 0 {subcmd => gateway}/cmd.go | 2 +- {subcmd => gateway}/disable.go | 2 +- {subcmd => gateway}/enable.go | 2 +- {subcmd => gateway}/get.go | 4 ++-- {subcmd => gateway}/get_test.go | 2 +- {subcmd => gateway}/help.go | 2 +- {subcmd => gateway}/help_test.go | 2 +- {subcmd => gateway}/list.go | 2 +- {subcmd => gateway}/list_test.go | 2 +- {subcmd => gateway}/migrate.go | 4 ++-- {subcmd => gateway}/migrate/lockjson.go | 0 {subcmd => gateway}/migrate/migrater.go | 0 .../migrate/plugconf-config-func.go | 2 +- {subcmd => gateway}/profile.go | 18 +++++++++--------- {subcmd => gateway}/profile_test.go | 2 +- {subcmd => gateway}/rm.go | 4 ++-- {subcmd => gateway}/rm_test.go | 2 +- {subcmd => gateway}/self_upgrade.go | 2 +- {subcmd => gateway}/self_upgrade_test.go | 2 +- {subcmd => gateway}/version.go | 2 +- {subcmd => gateway}/version_test.go | 2 +- main.go | 6 +++--- 30 files changed, 42 insertions(+), 42 deletions(-) rename {subcmd => gateway}/build.go (97%) rename {subcmd => gateway}/build_test.go (99%) rename {subcmd => gateway}/builder/base.go (98%) rename {subcmd => gateway}/builder/builder.go (98%) rename {subcmd => gateway}/builder/copy.go (99%) rename {subcmd => gateway}/builder/symlink.go (99%) rename {subcmd => gateway}/buildinfo/buildinfo.go (100%) rename {subcmd => gateway}/cmd.go (97%) rename {subcmd => gateway}/disable.go (99%) rename {subcmd => gateway}/enable.go (99%) rename {subcmd => gateway}/get.go (99%) rename {subcmd => gateway}/get_test.go (99%) rename {subcmd => gateway}/help.go (99%) rename {subcmd => gateway}/help_test.go (99%) rename {subcmd => gateway}/list.go (99%) rename {subcmd => gateway}/list_test.go (99%) rename {subcmd => gateway}/migrate.go (97%) rename {subcmd => gateway}/migrate/lockjson.go (100%) rename {subcmd => gateway}/migrate/migrater.go (100%) rename {subcmd => gateway}/migrate/plugconf-config-func.go (98%) rename {subcmd => gateway}/profile.go (97%) rename {subcmd => gateway}/profile_test.go (99%) rename {subcmd => gateway}/rm.go (98%) rename {subcmd => gateway}/rm_test.go (99%) rename {subcmd => gateway}/self_upgrade.go (99%) rename {subcmd => gateway}/self_upgrade_test.go (99%) rename {subcmd => gateway}/version.go (99%) rename {subcmd => gateway}/version_test.go (98%) diff --git a/Makefile b/Makefile index 58889afe..9b4ee2b8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME := volt SRC := $(shell find . -type d -name 'vendor' -prune -o -type f -name '*.go' -print) -VERSION := $(shell sed -n -E 's/var voltVersion = "([^"]+)"/\1/p' subcmd/version.go) +VERSION := $(shell sed -n -E 's/var voltVersion = "([^"]+)"/\1/p' gateway/version.go) RELEASE_LDFLAGS := -s -w -extldflags '-static' RELEASE_OS := linux windows darwin RELEASE_ARCH := amd64 386 diff --git a/subcmd/build.go b/gateway/build.go similarity index 97% rename from subcmd/build.go rename to gateway/build.go index 139113c0..095ae892 100644 --- a/subcmd/build.go +++ b/gateway/build.go @@ -1,12 +1,12 @@ -package subcmd +package gateway import ( "flag" "fmt" "os" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/logger" - "github.com/vim-volt/volt/subcmd/builder" "github.com/vim-volt/volt/transaction" ) diff --git a/subcmd/build_test.go b/gateway/build_test.go similarity index 99% rename from subcmd/build_test.go rename to gateway/build_test.go index 77b95172..1dcf2a9a 100644 --- a/subcmd/build_test.go +++ b/gateway/build_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "bytes" @@ -13,10 +13,10 @@ import ( "github.com/haya14busa/go-vimlparser" "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/fileutil" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/internal/testutil" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/pathutil" - "github.com/vim-volt/volt/subcmd/builder" ) // Checks: diff --git a/subcmd/builder/base.go b/gateway/builder/base.go similarity index 98% rename from subcmd/builder/base.go rename to gateway/builder/base.go index 6d4b63fa..612be43e 100644 --- a/subcmd/builder/base.go +++ b/gateway/builder/base.go @@ -12,10 +12,10 @@ import ( "github.com/hashicorp/go-multierror" "github.com/vim-volt/volt/fileutil" + "github.com/vim-volt/volt/gateway/buildinfo" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" - "github.com/vim-volt/volt/subcmd/buildinfo" ) // BaseBuilder is a base struct which all builders must implement diff --git a/subcmd/builder/builder.go b/gateway/builder/builder.go similarity index 98% rename from subcmd/builder/builder.go rename to gateway/builder/builder.go index adc557a6..34752a1b 100644 --- a/subcmd/builder/builder.go +++ b/gateway/builder/builder.go @@ -5,9 +5,9 @@ import ( "os" "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/gateway/buildinfo" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" - "github.com/vim-volt/volt/subcmd/buildinfo" ) // Builder creates/updates ~/.vim/pack/volt directory diff --git a/subcmd/builder/copy.go b/gateway/builder/copy.go similarity index 99% rename from subcmd/builder/copy.go rename to gateway/builder/copy.go index 5d079a2d..217071b9 100644 --- a/subcmd/builder/copy.go +++ b/gateway/builder/copy.go @@ -10,12 +10,12 @@ import ( "github.com/hashicorp/go-multierror" "github.com/vim-volt/volt/fileutil" + "github.com/vim-volt/volt/gateway/buildinfo" "github.com/vim-volt/volt/gitutil" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/plugconf" - "github.com/vim-volt/volt/subcmd/buildinfo" "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/object" diff --git a/subcmd/builder/symlink.go b/gateway/builder/symlink.go similarity index 99% rename from subcmd/builder/symlink.go rename to gateway/builder/symlink.go index 45c079d4..4326d697 100644 --- a/subcmd/builder/symlink.go +++ b/gateway/builder/symlink.go @@ -11,12 +11,12 @@ import ( "gopkg.in/src-d/go-git.v4" + "github.com/vim-volt/volt/gateway/buildinfo" "github.com/vim-volt/volt/gitutil" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/plugconf" - "github.com/vim-volt/volt/subcmd/buildinfo" ) type symlinkBuilder struct { diff --git a/subcmd/buildinfo/buildinfo.go b/gateway/buildinfo/buildinfo.go similarity index 100% rename from subcmd/buildinfo/buildinfo.go rename to gateway/buildinfo/buildinfo.go diff --git a/subcmd/cmd.go b/gateway/cmd.go similarity index 97% rename from subcmd/cmd.go rename to gateway/cmd.go index 62b48ecd..7eb4fb5c 100644 --- a/subcmd/cmd.go +++ b/gateway/cmd.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" diff --git a/subcmd/disable.go b/gateway/disable.go similarity index 99% rename from subcmd/disable.go rename to gateway/disable.go index 79f2d2d1..3723f8fd 100644 --- a/subcmd/disable.go +++ b/gateway/disable.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" diff --git a/subcmd/enable.go b/gateway/enable.go similarity index 99% rename from subcmd/enable.go rename to gateway/enable.go index 3d7bceac..d88226d0 100644 --- a/subcmd/enable.go +++ b/gateway/enable.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" diff --git a/subcmd/get.go b/gateway/get.go similarity index 99% rename from subcmd/get.go rename to gateway/get.go index bd7561d0..95fd2598 100644 --- a/subcmd/get.go +++ b/gateway/get.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" @@ -17,12 +17,12 @@ import ( "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/fileutil" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/gitutil" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/plugconf" - "github.com/vim-volt/volt/subcmd/builder" "github.com/vim-volt/volt/transaction" multierror "github.com/hashicorp/go-multierror" diff --git a/subcmd/get_test.go b/gateway/get_test.go similarity index 99% rename from subcmd/get_test.go rename to gateway/get_test.go index 051f9842..ce391b7d 100644 --- a/subcmd/get_test.go +++ b/gateway/get_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "bytes" diff --git a/subcmd/help.go b/gateway/help.go similarity index 99% rename from subcmd/help.go rename to gateway/help.go index 6d7dfa45..5d421f29 100644 --- a/subcmd/help.go +++ b/gateway/help.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" diff --git a/subcmd/help_test.go b/gateway/help_test.go similarity index 99% rename from subcmd/help_test.go rename to gateway/help_test.go index 86a5b9af..07c1c667 100644 --- a/subcmd/help_test.go +++ b/gateway/help_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "strings" diff --git a/subcmd/list.go b/gateway/list.go similarity index 99% rename from subcmd/list.go rename to gateway/list.go index bd997fc5..6e8f75d4 100644 --- a/subcmd/list.go +++ b/gateway/list.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "encoding/json" diff --git a/subcmd/list_test.go b/gateway/list_test.go similarity index 99% rename from subcmd/list_test.go rename to gateway/list_test.go index 0f2d0f03..7cf9e253 100644 --- a/subcmd/list_test.go +++ b/gateway/list_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "strconv" diff --git a/subcmd/migrate.go b/gateway/migrate.go similarity index 97% rename from subcmd/migrate.go rename to gateway/migrate.go index 707f46d5..97ea562c 100644 --- a/subcmd/migrate.go +++ b/gateway/migrate.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" @@ -6,8 +6,8 @@ import ( "github.com/pkg/errors" "os" + "github.com/vim-volt/volt/gateway/migrate" "github.com/vim-volt/volt/logger" - "github.com/vim-volt/volt/subcmd/migrate" ) func init() { diff --git a/subcmd/migrate/lockjson.go b/gateway/migrate/lockjson.go similarity index 100% rename from subcmd/migrate/lockjson.go rename to gateway/migrate/lockjson.go diff --git a/subcmd/migrate/migrater.go b/gateway/migrate/migrater.go similarity index 100% rename from subcmd/migrate/migrater.go rename to gateway/migrate/migrater.go diff --git a/subcmd/migrate/plugconf-config-func.go b/gateway/migrate/plugconf-config-func.go similarity index 98% rename from subcmd/migrate/plugconf-config-func.go rename to gateway/migrate/plugconf-config-func.go index 73d44ab8..1f3255d5 100644 --- a/subcmd/migrate/plugconf-config-func.go +++ b/gateway/migrate/plugconf-config-func.go @@ -7,11 +7,11 @@ import ( "path/filepath" "strings" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/plugconf" - "github.com/vim-volt/volt/subcmd/builder" "github.com/vim-volt/volt/transaction" ) diff --git a/subcmd/profile.go b/gateway/profile.go similarity index 97% rename from subcmd/profile.go rename to gateway/profile.go index 38be0b28..2ed72d3e 100644 --- a/subcmd/profile.go +++ b/gateway/profile.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" @@ -8,10 +8,10 @@ import ( "github.com/pkg/errors" "github.com/hashicorp/go-multierror" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" - "github.com/vim-volt/volt/subcmd/builder" "github.com/vim-volt/volt/transaction" ) @@ -29,8 +29,8 @@ func (cmd *profileCmd) ProhibitRootExecution(args []string) bool { if len(args) == 0 { return true } - subCmd := args[0] - switch subCmd { + gateway := args[0] + switch gateway { case "show": return false case "list": @@ -110,8 +110,8 @@ func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { return &Error{Code: 10, Msg: err.Error()} } - subCmd := args[0] - switch subCmd { + gateway := args[0] + switch gateway { case "set": err = cmd.doSet(args[1:]) case "show": @@ -129,7 +129,7 @@ func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { case "rm": err = cmd.doRm(args[1:]) default: - return &Error{Code: 11, Msg: "Unknown subcommand: " + subCmd} + return &Error{Code: 11, Msg: "Unknown subcommand: " + gateway} } if err != nil { @@ -519,10 +519,10 @@ func (cmd *profileCmd) doRm(args []string) error { return nil } -func (cmd *profileCmd) parseAddArgs(lockJSON *lockjson.LockJSON, subCmd string, args []string) (string, []pathutil.ReposPath, error) { +func (cmd *profileCmd) parseAddArgs(lockJSON *lockjson.LockJSON, gateway string, args []string) (string, []pathutil.ReposPath, error) { if len(args) == 0 { cmd.FlagSet().Usage() - logger.Errorf("'volt profile %s' receives profile name and one or more repositories.", subCmd) + logger.Errorf("'volt profile %s' receives profile name and one or more repositories.", gateway) return "", nil, nil } diff --git a/subcmd/profile_test.go b/gateway/profile_test.go similarity index 99% rename from subcmd/profile_test.go rename to gateway/profile_test.go index feea4621..44612aad 100644 --- a/subcmd/profile_test.go +++ b/gateway/profile_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "encoding/json" diff --git a/subcmd/rm.go b/gateway/rm.go similarity index 98% rename from subcmd/rm.go rename to gateway/rm.go index e09d635a..c571cd4c 100644 --- a/subcmd/rm.go +++ b/gateway/rm.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" @@ -10,11 +10,11 @@ import ( "github.com/pkg/errors" "github.com/vim-volt/volt/fileutil" + "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/plugconf" - "github.com/vim-volt/volt/subcmd/builder" "github.com/vim-volt/volt/transaction" ) diff --git a/subcmd/rm_test.go b/gateway/rm_test.go similarity index 99% rename from subcmd/rm_test.go rename to gateway/rm_test.go index 46616974..5092f183 100644 --- a/subcmd/rm_test.go +++ b/gateway/rm_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "fmt" diff --git a/subcmd/self_upgrade.go b/gateway/self_upgrade.go similarity index 99% rename from subcmd/self_upgrade.go rename to gateway/self_upgrade.go index fad61bbf..c7a2b9c8 100644 --- a/subcmd/self_upgrade.go +++ b/gateway/self_upgrade.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "encoding/json" diff --git a/subcmd/self_upgrade_test.go b/gateway/self_upgrade_test.go similarity index 99% rename from subcmd/self_upgrade_test.go rename to gateway/self_upgrade_test.go index 858dd344..b2617131 100644 --- a/subcmd/self_upgrade_test.go +++ b/gateway/self_upgrade_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "io/ioutil" diff --git a/subcmd/version.go b/gateway/version.go similarity index 99% rename from subcmd/version.go rename to gateway/version.go index 49962e48..225ff6c3 100644 --- a/subcmd/version.go +++ b/gateway/version.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import ( "flag" diff --git a/subcmd/version_test.go b/gateway/version_test.go similarity index 98% rename from subcmd/version_test.go rename to gateway/version_test.go index 13c51de9..0520c31c 100644 --- a/subcmd/version_test.go +++ b/gateway/version_test.go @@ -1,4 +1,4 @@ -package subcmd +package gateway import "testing" diff --git a/main.go b/main.go index 666564e5..200d0909 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,9 @@ import ( "github.com/pkg/errors" "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/gateway" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" - "github.com/vim-volt/volt/subcmd" ) func main() { @@ -39,7 +39,7 @@ func run(args []string) (int, string) { return 1, err.Error() } - c := subcmd.LookUpCmd(subCmd) + c := gateway.LookUpCmd(subCmd) if c == nil { return 3, "Unknown command '" + subCmd + "'" } @@ -62,7 +62,7 @@ func run(args []string) (int, string) { return 30, errors.Wrap(err, "failed to read config.toml").Error() } - result := c.Run(&subcmd.CmdContext{ + result := c.Run(&gateway.CmdContext{ Args: args, LockJSON: lockJSON, Config: cfg, From 348f6ea5553d7fa008bbb9b9240e69634e2616fe Mon Sep 17 00:00:00 2001 From: tyru Date: Sun, 28 Oct 2018 00:33:43 +0900 Subject: [PATCH 05/11] fix: fix compile error of test code --- gateway/self_upgrade_test.go | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/gateway/self_upgrade_test.go b/gateway/self_upgrade_test.go index b2617131..a4496f4e 100644 --- a/gateway/self_upgrade_test.go +++ b/gateway/self_upgrade_test.go @@ -5,6 +5,10 @@ import ( "os" "strings" "testing" + + "github.com/pkg/errors" + "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/lockjson" ) func TestVoltSelfUpgrade(t *testing.T) { @@ -30,8 +34,7 @@ func testVoltSelfUpgradeCheckFromOldVer(t *testing.T) { var err *Error out := captureOutput(t, func() { - args := []string{"volt", "self-upgrade", "-check"} - err = Run(args, DefaultRunner) + err = runVolt(t, "self-upgrade", "-check") }) if err != nil { @@ -46,8 +49,7 @@ func testVoltSelfUpgradeCheckFromOldVer(t *testing.T) { func testVoltSelfUpgradeCheckFromCurrentVer(t *testing.T) { var err *Error out := captureOutput(t, func() { - args := []string{"volt", "self-upgrade", "-check"} - err = Run(args, DefaultRunner) + err = runVolt(t, "self-upgrade", "-check") }) if err != nil { @@ -83,3 +85,23 @@ func captureOutput(t *testing.T, f func()) string { os.Stderr = oldStderr return <-outCh } + +func runVolt(t *testing.T, cmd string, args ...string) *Error { + c := LookUpCmd(cmd) + if c == nil { + t.Fatal("unknown command '" + cmd + "'") + } + lockJSON, err := lockjson.Read() + if err != nil { + t.Fatal(errors.Wrap(err, "failed to read lock.json").Error()) + } + cfg, err := config.Read() + if err != nil { + t.Fatal(errors.Wrap(err, "failed to read config.toml").Error()) + } + return c.Run(&CmdContext{ + Args: args, + LockJSON: lockJSON, + Config: cfg, + }) +} From d966a63ac3f54fcad8705fe63d462378299691fe Mon Sep 17 00:00:00 2001 From: Takuya Fujiwara Date: Sun, 28 Oct 2018 09:45:00 +0900 Subject: [PATCH 06/11] doc: update layer.md --- _docs/layer.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/_docs/layer.md b/_docs/layer.md index 6f3dc5f7..12052ac5 100644 --- a/_docs/layer.md +++ b/_docs/layer.md @@ -4,11 +4,9 @@ The volt commands like `volt get` which may modify lock.json, config.toml, filesystem, are executed in several steps: -1. (UI layer): pass subcommand arguments, lock.json & config.toml structure - to Gateway layer -2. (Gateway layer): Create an AST according to given information - * This layer cannot touch filesystem, because it makes unit testing difficult -3. (Usecase layer): Execute the AST. This note mainly describes this layer's design +1. (UI layer): Passes subcommand arguments, lock.json & config.toml structure to Gateway layer +2. (Gateway layer): Invokes usecase(s). This layer cannot touch filesystem, do network requests, because it makes unit testing difficult +3. (Usecase layer): Modify files, do network requests, and other business logic Below is the dependency graph: From 34e53ead8a0e365cce2d50f7daf413782031dd15 Mon Sep 17 00:00:00 2001 From: tyru Date: Sun, 28 Oct 2018 22:56:23 +0900 Subject: [PATCH 07/11] refactor: list,version: move core logic to usecase.* --- Makefile | 2 +- gateway/disable.go | 5 +- gateway/enable.go | 5 +- gateway/list.go | 70 +++------------------ gateway/profile.go | 65 +++++++++++-------- gateway/self_upgrade.go | 7 ++- gateway/version.go | 72 ++------------------- usecase/list.go | 62 ++++++++++++++++++ {gateway => usecase}/list_test.go | 12 ++-- {gateway => usecase}/self_upgrade_test.go | 6 +- usecase/version.go | 77 +++++++++++++++++++++++ {gateway => usecase}/version_test.go | 6 +- 12 files changed, 217 insertions(+), 172 deletions(-) create mode 100644 usecase/list.go rename {gateway => usecase}/list_test.go (93%) rename {gateway => usecase}/self_upgrade_test.go (95%) create mode 100644 usecase/version.go rename {gateway => usecase}/version_test.go (88%) diff --git a/Makefile b/Makefile index 9b4ee2b8..9b15929e 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ NAME := volt SRC := $(shell find . -type d -name 'vendor' -prune -o -type f -name '*.go' -print) -VERSION := $(shell sed -n -E 's/var voltVersion = "([^"]+)"/\1/p' gateway/version.go) +VERSION := $(shell sed -n -E 's/var VoltVersion = "([^"]+)"/\1/p' usecase/version.go) RELEASE_LDFLAGS := -s -w -extldflags '-static' RELEASE_OS := linux windows darwin RELEASE_ARCH := amd64 386 diff --git a/gateway/disable.go b/gateway/disable.go index 3723f8fd..6c92ebb8 100644 --- a/gateway/disable.go +++ b/gateway/disable.go @@ -51,10 +51,11 @@ func (cmd *disableCmd) Run(cmdctx *CmdContext) *Error { } profCmd := profileCmd{} - err = profCmd.doRm(append( + cmdctx.Args = append( []string{"-current"}, reposPathList.Strings()..., - )) + ) + err = profCmd.doRm(cmdctx) if err != nil { return &Error{Code: 11, Msg: err.Error()} } diff --git a/gateway/enable.go b/gateway/enable.go index d88226d0..94c5c589 100644 --- a/gateway/enable.go +++ b/gateway/enable.go @@ -51,10 +51,11 @@ func (cmd *enableCmd) Run(cmdctx *CmdContext) *Error { } profCmd := profileCmd{} - err = profCmd.doAdd(append( + cmdctx.Args = append( []string{"-current"}, reposPathList.Strings()..., - )) + ) + err = profCmd.doAdd(cmdctx) if err != nil { return &Error{Code: 11, Msg: err.Error()} } diff --git a/gateway/list.go b/gateway/list.go index 6e8f75d4..39ac7dc6 100644 --- a/gateway/list.go +++ b/gateway/list.go @@ -1,23 +1,27 @@ package gateway import ( - "encoding/json" "flag" "fmt" - "github.com/pkg/errors" + "io" "os" - "text/template" + "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/lockjson" + "github.com/vim-volt/volt/usecase" ) func init() { - cmdMap["list"] = &listCmd{} + cmdMap["list"] = &listCmd{ + List: usecase.List, + } } type listCmd struct { helped bool format string + + List func(w io.Writer, format string, lockJSON *lockjson.LockJSON, cfg *config.Config) error } func (cmd *listCmd) ProhibitRootExecution(args []string) bool { return false } @@ -131,64 +135,8 @@ func (cmd *listCmd) Run(cmdctx *CmdContext) *Error { if cmd.helped { return nil } - if err := cmd.list(cmd.format); err != nil { + if err := cmd.List(os.Stdout, cmd.format, cmdctx.LockJSON, cmdctx.Config); err != nil { return &Error{Code: 10, Msg: "Failed to render template: " + err.Error()} } return nil } - -func (cmd *listCmd) list(format string) error { - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - // Parse template string - t, err := template.New("volt").Funcs(cmd.funcMap(lockJSON)).Parse(format) - if err != nil { - return err - } - // Output templated information - return t.Execute(os.Stdout, lockJSON) -} - -func (*listCmd) funcMap(lockJSON *lockjson.LockJSON) template.FuncMap { - profileOf := func(name string) *lockjson.Profile { - profile, err := lockJSON.Profiles.FindByName(name) - if err != nil { - return &lockjson.Profile{} - } - return profile - } - - return template.FuncMap{ - "json": func(value interface{}, args ...string) string { - var b []byte - switch len(args) { - case 0: - b, _ = json.MarshalIndent(value, "", "") - case 1: - b, _ = json.MarshalIndent(value, args[0], "") - default: - b, _ = json.MarshalIndent(value, args[0], args[1]) - } - return string(b) - }, - "currentProfile": func() *lockjson.Profile { - return profileOf(lockJSON.CurrentProfileName) - }, - "profile": profileOf, - "version": func() string { - return voltVersion - }, - "versionMajor": func() int { - return voltVersionInfo()[0] - }, - "versionMinor": func() int { - return voltVersionInfo()[1] - }, - "versionPatch": func() int { - return voltVersionInfo()[2] - }, - } -} diff --git a/gateway/profile.go b/gateway/profile.go index 2ed72d3e..d749dc2b 100644 --- a/gateway/profile.go +++ b/gateway/profile.go @@ -13,6 +13,7 @@ import ( "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" "github.com/vim-volt/volt/transaction" + "github.com/vim-volt/volt/usecase" ) type profileCmd struct { @@ -110,26 +111,28 @@ func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { return &Error{Code: 10, Msg: err.Error()} } - gateway := args[0] - switch gateway { + subCmd := args[0] + cmdctx.Args = args[1:] + + switch subCmd { case "set": - err = cmd.doSet(args[1:]) + err = cmd.doSet(cmdctx) case "show": - err = cmd.doShow(args[1:]) + err = cmd.doShow(cmdctx) case "list": - err = cmd.doList(args[1:]) + err = cmd.doList(cmdctx) case "new": - err = cmd.doNew(args[1:]) + err = cmd.doNew(cmdctx) case "destroy": - err = cmd.doDestroy(args[1:]) + err = cmd.doDestroy(cmdctx) case "rename": - err = cmd.doRename(args[1:]) + err = cmd.doRename(cmdctx) case "add": - err = cmd.doAdd(args[1:]) + err = cmd.doAdd(cmdctx) case "rm": - err = cmd.doRm(args[1:]) + err = cmd.doRm(cmdctx) default: - return &Error{Code: 11, Msg: "Unknown subcommand: " + gateway} + return &Error{Code: 11, Msg: "Unknown subcommand: " + subCmd} } if err != nil { @@ -161,7 +164,8 @@ func (*profileCmd) getCurrentProfile() (string, error) { return lockJSON.CurrentProfileName, nil } -func (cmd *profileCmd) doSet(args []string) error { +func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { + args := cmdctx.Args // Parse args createProfile := false if len(args) > 0 && args[0] == "-n" { @@ -191,7 +195,8 @@ func (cmd *profileCmd) doSet(args []string) error { if !createProfile { return err } - if err = cmd.doNew([]string{profileName}); err != nil { + cmdctx.Args = []string{profileName} + if err = cmd.doNew(cmdctx); err != nil { return err } // Read lock.json again @@ -231,7 +236,8 @@ func (cmd *profileCmd) doSet(args []string) error { return nil } -func (cmd *profileCmd) doShow(args []string) error { +func (cmd *profileCmd) doShow(cmdctx *CmdContext) error { + args := cmdctx.Args if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile show' receives profile name.") @@ -254,25 +260,28 @@ func (cmd *profileCmd) doShow(args []string) error { } } - return (&listCmd{}).list(fmt.Sprintf(`name: %s + format := fmt.Sprintf(`name: %s repos path: {{- with profile %q -}} {{- range .ReposPath }} {{ . }} {{- end -}} {{- end }} -`, profileName, profileName)) +`, profileName, profileName) + return usecase.List(os.Stdout, format, cmdctx.LockJSON, cmdctx.Config) } -func (cmd *profileCmd) doList(args []string) error { - return (&listCmd{}).list(` +func (cmd *profileCmd) doList(cmdctx *CmdContext) error { + format := ` {{- range .Profiles -}} {{- if eq .Name $.CurrentProfileName -}}*{{- else }} {{ end }} {{ .Name }} {{ end -}} -`) +` + return usecase.List(os.Stdout, format, cmdctx.LockJSON, cmdctx.Config) } -func (cmd *profileCmd) doNew(args []string) error { +func (cmd *profileCmd) doNew(cmdctx *CmdContext) error { + args := cmdctx.Args if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile new' receives profile name.") @@ -316,7 +325,8 @@ func (cmd *profileCmd) doNew(args []string) error { return nil } -func (cmd *profileCmd) doDestroy(args []string) error { +func (cmd *profileCmd) doDestroy(cmdctx *CmdContext) error { + args := cmdctx.Args if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile destroy' receives profile name.") @@ -374,7 +384,8 @@ func (cmd *profileCmd) doDestroy(args []string) error { return merr.ErrorOrNil() } -func (cmd *profileCmd) doRename(args []string) error { +func (cmd *profileCmd) doRename(cmdctx *CmdContext) error { + args := cmdctx.Args if len(args) != 2 { cmd.FlagSet().Usage() logger.Error("'volt profile rename' receives profile name.") @@ -433,7 +444,8 @@ func (cmd *profileCmd) doRename(args []string) error { return nil } -func (cmd *profileCmd) doAdd(args []string) error { +func (cmd *profileCmd) doAdd(cmdctx *CmdContext) error { + args := cmdctx.Args // Read lock.json lockJSON, err := lockjson.Read() if err != nil { @@ -475,7 +487,8 @@ func (cmd *profileCmd) doAdd(args []string) error { return nil } -func (cmd *profileCmd) doRm(args []string) error { +func (cmd *profileCmd) doRm(cmdctx *CmdContext) error { + args := cmdctx.Args // Read lock.json lockJSON, err := lockjson.Read() if err != nil { @@ -519,10 +532,10 @@ func (cmd *profileCmd) doRm(args []string) error { return nil } -func (cmd *profileCmd) parseAddArgs(lockJSON *lockjson.LockJSON, gateway string, args []string) (string, []pathutil.ReposPath, error) { +func (cmd *profileCmd) parseAddArgs(lockJSON *lockjson.LockJSON, subCmd string, args []string) (string, []pathutil.ReposPath, error) { if len(args) == 0 { cmd.FlagSet().Usage() - logger.Errorf("'volt profile %s' receives profile name and one or more repositories.", gateway) + logger.Errorf("'volt profile %s' receives profile name and one or more repositories.", subCmd) return "", nil, nil } diff --git a/gateway/self_upgrade.go b/gateway/self_upgrade.go index c7a2b9c8..fa082cf3 100644 --- a/gateway/self_upgrade.go +++ b/gateway/self_upgrade.go @@ -18,6 +18,7 @@ import ( "github.com/vim-volt/volt/httputil" "github.com/vim-volt/volt/logger" + "github.com/vim-volt/volt/usecase" ) func init() { @@ -139,15 +140,15 @@ func (cmd *selfUpgradeCmd) doSelfUpgrade(latestURL string) error { return err } logger.Debugf("tag_name = %q", release.TagName) - tagNameVer, err := parseVersion(release.TagName) + tagNameVer, err := usecase.ParseVersion(release.TagName) if err != nil { return err } - if compareVersion(tagNameVer, voltVersionInfo()) <= 0 { + if usecase.CompareVersion(tagNameVer, usecase.Version()) <= 0 { logger.Info("No updates were found.") return nil } - logger.Infof("Found update: %s -> %s", voltVersion, release.TagName) + logger.Infof("Found update: %s -> %s", usecase.VersionString(), release.TagName) // Show release note fmt.Println("---") diff --git a/gateway/version.go b/gateway/version.go index 225ff6c3..149b3925 100644 --- a/gateway/version.go +++ b/gateway/version.go @@ -3,21 +3,19 @@ package gateway import ( "flag" "fmt" - "github.com/pkg/errors" "os" - "regexp" - "strconv" -) -// This variable is not constant for testing (to change it temporarily) -var voltVersion = "v0.3.6-alpha" + "github.com/vim-volt/volt/usecase" +) func init() { - cmdMap["version"] = &versionCmd{} + cmdMap["version"] = &versionCmd{Version: usecase.VersionString()} } type versionCmd struct { helped bool + + Version string } func (cmd *versionCmd) ProhibitRootExecution(args []string) bool { return false } @@ -47,64 +45,6 @@ func (cmd *versionCmd) Run(cmdctx *CmdContext) *Error { return nil } - fmt.Printf("volt version: %s\n", voltVersion) + fmt.Printf("volt version: %s\n", cmd.Version) return nil } - -// [major, minor, patch, alphaBeta] -type versionInfo []int - -const ( - suffixAlpha = 1 - suffixBeta = 2 - suffixStable = 9 -) - -var rxVersion = regexp.MustCompile(`^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(-alpha|-beta)?`) - -func voltVersionInfo() versionInfo { - // parseVersion(voltVersionInfo) must not return non-nil error! - voltVersionInfo, err := parseVersion(voltVersion) - if err != nil { - panic(err) - } - return voltVersionInfo -} - -func compareVersion(v1, v2 versionInfo) int { - for i := 0; i < 4; i++ { - if v1[i] > v2[i] { - return 1 - } else if v1[i] < v2[i] { - return -1 - } - } - return 0 -} - -func parseVersion(ver string) (versionInfo, error) { - m := rxVersion.FindStringSubmatch(ver) - if len(m) == 0 { - return nil, errors.New("version number format is invalid: " + ver) - } - info := make(versionInfo, 0, 4) - for i := 1; i <= 3 && m[i] != ""; i++ { - n, err := strconv.Atoi(m[i]) - if err != nil { - return nil, err - } - info = append(info, n) - } - if len(info) == 2 { - info = append(info, 0) - } - switch m[4] { - case "": - info = append(info, suffixStable) - case "-alpha": - info = append(info, suffixAlpha) - case "-beta": - info = append(info, suffixBeta) - } - return info, nil -} diff --git a/usecase/list.go b/usecase/list.go new file mode 100644 index 00000000..a84a7e94 --- /dev/null +++ b/usecase/list.go @@ -0,0 +1,62 @@ +package usecase + +import ( + "encoding/json" + "html/template" + "io" + + "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/lockjson" +) + +// List renders text/template format format to w with paramter lockJSON, cfg. +func List(w io.Writer, format string, lockJSON *lockjson.LockJSON, cfg *config.Config) error { + // Parse template string + t, err := template.New("volt").Funcs(funcMap(lockJSON)).Parse(format) + if err != nil { + return err + } + // Output templated information + return t.Execute(w, lockJSON) +} + +func funcMap(lockJSON *lockjson.LockJSON) template.FuncMap { + profileOf := func(name string) *lockjson.Profile { + profile, err := lockJSON.Profiles.FindByName(name) + if err != nil { + return &lockjson.Profile{} + } + return profile + } + + return template.FuncMap{ + "json": func(value interface{}, args ...string) string { + var b []byte + switch len(args) { + case 0: + b, _ = json.MarshalIndent(value, "", "") + case 1: + b, _ = json.MarshalIndent(value, args[0], "") + default: + b, _ = json.MarshalIndent(value, args[0], args[1]) + } + return string(b) + }, + "currentProfile": func() *lockjson.Profile { + return profileOf(lockJSON.CurrentProfileName) + }, + "profile": profileOf, + "version": func() string { + return VersionString() + }, + "versionMajor": func() int { + return Version()[0] + }, + "versionMinor": func() int { + return Version()[1] + }, + "versionPatch": func() int { + return Version()[2] + }, + } +} diff --git a/gateway/list_test.go b/usecase/list_test.go similarity index 93% rename from gateway/list_test.go rename to usecase/list_test.go index 7cf9e253..9695b86d 100644 --- a/gateway/list_test.go +++ b/usecase/list_test.go @@ -1,4 +1,4 @@ -package gateway +package usecase import ( "strconv" @@ -106,8 +106,8 @@ func TestVoltListFunctions(t *testing.T) { testutil.SuccessExit(t, out, err) // (b) - if string(out) != voltVersion { - t.Errorf("expected %q but got %q", voltVersion, string(out)) + if string(out) != VoltVersion { + t.Errorf("expected %q but got %q", VoltVersion, string(out)) } }) @@ -123,7 +123,7 @@ func TestVoltListFunctions(t *testing.T) { testutil.SuccessExit(t, out, err) // (c) - expected := strconv.Itoa(voltVersionInfo()[0]) + expected := strconv.Itoa(Version()[0]) if string(out) != expected { t.Errorf("expected %q but got %q", expected, string(out)) } @@ -141,7 +141,7 @@ func TestVoltListFunctions(t *testing.T) { testutil.SuccessExit(t, out, err) // (d) - expected := strconv.Itoa(voltVersionInfo()[1]) + expected := strconv.Itoa(Version()[1]) if string(out) != expected { t.Errorf("expected %q but got %q", expected, string(out)) } @@ -159,7 +159,7 @@ func TestVoltListFunctions(t *testing.T) { testutil.SuccessExit(t, out, err) // (e) - expected := strconv.Itoa(voltVersionInfo()[2]) + expected := strconv.Itoa(Version()[2]) if string(out) != expected { t.Errorf("expected %q but got %q", expected, string(out)) } diff --git a/gateway/self_upgrade_test.go b/usecase/self_upgrade_test.go similarity index 95% rename from gateway/self_upgrade_test.go rename to usecase/self_upgrade_test.go index a4496f4e..1f37fffb 100644 --- a/gateway/self_upgrade_test.go +++ b/usecase/self_upgrade_test.go @@ -1,4 +1,4 @@ -package gateway +package usecase import ( "io/ioutil" @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/gateway" "github.com/vim-volt/volt/lockjson" ) @@ -32,7 +33,7 @@ func testVoltSelfUpgradeCheckFromOldVer(t *testing.T) { // =============== run =============== // - var err *Error + var err *gateway.Error out := captureOutput(t, func() { err = runVolt(t, "self-upgrade", "-check") }) @@ -60,6 +61,7 @@ func testVoltSelfUpgradeCheckFromCurrentVer(t *testing.T) { } } +// TODO use https://github.com/rhysd/go-fakeio func captureOutput(t *testing.T, f func()) string { r, w, err := os.Pipe() if err != nil { diff --git a/usecase/version.go b/usecase/version.go new file mode 100644 index 00000000..b56230ef --- /dev/null +++ b/usecase/version.go @@ -0,0 +1,77 @@ +package usecase + +import ( + "errors" + "regexp" + "strconv" +) + +// NOTE: this is not constant for testing (to change it temporarily) +var voltVersion = "v0.3.6-alpha" + +// VersionString is current version string +func VersionString() string { + return voltVersion +} + +// VersionInfo is [major, minor, patch, alphaBeta] +type VersionInfo []int + +const ( + suffixAlpha = 1 + suffixBeta = 2 + suffixStable = 9 +) + +var rxVersion = regexp.MustCompile(`^v?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(-alpha|-beta)?`) + +// Version returns versions [major, minor, patch, alphaBeta] +func Version() VersionInfo { + // parseVersion(voltVersion) must not return non-nil error! + voltVersionInfo, err := ParseVersion(voltVersion) + if err != nil { + panic(err) + } + return voltVersionInfo +} + +// CompareVersion compares two versions. +// and returns negative if v1 < v2, or positive if v1 > v2, or 0 if v1 == v2. +func CompareVersion(v1, v2 VersionInfo) int { + for i := 0; i < 4; i++ { + if v1[i] > v2[i] { + return 1 + } else if v1[i] < v2[i] { + return -1 + } + } + return 0 +} + +// ParseVersion parses version string +func ParseVersion(ver string) (VersionInfo, error) { + m := rxVersion.FindStringSubmatch(ver) + if len(m) == 0 { + return nil, errors.New("version number format is invalid: " + ver) + } + info := make(VersionInfo, 0, 4) + for i := 1; i <= 3 && m[i] != ""; i++ { + n, err := strconv.Atoi(m[i]) + if err != nil { + return nil, err + } + info = append(info, n) + } + if len(info) == 2 { + info = append(info, 0) + } + switch m[4] { + case "": + info = append(info, suffixStable) + case "-alpha": + info = append(info, suffixAlpha) + case "-beta": + info = append(info, suffixBeta) + } + return info, nil +} diff --git a/gateway/version_test.go b/usecase/version_test.go similarity index 88% rename from gateway/version_test.go rename to usecase/version_test.go index 0520c31c..3a7b7689 100644 --- a/gateway/version_test.go +++ b/usecase/version_test.go @@ -1,4 +1,4 @@ -package gateway +package usecase import "testing" @@ -36,13 +36,13 @@ func TestCompareVersion(t *testing.T) { } } -func parse(t *testing.T, ver string) versionInfo { +func parse(t *testing.T, ver string) VersionInfo { vinfo, err := parseVersion(ver) if err != nil { t.Errorf("\"%s\" should be a version number but isn't: %s", ver, err.Error()) } if len(vinfo) != 4 { - t.Errorf("parseVersion(%q) returned invalid versionInfo: %q", ver, vinfo) + t.Errorf("parseVersion(%q) returned invalid version info: %q", ver, vinfo) } return vinfo } From 15024875bc0e14c586852e6d1fda428e4325a834 Mon Sep 17 00:00:00 2001 From: tyru Date: Sun, 28 Oct 2018 23:06:45 +0900 Subject: [PATCH 08/11] refactor: absorb enable,disable commands to profile --- gateway/cmd.go | 1 + gateway/disable.go | 89 ---------------------------------------------- gateway/enable.go | 89 ---------------------------------------------- gateway/profile.go | 25 +++++++++++-- main.go | 1 + 5 files changed, 24 insertions(+), 181 deletions(-) delete mode 100644 gateway/disable.go delete mode 100644 gateway/enable.go diff --git a/gateway/cmd.go b/gateway/cmd.go index 7eb4fb5c..2c3c8f7c 100644 --- a/gateway/cmd.go +++ b/gateway/cmd.go @@ -24,6 +24,7 @@ type Cmd interface { // CmdContext is a data transfer object between Subcmd and Gateway layer. type CmdContext struct { + Cmd string Args []string LockJSON *lockjson.LockJSON Config *config.Config diff --git a/gateway/disable.go b/gateway/disable.go deleted file mode 100644 index 6c92ebb8..00000000 --- a/gateway/disable.go +++ /dev/null @@ -1,89 +0,0 @@ -package gateway - -import ( - "flag" - "fmt" - "github.com/pkg/errors" - "os" - - "github.com/vim-volt/volt/pathutil" -) - -func init() { - cmdMap["disable"] = &disableCmd{} -} - -type disableCmd struct { - helped bool -} - -func (cmd *disableCmd) ProhibitRootExecution(args []string) bool { return true } - -func (cmd *disableCmd) FlagSet() *flag.FlagSet { - fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) - fs.SetOutput(os.Stdout) - fs.Usage = func() { - fmt.Print(` -Usage - volt disable [-help] {repository} [{repository2} ...] - -Quick example - $ volt disable tyru/caw.vim # will disable tyru/caw.vim plugin in current profile - -Description - This is shortcut of: - volt profile rm {current profile} {repository} [{repository2} ...]` + "\n\n") - //fmt.Println("Options") - //fs.PrintDefaults() - fmt.Println() - cmd.helped = true - } - return fs -} - -func (cmd *disableCmd) Run(cmdctx *CmdContext) *Error { - reposPathList, err := cmd.parseArgs(cmdctx.Args) - if err == ErrShowedHelp { - return nil - } - if err != nil { - return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()} - } - - profCmd := profileCmd{} - cmdctx.Args = append( - []string{"-current"}, - reposPathList.Strings()..., - ) - err = profCmd.doRm(cmdctx) - if err != nil { - return &Error{Code: 11, Msg: err.Error()} - } - - return nil -} - -func (cmd *disableCmd) parseArgs(args []string) (pathutil.ReposPathList, error) { - fs := cmd.FlagSet() - fs.Parse(args) - if cmd.helped { - return nil, ErrShowedHelp - } - - if len(fs.Args()) == 0 { - fs.Usage() - return nil, errors.New("repository was not given") - } - - // Normalize repos path - reposPathList := make(pathutil.ReposPathList, 0, len(fs.Args())) - for _, arg := range fs.Args() { - reposPath, err := pathutil.NormalizeRepos(arg) - if err != nil { - return nil, err - } - reposPathList = append(reposPathList, reposPath) - } - - return reposPathList, nil -} diff --git a/gateway/enable.go b/gateway/enable.go deleted file mode 100644 index 94c5c589..00000000 --- a/gateway/enable.go +++ /dev/null @@ -1,89 +0,0 @@ -package gateway - -import ( - "flag" - "fmt" - "github.com/pkg/errors" - "os" - - "github.com/vim-volt/volt/pathutil" -) - -func init() { - cmdMap["enable"] = &enableCmd{} -} - -type enableCmd struct { - helped bool -} - -func (cmd *enableCmd) ProhibitRootExecution(args []string) bool { return true } - -func (cmd *enableCmd) FlagSet() *flag.FlagSet { - fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) - fs.SetOutput(os.Stdout) - fs.Usage = func() { - fmt.Print(` -Usage - volt enable [-help] {repository} [{repository2} ...] - -Quick example - $ volt enable tyru/caw.vim # will enable tyru/caw.vim plugin in current profile - -Description - This is shortcut of: - volt profile add {current profile} {repository} [{repository2} ...]` + "\n\n") - //fmt.Println("Options") - //fs.PrintDefaults() - fmt.Println() - cmd.helped = true - } - return fs -} - -func (cmd *enableCmd) Run(cmdctx *CmdContext) *Error { - reposPathList, err := cmd.parseArgs(cmdctx.Args) - if err == ErrShowedHelp { - return nil - } - if err != nil { - return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()} - } - - profCmd := profileCmd{} - cmdctx.Args = append( - []string{"-current"}, - reposPathList.Strings()..., - ) - err = profCmd.doAdd(cmdctx) - if err != nil { - return &Error{Code: 11, Msg: err.Error()} - } - - return nil -} - -func (cmd *enableCmd) parseArgs(args []string) (pathutil.ReposPathList, error) { - fs := cmd.FlagSet() - fs.Parse(args) - if cmd.helped { - return nil, ErrShowedHelp - } - - if len(fs.Args()) == 0 { - fs.Usage() - return nil, errors.New("repository was not given") - } - - // Normalize repos path - reposPathList := make(pathutil.ReposPathList, 0, len(fs.Args())) - for _, arg := range fs.Args() { - reposPath, err := pathutil.NormalizeRepos(arg) - if err != nil { - return nil, err - } - reposPathList = append(reposPathList, reposPath) - } - - return reposPathList, nil -} diff --git a/gateway/profile.go b/gateway/profile.go index d749dc2b..2de5aa8d 100644 --- a/gateway/profile.go +++ b/gateway/profile.go @@ -24,6 +24,8 @@ var profileSubCmd = make(map[string]func([]string) error) func init() { cmdMap["profile"] = &profileCmd{} + cmdMap["enable"] = cmdMap["profile"] + cmdMap["disable"] = cmdMap["profile"] } func (cmd *profileCmd) ProhibitRootExecution(args []string) bool { @@ -69,6 +71,14 @@ Command profile rename {old} {new} Rename profile {old} to {new}. + enable {repository} [{repository2} ...] + This is shortcut of: + volt profile add -current {repository} [{repository2} ...] + + disable {repository} [{repository2} ...] + This is shortcut of: + volt profile rm -current {repository} [{repository2} ...] + profile add [-current | {name}] {repository} [{repository2} ...] Add one or more repositories to profile {name}. @@ -103,7 +113,7 @@ Quick example func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { // Parse args - args, err := cmd.parseArgs(cmdctx.Args) + args, err := cmd.parseArgs(cmdctx) if err == ErrShowedHelp { return nil } @@ -142,9 +152,18 @@ func (cmd *profileCmd) Run(cmdctx *CmdContext) *Error { return nil } -func (cmd *profileCmd) parseArgs(args []string) ([]string, error) { +func (cmd *profileCmd) parseArgs(cmdctx *CmdContext) ([]string, error) { + switch cmdctx.Cmd { + case "enable": + cmdctx.Cmd = "profile" + cmdctx.Args = append([]string{"add", "-current"}, cmdctx.Args...) + case "disable": + cmdctx.Cmd = "profile" + cmdctx.Args = append([]string{"rm", "-current"}, cmdctx.Args...) + } + fs := cmd.FlagSet() - fs.Parse(args) + fs.Parse(cmdctx.Args) if cmd.helped { return nil, ErrShowedHelp } diff --git a/main.go b/main.go index 200d0909..05ee6c19 100644 --- a/main.go +++ b/main.go @@ -63,6 +63,7 @@ func run(args []string) (int, string) { } result := c.Run(&gateway.CmdContext{ + Cmd: subCmd, Args: args, LockJSON: lockJSON, Config: cfg, From 68f5d4d5e26c1c455a8d85ca37b2927bd32d1632 Mon Sep 17 00:00:00 2001 From: tyru Date: Mon, 29 Oct 2018 00:07:34 +0900 Subject: [PATCH 09/11] refactor: remove unnecessary Read() of config,lock.json --- gateway/build.go | 2 +- gateway/builder/builder.go | 16 ++--- gateway/builder/copy.go | 8 +-- gateway/builder/symlink.go | 6 +- gateway/get.go | 12 +--- gateway/migrate.go | 2 +- gateway/migrate/lockjson.go | 11 +--- gateway/migrate/migrater.go | 7 ++- gateway/migrate/plugconf-config-func.go | 18 +++--- gateway/profile.go | 78 +++++++------------------ gateway/rm.go | 14 ++--- lockjson/lockjson.go | 54 +++++++++-------- 12 files changed, 83 insertions(+), 145 deletions(-) diff --git a/gateway/build.go b/gateway/build.go index 095ae892..fceed85d 100644 --- a/gateway/build.go +++ b/gateway/build.go @@ -69,7 +69,7 @@ func (cmd *buildCmd) Run(cmdctx *CmdContext) *Error { } defer transaction.Remove() - err = builder.Build(cmd.full) + err = builder.Build(cmd.full, cmdctx.Config, cmdctx.LockJSON) if err != nil { logger.Error() return &Error{Code: 12, Msg: "Failed to build: " + err.Error()} diff --git a/gateway/builder/builder.go b/gateway/builder/builder.go index 34752a1b..9d9a0bd9 100644 --- a/gateway/builder/builder.go +++ b/gateway/builder/builder.go @@ -1,30 +1,26 @@ package builder import ( - "github.com/pkg/errors" "os" + "github.com/pkg/errors" + "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/gateway/buildinfo" + "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/pathutil" ) // Builder creates/updates ~/.vim/pack/volt directory type Builder interface { - Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error + Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error } const currentBuildInfoVersion = 2 // Build creates/updates ~/.vim/pack/volt directory -func Build(full bool) error { - // Read config.toml - cfg, err := config.Read() - if err != nil { - return errors.Wrap(err, "could not read config.toml") - } - +func Build(full bool, cfg *config.Config, lockJSON *lockjson.LockJSON) error { // Get builder blder, err := getBuilder(cfg.Build.Strategy) if err != nil { @@ -75,7 +71,7 @@ func Build(full bool) error { } } - return blder.Build(buildInfo, buildReposMap) + return blder.Build(buildInfo, buildReposMap, lockJSON) } func getBuilder(strategy string) (Builder, error) { diff --git a/gateway/builder/copy.go b/gateway/builder/copy.go index 217071b9..96b0bbfd 100644 --- a/gateway/builder/copy.go +++ b/gateway/builder/copy.go @@ -25,19 +25,13 @@ type copyBuilder struct { BaseBuilder } -func (builder *copyBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error { +func (builder *copyBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error { // Exit if vim executable was not found in PATH vimExePath, err := pathutil.VimExecutable() if err != nil { return err } - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.New("could not read lock.json: " + err.Error()) - } - // Get current profile's repos list reposList, err := lockJSON.GetCurrentReposList() if err != nil { diff --git a/gateway/builder/symlink.go b/gateway/builder/symlink.go index 4326d697..13e0e488 100644 --- a/gateway/builder/symlink.go +++ b/gateway/builder/symlink.go @@ -24,17 +24,13 @@ type symlinkBuilder struct { } // TODO: rollback when return err (!= nil) -func (builder *symlinkBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos) error { +func (builder *symlinkBuilder) Build(buildInfo *buildinfo.BuildInfo, buildReposMap map[pathutil.ReposPath]*buildinfo.Repos, lockJSON *lockjson.LockJSON) error { // Exit if vim executable was not found in PATH if _, err := pathutil.VimExecutable(); err != nil { return err } // Get current profile's repos list - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "could not read lock.json") - } reposList, err := lockJSON.GetCurrentReposList() if err != nil { return err diff --git a/gateway/get.go b/gateway/get.go index 95fd2598..7fc3bc84 100644 --- a/gateway/get.go +++ b/gateway/get.go @@ -134,7 +134,7 @@ func (cmd *getCmd) Run(cmdctx *CmdContext) *Error { return &Error{Code: 13, Msg: "No repositories are specified"} } - err = cmd.doGet(reposPathList, cmdctx.LockJSON) + err = cmd.doGet(reposPathList, cmdctx.Config, cmdctx.LockJSON) if err != nil { return &Error{Code: 20, Msg: err.Error()} } @@ -181,7 +181,7 @@ func (cmd *getCmd) getReposPathList(args []string, lockJSON *lockjson.LockJSON) return reposPathList, nil } -func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson.LockJSON) error { +func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, cfg *config.Config, lockJSON *lockjson.LockJSON) error { // Find matching profile profile, err := lockJSON.Profiles.FindByName(lockJSON.CurrentProfileName) if err != nil { @@ -197,12 +197,6 @@ func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson. } defer transaction.Remove() - // Read config.toml - cfg, err := config.Read() - if err != nil { - return errors.Wrap(err, "could not read config.toml") - } - done := make(chan getParallelResult, len(reposPathList)) getCount := 0 // Invoke installing / upgrading tasks @@ -249,7 +243,7 @@ func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson. } // Build ~/.vim/pack/volt dir - err = builder.Build(false) + err = builder.Build(false, cfg, lockJSON) if err != nil { return errors.Wrap(err, "could not build "+pathutil.VimVoltDir()) } diff --git a/gateway/migrate.go b/gateway/migrate.go index 97ea562c..1e06baf4 100644 --- a/gateway/migrate.go +++ b/gateway/migrate.go @@ -64,7 +64,7 @@ func (cmd *migrateCmd) Run(cmdctx *CmdContext) *Error { return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()} } - if err := op.Migrate(); err != nil { + if err := op.Migrate(cmdctx.Config, cmdctx.LockJSON); err != nil { return &Error{Code: 11, Msg: "Failed to migrate: " + err.Error()} } diff --git a/gateway/migrate/lockjson.go b/gateway/migrate/lockjson.go index fb3e963b..b8a9c087 100644 --- a/gateway/migrate/lockjson.go +++ b/gateway/migrate/lockjson.go @@ -3,6 +3,7 @@ package migrate import ( "github.com/pkg/errors" + "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/transaction" ) @@ -31,15 +32,9 @@ Description To suppress this, running this command simply reads and writes migrated structure to lock.json.` } -func (*lockjsonMigrater) Migrate() error { - // Read lock.json - lockJSON, err := lockjson.ReadNoMigrationMsg() - if err != nil { - return errors.Wrap(err, "could not read lock.json") - } - +func (*lockjsonMigrater) Migrate(cfg *config.Config, lockJSON *lockjson.LockJSON) error { // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } diff --git a/gateway/migrate/migrater.go b/gateway/migrate/migrater.go index 2e26ff20..16f6c1aa 100644 --- a/gateway/migrate/migrater.go +++ b/gateway/migrate/migrater.go @@ -1,13 +1,16 @@ package migrate import ( - "github.com/pkg/errors" "sort" + + "github.com/pkg/errors" + "github.com/vim-volt/volt/config" + "github.com/vim-volt/volt/lockjson" ) // Migrater migrates many kinds of data. type Migrater interface { - Migrate() error + Migrate(cfg *config.Config, lockJSON *lockjson.LockJSON) error Name() string Description(brief bool) string } diff --git a/gateway/migrate/plugconf-config-func.go b/gateway/migrate/plugconf-config-func.go index 1f3255d5..6f2d39fe 100644 --- a/gateway/migrate/plugconf-config-func.go +++ b/gateway/migrate/plugconf-config-func.go @@ -1,12 +1,14 @@ package migrate import ( - "github.com/pkg/errors" "io/ioutil" "os" "path/filepath" "strings" + "github.com/pkg/errors" + + "github.com/vim-volt/volt/config" "github.com/vim-volt/volt/gateway/builder" "github.com/vim-volt/volt/lockjson" "github.com/vim-volt/volt/logger" @@ -39,13 +41,7 @@ Description All plugconf files are replaced with new contents.` } -func (*plugconfConfigMigrater) Migrate() error { - // Read lock.json - lockJSON, err := lockjson.ReadNoMigrationMsg() - if err != nil { - return errors.Wrap(err, "could not read lock.json") - } - +func (*plugconfConfigMigrater) Migrate(cfg *config.Config, lockJSON *lockjson.LockJSON) error { results, parseErr := plugconf.ParseMultiPlugconf(lockJSON.Repos) if parseErr.HasErrs() { logger.Error("Please fix the following errors before migration:") @@ -82,21 +78,21 @@ func (*plugconfConfigMigrater) Migrate() error { // After checking errors, write the content to files for _, info := range infoList { os.MkdirAll(filepath.Dir(info.path), 0755) - err = ioutil.WriteFile(info.path, info.content, 0644) + err := ioutil.WriteFile(info.path, info.content, 0644) if err != nil { return err } } // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } defer transaction.Remove() // Build ~/.vim/pack/volt dir - err = builder.Build(false) + err = builder.Build(false, cfg, lockJSON) if err != nil { return errors.Wrap(err, "could not build "+pathutil.VimVoltDir()) } diff --git a/gateway/profile.go b/gateway/profile.go index 2de5aa8d..93a4589e 100644 --- a/gateway/profile.go +++ b/gateway/profile.go @@ -175,16 +175,10 @@ func (cmd *profileCmd) parseArgs(cmdctx *CmdContext) ([]string, error) { return fs.Args(), nil } -func (*profileCmd) getCurrentProfile() (string, error) { - lockJSON, err := lockjson.Read() - if err != nil { - return "", errors.Wrap(err, "failed to read lock.json") - } - return lockJSON.CurrentProfileName, nil -} - func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { args := cmdctx.Args + lockJSON := cmdctx.LockJSON + // Parse args createProfile := false if len(args) > 0 && args[0] == "-n" { @@ -198,19 +192,13 @@ func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { } profileName := args[0] - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - // Exit if current profile is same as profileName if lockJSON.CurrentProfileName == profileName { return errors.Errorf("'%s' is current profile", profileName) } // Create given profile unless the profile exists - if _, err = lockJSON.Profiles.FindByName(profileName); err != nil { + if _, err := lockJSON.Profiles.FindByName(profileName); err != nil { if !createProfile { return err } @@ -219,7 +207,7 @@ func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { return err } // Read lock.json again - lockJSON, err = lockjson.Read() + err = lockJSON.Reload() if err != nil { return errors.Wrap(err, "failed to read lock.json") } @@ -229,7 +217,7 @@ func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { } // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } @@ -247,7 +235,7 @@ func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { logger.Info("Changed current profile: " + profileName) // Build ~/.vim/pack/volt dir - err = builder.Build(false) + err = builder.Build(false, cmdctx.Config, lockJSON) if err != nil { return errors.Wrap(err, "could not build "+pathutil.VimVoltDir()) } @@ -257,18 +245,14 @@ func (cmd *profileCmd) doSet(cmdctx *CmdContext) error { func (cmd *profileCmd) doShow(cmdctx *CmdContext) error { args := cmdctx.Args + lockJSON := cmdctx.LockJSON + if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile show' receives profile name.") return nil } - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - var profileName string if args[0] == "-current" { profileName = lockJSON.CurrentProfileName @@ -301,6 +285,8 @@ func (cmd *profileCmd) doList(cmdctx *CmdContext) error { func (cmd *profileCmd) doNew(cmdctx *CmdContext) error { args := cmdctx.Args + lockJSON := cmdctx.LockJSON + if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile new' receives profile name.") @@ -308,14 +294,8 @@ func (cmd *profileCmd) doNew(cmdctx *CmdContext) error { } profileName := args[0] - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - // Return error if profiles[]/name matches profileName - _, err = lockJSON.Profiles.FindByName(profileName) + _, err := lockJSON.Profiles.FindByName(profileName) if err == nil { return errors.New("profile '" + profileName + "' already exists") } @@ -346,20 +326,16 @@ func (cmd *profileCmd) doNew(cmdctx *CmdContext) error { func (cmd *profileCmd) doDestroy(cmdctx *CmdContext) error { args := cmdctx.Args + lockJSON := cmdctx.LockJSON + if len(args) == 0 { cmd.FlagSet().Usage() logger.Error("'volt profile destroy' receives profile name.") return nil } - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } @@ -405,6 +381,8 @@ func (cmd *profileCmd) doDestroy(cmdctx *CmdContext) error { func (cmd *profileCmd) doRename(cmdctx *CmdContext) error { args := cmdctx.Args + lockJSON := cmdctx.LockJSON + if len(args) != 2 { cmd.FlagSet().Usage() logger.Error("'volt profile rename' receives profile name.") @@ -413,12 +391,6 @@ func (cmd *profileCmd) doRename(cmdctx *CmdContext) error { oldName := args[0] newName := args[1] - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } - // Return error if profiles[]/name does not match oldName index := lockJSON.Profiles.FindIndexByName(oldName) if index < 0 { @@ -431,7 +403,7 @@ func (cmd *profileCmd) doRename(cmdctx *CmdContext) error { } // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } @@ -465,11 +437,7 @@ func (cmd *profileCmd) doRename(cmdctx *CmdContext) error { func (cmd *profileCmd) doAdd(cmdctx *CmdContext) error { args := cmdctx.Args - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } + lockJSON := cmdctx.LockJSON // Parse args profileName, reposPathList, err := cmd.parseAddArgs(lockJSON, "add", args) @@ -498,7 +466,7 @@ func (cmd *profileCmd) doAdd(cmdctx *CmdContext) error { } // Build ~/.vim/pack/volt dir - err = builder.Build(false) + err = builder.Build(false, cmdctx.Config, lockJSON) if err != nil { return errors.Wrap(err, "could not build "+pathutil.VimVoltDir()) } @@ -508,11 +476,7 @@ func (cmd *profileCmd) doAdd(cmdctx *CmdContext) error { func (cmd *profileCmd) doRm(cmdctx *CmdContext) error { args := cmdctx.Args - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return errors.Wrap(err, "failed to read lock.json") - } + lockJSON := cmdctx.LockJSON // Parse args profileName, reposPathList, err := cmd.parseAddArgs(lockJSON, "rm", args) @@ -543,7 +507,7 @@ func (cmd *profileCmd) doRm(cmdctx *CmdContext) error { } // Build ~/.vim/pack/volt dir - err = builder.Build(false) + err = builder.Build(false, cmdctx.Config, lockJSON) if err != nil { return errors.Wrap(err, "could not build "+pathutil.VimVoltDir()) } diff --git a/gateway/rm.go b/gateway/rm.go index c571cd4c..143502a3 100644 --- a/gateway/rm.go +++ b/gateway/rm.go @@ -72,13 +72,13 @@ func (cmd *rmCmd) Run(cmdctx *CmdContext) *Error { return &Error{Code: 10, Msg: err.Error()} } - err = cmd.doRemove(reposPathList) + err = cmd.doRemove(reposPathList, cmdctx.LockJSON) if err != nil { return &Error{Code: 11, Msg: "Failed to remove repository: " + err.Error()} } // Build opt dir - err = builder.Build(false) + err = builder.Build(false, cmdctx.Config, cmdctx.LockJSON) if err != nil { return &Error{Code: 12, Msg: "Could not build " + pathutil.VimVoltDir() + ": " + err.Error()} } @@ -109,15 +109,9 @@ func (cmd *rmCmd) parseArgs(args []string) ([]pathutil.ReposPath, error) { return reposPathList, nil } -func (cmd *rmCmd) doRemove(reposPathList []pathutil.ReposPath) error { - // Read lock.json - lockJSON, err := lockjson.Read() - if err != nil { - return err - } - +func (cmd *rmCmd) doRemove(reposPathList []pathutil.ReposPath, lockJSON *lockjson.LockJSON) error { // Begin transaction - err = transaction.Create() + err := transaction.Create() if err != nil { return err } diff --git a/lockjson/lockjson.go b/lockjson/lockjson.go index cf3dab90..3646dc3e 100644 --- a/lockjson/lockjson.go +++ b/lockjson/lockjson.go @@ -56,46 +56,48 @@ type Profile struct { const lockJSONVersion = 2 -func initialLockJSON() *LockJSON { - return &LockJSON{ - Version: lockJSONVersion, - CurrentProfileName: "default", - Repos: make([]Repos, 0), - Profiles: []Profile{ - Profile{ - Name: "default", - ReposPath: make([]pathutil.ReposPath, 0), - }, +func initLockJSON(lockJSON *LockJSON) { + lockJSON.Version = lockJSONVersion + lockJSON.CurrentProfileName = "default" + lockJSON.Repos = make([]Repos, 0) + lockJSON.Profiles = []Profile{ + Profile{ + Name: "default", + ReposPath: make([]pathutil.ReposPath, 0), }, } } // Read reads from lock.json and returns LockJSON func Read() (*LockJSON, error) { - return read(true) + var lockJSON LockJSON + err := read(true, &lockJSON) + return &lockJSON, err } // ReadNoMigrationMsg is same as Read, but no migration message is printed. func ReadNoMigrationMsg() (*LockJSON, error) { - return read(false) + var lockJSON LockJSON + err := read(false, &lockJSON) + return &lockJSON, err } -func read(doLog bool) (*LockJSON, error) { +func read(doLog bool, lockJSON *LockJSON) error { // Return initial lock.json struct if lockfile does not exist lockfile := pathutil.LockJSON() if !pathutil.Exists(lockfile) { - return initialLockJSON(), nil + initLockJSON(lockJSON) + return nil } // Read lock.json bytes, err := ioutil.ReadFile(lockfile) if err != nil { - return nil, err + return err } - var lockJSON LockJSON - err = json.Unmarshal(bytes, &lockJSON) + err = json.Unmarshal(bytes, lockJSON) if err != nil { - return nil, err + return err } if lockJSON.Version < lockJSONVersion { @@ -103,19 +105,18 @@ func read(doLog bool) (*LockJSON, error) { logger.Warnf("Performing auto-migration of lock.json: v%d -> v%d", lockJSON.Version, lockJSONVersion) logger.Warn("Please run 'volt migrate' to migrate explicitly if it's not updated by after operations") } - err = migrate(bytes, &lockJSON) + err = migrate(bytes, lockJSON) if err != nil { - return nil, err + return err } } // Validate lock.json - err = validate(&lockJSON) + err = validate(lockJSON) if err != nil { - return nil, errors.Wrap(err, "validation failed: lock.json") + return errors.Wrap(err, "validation failed: lock.json") } - - return &lockJSON, nil + return nil } func validate(lockJSON *LockJSON) error { @@ -252,6 +253,11 @@ func validateMissing(lockJSON *LockJSON) error { return nil } +// Reload reads lock.json again from filesystem. +func (lockJSON *LockJSON) Reload() error { + return read(true, lockJSON) +} + func (lockJSON *LockJSON) Write() error { // Validate lock.json err := validate(lockJSON) From bc8b528c9173818895be8380a37c4440a4c9dbcc Mon Sep 17 00:00:00 2001 From: tyru Date: Mon, 29 Oct 2018 00:15:08 +0900 Subject: [PATCH 10/11] refactor: move migration codes to usecase.* --- gateway/migrate.go | 13 +++++++------ .../lockjson.go => usecase/migrate-lockjson.go | 2 +- .../migrate-plugconf-config-func.go | 2 +- gateway/migrate/migrater.go => usecase/migrate.go | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) rename gateway/migrate/lockjson.go => usecase/migrate-lockjson.go (98%) rename gateway/migrate/plugconf-config-func.go => usecase/migrate-plugconf-config-func.go (99%) rename gateway/migrate/migrater.go => usecase/migrate.go (98%) diff --git a/gateway/migrate.go b/gateway/migrate.go index 1e06baf4..63d6fb00 100644 --- a/gateway/migrate.go +++ b/gateway/migrate.go @@ -3,11 +3,12 @@ package gateway import ( "flag" "fmt" - "github.com/pkg/errors" "os" - "github.com/vim-volt/volt/gateway/migrate" + "github.com/pkg/errors" + "github.com/vim-volt/volt/logger" + "github.com/vim-volt/volt/usecase" ) func init() { @@ -26,7 +27,7 @@ func (cmd *migrateCmd) FlagSet() *flag.FlagSet { fs.Usage = func() { args := fs.Args() if len(args) > 0 { - m, err := migrate.GetMigrater(args[0]) + m, err := usecase.GetMigrater(args[0]) if err != nil { return } @@ -72,7 +73,7 @@ func (cmd *migrateCmd) Run(cmdctx *CmdContext) *Error { return nil } -func (cmd *migrateCmd) parseArgs(args []string) (migrate.Migrater, error) { +func (cmd *migrateCmd) parseArgs(args []string) (usecase.Migrater, error) { fs := cmd.FlagSet() fs.Parse(args) if cmd.helped { @@ -82,11 +83,11 @@ func (cmd *migrateCmd) parseArgs(args []string) (migrate.Migrater, error) { if len(args) == 0 { return nil, errors.New("please specify migration operation") } - return migrate.GetMigrater(args[0]) + return usecase.GetMigrater(args[0]) } func (cmd *migrateCmd) showAvailableOps(write func(string)) { - for _, m := range migrate.ListMigraters() { + for _, m := range usecase.ListMigraters() { write(fmt.Sprintf(" %s", m.Name())) write(fmt.Sprintf(" %s", m.Description(true))) } diff --git a/gateway/migrate/lockjson.go b/usecase/migrate-lockjson.go similarity index 98% rename from gateway/migrate/lockjson.go rename to usecase/migrate-lockjson.go index b8a9c087..b45ff5b0 100644 --- a/gateway/migrate/lockjson.go +++ b/usecase/migrate-lockjson.go @@ -1,4 +1,4 @@ -package migrate +package usecase import ( "github.com/pkg/errors" diff --git a/gateway/migrate/plugconf-config-func.go b/usecase/migrate-plugconf-config-func.go similarity index 99% rename from gateway/migrate/plugconf-config-func.go rename to usecase/migrate-plugconf-config-func.go index 6f2d39fe..a5ed8a6b 100644 --- a/gateway/migrate/plugconf-config-func.go +++ b/usecase/migrate-plugconf-config-func.go @@ -1,4 +1,4 @@ -package migrate +package usecase import ( "io/ioutil" diff --git a/gateway/migrate/migrater.go b/usecase/migrate.go similarity index 98% rename from gateway/migrate/migrater.go rename to usecase/migrate.go index 16f6c1aa..a9b93b50 100644 --- a/gateway/migrate/migrater.go +++ b/usecase/migrate.go @@ -1,4 +1,4 @@ -package migrate +package usecase import ( "sort" From eb333c22717f0c602423ad6d7e416d4df880326e Mon Sep 17 00:00:00 2001 From: tyru Date: Thu, 29 Nov 2018 15:11:21 +0900 Subject: [PATCH 11/11] wip --- gateway/self_upgrade.go | 183 ++++------------------------------------ gateway/version.go | 6 +- usecase/self_upgrade.go | 166 ++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 170 deletions(-) create mode 100644 usecase/self_upgrade.go diff --git a/gateway/self_upgrade.go b/gateway/self_upgrade.go index fa082cf3..d2e9ae12 100644 --- a/gateway/self_upgrade.go +++ b/gateway/self_upgrade.go @@ -1,33 +1,27 @@ package gateway import ( - "encoding/json" "flag" "fmt" - "io" "os" - "os/exec" - "path/filepath" - "runtime" "strconv" - "strings" - "syscall" - "time" - "github.com/pkg/errors" - - "github.com/vim-volt/volt/httputil" - "github.com/vim-volt/volt/logger" "github.com/vim-volt/volt/usecase" ) func init() { - cmdMap["self-upgrade"] = &selfUpgradeCmd{} + cmdMap["self-upgrade"] = &selfUpgradeCmd{ + SelfUpgrade: usecase.SelfUpgrade, + RemoveOldBinary: usecase.RemoveOldBinary, + } } type selfUpgradeCmd struct { - helped bool - check bool + helped bool + checkOnly bool + + SelfUpgrade func(latestURL string, checkOnly bool) error + RemoveOldBinary func(ppid int) error } func (cmd *selfUpgradeCmd) ProhibitRootExecution(args []string) bool { return true } @@ -47,7 +41,7 @@ Description fmt.Println() cmd.helped = true } - fs.BoolVar(&cmd.check, "check", false, "only checks the newer version is available") + fs.BoolVar(&cmd.checkOnly, "check", false, "only checks the newer version is available") return fs } @@ -61,12 +55,16 @@ func (cmd *selfUpgradeCmd) Run(cmdctx *CmdContext) *Error { } if ppidStr := os.Getenv("VOLT_SELF_UPGRADE_PPID"); ppidStr != "" { - if err = cmd.doCleanUp(ppidStr); err != nil { + ppid, err := strconv.Atoi(ppidStr) + if err != nil { + return &Error{Code: 20, Msg: "Failed to parse VOLT_SELF_UPGRADE_PPID: " + err.Error()} + } + if err = cmd.RemoveOldBinary(ppid); err != nil { return &Error{Code: 11, Msg: "Failed to clean up old binary: " + err.Error()} } } else { latestURL := "https://api.github.com/repos/vim-volt/volt/releases/latest" - if err = cmd.doSelfUpgrade(latestURL); err != nil { + if err = cmd.SelfUpgrade(latestURL, cmd.checkOnly); err != nil { return &Error{Code: 12, Msg: "Failed to self-upgrade: " + err.Error()} } } @@ -82,152 +80,3 @@ func (cmd *selfUpgradeCmd) parseArgs(args []string) error { } return nil } - -func (cmd *selfUpgradeCmd) doCleanUp(ppidStr string) error { - ppid, err := strconv.Atoi(ppidStr) - if err != nil { - return errors.Wrap(err, "failed to parse VOLT_SELF_UPGRADE_PPID") - } - - // Wait until the parent process exits - if died := cmd.waitUntilParentExits(ppid); !died { - return errors.Errorf("parent pid (%s) is keeping alive for long time", ppidStr) - } - - // Remove old binary - voltExe, err := cmd.getExecutablePath() - if err != nil { - return err - } - return os.Remove(voltExe + ".old") -} - -func (cmd *selfUpgradeCmd) waitUntilParentExits(pid int) bool { - fib := []int{1, 1, 2, 3, 5, 8, 13} // 33 second - for i := 0; i < len(fib); i++ { - if !cmd.processIsAlive(pid) { - return true - } - time.Sleep(time.Duration(fib[i]) * time.Second) - } - return false -} - -func (*selfUpgradeCmd) processIsAlive(pid int) bool { - process, err := os.FindProcess(pid) - if err != nil { - return false - } - err = process.Signal(syscall.Signal(0)) - return err == nil -} - -type latestRelease struct { - TagName string `json:"tag_name"` - Body string `json:"body"` - Assets []releaseAsset -} - -type releaseAsset struct { - BrowserDownloadURL string `json:"browser_download_url"` - Name string `json:"name"` -} - -func (cmd *selfUpgradeCmd) doSelfUpgrade(latestURL string) error { - // Check the latest binary info - release, err := cmd.checkLatest(latestURL) - if err != nil { - return err - } - logger.Debugf("tag_name = %q", release.TagName) - tagNameVer, err := usecase.ParseVersion(release.TagName) - if err != nil { - return err - } - if usecase.CompareVersion(tagNameVer, usecase.Version()) <= 0 { - logger.Info("No updates were found.") - return nil - } - logger.Infof("Found update: %s -> %s", usecase.VersionString(), release.TagName) - - // Show release note - fmt.Println("---") - fmt.Println(release.Body) - fmt.Println("---") - - if cmd.check { - return nil - } - - // Download the latest binary as "volt[.exe].latest" - voltExe, err := cmd.getExecutablePath() - if err != nil { - return err - } - latestFile, err := os.OpenFile(voltExe+".latest", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) - if err != nil { - return err - } - err = cmd.download(latestFile, release) - latestFile.Close() - if err != nil { - return err - } - - // Rename dir/volt[.exe] to dir/volt[.exe].old - // NOTE: Windows can rename running executable file - if err := os.Rename(voltExe, voltExe+".old"); err != nil { - return err - } - - // Rename dir/volt[.exe].latest to dir/volt[.exe] - if err := os.Rename(voltExe+".latest", voltExe); err != nil { - return err - } - - // Spawn dir/volt[.exe] with env "VOLT_SELF_UPGRADE_PPID={pid}" - voltCmd := exec.Command(voltExe, "self-upgrade") - if err = voltCmd.Start(); err != nil { - return err - } - return nil -} - -func (*selfUpgradeCmd) getExecutablePath() (string, error) { - exe, err := os.Executable() - if err != nil { - return "", err - } - return filepath.EvalSymlinks(exe) -} - -func (*selfUpgradeCmd) checkLatest(url string) (*latestRelease, error) { - content, err := httputil.GetContent(url) - if err != nil { - return nil, err - } - var release latestRelease - if err = json.Unmarshal(content, &release); err != nil { - return nil, err - } - return &release, nil -} - -func (*selfUpgradeCmd) download(w io.Writer, release *latestRelease) error { - suffix := runtime.GOOS + "-" + runtime.GOARCH - for i := range release.Assets { - // e.g.: Name = "volt-v0.1.2-linux-amd64" - if strings.HasSuffix(release.Assets[i].Name, suffix) { - r, err := httputil.GetContentReader(release.Assets[i].BrowserDownloadURL) - if err != nil { - return err - } - defer r.Close() - if _, err = io.Copy(w, r); err != nil { - return err - } - break - } - } - return nil -} diff --git a/gateway/version.go b/gateway/version.go index 149b3925..06522484 100644 --- a/gateway/version.go +++ b/gateway/version.go @@ -9,13 +9,13 @@ import ( ) func init() { - cmdMap["version"] = &versionCmd{Version: usecase.VersionString()} + cmdMap["version"] = &versionCmd{VersionString: usecase.VersionString()} } type versionCmd struct { helped bool - Version string + VersionString string } func (cmd *versionCmd) ProhibitRootExecution(args []string) bool { return false } @@ -45,6 +45,6 @@ func (cmd *versionCmd) Run(cmdctx *CmdContext) *Error { return nil } - fmt.Printf("volt version: %s\n", cmd.Version) + fmt.Printf("volt version: %s\n", cmd.VersionString) return nil } diff --git a/usecase/self_upgrade.go b/usecase/self_upgrade.go new file mode 100644 index 00000000..43b0da02 --- /dev/null +++ b/usecase/self_upgrade.go @@ -0,0 +1,166 @@ +package usecase + +import ( + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "syscall" + "time" + + "github.com/pkg/errors" + + "github.com/vim-volt/volt/httputil" + "github.com/vim-volt/volt/logger" +) + +// SelfUpgrade upgrades running volt binary if checkOnly = false. +// if checkOnly = true, only check the latest version and shows the information. +func SelfUpgrade(latestURL string, checkOnly bool) error { + // Check the latest binary info + release, err := checkLatest(latestURL) + if err != nil { + return err + } + logger.Debugf("tag_name = %q", release.TagName) + tagNameVer, err := ParseVersion(release.TagName) + if err != nil { + return err + } + if CompareVersion(tagNameVer, Version()) <= 0 { + logger.Info("No updates were found.") + return nil + } + logger.Infof("Found update: %s -> %s", VersionString(), release.TagName) + + // Show release note + fmt.Println("---") + fmt.Println(release.Body) + fmt.Println("---") + + if checkOnly { + return nil + } + + // Download the latest binary as "volt[.exe].latest" + voltExe, err := getExecutablePath() + if err != nil { + return err + } + latestFile, err := os.OpenFile(voltExe+".latest", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) + if err != nil { + return err + } + err = download(latestFile, release) + latestFile.Close() + if err != nil { + return err + } + + // Rename dir/volt[.exe] to dir/volt[.exe].old + // NOTE: Windows can rename running executable file + if err := os.Rename(voltExe, voltExe+".old"); err != nil { + return err + } + + // Rename dir/volt[.exe].latest to dir/volt[.exe] + if err := os.Rename(voltExe+".latest", voltExe); err != nil { + return err + } + + // Spawn dir/volt[.exe] with env "VOLT_SELF_UPGRADE_PPID={pid}" + voltCmd := exec.Command(voltExe, "self-upgrade") + if err = voltCmd.Start(); err != nil { + return err + } + return nil +} + +func download(w io.Writer, release *Release) error { + suffix := runtime.GOOS + "-" + runtime.GOARCH + for i := range release.Assets { + // e.g.: Name = "volt-v0.1.2-linux-amd64" + if strings.HasSuffix(release.Assets[i].Name, suffix) { + r, err := httputil.GetContentReader(release.Assets[i].BrowserDownloadURL) + if err != nil { + return err + } + defer r.Close() + if _, err = io.Copy(w, r); err != nil { + return err + } + break + } + } + return nil +} + +// checkLatest returns the latest release information. +func checkLatest(url string) (*Release, error) { + content, err := httputil.GetContent(url) + if err != nil { + return nil, err + } + var release Release + if err = json.Unmarshal(content, &release); err != nil { + return nil, err + } + return &release, nil +} + +func getExecutablePath() (string, error) { + exe, err := os.Executable() + if err != nil { + return "", err + } + return filepath.EvalSymlinks(exe) +} + +// Release has information about a volt release. +type Release struct { + TagName string `json:"tag_name"` + Body string `json:"body"` + Assets []struct { + BrowserDownloadURL string `json:"browser_download_url"` + Name string `json:"name"` + } +} + +// RemoveOldBinary removes old +func RemoveOldBinary(ppid int) error { + // Wait until the parent process exits + if died := waitUntilParentExits(ppid); !died { + return errors.Errorf("parent pid (%d) is keeping alive for long time", ppid) + } + + // Remove old binary + voltExe, err := getExecutablePath() + if err != nil { + return err + } + return os.Remove(voltExe + ".old") +} + +func waitUntilParentExits(pid int) bool { + fib := []int{1, 1, 2, 3, 5, 8, 13} // 33 second + for i := 0; i < len(fib); i++ { + if !processIsAlive(pid) { + return true + } + time.Sleep(time.Duration(fib[i]) * time.Second) + } + return false +} + +func processIsAlive(pid int) bool { + process, err := os.FindProcess(pid) + if err != nil { + return false + } + err = process.Signal(syscall.Signal(0)) + return err == nil +}