modulesync 2.2.0 → 2.3.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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +18 -3
  3. data/.github/workflows/release.yml +2 -4
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +14 -8
  6. data/.rubocop_todo.yml +25 -17
  7. data/.simplecov +46 -0
  8. data/CHANGELOG.md +32 -0
  9. data/Gemfile +1 -1
  10. data/bin/msync +17 -1
  11. data/features/cli.feature +12 -6
  12. data/features/execute.feature +51 -0
  13. data/features/hook.feature +5 -8
  14. data/features/push.feature +46 -0
  15. data/features/reset.feature +57 -0
  16. data/features/step_definitions/git_steps.rb +29 -1
  17. data/features/support/env.rb +9 -0
  18. data/features/update/bump_version.feature +8 -12
  19. data/features/update/dot_sync.feature +52 -0
  20. data/features/update/pull_request.feature +180 -0
  21. data/features/update.feature +74 -103
  22. data/lib/modulesync/cli/thor.rb +12 -0
  23. data/lib/modulesync/cli.rb +122 -28
  24. data/lib/modulesync/git_service/base.rb +63 -0
  25. data/lib/modulesync/git_service/factory.rb +28 -0
  26. data/lib/modulesync/{pr → git_service}/github.rb +23 -21
  27. data/lib/modulesync/git_service/gitlab.rb +62 -0
  28. data/lib/modulesync/git_service.rb +96 -0
  29. data/lib/modulesync/hook.rb +11 -13
  30. data/lib/modulesync/renderer.rb +3 -6
  31. data/lib/modulesync/repository.rb +71 -25
  32. data/lib/modulesync/settings.rb +0 -1
  33. data/lib/modulesync/source_code.rb +28 -2
  34. data/lib/modulesync/util.rb +4 -4
  35. data/lib/modulesync.rb +104 -66
  36. data/modulesync.gemspec +7 -4
  37. data/spec/helpers/faker/puppet_module_remote_repo.rb +16 -1
  38. data/spec/spec_helper.rb +1 -23
  39. data/spec/unit/modulesync/git_service/factory_spec.rb +16 -0
  40. data/spec/unit/modulesync/git_service/github_spec.rb +81 -0
  41. data/spec/unit/modulesync/git_service/gitlab_spec.rb +90 -0
  42. data/spec/unit/modulesync/git_service_spec.rb +201 -0
  43. data/spec/unit/modulesync/source_code_spec.rb +22 -0
  44. data/spec/unit/modulesync_spec.rb +0 -12
  45. metadata +74 -12
  46. data/lib/modulesync/pr/gitlab.rb +0 -54
  47. data/spec/unit/modulesync/pr/github_spec.rb +0 -49
  48. data/spec/unit/modulesync/pr/gitlab_spec.rb +0 -81
@@ -33,10 +33,11 @@ module ModuleSync
33
33
  def default_branch
34
34
  symbolic_ref = repo.branches.find { |b| b.full =~ %r{remotes/origin/HEAD} }
35
35
  return unless symbolic_ref
36
+
36
37
  %r{remotes/origin/HEAD\s+->\s+origin/(?<branch>.+?)$}.match(symbolic_ref.full)[:branch]
37
38
  end
38
39
 
39
- def switch_branch(branch)
40
+ def switch(branch:)
40
41
  unless branch
41
42
  branch = default_branch
42
43
  puts "Using repository's default branch: #{branch}"
@@ -62,23 +63,50 @@ module ModuleSync
62
63
  end
63
64
  end
64
65
 
65
- def prepare_workspace(branch)
66
- # Repo needs to be cloned in the cwd
67
- if !Dir.exist?("#{@directory}/.git")
68
- puts "Cloning repository fresh from '#{@remote}'"
69
- @git = Git.clone(@remote, @directory)
70
- switch_branch(branch)
71
- # Repo already cloned, check out master and override local changes
66
+ def cloned?
67
+ Dir.exist? File.join(@directory, '.git')
68
+ end
69
+
70
+ def clone
71
+ puts "Cloning from '#{@remote}'"
72
+ @git = Git.clone(@remote, @directory)
73
+ end
74
+
75
+ def prepare_workspace(branch:, operate_offline:)
76
+ if cloned?
77
+ puts "Overriding any local changes to repository in '#{@directory}'"
78
+ git.fetch 'origin', prune: true unless operate_offline
79
+ git.reset_hard
80
+ switch(branch: branch)
81
+ git.pull('origin', branch) if !operate_offline && remote_branch_exists?(branch)
72
82
  else
