Skip to content

Commit

Permalink
Add support to parse URL from hd-home (#10)
Browse files Browse the repository at this point in the history
* Add support to parse URL from hd-home

* Parse binary field

* Test pass with install kind and kubekey

* Fix the errors found by gosec

* Typo fixes found by typoci
  • Loading branch information
LinuxSuRen committed Jan 30, 2021
1 parent d81be53 commit 7a27747
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 5 deletions.
16 changes: 16 additions & 0 deletions .github/.typo-ci.yml
@@ -0,0 +1,16 @@
dictionaries:
- en
- en_GB
exclude_fiels:
- ".github/**/*"
- "go.mod"
- "go.sum"
- Makefile
excluded_words:
- KubeSphere
- kubespheredev
- minio
- jcli
- ioutil
- Unmarshal
- Errorf
5 changes: 5 additions & 0 deletions Makefile
Expand Up @@ -2,6 +2,11 @@ build: fmt
export GOPROXY=https://goproxy.io
CGO_ENABLE=0 go build -ldflags "-w -s" -o bin/hd

build-linux: fmt
export GOPROXY=https://goproxy.io
CGO_ENABLE=0 GOOS=linux go build -ldflags "-w -s" -o bin/linux/hd
upx bin/linux/hd

run:
go run main.go

Expand Down
94 changes: 94 additions & 0 deletions cmd/get.go
@@ -1,13 +1,19 @@
package cmd

import (
"bytes"
"fmt"
"github.com/ghodss/yaml"
"github.com/linuxsuren/http-downloader/pkg"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"io/ioutil"
"net/url"
"os"
"path"
"runtime"
"strings"
"text/template"
)

// NewGetCmd return the get command
Expand Down Expand Up @@ -52,6 +58,7 @@ type downloadOption struct {

// inner fields
name string
Tar bool
}

const (
Expand Down Expand Up @@ -102,9 +109,72 @@ func (o *downloadOption) providerURLParse(path string) (url string, err error) {
org, repo, version, name, o.OS, o.Arch)
}
o.name = name

// try to parse from config
userHome, _ := homedir.Dir()
configDir := userHome + "/.config/hd-home"
matchedFile := configDir + "/config/" + org + "/" + repo + ".yml"
if ok, _ := pathExists(matchedFile); ok {
var data []byte
if data, err = ioutil.ReadFile(matchedFile); err == nil {
cfg := hdConfig{}

if err = yaml.Unmarshal(data, &cfg); err == nil {
hdPackage := &hdPackage{
Name: o.name,
Version: version,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
}
if version == "latest" {
ghClient := pkg.ReleaseClient{
Org: org,
Repo: repo,
}
ghClient.Init()
if asset, err := ghClient.GetLatestJCLIAsset(); err == nil {
hdPackage.Version = asset.TagName
} else {
fmt.Println(err, "cannot get the asset")
}
}

if cfg.Filename != "" {
tmp, _ := template.New("hd").Parse(cfg.Filename)

var buf bytes.Buffer
if err = tmp.Execute(&buf, hdPackage); err == nil {
url = fmt.Sprintf("https://github.com/%s/%s/releases/%s/download/%s",
org, repo, version, buf.String())

o.Output = buf.String()
}
}

o.Tar = cfg.Tar
if cfg.Binary != "" {
o.name = cfg.Binary
}
}
}
}
return
}

type hdConfig struct {
Name string
Filename string
Binary string
Tar bool
}

type hdPackage struct {
Name string
Version string
OS string
Arch string
}

