From 08add68314816b2dbcf28c69ba548c00c573e25c Mon Sep 17 00:00:00 2001 From: gabrie30 Date: Mon, 16 Sep 2024 18:22:06 -0700 Subject: [PATCH] Update/ghorgls (#450) --- CHANGELOG.md | 1 + README.md | 2 +- cmd/clone.go | 21 ++------------- cmd/ls.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++-- cmd/root.go | 3 +++ utils/slice.go | 11 -------- utils/utils.go | 34 ++++++++++++++++++++++++ 7 files changed, 111 insertions(+), 33 deletions(-) delete mode 100644 utils/slice.go create mode 100644 utils/utils.go diff --git a/CHANGELOG.md b/CHANGELOG.md index eebefec88a..0e7c52b166 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ### Added - GHORG_NO_DIR_SIZE flag to turn off directory size output which is now enabled by default - GHORG_STATS_ENABLED flag to track clone data over time, set to false by default +- Added two new flags to the `ghorg ls` command: `--long` and `--total`, which provide additional information about the cloned directories. ### Changed ### Deprecated ### Removed diff --git a/README.md b/README.md index d7f1771a4d..df244f617b 100644 --- a/README.md +++ b/README.md @@ -351,7 +351,7 @@ Below are the headers and their descriptions. Note that these headers may change - **newClonesCount**: Sum of all new repos cloned - **existingResourcesPulledCount**: Sum of all repos that were pulled - **dirSizeInMB**: The size in megabytes of the output dir -- **newCommits**: Sum of all new commits in all repos pulled or cloned +- **newCommits**: Sum of all new commits in all repos pulled - **cloneInfosCount**: Number of clone Info messages - **cloneErrorsCount**: Number of clone Issues/Errors - **updateRemoteCount**: Number of remotes updated diff --git a/cmd/clone.go b/cmd/clone.go index d2ba94e104..3db3b1872e 100644 --- a/cmd/clone.go +++ b/cmd/clone.go @@ -19,6 +19,7 @@ import ( "github.com/gabrie30/ghorg/configs" "github.com/gabrie30/ghorg/git" "github.com/gabrie30/ghorg/scm" + "github.com/gabrie30/ghorg/utils" "github.com/korovkin/limiter" "github.com/spf13/cobra" ) @@ -1090,7 +1091,7 @@ func printFinishedWithDirSize() { func getCachedOrCalculatedOutputDirSizeInMb() (float64, error) { if !isDirSizeCached { - dirSizeMB, err := calculateDirSizeInMb(outputDirAbsolutePath) + dirSizeMB, err := utils.CalculateDirSizeInMb(outputDirAbsolutePath) if err != nil { return 0, err } @@ -1100,24 +1101,6 @@ func getCachedOrCalculatedOutputDirSizeInMb() (float64, error) { return cachedDirSizeMB, nil } -func calculateDirSizeInMb(path string) (float64, error) { - var size int64 - err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - size += info.Size() - } - return nil - }) - if err != nil { - return 0, err - } - const bytesInMegabyte = 1000 * 1000 - return float64(size) / bytesInMegabyte, nil // Return size in Megabyte -} - func filterByTargetReposPath(cloneTargets []scm.Repo) []scm.Repo { _, err := os.Stat(os.Getenv("GHORG_TARGET_REPOS_PATH")) diff --git a/cmd/ls.go b/cmd/ls.go index 52bf089e37..bdf9e34809 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -1,11 +1,13 @@ package cmd import ( + "fmt" "os" "path/filepath" "strings" "github.com/gabrie30/ghorg/colorlog" + "github.com/gabrie30/ghorg/utils" "github.com/spf13/cobra" ) @@ -36,9 +38,76 @@ func listGhorgHome() { colorlog.PrintError("No clones found. Please clone some and try again.") } + longFormat := false + totalFormat := false + for _, arg := range os.Args { + if arg == "-l" || arg == "--long" { + longFormat = true + } + if arg == "-t" || arg == "--total" { + totalFormat = true + } + } + + if !longFormat && !totalFormat { + for _, f := range files { + if f.IsDir() { + colorlog.PrintInfo(path + f.Name()) + } + } + return + } + + var totalDirs int + var totalSizeMB float64 + var totalRepos int + for _, f := range files { if f.IsDir() { - colorlog.PrintInfo(path + f.Name()) + totalDirs++ + dirPath := filepath.Join(path, f.Name()) + dirSizeMB, err := utils.CalculateDirSizeInMb(dirPath) + if err != nil { + colorlog.PrintError(fmt.Sprintf("Error calculating directory size for %s: %v", dirPath, err)) + continue + } + totalSizeMB += dirSizeMB + + // Count the number of directories with a depth of 1 inside + subDirCount := 0 + subFiles, err := os.ReadDir(dirPath) + if err != nil { + colorlog.PrintError(fmt.Sprintf("Error reading directory contents for %s: %v", dirPath, err)) + continue + } + for _, subFile := range subFiles { + if subFile.IsDir() { + subDirCount++ + } + } + totalRepos += subDirCount + + if !totalFormat || longFormat { + if longFormat { + if dirSizeMB > 1000 { + dirSizeGB := dirSizeMB / 1000 + colorlog.PrintInfo(fmt.Sprintf("%-50s %10.2f GB %10d repos", dirPath, dirSizeGB, subDirCount)) + } else { + colorlog.PrintInfo(fmt.Sprintf("%-50s %10.2f MB %10d repos", dirPath, dirSizeMB, subDirCount)) + } + } else { + colorlog.PrintInfo(path + f.Name()) + } + } + } + } + + if totalFormat { + if totalSizeMB > 1000 { + totalSizeGB := totalSizeMB / 1000 + colorlog.PrintInfo(fmt.Sprintf("Total: %d directories, %.2f GB, %d repos", totalDirs, totalSizeGB, totalRepos)) + } else { + colorlog.PrintInfo(fmt.Sprintf("Total: %d directories, %.2f MB, %d repos", totalDirs, totalSizeMB, totalRepos)) } } } @@ -60,7 +129,6 @@ func listGhorgDir(arg string) { colorlog.PrintError("No clones found. Please clone some and try again.") } - for _, f := range files { if f.IsDir() { str := filepath.Join(path, f.Name()) diff --git a/cmd/root.go b/cmd/root.go index d8926735a0..040bac0baa 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -351,6 +351,9 @@ func init() { reCloneCmd.Flags().BoolVar(&ghorgReCloneList, "list", false, "Prints reclone commands and optional descriptions to stdout then will exit 0. Does not obsfucate tokens, and is only available as a commandline argument") reCloneCmd.Flags().BoolVar(&ghorgReCloneEnvConfigOnly, "env-config-only", false, "GHORG_RECLONE_ENV_CONFIG_ONLY - Only use environment variables to set the configuration for all reclones.") + lsCmd.Flags().BoolP("long", "l", false, "Display detailed information about each clone directory, including size and number of repositories. Note: This may take longer depending on the number and size of the cloned organizations.") + lsCmd.Flags().BoolP("total", "t", false, "Display total amounts of all repos cloned. Note: This may take longer depending on the number and size of the cloned organizations.") + rootCmd.AddCommand(lsCmd, versionCmd, cloneCmd, reCloneCmd, examplesCmd) } diff --git a/utils/slice.go b/utils/slice.go deleted file mode 100644 index 0e17cf108b..0000000000 --- a/utils/slice.go +++ /dev/null @@ -1,11 +0,0 @@ -package utils - -// IsStringInSlice check if a string is in a given slice -func IsStringInSlice(s string, sl []string) bool { - for i := range sl { - if sl[i] == s { - return true - } - } - return false -} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000000..84f8288f3a --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,34 @@ +package utils + +import ( + "os" + "path/filepath" +) + +// IsStringInSlice check if a string is in a given slice +func IsStringInSlice(s string, sl []string) bool { + for i := range sl { + if sl[i] == s { + return true + } + } + return false +} + +func CalculateDirSizeInMb(path string) (float64, error) { + var size int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + size += info.Size() + } + return nil + }) + if err != nil { + return 0, err + } + const bytesInMegabyte = 1000 * 1000 + return float64(size) / bytesInMegabyte, nil // Return size in Megabyte +}