73
- # Some versions of git can't properly handle managing a repo from outside the repo directory
74
- Dir.chdir(@directory) do
75
- puts "Overriding any local changes to repository in '#{@directory}'"
76
- @git = Git.open('.')
77
- repo.fetch
78
- repo.reset_hard
79
- switch_branch(branch)
80
- git.pull('origin', branch) if remote_branch_exists?(branch)
81
- end
83
+ raise ModuleSync::Error, 'Unable to clone in offline mode.' if operate_offline
84
+
85
+ clone
86
+ switch(branch: branch)
87
+ end
88
+ end
89
+
90
+ def default_reset_branch(branch)
91
+ remote_branch_exists?(branch) ? branch : default_branch
92
+ end
93
+
94
+ def reset_workspace(branch:, operate_offline:, source_branch: nil)
95
+ raise if branch.nil?
96
+
97
+ if cloned?
98
+ source_branch ||= "origin/#{default_reset_branch branch}"
99
+ puts "Hard-resetting any local changes to repository in '#{@directory}' from branch '#{source_branch}'"
100
+ switch(branch: branch)
101
+ git.fetch 'origin', prune: true unless operate_offline
102
+
103
+ git.reset_hard source_branch
104
+ git.clean(d: true, force: true)
105
+ else
106
+ raise ModuleSync::Error, 'Unable to clone in offline mode.' if operate_offline
107
+
108
+ clone
109
+ switch(branch: branch)
82
110
  end
83
111
  end
84
112
 
@@ -102,7 +130,7 @@ module ModuleSync
102
130
  files.each do |file|
103
131
  if repo.status.deleted.include?(file)
104
132
  repo.remove(file)
105
- elsif File.exist?("#{@directory}/#{file}")
133
+ elsif File.exist? File.join(@directory, file)
106
134
  repo.add(file)
107
135
  end
108
136
  end
@@ -119,9 +147,11 @@ module ModuleSync
119
147
  if options[:remote_branch]
120
148
  if remote_branch_differ?(branch, options[:remote_branch])
121
149
  repo.push('origin', "#{branch}:#{options[:remote_branch]}", opts_push)
150
+ puts "Changes have been pushed to: '#{branch}:#{options[:remote_branch]}'"
122
151
  end
123
152
  else
124
153
  repo.push('origin', branch, opts_push)
154
+ puts "Changes have been pushed to: '#{branch}'"
125
155
  end
126
156
  rescue Git::GitExecuteError => e
127
157
  raise unless e.message.match?(/working (directory|tree) clean/)
@@ -133,11 +163,21 @@ module ModuleSync
133
163
  true
134
164
  end
135
165
 
166
+ def push(branch:, remote_branch:, remote_name: 'origin')
167
+ raise ModuleSync::Error, 'Repository must be locally available before trying to push' unless cloned?
168
+
169
+ remote_url = git.remote(remote_name).url
170
+ remote_branch ||= branch
171
+ puts "Push branch '#{branch}' to '#{remote_url}' (#{remote_name}/#{remote_branch})"
172
+
173
+ git.push(remote_name, "#{branch}:#{remote_branch}", force: true)
174
+ end
175
+
136
176
  # Needed because of a bug in the git gem that lists ignored files as
137
177
  # untracked under some circumstances
138
178
  # https://github.com/schacon/ruby-git/issues/130
139
179
  def untracked_unignored_files
140
- ignore_path = "#{@directory}/.gitignore"
180
+ ignore_path = File.join @directory, '.gitignore'
141
181
  ignored = File.exist?(ignore_path) ? File.read(ignore_path).split : []
