dependency_checker 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0e51ad69dd9620d0bdf8edc59856b9c1ca489eb1cc259c03b7e230ad8e38bdaa
4
+ data.tar.gz: ca995ece8099855978f71fecb2b80e930528e62ad6790bce650fc75fe8bf2889
5
+ SHA512:
6
+ metadata.gz: 9d447cba1d6b93e9faff062534241b79df938d9ea520076e1adcaf25cb7d4295e68cc7cc8ce10593a06854a3b7c64073b819380d82bd8b55d85eef290b930518
7
+ data.tar.gz: f364be7088d1a4ad70895cc13df206d2b5eef11eb6a32582d8d747c23f3c6bc80a92a4f4b39b5d3cf0cbab767e64304f60da86af04246c397097d310ed65aba7
@@ -0,0 +1,63 @@
1
+ # dependency-checker
2
+
3
+ The dependency-checker tool validates dependencies in `metadata.json` files in Puppet modules or YAML files containing arrays of Puppet modules against the latest published versions on the [Puppet Forge](https://forge.puppet.com/).
4
+
5
+ ## Compatibility
6
+
7
+ dependency-checker is compatible with Ruby versions 2.0.0 and newer.
8
+
9
+ ## Installation
10
+
11
+ via `gem` command:
12
+
13
+ `gem install metadata_json_deps`
14
+
15
+ via Gemfile:
16
+
17
+ `gem 'metadata_json_deps`
18
+
19
+ ## Usage
20
+
21
+ Run against a single Puppet module metadata.json file
22
+
23
+ $ dependency-checker /path/to/metadata.json
24
+
25
+ You can use a local/remote YAML file containing an array of modules (using syntax `namespace/module`)
26
+
27
+ $ dependency-checker managed_modules.yaml
28
+
29
+ It can also be run verbosely to show valid dependencies:
30
+
31
+ $ dependency-checker -v modules/*/metadata.json
32
+
33
+ You can also run it inside a module during a pre-release to determine the effect of a version bump in the metadata.json:
34
+
35
+ $ dependency-checker -c ../*/metadata.json
36
+
37
+ Or you can supply an override value
38
+
39
+ $ dependency-checker ../*/metadata.json -o puppetlabs/stdlib,10.0.0
40
+
41
+ The following optional parameters are available:
42
+ ```
43
+ -o, --override module,version Forge name of module and semantic version to override
44
+ -c, --current Extract override version from metadata.json inside current working directory
45
+ -v, --[no-]verbose Run verbosely
46
+ -h, --help Display help
47
+ ```
48
+
49
+ If attempting to use both `-o` and `-c`, an error will be thrown as these can only be used exclusively.
50
+
51
+ ### Testing with dependency-checker as a Rake task
52
+
53
+ You can also integrate `dependency-checker` checks into your tests using a Rake task:
54
+
55
+ ```ruby
56
+ require 'dependency_checker'
57
+
58
+ desc 'Run dependency-checker'
59
+ task :metadata_deps do
60
+ files = FileList['modules/*/metadata.json']
61
+ DependencyChecker::Runner.run(files)
62
+ end
63
+ ```
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'dependency_checker'
5
+ require 'json'
6
+
7
+ options = {}
8
+ OptionParser.new { |opts|
9
+ opts.on('-o module,version', '--override module,version', Array, 'Forge name of module and semantic version to override') do |override|
10
+ options[:override] = override
11
+ end
12
+
13
+ opts.on('-c', '--current', 'Extract override version from metadata.json inside current working directory') do |current_override|
14
+ options[:current_override] = current_override
15
+ end
16
+
17
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') do |verbose|
18
+ options[:verbose] = verbose
19
+ end
20
+
21
+ opts.on('-h', '--help', 'Display help') do
22
+ puts opts
23
+ exit
24
+ end
25
+ }.parse!
26
+
27
+ # Determine which modules to pass on to runner
28
+ managed_modules = nil
29
+ unless ARGV.empty?
30
+ # If length == 1, only pass first argument, else pass the array of metadata.json files
31
+ managed_modules = ((ARGV.length == 1) ? ARGV[0] : ARGV)
32
+ end
33
+
34
+ # Raise error if both :override and :current_override are specified
35
+ if options[:override] && options[:current_override]
36
+ raise OptionParser::InvalidOption, 'You can not select both override and current override options'
37
+ end
38
+
39
+ # If :current_override is specified, retrieve name and version of module in current working directory
40
+ if options[:current_override]
41
+ metadata_json = JSON.parse(File.read('metadata.json'))
42
+ module_name = metadata_json['name'].sub('-', '/')
43
+ module_version = metadata_json['version']
44
+
45
+ override_check = metadata_json.is_a?(Hash) && !metadata_json.empty?
46
+ options[:override] = [module_name, module_version] if override_check
47
+ raise OptionParser::InvalidOption, 'Unable to find local metadata.json in current working directory' unless override_check
48
+ end
49
+
50
+ # Default :verbose to false
51
+ options[:verbose] ||= false
52
+
53
+ DependencyChecker::Runner.run_with_args(managed_modules, options[:override], options[:verbose])
@@ -0,0 +1,6 @@
1
+ # Module for checking the dependencies of Puppet Module using data retrieved from the Puppet Forge.
2
+ module DependencyChecker
3
+ autoload :ForgeHelper, 'dependency_checker/forge_helper'
4
+ autoload :MetadataChecker, 'dependency_checker/metadata_checker'
5
+ autoload :Runner, 'dependency_checker/runner'
6
+ end
@@ -0,0 +1,58 @@
1
+ require 'puppet_forge'
2
+ require 'semantic_puppet'
3
+
4
+ # Helper class for fetching data from the Forge and perform some basic operations
5
+ class DependencyChecker::ForgeHelper
6
+ def initialize(cache = {})
7
+ @cache = cache
8
+ end
9
+
10
+ # Retrieve current version of module
11
+ # @return [SemanticPuppet::Version]
12
+ def get_current_version(module_name)
13
+ module_name = module_name.sub('/', '-')
14
+ version = nil
15
+ version = get_version(@cache[module_name]) if @cache.key?(module_name)
16
+
17
+ unless version
18
+ version = get_version(get_module_data(module_name)) if check_module_exists(module_name)
19
+ end
20
+
21
+ version
22
+ end
23
+
24
+ # Retrieve module data from Forge
25
+ # @return [Hash] Hash containing JSON response from Forge
26
+ def get_module_data(module_name)
27
+ module_name = module_name.sub('/', '-')
28
+ module_data = @cache[module_name]
29
+ begin
30
+ @cache[module_name] = module_data = PuppetForge::Module.find(module_name) unless module_data
31
+ rescue Faraday::ClientError
32
+ return nil
33
+ end
34
+
35
+ module_data
36
+ end
37
+
38
+ # Retrieve module from Forge
39
+ # @return [PuppetForge::Module]
40
+ def check_module_exists(module_name)
41
+ !get_module_data(module_name).nil?
42
+ end
43
+
44
+ # Check if a module is deprecated from data fetched from the Forge
45
+ # @return [Boolean] boolean result stating whether module is deprecated
46
+ def check_module_deprecated(module_name)
47
+ module_name = module_name.sub('/', '-')
48
+ module_data = get_module_data(module_name)
49
+ version = get_current_version(module_name)
50
+ version.to_s.eql?('999.999.999') || version.to_s.eql?('99.99.99') || !module_data.attribute('deprecated_at').nil?
51
+ end
52
+
53
+ private
54
+
55
+ def get_version(module_data)
56
+ SemanticPuppet::Version.parse(module_data.current_release.version)
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'semantic_puppet'
2
+
3
+ # Checks dependencies of passed in metadata and performs checks to verify constraints
4
+ class DependencyChecker::MetadataChecker
5
+ def initialize(metadata, forge, updated_module, updated_module_version)
6
+ @metadata = metadata
7
+ @forge = forge
8
+ @updated_module = updated_module.sub('-', '/') if updated_module
9
+ @updated_module_version = updated_module_version if updated_module_version
10
+ end
11
+
12
+ # Perform constraint comparisons of dependencies based on their latest version, and also
13
+ # override any occurance of @updated_module with @updated_module_version
14
+ # @return [Map] a map of dependencies along with their constraint, current version and whether they satisfy the constraint
15
+ def check_dependencies
16
+ fetch_module_dependencies.map do |dependency, constraint|
17
+ dependency = dependency.sub('-', '/')
18
+ current = (dependency == @updated_module) ? SemanticPuppet::Version.parse(@updated_module_version) : @forge.get_current_version(dependency)
19
+ [dependency, constraint, current, constraint.include?(current)]
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # Retrieve dependencies from @metedata
26
+ # @return [Map] a map with the name of the dependency and its constraint
27
+ def fetch_module_dependencies
28
+ return [] unless @metadata[:dependencies]
29
+
30
+ @metadata[:dependencies].map do |dep|
31
+ constraint = dep[:version_requirement] || '>= 0'
32
+ [dep[:name], SemanticPuppet::VersionRange.parse(constraint)]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,197 @@
1
+ require 'json'
2
+ require 'yaml'
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'logger'
6
+ require 'parallel'
7
+
8
+ # Main runner for DependencyChecker
9
+ class DependencyChecker::Runner
10
+ def initialize(managed_modules_arg, override, verbose)
11
+ @managed_modules_arg = managed_modules_arg
12
+ @override = !override.nil?
13
+ @updated_module = override[0] if @override
14
+ @updated_module_version = override[1] if @override
15
+ @verbose = verbose
16
+ @forge = DependencyChecker::ForgeHelper.new
17
+ end
18
+
19
+ def run
20
+ validate_override if @override
21
+
22
+ message = "_*Starting dependency checks...*_\n\n"
23
+
24
+ # If override is enabled
25
+ message += "Overriding *#{@updated_module}* version with *#{@updated_module_version}*\n\n" if @override
26
+
27
+ # Post warning if @updated_module is deprecated
28
+ message += "The module you are comparing against *#{@updated_module}* is *deprecated*.\n\n" if @override && @forge.check_module_deprecated(@updated_module)
29
+
30
+ # Post message if using default managed_modules
31
+ if @managed_modules_arg.nil?
32
+ message += "No local path(s) to metadata.json or file argument specified. Defaulting to Puppet supported modules.\n\n"
33
+ @managed_modules_arg = 'https://gist.githubusercontent.com/eimlav/6df50eda0b1c57c1ab8c33b64c82c336/raw/managed_modules.yaml'
34
+ end
35
+
36
+ @use_local_files = @managed_modules_arg.instance_of?(Array) || @managed_modules_arg.end_with?('.json')
37
+
38
+ @modules = @use_local_files ? Array(@managed_modules_arg) : return_modules(@managed_modules_arg)
39
+
40
+ # Post results of dependency checks
41
+ message += run_dependency_checks
42
+ message += 'All modules have valid dependencies.' if run_dependency_checks.empty?
43
+
44
+ post(message)
45
+ end
46
+
47
+ # Validate override from runner and return an error if any issues are encountered
48
+ def validate_override
49
+ raise "*Error:* Could not find *#{@updated_module}* on Puppet Forge! Ensure updated_module argument is valid." unless check_module_exists(@updated_module)
50
+ raise "*Error:* Verify semantic versioning syntax *#{@updated_module_version}* of updated_module_version argument." unless SemanticPuppet::Version.valid?(@updated_module_version)
51
+ end
52
+
53
+ # Check with forge if a specified module exists
54
+ # @param module_name [String]
55
+ # @return [Boolean] boolean based on whether the module exists or not
56
+ def check_module_exists(module_name)
57
+ @forge.check_module_exists(module_name)
58
+ end
59
+
60
+ # Perform dependency checks on modules supplied by @modules
61
+ def run_dependency_checks
62
+ # Cross reference dependencies from managed_modules file with @updated_module and @updated_module_version
63
+ messages = Parallel.map(@modules) do |module_path|
64
+ module_name = @use_local_files ? get_name_from_metadata(module_path) : module_path
65
+ mod_message = "Checking *#{module_path}* dependencies.\n"
66
+ exists_on_forge = true
67
+
68
+ # Check module_path is valid
69
+ unless check_module_exists(module_name)
70
+ if @use_local_files
71
+ exists_on_forge = false
72
+ else
73
+ mod_message += "\t*Error:* Could not find *#{module_name}* on Puppet Forge! Ensure the module exists.\n\n"
74
+ next mod_message
75
+ end
76
+ end
77
+
78
+ # Fetch module dependencies
79
+
80
+ dependencies = @use_local_files ? get_dependencies_from_metadata(module_path) : get_dependencies(module_name)
81
+
82
+ # Post warning if module_path is deprecated
83
+ mod_deprecated = exists_on_forge ? @forge.check_module_deprecated(module_name) : false
84
+ mod_message += "\t*Warning:* *#{module_name}* is *deprecated*.\n" if mod_deprecated
85
+
86
+ if dependencies.empty?
87
+ mod_message += "\tNo dependencies listed\n\n"
88
+ next mod_message if @verbose && !mod_deprecated
89
+ end
90
+
91
+ # Check each dependency to see if the latest version matchs the current modules' dependency constraints
92
+ all_match = true
93
+ dependencies.each do |dependency, constraint, current, satisfied|
94
+ if satisfied && @verbose
95
+ mod_message += "\t#{dependency} (#{constraint}) *matches* #{current}\n"
96
+ elsif !satisfied
97
+ all_match = false
98
+ mod_message += "\t#{dependency} (#{constraint}) *doesn't match* #{current}\n"
99
+ end
100
+
101
+ if @forge.check_module_deprecated(dependency)
102
+ all_match = false
103
+ mod_message += "\t\t*Warning:* *#{dependency}* is *deprecated*.\n"
104
+ end
105
+
106
+ found_deprecation = true if @forge.check_module_deprecated(dependency)
107
+
108
+ # Post warning if dependency is deprecated
109
+ mod_message += "\tThe dependency module *#{dependency}* is *deprecated*.\n" if found_deprecation
110
+ end
111
+
112
+ mod_message += "\tAll dependencies match\n" if all_match
113
+ mod_message += "\n"
114
+
115
+ # If @verbose is true, always post message
116
+ # If @verbose is false, only post if all dependencies don't match and/or if a dependency is deprecated
117
+ (all_match && !@verbose) ? '' : mod_message
118
+ end
119
+
120
+ message = ''
121
+ messages.each do |result|
122
+ message += result
123
+ end
124
+
125
+ message
126
+ end
127
+
128
+ # Get dependencies of a supplied module and use the override values from @updated_module and @updated_module_version
129
+ # to verify if the depedencies are satisfied
130
+ # @param module_name [String]
131
+ # @return [Map] a map of dependencies along with their constraint, current version and whether they satisfy the constraint
132
+ def get_dependencies(module_name)
133
+ module_data = @forge.get_module_data(module_name)
134
+
135
+ metadata = module_data.current_release.metadata
136
+ checker = DependencyChecker::MetadataChecker.new(metadata, @forge, @updated_module, @updated_module_version)
137
+ checker.check_dependencies
138
+ end
139
+
140
+ # Get dependencies of a supplied module from a metadata.json file to verify if the depedencies are satisfied
141
+ # @param module_name [String]
142
+ # @return [Map] a map of dependencies along with their constraint, current version and whether they satisfy the constraint
143
+ def get_dependencies_from_metadata(metadata_path)
144
+ metadata = JSON.parse(File.read(metadata_path), symbolize_names: true)
145
+ checker = DependencyChecker::MetadataChecker.new(metadata, @forge, @updated_module, @updated_module_version)
146
+ checker.check_dependencies
147
+ end
148
+
149
+ # Get dependencies of a supplied module from a metadata.json file to verify if the depedencies are satisfied
150
+ # @param module_name [String]
151
+ # @return [Map] a map of dependencies along with their constraint, current version and whether they satisfy the constraint
152
+ def get_name_from_metadata(metadata_path)
153
+ metadata = JSON.parse(File.read(metadata_path), symbolize_names: true)
154
+ metadata[:name]
155
+ end
156
+
157
+ # Retrieve the array of module names from the supplied filename/URL
158
+ # @return [Array] an array of module names
159
+ def return_modules(managed_modules_path)
160
+ managed_modules = {}
161
+ managed_modules_yaml = {}
162
+
163
+ begin
164
+ if managed_modules_path =~ URI::DEFAULT_PARSER.make_regexp
165
+ managed_modules = Net::HTTP.get(URI.parse(managed_modules_path))
166
+ elsif File.file?(managed_modules_path)
167
+ managed_modules = File.read(managed_modules_path)
168
+ else
169
+ raise 'Error'
170
+ end
171
+ rescue StandardError
172
+ raise "*Error:* Ensure *#{managed_modules_path}* is a valid file path or URL"
173
+ end
174
+
175
+ begin
176
+ managed_modules_yaml = YAML.safe_load(managed_modules)
177
+ rescue StandardError
178
+ raise '*Error:* Ensure syntax of managed_modules file is a valid YAML array'
179
+ end
180
+
181
+ managed_modules_yaml
182
+ end
183
+
184
+ # Post message to terminal
185
+ # @param message [String]
186
+ def post(message)
187
+ puts message
188
+ end
189
+
190
+ def self.run_with_args(managed_modules_path, override, verbose)
191
+ new(managed_modules_path, override, verbose).run
192
+ end
193
+
194
+ def self.run(managed_modules_path)
195
+ new(managed_modules_path, nil, false, nil).run
196
+ end
197
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dependency_checker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Ewoud Kohl van Wijngaarden
8
+ - Puppet
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-03-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: puppet_forge
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '12.3'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '12.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: semantic_puppet
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: parallel
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rubocop
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: " Verify all your dependencies allow the latest versions on Puppet
99
+ Forge. \n Based on https://github.com/ekohl/metadata_json_deps\n"
100
+ email:
101
+ - info@puppet.com
102
+ executables:
103
+ - dependency-checker
104
+ extensions: []
105
+ extra_rdoc_files:
106
+ - README.md
107
+ files:
108
+ - README.md
109
+ - bin/dependency-checker
110
+ - lib/dependency_checker.rb
111
+ - lib/dependency_checker/forge_helper.rb
112
+ - lib/dependency_checker/metadata_checker.rb
113
+ - lib/dependency_checker/runner.rb
114
+ homepage: https://github.com/puppetlabs/dependency_checker
115
+ licenses:
116
+ - MIT
117
+ metadata:
118
+ source_code_uri: https://github.com/puppetlabs/dependency_checker
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: 2.0.0
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.7.7
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Check your Puppet metadata dependencies
139
+ test_files: []