Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
headyj committed Sep 30, 2020
0 parents commit 9509272
Show file tree
Hide file tree
Showing 15 changed files with 1,026 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
giterate
.DS_Store
.vscode
__debug_bin
*.json
*.yaml
67 changes: 67 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Project
Giterate is a wrapper that allows you to clone and pull multiple repositories from multiple providers with a single command

## Configuration
By default, giterate will use config.json or config.yaml file (in this order) in ~/.giterate folder
Each git provider will have the same available parameters:

- BaseURL: Base URL of the git provider
- API: Type of api (can be gitlab, bitbucketv1, bitbucketv2 or github)
- ApiURI: URI of the api
- ApiToken: API token (required with ssh authentication)
- CloneType: Type of authentication (can be ssh or http)
- Username: username for authentication
- Password: password for authentication
- SSHPrivateKeyPath: absolute path to private key (required with ssh authentication)
- Destination: default destination directory
- Entities: List of repositories and groups to clone
- Type: Type of entity, can be repository or group
- Path: path to the entity (e.g. group, project, etc)
- Name: name to the entity (e.g. name of the group, name of the repository)
- Destination: destination absolute path. If not given, will take the default destination + path of entity
- Recurse: in case of group, will clone recursively
- CloneOptions: clone options array
- Key: Name of the clone option
- Value: Value of the clone option

You can find an example of configuration file on this repository

## Available services
- Gitlab
- Bitbucket V1
- Bitbucket V2
- Github

## Available commands
- [x] clone: clone repositories according to configuration file
- if the repository already exists or is already clone, it will not be updated
- parameters:
- [ ] --force: will clean all an recreate from conf

- [x] pull: pull repositories on current branches according to configuration file
- if a new repository has been added to the configuration/to the git provider, it will not be cloned
- parameters
- [ ] --force: reset to configured branch

- [x] status: check status of each git repositories according to configuration file

- [ ] commit: check changes and ask for commit message in case of changes
- if you don't provide any message, it will go to the next one without commiting
- parameters
- [ ] --target: target one or multiple repositories
- [ ] -m: define a single message for all commits (you'll have to answer "yes" instead of providing a message)

- [ ] push: check commited changes and ask for push
- if you don't provide any message, it will go to the next one without commiting
- parameters
- [ ] --force: push without asking
- [ ] --target: target one or multiple repositories

## Global parameters
- [x] --config-file: set json/yaml configuration file path

## Roadmap
[ ] tests
[ ] commit command
[ ] push command
[ ] implement parameters
81 changes: 81 additions & 0 deletions config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
[
{
"BaseURL": "https://gitlab.*****.com",
"API": "gitlab",
"ApiURI": "/api/v4",
"ApiToken": "******",
"CloneType": "ssh",
"SSHPrivateKeyPath": "/home/usr/.ssh/id_rsa",
"Destination": "/home/usr/giterate/gitlab",
"Entities": [
{
"Type": "repository",
"Path": "link/to/repository",
"Name": "repository_name",
"Destination": "/home/usr/giterate/openjdk",
"CloneOptions": [
{
"Name": "RemoteName",
"Value": "master"
}
]
},
{
"Type": "group",
"Path": "link/to/group",
"Name": "group_name",
"Recurse": true
}
]
},
{
"BaseURL": "https://git.*****.com",
"API": "bitbucketv1",
"ApiURI": "/rest",
"CloneType": "http",
"Username": "usr",
"Password": "*****",
"Destination": "/home/usr/giterate/bitbucketv1",
"Entities": [
{
"Type": "repository",
"Path": "group_name",
"Name": "repository_name",
"Destination": "/home/usr/giterate/bitbucketv1/repository_name"
},
{
"Type": "group",
"Name": "group_name"
}
]
},
{
"BaseURL": "https://api.bitbucket.org",
"API": "bitbucketv2",
"ApiURI": "/2.0",
"CloneType": "ssh",
"Username": "usr",
"SSHPrivateKeyPath": "/home/usr/.ssh/id_rsa",
"Password": "*****",
"Destination": "/home/usr/giterate/bitbucketv2",
"Entities": [
{
"Type": "group",
"Name": "group_name"
}
]
},
{
"BaseURL": "https://github.com",
"API": "github",
"ApiURI": "/2.0",
"CloneType": "none",
"Destination": "/home/usr/giterate/github",
"Entities": [
{
"Type": "group",
"Name": "group_name"
}
]
}
]
53 changes: 53 additions & 0 deletions config.yaml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
- BaseURL: https://gitlab.*****.com
API: gitlab
ApiURI: "/api/v4"
ApiToken: "******"
CloneType: ssh
SSHPrivateKeyPath: "/home/usr/.ssh/id_rsa"
Destination: "/home/usr/giterate/gitlab"
Entities:
- Type: repository
Path: link/to/repository
Name: repository_name
Destination: "/home/usr/giterate/openjdk"
CloneOptions:
- Name: RemoteName
Value: master
- Type: group
Path: link/to/group
Name: group_name
Recurse: true
- BaseURL: https://git.*****.com
API: bitbucketv1
ApiURI: "/rest"
CloneType: http
Username: usr
Password: "*****"
Destination: "/home/usr/giterate/bitbucketv1"
Entities:
- Type: repository
Path: group_name
Name: repository_name
Destination: "/home/usr/giterate/bitbucketv1/repository_name"
- Type: group
Name: group_name
- BaseURL: https://api.bitbucket.org
API: bitbucketv2
ApiURI: "/2.0"
CloneType: ssh
Username: usr
SSHPrivateKeyPath: "/home/usr/.ssh/id_rsa"
Password: "*****"
Destination: "/home/usr/giterate/bitbucketv2"
Entities:
- Type: group
Name: group_name
- BaseURL: https://github.com
API: github
ApiURI: "/2.0"
CloneType: none
Destination: "/home/usr/giterate/github"
Entities:
- Type: group
Name: group_name
51 changes: 51 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
command "giterate/pkg/commands"
"log"
"os"

"github.com/mitchellh/cli"
)

