dependency_checker 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []