pig-ci-rails 0.2.0 → 1.1.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.
- 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"
|