Skip to content

Commit 8bb3d0e

Browse files
authored
Add search and fetch command (#25)
1 parent 2ec75bb commit 8bb3d0e

File tree

7 files changed

+194
-100
lines changed

7 files changed

+194
-100
lines changed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
![GitHub All Releases](https://img.shields.io/github/downloads/linuxsuren/http-downloader/total)
66

77
# Get started
8-
98
`hd` is a HTTP download tool.
109

1110
Install it via: `brew install linuxsuren/linuxsuren/hd`
@@ -18,24 +17,33 @@ mv hd /usr/local/bin
1817

1918
# Usage
2019

20+
## Download
2121
```
2222
hd get https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz --thread 6
2323
```
2424

25-
Or use a simple way:
25+
Or use a simple way instead of typing the whole URL:
2626

2727
```
2828
hd get jenkins-zh/jenkins-cli/jcli -t 6
2929
```
3030

31-
Or you can also install a package from GitHub:
31+
## Install
32+
You can also install a package from GitHub:
3233

3334
```
3435
hd install jenkins-zh/jenkins-cli/jcli -t 6
3536
```
3637

37-
# Features
38+
## Search
39+
hd can download or install via the format of `$org/$repo`. If you find that it's not working. It might because of there's
40+
no record in [hd-home](https://github.com/LinuxSuRen/hd-home). You're welcome to help us to maintain it.
41+
42+
When you first run it, please init via: `hd fetch`
3843

44+
then you can search it by a keyword: `hd search jenkins`
45+
46+
# Features
3947
* go library for HTTP
4048
* multi-thread
4149
* continuously (TODO)

cmd/fetch.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package cmd
2+
3+
import (
4+
"github.com/mitchellh/go-homedir"
5+
"github.com/spf13/cobra"
6+
"os"
7+
"path"
8+
)
9+
10+
func newFetchCmd() (cmd *cobra.Command) {
11+
cmd = &cobra.Command{
12+
Use: "fetch",
13+
Short: "Fetch the latest hd config",
14+
RunE: func(_ *cobra.Command, _ []string) (err error) {
15+
return fetchHomeConfig()
16+
},
17+
}
18+
return
19+
}
20+
21+
func getConfigDir() (configDir string, err error) {
22+
var userHome string
23+
if userHome, err = homedir.Dir(); err == nil {
24+
configDir = path.Join(userHome, "/.config/hd-home")
25+
}
26+
return
27+
}
28+
29+
func fetchHomeConfig() (err error) {
30+
var configDir string
31+
if configDir, err = getConfigDir(); err != nil {
32+
return
33+
}
34+
35+
if ok, _ := pathExists(configDir); ok {
36+
err = execCommandInDir("git", configDir, "reset", "--hard", "origin/master")
37+
if err == nil {
38+
err = execCommandInDir("git", configDir, "pull")
39+
}
40+
} else {
41+
if err = os.MkdirAll(configDir, 0644); err == nil {
42+
err = execCommand("git", "clone", "https://github.com/LinuxSuRen/hd-home", configDir)
43+
}
44+
}
45+
return
46+
}

cmd/get.go

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/spf13/cobra"
1010
"io/ioutil"
1111
"net/url"
12-
"os"
1312
"path"
1413
"runtime"
1514
"strings"
@@ -30,6 +29,8 @@ func NewGetCmd() (cmd *cobra.Command) {
3029
// set flags
3130
flags := cmd.Flags()
3231
flags.StringVarP(&opt.Output, "output", "o", "", "Write output to <file> instead of stdout.")
32+
flags.BoolVarP(&opt.Fetch, "fetch", "", true,
33+
"If fetch the latest config from https://github.com/LinuxSuRen/hd-home")
3334
flags.BoolVarP(&opt.ShowProgress, "show-progress", "", true, "If show the progress of download")
3435
flags.Int64VarP(&opt.ContinueAt, "continue-at", "", -1, "ContinueAt")
3536
flags.IntVarP(&opt.Thread, "thread", "t", 0,
@@ -199,6 +200,14 @@ func (o *downloadOption) preRunE(cmd *cobra.Command, args []string) (err error)
199200
return fmt.Errorf("no URL provided")
200201
}
201202

203+
if o.Fetch {
204+
cmd.Println("start to fetch the config")
205+
if err = fetchHomeConfig(); err != nil {
206+
err = fmt.Errorf("failed with fetching home config: %v", err)
207+
return
208+
}
209+
}
210+
202211
targetURL := args[0]
203212
if !strings.HasPrefix(targetURL, "http://") && !strings.HasPrefix(targetURL, "https://") {
204213
if targetURL, err = o.providerURLParse(targetURL); err != nil {
@@ -232,30 +241,3 @@ func (o *downloadOption) runE(cmd *cobra.Command, args []string) (err error) {
232241
}
233242
return
234243
}
235-
236-
func (o *downloadOption) fetchHomeConfig() (err error) {
237-
userHome, _ := homedir.Dir()
238-
configDir := userHome + "/.config/hd-home"
239-
if ok, _ := pathExists(configDir); ok {
240-
err = execCommandInDir("git", configDir, "reset", "--hard", "origin/master")
241-
if err == nil {
242-
err = execCommandInDir("git", configDir, "pull")
243-
}
244-
} else {
245-
if err = os.MkdirAll(configDir, 0644); err == nil {
246-
err = execCommand("git", "clone", "https://github.com/LinuxSuRen/hd-home", configDir)
247-
}
248-
}
249-
return
250-
}
251-
252-
func pathExists(path string) (bool, error) {
253-
_, err := os.Stat(path)
254-
if err == nil {
255-
return true, nil
256-
}
257-
if os.IsNotExist(err) {
258-
return false, nil
259-
}
260-
return false, err
261-
}

cmd/install.go

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"path/filepath"
1212
"runtime"
1313
"strings"
14-
"sync"
1514
"syscall"
1615
)
1716

@@ -46,12 +45,6 @@ type installOption struct {
4645
}
4746

4847
func (o *installOption) preRunE(cmd *cobra.Command, args []string) (err error) {
49-
if o.Fetch {
50-
if err = o.fetchHomeConfig(); err != nil {
51-
err = fmt.Errorf("failed with fetching home config: %v", err)
52-
return
53-
}
54-
}
5548
err = o.downloadOption.preRunE(cmd, args)
5649
return
5750
}
@@ -156,63 +149,3 @@ func (o *installOption) extractFiles(tarFile, targetName string) (err error) {
156149
}
157150
return
158151
}
159-
160-
func execCommandInDir(name, dir string, arg ...string) (err error) {
161-
command := exec.Command(name, arg...)
162-
if dir != "" {
163-
command.Dir = dir
164-
}
165-
166-
//var stdout []byte
167-
//var errStdout error
168-
stdoutIn, _ := command.StdoutPipe()
169-
stderrIn, _ := command.StderrPipe()
170-
err = command.Start()
171-
if err != nil {
172-
return err
173-
}
174-
175-
// cmd.Wait() should be called only after we finish reading
176-
// from stdoutIn and stderrIn.
177-
// wg ensures that we finish
178-
var wg sync.WaitGroup
179-
wg.Add(1)
180-
go func() {
181-
_, _ = copyAndCapture(os.Stdout, stdoutIn)
182-
wg.Done()
183-
}()
184-
185-
_, _ = copyAndCapture(os.Stderr, stderrIn)
186-
187-
wg.Wait()
188-
189-
err = command.Wait()
190-
return
191-
}
192-
193-
func execCommand(name string, arg ...string) (err error) {
194-
return execCommandInDir(name, "", arg...)
195-
}
196-
197-
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
198-
var out []byte
199-
buf := make([]byte, 1024, 1024)
200-
for {
201-
n, err := r.Read(buf[:])
202-
if n > 0 {
203-
d := buf[:n]
204-
out = append(out, d...)
205-
_, err := w.Write(d)
206-
if err != nil {
207-
return out, err
208-
}
209-
}
210-
if err != nil {
211-
// Read returns io.EOF at the end of file, which is not an error for us
212-
if err == io.EOF {
213-
err = nil
214-
}
215-
return out, err
216-
}
217-
}
218-
}

cmd/root.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func NewRoot() (cmd *cobra.Command) {
1313
}
1414

1515
cmd.AddCommand(
16-
NewGetCmd(), NewInstallCmd(),
16+
NewGetCmd(), NewInstallCmd(), newFetchCmd(), newSearchCmd(),
1717
extver.NewVersionCmd("linuxsuren", "http-downloader", "hd", nil))
1818
return
1919
}

cmd/search.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"github.com/spf13/cobra"
6+
"path"
7+
"path/filepath"
8+
"strings"
9+
)
10+
11+
func newSearchCmd() (cmd *cobra.Command) {
12+
cmd = &cobra.Command{
13+
Use: "search",
14+
Short: "Search packages from the hd config repo",
15+
Args: cobra.MinimumNArgs(1),
16+
RunE: func(_ *cobra.Command, args []string) (err error) {
17+
err = search(args[0])
18+
return
19+
},
20+
}
21+
return
22+
}
23+
24+
func search(keyword string) (err error) {
25+
var configDir string
26+
if configDir, err = getConfigDir(); err != nil {
27+
// TODO consider how to deal with the situation when config repo is not exists
28+
return
29+
}
30+
31+
var files []string
32+
if files, err = filepath.Glob(path.Join(configDir, "config/**/*.yml")); err == nil {
33+
for _, metaFile := range files {
34+
ext := path.Ext(metaFile)
35+
fileName := path.Base(metaFile)
36+
org := path.Base(path.Dir(metaFile))
37+
repo := strings.TrimSuffix(fileName, ext)
38+
39+
if !strings.Contains(repo, keyword) {
40+
continue
41+
}
42+
43+
fmt.Println(path.Join(org, repo))
44+
}
45+
}
46+
return
47+
}

