ra10ke 1.2.0 → 3.0.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/.github/dependabot.yml +14 -8
- data/.github/workflows/release.yml +1 -1
- data/.github/workflows/test.yml +31 -9
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +262 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +4 -4
- data/README.md +9 -0
- data/Rakefile +12 -6
- data/lib/ra10ke/dependencies.rb +168 -62
- data/lib/ra10ke/duplicates.rb +1 -1
- data/lib/ra10ke/git_repo.rb +25 -14
- data/lib/ra10ke/install.rb +10 -10
- data/lib/ra10ke/monkey_patches.rb +1 -1
- data/lib/ra10ke/puppetfile_parser.rb +73 -73
- data/lib/ra10ke/solve.rb +27 -26
- data/lib/ra10ke/syntax.rb +6 -8
- data/lib/ra10ke/validate.rb +1 -2
- data/lib/ra10ke/version.rb +1 -1
- data/lib/ra10ke.rb +3 -1
- data/ra10ke.gemspec +19 -19
- data/spec/fixtures/Puppetfile_deprecation_issue +78 -0
- data/spec/fixtures/Puppetfile_git_conversion +28 -0
- data/spec/ra10ke/dependencies_spec.rb +54 -19
- data/spec/ra10ke/deprecation_spec.rb +18 -4
- data/spec/ra10ke/git_repo_spec.rb +12 -8
- data/spec/ra10ke/puppetfile_parser_spec.rb +89 -100
- data/spec/ra10ke/validate_spec.rb +8 -6
- data/spec/ra10ke_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -1
- metadata +79 -32
- data/.ruby-version +0 -1
data/lib/ra10ke/dependencies.rb
CHANGED
@@ -1,37 +1,146 @@
|
|
1
|
+
require 'semverse'
|
2
|
+
require 'r10k/puppetfile'
|
3
|
+
require 'puppet_forge'
|
4
|
+
require 'table_print'
|
5
|
+
require 'git'
|
6
|
+
|
1
7
|
module Ra10ke::Dependencies
|
2
|
-
|
8
|
+
GOOD_EMOJI = ENV['GOOD_EMOJI'] || '👍'
|
9
|
+
BAD_EMOJI = ENV['BAD_EMOJI'] || '😨'
|
3
10
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@@version_formats[name] = block
|
9
|
-
end
|
11
|
+
class Verification
|
12
|
+
def self.version_formats
|
13
|
+
@version_formats ||= {}
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
# Registers a block that finds the latest version.
|
17
|
+
# The block will be called with a list of tags.
|
18
|
+
# If the block returns nil the next format will be tried.
|
19
|
+
def self.register_version_format(name, &block)
|
20
|
+
version_formats[name] = block
|
21
|
+
end
|
22
|
+
|
23
|
+
Ra10ke::Dependencies::Verification.register_version_format(:semver) do |tags|
|
24
|
+
latest_tag = tags.map do |tag|
|
15
25
|
Semverse::Version.new tag[/\Av?(.*)\Z/, 1]
|
16
26
|
rescue Semverse::InvalidVersionFormat
|
17
27
|
# ignore tags that do not comply to semver
|
18
28
|
nil
|
29
|
+
end.select { |tag| !tag.nil? }.sort.last.to_s.downcase
|
30
|
+
latest_ref = tags.detect { |tag| tag[/\Av?(.*)\Z/, 1] == latest_tag }
|
31
|
+
end
|
32
|
+
attr_reader :puppetfile
|
33
|
+
|
34
|
+
def initialize(pfile)
|
35
|
+
@puppetfile = pfile
|
36
|
+
# semver is the default version format.
|
37
|
+
|
38
|
+
puppetfile.load!
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_latest_ref(remote_refs)
|
42
|
+
tags = remote_refs['tags'].keys
|
43
|
+
latest_ref = nil
|
44
|
+
self.class.version_formats.detect { |_, block| latest_ref = block.call(tags) }
|
45
|
+
latest_ref = 'undef (tags do not follow any known pattern)' if latest_ref.nil?
|
46
|
+
latest_ref
|
47
|
+
end
|
48
|
+
|
49
|
+
def ignored_modules
|
50
|
+
# ignore file allows for "don't tell me about this"
|
51
|
+
@ignored_modules ||= begin
|
52
|
+
File.readlines('.r10kignore').each { |l| l.chomp! } if File.exist?('.r10kignore')
|
53
|
+
end || []
|
54
|
+
end
|
55
|
+
|
56
|
+
# @summary creates an array of module hashes with version info
|
57
|
+
# @param {Object} supplied_puppetfile - the parsed puppetfile object
|
58
|
+
# @returns {Array} array of version info for each module
|
59
|
+
# @note does not include ignored modules or modules up2date
|
60
|
+
def processed_modules(supplied_puppetfile = puppetfile)
|
61
|
+
threads = []
|
62
|
+
threads = supplied_puppetfile.modules.map do |puppet_module|
|
63
|
+
Thread.new do
|
64
|
+
next if ignored_modules.include? puppet_module.title
|
65
|
+
|
66
|
+
if puppet_module.instance_of?(::R10K::Module::Forge)
|
67
|
+
module_name = puppet_module.title.tr('/', '-')
|
68
|
+
forge_version = ::PuppetForge::Module.find(module_name).current_release.version
|
69
|
+
installed_version = puppet_module.expected_version
|
70
|
+
{
|
71
|
+
name: puppet_module.title,
|
72
|
+
installed: installed_version,
|
73
|
+
latest: forge_version,
|
74
|
+
type: 'forge',
|
75
|
+
message: (installed_version == forge_version) ? :current : :outdated,
|
76
|
+
}
|
77
|
+
|
78
|
+
elsif puppet_module.instance_of?(R10K::Module::Git)
|
79
|
+
# use helper; avoid `desired_ref`
|
80
|
+
# we do not want to deal with `:control_branch`
|
81
|
+
ref = puppet_module.version
|
82
|
+
next unless ref
|
83
|
+
|
84
|
+
remote = puppet_module.instance_variable_get(:@remote)
|
85
|
+
remote_refs = Git.ls_remote(remote)
|
86
|
+
|
87
|
+
# skip if ref is a branch
|
88
|
+
next if remote_refs['branches'].key?(ref)
|
89
|
+
|
90
|
+
if remote_refs['tags'].key?(ref)
|
91
|
+
# there are too many possible versioning conventions
|
92
|
+
# we have to be be opinionated here
|
93
|
+
# so semantic versioning (vX.Y.Z) it is for us
|
94
|
+
# as well as support for skipping the leading v letter
|
95
|
+
#
|
96
|
+
# register own version formats with
|
97
|
+
# Ra10ke::Dependencies.register_version_format(:name, &block)
|
98
|
+
latest_ref = get_latest_ref(remote_refs)
|
99
|
+
elsif /^[a-z0-9]{40}$/.match?(ref)
|
100
|
+
ref = ref.slice(0, 8)
|
101
|
+
# for sha just assume head should be tracked
|
102
|
+
latest_ref = remote_refs['head'][:sha].slice(0, 8)
|
103
|
+
else
|
104
|
+
raise "Unable to determine ref type for #{puppet_module.title}"
|
105
|
+
end
|
106
|
+
{
|
107
|
+
name: puppet_module.title,
|
108
|
+
installed: ref,
|
109
|
+
latest: latest_ref,
|
110
|
+
type: 'git',
|
111
|
+
message: (ref == latest_ref) ? :current : :outdated,
|
112
|
+
}
|
113
|
+
|
114
|
+
end
|
115
|
+
rescue R10K::Util::Subprocess::SubprocessError => e
|
116
|
+
{
|
117
|
+
name: puppet_module.title,
|
118
|
+
installed: nil,
|
119
|
+
latest: nil,
|
120
|
+
type: :error,
|
121
|
+
message: e.message,
|
122
|
+
}
|
123
|
+
end
|
19
124
|
end
|
20
|
-
|
21
|
-
|
22
|
-
end
|
125
|
+
threads.map { |th| th.join.value }.compact
|
126
|
+
end
|
23
127
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
128
|
+
def outdated(_supplied_puppetfile = puppetfile)
|
129
|
+
processed_modules.find_all do |mod|
|
130
|
+
mod[:message] == :outdated
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def print_table(mods)
|
135
|
+
puts
|
136
|
+
tp mods, { name: { width: 50 } }, :installed, :latest, :type, :message
|
137
|
+
end
|
30
138
|
end
|
31
139
|
|
32
|
-
def
|
33
|
-
desc
|
34
|
-
task :
|
140
|
+
def define_task_print_git_conversion(*_args)
|
141
|
+
desc 'Convert and print forge modules to git format'
|
142
|
+
task :print_git_conversion do
|
143
|
+
require 'ra10ke/git_repo'
|
35
144
|
require 'r10k/puppetfile'
|
36
145
|
require 'puppet_forge'
|
37
146
|
|
@@ -42,51 +151,48 @@ module Ra10ke::Dependencies
|
|
42
151
|
|
43
152
|
# ignore file allows for "don't tell me about this"
|
44
153
|
ignore_modules = []
|
45
|
-
if File.exist?('.r10kignore')
|
46
|
-
|
154
|
+
ignore_modules = File.readlines('.r10kignore').each { |l| l.chomp! } if File.exist?('.r10kignore')
|
155
|
+
forge_mods = puppetfile.modules.find_all do |mod|
|
156
|
+
mod.instance_of?(R10K::Module::Forge) && mod.v3_module.homepage_url?
|
47
157
|
end
|
48
158
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
puts "#{puppet_module.title} is OUTDATED: #{installed_version} vs #{forge_version}"
|
159
|
+
threads = forge_mods.map do |mod|
|
160
|
+
Thread.new do
|
161
|
+
source_url = mod.v3_module.attributes.dig(:current_release, :metadata, :source) || mod.v3_module.homepage_url
|
162
|
+
# git:// does not work with ls-remote command, convert to https
|
163
|
+
source_url = source_url.gsub('git://', 'https://')
|
164
|
+
source_url = source_url.gsub(/\Agit@(.*):(.*)/) do
|
165
|
+
"https://#{::Regexp.last_match(1)}/#{::Regexp.last_match(2)}"
|
57
166
|
end
|
167
|
+
repo = ::Ra10ke::GitRepo.new(source_url)
|
168
|
+
ref = repo.get_ref_like(mod.expected_version)
|
169
|
+
ref_name = ref ? ref[:name] : "bad url or tag #{mod.expected_version} is missing"
|
170
|
+
<<~EOF
|
171
|
+
mod '#{mod.name}',
|
172
|
+
:git => '#{source_url}',
|
173
|
+
:ref => '#{ref_name}'
|
174
|
+
|
175
|
+
EOF
|
58
176
|
end
|
177
|
+
end
|
178
|
+
output = threads.map { |th| th.join.value }
|
179
|
+
puts output
|
180
|
+
end
|
181
|
+
end
|
59
182
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# skip if ref is a branch
|
70
|
-
next if remote_refs['branches'].key?(ref)
|
71
|
-
|
72
|
-
if remote_refs['tags'].key?(ref)
|
73
|
-
# there are too many possible versioning conventions
|
74
|
-
# we have to be be opinionated here
|
75
|
-
# so semantic versioning (vX.Y.Z) it is for us
|
76
|
-
# as well as support for skipping the leading v letter
|
77
|
-
#
|
78
|
-
# register own version formats with
|
79
|
-
# Ra10ke::Dependencies.register_version_format(:name, &block)
|
80
|
-
latest_ref = get_latest_ref(remote_refs)
|
81
|
-
elsif ref.match(/^[a-z0-9]{40}$/)
|
82
|
-
# for sha just assume head should be tracked
|
83
|
-
latest_ref = remote_refs['head'][:sha]
|
84
|
-
else
|
85
|
-
raise "Unable to determine ref type for #{puppet_module.title}"
|
86
|
-
end
|
183
|
+
def define_task_dependencies(*_args)
|
184
|
+
desc 'Print outdated forge modules'
|
185
|
+
task :dependencies do
|
186
|
+
PuppetForge.user_agent = "ra10ke/#{Ra10ke::VERSION}"
|
187
|
+
puppetfile = get_puppetfile
|
188
|
+
PuppetForge.host = puppetfile.forge if /^http/.match?(puppetfile.forge)
|
189
|
+
dependencies = Ra10ke::Dependencies::Verification.new(puppetfile)
|
190
|
+
dependencies.print_table(dependencies.outdated)
|
87
191
|
|
88
|
-
|
89
|
-
|
192
|
+
if dependencies.outdated.any?
|
193
|
+
abort(BAD_EMOJI + ' Not all modules in the Puppetfile are up2date. '.red + BAD_EMOJI)
|
194
|
+
else
|
195
|
+
puts(GOOD_EMOJI + ' All modules in the Puppetfile are up2date. '.green + GOOD_EMOJI)
|
90
196
|
end
|
91
197
|
end
|
92
198
|
end
|
data/lib/ra10ke/duplicates.rb
CHANGED
@@ -5,7 +5,7 @@ require 'ra10ke/puppetfile_parser'
|
|
5
5
|
|
6
6
|
module Ra10ke::Duplicates
|
7
7
|
def define_task_duplicates(*_args)
|
8
|
-
desc
|
8
|
+
desc 'Check Puppetfile for duplicates'
|
9
9
|
task :duplicates do
|
10
10
|
duplicates = Ra10ke::Duplicates::Verification.new(get_puppetfile.puppetfile_path).duplicates
|
11
11
|
exit_code = 0
|
data/lib/ra10ke/git_repo.rb
CHANGED
@@ -82,20 +82,31 @@ module Ra10ke
|
|
82
82
|
# :sha=>"fcc0532bbc5a5b65f3941738339e9cc7e3d767ce", :ref=>"refs/pull/249/head", :type=>:pull, :subtype=>:head, :name=>"249"},
|
83
83
|
# :sha=>"8d54891fa5df75890ee15d53080c2a81b4960f92", :ref=>"refs/pull/267/head", :type=>:pull, :subtype=>:head, :name=>"267"}]
|
84
84
|
def all_refs
|
85
|
-
@all_refs ||=
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
85
|
+
@all_refs ||= remote_refs.each_with_object([]) do |line, refs|
|
86
|
+
sha, ref = line.split("\t")
|
87
|
+
next refs if line.include?('redirecting')
|
88
|
+
next refs if sha.eql?('ref: refs/heads/master')
|
89
|
+
|
90
|
+
_, type, name, subtype = ref.chomp.split('/')
|
91
|
+
next refs unless name
|
92
|
+
|
93
|
+
type = :tag if type.eql?('tags')
|
94
|
+
type = type.to_sym
|
95
|
+
subtype = subtype.to_sym if subtype
|
96
|
+
type = :branch if type.eql?(:heads)
|
97
|
+
refs << { sha: sha, ref: ref.chomp, type: type, subtype: subtype, name: name }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# @summary searches all the refs for a ref similar to the name provided
|
102
|
+
# This is useful when looking for tags if a v or not a v
|
103
|
+
# @param ref_name [String]
|
104
|
+
# @return [String] the matching ref_name or nil
|
105
|
+
def get_ref_like(ref_name)
|
106
|
+
return nil unless valid_url?
|
107
|
+
|
108
|
+
all_refs.find do |ref|
|
109
|
+
ref[:name].include?(ref_name)
|
99
110
|
end
|
100
111
|
end
|
101
112
|
|
data/lib/ra10ke/install.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
module Ra10ke::Install
|
2
2
|
def define_task_install(*_args)
|
3
|
-
desc
|
3
|
+
desc 'Install modules specified in Puppetfile'
|
4
4
|
task :install do
|
5
5
|
require 'r10k/puppetfile'
|
6
6
|
|
7
7
|
puppetfile = get_puppetfile
|
8
8
|
puppetfile.load!
|
9
9
|
|
10
|
-
puts
|
10
|
+
puts 'Processing Puppetfile for fixtures'
|
11
11
|
puppetfile.modules.each do |mod|
|
12
12
|
if mod.status == :insync
|
13
13
|
puts "Skipping #{mod.name} (#{mod.version}) already in sync"
|
14
14
|
else
|
15
|
-
if mod.status == :absent
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
msg = if mod.status == :absent
|
16
|
+
"installed #{mod.name}"
|
17
|
+
else
|
18
|
+
"updated #{mod.name} from #{mod.version} to"
|
19
|
+
end
|
20
20
|
mod.sync
|
21
|
-
if mod.status
|
22
|
-
puts "Failed to sync #{mod.name}".red
|
23
|
-
else
|
21
|
+
if mod.status == :insync
|
24
22
|
puts "Successfully #{msg} #{mod.version}".green
|
23
|
+
else
|
24
|
+
puts "Failed to sync #{mod.name}".red
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -1,90 +1,90 @@
|
|
1
|
-
# it might be desirable to parse the Puppetfile as a string instead of evaling it.
|
1
|
+
# it might be desirable to parse the Puppetfile as a string instead of evaling it.
|
2
2
|
# this module allows you to do just that.
|
3
3
|
require 'ra10ke/monkey_patches'
|
4
4
|
|
5
5
|
module Ra10ke
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
mod[:args].key?(:git)
|
12
|
-
end
|
6
|
+
module PuppetfileParser
|
7
|
+
# @return [Array] - returns a array of hashes that contain modules with a git source
|
8
|
+
def git_modules(file = puppetfile)
|
9
|
+
modules(file).find_all do |mod|
|
10
|
+
mod[:args].key?(:git)
|
13
11
|
end
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
14
|
+
# @return [Array] - returns a array of hashes that contain modules from the Forge
|
15
|
+
def forge_modules(file = puppetfile)
|
16
|
+
modules(file).reject do |mod|
|
17
|
+
mod[:args].key?(:git)
|
20
18
|
end
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
# @param puppetfile [String] - the absolute path to the puppetfile
|
22
|
+
# @return [Array] - returns an array of module hashes that represent the puppetfile
|
23
|
+
# @example
|
24
|
+
# [{:namespace=>"puppetlabs", :name=>"stdlib", :args=>[]},
|
25
|
+
# {:namespace=>"petems", :name=>"swap_file", :args=>["'4.0.0'"]}]
|
26
|
+
def modules(puppetfile)
|
27
|
+
@modules ||= begin
|
28
|
+
return [] unless File.exist?(puppetfile)
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
30
|
+
all_lines = File.read(puppetfile).lines.map(&:strip_comment)
|
31
|
+
# remove comments from all the lines
|
32
|
+
lines_without_comments = all_lines.reject { |line| line.match(/#.*\n/) || line.empty? }.join("\n")
|
33
|
+
lines_without_comments.split(/^mod/).map do |line|
|
34
|
+
next nil if /^forge/.match?(line)
|
35
|
+
next nil if line.empty?
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
37
|
+
parse_module_args(line)
|
38
|
+
end.compact.uniq
|
41
39
|
end
|
40
|
+
end
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
42
|
+
# @param data [String] - the string to parse the puppetfile args out of
|
43
|
+
# @return [Array] - an array of arguments in hash form
|
44
|
+
# @example
|
45
|
+
# {:namespace=>"puppetlabs", :name=>"stdlib", :args=>[]}
|
46
|
+
# {:namespace=>"petems", :name=>"swap_file", :args=>["'4.0.0'"]}
|
47
|
+
def parse_module_args(data)
|
48
|
+
return {} if data.empty?
|
49
|
+
|
50
|
+
args = data.split(',').map(&:strip)
|
51
|
+
# we can't guarantee that there will be a namespace when git is used
|
52
|
+
# remove quotes and dash and slash
|
53
|
+
namespace, name = args.shift.gsub(/'|"/, '').split(%r{-|/})
|
54
|
+
name ||= namespace
|
55
|
+
namespace = nil if namespace == name
|
56
|
+
{
|
57
|
+
namespace: namespace,
|
58
|
+
name: name,
|
59
|
+
args: process_args(args),
|
60
|
+
}
|
61
|
+
end
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
63
|
+
# @return [Array] - returns an array of hashes with the args in key value pairs
|
64
|
+
# @param [Array] - the arguments processed from each entry in the puppetfile
|
65
|
+
# @example
|
66
|
+
# [{:args=>[], :name=>"razor", :namespace=>"puppetlabs"},
|
67
|
+
# {:args=>[{:version=>"0.0.3"}], :name=>"ntp", :namespace=>"puppetlabs"},
|
68
|
+
# {:args=>[], :name=>"inifile", :namespace=>"puppetlabs"},
|
69
|
+
# {:args=>
|
70
|
+
# [{:git=>"https://github.com/nwops/reportslack.git"}, {:ref=>"1.0.20"}],
|
71
|
+
# :name=>"reportslack",
|
72
|
+
# :namespace=>"nwops"},
|
73
|
+
# {:args=>{:git=>"git://github.com/puppetlabs/puppetlabs-apt.git"},
|
74
|
+
# :name=>"apt",
|
75
|
+
# :namespace=>nil}
|
76
|
+
# ]
|
77
|
+
def process_args(args)
|
78
|
+
results = {}
|
79
|
+
args.each do |arg|
|
80
|
+
a = arg.gsub(/'|"/, '').split(/\A:|:\s|=>/).map(&:strip).reject(&:empty?)
|
81
|
+
if a.count < 2
|
82
|
+
results[:version] = a.first
|
83
|
+
else
|
84
|
+
results[a.first.to_sym] = a.last
|
86
85
|
end
|
87
|
-
results
|
88
86
|
end
|
87
|
+
results
|
89
88
|
end
|
89
|
+
end
|
90
90
|
end
|
data/lib/ra10ke/solve.rb
CHANGED
@@ -6,6 +6,7 @@ require 'set'
|
|
6
6
|
require 'solve'
|
7
7
|
require 'yaml/store'
|
8
8
|
require 'semverse/version'
|
9
|
+
require 'fileutils'
|
9
10
|
|
10
11
|
# How many versions to fetch from the Forge, at most
|
11
12
|
FETCH_LIMIT = 3
|
@@ -26,15 +27,16 @@ module Ra10ke::Solve
|
|
26
27
|
PuppetForge.user_agent = "ra10ke/#{Ra10ke::VERSION}"
|
27
28
|
puppetfile = get_puppetfile
|
28
29
|
puppetfile.load!
|
29
|
-
PuppetForge.host = puppetfile.forge if puppetfile.forge
|
30
|
+
PuppetForge.host = puppetfile.forge if /^http/.match?(puppetfile.forge)
|
30
31
|
|
31
32
|
# ignore file allows for "don't tell me about this"
|
32
33
|
ignore_modules = []
|
33
|
-
if File.exist?('.r10kignore')
|
34
|
-
ignore_modules = File.readlines('.r10kignore').each(&:chomp!)
|
35
|
-
end
|
34
|
+
ignore_modules = File.readlines('.r10kignore').each(&:chomp!) if File.exist?('.r10kignore')
|
36
35
|
# Actual new logic begins here:
|
37
36
|
cache = (ENV['XDG_CACHE_DIR'] || File.expand_path('~/.cache'))
|
37
|
+
|
38
|
+
FileUtils.mkdir_p(cache)
|
39
|
+
|
38
40
|
# Metadata cache, since the Forge is slow:
|
39
41
|
@metadata_cache = YAML::Store.new File.join(cache, 'ra10ke.metadata_cache')
|
40
42
|
# The graph of available module versions
|
@@ -50,7 +52,8 @@ module Ra10ke::Solve
|
|
50
52
|
|
51
53
|
puppetfile.modules.each do |puppet_module|
|
52
54
|
next if ignore_modules.include? puppet_module.title
|
53
|
-
|
55
|
+
|
56
|
+
if puppet_module.instance_of?(R10K::Module::Forge)
|
54
57
|
module_name = puppet_module.title.tr('/', '-')
|
55
58
|
installed_version = puppet_module.expected_version
|
56
59
|
puts "Processing Forge module #{module_name}-#{installed_version}"
|
@@ -77,7 +80,8 @@ module Ra10ke::Solve
|
|
77
80
|
add_reqs_to_graph(mod, meta)
|
78
81
|
end
|
79
82
|
|
80
|
-
next unless puppet_module.
|
83
|
+
next unless puppet_module.instance_of?(R10K::Module::Git)
|
84
|
+
|
81
85
|
# This downloads the git module to modules/modulename
|
82
86
|
meta = fetch_git_metadata(puppet_module)
|
83
87
|
version = get_key_or_sym(meta, :version)
|
@@ -93,9 +97,7 @@ module Ra10ke::Solve
|
|
93
97
|
end
|
94
98
|
puts
|
95
99
|
puts 'Resolving dependencies...'
|
96
|
-
if allow_major_bump
|
97
|
-
puts 'WARNING: Potentially breaking updates are allowed for this resolution'
|
98
|
-
end
|
100
|
+
puts 'WARNING: Potentially breaking updates are allowed for this resolution' if allow_major_bump
|
99
101
|
result = Solve.it!(@graph, @demands, sorted: true)
|
100
102
|
puts
|
101
103
|
print_module_diff(@current_modules, result)
|
@@ -126,7 +128,7 @@ module Ra10ke::Solve
|
|
126
128
|
return {
|
127
129
|
version: '0.0.0',
|
128
130
|
name: puppet_module.title,
|
129
|
-
dependencies: []
|
131
|
+
dependencies: [],
|
130
132
|
}
|
131
133
|
end
|
132
134
|
metadata = R10K::Module::MetadataFile.new(metadata_path)
|
@@ -134,7 +136,7 @@ module Ra10ke::Solve
|
|
134
136
|
{
|
135
137
|
version: metadata.version,
|
136
138
|
name: metadata.name,
|
137
|
-
dependencies: metadata.dependencies
|
139
|
+
dependencies: metadata.dependencies,
|
138
140
|
}
|
139
141
|
end
|
140
142
|
|
@@ -146,7 +148,7 @@ module Ra10ke::Solve
|
|
146
148
|
# At least puppet-extlib has malformed metadata
|
147
149
|
def get_version_req(dep)
|
148
150
|
req = get_key_or_sym(dep, :version_requirement)
|
149
|
-
req
|
151
|
+
req ||= get_key_or_sym(dep, :version_range)
|
150
152
|
req
|
151
153
|
end
|
152
154
|
|
@@ -182,17 +184,14 @@ module Ra10ke::Solve
|
|
182
184
|
# actually ask the solver for the versioned thing
|
183
185
|
@demands.add(name) unless no_demands
|
184
186
|
ver = get_version_req(dep)
|
185
|
-
|
186
|
-
|
187
|
-
ver = '>=0.0.0'
|
188
|
-
end
|
189
|
-
ver.split(/(?=[<])/).each do |bound|
|
187
|
+
ver ||= '>=0.0.0'
|
188
|
+
ver.split(/(?=<)/).each do |bound|
|
190
189
|
bound.strip!
|
191
190
|
v = begin
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
191
|
+
Semverse::Constraint.new(bound)
|
192
|
+
rescue StandardError
|
193
|
+
nil
|
194
|
+
end
|
196
195
|
if v
|
197
196
|
artifact.depends(name, v.to_s)
|
198
197
|
else
|
@@ -202,14 +201,16 @@ module Ra10ke::Solve
|
|
202
201
|
# Find the dependency in the forge, unless it's already been processed
|
203
202
|
# and add its releases to the global graph
|
204
203
|
next unless @processed_modules.add?(name)
|
204
|
+
|
205
205
|
puts "Fetching module info for #{name}"
|
206
206
|
mod = begin
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
207
|
+
PuppetForge::Module.find(name)
|
208
|
+
rescue StandardError
|
209
|
+
# It's probably a git module
|
210
|
+
nil
|
211
|
+
end
|
212
212
|
next unless mod # Git module, or non-forge dependency. Skip to next for now.
|
213
|
+
|
213
214
|
# Fetching metadata for all releases takes ages (which is weird, since it's mostly static info)
|
214
215
|
mod.releases.take(FETCH_LIMIT).each do |rel|
|
215
216
|
meta = get_release_metadata(name, rel)
|