git_duplicator 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c56bf9a2478b996375f297102452f577a678625f
4
- data.tar.gz: c9dcca68935f1f25245686697d9e1ee191811b33
3
+ metadata.gz: 41ed03abddf02fd443578e3f337713da86e085ae
4
+ data.tar.gz: 065fb1a481102c0ebc59425d2cd2d34321cf7481
5
5
  SHA512:
6
- metadata.gz: 43b7d1d5308dcfbe6b812166463282c7ad47f6d0dbcd87b550ff38dab6eefaf36c17b481210c09af1d775dde79c4a05f82b1a6168399ac06497815f058f2d41f
7
- data.tar.gz: a21c7b71c3b1269b19be4593c646873f05ade3fd6cf4e1dda211ffc1586e507dec80480d9bdbf0fe637936d1c401d144415dd89651e49d077e7713a364529ea9
6
+ metadata.gz: 55a30dabf88926510f0c62e81e1c5a6c35e806a7462714e8cfbef483bf99b0aff6e6e2880f4956234f0167fa1a0bd82ce78fd6956aaef779b22f2752c0e02692
7
+ data.tar.gz: 862524fa37e6105d6e8812d851bab25dfbf01104537e1641e8ec7d5f5b91e22e72295a4c9181201198a3eb5dbe8a62185780497fc4da6af15f9bada5ea54a86e
data/README.md CHANGED
@@ -1,10 +1,13 @@
1
1
  # Git Duplicator
2
2
 
