From d9987b46f02e625df7d2a165e9294f19b4cdeb00 Mon Sep 17 00:00:00 2001 From: Franciszek Walkowiak Date: Fri, 19 Jan 2024 15:53:36 +0100 Subject: [PATCH] Address review comments --- README.md | 12 +++++++++--- cmd/mirror.go | 20 ++++++++++---------- cmd/root.go | 9 ++++++--- go.mod | 2 ++ 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 05e6e63..321b7d5 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ [![build](https://github.com/insightsengineering/git-synchronizer/actions/workflows/test.yml/badge.svg)](https://github.com/insightsengineering/git-synchronizer/actions/workflows/test.yml) +`git-synchronizer` allows you to mirror a collection of `git` repositories from one location to another. For each source repository, you can set a destination repository to which the source should be mirrored (see example [configuration file](#configuration-file)). + +`git-synchronizer` will: +* push all branches and tags from source to destination repository, +* remove branches and tags from the destination repository which are no longer present in source repository. + ## Installing Simply download the project for your distribution from the [releases](https://github.com/insightsengineering/git-synchronizer/releases) page. `git-synchronizer` is distributed as a single binary file and does not require any additional system requirements. @@ -16,7 +22,7 @@ git-synchronizer --help ## Configuration file -If you'd like to set the options in a configuration file, by default `git-synchronizer` checks `~/.git-synchronizer`, `~/.git-synchronizer.yaml` and `~/.git-synchronizer.yml` files. +By default `git-synchronizer` attempts to read `~/.git-synchronizer`, `~/.git-synchronizer.yaml` and `~/.git-synchronizer.yml` configuration files. If any of these files exist, `git-synchronizer` uses options defined there, unless they are overridden by command line flags. You can also specify custom path to configuration file with `--config .yml` command line flag. @@ -29,12 +35,12 @@ defaults: source: auth: method: token - # Name of environment variable storing the token. + # Name of environment variable storing the Personal Access Token with permissions to read source repositories. token_name: GITHUB_TOKEN destination: auth: method: token - # Name of environment variable storing the token. + # Name of environment variable storing the Personal Access Token with permissions to push to destination repositories. token_name: GITLAB_TOKEN # List of repository pairs to be synchronized. diff --git a/cmd/mirror.go b/cmd/mirror.go index bf7e9d5..b8acc1a 100644 --- a/cmd/mirror.go +++ b/cmd/mirror.go @@ -23,7 +23,7 @@ import ( "time" git "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" + gitconfig "github.com/go-git/go-git/v5/config" githttp "github.com/go-git/go-git/v5/plumbing/transport/http" ) @@ -67,7 +67,7 @@ func ValidateRepositories(repositories []RepositoryPair) { for _, repo := range repositories { if stringInSlice(repo.Destination.RepositoryURL, allDestinationRepositories) { log.Fatal( - "Error: multiple repositories set to be synchronized to the same destination repository: ", + "Multiple repositories set to be synchronized to the same destination repository: ", repo.Source.RepositoryURL, ) } @@ -78,7 +78,7 @@ func ValidateRepositories(repositories []RepositoryPair) { destinationProjectName := destinationURL[len(destinationURL)-1] if sourceProjectName != destinationProjectName { log.Warn( - "Warning: Source project name (", sourceProjectName, + "Source project name (", sourceProjectName, ") and destination project name (", destinationProjectName, ") differ!", ) } @@ -178,7 +178,7 @@ func GetFetchOptions(refSpec string, sourceAuth Authentication) *git.FetchOption } if sourcePat != "" { gitFetchOptions := &git.FetchOptions{ - RefSpecs: []config.RefSpec{config.RefSpec(refSpec)}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec(refSpec)}, Auth: &githttp.BasicAuth{ Username: basicAuthUsername, Password: sourcePat, @@ -187,7 +187,7 @@ func GetFetchOptions(refSpec string, sourceAuth Authentication) *git.FetchOption return gitFetchOptions } gitFetchOptions := &git.FetchOptions{ - RefSpecs: []config.RefSpec{config.RefSpec(refSpec)}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec(refSpec)}, } return gitFetchOptions } @@ -253,7 +253,7 @@ func MirrorRepository(messages chan MirrorStatus, source, destination string, so cloneDuration := time.Since(cloneStart) cloneEnd, pushStart := time.Now(), time.Now() - _, err = repository.CreateRemote(&config.RemoteConfig{ + _, err = repository.CreateRemote(&gitconfig.RemoteConfig{ Name: "destination", URLs: []string{destination}, }) @@ -277,7 +277,7 @@ func MirrorRepository(messages chan MirrorStatus, source, destination string, so log.Debug("Pushing branch ", branch, " to ", destination) err = repository.Push(&git.PushOptions{ RemoteName: "destination", - RefSpecs: []config.RefSpec{config.RefSpec("+" + refBranchPrefix + branch + ":" + refBranchPrefix + branch)}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec("+" + refBranchPrefix + branch + ":" + refBranchPrefix + branch)}, Auth: destinationAuth, Force: true, Atomic: true}) ProcessError(err, "pushing branch "+branch+" to ", destination, &allErrors) } @@ -288,7 +288,7 @@ func MirrorRepository(messages chan MirrorStatus, source, destination string, so log.Info("Removing branch ", branch, " from ", destination) err = repository.Push(&git.PushOptions{ RemoteName: "destination", - RefSpecs: []config.RefSpec{config.RefSpec(":" + refBranchPrefix + branch)}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec(":" + refBranchPrefix + branch)}, Auth: destinationAuth, Force: true, Atomic: true}) ProcessError(err, "removing branch "+branch+" from ", destination, &allErrors) } @@ -297,7 +297,7 @@ func MirrorRepository(messages chan MirrorStatus, source, destination string, so log.Info("Pushing all tags from ", source, " to ", destination) err = repository.Push(&git.PushOptions{ RemoteName: "destination", - RefSpecs: []config.RefSpec{config.RefSpec("+" + refTagPrefix + "*:" + refTagPrefix + "*")}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec("+" + refTagPrefix + "*:" + refTagPrefix + "*")}, Auth: destinationAuth, Force: true, Atomic: true}) ProcessError(err, "pushing all tags to ", destination, &allErrors) @@ -307,7 +307,7 @@ func MirrorRepository(messages chan MirrorStatus, source, destination string, so log.Info("Removing tag ", tag, " from ", destination) err := repository.Push(&git.PushOptions{ RemoteName: "destination", - RefSpecs: []config.RefSpec{config.RefSpec(":" + refTagPrefix + tag)}, + RefSpecs: []gitconfig.RefSpec{gitconfig.RefSpec(":" + refTagPrefix + tag)}, Auth: destinationAuth, Force: true, Atomic: true}) ProcessError(err, "removing tag "+tag+" from ", destination, &allErrors) } diff --git a/cmd/root.go b/cmd/root.go index 16eebb7..6d63e2d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,6 +30,7 @@ import ( var cfgFile string var logLevel string +var workingDirectory string type RepositoryPair struct { Source Repository `mapstructure:"source"` @@ -101,9 +102,9 @@ func newRootCommand() { log.Trace("defaultSettings = ", string(defaultSettingsJSON)) if runtime.GOOS == "windows" { - localTempDirectory = os.Getenv("TMP") + `\tmp\git-synchronizer` + localTempDirectory = os.Getenv("TMP") + workingDirectory } else { - localTempDirectory = "/tmp/git-synchronizer" + localTempDirectory = workingDirectory } SetRepositoryAuth(&inputRepositories, defaultSettings) @@ -119,6 +120,8 @@ func newRootCommand() { "config file (default is $HOME/.git-synchronizer.yaml)") rootCmd.PersistentFlags().StringVarP(&logLevel, "logLevel", "l", "info", "Logging level (trace, debug, info, warn, error). ") + rootCmd.PersistentFlags().StringVarP(&workingDirectory, "workingDirectory", "w", "/tmp/git-synchronizer", + "Directory where synchronized repositories will be cloned.") // Add version command. rootCmd.AddCommand(extension.NewVersionCobraCmd()) @@ -168,7 +171,7 @@ func Execute() { func initializeConfig() { for _, v := range []string{ - "logLevel", + "logLevel", "workingDirectory", } { // If the flag has not been set in newRootCommand() and it has been set in initConfig(). // In other words: if it's not been provided in command line, but has been diff --git a/go.mod b/go.mod index aad0ebc..2cec9ee 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/insightsengineering/git-synchronizer go 1.21 +toolchain go1.21.6 + require ( github.com/go-git/go-git/v5 v5.11.0 github.com/jamiealquiza/envy v1.1.0