github.com/creativeprojects/go-selfupdate@v1.2.0/cmd/get-release/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "go/build" 8 "io" 9 "log" 10 "net/http" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strings" 15 16 "github.com/creativeprojects/go-selfupdate" 17 "github.com/creativeprojects/go-selfupdate/cmd" 18 ) 19 20 func main() { 21 var help, verbose bool 22 var cvsType string 23 flag.BoolVar(&help, "h", false, "Show help") 24 flag.BoolVar(&verbose, "v", false, "Display debugging information") 25 flag.StringVar(&cvsType, "t", "auto", "Version control: \"github\", \"gitea\" or \"gitlab\"") 26 27 flag.Usage = usage 28 flag.Parse() 29 30 if help || flag.NArg() != 1 { 31 usage() 32 return 33 } 34 35 if verbose { 36 selfupdate.SetLogger(log.New(os.Stdout, "", 0)) 37 } 38 39 repo := flag.Arg(0) 40 41 domain, slug, err := cmd.SplitDomainSlug(repo) 42 if err != nil { 43 fmt.Fprintln(os.Stderr, err) 44 os.Exit(1) 45 } 46 47 if verbose { 48 fmt.Printf("slug %q on domain %q\n", slug, domain) 49 } 50 51 source, err := cmd.GetSource(cvsType, domain) 52 if err != nil { 53 fmt.Fprintln(os.Stderr, err) 54 os.Exit(1) 55 } 56 57 updater, err := selfupdate.NewUpdater(selfupdate.Config{ 58 Source: source, 59 }) 60 if err != nil { 61 fmt.Fprintln(os.Stderr, err) 62 os.Exit(1) 63 } 64 latest, found, err := updater.DetectLatest(context.Background(), selfupdate.ParseSlug(slug)) 65 if err != nil { 66 fmt.Fprintln(os.Stderr, "Error while detecting the latest version:", err) 67 os.Exit(1) 68 } 69 if !found { 70 fmt.Fprintln(os.Stderr, "No release found in", slug) 71 os.Exit(1) 72 } 73 74 cmd := getCommand(flag.Arg(0)) 75 cmdPath := filepath.Join(build.Default.GOPATH, "bin", cmd) 76 if _, err := os.Stat(cmdPath); err != nil { 77 // When executable is not existing yet 78 if err := installFrom(latest.AssetURL, cmd, cmdPath); err != nil { 79 fmt.Fprintf(os.Stderr, "Error while installing the release binary from %s: %s\n", latest.AssetURL, err) 80 os.Exit(1) 81 } 82 } else { 83 if err := updater.UpdateTo(context.Background(), latest, cmdPath); err != nil { 84 fmt.Fprintf(os.Stderr, "Error while replacing the binary with %s: %s\n", latest.AssetURL, err) 85 os.Exit(1) 86 } 87 } 88 89 fmt.Printf(`Command was updated to the latest version %s: %s 90 91 Release Notes: 92 %s 93 `, latest.Version(), cmdPath, latest.ReleaseNotes) 94 } 95 96 func usage() { 97 fmt.Fprintln(os.Stderr, ` 98 Usage: get-release [flags] {package} 99 100 get-release is like "go get github.com/owner/repo@latest". 101 {package} is using the same format: "github.com/owner/repo". 102 103 Flags:`) 104 flag.PrintDefaults() 105 } 106 107 func getCommand(pkg string) string { 108 if strings.HasSuffix(pkg, "/") { 109 pkg = strings.TrimSuffix(pkg, "/") 110 } 111 _, cmd := filepath.Split(pkg) 112 return cmd 113 } 114 115 func installFrom(url, cmd, path string) error { 116 res, err := http.Get(url) 117 if err != nil { 118 return fmt.Errorf("failed to download release binary from %s: %s", url, err) 119 } 120 defer res.Body.Close() 121 if res.StatusCode != 200 { 122 return fmt.Errorf("failed to download release binary from %s: Invalid response ", url) 123 } 124 executable, err := selfupdate.DecompressCommand(res.Body, url, cmd, runtime.GOOS, runtime.GOARCH) 125 if err != nil { 126 return fmt.Errorf("failed to decompress downloaded asset from %s: %s", url, err) 127 } 128 bin, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0755) 129 if err != nil { 130 return err 131 } 132 if _, err := io.Copy(bin, executable); err != nil { 133 return fmt.Errorf("failed to write binary to %s: %s", path, err) 134 } 135 return nil 136 }