func main() {
args := os.Args[1:]
cli := cli.CLI{
Args: args,
Commands: Commands,
HelpFunc: cli.BasicHelpFunc("giterate"),
Version: "0.1",
}

status, err := cli.Run()
if err != nil {
log.Fatalf("Cannot run command: %s", err)
}

os.Exit(status)

}

var Commands map[string]cli.CommandFactory

func init() {
ui := &cli.BasicUi{Writer: os.Stdout}

Commands = map[string]cli.CommandFactory{
"clone": func() (cli.Command, error) {
return &command.CloneCommand{
Ui: ui,
}, nil
},
"pull": func() (cli.Command, error) {
return &command.PullCommand{
Ui: ui,
}, nil
},
"status": func() (cli.Command, error) {
return &command.StatusCommand{
Ui: ui,
}, nil
},
}
}
16 changes: 16 additions & 0 deletions pkg/commands/arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package command

type Arguments struct {
ConfigFile string
}

func (a *Arguments) process(args []string) *Arguments {
for i, v := range args {
switch v {
case "--config-file":
a.ConfigFile = args[i+1]
}
}
return a

}
86 changes: 86 additions & 0 deletions pkg/commands/clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package command

import (
"fmt"
"giterate/pkg/entities"
"log"
"os"
"strings"

"github.com/go-git/go-git"
"github.com/go-git/go-git/plumbing/transport"
"github.com/go-git/go-git/plumbing/transport/http"
"github.com/go-git/go-git/plumbing/transport/ssh"
"github.com/mitchellh/cli"
)

type CloneCommand struct {
Ui cli.Ui
Arguments
}

func (c *CloneCommand) Run(args []string) int {
file := initConf(c.Arguments.process(args))
services := entities.PopulateRepositories(file)
Clone(services)
//servicesJSON, _ := json.Marshal(services)
//fmt.Printf("%s", servicesJSON)
return 0
}

func (c *CloneCommand) Help() string {
helpText := `
Usage: giterate clone [options]
Clone repositories accordint to configuration file
By default, giterate will use config.json or config.yaml file (in this order) in ~/.giterate/ folder
Options:
--config-file set json/yaml configuration file path
`

return strings.TrimSpace(helpText)
}

func (c *CloneCommand) Synopsis() string {
return "clone repositories according to configuration file"
}

func Clone(services []entities.Service) {
for _, service := range services {
var cloneOptions = git.CloneOptions{Progress: os.Stdout}
if service.SSHPrivateKeyPath != "" {
publicKeys, err := ssh.NewPublicKeysFromFile("git", service.SSHPrivateKeyPath, "")
if err != nil {
log.Fatalf("Cannot get public key: %s", err)
}
cloneOptions.Auth = publicKeys
} else if service.Username != "" && service.Password != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: service.Username,
Password: service.Password,
}
}
for _, repository := range service.Repositories {
cloneOptions.URL = repository.URL
if repository.CloneOptions != nil {
entities.ProcessCloneOptions(&repository.CloneOptions, &cloneOptions)
}
fmt.Println("[INFO] Cloning " + repository.URL + "...")
_, err := git.PlainClone(repository.Destination, false, &cloneOptions)
if err != nil {
if err == transport.ErrEmptyRemoteRepository || err == git.ErrRepositoryAlreadyExists {
fmt.Println("[INFO] Repository already cloned, ignoring...")
continue
} else {
log.Fatalf("Cannot clone repository: %s", err)
}
} else {
fmt.Println("[INFO] Cloned " + repository.URL + "...")
}
}
}
}
Loading

0 comments on commit 9509272

Please sign in to comment.