<|start_filename|>pkg/strings/strings.go<|end_filename|>
package strings

import "strings"

func ContainsAny(s string, v []string) bool {
	for _, val := range v {
		if strings.Contains(s, val) {
			return true
		}
	}
	return false
}


<|start_filename|>cmd/update_test.go<|end_filename|>
package cmd

import (
	"reflect"
	"testing"

	"github.com/marcosnils/bin/pkg/config"
	"github.com/marcosnils/bin/pkg/providers"
)

type mockProvider struct {
	providers.Provider
	latestVersion    string
	latestVersionURL string
	err              error
}

func (m mockProvider) GetLatestVersion() (string, string, error) {
	return m.latestVersion, m.latestVersionURL, m.err
}

func (m mockProvider) GetID() string {
	return "github"
}

func TestGetLatestVersion(t *testing.T) {
	type mockValues struct {
		latestVersion    string
		latestVersionURL string
		err              error
	}
	cases := []struct {
		in  *config.Binary
		m   mockValues
		out *updateInfo
	}{
		{
			&config.Binary{
				Path:       "/home/user/bin/launchpad",
				Version:    "1.1.0",
				URL:        "https://github.com/Mirantis/launchpad/releases/download/1.1.0/launchpad-linux-x64",
				RemoteName: "launchpad-linux-x64",
				Provider:   "github",
			},
			mockValues{"1.1.1", "https://github.com/Mirantis/launchpad/releases/download/1.1.1/launchpad-linux-x64", nil},
			&updateInfo{
				version: "1.1.1",
				url:     "https://github.com/Mirantis/launchpad/releases/download/1.1.1/launchpad-linux-x64",
			},
		},
		{
			&config.Binary{
				Path:       "/home/user/bin/launchpad",
				Version:    "1.2.0-rc.1",
				URL:        "https://github.com/Mirantis/launchpad/releases/download/1.2.0-rc.1/launchpad-linux-x64",
				RemoteName: "launchpad-linux-x64",
				Provider:   "github",
			},
			mockValues{"1.1.1", "https://github.com/Mirantis/launchpad/releases/download/1.1.1/launchpad-linux-x64", nil},
			nil,
		},
	}

	for _, c := range cases {
		p := mockProvider{latestVersion: c.m.latestVersion, latestVersionURL: c.m.latestVersionURL, err: c.m.err}
		if v, err := getLatestVersion(c.in, p); err != nil {
			t.Fatalf("Error during getLatestVersion(%#v, %#v): %v", c.in, p, err)
		} else if !reflect.DeepEqual(v, c.out) {
			t.Fatalf("For case %#v: %#v does not match %#v", c.in, v, c.out)
		}
	}

}


<|start_filename|>pkg/providers/hashicorp_types.go<|end_filename|>
package providers

type hashiCorpRelease struct {
	Name             string           `json:"name"`
	Version          string           `json:"version"`
	Shasums          string           `json:"shasums"`
	ShasumsSignature string           `json:"shasums_signature"`
	Builds           []hashiCorpBuild `json:"builds"`
}

type hashiCorpRepo struct {
	Name     string                      `json:"name"`
	Versions map[string]hashiCorpVersion `json:"versions"`
}

type hashiCorpVersion struct {
	Name             string           `json:"name"`
	Version          string           `json:"version"`
	Shasums          string           `json:"shasums"`
	ShasumsSignature string           `json:"shasums_signature"`
	Builds           []hashiCorpBuild `json:"builds"`
}

type hashiCorpBuild struct {
	Name     string `json:"name"`
	Version  string `json:"version"`
	OS       string `json:"os"`
	Arch     string `json:"arch"`
	Filename string `json:"filename"`
	URL      string `json:"url"`
}


<|start_filename|>cmd/list.go<|end_filename|>
package cmd

import (
	"fmt"
	"os"
	"sort"

	"github.com/WeiZhang555/tabwriter"
	"github.com/fatih/color"
	"github.com/marcosnils/bin/pkg/config"
	"github.com/spf13/cobra"
)

type listCmd struct {
	cmd *cobra.Command
}

func newListCmd() *listCmd {
	root := &listCmd{}
	// nolint: dupl
	cmd := &cobra.Command{
		Use:           "list",
		Aliases:       []string{"ls"},
		Short:         "List binaries managed by bin",
		SilenceUsage:  true,
		SilenceErrors: true,
		RunE: func(cmd *cobra.Command, args []string) error {
			w := new(tabwriter.Writer)
			w.Init(os.Stdout, 8, 8, 3, '\t', 0)

			defer w.Flush()

			cfg := config.Get()

			fmt.Fprintf(w, "\n %s\t%s\t%s\t%s", "Path", "Version", "URL", "Status")
			binPaths := []string{}
			for k := range cfg.Bins {
				binPaths = append(binPaths, k)
			}
			sort.Strings(binPaths)
			for _, k := range binPaths {
				b := cfg.Bins[k]

				_, err := os.Stat(b.Path)

				status := color.GreenString("OK")
				if err != nil {
					status = color.RedString("missing file")
				}

				fmt.Fprintf(w, "\n %s\t%s\t%s\t%s", b.Path, b.Version, b.URL, status)
			}
			fmt.Fprintf(w, "\n\n")
			return nil
		},
	}

	root.cmd = cmd
	return root
}


<|start_filename|>pkg/providers/providers.go<|end_filename|>
package providers

import (
	"errors"
	"fmt"
	"hash"
	"io"
	"net/url"
	"regexp"
	"strings"
)

var ErrInvalidProvider = errors.New("invalid provider")

type File struct {
	Data    io.Reader
	Name    string
	Hash    hash.Hash
	Version string
	Length  int64
}

type FetchOpts struct {
	All bool
}

type Provider interface {
	// Fetch returns the file metadata to retrieve a specific binary given
	// for a provider
	Fetch(*FetchOpts) (*File, error)
	// GetLatestVersion returns the version and the URL of the
	// latest version for this binary
	GetLatestVersion() (string, string, error)

	// GetID returns the unique identiifer of this provider
	GetID() string
}

var (
	httpUrlPrefix   = regexp.MustCompile("^https?://")
	dockerUrlPrefix = regexp.MustCompile("^docker://")
)

func New(u, provider string) (Provider, error) {
	if dockerUrlPrefix.MatchString(u) {
		return newDocker(u)
	}
	if !httpUrlPrefix.MatchString(u) {
		u = fmt.Sprintf("https://%s", u)
	}

	purl, err := url.Parse(u)

	if err != nil {
		return nil, err
	}

	if strings.Contains(purl.Host, "github") || provider == "github" {
		return newGitHub(purl)
	}

	if strings.Contains(purl.Host, "gitlab") || provider == "gitlab" {
		return newGitLab(purl)
	}

	if strings.Contains(purl.Host, "releases.hashicorp.com") || provider == "hashicorp" {
		return newHashiCorp(purl)
	}

	return nil, fmt.Errorf("Can't find provider for url %s", u)
}
