From 33e147b3174827c9e93a2d57e4e099b6ea033f43 Mon Sep 17 00:00:00 2001 From: Jay Gabriels Date: Tue, 28 Sep 2021 20:12:11 -0700 Subject: [PATCH] Add no clean flag (#154) --- CHANGELOG.md | 9 +++++++++ README.md | 2 +- cmd/clone.go | 37 ++++++++++++++++++++++--------------- cmd/clone_test.go | 4 ++++ cmd/root.go | 8 +++++++- cmd/version.go | 2 +- git/git.go | 7 +++++++ sample-conf.yaml | 5 +++++ 8 files changed, 56 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b1be9..1383409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +## [1.7.2] - Unreleased +### Added +- GHORG_NO_CLEAN only clones new repos and does not perform a git clean on existing repos +### Changed +### Deprecated +### Removed +### Fixed +### Security + ## [1.7.1] - 9/27/21 ### Added - all-groups for cloning all groups on a hosted gitlab instance diff --git a/README.md b/README.md index b289f36..425ddb7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ghorg allows you to quickly clone all of an orgs, or users repos into a single d 4. Onboarding 5. Performing Audits -> When running ghorg a second time on the same org/user, all local changes in the cloned directory will be overwritten by what's on GitHub. If you are working out of this directory, make sure you rename it before running a second time otherwise all of your changes will be lost. +> When running ghorg a second time on the same org/user, all local changes in the cloned directory by default will be overwritten by what's on GitHub. If you want to work out of this directory, make sure you either rename the directory before running a second time otherwise all of your changes will be lost. Otherwise you will need use the `--no-clean` flag on all future clones to prevent losing your changes.

ghorg cli example diff --git a/cmd/clone.go b/cmd/clone.go index 8bc6acd..9375d46 100644 --- a/cmd/clone.go +++ b/cmd/clone.go @@ -86,6 +86,10 @@ func cloneFunc(cmd *cobra.Command, argz []string) { os.Setenv("GHORG_SKIP_ARCHIVED", "true") } + if cmd.Flags().Changed("no-clean") { + os.Setenv("GHORG_NO_CLEAN", "true") + } + if cmd.Flags().Changed("skip-forks") { os.Setenv("GHORG_SKIP_FORKS", "true") } @@ -264,7 +268,7 @@ func readGhorgIgnore() ([]string, error) { func filterByRegex(repos []scm.Repo) []scm.Repo { filteredRepos := []scm.Repo{} - regex := fmt.Sprintf(`%s`, os.Getenv("GHORG_MATCH_REGEX")) + regex := fmt.Sprint(os.Getenv("GHORG_MATCH_REGEX")) for i, r := range repos { re := regexp.MustCompile(regex) @@ -311,7 +315,7 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) { } } - if flag == false { + if !flag { filteredCloneTargets = append(filteredCloneTargets, cloned) } } @@ -352,7 +356,7 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) { action := "cloning" - if repoExistsLocally(repo.HostPath) == true { + if repoExistsLocally(repo.HostPath) { if os.Getenv("GHORG_BACKUP") == "true" { err := git.UpdateRemote(repo) @@ -361,6 +365,16 @@ func CloneAllRepos(git git.Gitter, cloneTargets []scm.Repo) { cloneErrors = append(cloneErrors, e) return } + } else if os.Getenv("GHORG_NO_CLEAN") == "true" { + action = "fetching" + err := git.FetchAll(repo) + + if err != nil { + e := fmt.Sprintf("Could not fetch remotes in Repo: %s Error: %v", repo.URL, err) + cloneErrors = append(cloneErrors, e) + return + } + } else { err := git.Checkout(repo) @@ -466,7 +480,6 @@ func PrintConfigs() { if os.Getenv("GHORG_BRANCH") != "" { colorlog.PrintInfo("* Branch : " + getGhorgBranch()) } - if os.Getenv("GHORG_SCM_BASE_URL") != "" { colorlog.PrintInfo("* Base URL : " + os.Getenv("GHORG_SCM_BASE_URL")) } @@ -479,7 +492,7 @@ func PrintConfigs() { if os.Getenv("GHORG_BACKUP") == "true" { colorlog.PrintInfo("* Backup : " + os.Getenv("GHORG_BACKUP")) } - if configs.GhorgIgnoreDetected() == true { + if configs.GhorgIgnoreDetected() { colorlog.PrintInfo("* Ghorgignore : true") } if os.Getenv("GHORG_MATCH_REGEX") != "" { @@ -488,6 +501,10 @@ func PrintConfigs() { if os.Getenv("GHORG_OUTPUT_DIR") != "" { colorlog.PrintInfo("* Output Dir : " + parentFolder) } + if os.Getenv("GHORG_NO_CLEAN") != "" { + colorlog.PrintInfo("* No Clean : " + "true") + } + colorlog.PrintInfo("* Config Used : " + os.Getenv("GHORG_CONF")) colorlog.PrintInfo("*************************************") @@ -502,16 +519,6 @@ func getGhorgBranch() string { return os.Getenv("GHORG_BRANCH") } -func addTokenToHTTPSCloneURL(url string, token string) string { - splitURL := strings.Split(url, "https://") - - if os.Getenv("GHORG_SCM_TYPE") == "gitlab" { - return "https://oauth2:" + token + "@" + splitURL[1] - } - - return "https://" + token + "@" + splitURL[1] -} - func parseParentFolder(argz []string) { if os.Getenv("GHORG_OUTPUT_DIR") != "" { parentFolder = os.Getenv("GHORG_OUTPUT_DIR") diff --git a/cmd/clone_test.go b/cmd/clone_test.go index 23c7655..9bdd1e2 100644 --- a/cmd/clone_test.go +++ b/cmd/clone_test.go @@ -88,6 +88,10 @@ func (g MockGitClient) Reset(repo scm.Repo) error { return nil } +func (g MockGitClient) FetchAll(repo scm.Repo) error { + return nil +} + func TestInitialClone(t *testing.T) { dir, err := ioutil.TempDir(".", "ghorg_tests") if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 590a31b..9715551 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -28,6 +28,8 @@ var ( skipArchived bool skipForks bool backup bool + noClean bool + preserveDir bool args []string cloneErrors []string cloneInfos []string @@ -82,6 +84,8 @@ func getOrSetDefaults(envVar string) { os.Setenv(envVar, "false") case "GHORG_SKIP_FORKS": os.Setenv(envVar, "false") + case "GHORG_NO_CLEAN": + os.Setenv(envVar, "false") case "GHORG_BACKUP": os.Setenv(envVar, "false") case "GHORG_COLOR": @@ -139,6 +143,7 @@ func InitConfig() { getOrSetDefaults("GHORG_SCM_TYPE") getOrSetDefaults("GHORG_SKIP_ARCHIVED") getOrSetDefaults("GHORG_SKIP_FORKS") + getOrSetDefaults("GHORG_NO_CLEAN") getOrSetDefaults("GHORG_BACKUP") getOrSetDefaults("GHORG_CONCURRENCY") getOrSetDefaults("GHORG_MATCH_PREFIX") @@ -181,8 +186,9 @@ func init() { cloneCmd.Flags().StringVarP(&scmType, "scm", "s", "", "GHORG_SCM_TYPE - type of scm used, github, gitlab, gitea or bitbucket (default github)") cloneCmd.Flags().StringVarP(&cloneType, "clone-type", "c", "", "GHORG_CLONE_TYPE - clone target type, user or org (default org)") cloneCmd.Flags().BoolVar(&skipArchived, "skip-archived", false, "GHORG_SKIP_ARCHIVED - skips archived repos, github/gitlab/gitea only") + cloneCmd.Flags().BoolVar(&noClean, "no-clean", false, "GHORG_NO_CLEAN - only clones new repos and does not perform a git clean on existing repos") cloneCmd.Flags().BoolVar(&skipForks, "skip-forks", false, "GHORG_SKIP_FORKS - skips repo if its a fork, github/gitlab/gitea only") - cloneCmd.Flags().BoolVar(&skipArchived, "preserve-dir", false, "GHORG_PRESERVE_DIRECTORY_STRUCTURE - clones repos in a directory structure that matches gitlab namespaces eg company/unit/subunit/app would clone into ghorg/unit/subunit/app, gitlab only") + cloneCmd.Flags().BoolVar(&preserveDir, "preserve-dir", false, "GHORG_PRESERVE_DIRECTORY_STRUCTURE - clones repos in a directory structure that matches gitlab namespaces eg company/unit/subunit/app would clone into ghorg/unit/subunit/app, gitlab only") cloneCmd.Flags().BoolVar(&backup, "backup", false, "GHORG_BACKUP - backup mode, clone as mirror, no working copy (ignores branch parameter)") cloneCmd.Flags().StringVarP(&baseURL, "base-url", "", "", "GHORG_SCM_BASE_URL - change SCM base url, for on self hosted instances (currently gitlab, gitea and github (use format of https://git.mydomain.com/api/v3))") cloneCmd.Flags().StringVarP(&concurrency, "concurrency", "", "", "GHORG_CONCURRENCY - max goroutines to spin up while cloning (default 25)") diff --git a/cmd/version.go b/cmd/version.go index b1f9843..323dbe4 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -11,6 +11,6 @@ var versionCmd = &cobra.Command{ Short: "Print the version number of Ghorg", Long: `All software has versions. This is Ghorg's`, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("v1.7.1") + fmt.Println("v1.7.2") }, } diff --git a/git/git.go b/git/git.go index d48116a..fc721b1 100644 --- a/git/git.go +++ b/git/git.go @@ -15,6 +15,7 @@ type Gitter interface { Clean(scm.Repo) error Checkout(scm.Repo) error UpdateRemote(scm.Repo) error + FetchAll(scm.Repo) error } type GitClient struct{} @@ -70,3 +71,9 @@ func (g GitClient) Reset(repo scm.Repo) error { cmd.Dir = repo.HostPath return cmd.Run() } + +func (g GitClient) FetchAll(repo scm.Repo) error { + cmd := exec.Command("git", "fetch", "--all") + cmd.Dir = repo.HostPath + return cmd.Run() +} diff --git a/sample-conf.yaml b/sample-conf.yaml index d94daa9..da6410f 100644 --- a/sample-conf.yaml +++ b/sample-conf.yaml @@ -125,6 +125,11 @@ GHORG_MATCH_PREFIX: # flag (--match-regex) GHORG_MATCH_REGEX: +# Only clones new repos and does not perform a git clean on existing repos +# default: false +# flag (--no-clean) +GHORG_NO_CLEAN: + # Specifies the location of your ghorg conf.yaml, allowing you to have many configuration files, or none at all # default: ghorg looks in $HOME/.config/ghorg/conf.yaml, if not set in that location nor as a commandline flag, ghorg will use all default values # NOTE: this cannot be set in the configuration file and only available in ghorg v1.7.0+