git_duplicator 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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