codeclimate-fede 0.85.21
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 +7 -0
- data/bin/check +18 -0
- data/bin/codeclimate +21 -0
- data/bin/prep-release +45 -0
- data/bin/publish +47 -0
- data/bin/release +41 -0
- data/bin/validate-release +18 -0
- data/config/engines.yml +322 -0
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +21 -0
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container.rb +208 -0
- data/lib/cc/analyzer/container_listener.rb +9 -0
- data/lib/cc/analyzer/engine.rb +125 -0
- data/lib/cc/analyzer/engine_output.rb +74 -0
- data/lib/cc/analyzer/engine_output_filter.rb +36 -0
- data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
- data/lib/cc/analyzer/filesystem.rb +50 -0
- data/lib/cc/analyzer/formatters/formatter.rb +53 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
- data/lib/cc/analyzer/formatters/spinner.rb +35 -0
- data/lib/cc/analyzer/formatters.rb +21 -0
- data/lib/cc/analyzer/issue.rb +69 -0
- data/lib/cc/analyzer/issue_sorter.rb +30 -0
- data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
- data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
- data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
- data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
- data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
- data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
- data/lib/cc/analyzer/issue_validations.rb +26 -0
- data/lib/cc/analyzer/issue_validator.rb +11 -0
- data/lib/cc/analyzer/location_description.rb +45 -0
- data/lib/cc/analyzer/logging_container_listener.rb +24 -0
- data/lib/cc/analyzer/measurement.rb +22 -0
- data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
- data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
- data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
- data/lib/cc/analyzer/measurement_validations.rb +16 -0
- data/lib/cc/analyzer/measurement_validator.rb +11 -0
- data/lib/cc/analyzer/mounted_path.rb +80 -0
- data/lib/cc/analyzer/raising_container_listener.rb +32 -0
- data/lib/cc/analyzer/source_buffer.rb +47 -0
- data/lib/cc/analyzer/source_extractor.rb +79 -0
- data/lib/cc/analyzer/source_fingerprint.rb +40 -0
- data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/analyzer.rb +50 -0
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/analyze.rb +90 -0
- data/lib/cc/cli/command.rb +85 -0
- data/lib/cc/cli/console.rb +12 -0
- data/lib/cc/cli/engines/engine_command.rb +15 -0
- data/lib/cc/cli/engines/install.rb +35 -0
- data/lib/cc/cli/engines/list.rb +18 -0
- data/lib/cc/cli/engines.rb +5 -0
- data/lib/cc/cli/file_store.rb +42 -0
- data/lib/cc/cli/global_cache.rb +47 -0
- data/lib/cc/cli/global_config.rb +35 -0
- data/lib/cc/cli/help.rb +51 -0
- data/lib/cc/cli/output.rb +34 -0
- data/lib/cc/cli/prepare.rb +98 -0
- data/lib/cc/cli/runner.rb +75 -0
- data/lib/cc/cli/validate_config.rb +84 -0
- data/lib/cc/cli/version.rb +16 -0
- data/lib/cc/cli/version_checker.rb +107 -0
- data/lib/cc/cli.rb +39 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +54 -0
- data/lib/cc/config/engine.rb +41 -0
- data/lib/cc/config/engine_set.rb +47 -0
- data/lib/cc/config/json_adapter.rb +17 -0
- data/lib/cc/config/prepare.rb +92 -0
- data/lib/cc/config/validation/check_validator.rb +34 -0
- data/lib/cc/config/validation/engine_validator.rb +93 -0
- data/lib/cc/config/validation/fetch_validator.rb +78 -0
- data/lib/cc/config/validation/file_validator.rb +112 -0
- data/lib/cc/config/validation/hash_validations.rb +52 -0
- data/lib/cc/config/validation/json.rb +31 -0
- data/lib/cc/config/validation/prepare_validator.rb +40 -0
- data/lib/cc/config/validation/yaml.rb +66 -0
- data/lib/cc/config/yaml_adapter.rb +73 -0
- data/lib/cc/config.rb +70 -0
- data/lib/cc/engine_registry.rb +74 -0
- data/lib/cc/resolv.rb +39 -0
- data/lib/cc/workspace/exclusion.rb +34 -0
- data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
- data/lib/cc/workspace/path_tree/file_node.rb +31 -0
- data/lib/cc/workspace/path_tree.rb +49 -0
- data/lib/cc/workspace.rb +39 -0
- metadata +279 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module CLI
|
|
3
|
+
module Engines
|
|
4
|
+
class List < EngineCommand
|
|
5
|
+
SHORT_HELP = "List all available engines".freeze
|
|
6
|
+
|
|
7
|
+
def run
|
|
8
|
+
say "Available engines:"
|
|
9
|
+
engine_registry.
|
|
10
|
+
sort_by { |engine, _| engine.name }.
|
|
11
|
+
each do |engine, metadata|
|
|
12
|
+
say "- #{engine.name}: #{metadata.description}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "yaml"
|
|
3
|
+
|
|
4
|
+
module CC
|
|
5
|
+
module CLI
|
|
6
|
+
class FileStore
|
|
7
|
+
# This class is not supposed to be directly used. It should be sublcassed
|
|
8
|
+
# and a few constants need to be defined on the sublass to be usable.
|
|
9
|
+
#
|
|
10
|
+
# FILE_NAME is the name of the file this class wraps.
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
load_data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def save
|
|
17
|
+
return false unless File.exist? self.class::FILE_NAME
|
|
18
|
+
|
|
19
|
+
File.open(self.class::FILE_NAME, "w") do |f|
|
|
20
|
+
YAML.dump data, f
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :data
|
|
29
|
+
|
|
30
|
+
def load_data
|
|
31
|
+
@data =
|
|
32
|
+
if File.exist? self.class::FILE_NAME
|
|
33
|
+
File.open(self.class::FILE_NAME, "r:bom|utf-8") do |f|
|
|
34
|
+
YAML.safe_load(f, [Time], [], false, self.class::FILE_NAME) || {}
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
{}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require "cc/cli/file_store"
|
|
2
|
+
|
|
3
|
+
module CC
|
|
4
|
+
module CLI
|
|
5
|
+
class GlobalCache < FileStore
|
|
6
|
+
FILE_NAME = "/cache.yml".freeze
|
|
7
|
+
|
|
8
|
+
# Cache entries
|
|
9
|
+
|
|
10
|
+
def last_version_check
|
|
11
|
+
data["last-version-check"] || Time.at(0)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def last_version_check=(value)
|
|
15
|
+
data["last-version-check"] =
|
|
16
|
+
if value.is_a? Time
|
|
17
|
+
value
|
|
18
|
+
else
|
|
19
|
+
Time.at(0)
|
|
20
|
+
end
|
|
21
|
+
save
|
|
22
|
+
value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def latest_version
|
|
26
|
+
data["latest-version"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def latest_version=(value)
|
|
30
|
+
data["latest-version"] = value
|
|
31
|
+
save
|
|
32
|
+
value
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def outdated
|
|
36
|
+
data["outdated"] == true
|
|
37
|
+
end
|
|
38
|
+
alias outdated? outdated
|
|
39
|
+
|
|
40
|
+
def outdated=(value)
|
|
41
|
+
data["outdated"] = value == true
|
|
42
|
+
save
|
|
43
|
+
value
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require "cc/cli/file_store"
|
|
2
|
+
require "uuid"
|
|
3
|
+
|
|
4
|
+
module CC
|
|
5
|
+
module CLI
|
|
6
|
+
class GlobalConfig < FileStore
|
|
7
|
+
FILE_NAME = "/config.yml".freeze
|
|
8
|
+
|
|
9
|
+
DEFAULT_CONFIG = {
|
|
10
|
+
"check-version" => true,
|
|
11
|
+
}.freeze
|
|
12
|
+
|
|
13
|
+
# Config entries
|
|
14
|
+
|
|
15
|
+
def check_version
|
|
16
|
+
data["check-version"]
|
|
17
|
+
end
|
|
18
|
+
alias check_version? check_version
|
|
19
|
+
|
|
20
|
+
def check_version=(value)
|
|
21
|
+
data["check-version"] = value == true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def uuid
|
|
25
|
+
data["uuid"] ||= UUID.new.generate
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def load_data
|
|
31
|
+
@data = DEFAULT_CONFIG.merge(super)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/cc/cli/help.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require "rainbow"
|
|
2
|
+
|
|
3
|
+
module CC
|
|
4
|
+
module CLI
|
|
5
|
+
class Help < Command
|
|
6
|
+
ARGUMENT_LIST = "[command]".freeze
|
|
7
|
+
SHORT_HELP = "Display help information.".freeze
|
|
8
|
+
HELP = "#{SHORT_HELP}\n" \
|
|
9
|
+
"\n" \
|
|
10
|
+
" no arguments Show help summary for all commands.\n" \
|
|
11
|
+
" [command] Show help for specific commands. Can be specified multiple times.".freeze
|
|
12
|
+
|
|
13
|
+
def run
|
|
14
|
+
if @args.any?
|
|
15
|
+
@args.each do |command|
|
|
16
|
+
show_help(command)
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
show_help_summary
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def show_help(command_name)
|
|
26
|
+
if (command = Command[command_name])
|
|
27
|
+
say "Usage: codeclimate #{command.synopsis}\n"
|
|
28
|
+
say "\n"
|
|
29
|
+
say "#{command.help}\n"
|
|
30
|
+
say "\n\n"
|
|
31
|
+
else
|
|
32
|
+
say "Unknown command: #{command_name}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def show_help_summary
|
|
37
|
+
short_helps =
|
|
38
|
+
Command.all.sort_by(&:command_name).map do |command|
|
|
39
|
+
[command.synopsis, command.short_help]
|
|
40
|
+
end.compact.to_h
|
|
41
|
+
|
|
42
|
+
longest_command_length = short_helps.keys.map(&:length).max
|
|
43
|
+
|
|
44
|
+
say "Usage: codeclimate COMMAND ...\n\nAvailable commands:\n"
|
|
45
|
+
short_helps.each do |command, help|
|
|
46
|
+
say format(" %-#{longest_command_length}s %s\n", command, help)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module CLI
|
|
3
|
+
module Output
|
|
4
|
+
def success(message)
|
|
5
|
+
terminal.say colorize(message, :green)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def say(message)
|
|
9
|
+
terminal.say message
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def warn(message)
|
|
13
|
+
terminal.say colorize("WARNING: #{message}", :yellow)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def fatal(message)
|
|
17
|
+
$stderr.puts colorize(message, :red)
|
|
18
|
+
exit 1
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def colorize(string, *args)
|
|
22
|
+
rainbow.wrap(string).color(*args)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def rainbow
|
|
26
|
+
@rainbow ||= Rainbow.new
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def terminal
|
|
30
|
+
@terminal ||= HighLine.new($stdin, $stdout)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "ipaddr"
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "pathname"
|
|
6
|
+
require "uri"
|
|
7
|
+
|
|
8
|
+
require "cc/resolv"
|
|
9
|
+
|
|
10
|
+
module CC
|
|
11
|
+
module CLI
|
|
12
|
+
class Prepare < Command
|
|
13
|
+
ARGUMENT_LIST = "[--allow-internal-ips]".freeze
|
|
14
|
+
SHORT_HELP = "Run the commands in your prepare step.".freeze
|
|
15
|
+
HELP = "#{SHORT_HELP}\n" \
|
|
16
|
+
"\n" \
|
|
17
|
+
" --allow-internal-ips Allow fetching from internal IPs.".freeze
|
|
18
|
+
|
|
19
|
+
InternalHostError = Class.new(StandardError)
|
|
20
|
+
FetchError = Class.new(StandardError)
|
|
21
|
+
|
|
22
|
+
PRIVATE_ADDRESS_SUBNETS = [
|
|
23
|
+
IPAddr.new("10.0.0.0/8"),
|
|
24
|
+
IPAddr.new("172.16.0.0/12"),
|
|
25
|
+
IPAddr.new("192.168.0.0/16"),
|
|
26
|
+
IPAddr.new("fd00::/8"),
|
|
27
|
+
IPAddr.new("127.0.0.1"),
|
|
28
|
+
IPAddr.new("0:0:0:0:0:0:0:1"),
|
|
29
|
+
].freeze
|
|
30
|
+
|
|
31
|
+
def run
|
|
32
|
+
::CC::Resolv.with_fixed_dns { fetch_all }
|
|
33
|
+
rescue FetchError, InternalHostError => ex
|
|
34
|
+
fatal(ex.message)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def allow_internal_ips?
|
|
40
|
+
@args.include?("--allow-internal-ips")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fetches
|
|
44
|
+
@fetches ||= config.prepare.fetch
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def config
|
|
48
|
+
@config ||= CC::Config.load
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def fetch_all
|
|
52
|
+
fetches.each do |entry|
|
|
53
|
+
fetch(entry.url, entry.path)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fetch(url, target_path)
|
|
58
|
+
ensure_external!(url) unless allow_internal_ips?
|
|
59
|
+
|
|
60
|
+
uri = URI.parse(url)
|
|
61
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
62
|
+
http.use_ssl = uri.scheme == "https"
|
|
63
|
+
resp = http.get(uri)
|
|
64
|
+
if resp.code == "200"
|
|
65
|
+
write_file(target_path, resp.body)
|
|
66
|
+
say("Wrote #{url} to #{target_path}")
|
|
67
|
+
else
|
|
68
|
+
raise FetchError, "Failed fetching #{url}: code=#{resp.code} body=#{resp.body}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def write_file(target_path, content)
|
|
73
|
+
FileUtils.mkdir_p(Pathname.new(target_path).parent.to_s)
|
|
74
|
+
File.write(target_path, content)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def ensure_external!(url)
|
|
78
|
+
uri = URI.parse(url)
|
|
79
|
+
|
|
80
|
+
if internal?(uri.host)
|
|
81
|
+
raise InternalHostError, "Won't fetch #{url.inspect}: it maps to an internal address"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# rubocop:disable Style/CaseEquality
|
|
86
|
+
def internal?(host)
|
|
87
|
+
address = ::Resolv.getaddress(host)
|
|
88
|
+
|
|
89
|
+
PRIVATE_ADDRESS_SUBNETS.any? do |subnet|
|
|
90
|
+
subnet === IPAddr.new(address.to_s)
|
|
91
|
+
end
|
|
92
|
+
rescue ::Resolv::ResolvError
|
|
93
|
+
true # localhost
|
|
94
|
+
end
|
|
95
|
+
# rubocop:enable Style/CaseEquality
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "active_support"
|
|
2
|
+
require "active_support/core_ext"
|
|
3
|
+
require "cc/cli/version_checker"
|
|
4
|
+
|
|
5
|
+
module CC
|
|
6
|
+
module CLI
|
|
7
|
+
class Runner
|
|
8
|
+
def self.run(argv)
|
|
9
|
+
new(argv).run
|
|
10
|
+
rescue => ex
|
|
11
|
+
$stderr.puts("error: (#{ex.class}) #{ex.message}")
|
|
12
|
+
CLI.logger.debug("backtrace: #{ex.backtrace.join("\n\t")}")
|
|
13
|
+
exit 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(args)
|
|
17
|
+
@args = args
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run
|
|
21
|
+
VersionChecker.new.check if check_version?
|
|
22
|
+
|
|
23
|
+
if command_class
|
|
24
|
+
command = command_class.new(command_arguments)
|
|
25
|
+
command.execute
|
|
26
|
+
else
|
|
27
|
+
command_not_found
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def command_not_found
|
|
32
|
+
$stderr.puts "unknown command #{command}"
|
|
33
|
+
exit 1
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def command_class
|
|
37
|
+
command_const = Command[command]
|
|
38
|
+
if command_const.abstract?
|
|
39
|
+
nil
|
|
40
|
+
else
|
|
41
|
+
command_const
|
|
42
|
+
end
|
|
43
|
+
rescue NameError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def command_arguments
|
|
48
|
+
Array(@args[1..-1])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def command
|
|
52
|
+
command_name = @args.first
|
|
53
|
+
case command_name
|
|
54
|
+
when nil, "-h", "-?", "--help"
|
|
55
|
+
"help"
|
|
56
|
+
when "-v", "--version"
|
|
57
|
+
"version"
|
|
58
|
+
else
|
|
59
|
+
command_name
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def check_version?
|
|
66
|
+
if ARGV.first == "--no-check-version"
|
|
67
|
+
ARGV.shift
|
|
68
|
+
false
|
|
69
|
+
else
|
|
70
|
+
true
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module CLI
|
|
3
|
+
class ValidateConfig < Command
|
|
4
|
+
NO_CONFIG_MESSAGE = "No checked in config: nothing to validate.".freeze
|
|
5
|
+
TOO_MANY_CONFIG_MESSAGE = "Don't commit both .codeclimate.yml & .codeclimate.json: only the JSON will be used.".freeze
|
|
6
|
+
SHORT_HELP = "Validate your .codeclimate.yml or .codeclimate.json.".freeze
|
|
7
|
+
VALID_CONFIG_MESSAGE = "No errors or warnings found in %s.".freeze
|
|
8
|
+
|
|
9
|
+
def run
|
|
10
|
+
require_json_or_yaml
|
|
11
|
+
process_args
|
|
12
|
+
|
|
13
|
+
if any_issues?
|
|
14
|
+
display_issues
|
|
15
|
+
else
|
|
16
|
+
puts format(VALID_CONFIG_MESSAGE, validator.path)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
exit 1 unless validator.valid?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader :config, :registry_path, :registry_prefix, :validator
|
|
25
|
+
|
|
26
|
+
def process_args
|
|
27
|
+
@registry_path = EngineRegistry::DEFAULT_MANIFEST_PATH
|
|
28
|
+
@registry_prefix = ""
|
|
29
|
+
|
|
30
|
+
# Undocumented; we only need these from Builder so we can validate
|
|
31
|
+
# engines/channels against our own registry and prefix.
|
|
32
|
+
while (arg = @args.shift)
|
|
33
|
+
case arg
|
|
34
|
+
when "--registry" then @registry_path = @args.shift
|
|
35
|
+
when "--registry-prefix" then @registry_prefix = @args.shift
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def any_issues?
|
|
41
|
+
validator.errors.any? ||
|
|
42
|
+
validator.warnings.any?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def display_issues
|
|
46
|
+
validator.errors.each do |error|
|
|
47
|
+
puts "#{colorize("ERROR", :red)}: #{error}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
validator.warnings.each do |warning|
|
|
51
|
+
puts "#{colorize("WARNING", :yellow)}: #{warning}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def require_json_or_yaml
|
|
56
|
+
if !filesystem.exist?(Config::YAMLAdapter::DEFAULT_PATH) && !filesystem.exist?(Config::JSONAdapter::DEFAULT_PATH)
|
|
57
|
+
puts NO_CONFIG_MESSAGE
|
|
58
|
+
exit 0
|
|
59
|
+
elsif filesystem.exist?(Config::YAMLAdapter::DEFAULT_PATH) && filesystem.exist?(Config::JSONAdapter::DEFAULT_PATH)
|
|
60
|
+
puts "#{colorize("WARNING", :yellow)}: #{TOO_MANY_CONFIG_MESSAGE}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def validator
|
|
65
|
+
@validator =
|
|
66
|
+
if filesystem.exist?(Config::JSONAdapter::DEFAULT_PATH)
|
|
67
|
+
Config::Validation::JSON.new(
|
|
68
|
+
Config::JSONAdapter::DEFAULT_PATH,
|
|
69
|
+
engine_registry,
|
|
70
|
+
)
|
|
71
|
+
elsif filesystem.exist?(Config::YAMLAdapter::DEFAULT_PATH)
|
|
72
|
+
Config::Validation::YAML.new(
|
|
73
|
+
Config::YAMLAdapter::DEFAULT_PATH,
|
|
74
|
+
engine_registry,
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def engine_registry
|
|
80
|
+
EngineRegistry.new(registry_path, registry_prefix)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
module CLI
|
|
3
|
+
class Version < Command
|
|
4
|
+
SHORT_HELP = "Display the CLI version.".freeze
|
|
5
|
+
|
|
6
|
+
def run
|
|
7
|
+
say version
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def version
|
|
11
|
+
path = File.expand_path("../../../../VERSION", __FILE__)
|
|
12
|
+
@version ||= File.read(path)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require "cc/cli/global_config"
|
|
2
|
+
require "cc/cli/global_cache"
|
|
3
|
+
|
|
4
|
+
module CC
|
|
5
|
+
module CLI
|
|
6
|
+
class VersionChecker
|
|
7
|
+
include CC::CLI::Output
|
|
8
|
+
|
|
9
|
+
VERSION_CHECK_TIMEOUT = 60 * 60 # 1 Hour in seconds
|
|
10
|
+
DEFAULT_VERSIONS_URL = "https://versions.codeclimate.com".freeze
|
|
11
|
+
|
|
12
|
+
def check
|
|
13
|
+
return unless global_config.check_version? && version_check_is_due?
|
|
14
|
+
|
|
15
|
+
print_new_version_message if outdated?
|
|
16
|
+
|
|
17
|
+
global_config.save
|
|
18
|
+
rescue => error
|
|
19
|
+
CLI.logger.debug(error)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def version_check_is_due?
|
|
25
|
+
Time.now > global_cache.last_version_check + VERSION_CHECK_TIMEOUT
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def outdated?
|
|
29
|
+
api_response["outdated"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def latest_version
|
|
33
|
+
api_response["latest"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def print_new_version_message
|
|
37
|
+
warn "A new version (v#{latest_version}) is available. Upgrade instructions are available at: https://github.com/codeclimate/codeclimate#packages"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def api_response
|
|
41
|
+
@api_response ||=
|
|
42
|
+
begin
|
|
43
|
+
cache! JSON.parse(api_response_body)
|
|
44
|
+
rescue JSON::ParserError => error
|
|
45
|
+
CLI.logger.debug(error)
|
|
46
|
+
{
|
|
47
|
+
"outdated" => false,
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def api_response_body
|
|
53
|
+
if http_response.is_a? Net::HTTPSuccess
|
|
54
|
+
http_response.body
|
|
55
|
+
else
|
|
56
|
+
raise Net::HTTPFatalError.new("HTTP Error", http_response)
|
|
57
|
+
end
|
|
58
|
+
rescue Net::HTTPFatalError => error
|
|
59
|
+
CLI.logger.debug(error)
|
|
60
|
+
""
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def http_response
|
|
64
|
+
@http_response ||=
|
|
65
|
+
begin
|
|
66
|
+
uri = URI.parse(ENV.fetch("CODECLIMATE_VERSIONS_URL", DEFAULT_VERSIONS_URL))
|
|
67
|
+
uri.query = { version: version, uid: global_config.uuid }.to_query
|
|
68
|
+
|
|
69
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
70
|
+
http.open_timeout = 5
|
|
71
|
+
http.read_timeout = 5
|
|
72
|
+
http.ssl_timeout = 5
|
|
73
|
+
http.use_ssl = uri.scheme == "https"
|
|
74
|
+
|
|
75
|
+
http.get(uri, "User-Agent" => user_agent)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def cache!(data)
|
|
80
|
+
global_cache.latest_version = data["latest"]
|
|
81
|
+
global_cache.outdated = data["outdated"] == true
|
|
82
|
+
global_cache.last_version_check = Time.now
|
|
83
|
+
data
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def version
|
|
87
|
+
@version ||= Version.new.version
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def user_agent
|
|
91
|
+
"Code Climate CLI #{version}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def global_config
|
|
95
|
+
@global_config ||= GlobalConfig.new
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def global_cache
|
|
99
|
+
@global_cache ||= GlobalCache.new
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def terminal
|
|
103
|
+
@terminal ||= HighLine.new(nil, $stderr)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
data/lib/cc/cli.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "active_support"
|
|
2
|
+
require "active_support/core_ext"
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "cc/analyzer"
|
|
5
|
+
require "cc/config"
|
|
6
|
+
require "cc/engine_registry"
|
|
7
|
+
require "cc/workspace"
|
|
8
|
+
|
|
9
|
+
require "cc/cli/analyze"
|
|
10
|
+
require "cc/cli/command"
|
|
11
|
+
require "cc/cli/console"
|
|
12
|
+
require "cc/cli/engines"
|
|
13
|
+
require "cc/cli/help"
|
|
14
|
+
require "cc/cli/output"
|
|
15
|
+
require "cc/cli/prepare"
|
|
16
|
+
require "cc/cli/runner"
|
|
17
|
+
require "cc/cli/validate_config"
|
|
18
|
+
require "cc/cli/version"
|
|
19
|
+
|
|
20
|
+
module CC
|
|
21
|
+
module CLI
|
|
22
|
+
def self.debug?
|
|
23
|
+
ENV["CODECLIMATE_DEBUG"].present?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.logger
|
|
27
|
+
@logger ||= ::Logger.new(STDERR).tap do |logger|
|
|
28
|
+
logger.level =
|
|
29
|
+
if debug?
|
|
30
|
+
::Logger::DEBUG
|
|
31
|
+
else
|
|
32
|
+
::Logger::ERROR
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Analyzer.logger = CLI.logger
|
|
39
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class ChecksAdapter
|
|
4
|
+
attr_reader :config
|
|
5
|
+
|
|
6
|
+
def initialize(data = {})
|
|
7
|
+
@config = data
|
|
8
|
+
|
|
9
|
+
return unless checks.present?
|
|
10
|
+
copy_qm_checks_config
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def copy_qm_checks_config
|
|
16
|
+
DefaultAdapter::ENGINES.keys.each do |name|
|
|
17
|
+
copy_checks(name)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def copy_checks(engine_name)
|
|
22
|
+
engine = config.fetch("plugins", {}).fetch(engine_name, {})
|
|
23
|
+
engine["config"] ||= {}
|
|
24
|
+
|
|
25
|
+
if engine["config"].is_a?(String)
|
|
26
|
+
engine["config"] = {
|
|
27
|
+
"file" => engine["config"],
|
|
28
|
+
"checks" => checks,
|
|
29
|
+
}
|
|
30
|
+
elsif engine["config"].is_a?(Hash)
|
|
31
|
+
engine["config"]["checks"] = checks
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def checks
|
|
36
|
+
config["checks"]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|