ra10ke 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22fd36a3a88f9a05d15fb0b4e964e3151e0eff8f
4
- data.tar.gz: f9234354db77a8bf07d29e826c7570ffc6bc29d0
3
+ metadata.gz: 4483e49e41d8b69e98d4730662b79084a670580b
4
+ data.tar.gz: 0bf2f4d16f66755a60f3653852900e46983136f1
5
5
  SHA512:
6
- metadata.gz: 8bba17ade9b5e494e75f604e8ec734c1c5489a127adc366554c2b35c69cade5a3e49d50b725fb036ac03e92e98db0651f18856e6eb2f4d0e1db797f1027636c4
7
- data.tar.gz: 3f59f147b0d1709ae9e3c4aa5cfbd2a2e1770b2d789661b9f501a003b99e4ad7e778b667aeba4166b836ffbfe20547d98838519f2f51433426a4221d5b23c451
6
+ metadata.gz: de78be1d8e3146d55b207bd48f100f00cd36e310f857d3de3d2cb831dc894c63b3196fa1aa75dd8fda9964c46e1f6f0412507ebb22863a2ec8e19a37bf39fb2c
7
+ data.tar.gz: 36087c043bb91a4a4b034fb86fc5695c6d2ea74c4ed5ababcb3737b2477ee9714fe30afb8544741feb73a306deacc41633341a7acbf41a354203480776ca223e
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  CHANGELOG
2
2
  =========
3
3
 
