feat(dependencies): Implement base of the dependency system
This commit is contained in:
commit
f04f5513ab
36
cmd/generate.go
Normal file
36
cmd/generate.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.home.m-and-m.ovh/mderasse/boot/helpers"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateCmd represents the generate command
|
||||||
|
var generateCmd = &cobra.Command{
|
||||||
|
Use: "generate",
|
||||||
|
Short: "Generate all the files (API, DB, ...)",
|
||||||
|
Long: `Generate will automatically create all the files of the Project.
|
||||||
|
You can also generate files for each 'modules' by using the command bellows`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
if isStrap, err := helpers.IsStrapProject(); err != nil {
|
||||||
|
|
||||||
|
fmt.Print("Fail to check if we are in a Strap Directory")
|
||||||
|
|
||||||
|
} else if !isStrap {
|
||||||
|
fmt.Printf("You are not in a Strap Project directory\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("generate called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(generateCmd)
|
||||||
|
}
|
24
cmd/generateAPI.go
Normal file
24
cmd/generateAPI.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// apiCmd represents the api command
|
||||||
|
var apiCmd = &cobra.Command{
|
||||||
|
Use: "api",
|
||||||
|
Short: "Generate Go-Swagger Files and Controllers",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("api called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(apiCmd)
|
||||||
|
}
|
24
cmd/generateDB.go
Normal file
24
cmd/generateDB.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dbCmd represents the db command
|
||||||
|
var dbCmd = &cobra.Command{
|
||||||
|
Use: "db",
|
||||||
|
Short: "Generate Database Struct from pkg/dbmodels/schema.yaml",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("db called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(dbCmd)
|
||||||
|
}
|
24
cmd/generateDocker.go
Normal file
24
cmd/generateDocker.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dockerCmd represents the docker command
|
||||||
|
var dockerCmd = &cobra.Command{
|
||||||
|
Use: "docker",
|
||||||
|
Short: "Generate Dockerfile for API & Workers",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("docker called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(dockerCmd)
|
||||||
|
}
|
24
cmd/generateHelm.go
Normal file
24
cmd/generateHelm.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helmCmd represents the helm command
|
||||||
|
var helmCmd = &cobra.Command{
|
||||||
|
Use: "helm",
|
||||||
|
Short: "Generate Kubernetes Helm chart for API & Workers",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("helm called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(helmCmd)
|
||||||
|
}
|
25
cmd/generateHelp.go
Normal file
25
cmd/generateHelp.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helpCmd represents the help command
|
||||||
|
var helpCmd = &cobra.Command{
|
||||||
|
Use: "help",
|
||||||
|
Short: "Show that help",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := generateCmd.Help()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(helpCmd)
|
||||||
|
}
|
24
cmd/generateLauncher.go
Normal file
24
cmd/generateLauncher.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// launcherCmd represents the launcher command
|
||||||
|
var launcherCmd = &cobra.Command{
|
||||||
|
Use: "launcher",
|
||||||
|
Short: "Generate Launcher Bash script",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("launcher called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(launcherCmd)
|
||||||
|
}
|
24
cmd/generateMakefile.go
Normal file
24
cmd/generateMakefile.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// makefileCmd represents the makefile command
|
||||||
|
var makefileCmd = &cobra.Command{
|
||||||
|
Use: "makefile",
|
||||||
|
Short: "Generate Makefile",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("makefile called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
generateCmd.AddCommand(makefileCmd)
|
||||||
|
}
|
61
cmd/init.go
Normal file
61
cmd/init.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initCmd represents the init command
|
||||||
|
var initCmd = &cobra.Command{
|
||||||
|
Use: "init",
|
||||||
|
Short: "Initialize a new project",
|
||||||
|
Long: `Initialize a new project by creating a new directory and:
|
||||||
|
- Initialize a Git Repository
|
||||||
|
- Create a Go-Swagger Struct
|
||||||
|
- Add default routes
|
||||||
|
- Generate the API.
|
||||||
|
|
||||||
|
That command is Interactive or can be control through args`,
|
||||||
|
Run: runInitAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(initCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runInitAction(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
log.Debugf("Starting command Init")
|
||||||
|
|
||||||
|
log.Debugf("Checking dependecies")
|
||||||
|
|
||||||
|
// isDependeciesOk, err := helpers.CheckDependencies()
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error(err.Error())
|
||||||
|
// return
|
||||||
|
// } else if isDependeciesOk {
|
||||||
|
// log.Error("Dependencies are not repescted")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if isGoProject, err := helpers.IsGoProject(); err != nil {
|
||||||
|
// log.Error("An error occured when checking your directory.")
|
||||||
|
// return
|
||||||
|
// } else if isGoProject {
|
||||||
|
// log.Error("Cannot initialize a new project because you are already in a Golang Project Directory.")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if isStrapProject, err := helpers.IsStrapProject(); err != nil {
|
||||||
|
// log.Error("An error occured when checking your directory.")
|
||||||
|
// return
|
||||||
|
// } else if isStrapProject {
|
||||||
|
// log.Error("Cannot initialize a new project because you are already in a Strap Project Directory.")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
}
|
51
cmd/root.go
Normal file
51
cmd/root.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var verbose = false
|
||||||
|
var acceptAll = false
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "boot",
|
||||||
|
Short: "Boot is a toolbox app to bootstrap quickly a Go-Swagger API",
|
||||||
|
Long: `Boot is a toolbox app to bootstrap quickly a Go-Swagger API.
|
||||||
|
|
||||||
|
It will:
|
||||||
|
- Auto-Update
|
||||||
|
- Download dependencies (go-swagger, ...)
|
||||||
|
- Initialize an API with default middlewares
|
||||||
|
- Generate Models / Controlers
|
||||||
|
- Generate Database structs
|
||||||
|
- ....`,
|
||||||
|
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
if verbose {
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "enable verbose logging")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&acceptAll, "yes", "y", false, "accept all change, disable interactive process")
|
||||||
|
}
|
24
cmd/test.go
Normal file
24
cmd/test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// testCmd represents the test command
|
||||||
|
var testCmd = &cobra.Command{
|
||||||
|
Use: "test",
|
||||||
|
Short: "Launch Test",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("test called")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(testCmd)
|
||||||
|
}
|
116
cmd/upgrade.go
Normal file
116
cmd/upgrade.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"git.home.m-and-m.ovh/mderasse/boot/helpers"
|
||||||
|
"git.home.m-and-m.ovh/mderasse/boot/helpers/dependencies"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// upgradeCmd represents the upgrade command
|
||||||
|
var upgradeCmd = &cobra.Command{
|
||||||
|
Use: "upgrade",
|
||||||
|
Short: "Upgrade all development dependencies",
|
||||||
|
Long: `Upgrade the following dependencies:
|
||||||
|
- Golang
|
||||||
|
- Go Swagger
|
||||||
|
- ...
|
||||||
|
`,
|
||||||
|
Run: runUpgradeAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(upgradeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUpgradeAction(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
dependencies := []dependencies.Dependency{dependencies.Golang{}}
|
||||||
|
|
||||||
|
for _, dependency := range dependencies {
|
||||||
|
|
||||||
|
log.Debugf("Checking if dependency %s is supported", dependency.GetName())
|
||||||
|
|
||||||
|
isSupported, err := dependency.IsVersionSupported()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Fail to check %s version. The following error happen: %s", dependency.GetName(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSupported {
|
||||||
|
log.Infof("%s version is supported, continue", dependency.GetName())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Your %s is not supported.", dependency.GetName())
|
||||||
|
|
||||||
|
if !acceptAll {
|
||||||
|
log.Infof("Do you want to install the following version: %s", dependency.GetMinimumVersion())
|
||||||
|
|
||||||
|
answer := helpers.YesOrNoInput()
|
||||||
|
if !answer {
|
||||||
|
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Trying to find the best installation directory")
|
||||||
|
|
||||||
|
installDirectory, err := dependency.GetInstallDirectory()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Fail to compute the %s install directory. The following error happen: %s", dependency.GetName(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask user if the path is correct, else ask new path and check validity
|
||||||
|
if !acceptAll {
|
||||||
|
log.Infof("Do you want to install %s in the directory %s ?", dependency.GetName(), installDirectory)
|
||||||
|
|
||||||
|
answer := helpers.YesOrNoInput()
|
||||||
|
if !answer {
|
||||||
|
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
||||||
|
installDirectory = helpers.IsValidPathInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Infof("Installing %s in the following directory: %s", dependency.GetName(), installDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Checking directory and creating it if needed")
|
||||||
|
|
||||||
|
// check if the path is ok, else ask user for new path if we are in a interactive mode, else stop
|
||||||
|
err = helpers.CheckAndCreateDir(installDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Impossible to create the instalation directory for %s", dependency.GetName())
|
||||||
|
if acceptAll {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Infof("Where do you want to install %s ?", dependency.GetName())
|
||||||
|
installDirectory = helpers.IsValidPathInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("The following command will be executed:\n%s", dependency.DescribeInstall(installDirectory))
|
||||||
|
log.Info("Do you want to continue ?")
|
||||||
|
answer := helpers.YesOrNoInput()
|
||||||
|
if !answer {
|
||||||
|
log.Warnf("Skipping installation of %s. Some part of the application might not be able to work correctly!", dependency.GetName())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Starting the installation")
|
||||||
|
|
||||||
|
err = dependency.Install(installDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Fail to install %s version. The following error happen: %s", dependency.GetName(), err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("%s successfully installed", dependency.GetName())
|
||||||
|
}
|
||||||
|
}
|
13
go.mod
Normal file
13
go.mod
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module git.home.m-and-m.ovh/mderasse/boot
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||||
|
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b // indirect
|
||||||
|
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||||
|
github.com/spf13/cobra v1.4.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
|
||||||
|
)
|
21
go.sum
Normal file
21
go.sum
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||||
|
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b h1:AxFeSQJfcm2O3ov1wqAkTKYFsnMw2g1B4PkYujfAdkY=
|
||||||
|
github.com/juju/errors v0.0.0-20220331221717-b38fca44723b/go.mod h1:jMGj9DWF/qbo91ODcfJq6z/RYc3FX3taCBZMCcpI4Ls=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||||
|
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
3
helpers/constant.go
Normal file
3
helpers/constant.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
const configFile = ".strap.yaml"
|
214
helpers/dependencies/golang.go
Normal file
214
helpers/dependencies/golang.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package dependencies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/blang/semver"
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"git.home.m-and-m.ovh/mderasse/boot/helpers"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// minimum minor required for the app to work
|
||||||
|
const minimumGolangVersion = "1.18.4"
|
||||||
|
|
||||||
|
// installation directory for fresh install.
|
||||||
|
// will be prefixed by $HOME
|
||||||
|
const defaultGolangInstallDir = "/local/go"
|
||||||
|
|
||||||
|
var regexGolangVersion = regexp.MustCompile(`^go version go(\d+\.\d+\.\d+)`)
|
||||||
|
|
||||||
|
type Golang struct{}
|
||||||
|
|
||||||
|
// regroup all golang dependencies function
|
||||||
|
|
||||||
|
// GetName
|
||||||
|
func (g Golang) GetName() string {
|
||||||
|
return "Golang"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinimumVersion
|
||||||
|
func (g Golang) GetMinimumVersion() string {
|
||||||
|
return minimumGolangVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInstalled
|
||||||
|
func (g Golang) IsInstalled() (bool, error) {
|
||||||
|
|
||||||
|
_, err := g.GetBinaryPath()
|
||||||
|
if err != nil && !errors.Is(err, exec.ErrNotFound) {
|
||||||
|
return false, errors.Trace(err)
|
||||||
|
} else if err != nil && errors.Is(err, exec.ErrNotFound) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBinaryPath
|
||||||
|
func (g Golang) GetBinaryPath() (string, error) {
|
||||||
|
log.Debug("looking for golang binary")
|
||||||
|
|
||||||
|
path, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("found golang binary in", path)
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion return the major, minor and patch version of Golang
|
||||||
|
func (g Golang) GetVersion() (string, error) {
|
||||||
|
|
||||||
|
isInstalled, err := g.IsInstalled()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isInstalled {
|
||||||
|
return "", errors.NotFoundf("golang is not installed on the system")
|
||||||
|
}
|
||||||
|
|
||||||
|
golangPath, err := g.GetBinaryPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("executing go version command")
|
||||||
|
|
||||||
|
cmd := exec.Command(golangPath, "version")
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
cleanOut := strings.TrimSpace(string(stdout))
|
||||||
|
|
||||||
|
log.Debugf("go version returned %s", cleanOut)
|
||||||
|
|
||||||
|
parseOutput := regexGolangVersion.FindStringSubmatch(cleanOut)
|
||||||
|
if len(parseOutput) != 2 {
|
||||||
|
return "", errors.NotSupportedf("failed to parse golang version output: %s", cleanOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseOutput[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVersionSupported
|
||||||
|
func (g Golang) IsVersionSupported() (bool, error) {
|
||||||
|
|
||||||
|
version, err := g.GetVersion()
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
installedVersion, err := semver.Make(version)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredVersion, _ := semver.Make(minimumGolangVersion)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if installedVersion.LT(*&requiredVersion) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescribeInstall
|
||||||
|
func (g Golang) DescribeInstall(path string) string {
|
||||||
|
|
||||||
|
description := fmt.Sprintf("rm -rf %s/* ", path)
|
||||||
|
description = fmt.Sprintf("%s\ncurl %s | tar --strip-components=1 -C %s -zxf -", description, getDownloadUrl(), path)
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install
|
||||||
|
func (g Golang) Install(path string) error {
|
||||||
|
|
||||||
|
err := helpers.RemoveDirectoryContent(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("fail to delete content of directory %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadUrl := getDownloadUrl()
|
||||||
|
content, err := downloadFile(downloadUrl)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("fail to download file from %s", downloadUrl)
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gzipReader, err := unGzip(content)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("fail to un-gzip downloaded file from %s, error:", downloadUrl)
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: unTar should take a subdir
|
||||||
|
err = unTar(gzipReader, "go/", path)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("fail to un-tar downloaded file from %s", downloadUrl)
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstallDirectory will try to find the current golang directory. If it doesn't exist or if it's in a
|
||||||
|
// not userspace directory, it will provide the "default"
|
||||||
|
// It doesn't mean that the directory is "writable"
|
||||||
|
func (g Golang) GetInstallDirectory() (string, error) {
|
||||||
|
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isInstalled, err := g.IsInstalled()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// concat default install dir with home and we have our path
|
||||||
|
if !isInstalled {
|
||||||
|
return fmt.Sprintf("%s/%s", homeDir, defaultGolangInstallDir), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// now let's play and find the current install path
|
||||||
|
golangPath, err := g.GetBinaryPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("executing go env GOROOT command")
|
||||||
|
|
||||||
|
cmd := exec.Command(golangPath, "env", "GOROOT")
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Trace(err)
|
||||||
|
}
|
||||||
|
cleanOut := strings.TrimSpace(string(stdout))
|
||||||
|
|
||||||
|
if !strings.Contains(cleanOut, homeDir) {
|
||||||
|
return fmt.Sprintf("%s/%s", homeDir, defaultGolangInstallDir), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanOut, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDownloadUrl() string {
|
||||||
|
|
||||||
|
return fmt.Sprintf("https://dl.google.com/go/go%s.%s-%s.tar.gz", minimumGolangVersion, runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
14
helpers/dependencies/interface.go
Normal file
14
helpers/dependencies/interface.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package dependencies
|
||||||
|
|
||||||
|
// Dependency
|
||||||
|
type Dependency interface {
|
||||||
|
DescribeInstall(path string) string
|
||||||
|
GetBinaryPath() (string, error)
|
||||||
|
GetInstallDirectory() (string, error)
|
||||||
|
GetName() string
|
||||||
|
GetMinimumVersion() string
|
||||||
|
GetVersion() (string, error)
|
||||||
|
Install(path string) error
|
||||||
|
IsInstalled() (bool, error)
|
||||||
|
IsVersionSupported() (bool, error)
|
||||||
|
}
|
106
helpers/dependencies/utils.go
Normal file
106
helpers/dependencies/utils.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package dependencies
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// downloadFile will download file from a given url and store in memory the content
|
||||||
|
// content will probably be untar, ungzip, ....
|
||||||
|
func downloadFile(url string) (io.Reader, error) {
|
||||||
|
|
||||||
|
// Get the data
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Trace(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unGzip(reader io.Reader) (io.Reader, error) {
|
||||||
|
|
||||||
|
gzipReader, err := gzip.NewReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Trace(err)
|
||||||
|
}
|
||||||
|
defer gzipReader.Close()
|
||||||
|
|
||||||
|
return gzipReader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unTar(reader io.Reader, subdir string, dest string) error {
|
||||||
|
tr := tar.NewReader(reader)
|
||||||
|
|
||||||
|
if subdir != "" && !strings.HasSuffix(subdir, "/") {
|
||||||
|
subdir = fmt.Sprintf("%s/", subdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := tr.Next()
|
||||||
|
switch {
|
||||||
|
// no more files
|
||||||
|
case err == io.EOF:
|
||||||
|
return nil
|
||||||
|
case err != nil:
|
||||||
|
return errors.Trace(err)
|
||||||
|
case header == nil:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := header.Name
|
||||||
|
|
||||||
|
if subdir != "" && strings.HasPrefix(filename, subdir) {
|
||||||
|
|
||||||
|
filename = strings.Replace(filename, subdir, "", 1)
|
||||||
|
|
||||||
|
if filename == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target := filepath.Join(dest, filename)
|
||||||
|
|
||||||
|
log.Debugf("Extacting %s", target)
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
// create directory if doesn't exit
|
||||||
|
case tar.TypeDir:
|
||||||
|
if _, err := os.Stat(target); err != nil {
|
||||||
|
if err := os.MkdirAll(target, 0755); err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create file
|
||||||
|
case tar.TypeReg:
|
||||||
|
f, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// copy contents to file
|
||||||
|
if _, err := io.Copy(f, tr); err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
140
helpers/file.go
Normal file
140
helpers/file.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsStrapProject() (bool, error) {
|
||||||
|
|
||||||
|
currentPath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fileExists(fmt.Sprintf("%s/%s", currentPath, configFile)) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsGoProject() (bool, error) {
|
||||||
|
|
||||||
|
currentPath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileExists(fmt.Sprintf("%s/%s", currentPath, "go.mod")) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileExists(fmt.Sprintf("%s/%s", currentPath, "go.sum")) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(filename string) bool {
|
||||||
|
info, err := os.Stat(filename)
|
||||||
|
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDirectoryWritable
|
||||||
|
func isDirectoryWritable(path string) (bool, error) {
|
||||||
|
|
||||||
|
dirInfo, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
if errors.Is(err, fs.ErrPermission) {
|
||||||
|
return false, errors.NewForbidden(err, "directory is not readeable")
|
||||||
|
} else if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return false, errors.NewNotFound(err, "directory does not exit")
|
||||||
|
} else {
|
||||||
|
return false, errors.Trace(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dirInfo.IsDir() {
|
||||||
|
return false, errors.NotSupportedf("given path is not a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dirInfo.Mode().Perm()&(1<<(uint(7))) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createDirectory
|
||||||
|
func createDirectory(path string) error {
|
||||||
|
|
||||||
|
// no need to check if path exist
|
||||||
|
// MkdirAll will do it for us
|
||||||
|
err := os.MkdirAll(path, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckAndCreateDir will check if path is writable and create directory if needed
|
||||||
|
func CheckAndCreateDir(path string) error {
|
||||||
|
|
||||||
|
dirWritable, err := isDirectoryWritable(path)
|
||||||
|
if dirWritable {
|
||||||
|
return nil
|
||||||
|
} else if err != nil && !errors.Is(err, errors.NotFound) {
|
||||||
|
log.Warnf("impossible to check if the directory is writable")
|
||||||
|
return err
|
||||||
|
} else if err == nil && !dirWritable {
|
||||||
|
log.Warnf("directory is not writable")
|
||||||
|
return errors.Forbiddenf("directory is not writable")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createDirectory(path)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Warnf("impossible to create directory (%s), please try again", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveDirectoryContent(path string) error {
|
||||||
|
|
||||||
|
dir, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range dir {
|
||||||
|
|
||||||
|
err := os.RemoveAll(
|
||||||
|
filepath.Join(
|
||||||
|
[]string{"path", d.Name()}...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Trace(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
1
helpers/http.go
Normal file
1
helpers/http.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package helpers
|
58
helpers/input.go
Normal file
58
helpers/input.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// YesOrNoInput
|
||||||
|
func YesOrNoInput() bool {
|
||||||
|
|
||||||
|
var userInput string
|
||||||
|
for {
|
||||||
|
|
||||||
|
_, err := fmt.Scanf("%s", &userInput)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("failed to read input, try again (%s)", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lUserInput := strings.ToLower(userInput)
|
||||||
|
|
||||||
|
for _, positiveAnswer := range []string{"yes", "y", "1", "true"} {
|
||||||
|
if lUserInput == positiveAnswer {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, negativeAnswer := range []string{"no", "n", "0", "false"} {
|
||||||
|
if lUserInput == negativeAnswer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Expecting a yes or no answer, Try again")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidPathInput
|
||||||
|
func IsValidPathInput() string {
|
||||||
|
var userInput string
|
||||||
|
for {
|
||||||
|
|
||||||
|
_, err := fmt.Scanf("%s", &userInput)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("failed to read input, try again (%s)", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CheckAndCreateDir(userInput)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("please, try again")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInput
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user