Skip to content

Commit

Permalink
Move tar.gz parsing login to GH provider.
Browse files Browse the repository at this point in the history
This commit also adds support to install and update
releases with slashes on the tag name.

Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
  • Loading branch information
marcosnils committed Jul 5, 2020
1 parent 2164841 commit c228cd6
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 166 deletions.
104 changes: 1 addition & 103 deletions cmd/install.go
@@ -1,22 +1,13 @@
package cmd

import (
"archive/tar"
"bytes"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/apex/log"
"github.com/h2non/filetype"
"github.com/h2non/filetype/matchers"
"github.com/marcosnils/bin/pkg/config"
"github.com/marcosnils/bin/pkg/options"
"github.com/marcosnils/bin/pkg/providers"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -137,33 +128,6 @@ func getFinalPath(path, fileName string) (string, error) {
//TODO check if other binary has the same hash and warn about it.
//TODO if the file is zipped, tared, whatever then extract it
func saveToDisk(f *providers.File, path string, overwrite bool) error {
defer f.Data.Close()

var buf bytes.Buffer
tee := io.TeeReader(f.Data, &buf)

t, err := filetype.MatchReader(tee)
if err != nil {
return err
}

var outputFile = io.MultiReader(&buf, f.Data)

// TODO: validating the type of the file will eventually be
// handled by each provider
// if t != matchers.TypeElf && t != matchers.TypeGz {
// return fmt.Errorf("File type [%v] not supported", t)
// }

if t == matchers.TypeGz {
fileName, file, err := processTarGz(outputFile)
if err != nil {
return err
}
outputFile = file
path = strings.Replace(path, filepath.Base(path), fileName, -1)

}

var extraFlags int = os.O_EXCL

Expand All @@ -181,76 +145,10 @@ func saveToDisk(f *providers.File, path string, overwrite bool) error {

//TODO add a spinner here indicating that the binary is being downloaded
log.Infof("Starting download for %s@%s into %s", f.Name, f.Version, path)
_, err = io.Copy(file, outputFile)
_, err = io.Copy(file, f.Data)
if err != nil {
return err
}

return nil
}

// processTar receives a tar.gz file and returns the
// correct file for bin to download
func processTarGz(r io.Reader) (string, io.Reader, error) {
// We're caching the whole file into memory so we can prompt
// the user which file they want to download

b, err := ioutil.ReadAll(r)
if err != nil {
return "", nil, err
}
br := bytes.NewReader(b)

gr, err := gzip.NewReader(br)
if err != nil {
return "", nil, err
}

tr := tar.NewReader(gr)
tarFiles := []interface{}{}
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return "", nil, err
}

if header.Typeflag == tar.TypeReg {
tarFiles = append(tarFiles, header.Name)
}
}
if len(tarFiles) == 0 {
return "", nil, errors.New("No files found in tar archive")
}

selectedFile := options.Select("Select file to download:", tarFiles).(string)

// Reset readers so we can scan the tar file
// again to get the correct file reader
br.Seek(0, io.SeekStart)
gr, err = gzip.NewReader(br)
if err != nil {
return "", nil, err
}
tr = tar.NewReader(gr)

var fr io.Reader
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return "", nil, err
}

if header.Name == selectedFile {
fr = tr
break
}
}
// return base of selected file since tar
// files usually have folders inside
return filepath.Base(selectedFile), fr, nil

}
73 changes: 50 additions & 23 deletions cmd/update.go
Expand Up @@ -18,49 +18,54 @@ type updateCmd struct {
type updateOpts struct {
}

type updateInfo struct{ version, url string }

func newUpdateCmd() *updateCmd {
var root = &updateCmd{}
// nolint: dupl
var cmd = &cobra.Command{
Use: "update",
Use: "update [binary_path]",
Aliases: []string{"u"},
Short: "Updates binaries managed by bin",
Short: "Updates one or multiple binaries managed by bin",
SilenceUsage: true,
Args: cobra.MaximumNArgs(1),
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {

//TODO add support o update a single binary with
//`bin update <binary>`
var bin string
if len(args) > 0 {
bin = args[0]
}

//TODO update should check all binaries with a
//certain configured parallelism (default 10, can be changed with -p) and report
//which binarines could be potentially upgraded.
//It's very likely that we have to extend the provider
//interface to support this use-case

type updateInfo struct{ version, url string }

toUpdate := map[updateInfo]*config.Binary{}
toUpdate := map[*updateInfo]*config.Binary{}
cfg := config.Get()
for _, b := range cfg.Bins {

p, err := providers.New(b.URL)

if err != nil {
return err
}

log.Debugf("Checking updates for %s", b.Path)
v, u, err := p.GetLatestVersion(b.RemoteName)

if err != nil {
return fmt.Errorf("Error checking updates for %s, %w", b.Path, err)
// Update single binary
if bin != "" {
if b, found := cfg.Bins[bin]; !found {
return fmt.Errorf("Binary path %s not found", bin)

} else {
if ui, err := getLatestVersion(b); err != nil {
return err
} else if ui != nil {
toUpdate[ui] = b
}
}

if b.Version != v {
log.Debugf("Found new version %s for %s", v, b.Path)
log.Infof("%s %s -> %s ", b.Path, color.YellowString(b.Version), color.GreenString(v))
toUpdate[updateInfo{v, u}] = b
} else {
for _, b := range cfg.Bins {
if ui, err := getLatestVersion(b); err != nil {
return err
} else if ui != nil {
toUpdate[ui] = b
}
}
}

Expand Down Expand Up @@ -122,3 +127,25 @@ func newUpdateCmd() *updateCmd {
root.cmd = cmd
return root
}

func getLatestVersion(b *config.Binary) (*updateInfo, error) {
p, err := providers.New(b.URL)

if err != nil {
return nil, err
}

log.Debugf("Checking updates for %s", b.Path)
v, u, err := p.GetLatestVersion()

if err != nil {
return nil, fmt.Errorf("Error checking updates for %s, %w", b.Path, err)
}

if b.Version == v {
return nil, nil
}
log.Debugf("Found new version %s for %s at %s", v, b.Path, u)
log.Infof("%s %s -> %s ", b.Path, color.YellowString(b.Version), color.GreenString(v))
return &updateInfo{v, u}, nil
}
2 changes: 1 addition & 1 deletion pkg/providers/docker.go
Expand Up @@ -63,7 +63,7 @@ func getImageName(repo string) string {
}

// TODO: implement
func (d *docker) GetLatestVersion(name string) (string, string, error) {
func (d *docker) GetLatestVersion() (string, string, error) {
return "", "", nil
}

Expand Down

0 comments on commit c228cd6

Please sign in to comment.