diff --git a/.ferconfig.json.example b/.ferconfig.json.example
new file mode 100644
index 0000000..f99da7d
--- /dev/null
+++ b/.ferconfig.json.example
@@ -0,0 +1,6 @@
+{
+ "gocek": {
+ "project_directories": [],
+ "output_save_folder": ""
+ }
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ea19da..0c9a61a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,24 @@
#Fer
+
+## [v1.6.0] - 2020-04-16
+### New Features
+- add fer & gocek config
+- add gocek all to check all modules provided
+- add gocek to console
+- add checker
+- add mod model
+
+### Other Improvements
+- add .ferconfig.json.example & update README
+- remove debug log
+- add .ferconfig.json example
+
+
## [v1.5.4] - 2020-04-14
### Fixes
-- fix self update
+- self update ([#17](https://github.com/kumparan/fer/issues/17))
@@ -84,7 +99,8 @@
- db migrationfile generator ([#3](https://github.com/kumparan/fer/issues/3))
-[Unreleased]: https://github.com/kumparan/fer/compare/v1.5.4...HEAD
+[Unreleased]: https://github.com/kumparan/fer/compare/v1.6.0...HEAD
+[v1.6.0]: https://github.com/kumparan/fer/compare/v1.5.4...v1.6.0
[v1.5.4]: https://github.com/kumparan/fer/compare/v1.5.3...v1.5.4
[v1.5.3]: https://github.com/kumparan/fer/compare/v1.5.2...v1.5.3
[v1.5.2]: https://github.com/kumparan/fer/compare/v1.5.1...v1.5.2
diff --git a/README.md b/README.md
index d83cf5f..51da143 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,13 @@ fer version
- [x] Generate Service&test files and Client files From Proto
- [x] DB migration file generator
- [x] Generate Repository (include model)
+- [x] Gocek, checks go mod update info
- [ ] Add worker with command
- [ ] Add Nats Subscriber with command
+## Config
+Add `.ferconfig.json` into `$HOME/.ferconfig.json`, use the `.ferconfig.json.example` as reference
+
## Kumparan Microservices Generator
`fer generate project content-service --proto pb/example/example.proto`
@@ -99,3 +103,9 @@ for deployment, push deployment tag
`fer create chglog vx.x.x`
to generate CHANGELOG.md
+## Gocek
+`fer gocek`
+check current working directory
+
+`fer gocek all`
+check all directory listed in `.ferconfig.json`
\ No newline at end of file
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..bdb2ef8
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,11 @@
+package config
+
+import (
+ "os"
+ "path/filepath"
+)
+
+// FerConfigPath :nodoc:
+func FerConfigPath() string {
+ return filepath.Join(os.Getenv("HOME"), ".ferconfig.json")
+}
diff --git a/config/default.go b/config/default.go
index e95029a..8998f67 100644
--- a/config/default.go
+++ b/config/default.go
@@ -18,7 +18,8 @@ const (
RichgoInstallerURL = "github.com/kyoh86/richgo"
// GolintInstallerURL :nodoc:
GolintInstallerURL = "github.com/golangci/golangci-lint/cmd/golangci-lint"
- ProtobufVersion = "3.7.1"
+ // ProtobufVersion :nodoc:
+ ProtobufVersion = "3.7.1"
// ProtobufOSXInstallerURL :nodoc:
ProtobufOSXInstallerURL = "https://github.com/protocolbuffers/protobuf/releases/download/v3.7.1/protoc-3.7.1-osx-x86_64.zip"
// ProtobufLinuxInstallerURL :nodoc:
diff --git a/config/ferconfig.go b/config/ferconfig.go
new file mode 100644
index 0000000..239a90a
--- /dev/null
+++ b/config/ferconfig.go
@@ -0,0 +1,69 @@
+package config
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "sync"
+
+ log "github.com/sirupsen/logrus"
+)
+
+var cfg FerConfig
+var once sync.Once
+
+// GocekConfig :nodoc:
+type GocekConfig struct {
+ ProjectDirs []string `json:"project_directories"`
+ SaveOutputDir string `json:"output_save_folder"`
+}
+
+// FerConfig :nodoc:
+type FerConfig struct {
+ Gocek GocekConfig `json:"gocek"`
+}
+
+func init() {
+ once.Do(func() {
+ loadCfg()
+ })
+}
+
+func loadCfg() {
+ cfgPath := FerConfigPath()
+ _, err := os.Stat(cfgPath)
+ switch {
+ case os.IsNotExist(err):
+ f, err := os.Create(cfgPath)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+
+ cfg.Gocek.ProjectDirs = make([]string, 0)
+ bt, _ := json.MarshalIndent(cfg, "", " ")
+ _, err = f.Write([]byte(bt))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ case err == nil:
+ b, err := ioutil.ReadFile(FerConfigPath())
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(b, &cfg)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ default:
+ log.Fatal(err)
+ }
+}
+
+// GetFerConfig :nodoc:
+func GetFerConfig() FerConfig {
+ return cfg
+}
diff --git a/config/version.go b/config/version.go
index b12b6b7..915eeb0 100644
--- a/config/version.go
+++ b/config/version.go
@@ -1,4 +1,4 @@
package config
// Version define version of fer
-const Version = "v1.5.4"
+const Version = "v1.6.0"
diff --git a/console/gocek.go b/console/gocek.go
new file mode 100644
index 0000000..9d3a3f1
--- /dev/null
+++ b/console/gocek.go
@@ -0,0 +1,39 @@
+package console
+
+import (
+ "github.com/kumparan/fer/config"
+ "github.com/kumparan/fer/gocek"
+ "github.com/spf13/cobra"
+)
+
+func init() {
+ gocekCmd.AddCommand(gocekAllCmd)
+ rootCmd.AddCommand(gocekCmd)
+}
+
+var gocekCmd = &cobra.Command{
+ Use: "gocek",
+ Short: "gocek check a module update info",
+ Long: `default check current working directory`,
+ Run: gocekCWD,
+}
+
+var gocekAllCmd = &cobra.Command{
+ Use: "all",
+ Short: "check all module update info",
+ Run: gocekAll,
+}
+
+func gocekAll(cmd *cobra.Command, args []string) {
+ cfg := config.GetFerConfig()
+ checker := gocek.ModuleChecker{
+ RootDir: cfg.Gocek.SaveOutputDir,
+ }
+
+ checker.Checks(cfg.Gocek.ProjectDirs)
+}
+
+func gocekCWD(cmd *cobra.Command, args []string) {
+ checker := gocek.ModuleChecker{}
+ checker.CheckCWD()
+}
diff --git a/go.mod b/go.mod
index a1dc256..6c8ee2f 100644
--- a/go.mod
+++ b/go.mod
@@ -10,9 +10,10 @@ require (
github.com/hashicorp/go-version v1.2.0
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365
github.com/kr/pretty v0.1.0 // indirect
- github.com/magiconair/properties v1.8.0
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
+ github.com/olekukonko/tablewriter v0.0.4
+ github.com/sirupsen/logrus v1.5.0
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.4.0
diff --git a/go.sum b/go.sum
index 6d768cf..f91c5b8 100644
--- a/go.sum
+++ b/go.sum
@@ -30,6 +30,8 @@ github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 h1:ECW73yc9MY79
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -45,15 +47,21 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
+github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
+github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q=
+github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
@@ -78,6 +86,7 @@ github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f h1:hjzMYz/7Ea1mNKfOnFOfktR0mlA5jqhvywClCMHM/qw=
diff --git a/gocek/checker.go b/gocek/checker.go
new file mode 100644
index 0000000..910c44e
--- /dev/null
+++ b/gocek/checker.go
@@ -0,0 +1,205 @@
+package gocek
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "path"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/olekukonko/tablewriter"
+ log "github.com/sirupsen/logrus"
+)
+
+const (
+ _defaultMaxQueueSize = int(10)
+)
+
+// ModuleChecker :nodoc:
+type ModuleChecker struct {
+ RootDir string
+}
+
+// CheckCWD call CheckCWD() and print into stdout
+func (mc *ModuleChecker) CheckCWD() {
+ modlist, err := CheckCWD()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetHeader([]string{"Path", "Version", "Next Version"})
+
+ for _, v := range modlist {
+ table.Append([]string{v.Path, v.Version, v.NextVersion})
+ }
+ table.Render() // Send output
+}
+
+// Checks check module projects and save the json file
+func (mc *ModuleChecker) Checks(dirs []string) {
+ for _, dir := range dirs {
+ os.Chdir(dir)
+ modules, err := CheckCWD()
+ if err != nil {
+ continue
+ }
+
+ sdir := strings.Split(dir, "/")
+ err = mc.save(sdir[len(sdir)-1], modules)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+ os.Chdir(mc.RootDir)
+ }
+}
+
+// save modules as json
+func (mc *ModuleChecker) save(modName string, modules []*SimpleModule) error {
+ now := time.Now()
+ layout := "2006-01-02"
+ fileName := fmt.Sprintf("%s.%s.json", modName, now.Format(layout))
+ dst := path.Join(mc.RootDir, fileName)
+
+ f, err := os.Create(dst)
+ if err != nil {
+ log.Error(err)
+ return err
+ }
+ defer f.Close()
+
+ log.Infof("saving %s", fileName)
+
+ j, err := json.MarshalIndent(modules, " ", " ")
+ if err != nil {
+ return err
+ }
+
+ _, err = f.Write([]byte(j))
+ return err
+}
+
+// CheckCWD check current working directory
+func CheckCWD() (modules []*SimpleModule, err error) {
+ modList, err := findDirectModList()
+ if err != nil {
+ log.Error(err)
+ return
+ }
+
+ return findAllModuleUpdate(modList)
+}
+
+func findAllModuleUpdate(mods []string) (modules []*SimpleModule, err error) {
+ queue := make(map[int][]string)
+ count := 0
+
+ // group the module per QueueSize,
+ // so each group can be conccurently execute
+ for _, m := range mods {
+ queue[count] = append(queue[count], m)
+ if len(queue[count]) == _defaultMaxQueueSize {
+ count++
+ }
+ }
+
+ modsCh := make(chan *Module, len(mods))
+ for _, q := range queue {
+ wg := sync.WaitGroup{}
+
+ wg.Add(len(q))
+ for _, m := range q {
+ go func(m string) {
+ defer wg.Done()
+
+ mod, err := findModuleUpdate(m)
+ if err != nil {
+ log.Error(err)
+ return
+ }
+
+ modsCh <- mod
+ }(m)
+ }
+
+ wg.Wait()
+ }
+
+ close(modsCh)
+ for module := range modsCh {
+ if module.Update.Version == "" {
+ continue
+ }
+ modules = append(modules, &SimpleModule{
+ Path: module.Path,
+ Version: module.Version,
+ NextVersion: module.Update.Version,
+ })
+ }
+
+ return
+}
+
+func findModuleUpdate(modName string) (*Module, error) {
+ var err error
+ out, err := exec.Command("go", "list", "-m", "-u", "-json", modName).Output()
+ if err != nil {
+ log.WithField("mod", modName).Error(err)
+ return nil, err
+ }
+
+ m := Module{}
+ if err = json.Unmarshal(out, &m); err != nil {
+ log.WithField("out", string(out)).Error(err)
+ return nil, err
+ }
+
+ return &m, nil
+}
+
+func findModList() ([]string, error) {
+ out, err := exec.Command("go", "list", "-m", "all").Output()
+ if err != nil {
+ return nil, err
+ }
+ splitted := strings.Split(string(out), "\n")
+
+ var list []string
+ for _, s := range splitted {
+ ss := sanitize(s)
+ if ss == "" {
+ continue
+ }
+ list = append(list, ss)
+ }
+
+ return list, nil
+}
+
+func findDirectModList() ([]string, error) {
+ out, err := exec.Command("go", "list", "-m", "-f", `{{ .Path }} | {{ .Indirect }}`, "all").Output()
+ if err != nil {
+ return nil, err
+ }
+ splitted := strings.Split(string(out), "\n")
+
+ var list []string
+ for _, ss := range splitted {
+ if strings.Trim(ss, " ") == "" || strings.Contains(ss, "true") {
+ continue
+ }
+
+ list = append(list, strings.Split(ss, " | ")[0])
+ }
+
+ return list, nil
+}
+
+func sanitize(raw string) string {
+ clean := strings.Trim(raw, " ")
+ return strings.Split(clean, " ")[0]
+}
diff --git a/gocek/mod.go b/gocek/mod.go
new file mode 100644
index 0000000..c19719b
--- /dev/null
+++ b/gocek/mod.go
@@ -0,0 +1,25 @@
+package gocek
+
+import "time"
+
+// Module :nodoc:
+type Module struct {
+ Path string `json:"Path"`
+ Version string `json:"Version"`
+ Time time.Time `json:"Time"`
+ Update struct {
+ Path string `json:"Path"`
+ Version string `json:"Version"`
+ Time time.Time `json:"Time"`
+ } `json:"Update"`
+ Indirect bool `json:"Indirect"`
+ Dir string `json:"Dir"`
+ GoMod string `json:"GoMod"`
+}
+
+// SimpleModule :nodoc:
+type SimpleModule struct {
+ Path string `json:"path"`
+ Version string `json:"version"`
+ NextVersion string `json:"next_version"`
+}