4
+ 0.3.0
5
+ -----
6
+
7
+ 2017-10-08
8
+
9
+ * [#6](https://github.com/voxpupuli/ra10ke/pull/6) Add a dependency solver based on the Ruby Solve library (thanks to [Jarkko Oranen](https://github.com/oranenj))
10
+ * [#8](https://github.com/voxpupuli/ra10ke/pull/8) Add numeric tag convention support (thanks to [Hervé MARTIN](https://github.com/HerveMARTIN))
11
+
4
12
  0.2.0
5
13
  -----
6
14
 
data/README.md CHANGED
@@ -40,6 +40,18 @@ Ignoring specific modules:
40
40
  Under specific conditions you may not wish to report on specific modules being out of date,
41
41
  to ignore a module create `.r10kignore` file in the same directory as your Puppetfile.
42
42
 
43
+ ### r10k:solve_dependencies
44
+
45
+ Reads the Puppetfile in the current directory and uses the ruby 'solve' library to find
46
+ missing and outdated dependencies based on their metadata.
47
+
48
+ The solver does not allow major version bumps according to SemVer by default. To allow
49
+ major upgrades, call the rake task with any parameter.
50
+
51
+ The rake task will download git modules into the modules/ directory to access their metadata.json.
52
+ It will also cache forge metadata in ̃$XDG_CACHE_DIR/ra10ke.metadata_cache in order to make subsequent
53
+ runs faster.
54
+
43
55
  #### Limitations
44
56
 
45
57
  * It works only with modules from the [Forge](https://forge.puppetlabs.com), and Git.
@@ -0,0 +1,227 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'ra10ke/version'
4
+ require 'git'
5
+ require 'set'
6
+ require 'solve'
7
+ require 'yaml/store'
8
+ require 'semverse/version'
9
+
10
+ # How many versions to fetch from the Forge, at most
11
+ FETCH_LIMIT = 3
12
+
13
+ module Ra10ke::Solve
14
+ class RakeTask < ::Rake::TaskLib
15
+ def initialize(*_args)
16
+ namespace :r10k do
17
+ desc 'Find missing or outdated module dependencies'
18
+ task :solve_dependencies, [:allow_major_bump] do |_t, args|
19
+ require 'r10k/puppetfile'
20
+ require 'r10k/module/git'
21
+ require 'r10k/module/metadata_file'
22
+ require 'puppet_forge'
23
+
24
+ allow_major_bump = false
25
+ allow_major_bump = true if args[:allow_major_bump]
26
+
27
+ # Same as in the dependencies task, but oh well.
28
+ PuppetForge.user_agent = "ra10ke/#{Ra10ke::VERSION}"
29
+ puppetfile = R10K::Puppetfile.new('.')
30
+ puppetfile.load!
31
+
32
+ # ignore file allows for "don't tell me about this"
33
+ ignore_modules = []
34
+ if File.exist?('.r10kignore')
35
+ ignore_modules = File.readlines('.r10kignore').each(&:chomp!)
36
+ end
37
+ # Actual new logic begins here:
38
+ cache = (ENV['XDG_CACHE_DIR'] || File.expand_path('~/.cache'))
39
+ # Metadata cache, since the Forge is slow:
40
+ @metadata_cache = YAML::Store.new File.join(cache, 'ra10ke.metadata_cache')
41
+ # The graph of available module versions
42
+ @graph = Solve::Graph.new
43
+ # Set of modules that we have already added to the graph
44
+ @processed_modules = Set.new
45
+ # The set of "demands" we make of the solver. Will be a list of module names
46
+ # Could also demand certain version constraints to hold, but the code does not do it
47
+ # Can be either "module-name" or ["module-name", "version-constraint"]
48
+ @demands = Set.new
49
+ # List of modules we have in the Puppetfile, as [name, version] pairs
50
+ @current_modules = []
51
+
52
+ puppetfile.modules.each do |puppet_module|
53
+ next if ignore_modules.include? puppet_module.title
54
+ if puppet_module.class == R10K::Module::Forge
55
+ module_name = puppet_module.title.tr('/', '-')
56
+ installed_version = puppet_module.expected_version
57
+ puts "Processing Forge module #{module_name}-#{installed_version}"
58
+ @current_modules << [module_name, installed_version]
59
+ @graph.artifact(module_name, installed_version)
60
+ constraint = '>=0.0.0'
61
+ unless allow_major_bump
62
+ ver = Semverse::Version.new installed_version
63
+ if ver.major.zero?
64
+ constraint = "~>#{installed_version}"
65
+ else
66
+ nver = Semverse::Version.new([ver.major + 1, 0, 0])
67
+ constraint = "<#{nver}"
68
+ end
69
+ end
70
+ puts "...Adding a demand: #{module_name} #{constraint}"
71
+
72
+ @demands.add([module_name, constraint])
73
+ puts '...Fetching latest release version information'
74
+ forge_rel = PuppetForge::Module.find(module_name).current_release
75
+ mod = @graph.artifact(module_name, forge_rel.version)
76
+ puts '...Adding its requirements to the graph'
77
+ meta = get_release_metadata(module_name, forge_rel)
78
+ add_reqs_to_graph(mod, meta)
79
+ end
80
+
81
+ next unless puppet_module.class == R10K::Module::Git
82
+ # This downloads the git module to modules/modulename
83
+ meta = fetch_git_metadata(puppet_module)
84
+ version = get_key_or_sym(meta, :version)
85
+ module_name = puppet_module.title.tr('/', '-')
86
+ @current_modules << [module_name, version]
87
+ # We should add git modules with exact versions, or the system might recommend updating to a
88
+ # Forge version.
89
+ puts "Adding git module #{module_name} to the list of required modules with exact version: #{version}"
90
+ @demands.add([module_name, version])
91
+ mod = @graph.artifact(module_name, version)
92
+ puts "...Adding requirements for git module #{module_name}-#{version}"
93
+ add_reqs_to_graph(mod, meta)
94
+ end
95
+ puts
96
+ puts 'Resolving dependencies...'
97
+ if allow_major_bump
98
+ puts 'WARNING: Potentially breaking updates are allowed for this resolution'
99
+ end
100
+ result = Solve.it!(@graph, @demands, sorted: true)
101
+ puts
102
+ print_module_diff(@current_modules, result)
103
+ end
104
+ end
105
+ end
106
+
107
+ def get_release_metadata(name, release)
108
+ meta = nil
109
+ @metadata_cache.transaction do
110
+ meta = @metadata_cache["#{name}-#{release.version}"]
111
+ unless meta
112
+ meta = release.metadata
113
+ @metadata_cache["#{name}-#{release.version}"] = meta
114
+ end
115
+ end
116
+ meta
117
+ end
118
+
119
+ def fetch_git_metadata(puppet_module)
120
+ # No caching here. I don't think it's really possible to do in a sane way.
121
+ puts "Fetching git module #{puppet_module.title}, saving to modules/"
122
+ puppet_module.sync
123
+ metadata_path = Pathname.new(puppet_module.full_path) + 'metadata.json'
124
+ unless metadata_path.exist?
125
+ puts 'WARNING: metadata.json does not exist, assuming version 0.0.0 and no dependencies'
126
+ return {
127
+ version: '0.0.0',
128
+ name: puppet_module.title,
129
+ dependencies: []
130
+ }
131
+ end
132
+ metadata = R10K::Module::MetadataFile.new(metadata_path)
133
+ metadata = metadata.read
134
+ {
135
+ version: metadata.version,
136
+ name: metadata.name,
137
+ dependencies: metadata.dependencies
138
+ }
139
+ end
140
+
141
+ # Is there a better way? :(
142
+ def get_key_or_sym(hash, k)
143
+ hash.fetch(k.to_sym, hash.fetch(k.to_s, nil))
144
+ end
145
+
146
+ # At least puppet-extlib has malformed metadata
147
+ def get_version_req(dep)
148
+ req = get_key_or_sym(dep, :version_requirement)
149
+ req = get_key_or_sym(dep, :version_range) unless req
150
+ req
151
+ end
152
+
153
+ def print_module_diff(current, resolution)
154
+ current.sort!
155
+ resolution.sort!
156
+ outdated = []
157
+ missing = []
158
+ resolution.each do |mod|
159
+ cur_mod, cur_version = current.shift
160
+ mod, version = mod
161
+ if (cur_mod == mod) && cur_version && (cur_version != version)
162
+ outdated << [mod, cur_version, version]
163
+ elsif cur_mod != mod
164
+ missing << [mod, version]
165
+ current.unshift [cur_mod, cur_version]
166
+ end
167
+ end
168
+ missing.each do |m|
169
+ puts format('MISSING: %-25s %s', *m)
170
+ end
171
+ outdated.each do |o|
172
+ puts format('OUTDATED: %-25s %s -> %s', *o)
173
+ end
174
+ end
175
+
176
+ def add_reqs_to_graph(artifact, metadata, no_demands = nil)
177
+ deps = get_key_or_sym(metadata, :dependencies)
178
+ my_name = get_key_or_sym(metadata, :name)
179
+ deps.each do |dep|
180
+ name = get_key_or_sym(dep, :name).tr('/', '-')
181
+ # Add dependency to the global set of modules we want, so that we can
182
+ # actually ask the solver for the versioned thing
183
+ @demands.add(name) unless no_demands
184
+ ver = get_version_req(dep)
185
+ unless ver
186
+ # no version specified, so anything will do
187
+ ver = '>=0.0.0'
188
+ end
189
+ ver.split(/(?=[<])/).each do |bound|
190
+ bound.strip!
191
+ v = begin
192
+ Semverse::Constraint.new(bound)
193
+ rescue
194
+ nil
195
+ end
196
+ if v
197
+ artifact.depends(name, v.to_s)
198
+ else
199
+ puts "WARNING: Invalid version constraint: #{bound}"
200
+ end
201
+ end
202
+ # Find the dependency in the forge, unless it's already been processed
203
+ # and add its releases to the global graph
204
+ next unless @processed_modules.add?(name)
205
+ puts "Fetching module info for #{name}"
206
+ mod = begin
207
+ PuppetForge::Module.find(name)
208
+ rescue
209
+ # It's probably a git module
210
+ nil
211
+ end
212
+ next unless mod # Git module, or non-forge dependency. Skip to next for now.
213
+ # Fetching metadata for all releases takes ages (which is weird, since it's mostly static info)
214
+ mod.releases.take(FETCH_LIMIT).each do |rel|
215
+ meta = get_release_metadata(name, rel)
216
+ rel_artifact = @graph.artifact(name, rel.version)
217
+ puts "...Recursively adding requirements for dependency #{name} version #{rel.version}"
218
+ # We don't want to add the requirements to the list of demands for all versions,
219
+ # but we need them in the graph to be able to solve dependencies
220
+ add_reqs_to_graph(rel_artifact, meta, :no_demands)
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ Ra10ke::Solve::RakeTask.new
@@ -1,3 +1,3 @@
1
1
  module Ra10ke
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/ra10ke.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
3
  require 'ra10ke/version'
4
+ require 'ra10ke/solve'
4
5
  require 'git'
5
6
 
6
7
  module Ra10ke
@@ -52,12 +53,14 @@ module Ra10ke
52
53
  when 'sha'
53
54
  latest_ref = remote_refs['head'][:sha]
54
55
  when 'tag'
55
- # we have to be opinionated here, due to multiple conventions only one can reign suppreme
56
- # as such tag v#.#.# is what will pick.
57
- if ref.match(/^[vV]\d/)
56
+ # we have to be opinionated here, due to multiple conventions only the two main will be accepted
57
+ # v#.#.# or #.#.# is what we will pick.
58
+ if ref.match(/^[vV]?\d[\.\d]*/)
58
59
  tags = remote_refs['tags']
59
- version_tags = tags.select { |f| /^[vV]\d/.match(f) }
60
+ version_tags = tags.select { |f| /^[vV]?\d[\.\d]*/.match(f) }
60
61
  latest_ref = version_tags.keys.sort.last
62
+ else
63
+ latest_ref = "undef (tags don't match v#.#.# or #.#.#)"
61
64
  end
62
65
  else
63
66
  raise "Unable to determine ref_type for #{puppet_module.title}"
data/ra10ke.gemspec CHANGED
@@ -19,4 +19,5 @@ Gem::Specification.new do |spec|
19
19
  spec.add_dependency "puppet_forge"
20
20
  spec.add_dependency "r10k"
21
21
  spec.add_dependency "git"
22
+ spec.add_dependency "solve"
22
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ra10ke
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Theo Chatzimichos
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-03 00:00:00.000000000 Z
11
+ date: 2017-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: solve
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  description: R10K and Puppetfile rake tasks
70
84
  email:
71
85
  - tampakrap@gmail.com
@@ -79,6 +93,7 @@ files:
79
93
  - LICENSE.txt
80
94
  - README.md
81
95
  - lib/ra10ke.rb
96
+ - lib/ra10ke/solve.rb
82
97
  - lib/ra10ke/version.rb
83
98
  - ra10ke.gemspec
84
99
  homepage: https://github.com/voxpupuli/ra10ke
@@ -101,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
116
  version: '0'
102
117
  requirements: []
103
118
  rubyforge_project:
104
- rubygems_version: 2.4.5.1
119
+ rubygems_version: 2.6.11
105
120
  signing_key:
106
121
  specification_version: 4
107
122
  summary: Syntax check for the Puppetfile, check for outdated installed puppet modules