modulesync 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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