package dependencies import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "github.com/blang/semver" "github.com/juju/errors" log "github.com/sirupsen/logrus" ) // minimum minor required for the app to work const minimumSwaggerVersion = "0.29.0" // installation directory for fresh install. // will be prefixed by $HOME const defaultSwaggerInstallDir = "/bin" var regexSwaggerVersion = regexp.MustCompile(`^version: v(\d+\.\d+\.\d+)`) type Swagger struct{} // regroup all swagger dependencies function // GetName func (s Swagger) GetName() string { return "Go Swagger" } // GetMinimumVersion func (s Swagger) GetMinimumVersion() string { return minimumSwaggerVersion } // IsInstalled func (s Swagger) IsInstalled() (bool, error) { _, err := s.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 (s Swagger) GetBinaryPath() (string, error) { log.Debug("looking for swagger binary") path, err := exec.LookPath("swagger") if err != nil { return "", errors.Trace(err) } log.Debug("found swagger binary in", path) return path, nil } // GetVersion func (s Swagger) GetVersion() (string, error) { isInstalled, err := s.IsInstalled() if err != nil { return "", errors.Trace(err) } if !isInstalled { return "", errors.NotFoundf("swagger is not installed on the system") } binaryPath, err := s.GetBinaryPath() if err != nil { return "", errors.Trace(err) } log.Debug("executing swagger version command") cmd := exec.Command(binaryPath, "version") stdout, err := cmd.Output() if err != nil { return "", errors.Trace(err) } cleanOut := strings.TrimSpace(string(stdout)) log.Debugf("swagger version returned %s", cleanOut) parseOutput := regexSwaggerVersion.FindStringSubmatch(cleanOut) if len(parseOutput) != 2 { return "", errors.NotSupportedf("failed to parse swagger version output: %s", cleanOut) } return parseOutput[1], nil } // IsVersionSupported func (s Swagger) IsVersionSupported() (bool, error) { isInstalled, err := s.IsInstalled() if err != nil { return false, errors.Trace(err) } if !isInstalled { return false, nil } version, err := s.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(minimumSwaggerVersion) if err != nil { return false, errors.Trace(err) } if installedVersion.LT(requiredVersion) { return false, nil } return true, nil } // CanBeInstalled func (s Swagger) CanBeInstalled() bool { return true } // DescribeInstall func (s Swagger) DescribeInstall(path string) string { commands := []string{ "The following commands will be executed", fmt.Sprintf("rm -rf %s/swagger ", path), fmt.Sprintf("wget %s -O %s/swagger", s.getDownloadUrl(), path), fmt.Sprintf("chmod 644 %s/swagger", path), } return strings.Join(commands, "\n") } // Install func (s Swagger) Install(path string) error { downloadUrl := s.getDownloadUrl() content, err := downloadFile(downloadUrl) if err != nil { log.Warnf("fail to download file from %s", downloadUrl) return errors.Trace(err) } fh, err := os.OpenFile( filepath.Join(path, "swagger"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0744, ) if err != nil { return errors.Trace(err) } defer fh.Close() bContent, err := ioutil.ReadAll(content) if err != nil { return errors.Trace(err) } _, err = fh.Write(bContent) if err != nil { return errors.Trace(err) } return nil } // DescribePostInstall func (s Swagger) DescribePostInstall(path string) string { return `For your environment to work correctly, we will add if needed the following environment variable to your .bashrc: PATH=\"$HOME/bin:$PATH\" You will have to reopen a new terminal to apply the changes or execute the following command: source $HOME/.bashrc ` } // PostInstall func (s Swagger) PostInstall(path string) error { lineBashRc := []string{ "# Swagger - Added by gouick", } // checking if swagger binary is found after installation // if not it's probably not in the path isInstalled, err := s.IsInstalled() if err != nil { return errors.Trace(err) } if !isInstalled { lineBashRc = append(lineBashRc, "PATH=\"$HOME/bin:$PATH\"") } homeDir, err := os.UserHomeDir() if err != nil { return errors.Trace(err) } if len(lineBashRc) > 1 { log.Debug("Adding env variable to .bashrc") fh, err := os.OpenFile( filepath.Join(homeDir, ".bashrc"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644, ) if err != nil { return errors.Trace(err) } defer fh.Close() _, err = fh.WriteString( fmt.Sprintf("\n\n%s\n", strings.Join(lineBashRc, "\n")), ) if err != nil { return errors.Trace(err) } } return nil } // GetInstallDirectory will try to find the current swagger 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 (s Swagger) GetInstallDirectory() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { return "", errors.Trace(err) } isInstalled, err := s.IsInstalled() if err != nil { return "", errors.Trace(err) } // concat default install dir with home and we have our path if !isInstalled { return filepath.Join(homeDir, defaultSwaggerInstallDir), nil } // now let's play and find the current install path binaryPath, err := s.GetBinaryPath() if err != nil { return "", errors.Trace(err) } return strings.TrimSuffix(binaryPath, "/swagger"), nil } func (s Swagger) getDownloadUrl() string { return fmt.Sprintf("https://github.com/go-swagger/go-swagger/releases/download/v%s/swagger_%s_%s", minimumSwaggerVersion, runtime.GOOS, runtime.GOARCH) }