pig-ci-rails 0.2.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/changelog.yml +42 -0
- data/.github/workflows/gempush.yml +6 -17
- data/.github/workflows/rspec.yml +27 -0
- data/.github/workflows/standard.yml +27 -0
- data/CHANGELOG.md +138 -26
- data/Gemfile +1 -1
- data/README.md +43 -35
- data/Rakefile +2 -2
- data/config/locales/pig_ci/en.yml +1 -6
- data/lib/pig_ci.rb +37 -37
- data/lib/pig_ci/configuration.rb +2 -2
- data/lib/pig_ci/decorator.rb +1 -1
- data/lib/pig_ci/decorator/report_terminal_decorator.rb +3 -3
- data/lib/pig_ci/metric.rb +2 -2
- data/lib/pig_ci/metric/current.rb +1 -1
- data/lib/pig_ci/metric/historial/change_percentage.rb +1 -1
- data/lib/pig_ci/metric/historical.rb +5 -5
- data/lib/pig_ci/profiler.rb +7 -7
- data/lib/pig_ci/profiler/memory.rb +1 -1
- data/lib/pig_ci/profiler_engine.rb +2 -2
- data/lib/pig_ci/profiler_engine/rails.rb +17 -12
- data/lib/pig_ci/report.rb +7 -7
- data/lib/pig_ci/report/memory.rb +1 -1
- data/lib/pig_ci/summary.rb +3 -3
- data/lib/pig_ci/summary/ci.rb +7 -7
- data/lib/pig_ci/summary/html.rb +8 -8
- data/lib/pig_ci/summary/terminal.rb +3 -3
- data/lib/pig_ci/test_frameworks.rb +3 -0
- data/lib/pig_ci/test_frameworks/rspec.rb +21 -0
- data/lib/pig_ci/version.rb +1 -1
- data/lib/pig_ci/views/index.erb +4 -2
- data/pig_ci.gemspec +34 -33
- metadata +37 -44
- data/.github/FUNDING.yml +0 -3
- data/.rubocop.yml +0 -24
- data/.ruby-version +0 -1
- data/.travis.yml +0 -27
- data/lib/pig_ci/api.rb +0 -14
- data/lib/pig_ci/api/reports.rb +0 -43
data/Rakefile
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
en:
|
2
2
|
pig_ci:
|
3
|
-
api:
|
4
|
-
reports:
|
5
|
-
success: 'PigCI: Successfully submitted report to pigci.com'
|
6
|
-
error: 'Unable to connect to PigCI API: %{error}'
|
7
|
-
api_error: 'Unable to connect to PigCI API'
|
8
3
|
summary:
|
9
4
|
ci_start: 'PigCI Thresholds Summary:'
|
10
5
|
ci_failure: 'PigCI: This commit has exceeded the thresholds defined in PigCI.thresholds'
|
@@ -13,7 +8,7 @@ en:
|
|
13
8
|
view_historic_reports: 'Historic Reports'
|
14
9
|
footer_html: |
|
15
10
|
<p>Generated <time datetime="%{generated_at}">%{generated_at}</time> by PigCI.</p>
|
16
|
-
<p>Support <a href="https://pigci.
|
11
|
+
<p>Support <a href="https://pigci.mikerogers.io/" target="_blank" rel="noopener">PigCI</a> by <a href="https://www.buymeacoffee.com/MikeRogers0" target="_blank" rel="noopener">buying me a coffee</a>.</p>
|
17
12
|
|
18
13
|
report:
|
19
14
|
memory:
|
data/lib/pig_ci.rb
CHANGED
@@ -1,30 +1,42 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
require "rake"
|
4
|
+
|
5
|
+
require "pig_ci/version"
|
6
|
+
require "pig_ci/configuration"
|
7
|
+
require "pig_ci/decorator"
|
8
|
+
require "pig_ci/summary"
|
9
|
+
require "pig_ci/profiler_engine"
|
10
|
+
require "pig_ci/profiler"
|
11
|
+
require "pig_ci/metric"
|
12
|
+
require "pig_ci/report"
|
13
|
+
require "pig_ci/test_frameworks"
|
14
14
|
|
15
15
|
module PigCI
|
16
16
|
extend self
|
17
17
|
|
18
18
|
attr_accessor :pid
|
19
19
|
|
20
|
+
attr_writer :enabled
|
21
|
+
def enabled?
|
22
|
+
@enabled.nil? ? true : @enabled
|
23
|
+
end
|
24
|
+
|
25
|
+
# Rails caches repeated queries within the same request. You can not count
|
26
|
+
# any cached queries if you'd like.
|
27
|
+
attr_writer :ignore_cached_queries
|
28
|
+
def ignore_cached_queries?
|
29
|
+
@ignore_cached_queries.nil? ? false : @ignore_cached_queries
|
30
|
+
end
|
31
|
+
|
20
32
|
attr_writer :tmp_directory
|
21
33
|
def tmp_directory
|
22
|
-
@tmp_directory || Pathname.new(Dir.getwd).join(
|
34
|
+
@tmp_directory || Pathname.new(Dir.getwd).join("tmp", "pig-ci")
|
23
35
|
end
|
24
36
|
|
25
37
|
attr_writer :output_directory
|
26
38
|
def output_directory
|
27
|
-
@output_directory || Pathname.new(Dir.getwd).join(
|
39
|
+
@output_directory || Pathname.new(Dir.getwd).join("pig-ci")
|
28
40
|
end
|
29
41
|
|
30
42
|
attr_accessor :generate_terminal_summary
|
@@ -88,29 +100,19 @@ module PigCI
|
|
88
100
|
@profiler_engine ||= PigCI::ProfilerEngine::Rails.new
|
89
101
|
end
|
90
102
|
|
91
|
-
attr_writer :api_base_uri
|
92
|
-
def api_base_uri
|
93
|
-
@api_base_uri || 'https://api.pigci.com/api'
|
94
|
-
end
|
95
|
-
|
96
|
-
attr_accessor :api_verify_ssl
|
97
|
-
def api_verify_ssl?
|
98
|
-
!@api_verify_ssl.nil? ? @api_verify_ssl : true
|
99
|
-
end
|
100
|
-
|
101
|
-
attr_accessor :api_key
|
102
|
-
def api_key?
|
103
|
-
!@api_key.nil? && @api_key != ''
|
104
|
-
end
|
105
|
-
|
106
103
|
attr_writer :commit_sha1
|
107
104
|
def commit_sha1
|
108
|
-
@commit_sha1 || ENV[
|
105
|
+
@commit_sha1 || ENV["CI_COMMIT_ID"] || ENV["CIRCLE_SHA1"] || ENV["TRAVIS_COMMIT"] || `git rev-parse HEAD`.strip
|
109
106
|
end
|
110
107
|
|
111
108
|
attr_writer :head_branch
|
112
109
|
def head_branch
|
113
|
-
@head_branch || ENV[
|
110
|
+
@head_branch || ENV["CI_BRANCH"] || ENV["CIRCLE_BRANCH"] || ENV["TRAVIS_BRANCH"] || `git rev-parse --abbrev-ref HEAD`.strip
|
111
|
+
end
|
112
|
+
|
113
|
+
# Throw deprecation notice for setting API
|
114
|
+
def api_key=(value)
|
115
|
+
puts "DEPRECATED: PigCI.com API has been retired, you no longer need to set config.api_key in your spec/rails_helper.rb file."
|
114
116
|
end
|
115
117
|
|
116
118
|
attr_writer :locale
|
@@ -130,8 +132,9 @@ module PigCI
|
|
130
132
|
|
131
133
|
def start(&block)
|
132
134
|
self.pid = Process.pid
|
135
|
+
PigCI::TestFrameworks::Rspec.configure! if defined?(::RSpec)
|
133
136
|
|
134
|
-
block
|
137
|
+
block&.call(self)
|
135
138
|
|
136
139
|
# Add our translations
|
137
140
|
load_i18ns!
|
@@ -146,7 +149,7 @@ module PigCI
|
|
146
149
|
|
147
150
|
def load_i18ns!
|
148
151
|
I18n.available_locales << PigCI.locale
|
149
|
-
I18n.load_path += Dir["#{File.expand_path(
|
152
|
+
I18n.load_path += Dir["#{File.expand_path("../config/locales/pig_ci", __dir__)}/*.{rb,yml}"]
|
150
153
|
end
|
151
154
|
|
152
155
|
def run_exit_tasks!
|
@@ -161,9 +164,6 @@ module PigCI
|
|
161
164
|
# Save the report summary to the project root.
|
162
165
|
PigCI::Summary::HTML.new(reports: profiler_engine.reports).save! if PigCI.generate_html_summary?
|
163
166
|
|
164
|
-
# If they have an API key, share it with PigCI.com
|
165
|
-
PigCI::Api::Reports.new(reports: profiler_engine.reports).share! if PigCI.api_key?
|
166
|
-
|
167
167
|
# Make sure CI fails when metrics are over thresholds.
|
168
168
|
PigCI::Summary::CI.new(reports: profiler_engine.reports).call!
|
169
169
|
end
|
data/lib/pig_ci/configuration.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class PigCI::Configuration
|
2
|
-
Thresholds = Struct.new(:memory, :request_time, :database_request)
|
2
|
+
Thresholds = Struct.new(:memory, :request_time, :database_request) {
|
3
3
|
def initialize(memory: 350, request_time: 250, database_request: 35)
|
4
4
|
super(memory, request_time, database_request)
|
5
5
|
end
|
6
|
-
|
6
|
+
}
|
7
7
|
end
|
data/lib/pig_ci/decorator.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "colorized_string"
|
2
2
|
|
3
3
|
class PigCI::Decorator::ReportTerminalDecorator < PigCI::Decorator
|
4
4
|
%i[key max min mean number_of_requests].each do |field|
|
@@ -8,9 +8,9 @@ class PigCI::Decorator::ReportTerminalDecorator < PigCI::Decorator
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def max_change_percentage
|
11
|
-
if @object[:max_change_percentage].start_with?(
|
11
|
+
if @object[:max_change_percentage].start_with?("-")
|
12
12
|
ColorizedString[@object[:max_change_percentage]].colorize(:green)
|
13
|
-
elsif @object[:max_change_percentage].start_with?(
|
13
|
+
elsif @object[:max_change_percentage].start_with?("0.0")
|
14
14
|
@object[:max_change_percentage]
|
15
15
|
else
|
16
16
|
ColorizedString[@object[:max_change_percentage]].colorize(:red)
|
data/lib/pig_ci/metric.rb
CHANGED
@@ -11,7 +11,7 @@ class PigCI::Metric::Historical::ChangePercentage
|
|
11
11
|
previous_run_data = previous_run_data_for_key(data[:key]) || data
|
12
12
|
|
13
13
|
data[:max_change_percentage] = (((BigDecimal(data[:max]) - BigDecimal(previous_run_data[:max])) / BigDecimal(previous_run_data[:max])) * 100).round(PigCI.max_change_percentage_precision)
|
14
|
-
data[:max_change_percentage] = BigDecimal(
|
14
|
+
data[:max_change_percentage] = BigDecimal("0") if data[:max_change_percentage].to_s == "NaN" || data[:max_change_percentage] == BigDecimal("-0.0")
|
15
15
|
data[:max_change_percentage] = data[:max_change_percentage].to_f
|
16
16
|
|
17
17
|
data
|
@@ -29,15 +29,15 @@ class PigCI::Metric::Historical
|
|
29
29
|
|
30
30
|
def remove_old_historical_data!
|
31
31
|
new_historical_data = @to_h
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
.sort_by { |timestamp, _data| timestamp.to_s.to_i * -1 }[0..(PigCI.historical_data_run_limit - 1)]
|
33
|
+
.to_h
|
34
|
+
.sort_by { |timestamp, _data| timestamp.to_s.to_i * -1 }.to_h
|
35
35
|
@to_h = new_historical_data
|
36
36
|
end
|
37
37
|
|
38
38
|
def read_historical_log_file
|
39
39
|
if File.exist?(@historical_log_file)
|
40
|
-
JSON.parse(File.open(@historical_log_file,
|
40
|
+
JSON.parse(File.open(@historical_log_file, "r").read, symbolize_names: true)
|
41
41
|
else
|
42
42
|
{}
|
43
43
|
end
|
@@ -49,4 +49,4 @@ class PigCI::Metric::Historical
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
require
|
52
|
+
require "pig_ci/metric/historial/change_percentage"
|
data/lib/pig_ci/profiler.rb
CHANGED
@@ -2,14 +2,14 @@ class PigCI::Profiler
|
|
2
2
|
attr_accessor :log_value, :log_file, :historical_log_file, :i18n_key
|
3
3
|
|
4
4
|
def initialize(i18n_key: nil, log_file: nil, historical_log_file: nil)
|
5
|
-
@i18n_key = i18n_key || self.class.name.underscore.split(
|
5
|
+
@i18n_key = i18n_key || self.class.name.underscore.split("/").last
|
6
6
|
@log_file = log_file || PigCI.tmp_directory.join("#{@i18n_key}.txt")
|
7
7
|
@historical_log_file = historical_log_file || PigCI.tmp_directory.join("#{@i18n_key}.json")
|
8
8
|
@log_value = 0
|
9
9
|
end
|
10
10
|
|
11
11
|
def setup!
|
12
|
-
File.open(log_file,
|
12
|
+
File.open(log_file, "w") { |file| file.truncate(0) }
|
13
13
|
end
|
14
14
|
|
15
15
|
def reset!
|
@@ -17,8 +17,8 @@ class PigCI::Profiler
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def log_request!(request_key)
|
20
|
-
File.open(log_file,
|
21
|
-
f.puts([request_key, log_value].join(
|
20
|
+
File.open(log_file, "a+") do |f|
|
21
|
+
f.puts([request_key, log_value].join("|"))
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -36,6 +36,6 @@ class PigCI::Profiler
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
require
|
40
|
-
require
|
41
|
-
require
|
39
|
+
require "pig_ci/profiler/memory"
|
40
|
+
require "pig_ci/profiler/request_time"
|
41
|
+
require "pig_ci/profiler/database_request"
|
@@ -8,7 +8,7 @@ class PigCI::ProfilerEngine
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def request_key?
|
11
|
-
!@request_key.nil? && @request_key !=
|
11
|
+
!@request_key.nil? && @request_key != ""
|
12
12
|
end
|
13
13
|
|
14
14
|
def request_captured?
|
@@ -37,4 +37,4 @@ class PigCI::ProfilerEngine
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
require
|
40
|
+
require "pig_ci/profiler_engine/rails"
|
@@ -31,10 +31,13 @@ class PigCI::ProfilerEngine::Rails < ::PigCI::ProfilerEngine
|
|
31
31
|
def precompile_assets!
|
32
32
|
# From: https://github.com/rails/sprockets-rails/blob/e9ca63edb6e658cdfcf8a35670c525b369c2ccca/test/test_railtie.rb#L7-L13
|
33
33
|
::Rails.application.load_tasks
|
34
|
-
::Rake.application[
|
34
|
+
::Rake.application["assets:precompile"].execute
|
35
35
|
end
|
36
36
|
|
37
37
|
def eager_load_rails!
|
38
|
+
# None of these methods will work pre-rails 5.
|
39
|
+
return unless ::Rails.version.to_f >= 5.0
|
40
|
+
|
38
41
|
# Eager load rails to give more accurate memory levels.
|
39
42
|
::Rails.application.eager_load!
|
40
43
|
::Rails.application.routes.eager_load!
|
@@ -45,30 +48,32 @@ class PigCI::ProfilerEngine::Rails < ::PigCI::ProfilerEngine
|
|
45
48
|
def make_blank_application_request!
|
46
49
|
# Make a call to the root path to load up as much of rails as possible
|
47
50
|
# Done within a timezone block as it affects the timezone.
|
48
|
-
Time.use_zone(
|
49
|
-
::Rails.application.call(::Rack::MockRequest.env_for(
|
51
|
+
Time.use_zone("UTC") do
|
52
|
+
::Rails.application.call(::Rack::MockRequest.env_for("/"))
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
53
56
|
def attach_listeners!
|
54
|
-
::ActiveSupport::Notifications.subscribe
|
57
|
+
::ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |_name, _started, _finished, _unique_id, payload|
|
55
58
|
request_key_from_payload!(payload)
|
56
59
|
|
57
60
|
profilers.each(&:reset!)
|
58
61
|
end
|
59
62
|
|
60
|
-
::ActiveSupport::Notifications.subscribe
|
61
|
-
if request_key?
|
62
|
-
profilers.select { |profiler| profiler.
|
63
|
+
::ActiveSupport::Notifications.subscribe "sql.active_record" do |_name, _started, _finished, _unique_id, payload|
|
64
|
+
if request_key? && PigCI.enabled? && (!PigCI.ignore_cached_queries? || (PigCI.ignore_cached_queries? && !payload[:cached]))
|
65
|
+
profilers.select { |profiler| profiler.instance_of?(PigCI::Profiler::DatabaseRequest) }.each(&:increment!)
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
66
|
-
::ActiveSupport::Notifications.subscribe
|
67
|
-
|
68
|
-
profiler
|
69
|
-
|
69
|
+
::ActiveSupport::Notifications.subscribe "process_action.action_controller" do |_name, _started, _finished, _unique_id, _payload|
|
70
|
+
if PigCI.enabled?
|
71
|
+
profilers.each do |profiler|
|
72
|
+
profiler.log_request!(request_key)
|
73
|
+
end
|
70
74
|
|
71
|
-
|
75
|
+
request_captured!
|
76
|
+
end
|
72
77
|
self.request_key = nil
|
73
78
|
end
|
74
79
|
end
|
data/lib/pig_ci/report.rb
CHANGED
@@ -2,7 +2,7 @@ class PigCI::Report
|
|
2
2
|
attr_accessor :historical_log_file, :i18n_key
|
3
3
|
|
4
4
|
def initialize(historical_log_file: nil, i18n_key: nil, timestamp: nil)
|
5
|
-
@i18n_key = i18n_key || self.class.name.underscore.split(
|
5
|
+
@i18n_key = i18n_key || self.class.name.underscore.split("/").last
|
6
6
|
@historical_log_file = historical_log_file || PigCI.tmp_directory.join("#{@i18n_key}.json")
|
7
7
|
@timestamp = timestamp || PigCI.run_timestamp
|
8
8
|
end
|
@@ -12,7 +12,7 @@ class PigCI::Report
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def i18n_name
|
15
|
-
I18n.t(
|
15
|
+
I18n.t(".name", scope: i18n_scope, locale: PigCI.locale)
|
16
16
|
end
|
17
17
|
|
18
18
|
def max_for(timestamp)
|
@@ -30,9 +30,9 @@ class PigCI::Report
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def sorted_and_formatted_data_for(timestamp)
|
33
|
-
data_for(timestamp)[@i18n_key.to_sym].sort_by
|
33
|
+
data_for(timestamp)[@i18n_key.to_sym].sort_by { |data|
|
34
34
|
PigCI.report_row_sort_by(data)
|
35
|
-
|
35
|
+
}.collect do |data|
|
36
36
|
self.class.format_row(data)
|
37
37
|
end
|
38
38
|
end
|
@@ -73,6 +73,6 @@ class PigCI::Report
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
require
|
77
|
-
require
|
78
|
-
require
|
76
|
+
require "pig_ci/report/memory"
|
77
|
+
require "pig_ci/report/request_time"
|
78
|
+
require "pig_ci/report/database_request"
|
data/lib/pig_ci/report/memory.rb
CHANGED
data/lib/pig_ci/summary.rb
CHANGED
data/lib/pig_ci/summary/ci.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "colorized_string"
|
2
2
|
|
3
3
|
class PigCI::Summary::CI < PigCI::Summary
|
4
4
|
def initialize(reports:)
|
@@ -7,8 +7,8 @@ class PigCI::Summary::CI < PigCI::Summary
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call!
|
10
|
-
puts
|
11
|
-
puts I18n.t(
|
10
|
+
puts ""
|
11
|
+
puts I18n.t("pig_ci.summary.ci_start")
|
12
12
|
|
13
13
|
over_threshold = false
|
14
14
|
@reports.each do |report|
|
@@ -17,22 +17,22 @@ class PigCI::Summary::CI < PigCI::Summary
|
|
17
17
|
end
|
18
18
|
|
19
19
|
fail_with_error! if over_threshold
|
20
|
-
puts
|
20
|
+
puts ""
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def fail_with_error!
|
26
|
-
puts I18n.t(
|
26
|
+
puts I18n.t("pig_ci.summary.ci_failure")
|
27
27
|
Kernel.exit(2)
|
28
28
|
end
|
29
29
|
|
30
30
|
def print_report(report)
|
31
31
|
max_and_threshold = [
|
32
32
|
report.max_for(@timestamp).to_s,
|
33
|
-
|
33
|
+
"/",
|
34
34
|
report.threshold
|
35
|
-
].join(
|
35
|
+
].join(" ")
|
36
36
|
|
37
37
|
if report.over_threshold_for?(@timestamp)
|
38
38
|
puts "#{report.i18n_name}: #{ColorizedString[max_and_threshold].colorize(:red)}\n"
|