142
182
  repo.status.untracked.keep_if { |f, _| ignored.none? { |i| File.fnmatch(i, f) } }
143
183
  end
@@ -145,18 +185,24 @@ module ModuleSync
145
185
  def show_changes(options)
146
186
  checkout_branch(options[:branch])
147
187
 
148
- puts 'Files changed:'
188
+ $stdout.puts 'Files changed:'
149
189
  repo.diff('HEAD', '--').each do |diff|
150
- puts diff.patch
190
+ $stdout.puts diff.patch
151
191
  end
152
192
 
153
- puts 'Files added:'
193
+ $stdout.puts 'Files added:'
154
194
  untracked_unignored_files.each_key do |file|
155
- puts file
195
+ $stdout.puts file
156
196
  end
157
197
 
158
- puts "\n\n"
159
- puts '--------------------------------'
198
+ $stdout.puts "\n\n"
199
+ $stdout.puts '--------------------------------'
200
+
201
+ git.diff('HEAD', '--').any? || untracked_unignored_files.any?
202
+ end
203
+
204
+ def puts(*args)
205
+ $stdout.puts(*args) if ModuleSync.options[:verbose]
160
206
  end
161
207
  end
162
208
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module ModuleSync
3
2
  # Encapsulate a configs for a module, providing easy access to its parts
4
3
  # All configs MUST be keyed by the relative target filename
@@ -1,12 +1,13 @@
1
1
  require 'modulesync'
2
+ require 'modulesync/git_service'
3
+ require 'modulesync/git_service/factory'
2
4
  require 'modulesync/repository'
3
5
  require 'modulesync/util'
4
6
 
5
7
  module ModuleSync
6
8
  # Provide methods to retrieve source code attributes
7
9
  class SourceCode
8
- attr_reader :given_name
9
- attr_reader :options
10
+ attr_reader :given_name, :options
10
11
 
11
12
  def initialize(given_name, options)
12
13
  @options = Util.symbolize_keys(options || {})
@@ -47,6 +48,31 @@ module ModuleSync
47
48
  File.join(working_directory, *parts)
48
49
  end
49
50
 
51
+ def git_service
52
+ return nil if git_service_configuration.nil?
53
+
54
+ @git_service ||= GitService::Factory.instantiate(**git_service_configuration)
55
+ end
56
+
57
+ def git_service_configuration
58
+ @git_service_configuration ||= GitService.configuration_for(sourcecode: self)
59
+ rescue GitService::UnguessableTypeError
60
+ nil
61
+ end
62
+
63
+ def open_pull_request
64
+ git_service.open_pull_request(
65
+ repo_path: repository_path,
66
+ namespace: repository_namespace,
67
+ title: ModuleSync.options[:pr_title],
68
+ message: ModuleSync.options[:message],
69
+ source_branch: ModuleSync.options[:remote_branch] || ModuleSync.options[:branch] || repository.default_branch,
70
+ target_branch: ModuleSync.options[:pr_target_branch] || repository.default_branch,
71
+ labels: ModuleSync::Util.parse_list(ModuleSync.options[:pr_labels]),
72
+ noop: ModuleSync.options[:noop],
73
+ )
74
+ end
75
+
50
76
  private
51
77
 
52
78
  def _repository_remote
@@ -3,9 +3,8 @@ require 'yaml'
3
3
  module ModuleSync
4
4
  module Util
5
5
  def self.symbolize_keys(hash)
