mirror of
https://github.com/gabrie30/ghorg.git
synced 2025-08-06 06:17:09 +02:00
parent
5885fa010c
commit
334e730616
@ -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.8.1] - unreleased
|
||||
### Added
|
||||
- Reclone command
|
||||
### Changed
|
||||
### Deprecated
|
||||
### Removed
|
||||
### Fixed
|
||||
### Security
|
||||
|
||||
## [1.8.0] - 6/25/22
|
||||
### Added
|
||||
- Exit 1 when any issue messages are produced; thanks @i3v
|
||||
|
24
README.md
24
README.md
@ -273,6 +273,30 @@ git init
|
||||
git checkout master
|
||||
```
|
||||
|
||||
## ReCloning Multiple Users/Orgs/Configurations
|
||||
|
||||
If you have multiple orgs/users to clone that all have different configurations you can use `ghorg reclone` to clone each of these at once or individually.
|
||||
|
||||
To use, add a reclone.yaml to your $HOME/.config/ghorg directory. You can use the following command to set it for you with examples to use as a template
|
||||
|
||||
```
|
||||
curl https://raw.githubusercontent.com/gabrie30/ghorg/master/sample-reclone.yaml > $HOME/.config/ghorg/reclone.yaml
|
||||
```
|
||||
|
||||
After updating your reclone.yaml you can run
|
||||
|
||||
```
|
||||
# To clone all the entries in your reclone.yaml
|
||||
ghorg reclone
|
||||
```
|
||||
|
||||
```
|
||||
# To run one or more entries in your reclone.yaml
|
||||
ghorg reclone kubernetes-sig gitlab-examples
|
||||
```
|
||||
|
||||
This will essentially run a for loop over each specified key in your reclone.yaml configuration.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- If you are having trouble cloning repos. Try to clone one of the repos locally e.g. manually running `git clone https://github.com/your_private_org/your_private_repo.git` if this does not work, ghorg will also not work. Your git client must first be setup to clone the target repos. If you normally clone using an ssh key use the `--protocol=ssh` flag with ghorg. This will fetch the ssh clone urls instead of the https clone urls.
|
||||
|
@ -112,6 +112,11 @@ func cloneFunc(cmd *cobra.Command, argz []string) {
|
||||
os.Setenv("GHORG_IGNORE_PATH", path)
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("reclone-path") {
|
||||
path := cmd.Flag("reclone-path").Value.String()
|
||||
os.Setenv("GHORG_RECLONE_PATH", path)
|
||||
}
|
||||
|
||||
if cmd.Flags().Changed("skip-archived") {
|
||||
os.Setenv("GHORG_SKIP_ARCHIVED", "true")
|
||||
}
|
||||
|
88
cmd/reclone.go
Normal file
88
cmd/reclone.go
Normal file
@ -0,0 +1,88 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gabrie30/ghorg/colorlog"
|
||||
"github.com/gabrie30/ghorg/configs"
|
||||
"github.com/spf13/cobra"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var reCloneCmd = &cobra.Command{
|
||||
Use: "reclone",
|
||||
Short: "Reruns one, multiple, or all preconfigured clones from configuration set in $HOME/.config/ghorg/reclone.yaml",
|
||||
Long: `Allows you to set preconfigured clone commands for handling multiple users/orgs at once. See README.md at https://github.com/gabrie30/ghorg for setup information.`,
|
||||
Run: reCloneFunc,
|
||||
}
|
||||
|
||||
type ReClone struct {
|
||||
Cmd string `yaml:"cmd"`
|
||||
}
|
||||
|
||||
func reCloneFunc(cmd *cobra.Command, argz []string) {
|
||||
path := configs.GhorgReCloneLocation()
|
||||
yamlBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("ERROR: parsing reclone.yaml, error: %v", err)
|
||||
colorlog.PrintErrorAndExit(e)
|
||||
}
|
||||
|
||||
mapOfReClones := make(map[string]ReClone)
|
||||
|
||||
err = yaml.Unmarshal(yamlBytes, &mapOfReClones)
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("ERROR: unmarshaling reclone.yaml, error:%v", err)
|
||||
colorlog.PrintErrorAndExit(e)
|
||||
}
|
||||
|
||||
if len(argz) == 0 {
|
||||
for _, key := range mapOfReClones {
|
||||
runReClone(key)
|
||||
}
|
||||
} else {
|
||||
for _, arg := range argz {
|
||||
if _, ok := mapOfReClones[arg]; !ok {
|
||||
e := fmt.Sprintf("ERROR: The key %v was not found in reclone.yaml", arg)
|
||||
colorlog.PrintErrorAndExit(e)
|
||||
}
|
||||
runReClone(mapOfReClones[arg])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func runReClone(rc ReClone) {
|
||||
// make sure command starts with ghorg clone
|
||||
splitCommand := strings.Split(rc.Cmd, " ")
|
||||
ghorg, clone, remainingCommand := splitCommand[0], splitCommand[1], splitCommand[1:]
|
||||
|
||||
if ghorg != "ghorg" || clone != "clone" {
|
||||
colorlog.PrintErrorAndExit("ERROR: Only ghorg clone commands are permitted in your reclone.yaml")
|
||||
}
|
||||
ghorgClone := exec.Command("ghorg", remainingCommand...)
|
||||
|
||||
stdout, err := ghorgClone.StdoutPipe()
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("ERROR: Problem with piping to stdout, err: %v", err)
|
||||
colorlog.PrintErrorAndExit(e)
|
||||
}
|
||||
|
||||
err = ghorgClone.Start()
|
||||
|
||||
if err != nil {
|
||||
e := fmt.Sprintf("ERROR: Running ghorg clone cmd: %v, err: %v", rc.Cmd, err)
|
||||
colorlog.PrintErrorAndExit(e)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
for scanner.Scan() {
|
||||
m := scanner.Text()
|
||||
fmt.Println(m)
|
||||
}
|
||||
ghorgClone.Wait()
|
||||
}
|
@ -51,6 +51,7 @@ var (
|
||||
config string
|
||||
gitlabGroupExcludeMatchRegex string
|
||||
ghorgIgnorePath string
|
||||
ghorgReClonePath string
|
||||
quietMode bool
|
||||
)
|
||||
|
||||
@ -90,6 +91,8 @@ func getOrSetDefaults(envVar string) {
|
||||
os.Setenv(envVar, configs.GetAbsolutePathToCloneTo())
|
||||
case "GHORG_IGNORE_PATH":
|
||||
os.Setenv(envVar, configs.GhorgIgnoreLocation())
|
||||
case "GHORG_RECLONE_PATH":
|
||||
os.Setenv(envVar, configs.GhorgReCloneLocation())
|
||||
case "GHORG_CLONE_PROTOCOL":
|
||||
os.Setenv(envVar, "https")
|
||||
case "GHORG_CLONE_TYPE":
|
||||
@ -162,7 +165,7 @@ func InitConfig() {
|
||||
} else {
|
||||
config = configs.DefaultConfFile()
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(configs.GhorgDir())
|
||||
viper.AddConfigPath(configs.GhorgConfDir())
|
||||
viper.SetConfigName("conf")
|
||||
os.Setenv("GHORG_CONFIG", configs.DefaultConfFile())
|
||||
}
|
||||
@ -215,6 +218,7 @@ func InitConfig() {
|
||||
getOrSetDefaults("GHORG_EXCLUDE_MATCH_PREFIX")
|
||||
getOrSetDefaults("GHORG_GITLAB_GROUP_EXCLUDE_MATCH_REGEX")
|
||||
getOrSetDefaults("GHORG_IGNORE_PATH")
|
||||
getOrSetDefaults("GHORG_RECLONE_PATH")
|
||||
getOrSetDefaults("GHORG_QUIET")
|
||||
|
||||
if os.Getenv("GHORG_DEBUG") != "" {
|
||||
@ -265,9 +269,10 @@ func init() {
|
||||
cloneCmd.Flags().StringVarP(&excludeMatchRegex, "exclude-match-regex", "", "", "GHORG_EXCLUDE_MATCH_REGEX - Exclude cloning repos that match name to regex provided")
|
||||
cloneCmd.Flags().StringVarP(&gitlabGroupExcludeMatchRegex, "gitlab-group-exclude-match-regex", "", "", "GHORG_GITLAB_GROUP_EXCLUDE_MATCH_REGEX - Exclude cloning gitlab groups that match name to regex provided")
|
||||
cloneCmd.Flags().StringVarP(&ghorgIgnorePath, "ghorgignore-path", "", "", "GHORG_IGNORE_PATH - If you want to set a path other than $HOME/.config/ghorg/ghorgignore for your ghorgignore")
|
||||
cloneCmd.Flags().StringVarP(&ghorgReClonePath, "reclone-path", "", "", "GHORG_RECLONE_PATH - If you want to set a path other than $HOME/.config/ghorg/reclone.yaml for your reclone configuration")
|
||||
cloneCmd.Flags().StringVarP(&exitCodeOnCloneInfos, "exit-code-on-clone-infos", "", "", "GHORG_EXIT_CODE_ON_CLONE_INFOS - Allows you to control the exit code when ghorg runs into a problem (info level message) cloning a repo from the remote. Info messages will appear after a clone is complete, similar to success messages. (default 0)")
|
||||
cloneCmd.Flags().StringVarP(&exitCodeOnCloneIssues, "exit-code-on-clone-issues", "", "", "GHORG_EXIT_CODE_ON_CLONE_ISSUES - Allows you to control the exit code when ghorg runs into a problem (issue level message) cloning a repo from the remote. Issue messages will appear after a clone is complete, similar to success messages (default 1)")
|
||||
rootCmd.AddCommand(lsCmd, versionCmd, cloneCmd)
|
||||
rootCmd.AddCommand(lsCmd, versionCmd, cloneCmd, reCloneCmd)
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const ghorgVersion = "v1.8.0"
|
||||
const ghorgVersion = "v1.8.1"
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
|
@ -42,6 +42,18 @@ func PrintError(msg interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// PrintErrorAndExit prints red colored text to standard out then exits 1
|
||||
func PrintErrorAndExit(msg interface{}) {
|
||||
switch os.Getenv("GHORG_COLOR") {
|
||||
case "enabled":
|
||||
color.New(color.FgRed).Println(msg)
|
||||
default:
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// PrintSubtleInfo prints magenta colored text to standard out
|
||||
func PrintSubtleInfo(msg interface{}) {
|
||||
if os.Getenv("GHORG_QUIET") == "true" {
|
||||
|
@ -96,7 +96,17 @@ func GhorgIgnoreLocation() string {
|
||||
return ignoreLocation
|
||||
}
|
||||
|
||||
return filepath.Join(GhorgDir(), "ghorgignore")
|
||||
return filepath.Join(GhorgConfDir(), "ghorgignore")
|
||||
}
|
||||
|
||||
// GhorgReCloneLocation returns the path of users ghorgignore
|
||||
func GhorgReCloneLocation() string {
|
||||
recloneConfLocation := os.Getenv("GHORG_RECLONE_PATH")
|
||||
if recloneConfLocation != "" {
|
||||
return recloneConfLocation
|
||||
}
|
||||
|
||||
return filepath.Join(GhorgConfDir(), "reclone.yaml")
|
||||
}
|
||||
|
||||
// GhorgIgnoreDetected returns true if a ghorgignore file exists.
|
||||
@ -105,8 +115,14 @@ func GhorgIgnoreDetected() bool {
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// GhorgDir returns the ghorg directory path
|
||||
func GhorgDir() string {
|
||||
// GhorgReCloneDetected returns true if a reclone.yaml file exists.
|
||||
func GhorgReCloneDetected() bool {
|
||||
_, err := os.Stat(GhorgReCloneLocation())
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// GhorgConfDir returns the ghorg directory path
|
||||
func GhorgConfDir() string {
|
||||
if XConfigHomeSet() {
|
||||
xdg := os.Getenv("XDG_CONFIG_HOME")
|
||||
return filepath.Join(xdg, "ghorg")
|
||||
@ -125,7 +141,7 @@ func XConfigHomeSet() bool {
|
||||
}
|
||||
|
||||
func DefaultConfFile() string {
|
||||
return filepath.Join(GhorgDir(), "conf.yaml")
|
||||
return filepath.Join(GhorgConfDir(), "conf.yaml")
|
||||
}
|
||||
|
||||
// HomeDir finds the users home directory
|
||||
|
@ -102,6 +102,11 @@ GHORG_FETCH_ALL: false
|
||||
# flag (--ghorgignore-path)
|
||||
GHORG_IGNORE_PATH:
|
||||
|
||||
# If set allows you to specify the location of your reclone.yaml
|
||||
# Defaults to $HOME/.config/ghorg/reclone.yaml
|
||||
# flag (--reclone-path)
|
||||
GHORG_RECLONE_PATH:
|
||||
|
||||
# Only emit critical output.
|
||||
# flag (--quiet)
|
||||
GHORG_QUIET: false
|
||||
|
10
sample-reclone.yaml
Normal file
10
sample-reclone.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# Example format below. You can reclone, one, multiple or all at once just use with no arguments e.g. ghorg reclone
|
||||
|
||||
# name-of-reclone:
|
||||
# cmd: "ghorg clone command here"
|
||||
# backup-my-org:
|
||||
# cmd: "ghorg clone my-org --backup --clone-wiki"
|
||||
# kubernetes-sig:
|
||||
# cmd: "ghorg clone kubernetes --match-regex=^sig- --output-dir=kubernetes-sig-only"
|
||||
# gitlab-examples:
|
||||
# cmd: "ghorg clone gitlab-examples --scm=gitlab --preserve-dir --config=/Users/me/.config/ghorg/gitlab-cloud-conf.yaml"
|
Loading…
Reference in New Issue
Block a user