// +build windows //go:generate goversioninfo -icon=aminal.ico /* Looks at directory "Versions" next to this executable. Finds the latest version and runs the executable with the same name as this executable in that directory. Eg.: Aminal.exe (=launcher.exe) Versions/ 1.0.0/ Aminal.exe 1.0.1 Aminal.exe -> Launches Versions/1.0.1/Aminal.exe. */ package main import ( "errors" "fmt" "github.com/liamg/aminal/windows/winutil" "io/ioutil" "os" "os/exec" "os/user" "path/filepath" "sort" "strconv" "strings" "syscall" ) type Version struct { number [3]int name string } type Versions []Version func main() { executable, err := winutil.GetExecutablePath() check(err) executableDir, executableName := filepath.Split(executable) versionsDir := filepath.Join(executableDir, "Versions") latestVersion, err := getLatestVersion(versionsDir) check(err) usr, err := user.Current() check(err) cmd := exec.Command("C:\\Windows\\System32\\cmd.exe", "/C", "start", "Aminal", "/B", executableName) cmd.Dir = usr.HomeDir latestVersionDir := filepath.Join(versionsDir, latestVersion) path, pathSet := os.LookupEnv("PATH") if pathSet { path += ";" + latestVersionDir } else { path = latestVersionDir } cmd.Env = append(os.Environ(), "PATH="+path) const CREATE_NO_WINDOW = 0x08000000 cmd.SysProcAttr = &syscall.SysProcAttr{ HideWindow: true, CreationFlags: CREATE_NO_WINDOW, } check(cmd.Start()) } func getLatestVersion(versionsDir string) (string, error) { potentialVersions, err := ioutil.ReadDir(versionsDir) if err != nil { return "", err } var versions Versions for _, file := range potentialVersions { if !file.IsDir() { continue } version, err := parseVersionString(file.Name()) if err != nil { continue } versions = append(versions, version) } if len(versions) == 0 { errMsg := fmt.Sprintf("No valid version in %s.", versionsDir) return "", errors.New(errMsg) } sort.Sort(versions) return versions[len(versions)-1].String(), nil } func parseVersionString(version string) (Version, error) { var result Version result.name = version err := error(nil) version = strings.TrimSuffix(version, "-SNAPSHOT") parts := strings.Split(version, ".") if len(parts) != len(result.number) { err = errors.New("Wrong number of parts.") } else { for i, partStr := range parts { result.number[i], err = strconv.Atoi(partStr) if err != nil { break } } } return result, err } func (arr Versions) Len() int { return len(arr) } func (arr Versions) Less(i, j int) bool { for k, left := range arr[i].number { right := arr[j].number[k] if left > right { return false } else if left < right { return true } } fmt.Printf("%s < %s\n", arr[i], arr[j]) return true } func (arr Versions) Swap(i, j int) { tmp := arr[j] arr[j] = arr[i] arr[i] = tmp } func (version Version) String() string { return version.name } func check(e error) { if e != nil { panic(e) } }