modulesync 0.8.2 → 1.2.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 +4 -4
- data/.gitignore +8 -3
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +0 -5
- data/.travis.yml +8 -8
- data/CHANGELOG.md +44 -0
- data/Gemfile +3 -0
- data/README.md +63 -15
- data/Rakefile +1 -0
- data/features/step_definitions/git_steps.rb +23 -0
- data/features/update.feature +152 -28
- data/lib/modulesync.rb +79 -34
- data/lib/modulesync/cli.rb +28 -6
- data/lib/modulesync/git.rb +29 -8
- data/lib/modulesync/pr/github.rb +41 -0
- data/lib/modulesync/pr/gitlab.rb +40 -0
- data/lib/modulesync/renderer.rb +4 -3
- data/lib/modulesync/settings.rb +1 -2
- data/lib/modulesync/util.rb +10 -0
- data/lib/monkey_patches.rb +15 -0
- data/modulesync.gemspec +4 -2
- data/spec/unit/modulesync/pr/github_spec.rb +49 -0
- data/spec/unit/modulesync/pr/gitlab_spec.rb +81 -0
- data/spec/unit/modulesync_spec.rb +22 -0
- metadata +43 -8
data/lib/modulesync.rb
CHANGED
|
@@ -9,7 +9,7 @@ require 'modulesync/settings'
|
|
|
9
9
|
require 'modulesync/util'
|
|
10
10
|
require 'monkey_patches'
|
|
11
11
|
|
|
12
|
-
module ModuleSync
|
|
12
|
+
module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
13
13
|
include Constants
|
|
14
14
|
|
|
15
15
|
def self.config_defaults
|
|
@@ -22,31 +22,39 @@ module ModuleSync
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def self.local_file(config_path, file)
|
|
25
|
-
|
|
25
|
+
File.join(config_path, MODULE_FILES_DIR, file)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def self.module_file(project_root, puppet_module,
|
|
29
|
-
|
|
28
|
+
def self.module_file(project_root, namespace, puppet_module, *parts)
|
|
29
|
+
File.join(project_root, namespace, puppet_module, *parts)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
# List all template files.
|
|
33
|
+
#
|
|
34
|
+
# Only select *.erb files, and strip the extension. This way all the code will only have to handle bare paths,
|
|
35
|
+
# except when reading the actual ERB text
|
|
36
|
+
def self.find_template_files(local_template_dir)
|
|
37
|
+
if File.exist?(local_template_dir)
|
|
38
|
+
Find.find(local_template_dir).find_all { |p| p =~ /.erb$/ && !File.directory?(p) }
|
|
39
|
+
.collect { |p| p.chomp('.erb') }
|
|
40
|
+
.to_a
|
|
36
41
|
else
|
|
37
|
-
puts "#{
|
|
42
|
+
$stdout.puts "#{local_template_dir} does not exist." \
|
|
43
|
+
' Check that you are working in your module configs directory or' \
|
|
44
|
+
' that you have passed in the correct directory with -c.'
|
|
38
45
|
exit
|
|
39
46
|
end
|
|
40
47
|
end
|
|
41
48
|
|
|
42
|
-
def self.
|
|
43
|
-
|
|
49
|
+
def self.relative_names(file_list, path)
|
|
50
|
+
file_list.map { |file| file.sub(/#{path}/, '') }
|
|
44
51
|
end
|
|
45
52
|
|
|
46
|
-
def self.managed_modules(
|
|
47
|
-
managed_modules = Util.parse_config(
|
|
53
|
+
def self.managed_modules(config_file, filter, negative_filter)
|
|
54
|
+
managed_modules = Util.parse_config(config_file)
|
|
48
55
|
if managed_modules.empty?
|
|
49
|
-
puts "No modules found
|
|
56
|
+
$stdout.puts "No modules found in #{config_file}." \
|
|
57
|
+
' Check that you specified the right :configs directory and :managed_modules_conf file.'
|
|
50
58
|
exit
|
|
51
59
|
end
|
|
52
60
|
managed_modules.select! { |m| m =~ Regexp.new(filter) } unless filter.nil?
|
|
@@ -71,72 +79,109 @@ module ModuleSync
|
|
|
71
79
|
end
|
|
72
80
|
|
|
73
81
|
def self.manage_file(filename, settings, options)
|
|
82
|
+
namespace = settings.additional_settings[:namespace]
|
|
74
83
|
module_name = settings.additional_settings[:puppet_module]
|
|
75
84
|
configs = settings.build_file_configs(filename)
|
|
85
|
+
target_file = module_file(options[:project_root], namespace, module_name, filename)
|
|
76
86
|
if configs['delete']
|
|
77
|
-
Renderer.remove(
|
|
87
|
+
Renderer.remove(target_file)
|
|
78
88
|
else
|
|
79
89
|
templatename = local_file(options[:configs], filename)
|
|
80
90
|
begin
|
|
81
91
|
erb = Renderer.build(templatename)
|
|
82
|
-
template
|
|
83
|
-
|
|
92
|
+
# Meta data passed to the template as @metadata[:name]
|
|
93
|
+
metadata = {
|
|
94
|
+
:module_name => module_name,
|
|
95
|
+
:workdir => module_file(options[:project_root], namespace, module_name),
|
|
96
|
+
:target_file => target_file,
|
|
97
|
+
}
|
|
98
|
+
template = Renderer.render(erb, configs, metadata)
|
|
99
|
+
Renderer.sync(template, target_file)
|
|
84
100
|
rescue # rubocop:disable Lint/RescueWithoutErrorClass
|
|
85
|
-
|
|
101
|
+
$stderr.puts "Error while rendering #{filename}"
|
|
86
102
|
raise
|
|
87
103
|
end
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
|
|
91
107
|
def self.manage_module(puppet_module, module_files, module_options, defaults, options)
|
|
92
|
-
puts "Syncing #{puppet_module}"
|
|
93
108
|
namespace, module_name = module_name(puppet_module, options[:namespace])
|
|
109
|
+
git_repo = File.join(namespace, module_name)
|
|
94
110
|
unless options[:offline]
|
|
95
|
-
git_base
|
|
96
|
-
git_uri = "#{git_base}#{namespace}"
|
|
97
|
-
Git.pull(git_uri, module_name, options[:branch], options[:project_root], module_options || {})
|
|
111
|
+
Git.pull(options[:git_base], git_repo, options[:branch], options[:project_root], module_options || {})
|
|
98
112
|
end
|
|
99
|
-
|
|
113
|
+
|
|
114
|
+
module_configs = Util.parse_config(module_file(options[:project_root], namespace, module_name, MODULE_CONF_FILE))
|
|
100
115
|
settings = Settings.new(defaults[GLOBAL_DEFAULTS_KEY] || {},
|
|
101
116
|
defaults,
|
|
102
117
|
module_configs[GLOBAL_DEFAULTS_KEY] || {},
|
|
103
118
|
module_configs,
|
|
104
119
|
:puppet_module => module_name,
|
|
105
|
-
:git_base => git_base,
|
|
120
|
+
:git_base => options[:git_base],
|
|
106
121
|
:namespace => namespace)
|
|
107
122
|
settings.unmanaged_files(module_files).each do |filename|
|
|
108
|
-
puts "Not managing #{filename} in #{module_name}"
|
|
123
|
+
$stdout.puts "Not managing #{filename} in #{module_name}"
|
|
109
124
|
end
|
|
110
125
|
|
|
111
126
|
files_to_manage = settings.managed_files(module_files)
|
|
112
127
|
files_to_manage.each { |filename| manage_file(filename, settings, options) }
|
|
113
128
|
|
|
114
129
|
if options[:noop]
|
|
115
|
-
Git.update_noop(
|
|
130
|
+
Git.update_noop(git_repo, options)
|
|
116
131
|
elsif !options[:offline]
|
|
117
|
-
Git.update(
|
|
132
|
+
pushed = Git.update(git_repo, files_to_manage, options)
|
|
133
|
+
pushed && options[:pr] && @pr.manage(namespace, module_name, options)
|
|
118
134
|
end
|
|
119
135
|
end
|
|
120
136
|
|
|
121
137
|
def self.update(options)
|
|
122
138
|
options = config_defaults.merge(options)
|
|
123
|
-
defaults = Util.parse_config(
|
|
139
|
+
defaults = Util.parse_config(File.join(options[:configs], CONF_FILE))
|
|
140
|
+
if options[:pr]
|
|
141
|
+
unless options[:branch]
|
|
142
|
+
$stderr.puts 'A branch must be specified with --branch to use --pr!'
|
|
143
|
+
raise
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
@pr = create_pr_manager if options[:pr]
|
|
147
|
+
end
|
|
124
148
|
|
|
125
|
-
|
|
126
|
-
local_files =
|
|
127
|
-
module_files =
|
|
149
|
+
local_template_dir = File.join(options[:configs], MODULE_FILES_DIR)
|
|
150
|
+
local_files = find_template_files(local_template_dir)
|
|
151
|
+
module_files = relative_names(local_files, local_template_dir)
|
|
128
152
|
|
|
129
|
-
managed_modules = self.managed_modules(
|
|
153
|
+
managed_modules = self.managed_modules(File.join(options[:configs], options[:managed_modules_conf]),
|
|
154
|
+
options[:filter],
|
|
155
|
+
options[:negative_filter])
|
|
130
156
|
|
|
157
|
+
errors = false
|
|
131
158
|
# managed_modules is either an array or a hash
|
|
132
159
|
managed_modules.each do |puppet_module, module_options|
|
|
133
160
|
begin
|
|
134
161
|
manage_module(puppet_module, module_files, module_options, defaults, options)
|
|
135
162
|
rescue # rubocop:disable Lint/RescueWithoutErrorClass
|
|
136
|
-
|
|
163
|
+
$stderr.puts "Error while updating #{puppet_module}"
|
|
137
164
|
raise unless options[:skip_broken]
|
|
138
|
-
|
|
165
|
+
errors = true
|
|
166
|
+
$stdout.puts "Skipping #{puppet_module} as update process failed"
|
|
139
167
|
end
|
|
140
168
|
end
|
|
169
|
+
exit 1 if errors && options[:fail_on_warnings]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def self.create_pr_manager
|
|
173
|
+
github_token = ENV.fetch('GITHUB_TOKEN', '')
|
|
174
|
+
gitlab_token = ENV.fetch('GITLAB_TOKEN', '')
|
|
175
|
+
|
|
176
|
+
if !github_token.empty?
|
|
177
|
+
require 'modulesync/pr/github'
|
|
178
|
+
ModuleSync::PR::GitHub.new(github_token, ENV.fetch('GITHUB_BASE_URL', 'https://api.github.com'))
|
|
179
|
+
elsif !gitlab_token.empty?
|
|
180
|
+
require 'modulesync/pr/github'
|
|
181
|
+
ModuleSync::PR::GitLab.new(gitlab_token, ENV.fetch('GITLAB_BASE_URL', 'https://gitlab.com/api/v4'))
|
|
182
|
+
else
|
|
183
|
+
$stderr.puts 'Environment variables GITHUB_TOKEN or GITLAB_TOKEN must be set to use --pr!'
|
|
184
|
+
raise
|
|
185
|
+
end
|
|
141
186
|
end
|
|
142
187
|
end
|
data/lib/modulesync/cli.rb
CHANGED
|
@@ -43,7 +43,8 @@ module ModuleSync
|
|
|
43
43
|
:default => CLI.defaults[:git_base] || 'git@github.com:'
|
|
44
44
|
class_option :namespace,
|
|
45
45
|
:aliases => '-n',
|
|
46
|
-
:desc => 'Remote github namespace (user or organization) to clone from and push to.
|
|
46
|
+
:desc => 'Remote github namespace (user or organization) to clone from and push to.' \
|
|
47
|
+
' Defaults to puppetlabs',
|
|
47
48
|
:default => CLI.defaults[:namespace] || 'puppetlabs'
|
|
48
49
|
class_option :filter,
|
|
49
50
|
:aliases => '-f',
|
|
@@ -53,8 +54,9 @@ module ModuleSync
|
|
|
53
54
|
:desc => 'A regular expression to skip repositories.'
|
|
54
55
|
class_option :branch,
|
|
55
56
|
:aliases => '-b',
|
|
56
|
-
:desc => 'Branch name to make the changes in.
|
|
57
|
-
|
|
57
|
+
:desc => 'Branch name to make the changes in.' \
|
|
58
|
+
' Defaults to the default branch of the upstream repository, but falls back to "master".',
|
|
59
|
+
:default => CLI.defaults[:branch]
|
|
58
60
|
|
|
59
61
|
desc 'update', 'Update the modules in managed_modules.yml'
|
|
60
62
|
option :message,
|
|
@@ -63,7 +65,8 @@ module ModuleSync
|
|
|
63
65
|
:default => CLI.defaults[:message]
|
|
64
66
|
option :configs,
|
|
65
67
|
:aliases => '-c',
|
|
66
|
-
:desc => 'The local directory or remote repository to define the list of managed modules,
|
|
68
|
+
:desc => 'The local directory or remote repository to define the list of managed modules,' \
|
|
69
|
+
' the file templates, and the default values for template variables.'
|
|
67
70
|
option :remote_branch,
|
|
68
71
|
:aliases => '-r',
|
|
69
72
|
:desc => 'Remote branch name to push the changes to. Defaults to the branch name.',
|
|
@@ -85,6 +88,17 @@ module ModuleSync
|
|
|
85
88
|
:type => :boolean,
|
|
86
89
|
:desc => 'No-op mode',
|
|
87
90
|
:default => false
|
|
91
|
+
option :pr,
|
|
92
|
+
:type => :boolean,
|
|
93
|
+
:desc => 'Submit pull/merge request',
|
|
94
|
+
:default => false
|
|
95
|
+
option :pr_title,
|
|
96
|
+
:desc => 'Title of pull/merge request',
|
|
97
|
+
:default => CLI.defaults[:pr_title] || 'Update to module template files'
|
|
98
|
+
option :pr_labels,
|
|
99
|
+
:type => :array,
|
|
100
|
+
:desc => 'Labels to add to the pull/merge request',
|
|
101
|
+
:default => CLI.defaults[:pr_labels] || []
|
|
88
102
|
option :offline,
|
|
89
103
|
:type => :boolean,
|
|
90
104
|
:desc => 'Do not run any Git commands. Allows the user to manage Git outside of ModuleSync.',
|
|
@@ -104,13 +118,21 @@ module ModuleSync
|
|
|
104
118
|
option :tag_pattern,
|
|
105
119
|
:desc => 'The pattern to use when tagging releases.'
|
|
106
120
|
option :pre_commit_script,
|
|
107
|
-
:desc => 'A script to be run before
|
|
121
|
+
:desc => 'A script to be run before committing',
|
|
108
122
|
:default => CLI.defaults[:pre_commit_script]
|
|
123
|
+
option :fail_on_warnings,
|
|
124
|
+
:type => :boolean,
|
|
125
|
+
:aliases => '-F',
|
|
126
|
+
:desc => 'Produce a failure exit code when there are warnings' \
|
|
127
|
+
' (only has effect when --skip_broken is enabled)',
|
|
128
|
+
:default => false
|
|
109
129
|
|
|
110
130
|
def update
|
|
111
131
|
config = { :command => 'update' }.merge(options)
|
|
112
132
|
config = Util.symbolize_keys(config)
|
|
113
|
-
raise Thor::Error, 'No value provided for required option "--message"' unless config[:noop]
|
|
133
|
+
raise Thor::Error, 'No value provided for required option "--message"' unless config[:noop] \
|
|
134
|
+
|| config[:message] \
|
|
135
|
+
|| config[:offline]
|
|
114
136
|
config[:git_opts] = { 'amend' => config[:amend], 'force' => config[:force] }
|
|
115
137
|
ModuleSync.update(config)
|
|
116
138
|
end
|
data/lib/modulesync/git.rb
CHANGED
|
@@ -2,7 +2,7 @@ require 'git'
|
|
|
2
2
|
require 'puppet_blacksmith'
|
|
3
3
|
|
|
4
4
|
module ModuleSync
|
|
5
|
-
module Git
|
|
5
|
+
module Git # rubocop:disable Metrics/ModuleLength
|
|
6
6
|
include Constants
|
|
7
7
|
|
|
8
8
|
def self.remote_branch_exists?(repo, branch)
|
|
@@ -18,7 +18,17 @@ module ModuleSync
|
|
|
18
18
|
repo.diff("#{local_branch}..origin/#{remote_branch}").any?
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
def self.default_branch(repo)
|
|
22
|
+
symbolic_ref = repo.branches.find { |b| b.full =~ %r{remotes/origin/HEAD} }
|
|
23
|
+
return unless symbolic_ref
|
|
24
|
+
%r{remotes/origin/HEAD\s+->\s+origin/(?<branch>.+?)$}.match(symbolic_ref.full)[:branch]
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
def self.switch_branch(repo, branch)
|
|
28
|
+
unless branch
|
|
29
|
+
branch = default_branch(repo)
|
|
30
|
+
puts "Using repository's default branch: #{branch}"
|
|
31
|
+
end
|
|
22
32
|
return if repo.current_branch == branch
|
|
23
33
|
|
|
24
34
|
if local_branch_exists?(repo, branch)
|
|
@@ -36,12 +46,13 @@ module ModuleSync
|
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
def self.pull(git_base, name, branch, project_root, opts)
|
|
49
|
+
puts "Syncing #{name}"
|
|
39
50
|
Dir.mkdir(project_root) unless Dir.exist?(project_root)
|
|
40
51
|
|
|
41
52
|
# Repo needs to be cloned in the cwd
|
|
42
53
|
if !Dir.exist?("#{project_root}/#{name}") || !Dir.exist?("#{project_root}/#{name}/.git")
|
|
43
54
|
puts 'Cloning repository fresh'
|
|
44
|
-
remote = opts[:remote] || (git_base.start_with?('file://') ? "#{git_base}
|
|
55
|
+
remote = opts[:remote] || (git_base.start_with?('file://') ? "#{git_base}#{name}" : "#{git_base}#{name}.git")
|
|
45
56
|
local = "#{project_root}/#{name}"
|
|
46
57
|
puts "Cloning from #{remote}"
|
|
47
58
|
repo = ::Git.clone(remote, local)
|
|
@@ -95,12 +106,19 @@ module ModuleSync
|
|
|
95
106
|
repo.push('origin', tag)
|
|
96
107
|
end
|
|
97
108
|
|
|
109
|
+
def self.checkout_branch(repo, branch)
|
|
110
|
+
selected_branch = branch || repo.current_branch || 'master'
|
|
111
|
+
repo.branch(selected_branch).checkout
|
|
112
|
+
selected_branch
|
|
113
|
+
end
|
|
114
|
+
private_class_method :checkout_branch
|
|
115
|
+
|
|
98
116
|
# Git add/rm, git commit, git push
|
|
99
117
|
def self.update(name, files, options)
|
|
100
118
|
module_root = "#{options[:project_root]}/#{name}"
|
|
101
119
|
message = options[:message]
|
|
102
120
|
repo = ::Git.open(module_root)
|
|
103
|
-
|
|
121
|
+
branch = checkout_branch(repo, options[:branch])
|
|
104
122
|
files.each do |file|
|
|
105
123
|
if repo.status.deleted.include?(file)
|
|
106
124
|
repo.remove(file)
|
|
@@ -119,11 +137,11 @@ module ModuleSync
|
|
|
119
137
|
end
|
|
120
138
|
repo.commit(message, opts_commit)
|
|
121
139
|
if options[:remote_branch]
|
|
122
|
-
if remote_branch_differ?(repo,
|
|
123
|
-
repo.push('origin', "#{
|
|
140
|
+
if remote_branch_differ?(repo, branch, options[:remote_branch])
|
|
141
|
+
repo.push('origin', "#{branch}:#{options[:remote_branch]}", opts_push)
|
|
124
142
|
end
|
|
125
143
|
else
|
|
126
|
-
repo.push('origin',
|
|
144
|
+
repo.push('origin', branch, opts_push)
|
|
127
145
|
end
|
|
128
146
|
# Only bump/tag if pushing didn't fail (i.e. there were changes)
|
|
129
147
|
m = Blacksmith::Modulefile.new("#{module_root}/metadata.json")
|
|
@@ -134,11 +152,14 @@ module ModuleSync
|
|
|
134
152
|
rescue ::Git::GitExecuteError => git_error
|
|
135
153
|
if git_error.message =~ /working (directory|tree) clean/
|
|
136
154
|
puts "There were no files to update in #{name}. Not committing."
|
|
155
|
+
return false
|
|
137
156
|
else
|
|
138
157
|
puts git_error
|
|
139
|
-
|
|
158
|
+
raise
|
|
140
159
|
end
|
|
141
160
|
end
|
|
161
|
+
|
|
162
|
+
true
|
|
142
163
|
end
|
|
143
164
|
|
|
144
165
|
# Needed because of a bug in the git gem that lists ignored files as
|
|
@@ -154,7 +175,7 @@ module ModuleSync
|
|
|
154
175
|
puts "Using no-op. Files in #{name} may be changed but will not be committed."
|
|
155
176
|
|
|
156
177
|
repo = ::Git.open("#{options[:project_root]}/#{name}")
|
|
157
|
-
repo
|
|
178
|
+
checkout_branch(repo, options[:branch])
|
|
158
179
|
|
|
159
180
|
puts 'Files changed:'
|
|
160
181
|
repo.diff('HEAD', '--').each do |diff|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'octokit'
|
|
2
|
+
require 'modulesync/util'
|
|
3
|
+
|
|
4
|
+
module ModuleSync
|
|
5
|
+
module PR
|
|
6
|
+
# GitHub creates and manages pull requests on github.com or GitHub
|
|
7
|
+
# Enterprise installations.
|
|
8
|
+
class GitHub
|
|
9
|
+
def initialize(token, endpoint)
|
|
10
|
+
Octokit.configure do |c|
|
|
11
|
+
c.api_endpoint = endpoint
|
|
12
|
+
end
|
|
13
|
+
@api = Octokit::Client.new(:access_token => token)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def manage(namespace, module_name, options)
|
|
17
|
+
repo_path = File.join(namespace, module_name)
|
|
18
|
+
head = "#{namespace}:#{options[:branch]}"
|
|
19
|
+
|
|
20
|
+
pull_requests = @api.pull_requests(repo_path, :state => 'open', :base => 'master', :head => head)
|
|
21
|
+
if pull_requests.empty?
|
|
22
|
+
pr = @api.create_pull_request(repo_path, 'master', options[:branch], options[:pr_title], options[:message])
|
|
23
|
+
$stdout.puts "Submitted PR '#{options[:pr_title]}' to #{repo_path} - merges #{options[:branch]} into master"
|
|
24
|
+
else
|
|
25
|
+
# Skip creating the PR if it exists already.
|
|
26
|
+
$stdout.puts "Skipped! #{pull_requests.length} PRs found for branch #{options[:branch]}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# PR labels can either be a list in the YAML file or they can pass in a comma
|
|
30
|
+
# separated list via the command line argument.
|
|
31
|
+
pr_labels = ModuleSync::Util.parse_list(options[:pr_labels])
|
|
32
|
+
|
|
33
|
+
# We only assign labels to the PR if we've discovered a list > 1. The labels MUST
|
|
34
|
+
# already exist. We DO NOT create missing labels.
|
|
35
|
+
return if pr_labels.empty?
|
|
36
|
+
$stdout.puts "Attaching the following labels to PR #{pr['number']}: #{pr_labels.join(', ')}"
|
|
37
|
+
@api.add_labels_to_an_issue(repo_path, pr['number'], pr_labels)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'gitlab'
|
|
2
|
+
require 'modulesync/util'
|
|
3
|
+
|
|
4
|
+
module ModuleSync
|
|
5
|
+
module PR
|
|
6
|
+
# GitLab creates and manages merge requests on gitlab.com or private GitLab
|
|
7
|
+
# installations.
|
|
8
|
+
class GitLab
|
|
9
|
+
def initialize(token, endpoint)
|
|
10
|
+
@api = Gitlab::Client.new(
|
|
11
|
+
:endpoint => endpoint,
|
|
12
|
+
:private_token => token
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def manage(namespace, module_name, options)
|
|
17
|
+
repo_path = File.join(namespace, module_name)
|
|
18
|
+
|
|
19
|
+
head = "#{namespace}:#{options[:branch]}"
|
|
20
|
+
merge_requests = @api.merge_requests(repo_path,
|
|
21
|
+
:state => 'opened',
|
|
22
|
+
:source_branch => head,
|
|
23
|
+
:target_branch => 'master')
|
|
24
|
+
if merge_requests.empty?
|
|
25
|
+
mr_labels = ModuleSync::Util.parse_list(options[:pr_labels])
|
|
26
|
+
mr = @api.create_merge_request(repo_path, options[:pr_title],
|
|
27
|
+
:source_branch => options[:branch],
|
|
28
|
+
:target_branch => 'master',
|
|
29
|
+
:labels => mr_labels)
|
|
30
|
+
$stdout.puts "Submitted MR '#{options[:pr_title]}' to #{repo_path} - merges #{options[:branch]} into master"
|
|
31
|
+
return if mr_labels.empty?
|
|
32
|
+
$stdout.puts "Attached the following labels to MR #{mr.iid}: #{mr_labels.join(', ')}"
|
|
33
|
+
else
|
|
34
|
+
# Skip creating the MR if it exists already.
|
|
35
|
+
$stdout.puts "Skipped! #{merge_requests.length} MRs found for branch #{options[:branch]}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/modulesync/renderer.rb
CHANGED
|
@@ -4,8 +4,9 @@ require 'find'
|
|
|
4
4
|
module ModuleSync
|
|
5
5
|
module Renderer
|
|
6
6
|
class ForgeModuleFile
|
|
7
|
-
def initialize(configs = {})
|
|
7
|
+
def initialize(configs = {}, metadata = {})
|
|
8
8
|
@configs = configs
|
|
9
|
+
@metadata = metadata
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -26,8 +27,8 @@ module ModuleSync
|
|
|
26
27
|
File.delete(file) if File.exist?(file)
|
|
27
28
|
end
|
|
28
29
|
|
|
29
|
-
def self.render(_template, configs = {})
|
|
30
|
-
ForgeModuleFile.new(configs).render
|
|
30
|
+
def self.render(_template, configs = {}, metadata = {})
|
|
31
|
+
ForgeModuleFile.new(configs, metadata).render
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def self.sync(template, target_name)
|