modulesync 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ Feature: Run `msync update` without a good context
2
+
3
+ Scenario: Run `msync update` without any module
4
+ Given a directory named "moduleroot"
5
+ When I run `msync update --message "In a bad context"`
6
+ Then the exit status should be 1
7
+ And the stderr should contain:
8
+ """
9
+ No modules found
10
+ """
11
+
12
+ Scenario: Run `msync update` without the "moduleroot" directory
13
+ Given a basic setup with a puppet module "puppet-test" from "fakenamespace"
14
+ When I run `msync update --message "In a bad context"`
15
+ Then the exit status should be 1
16
+ And the stderr should contain "moduleroot"
17
+
18
+ Scenario: Run `msync update` without commit message
19
+ Given a basic setup with a puppet module "puppet-test" from "fakenamespace"
20
+ And a directory named "moduleroot"
21
+ When I run `msync update`
22
+ Then the exit status should be 1
23
+ And the stderr should contain:
24
+ """
25
+ No value provided for required option "--message"
26
+ """
data/lib/modulesync.rb CHANGED
@@ -39,10 +39,10 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
39
39
  .collect { |p| p.chomp('.erb') }
40
40
  .to_a
41
41
  else
42
- $stdout.puts "#{local_template_dir} does not exist." \
42
+ $stderr.puts "#{local_template_dir} does not exist." \
43
43
  ' Check that you are working in your module configs directory or' \
44
44
  ' that you have passed in the correct directory with -c.'
45
- exit
45
+ exit 1
46
46
  end
47
47
  end
48
48
 
@@ -53,9 +53,9 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
53
53
  def self.managed_modules(config_file, filter, negative_filter)
54
54
  managed_modules = Util.parse_config(config_file)
55
55
  if managed_modules.empty?
56
- $stdout.puts "No modules found in #{config_file}." \
56
+ $stderr.puts "No modules found in #{config_file}." \
57
57
  ' Check that you specified the right :configs directory and :managed_modules_conf file.'
58
- exit
58
+ exit 1
59
59
  end
60
60
  managed_modules.select! { |m| m =~ Regexp.new(filter) } unless filter.nil?
61
61
  managed_modules.reject! { |m| m =~ Regexp.new(negative_filter) } unless negative_filter.nil?
@@ -1,10 +1,12 @@
1
1
  require 'thor'
2
+
2
3
  require 'modulesync'
4
+ require 'modulesync/cli/thor'
3
5
  require 'modulesync/constants'
4
6
  require 'modulesync/util'
5
7
 
6
8
  module ModuleSync
7
- class CLI
9
+ module CLI
8
10
  def self.defaults
9
11
  @defaults ||= Util.symbolize_keys(Util.parse_config(Constants::MODULESYNC_CONF_FILE))
10
12
  end
@@ -36,15 +38,14 @@ module ModuleSync
36
38
  class Base < Thor
37
39
  class_option :project_root,
38
40
  :aliases => '-c',
39
- :desc => 'Path used by git to clone modules into. Defaults to "modules"',
41
+ :desc => 'Path used by git to clone modules into.',
40
42
  :default => CLI.defaults[:project_root] || 'modules'
41
43
  class_option :git_base,
42
44
  :desc => 'Specify the base part of a git URL to pull from',
43
45
  :default => CLI.defaults[:git_base] || 'git@github.com:'
44
46
  class_option :namespace,
45
47
  :aliases => '-n',
46
- :desc => 'Remote github namespace (user or organization) to clone from and push to.' \
47
- ' Defaults to puppetlabs',
48
+ :desc => 'Remote github namespace (user or organization) to clone from and push to.',
48
49
  :default => CLI.defaults[:namespace] || 'puppetlabs'
49
50
  class_option :filter,
50
51
  :aliases => '-f',
