modulesync 1.1.0 → 2.0.2

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.
data/modulesync.gemspec CHANGED
@@ -3,14 +3,14 @@ $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 = '1.1.0'
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'
10
10
  spec.description = 'Utility to synchronize common files across puppet modules in Github.'
11
11
  spec.homepage = 'http://github.com/voxpupuli/modulesync'
12
12
  spec.license = 'Apache-2.0'
13
- spec.required_ruby_version = '>= 2.0.0'
13
+ spec.required_ruby_version = '>= 2.5.0'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0")
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -23,8 +23,9 @@ 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
+ spec.add_runtime_dependency 'gitlab', '~>4.0'
27
28
  spec.add_runtime_dependency 'octokit', '~>4.0'
28
- spec.add_runtime_dependency 'puppet-blacksmith', '~>3.0'
29
+ spec.add_runtime_dependency 'puppet-blacksmith', '>= 3.0', '< 7'
29
30
  spec.add_runtime_dependency 'thor'
30
31
  end
@@ -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
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'modulesync/pr/github'
3
+
4
+ describe ModuleSync::PR::GitHub do
5
+ context '::manage' do
6
+ before(:each) do
7
+ @git_repo = 'test/modulesync'
8
+ @namespace, @repo_name = @git_repo.split('/')
9
+ @options = {
10
+ :pr => true,
11
+ :pr_title => 'Test PR is submitted',
12
+ :branch => 'test',
13
+ :message => 'Hello world',
14
+ :pr_auto_merge => false,
15
+ }
16
+
17
+ @client = double()
18
+ allow(Octokit::Client).to receive(:new).and_return(@client)
19
+ @it = ModuleSync::PR::GitHub.new('test', 'https://api.github.com')
20
+ end
21
+
22
+ it 'submits PR when --pr is set' do
23
+ allow(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([])
24
+ expect(@client).to receive(:create_pull_request).with(@git_repo, 'master', @options[:branch], @options[:pr_title], @options[:message]).and_return({"html_url" => "http://example.com/pulls/22"})
25
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Submitted PR/).to_stdout
26
+ end
27
+
28
+ it 'skips submitting PR if one has already been issued' do
29
+ pr = {
30
+ "title" => "Test title",
31
+ "html_url" => "https://example.com/pulls/44",
32
+ "number" => "44"
33
+ }
34
+
35
+ expect(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([pr])
36
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Skipped! 1 PRs found for branch test/).to_stdout
37
+ end
38
+
39
+ it 'adds labels to PR when --pr-labels is set' do
40
+ @options[:pr_labels] = "HELLO,WORLD"
41
+
42
+ allow(@client).to receive(:create_pull_request).and_return({"html_url" => "http://example.com/pulls/22", "number" => "44"})
43
+ allow(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([])
44
+
45
+ expect(@client).to receive(:add_labels_to_an_issue).with(@git_repo, "44", ["HELLO", "WORLD"])
46
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Attaching the following labels to PR 44: HELLO, WORLD/).to_stdout
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'modulesync/pr/gitlab'
3
+
4
+ describe ModuleSync::PR::GitLab do
5
+ context '::manage' do
6
+ before(:each) do
7
+ @git_repo = 'test/modulesync'
8
+ @namespace, @repo_name = @git_repo.split('/')
9
+ @options = {
10
+ :pr => true,
11
+ :pr_title => 'Test PR is submitted',
12
+ :branch => 'test',
13
+ :message => 'Hello world',
14
+ :pr_auto_merge => false,
15
+ }
16
+
17
+ @client = double()
18
+ allow(Gitlab::Client).to receive(:new).and_return(@client)
19
+ @it = ModuleSync::PR::GitLab.new('test', 'https://gitlab.com/api/v4')
20
+ end
21
+
22
+ it 'submits MR when --pr is set' do
23
+ allow(@client).to receive(:merge_requests)
24
+ .with(@git_repo,
25
+ :state => 'opened',
26
+ :source_branch => "#{@namespace}:#{@options[:branch]}",
27
+ :target_branch => 'master',
28
+ ).and_return([])
29
+
30
+ expect(@client).to receive(:create_merge_request)
31
+ .with(@git_repo,
32
+ @options[:pr_title],
33
+ :labels => [],
34
+ :source_branch => @options[:branch],
35
+ :target_branch => 'master',
36
+ ).and_return({"html_url" => "http://example.com/pulls/22"})
37
+
38
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Submitted MR/).to_stdout
39
+ end
40
+
41
+ it 'skips submitting MR if one has already been issued' do
42
+ mr = {
43
+ "title" => "Test title",
44
+ "html_url" => "https://example.com/pulls/44",
45
+ "iid" => "44"
46
+ }
47
+
48
+ expect(@client).to receive(:merge_requests)
49
+ .with(@git_repo,
50
+ :state => 'opened',
51
+ :source_branch => "#{@namespace}:#{@options[:branch]}",
52
+ :target_branch => 'master',
53
+ ).and_return([mr])
54
+
55
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Skipped! 1 MRs found for branch test/).to_stdout
56
+ end
57
+
58
+ it 'adds labels to MR when --pr-labels is set' do
59
+ @options[:pr_labels] = "HELLO,WORLD"
60
+ mr = double()
61
+ allow(mr).to receive(:iid).and_return("42")
62
+
63
+ expect(@client).to receive(:create_merge_request)
64
+ .with(@git_repo,
65
+ @options[:pr_title],
66
+ :labels => ["HELLO", "WORLD"],
67
+ :source_branch => @options[:branch],
68
+ :target_branch => 'master',
69
+ ).and_return(mr)
70
+
71
+ allow(@client).to receive(:merge_requests)
72
+ .with(@git_repo,
73
+ :state => 'opened',
74
+ :source_branch => "#{@namespace}:#{@options[:branch]}",
75
+ :target_branch => 'master',
76
+ ).and_return([])
77
+
78
+ expect { @it.manage(@namespace, @repo_name, @options) }.to output(/Attached the following labels to MR 42: HELLO, WORLD/).to_stdout
79
+ end
80
+ end
81
+ end
@@ -12,58 +12,11 @@ describe ModuleSync do
12
12
  end
