Skip to content

Commit 7a27747

Browse files
authored
Add support to parse URL from hd-home (#10)
* 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
1 parent d81be53 commit 7a27747

File tree

7 files changed

+226
-5
lines changed

7 files changed

+226
-5
lines changed

.github/.typo-ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
dictionaries:
2+
- en
3+
- en_GB
4+
exclude_fiels:
5+
- ".github/**/*"
6+
- "go.mod"
7+
- "go.sum"
8+
- Makefile
9+
excluded_words:
10+
- KubeSphere
11+
- kubespheredev
12+
- minio
13+
- jcli
14+
- ioutil
15+
- Unmarshal
16+
- Errorf

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ build: fmt
22
export GOPROXY=https://goproxy.io
33
CGO_ENABLE=0 go build -ldflags "-w -s" -o bin/hd
44

5+
build-linux: fmt
6+
export GOPROXY=https://goproxy.io
7+
CGO_ENABLE=0 GOOS=linux go build -ldflags "-w -s" -o bin/linux/hd
8+
upx bin/linux/hd
9+
510
run:
611
go run main.go
712

cmd/get.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package cmd
22

33
import (
4+
"bytes"
45
"fmt"
6+
"github.com/ghodss/yaml"
57
"github.com/linuxsuren/http-downloader/pkg"
8+
"github.com/mitchellh/go-homedir"
69
"github.com/spf13/cobra"
10+
"io/ioutil"
711
"net/url"
12+
"os"
813
"path"
914
"runtime"
1015
"strings"
16+
"text/template"
1117
)
1218

1319
// NewGetCmd return the get command
@@ -52,6 +58,7 @@ type downloadOption struct {
5258

5359
// inner fields
5460
name string
61+
Tar bool
5562
}
5663

5764
const (
@@ -102,9 +109,72 @@ func (o *downloadOption) providerURLParse(path string) (url string, err error) {
102109
org, repo, version, name, o.OS, o.Arch)
103110
}
104111
o.name = name
112+
113+
// try to parse from config
114+
userHome, _ := homedir.Dir()
115+
configDir := userHome + "/.config/hd-home"
116+
matchedFile := configDir + "/config/" + org + "/" + repo + ".yml"
117+
if ok, _ := pathExists(matchedFile); ok {
118+
var data []byte
119+
if data, err = ioutil.ReadFile(matchedFile); err == nil {
120+
cfg := hdConfig{}
121+
122+
if err = yaml.Unmarshal(data, &cfg); err == nil {
123+
hdPackage := &hdPackage{
124+
Name: o.name,
125+
Version: version,
126+
OS: runtime.GOOS,
127+
Arch: runtime.GOARCH,
128+
}
129+
if version == "latest" {
130+
ghClient := pkg.ReleaseClient{
131+
Org: org,
132+
Repo: repo,
133+
}
134+
ghClient.Init()
135+
if asset, err := ghClient.GetLatestJCLIAsset(); err == nil {
136+
hdPackage.Version = asset.TagName
137+
} else {
138+
fmt.Println(err, "cannot get the asset")
139+
}
140+
}
141+
142+
if cfg.Filename != "" {
143+
tmp, _ := template.New("hd").Parse(cfg.Filename)
144+
145+
var buf bytes.Buffer
146+
if err = tmp.Execute(&buf, hdPackage); err == nil {
147+
url = fmt.Sprintf("https://github.com/%s/%s/releases/%s/download/%s",
148+
org, repo, version, buf.String())
149+
150+
o.Output = buf.String()
151+
}
152+
}
153+
154+
o.Tar = cfg.Tar
155+
if cfg.Binary != "" {
156+
o.name = cfg.Binary
157+
}
158+
}
159+
}
160+
}
105161
return
106162
}
107163