@@ -0,0 +1,24 @@
1
+ require 'thor'
2
+ require 'modulesync/cli'
3
+
4
+ module ModuleSync
5
+ module CLI
6
+ # Workaround some, still unfixed, Thor behaviors
7
+ #
8
+ # This class extends ::Thor class to
9
+ # - exit with status code sets to `1` on Thor failure (e.g. missing required option)
10
+ # - exit with status code sets to `1` when user calls `msync` (or a subcommand) without required arguments
11
+ class Thor < ::Thor
12
+ desc '_invalid_command_call', 'Invalid command', hide: true
13
+ def _invalid_command_call
14
+ self.class.new.help
15
+ exit 1
16
+ end
17
+ default_task :_invalid_command_call
18
+
19
+ def self.exit_on_failure?
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
@@ -150,7 +150,7 @@ module ModuleSync
150
150
  tag(repo, new, options[:tag_pattern]) if options[:tag]
151
151
  end
152
152
  rescue ::Git::GitExecuteError => git_error
153
- if git_error.message =~ /working (directory|tree) clean/
153
+ if git_error.message.match?(/working (directory|tree) clean/)
154
154
  puts "There were no files to update in #{name}. Not committing."
155
155
  return false
156
156
  else
@@ -1,55 +1,16 @@
1
1
  module Git
2
- class Diff
3
- # Monkey patch process_full_diff until https://github.com/schacon/ruby-git/issues/326 is resolved
4
- def process_full_diff
5
- defaults = {
6
- :mode => '',
7
- :src => '',
8
- :dst => '',
9
- :type => 'modified'
10
- }
11
- final = {}
12
- current_file = nil
13
- full_diff_utf8_encoded = @full_diff.encode("UTF-8", "binary", {
14
- :invalid => :replace,
15
- :undef => :replace
16
- })
17
- full_diff_utf8_encoded.split("\n").each do |line|
18
- if m = /^diff --git a\/(.*?) b\/(.*?)/.match(line)
19
- current_file = m[1]
20
- final[current_file] = defaults.merge({:patch => line, :path => current_file})
21
- elsif !current_file.nil?
22
- if m = /^index (.......)\.\.(.......)( ......)*/.match(line)
23
- final[current_file][:src] = m[1]
24
- final[current_file][:dst] = m[2]
25
- final[current_file][:mode] = m[3].strip if m[3]
26
- end
27
- if m = /^([[:alpha:]]*?) file mode (......)/.match(line)
28
- final[current_file][:type] = m[1]
29
- final[current_file][:mode] = m[2]
30
- end
31
- if m = /^Binary files /.match(line)
32
- final[current_file][:binary] = true
33
- end
34
- final[current_file][:patch] << "\n" + line
35
- end
36
- end
37
- final.map { |e| [e[0], DiffFile.new(@base, e[1])] }
2
+ module LibMonkeyPatch
3
+ # Monkey patch set_custom_git_env_variables due to our ::Git::GitExecuteError handling.
4
+ #
5
+ # We rescue on the GitExecuteError and proceed differently based on the output of git.
6
+ # This way makes code language-dependent, so here we ensure that Git gem throw git commands with the "C" language
7
+ def set_custom_git_env_variables
8
+ super
9
+ ENV['LANG'] = 'C.UTF-8'
38
10
  end
39
11
  end
40
12
 
41
13
  class Lib
42
- # Monkey patch ls_files until https://github.com/ruby-git/ruby-git/pull/320 is resolved
43
- def ls_files(location=nil)
44
- location ||= '.'
45
- hsh = {}
46
- command_lines('ls-files', ['--stage', location]).each do |line|
47
- (info, file) = line.split("\t")
48
- (mode, sha, stage) = info.split
49
- file = eval(file) if file =~ /^\".*\"$/ # This takes care of quoted strings returned from git
50
- hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
51
- end
52
- hsh
53
- end
14
+ prepend LibMonkeyPatch
54
15
  end
55
16
  end
data/modulesync.gemspec CHANGED
@@ -3,7 +3,7 @@ $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.0.1'
6
+ spec.version = '2.0.2'
7
7
  spec.authors = ['Vox Pupuli']
8
8
  spec.email = ['voxpupuli@groups.io']