cmd/util.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package cmd
22

3+
import (
4+
"io"
5+
"os"
6+
"os/exec"
7+
"sync"
8+
)
9+
310
func getOrDefault(key, def string, data map[string]string) (result string) {
411
var ok bool
512
if result, ok = data[key]; !ok {
@@ -11,3 +18,74 @@ func getOrDefault(key, def string, data map[string]string) (result string) {
1118
func getReplacement(key string, data map[string]string) (result string) {
1219
return getOrDefault(key, key, data)
1320
}
21+
22+
func pathExists(path string) (bool, error) {
23+
_, err := os.Stat(path)
24+
if err == nil {
25+
return true, nil
26+
}
27+
if os.IsNotExist(err) {
28+
return false, nil
29+
}
30+
return false, err
31+
}
32+
33+
func execCommandInDir(name, dir string, arg ...string) (err error) {
34+
command := exec.Command(name, arg...)
35+
if dir != "" {
36+
command.Dir = dir
37+
}
38+
39+
//var stdout []byte
40+
//var errStdout error
41+
stdoutIn, _ := command.StdoutPipe()
42+
stderrIn, _ := command.StderrPipe()
43+
err = command.Start()
44+
if err != nil {
45+
return err
46+
}
47+
48+
// cmd.Wait() should be called only after we finish reading
49+
// from stdoutIn and stderrIn.
50+
// wg ensures that we finish
51+
var wg sync.WaitGroup
52+
wg.Add(1)
53+
go func() {
54+
_, _ = copyAndCapture(os.Stdout, stdoutIn)
55+
wg.Done()
56+
}()
57+
58+
_, _ = copyAndCapture(os.Stderr, stderrIn)
59+
60+
wg.Wait()
61+
62+
err = command.Wait()
63+
return
64+
}
65+
66+
func execCommand(name string, arg ...string) (err error) {
67+
return execCommandInDir(name, "", arg...)
68+
}
69+
70+
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
71+
var out []byte
72+
buf := make([]byte, 1024, 1024)
73+
for {
74+
n, err := r.Read(buf[:])
75+
if n > 0 {
76+
d := buf[:n]
77+
out = append(out, d...)
78+
_, err := w.Write(d)
79+
if err != nil {
80+
return out, err
81+
}
82+
}
83+
if err != nil {
84+
// Read returns io.EOF at the end of file, which is not an error for us
85+
if err == io.EOF {
86+
err = nil
87+
}
88+
return out, err
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)