Skip to content

Commit

Permalink
move all survey to cli commands
Browse files Browse the repository at this point in the history
  • Loading branch information
qiushiyan committed May 23, 2024
1 parent e439fb4 commit ba61f1b
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 88 deletions.
89 changes: 85 additions & 4 deletions cmd/clear.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,105 @@
package cmd

import (
"fmt"
"os"
"path"
"path/filepath"
"strings"

"github.com/AlecAivazis/survey/v2"
degit "github.com/qiushiyan/degit/pkg"
"github.com/spf13/cobra"
)

// clearCmd represents the clear command
var clearCmd = &cobra.Command{
Use: "clear",
Short: "Clear all existing download caches. Accept an optional argument to filter by the repository",
Long: `Clear all existing download caches. Accept an optional argument to filter by the repository`,
Use: "clear [filter]",
Short: "Clear download caches",
Long: `Clear all existing download caches. Accept an optional argument to filter by the repository.`,
Args: cobra.MatchAll(cobra.MaximumNArgs(1)),
RunE: func(cmd *cobra.Command, args []string) error {
var filter string
if len(args) > 0 {
filter = args[0]
}
return degit.ClearCache(filter, Verbose)

dir := degit.GetCacheDir()
if stat, err := os.Stat(dir); err != nil || !stat.IsDir() {
fmt.Println("no cache found, skipping")
return nil
}

var confirm bool

if filter == "" {
count, err := countRepoLevelDirectories(dir)
if err != nil {
return err
}
err = survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf(
"Are you sure you want to clear caches for %d repositories?",
count,
),
},
&confirm,
)
if err != nil {
return err
}
} else {
r, err := degit.ParseRepo(filter)
if err != nil {
return err
}

dir := path.Join(dir, r.Site, r.User, r.Name)
if stat, err := os.Stat(dir); err != nil || !stat.IsDir() {
fmt.Printf("no cache found for %s\n", filter)
return nil
}

err = survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf("Are you sure you want to clear cache for %s?", filter),
},
&confirm,
)

if err != nil {
return err
}
}

if confirm {
return degit.ClearCache(filter, Verbose)
}

return nil
},
}

func init() {
rootCmd.AddCommand(clearCmd)
}

func countRepoLevelDirectories(root string) (int, error) {
count := 0
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// site/user/repo
if info.IsDir() && depth(root, path) == 3 {
count++
}
return nil
})
return count, err
}

func depth(root, path string) int {
return len(strings.Split(strings.TrimPrefix(path, root), "/"))
}
24 changes: 18 additions & 6 deletions cmd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"os"

degit "github.com/qiushiyan/degit/pkg"
"github.com/spf13/cobra"
Expand All @@ -10,21 +11,32 @@ import (
// cloneCmd represents the clone command
var cloneCmd = &cobra.Command{
Use: "clone <src> <dst>",
Short: "Clone a repository into a local destination directory",
Long: `Clone a repository into a local destination directory`,
Short: "Clone a repository locally",
Long: `Downloads a repository into a local destination directory.`,
Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("clone requires two arguments")
dst := args[1]
if !Force {
stat, err := os.Stat(dst)
if err == nil && stat.IsDir() {
return fmt.Errorf("destination %s already exists, use --force to overwrite", dst)
}
}
repo, err := degit.Parse(args[0])

err := degit.Clone(args[0], args[1], Force, Verbose)
if err != nil {
return err
}
err = repo.Clone(args[1], Force, Verbose)

entries, err := os.ReadDir(dst)
if err != nil {
return err
}

if len(entries) == 0 {
fmt.Println("output directory is empty. did you specify the correct directory?")
}

return nil
},
}
Expand Down
8 changes: 4 additions & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ var Force bool
var rootCmd = &cobra.Command{
Use: "degit user/repo#ref output-dir",
Short: "Straightforward project scaffolding",
Long: `A Go port of the node degit cli.
Long: `A Go port of the node degit cli https://github.com/rich-harris/degit.
Usage:
degit user/repo#ref output-dir
downloads the github repository locally. You can specify subdirectories and use Gitlab and Bitbucket repositories as well. If the commit hash does not change, degit uses the cached version to save downloading again.`,
This will download a tarball for the repository github.com/user/repo at "ref" locally, and extracts it to output-dir. You can specify subdirectories and use Gitlab and Bitbucket repositories as well. degit also maintains a cache of downloaded tarballs that can be cleared with "degit clear".`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
Expand Down
72 changes: 9 additions & 63 deletions pkg/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@ import (
"io"
"os"
"path"
"path/filepath"
"strings"
"time"

"github.com/AlecAivazis/survey/v2"
)

var accessLogName = "access.json"
var hashLogName = "map.json"
var base = path.Join(homeOrTmp(), ".go-degit")

// ClearCache remove cache folder for repositories. If filter is empty, all caches are cleared.
func ClearCache(filter string, verbose bool) error {
base := GetCacheDir()
ok, err := exists(base)
if err != nil {
return err
Expand All @@ -28,34 +25,11 @@ func ClearCache(filter string, verbose bool) error {
}
return nil
}
var confirm bool
if filter == "" {
if verbose {
count, err := countRepoLevelDirectories(base)
if err != nil {
return err
}
err = survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf(
"Are you sure you want to clear all caches for %d repositories?",
count,
),
},
&confirm,
)
if err != nil {
return err
}
if confirm {
os.RemoveAll(base)
}
} else {
return os.RemoveAll(base)
}
return os.RemoveAll(base)
}