func (o *downloadOption) preRunE(cmd *cobra.Command, args []string) (err error) {
if len(args) <= 0 {
return fmt.Errorf("no URL provided")
Expand Down Expand Up @@ -143,3 +213,27 @@ func (o *downloadOption) runE(cmd *cobra.Command, args []string) (err error) {
}
return
}

func (o *downloadOption) fetchHomeConfig() (err error) {
userHome, _ := homedir.Dir()
configDir := userHome + "/.config/hd-home"
if ok, _ := pathExists(configDir); ok {
err = execCommand("git", "pull", "-C", configDir)
} else {
if err = os.MkdirAll(configDir, 0644); err == nil {
err = execCommand("git", "clone", "https://github.com/LinuxSuRen/hd-home", configDir)
}
}
return
}

func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
40 changes: 35 additions & 5 deletions cmd/install.go
Expand Up @@ -27,7 +27,7 @@ func NewInstallCmd() (cmd *cobra.Command) {
//flags.StringVarP(&opt.Mode, "mode", "m", "package",
// "If you want to install it via platform package manager")
flags.BoolVarP(&opt.ShowProgress, "show-progress", "", true, "If show the progress of download")
flags.IntVarP(&opt.Thread, "thread", "t", 0,
flags.IntVarP(&opt.Thread, "thread", "t", 4,
`Download file with multi-threads. It only works when its value is bigger than 1`)
flags.BoolVarP(&opt.KeepPart, "keep-part", "", false,
"If you want to keep the part files instead of deleting them")
Expand All @@ -42,22 +42,52 @@ type installOption struct {
Mode string
}

func (o *installOption) preRunE(cmd *cobra.Command, args []string) (err error) {
if err = o.fetchHomeConfig(); err != nil {
// this is not a fatal, don't block the process
cmd.Printf("Failed with fetching home config: %v\n", err)
}
err = o.downloadOption.preRunE(cmd, args)
return
}

func (o *installOption) runE(cmd *cobra.Command, args []string) (err error) {
if err = o.downloadOption.runE(cmd, args); err != nil {
return
}

if err = o.extractFiles(o.Output, o.name); err == nil {
err = o.overWriteBinary(fmt.Sprintf("%s/%s", filepath.Dir(o.Output), o.name), fmt.Sprintf("/usr/local/bin/%s", o.name))
var source string
var target string
if o.Tar {
if err = o.extractFiles(o.Output, o.name); err == nil {
source = fmt.Sprintf("%s/%s", filepath.Dir(o.Output), o.name)
target = fmt.Sprintf("/usr/local/bin/%s", o.name)
} else {
err = fmt.Errorf("cannot extract %s from tar file, error: %v", o.Output, err)
}
} else {
err = fmt.Errorf("cannot extract %s from tar file, error: %v", o.Output, err)
source = o.downloadOption.Output
target = fmt.Sprintf("/usr/local/bin/%s", o.name)
}

if err == nil {
fmt.Println("install", source, "to", target)
err = o.overWriteBinary(source, target)
}
return
}

func (o *installOption) overWriteBinary(sourceFile, targetPath string) (err error) {
switch runtime.GOOS {
case "linux":
case "linux", "darwin":
if err = execCommand("chmod", "u+x", sourceFile); err != nil {
return
}

if err = execCommand("rm", "-rf", targetPath); err != nil {
return
}

var cp string
if cp, err = exec.LookPath("cp"); err == nil {
err = syscall.Exec(cp, []string{"cp", sourceFile, targetPath}, os.Environ())
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Expand Up @@ -3,9 +3,12 @@ module github.com/linuxsuren/http-downloader
go 1.15

require (
github.com/ghodss/yaml v1.0.0
github.com/golang/mock v1.4.4
github.com/google/go-github/v29 v29.0.3
github.com/gosuri/uiprogress v0.0.1
github.com/linuxsuren/cobra-extension v0.0.10
github.com/mitchellh/go-homedir v1.1.0
github.com/onsi/ginkgo v1.14.2
github.com/onsi/gomega v1.10.4
github.com/spf13/cobra v1.1.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -55,6 +55,7 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
Expand Down Expand Up @@ -180,6 +181,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
Expand Down
71 changes: 71 additions & 0 deletions pkg/release.go
@@ -0,0 +1,71 @@
package pkg

import (
"context"
"github.com/google/go-github/v29/github"
)

// ReleaseClient is the client of jcli github
type ReleaseClient struct {
Client *github.Client
Org string
Repo string
}

// ReleaseAsset is the asset from GitHub release
type ReleaseAsset struct {
TagName string
Body string
}

// Init init the GitHub client
func (g *ReleaseClient) Init() {
g.Client = github.NewClient(nil)
}

// GetLatestJCLIAsset returns the latest jcli asset
func (g *ReleaseClient) GetLatestJCLIAsset() (*ReleaseAsset, error) {
return g.GetLatestReleaseAsset(g.Org, g.Repo)
}

// GetLatestReleaseAsset returns the latest release asset
func (g *ReleaseClient) GetLatestReleaseAsset(owner, repo string) (ra *ReleaseAsset, err error) {
ctx := context.Background()

var release *github.RepositoryRelease
if release, _, err = g.Client.Repositories.GetLatestRelease(ctx, owner, repo); err == nil {
ra = &ReleaseAsset{
TagName: release.GetTagName(),
Body: release.GetBody(),
}
}
return
}

// GetJCLIAsset returns the asset from a tag name
func (g *ReleaseClient) GetJCLIAsset(tagName string) (*ReleaseAsset, error) {
return g.GetReleaseAssetByTagName(g.Org, g.Repo, tagName)
}

// GetReleaseAssetByTagName returns the release asset by tag name
func (g *ReleaseClient) GetReleaseAssetByTagName(owner, repo, tagName string) (ra *ReleaseAsset, err error) {
ctx := context.Background()

opt := &github.ListOptions{
PerPage: 99999,
}

var releaseList []*github.RepositoryRelease
if releaseList, _, err = g.Client.Repositories.ListReleases(ctx, owner, repo, opt); err == nil {
for _, item := range releaseList {
if item.GetTagName() == tagName {
ra = &ReleaseAsset{
TagName: item.GetTagName(),
Body: item.GetBody(),
}
break
}
}
}
return
}

0 comments on commit 7a27747

Please sign in to comment.