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  }