6
- hash.inject({}) do |memo, (k, v)|
6
+ hash.each_with_object({}) do |(k, v), memo|
7
7
  memo[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v
8
- memo
9
8
  end
10
9
  end
11
10
 
@@ -19,9 +18,10 @@ module ModuleSync
19
18
  end
20
19
 
21
20
  def self.parse_list(option_value)
22
- if option_value.is_a? String
21
+ case option_value
22
+ when String
23
23
  option_value.split(',')
24
- elsif option_value.is_a? Array
24
+ when Array
25
25
  option_value
26
26
  else
27
27
  []
data/lib/modulesync.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'English'
1
2
  require 'fileutils'
2
3
  require 'pathname'
3
4
 
@@ -18,10 +19,10 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
18
19
 
19
20
  def self.config_defaults
20
21
  {
21
- :project_root => 'modules/',
22
+ :project_root => 'modules/',
22
23
  :managed_modules_conf => 'managed_modules.yml',
23
- :configs => '.',
24
- :tag_pattern => '%s'
24
+ :configs => '.',
25
+ :tag_pattern => '%s',
25
26
  }
26
27
  end
27
28
 
@@ -44,8 +45,8 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
44
45
  .to_a
45
46
  else
46
47
  $stderr.puts "#{local_template_dir} does not exist." \
47
- ' Check that you are working in your module configs directory or' \
48
- ' that you have passed in the correct directory with -c.'
48
+ ' Check that you are working in your module configs directory or' \
49
+ ' that you have passed in the correct directory with -c.'
49
50
  exit 1
50
51
  end
51
52
  end
@@ -62,7 +63,7 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
62
63
  managed_modules = Util.parse_config(config_file)
63
64
  if managed_modules.empty?
64
65
  $stderr.puts "No modules found in #{config_file}." \
65
- ' Check that you specified the right :configs directory and :managed_modules_conf file.'
66
+ ' Check that you specified the right :configs directory and :managed_modules_conf file.'
66
67
  exit 1
67
68
  end
68
69
  managed_modules.select! { |m| m =~ Regexp.new(filter) } unless filter.nil?
@@ -82,7 +83,6 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
82
83
  end
83
84
 
84
85
  def self.manage_file(puppet_module, filename, settings, options)
85
- namespace = settings.additional_settings[:namespace]
86
86
  module_name = settings.additional_settings[:puppet_module]
87
87
  configs = settings.build_file_configs(filename)
88
88
  target_file = puppet_module.path(filename)
@@ -95,12 +95,12 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
95
95
  # Meta data passed to the template as @metadata[:name]
96
96
  metadata = {
97
97
  :module_name => module_name,
98
- :workdir => puppet_module.working_directory,
98
+ :workdir => puppet_module.working_directory,
99
99
  :target_file => target_file,
100
100
  }
101
101
  template = Renderer.render(erb, configs, metadata)
102
102
  Renderer.sync(template, target_file)
103
- rescue StandardError => e
103
+ rescue StandardError
104
104
  $stderr.puts "#{puppet_module.given_name}: Error while rendering file: '#{filename}'"
105
105
  raise
106
106
  end
@@ -109,7 +109,12 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
109
109
 
110
110
  def self.manage_module(puppet_module, module_files, defaults)
111
111
  puts "Syncing '#{puppet_module.given_name}'"
112
- puppet_module.repository.prepare_workspace(options[:branch]) unless options[:offline]
112
+ # NOTE: #prepare_workspace now supports to execute only offline operations
113
+ # but we totally skip the workspace preparation to keep the current behavior
114
+ unless options[:offline]
115
+ puppet_module.repository.prepare_workspace(branch: options[:branch],
116
+ operate_offline: false)
117
+ end
113
118
 
114
119
  module_configs = Util.parse_config puppet_module.path(MODULE_CONF_FILE)
115
120
  settings = Settings.new(defaults[GLOBAL_DEFAULTS_KEY] || {},
@@ -129,9 +134,8 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
129
134
 
130
135
  if options[:noop]
131
136
  puts "Using no-op. Files in '#{puppet_module.given_name}' may be changed but will not be committed."
132
- puppet_module.repository.show_changes(options)
133
- options[:pr] && \
134
- pr(puppet_module).manage(puppet_module.repository_namespace, puppet_module.repository_name, options)
137
+ changed = puppet_module.repository.show_changes(options)
138
+ changed && options[:pr] && puppet_module.open_pull_request
135
139
  elsif !options[:offline]
136
140
  pushed = puppet_module.repository.submit_changes(files_to_manage, options)
137
141
  # Only bump/tag if pushing didn't fail (i.e. there were changes)
@@ -139,13 +143,13 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
139
143
  new = puppet_module.bump(options[:message], options[:changelog])
140
144
  puppet_module.repository.tag(new, options[:tag_pattern]) if options[:tag]
141
145
  end
142
- pushed && options[:pr] && \
143
- pr(puppet_module).manage(puppet_module.repository_namespace, puppet_module.repository_name, options)
146
+ pushed && options[:pr] && puppet_module.open_pull_request
144
147
  end
145
148
  end
146
149
 
147
150
  def self.config_path(file, options)
148
151
  return file if Pathname.new(file).absolute?
152
+
149
153
  File.join(options[:configs], file)
150
154
  end
151
155
 
@@ -157,15 +161,6 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
157
161
  @options = config_defaults.merge(cli_options)
158
162
  defaults = Util.parse_config(config_path(CONF_FILE, options))
159
163
 
160
- if options[:pr]
161
- unless options[:branch]
162
- $stderr.puts 'A branch must be specified with --branch to use --pr!'
163
- raise
164
- end
165
-
166
- @pr = create_pr_manager if options[:pr]
167
- end
168
-
169
164
  local_template_dir = config_path(MODULE_FILES_DIR, options)
170
165
  local_files = find_template_files(local_template_dir)
171
166
  module_files = relative_names(local_files, local_template_dir)
@@ -173,56 +168,99 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
173
168
  errors = false
174
169
  # managed_modules is either an array or a hash
175
170
  managed_modules.each do |puppet_module|
176
- begin
177
- manage_module(puppet_module, module_files, defaults)
178
- rescue ModuleSync::Error, Git::GitExecuteError => e
179
- message = e.message || "Error during '#{options[:command]}'"
180
- $stderr.puts "#{puppet_module.given_name}: #{message}"
181
- exit 1 unless options[:skip_broken]
182
- errors = true
183
- $stdout.puts "Skipping '#{puppet_module.given_name}' as update process failed"
184
- rescue StandardError => e
185
- raise unless options[:skip_broken]
186
- errors = true
187
- $stdout.puts "Skipping '#{puppet_module.given_name}' as update process failed"
188
- end
171
+ manage_module(puppet_module, module_files, defaults)
172
+ rescue ModuleSync::Error, Git::GitExecuteError => e
173
+ message = e.message || 'Error during `update`'
174
+ $stderr.puts "#{puppet_module.given_name}: #{message}"
175
+ exit 1 unless options[:skip_broken]
176
+ errors = true
177
+ $stdout.puts "Skipping '#{puppet_module.given_name}' as update process failed"
178
+ rescue StandardError
179
+ raise unless options[:skip_broken]
180
+
181
+ errors = true
182
+ $stdout.puts "Skipping '#{puppet_module.given_name}' as update process failed"
189
183
  end
190
184
  exit 1 if errors && options[:fail_on_warnings]
191
185
  end
192
186
 
193
- def self.pr(puppet_module)
194
- module_options = puppet_module.options
195
- github_conf = module_options[:github]
196
- gitlab_conf = module_options[:gitlab]
197
-
198
- if !github_conf.nil?
199
- base_url = github_conf[:base_url] || ENV.fetch('GITHUB_BASE_URL', 'https://api.github.com')
200
- require 'modulesync/pr/github'
201
- ModuleSync::PR::GitHub.new(github_conf[:token], base_url)
202
- elsif !gitlab_conf.nil?
203
- base_url = gitlab_conf[:base_url] || ENV.fetch('GITLAB_BASE_URL', 'https://gitlab.com/api/v4')
204
- require 'modulesync/pr/gitlab'
205
- ModuleSync::PR::GitLab.new(gitlab_conf[:token], base_url)
206
- elsif @pr.nil?
207
- $stderr.puts 'No GitHub or GitLab token specified for --pr!'
208
- raise
209
- else
210
- @pr
187
+ def self.clone(cli_options)
188
+ @options = config_defaults.merge(cli_options)
189
+
190
+ managed_modules.each do |puppet_module|
191
+ puppet_module.repository.clone unless puppet_module.repository.cloned?
211
192
  end
212
193
  end
213
194
 
214
- def self.create_pr_manager
215
- github_token = ENV.fetch('GITHUB_TOKEN', '')
216
- gitlab_token = ENV.fetch('GITLAB_TOKEN', '')
195
+ def self.execute(cli_options)
196
+ @options = config_defaults.merge(cli_options)
217
197
 
218
- if !github_token.empty?
219
- require 'modulesync/pr/github'
220
- ModuleSync::PR::GitHub.new(github_token, ENV.fetch('GITHUB_BASE_URL', 'https://api.github.com'))
221
- elsif !gitlab_token.empty?
222
- require 'modulesync/pr/gitlab'
223
- ModuleSync::PR::GitLab.new(gitlab_token, ENV.fetch('GITLAB_BASE_URL', 'https://gitlab.com/api/v4'))
224
- else
225
- warn '--pr specified without environment variables GITHUB_TOKEN or GITLAB_TOKEN'
198
+ errors = {}
199
+ managed_modules.each do |puppet_module|
200
+ $stdout.puts "#{puppet_module.given_name}:"
201
+
202
+ puppet_module.repository.clone unless puppet_module.repository.cloned?
203
+ puppet_module.repository.switch branch: @options[:branch]
204
+
205
+ command_args = cli_options[:command_args]
206
+ local_script = File.expand_path command_args[0]
207
+ command_args[0] = local_script if File.exist?(local_script)
208
+
209
+ # Remove bundler-related env vars to allow the subprocess to run `bundle`
210
+ command_env = ENV.reject { |k, _v| k.match?(/(^BUNDLE|^SOURCE_DATE_EPOCH$|^GEM_|RUBY)/) }
211
+
212
+ result = system command_env, *command_args, unsetenv_others: true, chdir: puppet_module.working_directory
213
+ unless result
214
+ message = "Command execution failed ('#{@options[:command_args].join ' '}': #{$CHILD_STATUS})"
215
+ raise Thor::Error, message if @options[:fail_fast]
216
+
217
+ errors.merge!(
218
+ puppet_module.given_name => message,
219
+ )
220
+ $stderr.puts message
221
+ end
222
+
223
+ $stdout.puts ''
224
+ end
225
+
226
+ unless errors.empty?
227
+ raise Thor::Error, <<~MSG
228
+ Error(s) during `execute` command:
229
+ #{errors.map { |name, message| " * #{name}: #{message}" }.join "\n"}
230
+ MSG
231
+ end
232
+
233
+ exit 1 unless errors.empty?
234
+ end
235
+
236
+ def self.reset(cli_options)
237
+ @options = config_defaults.merge(cli_options)
238
+ if @options[:branch].nil?
239
+ raise Thor::Error,
240
+ "Error: 'branch' option is missing, please set it in configuration or in command line."
241
+ end
242
+
243
+ managed_modules.each do |puppet_module|
244
+ puppet_module.repository.reset_workspace(
245
+ branch: @options[:branch],
246
+ source_branch: @options[:source_branch],
247
+ operate_offline: @options[:offline],
248
+ )
249
+ end
250
+ end
251
+
252
+ def self.push(cli_options)
253
+ @options = config_defaults.merge(cli_options)
254
+
255
+ if @options[:branch].nil?
256
+ raise Thor::Error,
257
+ "Error: 'branch' option is missing, please set it in configuration or in command line."
258
+ end
259
+
260
+ managed_modules.each do |puppet_module|
261
+ puppet_module.repository.push branch: @options[:branch], remote_branch: @options[:remote_branch]
262
+ rescue ModuleSync::Error => e
263
+ raise Thor::Error, "#{puppet_module.given_name}: #{e.message}"
226
264
  end
227
265
  end
228
266
  end
data/modulesync.gemspec CHANGED
@@ -1,9 +1,9 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = 'modulesync'
6
- spec.version = '2.2.0'
6
+ spec.version = '2.3.0'
7
7
  spec.authors = ['Vox Pupuli']
8
8
  spec.email = ['voxpupuli@groups.io']
9
9
  spec.summary = 'Puppet Module Synchronizer'
@@ -19,10 +19,13 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_development_dependency 'aruba', '>= 0.14', '< 2'
21
21
  spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'cucumber'
22
23
  spec.add_development_dependency 'rake'
23
24
  spec.add_development_dependency 'rspec'
24
- spec.add_development_dependency 'rubocop', '~> 0.50.0'
25
- spec.add_development_dependency 'cucumber'
25
+ spec.add_development_dependency 'rubocop', '~> 1.2'
26
+ spec.add_development_dependency 'rubocop-rake'
27
+ spec.add_development_dependency 'rubocop-rspec'
28
+ spec.add_development_dependency 'simplecov'
26
29
 
27
30
  spec.add_runtime_dependency 'git', '~>1.7'
28
31
  spec.add_runtime_dependency 'gitlab', '~>4.0'
@@ -77,6 +77,7 @@ module ModuleSync
77
77
  branch ||= default_branch
78
78
  FileUtils.chdir(tmp_repo_dir) do
79
79
  run %W[git checkout #{branch}]
80
+ run %w[git pull --force --prune]
80
81
  File.write filename, content
81
82
  run %W[git add #{filename}]
82
83
  run %w[git commit --message] << "Add file: '#{filename}'"
@@ -94,7 +95,7 @@ module ModuleSync
94
95
  def commit_count_by(author, commit = nil)
95
96
  FileUtils.chdir(bare_repo_dir) do
96
97
  commit ||= '--all'
97
- stdout = run %W[git rev-list #{commit} --author #{author} --count]
98
+ stdout = run %W[git rev-list --author #{author} --count #{commit} --]
98
99
  return Integer(stdout)
99
100
  end
100
101
  end
@@ -105,6 +106,20 @@ module ModuleSync
105
106
  end
106
107
  end
107
108
 
109
+ def delete_branch(branch)
110
+ FileUtils.chdir(bare_repo_dir) do
111
+ run %W{git branch -D #{branch}}
112
+ end
113
+ end
114
+
115
+ def create_branch(branch, from = nil)
116
+ from ||= default_branch
117
+ FileUtils.chdir(tmp_repo_dir) do
118
+ run %W{git branch -c #{from} #{branch}}
119
+ run %W{git push --set-upstream origin #{branch}}
120
+ end
121
+ end
122
+
108
123
  def remote_url
109
124
  "file://#{bare_repo_dir}"
110
125
  end
data/spec/spec_helper.rb CHANGED
@@ -1,25 +1,3 @@
1
- begin
2
- require 'simplecov'
3
- require 'simplecov-console'
4
- require 'codecov'
5
- rescue LoadError
6
- else
7
- SimpleCov.start do
8
- track_files 'lib/**/*.rb'
9
-
10
- add_filter '/spec'
11
-
12
- enable_coverage :branch
13
-
14
- # do not track vendored files
15
- add_filter '/vendor'
16
- add_filter '/.vendor'
17
- end
18
-
19
- SimpleCov.formatters = [
20
- SimpleCov::Formatter::Console,
21
- SimpleCov::Formatter::Codecov,
22
- ]
23
- end
1
+ require 'simplecov'
24
2
 
25
3
  require 'modulesync'
@@ -0,0 +1,16 @@
1
+ require 'modulesync'
2
+ require 'modulesync/git_service/factory'
3
+
4
+ describe ModuleSync::GitService::Factory do
5
+ context 'when instantiate a GitHub service without credentials' do
6
+ it 'raises an error' do
7
+ expect { ModuleSync::GitService::Factory.instantiate(type: :github, endpoint: nil, token: nil) }.to raise_error(ModuleSync::GitService::MissingCredentialsError)
8
+ end
9
+ end
10
+
11
+ context 'when instantiate a GitLab service without credentials' do
12
+ it 'raises an error' do
13
+ expect { ModuleSync::GitService::Factory.instantiate(type: :gitlab, endpoint: nil, token: nil) }.to raise_error(ModuleSync::GitService::MissingCredentialsError)
14
+ end
15
+ end
16
+ end