package installer import ( "crypto/sha256" "encoding/hex" "fmt" "io" "io/ioutil" "log" "net/http" "os" "os/exec" "path/filepath" "regexp" "runtime" "github.com/mitchellh/go-homedir" "github.com/pivotal-golang/archiver/extractor" ) const ( version = "0.16" baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/" ) var caddy, bin, temp, hugo, tempfile, zipname, exename string // GetPath retrives the Hugo path for the user or install it if it's not found func GetPath() string { initializeVariables() var err error // Check if Hugo is already on $PATH if hugo, err = exec.LookPath("hugo"); err == nil { if checkVersion() { return hugo } } // Check if Hugo is on $HOME/.caddy/bin if _, err = os.Stat(hugo); err == nil { if checkVersion() { return hugo } } fmt.Println("Unable to find Hugo on your computer.") // Create the neccessary folders os.MkdirAll(caddy, 0774) os.Mkdir(bin, 0774) if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil { fmt.Println(err) os.Exit(-1) } downloadHugo() checkSHA256() fmt.Print("Unzipping... ") // Unzip or Ungzip the file switch runtime.GOOS { case "darwin", "windows": zp := extractor.NewZip() err = zp.Extract(tempfile, temp) default: gz := extractor.NewTgz() err = gz.Extract(tempfile, temp) } if err != nil { fmt.Println(err) os.Exit(-1) } fmt.Println("done.") var exetorename string err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error { if f.Name() == exename { exetorename = path } return nil }) // Copy the file fmt.Print("Moving Hugo executable... ") err = CopyFile(exetorename, hugo) if err != nil { fmt.Println(err) os.Exit(-1) } err = os.Chmod(hugo, 0755) if err != nil { fmt.Println(err) os.Exit(-1) } fmt.Println("done.") fmt.Println("Hugo installed at " + hugo) defer os.RemoveAll(temp) return hugo } func initializeVariables() { var arch string switch runtime.GOARCH { case "amd64": arch = "64bit" case "386": arch = "32bit" case "arm": arch = "arm32" default: arch = runtime.GOARCH } var ops = runtime.GOOS if runtime.GOOS == "darwin" && runtime.GOARCH != "arm" { ops = "osx" } exename = "hugo" zipname = "hugo_" + version + "_" + ops + "-" + arch homedir, err := homedir.Dir() if err != nil { fmt.Println(err) os.Exit(-1) } caddy = filepath.Join(homedir, ".caddy") bin = filepath.Join(caddy, "bin") hugo = filepath.Join(bin, "hugo") switch runtime.GOOS { case "windows": zipname += ".zip" exename += ".exe" hugo += ".exe" default: zipname += ".tgz" } } func checkVersion() bool { out, _ := exec.Command("hugo", "version").Output() r := regexp.MustCompile(`v\d\.\d{2}`) v := r.FindStringSubmatch(string(out))[0] v = v[1:len(v)] return (v == version) } func downloadHugo() { tempfile = filepath.Join(temp, zipname) fmt.Print("Downloading Hugo from GitHub releases... ") // Create the file out, err := os.Create(tempfile) out.Chmod(0774) if err != nil { defer os.RemoveAll(temp) fmt.Println(err) os.Exit(-1) } defer out.Close() // Get the data resp, err := http.Get(baseurl + zipname) if err != nil { fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.") fmt.Println(err) os.Exit(-1) } defer resp.Body.Close() // Writer the body to file _, err = io.Copy(out, resp.Body) if err != nil { fmt.Println(err) os.Exit(-1) } fmt.Println("downloaded.") } func checkSHA256() { fmt.Print("Checking SHA256...") hasher := sha256.New() f, err := os.Open(tempfile) if err != nil { log.Fatal(err) } defer f.Close() if _, err := io.Copy(hasher, f); err != nil { log.Fatal(err) } if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] { fmt.Println("can't verify SHA256.") os.Exit(-1) } fmt.Println("checked!") } // CopyFile is used to copy a file func CopyFile(old, new string) error { // Open the file and create a new one r, err := os.Open(old) if err != nil { return err } defer r.Close() w, err := os.Create(new) if err != nil { return err } defer w.Close() // Copy the content _, err = io.Copy(w, r) if err != nil { return err } return nil }