r, err := Parse(filter)
r, err := ParseRepo(filter)
if err != nil {
return err
}
Expand All @@ -72,21 +46,12 @@ func ClearCache(filter string, verbose bool) error {
return nil
}

err = survey.AskOne(
&survey.Confirm{
Message: fmt.Sprintf("Are you sure you want to clear cache for %s?", filter),
},
&confirm,
)

if err != nil {
return err
}
if confirm {
os.RemoveAll(dir)
}
return os.RemoveAll(dir)
}

return nil
// GetCacheDir returns the cache directory, usually $HOME/.go-degit
func GetCacheDir() string {
return path.Join(homeOrTmp(), ".go-degit")
}

func updateCache(dir string, ref string, hash string, verbose bool) error {
Expand Down Expand Up @@ -202,25 +167,6 @@ func updateHashLog(dir string, ref string, hash string, verbose bool) error {
return err
}

func countRepoLevelDirectories(root string) (int, error) {
count := 0
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// site/user/repo
if info.IsDir() && depth(root, path) == 3 {
count++
}
return nil
})
return count, err
}

func depth(root, path string) int {
return len(strings.Split(strings.TrimPrefix(path, root), "/"))
}

func homeOrTmp() string {
if s, err := os.UserHomeDir(); s != "" && err == nil {
return s
Expand Down
11 changes: 11 additions & 0 deletions pkg/clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package degit

// Clone parses input string as a remote repository and downloads its tarball into the destination directory
func Clone(src string, dst string, force bool, verbose bool) error {
repo, err := ParseRepo(src)
if err != nil {
return err
}

return repo.Clone(dst, force, verbose)
}
4 changes: 2 additions & 2 deletions pkg/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ var supportedHosts = map[string]bool{
"git.sr.ht": true,
}

func Parse(src string) (*Repo, error) {
func ParseRepo(src string) (*Repo, error) {
re := regexp.MustCompile(
`^(?:(?:https:\/\/)?([^:/]+\.[^:/]+)\/|git@([^:/]+)[:/]|([^/]+):)?([^/\s]+)\/([^/\s#]+)(?:((?:\/[^/\s#]+)+))?(?:\/)?(?:#(.+))?`,
)
match := re.FindStringSubmatch(src)
if match == nil {
return nil, errors.New(fmt.Sprintf("could not parse %s", src))
return nil, fmt.Errorf("could not parse %s", src)
}

site := firstNonEmpty(match[1], match[2], match[3], "github")
Expand Down
2 changes: 1 addition & 1 deletion pkg/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestParse(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
repo, err := Parse(tc.url)
repo, err := ParseRepo(tc.url)
if tc.err != nil {
if err == nil || err.Error() != tc.err.Error() {
t.Errorf("Expected error '%v', got '%v'", tc.err, err)
Expand Down
20 changes: 13 additions & 7 deletions pkg/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Repo struct {
Subdir string
}

// Clone copies the repository to the destination directory
// Clone downloads the repository into the destination
func (r *Repo) Clone(dst string, force bool, verbose bool) error {
dstExists, err := exists(dst)
if err != nil {
Expand All @@ -38,7 +38,7 @@ func (r *Repo) Clone(dst string, force bool, verbose bool) error {
return err
}
} else {
return fmt.Errorf("output location %s already exists, use --force to overwrite", dst)
return fmt.Errorf("output location %s already exists", dst)
}
}

Expand Down Expand Up @@ -80,7 +80,12 @@ func (r *Repo) Clone(dst string, force bool, verbose bool) error {
return err
}

return untar(file, dst, r.Subdir, fmt.Sprintf("%s-%s", r.Name, hash))
err = untar(file, dst, r.Subdir, fmt.Sprintf("%s-%s", r.Name, hash))
if err != nil {
return err
}

return nil
}

func (r *Repo) download(dst string, hash string, verbose bool) error {
Expand Down Expand Up @@ -119,7 +124,7 @@ func (r *Repo) download(dst string, hash string, verbose bool) error {
}

func (r *Repo) getOutputFile(hash string) string {
return path.Join(base, r.Site, r.User, r.Name, fmt.Sprintf("%s.tar.gz", hash))
return path.Join(GetCacheDir(), r.Site, r.User, r.Name, fmt.Sprintf("%s.tar.gz", hash))
}

func (r *Repo) getHash(refs []*ref) (string, error) {
Expand Down Expand Up @@ -150,7 +155,7 @@ func (r *Repo) getHash(refs []*ref) (string, error) {
}
}

return "", fmt.Errorf("could not find ref %s", r.Ref)
return "", fmt.Errorf("could not find ref %s for repo %s", r.Ref, r.URL)
}

func log(verbose bool, msg ...any) {
Expand All @@ -170,8 +175,9 @@ func (r *Repo) getRefs() ([]*ref, error) {

var output []byte
var err error

if output, err = cmd.Output(); err != nil {
return nil, err
return nil, fmt.Errorf("could not find repository %s", r.URL)
}

var result []*ref
Expand All @@ -191,7 +197,7 @@ func (r *Repo) getRefs() ([]*ref, error) {
re := regexp.MustCompile(`refs\/([\w-]+)\/(.+)`)
match := re.FindStringSubmatch(r)
if match == nil {
return nil, fmt.Errorf("could not parse %s", r)
return nil, fmt.Errorf("could not parse git history %s", r)
}

var refType string
Expand Down
Loading

0 comments on commit ba61f1b

Please sign in to comment.