Skip to content

Commit

Permalink
Check for outdated minectl cli version (#124)
Browse files Browse the repository at this point in the history
Signed-off-by: Engin Diri <[email protected]>
  • Loading branch information
Engin Diri authored Aug 12, 2021
1 parent b163159 commit a81e0cf
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 29 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ Apache License, Version 2.0
- https://github.com/c-bata/go-prompt
- https://github.com/vultr/govultr
- https://github.com/Azure/azure-sdk-for-go
- https://github.com/blang/semver
- https://github.com/tcnksm/go-latest

### Legal Disclaimer 👮

Expand Down
10 changes: 5 additions & 5 deletions cmd/minectl/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package minectl

import (
"fmt"
"log"
"os"

"github.com/minectl/pkg/common"
Expand All @@ -24,7 +23,7 @@ var createCmd = &cobra.Command{
Short: "Create an Minecraft Server.",
Example: `mincetl create \
--filename server-do.yaml`,
RunE: runCreate,
RunE: RunFunc(runCreate),
SilenceUsage: true,
SilenceErrors: true,
}
Expand All @@ -39,15 +38,16 @@ func runCreate(cmd *cobra.Command, _ []string) error {
}
p, err := provisioner.NewProvisioner(filename)
if err != nil {
log.Fatal(err)
fmt.Println(err)
}
wait := true
if cmd.Flags().Changed("wait") {
wait, _ = cmd.Flags().GetBool("wait")
}
res, err := p.CreateServer(wait)
if err != nil {
log.Fatal(err)
fmt.Println(err)
return nil
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"ID", "NAME", "REGION", "TAGS", "IP"})
Expand All @@ -64,5 +64,5 @@ func runCreate(cmd *cobra.Command, _ []string) error {
common.PrintMixedGreen("⤴️ To upload a plugin type:\n\n %s",
fmt.Sprintf("minectl plugins -f %s --id %s --plugin <folder>/x.jar --destination /minecraft/plugins\n", filename, res.ID))
common.PrintMixedGreen("\n🔌 Connected to RCON type:\n\n %s", fmt.Sprintf("minectl rcon -f %s --id %s\n", filename, res.ID))
return err
return nil
}
7 changes: 5 additions & 2 deletions cmd/minectl/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var deleteCmd = &cobra.Command{
--filename server-do.yaml
--id xxx-xxx-xxx-xxx
`,
RunE: runDelete,
RunE: RunFunc(runDelete),
SilenceUsage: true,
SilenceErrors: true,
}
Expand All @@ -48,5 +48,8 @@ func runDelete(cmd *cobra.Command, _ []string) error {
return err
}
err = newProvisioner.DeleteServer()
return err
if err != nil {
return err
}
return nil
}
4 changes: 2 additions & 2 deletions cmd/minectl/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var listCmd = &cobra.Command{
Example: `mincetl list \
--provider civo \
--region LON1`,
RunE: runList,
RunE: RunFunc(runList),
SilenceUsage: true,
SilenceErrors: true,
}
Expand Down Expand Up @@ -62,7 +62,7 @@ func runList(cmd *cobra.Command, _ []string) error {
table.SetBorder(false)
table.Render()
} else {
fmt.Println("🤷 No server found")
return errors.New("🤷 No server found")
}
return nil
}
201 changes: 198 additions & 3 deletions cmd/minectl/minectl.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package minectl

import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"

"github.com/Azure/go-autorest/autorest/to"

"github.com/blang/semver/v4"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/tcnksm/go-latest"
)

var (
Expand All @@ -18,10 +29,196 @@ func init() {
minectlCmd.AddCommand(versionCmd)
}

var updateCheckResult chan *string

var minectlCmd = &cobra.Command{
Use: "minectl",
Short: "Create Minecraft Server on different cloud provider.",
Run: runMineCtl,
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: false,
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("PersistentPostRun")
},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
var waitForUpdateCheck bool
defer func() {
if !waitForUpdateCheck {
close(updateCheckResult)
}
}()

updateCheckResult = make(chan *string)
waitForUpdateCheck = true
go func() {
updateCheckResult <- checkForUpdate()
close(updateCheckResult)
}()
return nil
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
checkVersionMsg, ok := <-updateCheckResult
if ok && checkVersionMsg != nil {
fmt.Println()
fmt.Println(to.String(checkVersionMsg))
}
return nil
},
}

func isDevVersion(s semver.Version) bool {
if len(s.Pre) == 0 {
return false
}

devStrings := regexp.MustCompile(`alpha|beta|dev|rc`)
return !s.Pre[0].IsNum && devStrings.MatchString(s.Pre[0].VersionStr)
}

func isBrewInstall(exe string) (bool, error) {
if runtime.GOOS != "darwin" {
return false, nil
}

exePath, err := filepath.EvalSymlinks(exe)
if err != nil {
return false, err
}

brewBin, err := exec.LookPath("brew")
if err != nil {
return false, err
}

brewPrefixCmd := exec.Command(brewBin, "--prefix", "minectl")

var stdout bytes.Buffer
var stderr bytes.Buffer
brewPrefixCmd.Stdout = &stdout
brewPrefixCmd.Stderr = &stderr
if err = brewPrefixCmd.Run(); err != nil {
if ee, ok := err.(*exec.ExitError); ok {
ee.Stderr = stderr.Bytes()
}
return false, errors.Wrapf(err, "'brew --prefix minectl' failed")
}

brewPrefixCmdOutput := strings.TrimSpace(stdout.String())
if brewPrefixCmdOutput == "" {
return false, errors.New("trimmed output from 'brew --prefix minectl' is empty")
}

brewPrefixPath, err := filepath.EvalSymlinks(brewPrefixCmdOutput)
if err != nil {
return false, err
}

brewPrefixExePath := filepath.Join(brewPrefixPath, "minectl")
return exePath == brewPrefixExePath, nil
}

func runPostCommandHooks(c *cobra.Command, args []string) error {
if c.PostRunE != nil {
if err := c.PostRunE(c, args); err != nil {
return err
}
} else if c.PostRun != nil {
c.PostRun(c, args)
}
for p := c; p != nil; p = p.Parent() {
if p.PersistentPostRunE != nil {
if err := p.PersistentPostRunE(c, args); err != nil {
return err
}
break
} else if p.PersistentPostRun != nil {
p.PersistentPostRun(c, args)
break
}
}
return nil
}

func RunFunc(run func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error {
if res := run(cmd, args); res != nil {
fmt.Println(res)
if postRunErr := runPostCommandHooks(cmd, args); postRunErr != nil {
fmt.Println(res)
}
os.Exit(1)
}
os.Exit(0)
return nil
}
}

func getUpgradeCommand() string {
exe, err := os.Executable()
if err != nil {
return ""
}

isBrew, err := isBrewInstall(exe)
if err != nil {
fmt.Printf("error determining if the running executable was installed with brew: %s", err)
}
if isBrew {
return "$ brew upgrade minectl"
}

if runtime.GOOS != "windows" {
return "$ curl -sSL https://get.minectl.dev | sh"
}
return ""
}

func getUpgradeMessage(latest *semver.Version, current *semver.Version) *string {
cmd := getUpgradeCommand()
msg := fmt.Sprintf("A new version of minectl is available. To upgrade from version '%s' to '%s', ", current, latest)
if cmd != "" {
msg += "run \n " + cmd + "\n\nor "
}

msg += "visit https://github.com/dirien/minectl#installing-minectl- for manual instructions."
return &msg
}

func getCLIVersionInfo(current *semver.Version) (*semver.Version, error) {
githubTag := &latest.GithubTag{
Owner: "dirien",
Repository: "minectl",
}

res, err := latest.Check(githubTag, current.String())
if err != nil {
return nil, err
}
version, err := semver.New(res.Current)
if err != nil {
return nil, err
}
return version, nil
}

func checkForUpdate() *string {
curVer, err := semver.ParseTolerant(getVersion())
if err != nil {
fmt.Printf("error parsing current version: %s", err)
}
if isDevVersion(curVer) {
return nil
}
latestVer, err := getCLIVersionInfo(&curVer)
if err != nil {
return nil
}
if latestVer.GT(curVer) {
return getUpgradeMessage(latestVer, &curVer)
}

return nil
}

var versionCmd = &cobra.Command{
Expand All @@ -34,16 +231,14 @@ func getVersion() string {
if len(Version) != 0 {
return Version
}
return "dev"
return "0.1.0-dev"
}

func parseBaseCommand(_ *cobra.Command, _ []string) {
printLogo()

fmt.Println("Version:", getVersion())
fmt.Println("Git Commit:", GitCommit)
fmt.Println("Build date:", Date)
os.Exit(0)
}

func Execute(version, gitCommit, date string) error {
Expand Down
6 changes: 2 additions & 4 deletions cmd/minectl/plugins.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package minectl

import (
"log"

"github.com/minectl/pkg/provisioner"
"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -39,7 +37,7 @@ var pluginCmd = &cobra.Command{
--id xxx-xxx-xxx-xxx
--plugin plugin.jar
--destination /minecraft/mods`,
RunE: runPlugin,
RunE: RunFunc(runPlugin),
SilenceUsage: true,
SilenceErrors: true,
}
Expand Down Expand Up @@ -71,7 +69,7 @@ func runPlugin(cmd *cobra.Command, _ []string) error {
}
p, err := provisioner.NewProvisioner(filename, id)
if err != nil {
log.Fatal(err)
return err
}
plugin, _ := cmd.Flags().GetString("plugin")
destination, _ := cmd.Flags().GetString("destination")
Expand Down
8 changes: 3 additions & 5 deletions cmd/minectl/rcon.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package minectl

import (
"log"

"github.com/minectl/pkg/provisioner"
"github.com/pkg/errors"
"github.com/spf13/cobra"
Expand All @@ -20,7 +18,7 @@ var rconCmd = &cobra.Command{
Example: `mincetl rcon \
--filename server-do.yaml \
--id xxxx`,
RunE: runRCON,
RunE: RunFunc(runRCON),
SilenceUsage: true,
SilenceErrors: true,
}
Expand All @@ -42,11 +40,11 @@ func runRCON(cmd *cobra.Command, _ []string) error {
}
p, err := provisioner.NewProvisioner(filename, id)
if err != nil {
log.Fatal(err)
return err
}
err = p.DoRCON()
if err != nil {
log.Fatal(err)
return err
}
return nil
}
Loading

0 comments on commit a81e0cf

Please sign in to comment.