13
13
  end
14
14
 
15
- context '::manage_pr' do
16
- before(:each) do
17
- stub_const('GITHUB_TOKEN', 'test')
18
- @git_repo = 'test/modulesync'
19
- @namespace, @repo_name = @git_repo.split('/')
20
- @options = {
21
- :pr => true,
22
- :pr_title => 'Test PR is submitted',
23
- :branch => 'test',
24
- :message => 'Hello world',
25
- :pr_auto_merge => false,
26
- }
27
-
28
- @client = double()
29
- end
30
-
31
- it 'rasies an error when GITHUB_TOKEN not set for PRs' do
32
- stub_const('GITHUB_TOKEN', '')
33
- options = {:pr => true, :skip_broken => false}
34
-
35
- expect { ModuleSync.manage_pr(@namespace, @repo_name, options) }.to raise_error(RuntimeError).and output(/GITHUB_TOKEN/).to_stderr
36
- end
37
-
38
- it 'submits PR when --pr is set' do
39
- allow(Octokit::Client).to receive(:new).and_return(@client)
40
- allow(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([])
41
- expect(@client).to receive(:create_pull_request).with(@git_repo, 'master', @options[:branch], @options[:pr_title], @options[:message]).and_return({"html_url" => "http://example.com/pulls/22"})
42
- expect { ModuleSync.manage_pr(@namespace, @repo_name, @options) }.to output(/Submitted PR/).to_stdout
43
- end
44
-
45
- it 'skips submitting PR if one has already been issued' do
46
- allow(Octokit::Client).to receive(:new).and_return(@client)
47
-
48
- pr = {
49
- "title" => "Test title",
50
- "html_url" => "https://example.com/pulls/44",
51
- "number" => "44"
52
- }
53
-
54
- expect(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([pr])
55
- expect { ModuleSync.manage_pr(@namespace, @repo_name, @options) }.to output(/Skipped! 1 PRs found for branch test/).to_stdout
56
- end
57
-
58
- it 'adds labels to PR when --pr-labels is set' do
59
- @options[:pr_labels] = "HELLO,WORLD"
60
-
61
- allow(Octokit::Client).to receive(:new).and_return(@client)
62
- allow(@client).to receive(:create_pull_request).and_return({"html_url" => "http://example.com/pulls/22", "number" => "44"})
63
- allow(@client).to receive(:pull_requests).with(@git_repo, :state => 'open', :base => 'master', :head => "#{@namespace}:#{@options[:branch]}").and_return([])
64
-
65
- expect(@client).to receive(:add_labels_to_an_issue).with(@git_repo, "44", ["HELLO", "WORLD"])
66
- expect { ModuleSync.manage_pr(@namespace, @repo_name, @options) }.to output(/Attaching the following labels to PR 44: HELLO, WORLD/).to_stdout
15
+ context '::pr' do
16
+ describe "Raise Error" do
17
+ it 'raises an error when neither GITHUB_TOKEN nor GITLAB_TOKEN are set for PRs' do
18
+ expect { ModuleSync.pr({}) }.to raise_error(RuntimeError).and output(/No GitHub or GitLab token specified for --pr/).to_stderr
19
+ end
67
20
  end
68
21
  end
69
22
  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: 1.1.0
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-05-04 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,28 @@ 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
+ - !ruby/object:Gem::Dependency
98
+ name: gitlab
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: octokit
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -112,16 +126,22 @@ dependencies:
112
126
  name: puppet-blacksmith
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - "~>"
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
131
  version: '3.0'
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '7'
118
135
  type: :runtime
119
136
  prerelease: false
120
137
  version_requirements: !ruby/object:Gem::Requirement
121
138
  requirements:
122
- - - "~>"
139
+ - - ">="
123
140
  - !ruby/object:Gem::Version
124
141
  version: '3.0'
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '7'
125
145
  - !ruby/object:Gem::Dependency
126
146
  name: thor
127
147
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +164,7 @@ executables:
144
164
  extensions: []
145
165
  extra_rdoc_files: []
146
166
  files:
167
+ - ".config/cucumber.yml"
147
168
  - ".gitignore"
148
169
  - ".rspec"
149
170
  - ".rubocop.yml"
@@ -151,6 +172,7 @@ files:
151
172
  - ".travis.yml"
152
173
  - CHANGELOG.md
153
174
  - Gemfile
175
+ - HISTORY.md
154
176
  - LICENSE
155
177
  - README.md
156
178
  - Rakefile
@@ -161,17 +183,25 @@ files:
161
183
  - features/step_definitions/git_steps.rb
162
184
  - features/support/env.rb
163
185
  - features/update.feature
186
+ - features/update/bad_context.feature
164
187
  - lib/modulesync.rb
165
188
  - lib/modulesync/cli.rb
189
+ - lib/modulesync/cli/thor.rb
166
190
  - lib/modulesync/constants.rb
167
191
  - lib/modulesync/git.rb
168
192
  - lib/modulesync/hook.rb
193
+ - lib/modulesync/pr/github.rb
194
+ - lib/modulesync/pr/gitlab.rb
169
195
  - lib/modulesync/renderer.rb
170
196
  - lib/modulesync/settings.rb
171
197
  - lib/modulesync/util.rb
172
198
  - lib/monkey_patches.rb
173
199
  - modulesync.gemspec
200
+ - spec/helpers/faker.rb
201
+ - spec/helpers/faker/puppet_module_remote_repo.rb
174
202
  - spec/spec_helper.rb
203
+ - spec/unit/modulesync/pr/github_spec.rb
204
+ - spec/unit/modulesync/pr/gitlab_spec.rb
175
205
  - spec/unit/modulesync/settings_spec.rb
176
206
  - spec/unit/modulesync_spec.rb
177
207
  homepage: http://github.com/voxpupuli/modulesync
@@ -186,7 +216,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
186
216
  requirements:
187
217
  - - ">="
188
218
  - !ruby/object:Gem::Version
189
- version: 2.0.0
219
+ version: 2.5.0
190
220
  required_rubygems_version: !ruby/object:Gem::Requirement
191
221
  requirements:
192
222
  - - ">="
@@ -203,6 +233,11 @@ test_files:
203
233
  - features/step_definitions/git_steps.rb
204
234
  - features/support/env.rb
205
235
  - features/update.feature
236
+ - features/update/bad_context.feature
237
+ - spec/helpers/faker.rb
238
+ - spec/helpers/faker/puppet_module_remote_repo.rb
206
239
  - spec/spec_helper.rb
240
+ - spec/unit/modulesync/pr/github_spec.rb
241
+ - spec/unit/modulesync/pr/gitlab_spec.rb
207
242
  - spec/unit/modulesync/settings_spec.rb
208
243
  - spec/unit/modulesync_spec.rb