164+
type hdConfig struct {
165+
Name string
166+
Filename string
167+
Binary string
168+
Tar bool
169+
}
170+
171+
type hdPackage struct {
172+
Name string
173+
Version string
174+
OS string
175+
Arch string
176+
}
177+
108178
func (o *downloadOption) preRunE(cmd *cobra.Command, args []string) (err error) {
109179
if len(args) <= 0 {
110180
return fmt.Errorf("no URL provided")
@@ -143,3 +213,27 @@ func (o *downloadOption) runE(cmd *cobra.Command, args []string) (err error) {
143213
}
144214
return
145215
}
216+
217+
func (o *downloadOption) fetchHomeConfig() (err error) {
218+
userHome, _ := homedir.Dir()
219+
configDir := userHome + "/.config/hd-home"
220+
if ok, _ := pathExists(configDir); ok {
221+
err = execCommand("git", "pull", "-C", configDir)
222+
} else {
223+
if err = os.MkdirAll(configDir, 0644); err == nil {
224+
err = execCommand("git", "clone", "https://github.com/LinuxSuRen/hd-home", configDir)
225+
}
226+
}
227+
return
228+
}
229+
230+
func pathExists(path string) (bool, error) {
231+
_, err := os.Stat(path)
232+
if err == nil {
233+
return true, nil
234+
}
235+
if os.IsNotExist(err) {
236+
return false, nil
237+
}
238+
return false, err
239+
}

cmd/install.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func NewInstallCmd() (cmd *cobra.Command) {
2727
//flags.StringVarP(&opt.Mode, "mode", "m", "package",
2828
// "If you want to install it via platform package manager")
2929
flags.BoolVarP(&opt.ShowProgress, "show-progress", "", true, "If show the progress of download")
30-
flags.IntVarP(&opt.Thread, "thread", "t", 0,
30+
flags.IntVarP(&opt.Thread, "thread", "t", 4,
3131
`Download file with multi-threads. It only works when its value is bigger than 1`)
3232
flags.BoolVarP(&opt.KeepPart, "keep-part", "", false,
3333
"If you want to keep the part files instead of deleting them")
@@ -42,22 +42,52 @@ type installOption struct {
4242
Mode string
4343
}
4444

45+
func (o *installOption) preRunE(cmd *cobra.Command, args []string) (err error) {
46+
if err = o.fetchHomeConfig(); err != nil {
47+
// this is not a fatal, don't block the process
48+
cmd.Printf("Failed with fetching home config: %v\n", err)
49+
}
50+
err = o.downloadOption.preRunE(cmd, args)
51+
return
52+
}
53+
4554
func (o *installOption) runE(cmd *cobra.Command, args []string) (err error) {
4655
if err = o.downloadOption.runE(cmd, args); err != nil {
4756
return
4857
}
4958

50-
if err = o.extractFiles(o.Output, o.name); err == nil {
51-
err = o.overWriteBinary(fmt.Sprintf("%s/%s", filepath.Dir(o.Output), o.name), fmt.Sprintf("/usr/local/bin/%s", o.name))
59+
var source string
60+
var target string
61+
if o.Tar {
62+
if err = o.extractFiles(o.Output, o.name); err == nil {
63+
source = fmt.Sprintf("%s/%s", filepath.Dir(o.Output), o.name)
64+
target = fmt.Sprintf("/usr/local/bin/%s", o.name)
65+
} else {
66+
err = fmt.Errorf("cannot extract %s from tar file, error: %v", o.Output, err)
67+
}
5268
} else {
53-
err = fmt.Errorf("cannot extract %s from tar file, error: %v", o.Output, err)
69+
source = o.downloadOption.Output
70+
target = fmt.Sprintf("/usr/local/bin/%s", o.name)
71+
}
72+
73+
if err == nil {
74+
fmt.Println("install", source, "to", target)
75+
err = o.overWriteBinary(source, target)
5476
}
5577
return
5678
}
5779

5880
func (o *installOption) overWriteBinary(sourceFile, targetPath string) (err error) {
5981
switch runtime.GOOS {
60-
case "linux":
82+
case "linux", "darwin":
83+
if err = execCommand("chmod", "u+x", sourceFile); err != nil {
84+
return
85+
}
86+
87+
if err = execCommand("rm", "-rf", targetPath); err != nil {
88+
return
89+
}
90+
6191
var cp string
6292
if cp, err = exec.LookPath("cp"); err == nil {
6393
err = syscall.Exec(cp, []string{"cp", sourceFile, targetPath}, os.Environ())

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ module github.com/linuxsuren/http-downloader
33
go 1.15
44

55
require (
6+
github.com/ghodss/yaml v1.0.0
67
github.com/golang/mock v1.4.4
8+
github.com/google/go-github/v29 v29.0.3
79
github.com/gosuri/uiprogress v0.0.1
810
github.com/linuxsuren/cobra-extension v0.0.10
11+
github.com/mitchellh/go-homedir v1.1.0
912
github.com/onsi/ginkgo v1.14.2
1013
github.com/onsi/gomega v1.10.4
1114
github.com/spf13/cobra v1.1.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
5555
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
5656
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
5757
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
58+
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
5859
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
5960
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
6061
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -180,6 +181,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex
180181
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
181182
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
182183
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
184+
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
183185
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
184186
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
185187
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=

pkg/release.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package pkg
2+
3+
import (
4+
"context"
5+
"github.com/google/go-github/v29/github"
6+
)
7+
8+
// ReleaseClient is the client of jcli github
9+
type ReleaseClient struct {
10+
Client *github.Client
11+
Org string
12+
Repo string
13+
}
14+
15+
// ReleaseAsset is the asset from GitHub release
16+
type ReleaseAsset struct {
17+
TagName string
18+
Body string
19+
}
20+
21+
// Init init the GitHub client
22+
func (g *ReleaseClient) Init() {
23+
g.Client = github.NewClient(nil)
24+
}
25+
26+
// GetLatestJCLIAsset returns the latest jcli asset
27+
func (g *ReleaseClient) GetLatestJCLIAsset() (*ReleaseAsset, error) {
28+
return g.GetLatestReleaseAsset(g.Org, g.Repo)
29+
}
30+
31+
// GetLatestReleaseAsset returns the latest release asset
32+
func (g *ReleaseClient) GetLatestReleaseAsset(owner, repo string) (ra *ReleaseAsset, err error) {
33+
ctx := context.Background()
34+
35+
var release *github.RepositoryRelease
36+
if release, _, err = g.Client.Repositories.GetLatestRelease(ctx, owner, repo); err == nil {
37+
ra = &ReleaseAsset{
38+
TagName: release.GetTagName(),
39+
Body: release.GetBody(),
40+
}
41+
}
42+
return
43+
}
44+
45+
// GetJCLIAsset returns the asset from a tag name
46+
func (g *ReleaseClient) GetJCLIAsset(tagName string) (*ReleaseAsset, error) {
47+
return g.GetReleaseAssetByTagName(g.Org, g.Repo, tagName)
48+
}
49+
50+
// GetReleaseAssetByTagName returns the release asset by tag name
51+
func (g *ReleaseClient) GetReleaseAssetByTagName(owner, repo, tagName string) (ra *ReleaseAsset, err error) {
52+
ctx := context.Background()
53+
54+
opt := &github.ListOptions{
55+
PerPage: 99999,
56+
}
57+
58+
var releaseList []*github.RepositoryRelease
59+
if releaseList, _, err = g.Client.Repositories.ListReleases(ctx, owner, repo, opt); err == nil {
60+
for _, item := range releaseList {
61+
if item.GetTagName() == tagName {
62+
ra = &ReleaseAsset{
63+
TagName: item.GetTagName(),
64+
Body: item.GetBody(),
65+
}
66+
break
67+
}
68+
}
69+
}
70+
return
71+
}

0 commit comments

Comments
 (0)