Skip to content

Commit

Permalink
feat: better err handling, print fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
beetcb committed Feb 11, 2022
1 parent d640acb commit c98b896
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 52 deletions.
38 changes: 28 additions & 10 deletions cmd/ghdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/beetcb/ghdl"
h "github.com/beetcb/ghdl/helper"
"github.com/spf13/cobra"
)

Expand All @@ -17,29 +18,45 @@ var rootCmd = &cobra.Command{
gh-dl handles archived or compressed file as well`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
repo, tag := parseArg(args[0])
ghRelease := ghdl.GHRelease{RepoPath: repo, TagName: tag}
ghReleaseDl, err := ghRelease.GetGHReleases()
cmdFlags := cmd.Flags()
binaryNameFlag, err := cmdFlags.GetString("name")
if err != nil {
panic(err)
}
binaryNameFlag, err := cmd.Flags().GetString("name")
pathFlag, err := cmdFlags.GetString("path")
if err != nil {
panic(err)
}
repo, tag := parseArg(args[0])
ghRelease := ghdl.GHRelease{RepoPath: repo, TagName: tag}
ghReleaseDl, err := ghRelease.GetGHReleases()
if err != nil {
h.Print(fmt.Sprintf("get gh releases failed: %s\n", err), h.PrintModeErr)
}

if binaryNameFlag != "" {
ghReleaseDl.BinaryName = binaryNameFlag
}
if err := ghReleaseDl.DlTo("."); err != nil {
fmt.Printf(" error: %s\n", err)
return
if err := ghReleaseDl.DlTo(pathFlag); err != nil {
h.Print(fmt.Sprintf("download failed: %s", err), h.PrintModeErr)
os.Exit(1)
}
if err := ghReleaseDl.ExtractBinary(); err != nil {
fmt.Printf(" error: %s\n", err)
return
switch err {
case ghdl.NeedInstallError:
h.Print(fmt.Sprintf("%s. You can install %s with the appropriate commands", err, ghReleaseDl.BinaryName), h.PrintModeInfo)
os.Exit(0)
case ghdl.NoBinError:
h.Print(fmt.Sprintf("%s. Try specify binary name flag", err), h.PrintModeInfo)
os.Exit(0)
default:
h.Print(fmt.Sprintf("extract failed: %s", err), h.PrintModeErr)
os.Exit(1)
}
}
h.Print(fmt.Sprintf("saved binary executable to %s", ghReleaseDl.BinaryName), h.PrintModeSuccess)
if err := os.Chmod(ghReleaseDl.BinaryName, 0777); err != nil {
fmt.Printf(" error: %s\n", err)
h.Print(fmt.Sprintf("chmod failed: %s", err), h.PrintModeErr)
}
},
}
Expand All @@ -53,6 +70,7 @@ func main() {

func init() {
rootCmd.PersistentFlags().StringP("name", "n", "", "specify binary file name")
rootCmd.PersistentFlags().StringP("path", "p", ".", "save binary to `path`")
}

// parse user/repo[#tagname] arg
Expand Down
73 changes: 40 additions & 33 deletions dl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"archive/tar"
"archive/zip"
"compress/gzip"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -15,17 +16,24 @@ import (
humanize "github.com/dustin/go-humanize"
)

var (
NeedInstallError = errors.New(
"detected deb/rpm/apk package, download directly")
NoBinError = errors.New("binary file not found")
)

type GHReleaseDl struct {
BinaryName string
Url string
Size int64
}

// Download asset from github release
// dl.BinaryName path might change mutably
func (dl *GHReleaseDl) DlTo(path string) error {
if path != "" {
dl.BinaryName = filepath.Join(path, dl.BinaryName)
// dl.BinaryName shall change with full path mutably
func (dl *GHReleaseDl) DlTo(path string) (err error) {
dl.BinaryName, err = filepath.Abs(filepath.Join(path, dl.BinaryName))
if err != nil {
return err
}
req, err := http.NewRequest("GET", dl.Url, nil)
if err != nil {
Expand All @@ -42,14 +50,15 @@ func (dl *GHReleaseDl) DlTo(path string) error {
return err
}

file, err := os.Create(dl.BinaryName)
tmpfile, err := os.Create(dl.BinaryName + ".tmp")
if err != nil {
return err
}
defer tmpfile.Close()

// create progress tui
starter := func(updater func(float64)) {
if _, err := io.Copy(file, &pg.ProgressBytesReader{Reader: resp.Body, Handler: func(p int) {
if _, err := io.Copy(tmpfile, &pg.ProgressBytesReader{Reader: resp.Body, Handler: func(p int) {
updater(float64(p) / float64(dl.Size))
}}); err != nil {
panic(err)
Expand All @@ -60,18 +69,22 @@ func (dl *GHReleaseDl) DlTo(path string) error {
}

func (dl GHReleaseDl) ExtractBinary() error {
// `file` has no content, we must open it for reading
openfile, err := os.Open(dl.BinaryName)
tmpfileName := dl.BinaryName + ".tmp"
openfile, err := os.Open(tmpfileName)
if err != nil {
return err
}
defer openfile.Close()

fileExt := filepath.Ext(dl.Url)
var decompressedBinary io.Reader = nil
var decompressedBinary io.Reader
switch fileExt {
case ".zip":
decompressedBinary, err = dl.ZipBinary(openfile)
zipFile, err := dl.ZipBinary(openfile)
if err != nil {
return err
}
decompressedBinary, err = zipFile.Open()
if err != nil {
return err
}
Expand All @@ -93,48 +106,42 @@ func (dl GHReleaseDl) ExtractBinary() error {
case ".rpm":
case ".apk":
fileName := dl.BinaryName + fileExt
fmt.Printf("Detected deb/rpm/apk package, download directly to ./%s\nYou can install it with the appropriate commands\n", fileName)
if err := os.Rename(dl.BinaryName, fileName); err != nil {
if err := os.Rename(tmpfileName, fileName); err != nil {
panic(err)
}
return nil
return NeedInstallError
default:
defer os.Remove(dl.BinaryName)
return fmt.Errorf("unsupported file format")
defer os.Remove(tmpfileName)
return fmt.Errorf("unsupported file format: %v", fileExt)
}

// rewrite the file
defer os.Remove(tmpfileName)
out, err := os.Create(dl.BinaryName)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, decompressedBinary); err != nil {
return nil
return err
}

return nil
}

func (dl GHReleaseDl) ZipBinary(r *os.File) (io.Reader, error) {
func (dl GHReleaseDl) ZipBinary(r *os.File) (*zip.File, error) {
b := filepath.Base(dl.BinaryName)
zipR, err := zip.NewReader(r, dl.Size)
if err != nil {
return nil, err
}

for _, f := range zipR.File {
if filepath.Base(f.Name) == dl.BinaryName || len(zipR.File) == 1 {
open, err := f.Open()
if err != nil {
return nil, err
}
return open, nil
if filepath.Base(f.Name) == b || len(zipR.File) == 1 {
return f, nil
}
}
return nil, fmt.Errorf("Binary file %v not found", dl.BinaryName)
return nil, NoBinError
}

func (GHReleaseDl) GzBinary(r *os.File) (io.Reader, error) {
func (GHReleaseDl) GzBinary(r *os.File) (*gzip.Reader, error) {
gzR, err := gzip.NewReader(r)
if err != nil {
return nil, err
Expand All @@ -143,7 +150,8 @@ func (GHReleaseDl) GzBinary(r *os.File) (io.Reader, error) {
return gzR, nil
}

func (dl GHReleaseDl) TargzBinary(r *os.File) (io.Reader, error) {
func (dl GHReleaseDl) TargzBinary(r *os.File) (*tar.Reader, error) {
b := filepath.Base(dl.BinaryName)
gzR, err := gzip.NewReader(r)
if err != nil {
return nil, err
Expand All @@ -159,13 +167,12 @@ func (dl GHReleaseDl) TargzBinary(r *os.File) (io.Reader, error) {
if err != nil {
return nil, err
}
if (header.Typeflag != tar.TypeDir) && filepath.Base(header.Name) == dl.BinaryName {

if (header.Typeflag != tar.TypeDir) && filepath.Base(header.Name) == b {
if err != nil {
return nil, err
}
break
return tarR, nil
}
}
return tarR, nil
return nil, NoBinError
}
8 changes: 4 additions & 4 deletions helper/pg/pg.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"strings"

h "github.com/beetcb/ghdl/helper"
"github.com/charmbracelet/bubbles/progress"
tea "github.com/charmbracelet/bubbletea"
)
Expand Down Expand Up @@ -33,7 +34,7 @@ func (pbr *ProgressBytesReader) Read(b []byte) (n int, err error) {
}

const (
padding = 2
padding = 4
maxWidth = 80
)

Expand All @@ -50,8 +51,7 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

func (e model) View() string {
pad := strings.Repeat(" ", padding)
return "\n" +
pad + e.progress.ViewAs(e.percent) + fmt.Sprintf(" of %s", e.humanize) + "\n"
return "\n" + pad + e.progress.ViewAs(e.percent) + fmt.Sprintf(" of %s", e.humanize) + "\n\n"
}

func Progress(starter func(updater func(float64)), humanize string) {
Expand All @@ -66,7 +66,7 @@ func Progress(starter func(updater func(float64)), humanize string) {
}

if err := tea.NewProgram(&state).Start(); err != nil {
fmt.Println("Oh no!", err)
h.Print(fmt.Sprintln("Oh no!", err), h.PrintModeErr)
os.Exit(1)
}
}
27 changes: 27 additions & 0 deletions helper/print.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package helper

import (
"fmt"

"github.com/charmbracelet/lipgloss"
)

const maxWidth = 80

const (
PrintModeInfo = 0
PrintModeSuccess = 1
PrintModeErr = 2
)

func Print(str string, printMode int) {
var PaddingLeft = lipgloss.NewStyle().PaddingLeft(2).MaxWidth(maxWidth)
switch printMode {
case PrintModeInfo:
fmt.Println(PaddingLeft.Foreground(lipgloss.Color("11")).Render(str))
case PrintModeSuccess:
fmt.Println(PaddingLeft.Foreground(lipgloss.Color("14")).Render(str))
case PrintModeErr:
fmt.Println(PaddingLeft.Foreground(lipgloss.Color("202")).Render(str))
}
}
12 changes: 7 additions & 5 deletions helper/sl/sl.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"

h "github.com/beetcb/ghdl/helper"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
Expand Down Expand Up @@ -51,11 +52,12 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

func (m model) View() string {
blue := lipgloss.Color("14")
yellow := lipgloss.Color("11")
paddingS := lipgloss.NewStyle().PaddingLeft(2).MaxWidth(maxWidth)
colorS := paddingS.Copy().
Foreground(blue).BorderLeft(true).BorderForeground(blue)
if m.selected == -1 {
s := "\n" + paddingS.Render("gh-dl can't figure out which release to download\nplease select it manully") + "\n\n"
s := paddingS.Copy().Foreground(yellow).Render("gh-dl can't figure out which release to download\nplease select it manully") + "\n"
for i, choice := range m.choices {
if m.cursor == i {
s += colorS.Render(choice) + "\n"
Expand All @@ -64,18 +66,18 @@ func (m model) View() string {
}
}
// Send the UI for rendering
return s
return s + "\n"
} else {
s := paddingS.Render(fmt.Sprintf("start downloading %s", lipgloss.NewStyle().Foreground(blue).Render(m.choices[m.selected])))
return "\n" + s + "\n"
s := paddingS.Copy().Foreground(yellow).Render(fmt.Sprintf("start downloading %s", lipgloss.NewStyle().Foreground(blue).Render(m.choices[m.selected]))) + "\n"
return s
}
}

func Select(choices *[]string) int {
state := initialModel(choices)
p := tea.NewProgram(&state)
if err := p.Start(); err != nil {
fmt.Printf("Alas, there's been an error: %v", err)
h.Print(fmt.Sprintf("Alas, there's been an error: %v", err), h.PrintModeErr)
os.Exit(1)
}
return state.selected
Expand Down

0 comments on commit c98b896

Please sign in to comment.