9
9
  spec.summary = 'Puppet Module Synchronizer'
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'rspec'
24
24
  spec.add_development_dependency 'rubocop', '~> 0.50.0'
25
25
 
26
- spec.add_runtime_dependency 'git', '~>1.3'
26
+ spec.add_runtime_dependency 'git', '~>1.7'
27
27
  spec.add_runtime_dependency 'gitlab', '~>4.0'
28
28
  spec.add_runtime_dependency 'octokit', '~>4.0'
29
29
  spec.add_runtime_dependency 'puppet-blacksmith', '>= 3.0', '< 7'
@@ -0,0 +1,14 @@
1
+ module ModuleSync
2
+ # Faker is a top-level module to keep global faker config
3
+ module Faker
4
+ def self.working_directory=(path)
5
+ @working_directory = path
6
+ end
7
+
8
+ def self.working_directory
9
+ raise 'Working directory must be set' if @working_directory.nil?
10
+ FileUtils.mkdir_p @working_directory
11
+ @working_directory
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,140 @@
1
+ require 'open3'
2
+
3
+ require_relative '../faker'
4
+
5
+ module ModuleSync
6
+ # Fake a remote git repository that holds a puppet module
7
+ #
8
+ # This module allows to fake a remote repositiory using:
9
+ # - a bare repo
10
+ # - a temporary cloned repo to operate on the remote before exposing to modulesync
11
+ #
12
+ # Note: This module needs to have working_directory sets before using it
13
+ module Faker
14
+ class PuppetModuleRemoteRepo
15
+ class CommandExecutionError < StandardError; end
16
+
17
+ attr_reader :name, :namespace
18
+
19
+ def initialize(name, namespace)
20
+ @name = name
21
+ @namespace = namespace
22
+ end
23
+
24
+ def populate
25
+ FileUtils.chdir(Faker.working_directory) do
26
+ run %W[git init --bare #{bare_repo_dir}]
27
+ run %W[git clone #{bare_repo_dir} #{tmp_repo_dir}]
28
+
29
+ module_short_name = name.split('-').last
30
+
31
+ FileUtils.chdir(tmp_repo_dir) do
32
+ metadata = {
33
+ name: "modulesync-#{module_short_name}",
34
+ version: '0.4.2',
35
+ author: 'ModuleSync team',
36
+ }
37
+
38
+ File.write 'metadata.json', metadata.to_json
39
+ run %w[git add metadata.json]
40
+ run %w[git commit --message] << 'Initial commit'
41
+ run %w[git push]
42
+ end
43
+ end
44
+ end
45
+
46
+ def read_only=(value)
47
+ mode = value ? '0444' : '0644'
48
+ FileUtils.chdir(bare_repo_dir) do
49
+ run %W[git config core.sharedRepository #{mode}]
50
+ end
51
+ end
52
+
53
+ def default_branch
54
+ FileUtils.chdir(bare_repo_dir) do
55
+ stdout = run %w[git symbolic-ref --short HEAD]
56
+ return stdout.chomp
57
+ end
58
+ end
59
+
60
+ def default_branch=(value)
61
+ FileUtils.chdir(bare_repo_dir) do
62
+ run %W[git branch -M #{default_branch} #{value}]
63
+ run %W[git symbolic-ref HEAD refs/heads/#{value}]
64
+ end
65
+ end
66
+
67
+ def read_file(filename, branch = nil)
68
+ branch ||= default_branch
69
+ FileUtils.chdir(bare_repo_dir) do
70
+ return run %W[git show #{branch}:#{filename}]
71
+ rescue CommandExecutionError
72
+ return nil
73
+ end
74
+ end
75
+
76
+ def add_file(filename, content, branch = nil)
77
+ branch ||= default_branch
78
+ FileUtils.chdir(tmp_repo_dir) do
79
+ run %W[git checkout #{branch}]
80
+ File.write filename, content
81
+ run %W[git add #{filename}]
82
+ run %w[git commit --message] << "Add file: '#{filename}'"
83
+ run %w[git push]
84
+ end
85
+ end
86
+
87
+ def commit_count_between(commit1, commit2)
88
+ FileUtils.chdir(bare_repo_dir) do
89
+ stdout = run %W[git rev-list --count #{commit1}..#{commit2}]
90
+ return Integer(stdout)
91
+ end
92
+ end
93
+
94
+ def commit_count_by(author, commit = nil)
95
+ FileUtils.chdir(bare_repo_dir) do
96
+ commit ||= '--all'
97
+ stdout = run %W[git rev-list #{commit} --author #{author} --count]
98
+ return Integer(stdout)
99
+ end
100
+ end
101
+
102
+ def remote_url
103
+ "file://#{bare_repo_dir}"
104
+ end
105
+
106
+ def self.git_base
107
+ "file://#{Faker.working_directory}/bare/"
108
+ end
109
+
110
+ private
111
+
112
+ def tmp_repo_dir
113
+ File.join Faker.working_directory, 'tmp', namespace, name
114
+ end
115
+
116
+ def bare_repo_dir
117
+ File.join Faker.working_directory, 'bare', namespace, "#{name}.git"
118
+ end
119
+
120
+ GIT_ENV = {
121
+ 'GIT_AUTHOR_NAME' => 'Faker',
122
+ 'GIT_AUTHOR_EMAIL' => 'faker@example.com',
123
+ 'GIT_COMMITTER_NAME' => 'Faker',
124
+ 'GIT_COMMITTER_EMAIL' => 'faker@example.com',
125
+ }.freeze
126
+
127
+ def run(command)
128
+ stdout, stderr, status = Open3.capture3(GIT_ENV, *command)
129
+ return stdout if status.success?
130
+
131
+ warn "Command '#{command}' failed: #{status}"
132
+ warn ' STDOUT:'
133
+ warn stdout
134
+ warn ' STDERR:'
135
+ warn stderr
136
+ raise CommandExecutionError, "Command '#{command}' failed: #{status}"
137
+ end
138
+ end
139
+ end
140
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulesync
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vox Pupuli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-06 00:00:00.000000000 Z
11
+ date: 2021-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aruba
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.3'
89
+ version: '1.7'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.3'
96
+ version: '1.7'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: gitlab
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,7 @@ executables:
164
164
  extensions: []
165
165
  extra_rdoc_files: []
166
166
  files:
167
+ - ".config/cucumber.yml"
167
168
  - ".gitignore"
168
169
  - ".rspec"
169
170
  - ".rubocop.yml"
@@ -182,8 +183,10 @@ files:
182
183
  - features/step_definitions/git_steps.rb
183
184
  - features/support/env.rb
184
185
  - features/update.feature
186
+ - features/update/bad_context.feature
185
187
  - lib/modulesync.rb
186
188
  - lib/modulesync/cli.rb
189
+ - lib/modulesync/cli/thor.rb
187
190
  - lib/modulesync/constants.rb
188
191
  - lib/modulesync/git.rb
189
192
  - lib/modulesync/hook.rb
@@ -194,6 +197,8 @@ files:
194
197
  - lib/modulesync/util.rb
195
198
  - lib/monkey_patches.rb
196
199
  - modulesync.gemspec
200
+ - spec/helpers/faker.rb
201
+ - spec/helpers/faker/puppet_module_remote_repo.rb
197
202
  - spec/spec_helper.rb
198
203
  - spec/unit/modulesync/pr/github_spec.rb
199
204
  - spec/unit/modulesync/pr/gitlab_spec.rb
@@ -228,6 +233,9 @@ test_files:
228
233
  - features/step_definitions/git_steps.rb
229
234
  - features/support/env.rb
230
235
  - features/update.feature
236
+ - features/update/bad_context.feature
237
+ - spec/helpers/faker.rb
238
+ - spec/helpers/faker/puppet_module_remote_repo.rb
231
239
  - spec/spec_helper.rb
232
240
  - spec/unit/modulesync/pr/github_spec.rb
233
241
  - spec/unit/modulesync/pr/gitlab_spec.rb