ci_runner 0.4.0 → 0.5.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/ci_runner.gemspec +3 -1
- data/lib/ci_runner/check/buildkite.rb +15 -8
- data/lib/ci_runner/check/circle_ci.rb +1 -1
- data/lib/ci_runner/check/concurrent_download.rb +1 -0
- data/lib/ci_runner/cli.rb +3 -3
- data/lib/ci_runner/client/github.rb +8 -4
- data/lib/ci_runner/configuration/project.rb +38 -0
- data/lib/ci_runner/runners/base.rb +2 -2
- data/lib/ci_runner/runners/minitest_runner.rb +3 -1
- data/lib/ci_runner/test_run_finder.rb +3 -3
- data/lib/ci_runner/version.rb +1 -1
- data/lib/minitest/ci_runner_plugin.rb +5 -0
- metadata +33 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 361a2617b4dfcf8a20ed8566286fed6b10c88f9618d712dfd939520e31ab3400
|
|
4
|
+
data.tar.gz: 39a23722fd556bac27f95fd5417bf0a6b3db5d736c6effbc4ea55e9025f91965
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e904af08558ae212fe8cf2ca471d56c74ff25a74c416c085478bd6f3e840cde12fd5e4f2e9a5444d00c6d92ee1ce1caf5b19d432a9294947a6451c6c19907bb
|
|
7
|
+
data.tar.gz: a1c780586d26f82d1524450cb930c4be253017f1d739b6be6504fe76907ec09283bd8ba6b542cca92850f9d33f437c4ed58b1c8fa2002c544b2f38c9995c44dc
|
data/ci_runner.gemspec
CHANGED
|
@@ -34,10 +34,12 @@ Gem::Specification.new do |spec|
|
|
|
34
34
|
spec.executables = ["ci_runner"]
|
|
35
35
|
spec.require_paths = ["lib"]
|
|
36
36
|
|
|
37
|
-
spec.add_dependency("cli-ui", "~>
|
|
37
|
+
spec.add_dependency("cli-ui", "~> 2.7")
|
|
38
|
+
spec.add_dependency("drb")
|
|
38
39
|
spec.add_dependency("rake", "~> 13.0")
|
|
39
40
|
spec.add_dependency("thor", "~> 1.2")
|
|
40
41
|
|
|
42
|
+
spec.add_development_dependency("base64")
|
|
41
43
|
spec.add_development_dependency("rspec", "~> 3.11")
|
|
42
44
|
spec.add_development_dependency("rubocop-shopify", "~> 2.8")
|
|
43
45
|
spec.add_development_dependency("webmock", "~> 3.14")
|
|
@@ -39,14 +39,18 @@ module CIRunner
|
|
|
39
39
|
def download_log
|
|
40
40
|
uri = URI(url)
|
|
41
41
|
_, org, pipeline, _, build = uri.path.split("/")
|
|
42
|
-
@
|
|
42
|
+
@token = nil
|
|
43
|
+
client = Client::Buildkite.new
|
|
43
44
|
|
|
44
|
-
unless
|
|
45
|
-
token = retrieve_token_from_config(org, url)
|
|
46
|
-
|
|
45
|
+
unless client.public_build?(org, pipeline, build)
|
|
46
|
+
@token = retrieve_token_from_config(org, url)
|
|
47
|
+
client = Client::AuthenticatedBuildkite.new(@token)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
# Remember the client class so each concurrent download can build its own instance in #process.
|
|
51
|
+
@client_class = client.class
|
|
52
|
+
|
|
53
|
+
client.job_logs(org, pipeline, build).each do |log_url|
|
|
50
54
|
@queue << log_url
|
|
51
55
|
end
|
|
52
56
|
|
|
@@ -55,14 +59,17 @@ module CIRunner
|
|
|
55
59
|
|
|
56
60
|
private
|
|
57
61
|
|
|
62
|
+
# Net::HTTP is not thread-safe, so each concurrent download gets its own client instance
|
|
63
|
+
# rather than sharing (and repeatedly resetting) a single one across threads.
|
|
64
|
+
#
|
|
58
65
|
# @param url [String]
|
|
59
66
|
#
|
|
60
67
|
# @return [void]
|
|
61
68
|
def process(url)
|
|
62
|
-
@
|
|
63
|
-
|
|
69
|
+
response = @client_class.new(@token).download_log(url)
|
|
70
|
+
content = response.read
|
|
64
71
|
|
|
65
|
-
@tempfile.write(
|
|
72
|
+
@write_mutex.synchronize { @tempfile.write(content) }
|
|
66
73
|
end
|
|
67
74
|
|
|
68
75
|
# Retrieve a Buildkite token from the user confg.
|
|
@@ -113,7 +113,7 @@ module CIRunner
|
|
|
113
113
|
parsed_response = JSON.parse(response.read)
|
|
114
114
|
log_output = parsed_response.map! { |res| res["message"] }.join
|
|
115
115
|
|
|
116
|
-
@tempfile.write(log_output)
|
|
116
|
+
@write_mutex.synchronize { @tempfile.write(log_output) }
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
# The URL on the commit status will look something like: https://circleci.com/gh/owner/repo/1234?query_string.
|
data/lib/ci_runner/cli.rb
CHANGED
|
@@ -29,9 +29,9 @@ module CIRunner
|
|
|
29
29
|
Please note that CI Runner will **not** ensure that the Git HEAD of your local repository matches
|
|
30
30
|
the commit that failed upstream.
|
|
31
31
|
EOM
|
|
32
|
-
option :commit, type: :string, desc: "The Git commit that was pushed to GitHub and has a failing CI. The HEAD commit of your local repository will be used by default."
|
|
33
|
-
option :repository, type: :string, desc: "The repository on which the CI failed. The repository will be infered from your git remote by default.", banner: "catanacorp/catana"
|
|
34
|
-
option :run_name, type: :string, desc: "The CI check you which to rerun in case multiple checks failed for a commit. CI Runner will prompt you by default."
|
|
32
|
+
option :commit, type: :string, desc: "The Git commit that was pushed to GitHub and has a failing CI. The HEAD commit of your local repository will be used by default."
|
|
33
|
+
option :repository, type: :string, desc: "The repository on which the CI failed. The repository will be infered from your git remote by default.", banner: "catanacorp/catana"
|
|
34
|
+
option :run_name, type: :string, desc: "The CI check you which to rerun in case multiple checks failed for a commit. CI Runner will prompt you by default."
|
|
35
35
|
def rerun
|
|
36
36
|
::CLI::UI::StdoutRouter.enable
|
|
37
37
|
|
|
@@ -45,16 +45,20 @@ module CIRunner
|
|
|
45
45
|
get("/repos/#{repository}/commits/#{commit}/check-runs")
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Makes an API request to get the Commit
|
|
48
|
+
# Makes an API request to get the combined Commit status for the +commit+.
|
|
49
|
+
#
|
|
50
|
+
# Unlike the "list commit statuses" endpoint (which is append-only and returns every status
|
|
51
|
+
# update), the combined status endpoint returns only the latest status per context, matching
|
|
52
|
+
# what GitHub displays. This avoids presenting duplicate checks to the user.
|
|
49
53
|
#
|
|
50
54
|
# @param repository [String] The full repository name, including the owner (rails/rails)
|
|
51
55
|
# @param commit [String] The Git commit that has been pushed to GitHub.
|
|
52
56
|
#
|
|
53
57
|
# @return [Hash] See GitHub documentation.
|
|
54
58
|
#
|
|
55
|
-
# @see https://docs.github.com/en/rest/commits/statuses#
|
|
56
|
-
def
|
|
57
|
-
get("/repos/#{repository}/commits/#{commit}/
|
|
59
|
+
# @see https://docs.github.com/en/rest/commits/statuses#get-the-combined-status-for-a-specific-reference
|
|
60
|
+
def commit_status(repository, commit)
|
|
61
|
+
get("/repos/#{repository}/commits/#{commit}/status")
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
# Makes two requests to get the CI log for a check run.
|
|
@@ -183,6 +183,44 @@ module CIRunner
|
|
|
183
183
|
value.nil? ? true : value
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
# Whether CI Runner should detect the Ruby version used on CI and re-run the tests with the
|
|
187
|
+
# matching Ruby from ~/.rubies.
|
|
188
|
+
#
|
|
189
|
+
# Set this to +false+ for projects whose gems are bound to a specific interpreter/gem path
|
|
190
|
+
# that CI Runner shouldn't override (e.g. a Nix- or shadowenv-managed monorepo). When false,
|
|
191
|
+
# the rerun uses the Ruby and gems of the environment CI Runner is invoked from.
|
|
192
|
+
#
|
|
193
|
+
# @return [Boolean] True by default.
|
|
194
|
+
#
|
|
195
|
+
# @example Storing this configuration
|
|
196
|
+
# `cat myproject/.github/ci_runner.yml`
|
|
197
|
+
#
|
|
198
|
+
# ---
|
|
199
|
+
# detect_ruby: false
|
|
200
|
+
def detect_ruby?
|
|
201
|
+
value = @yaml_config.dig("detect_ruby")
|
|
202
|
+
|
|
203
|
+
value.nil? ? true : value
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Whether CI Runner should detect the Gemfile used on CI and re-run the tests with it.
|
|
207
|
+
#
|
|
208
|
+
# Set this to +false+ for the same reasons as +detect_ruby?+: when the environment CI Runner
|
|
209
|
+
# runs in already provides the correct set of dependencies.
|
|
210
|
+
#
|
|
211
|
+
# @return [Boolean] True by default.
|
|
212
|
+
#
|
|
213
|
+
# @example Storing this configuration
|
|
214
|
+
# `cat myproject/.github/ci_runner.yml`
|
|
215
|
+
#
|
|
216
|
+
# ---
|
|
217
|
+
# detect_gemfile: false
|
|
218
|
+
def detect_gemfile?
|
|
219
|
+
value = @yaml_config.dig("detect_gemfile")
|
|
220
|
+
|
|
221
|
+
value.nil? ? true : value
|
|
222
|
+
end
|
|
223
|
+
|
|
186
224
|
# Main detection regex used to detect test failures.
|
|
187
225
|
#
|
|
188
226
|
# **Important** This regexp 3 named capture groups. The order doesn't matter.
|
|
@@ -49,11 +49,11 @@ module CIRunner
|
|
|
49
49
|
when seed_regex
|
|
50
50
|
@seed = first_matching_group(Regexp.last_match)
|
|
51
51
|
when ruby_detection_regex
|
|
52
|
-
@ruby_version = first_matching_group(Regexp.last_match)
|
|
52
|
+
@ruby_version = first_matching_group(Regexp.last_match) if Configuration::Project.instance.detect_ruby?
|
|
53
53
|
|
|
54
54
|
@buffer << line_no_ansi_color if buffering?
|
|
55
55
|
when gemfile_detection_regex
|
|
56
|
-
@gemfile = first_matching_group(Regexp.last_match)
|
|
56
|
+
@gemfile = first_matching_group(Regexp.last_match) if Configuration::Project.instance.detect_gemfile?
|
|
57
57
|
when buffer_detection_regex
|
|
58
58
|
if Configuration::Project.instance.process_on_new_match? && buffering?
|
|
59
59
|
process_buffer
|
|
@@ -66,12 +66,14 @@ module CIRunner
|
|
|
66
66
|
|
|
67
67
|
minitest_plugin_path = File.expand_path("../..", __dir__)
|
|
68
68
|
rake_load_path = Gem.loaded_specs["rake"].full_require_paths.first
|
|
69
|
+
drb_load_path = Gem.loaded_specs["drb"].full_require_paths.first
|
|
69
70
|
|
|
70
71
|
code = <<~EOM
|
|
71
72
|
Rake::TestTask.new(:__ci_runner_test) do |t|
|
|
72
73
|
t.libs << "test"
|
|
73
74
|
t.libs << "lib"
|
|
74
75
|
t.libs << "#{rake_load_path}"
|
|
76
|
+
t.libs << "#{drb_load_path}"
|
|
75
77
|
t.libs << "#{minitest_plugin_path}"
|
|
76
78
|
t.test_files = #{failures.map(&:path)}
|
|
77
79
|
t.ruby_opts = ["-W0"] if ENV["NO_WARNING"]
|
|
@@ -122,7 +124,7 @@ module CIRunner
|
|
|
122
124
|
#
|
|
123
125
|
# For failure, Minitest will print the location of the test file, but it can be wrong.
|
|
124
126
|
#
|
|
125
|
-
# MaintenanceTasks::RunsTest#test_run_a_CSV_Task [/home/runner/work/maintenance_tasks/maintenance_tasks/vendor/bundle/ruby/2.7.0/gems/capybara-3.37.1/lib/capybara/minitest.rb:295]
|
|
127
|
+
# MaintenanceTasks::RunsTest#test_run_a_CSV_Task [/home/runner/work/maintenance_tasks/maintenance_tasks/vendor/bundle/ruby/2.7.0/gems/capybara-3.37.1/lib/capybara/minitest.rb:295]
|
|
126
128
|
#
|
|
127
129
|
# In this case the location of the file points to a file inside a gem, which is for sure not where the test lives.
|
|
128
130
|
# When this happen, we discard the location provided by Minitest and try to match another possible location.
|
|
@@ -64,10 +64,10 @@ module CIRunner
|
|
|
64
64
|
# @see https://docs.github.com/en/rest/checks/runs#get-a-check-run
|
|
65
65
|
def other_ci(repository, commit)
|
|
66
66
|
github_client = Client::Github.new(Configuration::User.instance.github_token)
|
|
67
|
-
|
|
67
|
+
commit_status = github_client.commit_status(repository, commit)
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
check_class_from_url(
|
|
69
|
+
commit_status["statuses"].map do |status|
|
|
70
|
+
check_class_from_url(status, repository, commit)
|
|
71
71
|
end.compact
|
|
72
72
|
end
|
|
73
73
|
|
data/lib/ci_runner/version.rb
CHANGED
|
@@ -25,6 +25,11 @@ module Minitest
|
|
|
25
25
|
"#{failure.klass}##{failure.test_name}" == runnable
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
# Vanilla Minitest filters with `filter === runnable`, but the minitest-reporters gem's
|
|
30
|
+
# DelegateReporter calls `filter.match?(runnable)` instead. Alias the two so CI Runner
|
|
31
|
+
# works whether or not the project under test uses minitest-reporters.
|
|
32
|
+
alias_method :match?, :===
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
options[:filter] = filter.new(failures)
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ci_runner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Edouard Chin
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: cli-ui
|
|
@@ -16,14 +15,28 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
18
|
+
version: '2.7'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
25
|
+
version: '2.7'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: drb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
27
40
|
- !ruby/object:Gem::Dependency
|
|
28
41
|
name: rake
|
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,6 +65,20 @@ dependencies:
|
|
|
52
65
|
- - "~>"
|
|
53
66
|
- !ruby/object:Gem::Version
|
|
54
67
|
version: '1.2'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: base64
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
55
82
|
- !ruby/object:Gem::Dependency
|
|
56
83
|
name: rspec
|
|
57
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -146,7 +173,6 @@ metadata:
|
|
|
146
173
|
changelog_uri: https://github.com/Edouard-chin/ci_runner/blob/main/CHANGELOG.md
|
|
147
174
|
allowed_push_host: https://rubygems.org
|
|
148
175
|
rubygems_mfa_required: 'true'
|
|
149
|
-
post_install_message:
|
|
150
176
|
rdoc_options: []
|
|
151
177
|
require_paths:
|
|
152
178
|
- lib
|
|
@@ -161,8 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
161
187
|
- !ruby/object:Gem::Version
|
|
162
188
|
version: '0'
|
|
163
189
|
requirements: []
|
|
164
|
-
rubygems_version:
|
|
165
|
-
signing_key:
|
|
190
|
+
rubygems_version: 4.0.16
|
|
166
191
|
specification_version: 4
|
|
167
192
|
summary: Re-run failing tests from CI on your local machine without copy/pasting.
|
|
168
193
|
test_files: []
|