3
- Duplicating git repositories without forking.
3
+ [![Gem Version](https://badge.fury.io/rb/git_duplicator.svg)](http://badge.fury.io/rb/git_duplicator) [![Build Status](https://travis-ci.org/Startappz/git_duplicator.svg?branch=master)](https://travis-ci.org/Startappz/git_duplicator) [![Coverage Status](https://coveralls.io/repos/Startappz/git_duplicator/badge.png?branch=master)](https://coveralls.io/r/Startappz/git_duplicator?branch=master) [![Dependency Status](https://gemnasium.com/Startappz/git_duplicator.svg)](https://gemnasium.com/Startappz/git_duplicator) [![Code Climate](https://codeclimate.com/github/Startappz/git_duplicator/badges/gpa.svg)](https://codeclimate.com/github/Startappz/git_duplicator)
4
4
 
5
- - [x] Duplicate any repository
6
- - [x] Additional Github support
7
- - [x] Additional Bibucket support
5
+
6
+ Duplicating git repositories without forking. It depends on the flow described in [this](https://help.github.com/articles/duplicating-a-repository) article.
7
+
8
+ - [x] Duplicate any repository in two ways: one time usage and frequent updates.
9
+ - [x] Additional Github [support](https://github.com/Startappz/git_duplicator/blob/master/lib/git_duplicator/services/github.rb) to create and delete repositories.
10
+ - [x] Additional Bibucket [support](https://github.com/Startappz/git_duplicator/blob/master/lib/git_duplicator/services/bitbucket.rb) to create and delete repositories.
8
11
 
9
12
  ## Installation
10
13
 
@@ -22,30 +25,105 @@ Or install it yourself as:
22
25
 
23
26
  ## Usage
24
27
 
25
- ### Basic usage
28
+ ### Duplicate with no future updates
29
+
30
+ Duplicate a repository. Then you can turn off the source repo and move to the mirrored one.
26
31
 
27
- Duplicate a repository. It assumes that you have the destination repository initiated.
32
+ #### Basic usage
33
+
34
+ It assumes that you have the destination repository initiated.
28
35
 
29
36
  ```ruby
30
37
  require 'git_duplicator'
31
- from = GitDuplicator::Repository
32
- .new('source repo name', 'source repo url')
33
- to = GitDuplicator::Repository
34
- .new('destination repo name', 'destination repo url')
38
+
39
+ from = GitDuplicator::Repository.new('source repo name', 'source repo url')
40
+
41
+ to = GitDuplicator::Repository.new('mirrored repo name', 'mirrored repo url')
42
+
35
43
  GitDuplicator.perform(from, to)
36
44
 
45
+
37
46
  ```
38
- ### Advanced usage
47
+ #### Advanced usage
39
48
  - You can create the destination repository automatically. This needs you to provide the needed authentication credentials for the script.
40
49
  - You can set the clone working path locally for the script to work. It's a temporary workplace that will get swiped after finishing.
41
50
 
42
51
  ```ruby
43
52
  require 'git_duplicator'
44
- from = GitDuplicator::Repository
45
- .new('source repo name', 'source repo url')
46
- to = GitDuplicator::Services::GithubRepository
47
- .new('destination repo name', 'destination owner', {auth2_token: 'some token'})
48
- GitDuplicator.perform(from, to, force_create_destination: true, clone_path: 'path/to/tmp')
53
+
54
+ from = GitDuplicator::Repository.new('source repo name', 'source repo url')
55
+
56
+ to = GitDuplicator::Services::GithubRepository.new(
57
+ 'mirrored repo name', 'mirrored owner',
58
+ credentials: { oauth2_token: 'some token' },
59
+ remote_options: { has_issues: false, has_wiki: false }
60
+ )
61
+
62
+ GitDuplicator.perform(
63
+ from, to,
64
+ force_create_destination: true,
65
+ clone_path: 'path/to/clone/folder'
66
+ )
67
+
68
+ ```
69
+ ### Duplicate with future updates
70
+
71
+ Duplicate a repository. To update your mirror, fetch updates and push, which could be automated by running a cron job.
72
+
73
+ #### Basic usage
74
+
75
+ It assumes that you have the destination repository initiated.
76
+
77
+ ```ruby
78
+ require 'git_duplicator'
79
+
80
+ from = GitDuplicator::Repository.new('source repo name', 'source repo url')
81
+
82
+ to = GitDuplicator::Repository.new('mirrored repo name', 'mirrored repo url')
83
+
84
+ GitDuplicator.perform_for_update(from, to)
85
+
86
+ # Later on if you want to update the mirrored one
87
+ local_repo = GitDuplicator::Repository.new(
88
+ 'source repo name',
89
+ 'source repo url',
90
+ 'path/to/working/directory'
91
+ )
92
+
93
+ local_repo.update_mirrored
94
+
95
+ ```
96
+ #### Advanced usage
97
+ - You can create the destination repository automatically. This needs you to provide the needed authentication credentials for the script.
98
+ - You can set the clone working path locally for the script to work. It's a temporary workplace that will get swiped after finishing.
99
+
100
+ ```ruby
101
+ require 'git_duplicator'
102
+
103
+ from = GitDuplicator::Repository.new('source repo name', 'source repo url')
104
+
105
+ to = GitDuplicator::Services::GithubRepository.new(
106
+ 'mirrored repo name',
107
+ 'mirrored owner',
108
+ credentials: { oauth2_token: 'some token' },
109
+ remote_options: { has_issues: false, has_wiki: false }
110
+ )
111
+
112
+ GitDuplicator.perform_for_update(
113
+ from, to,
114
+ force_create_destination: true,
115
+ clone_path: 'path/to/clone/folder'
116
+ )
117
+
118
+ # Later on if you want to update the mirrored one
119
+ local_repo = GitDuplicator::Repository.new(
120
+ 'source repo name',
121
+ 'source repo url',
122
+ 'path/to/working/directory'
123
+ )
124
+
125
+ local_repo.update_mirrored
126
+
49
127
  ```
50
128
 
51
129
  ### Available Services
@@ -1,5 +1,5 @@
1
1
  require_relative 'git_duplicator/version'
2
- require_relative 'git_duplicator/duplicator'
2
+ require_relative 'git_duplicator/duplicators'
3
3
  require_relative 'git_duplicator/repositories'
4
4
  require_relative 'git_duplicator/services'
5
5
 
@@ -7,9 +7,15 @@ require_relative 'git_duplicator/services'
7
7
  module GitDuplicator
8
8
  class << self
9
9
  # Perform the duplication
10
- # @see GitDuplicator::Duplicator
10
+ # @see GitDuplicator::MirrorDuplicator
11
11
  def perform(from, to, options)
12
- Duplicator.new(from, to, options).perform
12
+ MirrorDuplicator.new(from, to, options).perform
13
+ end
14
+
15
+ # Perform the duplication for updates
16
+ # @see GitDuplicator::UpdateDuplicator
17
+ def perform_for_update(from, to, options)
18
+ UpdateDuplicator.new(from, to, options).perform
13
19
  end
14
20
  end
15
21
  end
@@ -1,7 +1,5 @@
1
- require 'fileutils'
2
- require_relative 'null_logger'
3
-
4
1
  module GitDuplicator
2
+ # Abstract class
5
3
  class Duplicator
6
4
  attr_accessor :from, :to, :logger, :clone_path, :force_create_destination
7
5
 
@@ -33,10 +31,6 @@ module GitDuplicator
33
31
  clean_up
34
32
  end
35
33
 
36
- private
37
-
38
- attr_accessor :source_cloned
39
-
40
34
  def recreate_destination
41
35
  return unless force_create_destination
42
36
  logger.info("Deleting existing destination repo: #{to.url}")
@@ -47,20 +41,29 @@ module GitDuplicator
47
41
 
48
42
  def clone_source
49
43
  logger.info("Cloning bare Gitorious repo: #{from.url}")
50
- from.bare_clone(clone_path)
51
- self.source_cloned = true
44
+ perform_clone_source
52
45
  end
53
46
 
54
47
  def mirror
55
48
  logger.info("Mirroring Gitorious to Bitbucket: #{from.url}")
56
- from.mirror(to.url)
49
+ perform_mirror
57
50
  end
58
51
 
59
52
  def clean_up
60
- return unless source_cloned
61
53
  logger.info 'Clean local source repo'
62
- FileUtils.rm_rf("#{clone_path}/#{from.name}")
63
- @cloned = nil
54
+ perform_clean_up
55
+ end
56
+
57
+ def perform_clone_source
58
+ fail NotImplementedError
59
+ end
60
+
61
+ def perform_mirror
62
+ fail NotImplementedError
63
+ end
64
+
65
+ def perform_clean_up
66
+ fail NotImplementedError
64
67
  end
65
68
  end
66
69
  end
@@ -0,0 +1,18 @@
1
+ module GitDuplicator
2
+ # Mirror a repository
3
+ class MirrorDuplicator < Duplicator
4
+ protected
5
+
6
+ def perform_clone_source
7
+ from.bare_clone(clone_path)
8
+ end
9
+
10
+ def perform_mirror
11
+ from.mirror(to.url)
12
+ end
13
+
14
+ def perform_clean_up
15
+ FileUtils.rm_rf("#{clone_path}/#{from.name}")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module GitDuplicator
2
+ # Mirror a repository and end make it ready for updates
3
+ class UpdateDuplicator < Duplicator
4
+ protected
5
+
6
+ def perform_clone_source
7
+ from.mirror_clone(clone_path)
8
+ end
9
+
10
+ def perform_mirror
11
+ from.set_mirrored_remote(to.url)
12
+ from.update_mirrored
13
+ end
14
+
15
+ def perform_clean_up; end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'fileutils'
2
+ require_relative 'null_logger'
3
+ require_relative 'duplicator/duplicator'
4
+ require_relative 'duplicator/mirror_duplicator'
5
+ require_relative 'duplicator/update_duplicator'
@@ -8,4 +8,8 @@ module GitDuplicator
8
8
  class RepositoryCloningError < MigrationError; end
9
9
 
10
10
  class RepositoryMirorringError < MigrationError; end
11
+
12
+ class RepositoryMirorredUpdatingError < MigrationError; end
13
+
14
+ class RepositorySettingRemoteError < MigrationError; end
11
15
  end
@@ -1,3 +1,4 @@
1
1
  require_relative 'errors'
2
+ require_relative 'helpers/authorization_header'
2
3
  require_relative 'repository/repository'
3
4
  require_relative 'repository/service_repository'
@@ -4,21 +4,21 @@ module GitDuplicator
4
4
  # Basic Repostiroy
5
5
  class Repository
6
6
  attr_accessor :name, :url
7
- attr_reader :repo
7
+ attr_reader :working_directory
8
8
 
9
9
  # Initializer
10
10
  # @param [String] name name of the repository
11
11
  # @param [String] url URL of the repository
12
- def initialize(name, url)
12
+ # @param [String] working_directory working directory of the repository
13
+ def initialize(name, url, working_directory = nil)
13
14
  self.name = name
14
15
  self.url = url
16
+ self.working_directory = working_directory
15
17
  end
16
18
 
17
- # Repository attribute setter
18
- # @param [Git::Base] repository
19
- def repo=(repository)
20
- fail(TypeError) unless repository.is_a?(Git::Base)
21
- @repo = repository
19
+ def working_directory=(value)
20
+ @working_directory = value
21
+ @repo = repo_from_path
22
22
  end
23
23
 
24
24
  # Bare clone the repository
@@ -29,13 +29,89 @@ module GitDuplicator
29
29
  raise RepositoryCloningError, exception.message
30
30
  end
31
31
 
32
+ # Mirror clone the repository
33
+ # @param [String] path_to_repo path to clone the repository to
34
+ def mirror_clone(path_to_repo)
35
+ path = File.join(path_to_repo, name)
36
+ command('clone', '--mirror', url, path)
37
+ self.repo = Git.bare(path)
38
+ rescue => exception
39
+ raise RepositoryCloningError, exception.message
40
+ end
41
+
32
42
  # Mirror the repository
33
- # @param [String] destination_url URL of destination repository
34
- def mirror(destination_url)
43
+ # @param [String] mirrored_url URL of mirrored repository
44
+ def mirror(mirrored_url)
35
45
  fail('No local repo defined. Set the "repo" attribute') unless repo
36
- repo.push(destination_url, '--mirror')
46
+ repo.push(mirrored_url, '--mirror')
37
47
  rescue => exception
38
48
  raise RepositoryMirorringError, exception.message
39
49
  end
50
+
51
+ # Set the remote URL of the mirrored
52
+ # @param [String] mirrored_url URL of mirrored repository
53
+ def set_mirrored_remote(mirrored_url)
54
+ fail('No local repo defined. Set the "repo" attribute') unless repo
55
+ command('remote', 'set-url', '--push', 'origin', mirrored_url)
56
+ rescue => exception
57
+ raise RepositorySettingRemoteError, exception.message
58
+ end
59
+
60
+ # Update a mirrored repository
61
+ def update_mirrored
62
+ fail('No local repo defined. Set the "repo" attribute') unless repo
63
+ command('fetch', '-p', 'origin')
64
+ command('push', '--mirror')
65
+ rescue => exception
66
+ raise RepositoryMirorredUpdatingError, exception.message
67
+ end
68
+
69
+ protected
70
+
71
+ attr_reader :repo # for testing
72
+
73
+ def repo=(value)
74
+ @repo = value
75
+ @working_directory = working_directory_from_repo
76
+ end
77
+
78
+ def repo_from_path
79
+ if working_directory
80
+ Git.open(working_directory) rescue Git.bare(working_directory)
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ def working_directory_from_repo
87
+ return nil if repo.nil?
88
+ if repo.dir
89
+ repo.dir.path
90
+ elsif repo.repo
91
+ repo.repo.path
92
+ else
93
+ nil
94
+ end
95
+ end
96
+
97
+ def command(cmd, *opts)
98
+ git_cmd = "git #{cmd} #{opts.join(' ')}"
99
+ out = run_command(git_cmd)
100
+ if $?.exitstatus > 0
101
+ if $?.exitstatus == 1 && out == ''
102
+ return ''
103
+ end
104
+ fail(git_cmd + ': ' + out.to_s)
105
+ end
106
+ end
107
+
108
+ def run_command(cmd)
109
+ exec = "#{cmd} 2>&1"
110
+ if working_directory
111
+ Dir.chdir(working_directory) { `#{exec}` }
112
+ else
113
+ `#{exec}`
114
+ end.chomp
115
+ end
40
116
  end
41
117
  end
@@ -6,9 +6,10 @@ module GitDuplicator
6
6
  # Initializer
7
7
  # @param [String] name name of the repository
8
8
  # @param [String] owner owner of the repository
9
- def initialize(name, owner)
9
+ # @param [String] working_directory working directory of the repository
10
+ def initialize(name, owner, working_directory = nil)
10
11
  self.owner = owner
11
- super(name, url)
12
+ super(name, url, working_directory)
12
13
  end
13
14
 
14
15
  # URL of the repositroy
@@ -1,30 +1,31 @@
1
1
  require 'http'
2
- require_relative '../helpers/authorization_header'
3
-
4
2
  module GitDuplicator
5
3
  module Services
6
4
  # Bitbucket based repository
7
5
  class BitbucketRepository < ServiceRepository
8
6
  BASE_URI = 'https://api.bitbucket.org/2.0'
9
7
 
10
- attr_accessor :credentials, :options
8
+ attr_accessor :credentials, :remote_options
11
9
 
12
10
  # Initializer
13
11
  # @param [String] name name of the repository
14
12
  # @param [String] owner owner of the repository
15
- # @param [Hash] credentials
16
- # @option credentials [Symbol] :consumer_key used in oAuth authentication
17
- # @option credentials [Symbol] :consumer_secret used in oAuth authentication
18
- # @option credentials [Symbol] :token used in oAuth authentication
19
- # @option credentials [Symbol] :token_secret used in oAuth authentication
20
- # @option credentials [Symbol] :username used in basic authentication
21
- # @option credentials [Symbol] :password used in basic authentication
22
- # @param [Hash] options options for creation
23
- # @see https://confluence.atlassian.com/display/BITBUCKET/repository+Resource#repositoryResource-POSTanewrepository
24
- def initialize(name, owner, credentials = {}, options = {})
25
- super(name, owner)
26
- self.credentials = credentials
27
- self.options = options
13
+ # @param [Hash] options
14
+ # * :credentials (Hash) credentials for remote service
15
+ # * :consumer_key (Symbol) used in oAuth authentication
16
+ # * :consumer_secret (Symbol) used in oAuth authentication
17
+ # * :token (Symbol) used in oAuth authentication
18
+ # * :token_secret (Symbol) used in oAuth authentication
19
+ # * :username (Symbol) used in basic authentication
20
+ # * :password (Symbol) used in basic authentication
21
+ # * :remote_options (Hash) creation options for remote service
22
+ # @see https://confluence.atlassian.com/display/BITBUCKET/repository+Resource#repositoryResource-POSTanewrepository
23
+ # * :working_directory (String) assing a working directory
24
+ def initialize(name, owner, options = {})
25
+ self.credentials = options.fetch(:credentials) { {} }
26
+ self.remote_options = options.fetch(:remote_options) { {} }
27
+ self.working_directory = options.fetch(:working_directory) { nil }
28
+ super(name, owner, working_directory)
28
29
  end
29
30
 
30
31
  # URL of the repositroy
@@ -37,7 +38,7 @@ module GitDuplicator
37
38
  def create
38
39
  request_url = BASE_URI + "/repositories/#{owner}/#{name}"
39
40
  response = HTTP.with(headers(:post, request_url))
40
- .post(request_url, json: options)
41
+ .post(request_url, json: remote_options)
41
42
  code, body = response.code.to_i, response.body
42
43
  fail(RepositoryCreationError, body) unless 200 == code
43
44
  end