jiminy 0.1.0.pre1
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/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +245 -0
- data/LICENSE +21 -0
- data/README.md +173 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/example.png +0 -0
- data/exe/jiminy +9 -0
- data/jiminy.gemspec +42 -0
- data/lib/jiminy/cli.rb +137 -0
- data/lib/jiminy/configuration.rb +97 -0
- data/lib/jiminy/github_apiable.rb +17 -0
- data/lib/jiminy/recording/n_plus_one.rb +50 -0
- data/lib/jiminy/recording/prosopite_ext/send_notifications_with_tmp_file.rb +44 -0
- data/lib/jiminy/recording/prosopite_ext/tmp_file_recorder.rb +40 -0
- data/lib/jiminy/recording/rspec.rb +22 -0
- data/lib/jiminy/recording/test_controller_concern.rb +26 -0
- data/lib/jiminy/recording.rb +14 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/api_request.rb +45 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/artifact.rb +17 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/base.rb +46 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/job.rb +17 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/pipeline.rb +49 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/vcs.rb +13 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci/workflow.rb +41 -0
- data/lib/jiminy/reporting/ci_providers/circle_ci.rb +54 -0
- data/lib/jiminy/reporting/ci_providers/github.rb +29 -0
- data/lib/jiminy/reporting/ci_providers/local/artifact.rb +25 -0
- data/lib/jiminy/reporting/ci_providers/local.rb +30 -0
- data/lib/jiminy/reporting/ci_providers/provider_configuration.rb +28 -0
- data/lib/jiminy/reporting/ci_providers.rb +12 -0
- data/lib/jiminy/reporting/n_plus_one.rb +39 -0
- data/lib/jiminy/reporting/reporters/base_reporter.rb +26 -0
- data/lib/jiminy/reporting/reporters/dry_run_reporter.rb +13 -0
- data/lib/jiminy/reporting/reporters/github_reporter.rb +28 -0
- data/lib/jiminy/reporting/reporters.rb +11 -0
- data/lib/jiminy/reporting/yaml_file_comment_presenter.rb +71 -0
- data/lib/jiminy/reporting.rb +32 -0
- data/lib/jiminy/rspec.rb +3 -0
- data/lib/jiminy/setup.rb +10 -0
- data/lib/jiminy/templates/reporting/comment_header.md.erb +5 -0
- data/lib/jiminy/templates/reporting/n_plus_one.md.erb +12 -0
- data/lib/jiminy/version.rb +5 -0
- data/lib/jiminy.rb +13 -0
- metadata +254 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "provider_configuration"
|
4
|
+
|
5
|
+
require_relative "circle_ci/base"
|
6
|
+
require_relative "circle_ci/api_request"
|
7
|
+
require_relative "circle_ci/artifact"
|
8
|
+
require_relative "circle_ci/job"
|
9
|
+
require_relative "circle_ci/pipeline"
|
10
|
+
require_relative "circle_ci/vcs"
|
11
|
+
require_relative "circle_ci/workflow"
|
12
|
+
|
13
|
+
module Jiminy
|
14
|
+
module Reporting
|
15
|
+
module CIProviders
|
16
|
+
module CircleCI
|
17
|
+
# Not currently used, but kept for future use.
|
18
|
+
class Configuration < ProviderConfiguration
|
19
|
+
PR_URL_MATCHERS = %r{github\.com/
|
20
|
+
(?<username>[\w\-_]+)/
|
21
|
+
(?<reponame>[\w\-_]+)/
|
22
|
+
pull/
|
23
|
+
(?<pr_number>\d+)}x.freeze
|
24
|
+
|
25
|
+
def repo_path
|
26
|
+
[project_username, project_reponame].join("/")
|
27
|
+
end
|
28
|
+
|
29
|
+
def pr_number
|
30
|
+
match_data[:pr_number]
|
31
|
+
end
|
32
|
+
|
33
|
+
def project_username
|
34
|
+
match_data[:username]
|
35
|
+
end
|
36
|
+
|
37
|
+
def project_reponame
|
38
|
+
match_data[:reponame]
|
39
|
+
end
|
40
|
+
|
41
|
+
def github_token
|
42
|
+
ensure_env_variable("GITHUB_TOKEN")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def match_data
|
48
|
+
@_match_data ||= ensure_env_variable("CIRCLE_PULL_REQUEST").match(PR_URL_MATCHERS)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "provider_configuration"
|
4
|
+
|
5
|
+
module Jiminy
|
6
|
+
module Reporting
|
7
|
+
module CIProviders
|
8
|
+
module Github
|
9
|
+
class Configuration < ProviderConfiguration
|
10
|
+
def repo_path
|
11
|
+
ensure_configuration(:repo_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def project_username
|
15
|
+
repo_path.to_s.split("/").first
|
16
|
+
end
|
17
|
+
|
18
|
+
def project_reponame
|
19
|
+
repo_path.to_s.split("/").last
|
20
|
+
end
|
21
|
+
|
22
|
+
def github_token
|
23
|
+
ensure_configuration(:github_token)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
module CIProviders
|
6
|
+
module Local
|
7
|
+
class Artifact
|
8
|
+
attr_accessor :url
|
9
|
+
|
10
|
+
def self.all
|
11
|
+
Dir[Jiminy.configuration.temp_file_location].map do |filepath|
|
12
|
+
new(url: filepath)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(attributes)
|
17
|
+
attributes.each do |key, value|
|
18
|
+
public_send(:"#{key}=", value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "provider_configuration"
|
4
|
+
require_relative "local/artifact"
|
5
|
+
|
6
|
+
module Jiminy
|
7
|
+
module Reporting
|
8
|
+
module CIProviders
|
9
|
+
module Local
|
10
|
+
class Configuration < ProviderConfiguration
|
11
|
+
def repo_path
|
12
|
+
ensure_configuration(:repo_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def project_username
|
16
|
+
repo_path.to_s.split("/").first
|
17
|
+
end
|
18
|
+
|
19
|
+
def project_reponame
|
20
|
+
repo_path.to_s.split("/").last
|
21
|
+
end
|
22
|
+
|
23
|
+
def github_token
|
24
|
+
ensure_configuration(:github_token)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
module CIProviders
|
6
|
+
class ProviderConfiguration
|
7
|
+
TEMPLATE_METHOD_PROC = -> { raise NotImplementedError, "Define #{__callee__} in #{self}" }
|
8
|
+
|
9
|
+
define_method(:repo_path, TEMPLATE_METHOD_PROC)
|
10
|
+
|
11
|
+
define_method(:project_username, TEMPLATE_METHOD_PROC)
|
12
|
+
|
13
|
+
define_method(:project_reponame, TEMPLATE_METHOD_PROC)
|
14
|
+
|
15
|
+
define_method(:github_token, TEMPLATE_METHOD_PROC)
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def ensure_configuration(name)
|
20
|
+
value = Jiminy.config.public_send(name)
|
21
|
+
return value unless value.empty?
|
22
|
+
|
23
|
+
raise("Please provide a value for Jiminy.config.#{name}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
module CIProviders
|
6
|
+
require_relative "ci_providers/provider_configuration"
|
7
|
+
require_relative "ci_providers/circle_ci"
|
8
|
+
require_relative "ci_providers/github"
|
9
|
+
require_relative "ci_providers/local"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
class NPlusOne
|
6
|
+
require "erb"
|
7
|
+
|
8
|
+
# https://docs.ruby-lang.org/en/2.3.0/ERB.html#method-c-new
|
9
|
+
ERB_SAFE_LEVEL = nil
|
10
|
+
|
11
|
+
TRIM_MODE = "-"
|
12
|
+
|
13
|
+
attr_reader :file, :line, :method, :examples
|
14
|
+
|
15
|
+
attr_accessor :blob_url
|
16
|
+
|
17
|
+
def initialize(file:, line:, method:, examples: [])
|
18
|
+
@examples = examples
|
19
|
+
@file = file
|
20
|
+
@line = line
|
21
|
+
@method = method
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_markdown
|
25
|
+
ERB.new(markdown_template, trim_mode: TRIM_MODE).result(binding)
|
26
|
+
end
|
27
|
+
|
28
|
+
def blob_url_with_line
|
29
|
+
"#{blob_url}#L#{line}"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def markdown_template
|
35
|
+
@_markdown_template ||= File.read(File.join(TEMPLATES_DIR, "n_plus_one.md.erb"))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
module Reporters
|
6
|
+
class BaseReporter
|
7
|
+
def initialize(header:, body:)
|
8
|
+
@header = header
|
9
|
+
@body = body
|
10
|
+
end
|
11
|
+
|
12
|
+
def report!
|
13
|
+
raise NotImplementedError, "Please define #{report!} in #{self}"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :header, :body
|
19
|
+
|
20
|
+
def report_body
|
21
|
+
"#{header}\n\n#{body}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
module Reporters
|
6
|
+
class GithubReporter < BaseReporter
|
7
|
+
require "jiminy/github_apiable"
|
8
|
+
|
9
|
+
include GithubAPIable
|
10
|
+
|
11
|
+
def initialize(header:, body:, pr_number:)
|
12
|
+
super(header: header, body: body)
|
13
|
+
@pr_number = pr_number
|
14
|
+
end
|
15
|
+
|
16
|
+
def report!
|
17
|
+
client.add_comment(env_config.repo_path, pr_number, comment_body)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :pr_number
|
23
|
+
|
24
|
+
alias comment_body report_body
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
class YAMLFileCommentPresenter
|
6
|
+
require "open-uri"
|
7
|
+
require "yaml"
|
8
|
+
require "jiminy/github_apiable"
|
9
|
+
|
10
|
+
class MissingFileError < StandardError; end
|
11
|
+
|
12
|
+
include GithubAPIable
|
13
|
+
|
14
|
+
INSTANCE_SEPARATOR = "\n"
|
15
|
+
|
16
|
+
def initialize(source_filepath:, pr_number:)
|
17
|
+
@source_filepath = source_filepath
|
18
|
+
@pr_number = pr_number
|
19
|
+
end
|
20
|
+
|
21
|
+
def comment_body
|
22
|
+
@_comment_body ||= build_comment_body
|
23
|
+
end
|
24
|
+
|
25
|
+
alias to_s comment_body
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :source_filepath, :pr_number
|
30
|
+
|
31
|
+
def instances
|
32
|
+
@_instances ||= YAML.safe_load(file_content).map do |hash|
|
33
|
+
options = hash.values.first.transform_keys!(&:to_sym)
|
34
|
+
NPlusOne.new(**options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def file_content
|
39
|
+
@_file_content ||= file_content_for_filepath(source_filepath)
|
40
|
+
end
|
41
|
+
|
42
|
+
def file_content_for_filepath(source_filepath)
|
43
|
+
return file_content_for_remote_file(source_filepath) if source_filepath.start_with?("https://")
|
44
|
+
|
45
|
+
file_content_for_local_file(source_filepath)
|
46
|
+
end
|
47
|
+
|
48
|
+
def file_content_for_remote_file(source_filepath)
|
49
|
+
URI.parse(source_filepath).open({ "Circle-Token" => ENV["CIRCLE_CI_API_TOKEN"] }).read
|
50
|
+
end
|
51
|
+
|
52
|
+
def file_content_for_local_file(source_filepath)
|
53
|
+
File.read(source_filepath)
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_comment_body
|
57
|
+
instances.map do |instance|
|
58
|
+
file = file_from_instance(instance)
|
59
|
+
instance.blob_url = file.blob_url
|
60
|
+
instance.to_markdown
|
61
|
+
end.join(INSTANCE_SEPARATOR)
|
62
|
+
end
|
63
|
+
|
64
|
+
def file_from_instance(instance)
|
65
|
+
client.pull_request_files(env_config.repo_path, pr_number).detect do |file|
|
66
|
+
file.filename == instance.file
|
67
|
+
end or raise(MissingFileError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jiminy
|
4
|
+
module Reporting
|
5
|
+
require_relative "reporting/n_plus_one"
|
6
|
+
require_relative "reporting/yaml_file_comment_presenter"
|
7
|
+
require_relative "reporting/ci_providers"
|
8
|
+
require_relative "reporting/reporters"
|
9
|
+
|
10
|
+
TEMPLATES_DIR = File.expand_path("templates/reporting", __dir__).freeze
|
11
|
+
|
12
|
+
COMMENT_HEADER = ERB.new(File.read(File.join(TEMPLATES_DIR, "comment_header.md.erb"))).result.freeze
|
13
|
+
|
14
|
+
LINE_SEPARATOR = "\n"
|
15
|
+
|
16
|
+
module_function
|
17
|
+
|
18
|
+
def report!(*yaml_files, **options)
|
19
|
+
return if yaml_files.empty?
|
20
|
+
|
21
|
+
comment_content = yaml_files.map do |yaml_file|
|
22
|
+
YAMLFileCommentPresenter.new(source_filepath: yaml_file, pr_number: options[:pr_number]).to_s
|
23
|
+
end.join(LINE_SEPARATOR)
|
24
|
+
if options[:dry_run]
|
25
|
+
Reporters::DryRunReporter.new(header: COMMENT_HEADER, body: comment_content).report!
|
26
|
+
else
|
27
|
+
Reporters::GithubReporter.new(header: COMMENT_HEADER, body: comment_content,
|
28
|
+
pr_number: options[:pr_number]).report!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/jiminy/rspec.rb
ADDED
data/lib/jiminy/setup.rb
ADDED
data/lib/jiminy.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "jiminy/version"
|
4
|
+
require_relative "jiminy/setup"
|
5
|
+
require_relative "jiminy/recording" if defined?(Rails)
|
6
|
+
|
7
|
+
module Jiminy
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def reset_results_file!
|
11
|
+
Jiminy::Recording.reset_results_file!
|
12
|
+
end
|
13
|
+
end
|