ci_runner 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ci_runner/check/base.rb +3 -1
- data/lib/ci_runner/check/buildkite.rb +88 -0
- data/lib/ci_runner/check/circle_ci.rb +2 -39
- data/lib/ci_runner/check/concurrent_download.rb +57 -0
- data/lib/ci_runner/cli.rb +49 -0
- data/lib/ci_runner/client/authenticated_buildkite.rb +67 -0
- data/lib/ci_runner/client/base.rb +8 -1
- data/lib/ci_runner/client/buildkite.rb +60 -0
- data/lib/ci_runner/client/github.rb +11 -0
- data/lib/ci_runner/configuration/user.rb +30 -0
- data/lib/ci_runner/test_failure.rb +3 -0
- data/lib/ci_runner/test_run_finder.rb +2 -0
- data/lib/ci_runner/version.rb +1 -1
- data/lib/ci_runner/version_verifier.rb +53 -0
- data/lib/ci_runner.rb +16 -11
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7834106a8db1f0770e5184dcf31fbfc892a92890b52f4709ab900ccd560f1b2
|
4
|
+
data.tar.gz: 69c9c0533ff5a52a1f97cb1f4374e012f56004a06a584b2b60ab80e4c9897517
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67d81327e046862ccb85ead5688aac13d9818d8ffc1487ef92001fba07ff6c6795dee7a537566e410c3c864ed0ee8e3712668310412cae9bbab3eddaeff52114
|
7
|
+
data.tar.gz: ac943446f8cf3ebd2a65c5d35e021aebcfee0cca6c5e693bafe7f4ea41b673bc669a3eabe1914eed70a98c646dc12d51dba12d14a066e6b51538a9bfc37318d8
|
data/lib/ci_runner/check/base.rb
CHANGED
@@ -53,8 +53,10 @@ module CIRunner
|
|
53
53
|
end
|
54
54
|
|
55
55
|
# @return [Boolean]
|
56
|
+
#
|
57
|
+
# @see https://docs.github.com/en/rest/commits/statuses#get-the-combined-status-for-a-specific-reference
|
56
58
|
def failed?
|
57
|
-
|
59
|
+
["error", "failure"].include?(status)
|
58
60
|
end
|
59
61
|
end
|
60
62
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
module CIRunner
|
7
|
+
module Check
|
8
|
+
# Check class used when a project is configured to run its CI using Buildkite.
|
9
|
+
class Buildkite < Base
|
10
|
+
include ConcurrentDownload
|
11
|
+
|
12
|
+
attr_reader :url # :private:
|
13
|
+
|
14
|
+
# @param args (See Base#initialize)
|
15
|
+
# @param url [String] The html URL pointing to the Buildkite build.
|
16
|
+
def initialize(*args, url)
|
17
|
+
super(*args)
|
18
|
+
|
19
|
+
@url = url
|
20
|
+
end
|
21
|
+
|
22
|
+
# Used to tell the user which CI provider we are downloading the log output from.
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
def provider
|
26
|
+
"Buildkite"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Download the CI logs for this Buildkite build.
|
30
|
+
#
|
31
|
+
# The Buildkite API scopes tokens per organizations (token generated for org A can't access
|
32
|
+
# resource on org B, even for public resources). This means that for opensource projects using
|
33
|
+
# Buildkite, users that are not members of the buildkite org normally can't use CI Runner.
|
34
|
+
#
|
35
|
+
# To bypass this problem, for builds that are public, CI Runner uses a different API.
|
36
|
+
# For private build, CI runner will check if the user had stored a Buildkite token in its config.
|
37
|
+
#
|
38
|
+
# @return [Tempfile]
|
39
|
+
def download_log
|
40
|
+
uri = URI(url)
|
41
|
+
_, org, pipeline, _, build = uri.path.split("/")
|
42
|
+
@client = Client::Buildkite.new
|
43
|
+
|
44
|
+
unless @client.public_build?(org, pipeline, build)
|
45
|
+
token = retrieve_token_from_config(org, url)
|
46
|
+
@client = Client::AuthenticatedBuildkite.new(token)
|
47
|
+
end
|
48
|
+
|
49
|
+
@client.job_logs(org, pipeline, build).each do |log_url|
|
50
|
+
@queue << log_url
|
51
|
+
end
|
52
|
+
|
53
|
+
process_queue
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @param url [String]
|
59
|
+
#
|
60
|
+
# @return [void]
|
61
|
+
def process(url)
|
62
|
+
@client.reset!
|
63
|
+
response = @client.download_log(url)
|
64
|
+
|
65
|
+
@tempfile.write(response.read)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Retrieve a Buildkite token from the user confg.
|
69
|
+
#
|
70
|
+
# @param organization [String] The organization that owns this buildkite build.
|
71
|
+
# @param url [String] The FQDN pointing to the buildkite build.
|
72
|
+
#
|
73
|
+
# @return [String] The token
|
74
|
+
#
|
75
|
+
# @raise [Error] If no token for that organization exists in the config.
|
76
|
+
def retrieve_token_from_config(organization, url)
|
77
|
+
token = Configuration::User.instance.buildkite_token(organization.downcase)
|
78
|
+
|
79
|
+
token || raise(Error, <<~EOM)
|
80
|
+
Can't get the log output from the Buildkite build #{url} because it requires authentication.
|
81
|
+
|
82
|
+
Please store a Buildkite token scoped to the organization #{organization} and retry.
|
83
|
+
See {{command:ci_runner help buildkite_token}}
|
84
|
+
EOM
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -4,7 +4,6 @@ require_relative "base"
|
|
4
4
|
require "uri"
|
5
5
|
require "open-uri"
|
6
6
|
require "json"
|
7
|
-
require "tempfile"
|
8
7
|
|
9
8
|
module CIRunner
|
10
9
|
module Check
|
@@ -54,6 +53,8 @@ module CIRunner
|
|
54
53
|
|
55
54
|
# Check class used when a project is configured to run its CI using CircleCI.
|
56
55
|
class CircleCI < Base
|
56
|
+
include ConcurrentDownload
|
57
|
+
|
57
58
|
attr_reader :url # :private:
|
58
59
|
|
59
60
|
# @param args (See Base#initialize)
|
@@ -62,8 +63,6 @@ module CIRunner
|
|
62
63
|
super(*args)
|
63
64
|
|
64
65
|
@url = url
|
65
|
-
@queue = Queue.new
|
66
|
-
@tempfile = Tempfile.new
|
67
66
|
end
|
68
67
|
|
69
68
|
# Used to tell the user which CI provider we are downloading the log output from.
|
@@ -102,37 +101,10 @@ module CIRunner
|
|
102
101
|
end
|
103
102
|
|
104
103
|
process_queue
|
105
|
-
|
106
|
-
@tempfile.tap(&:flush)
|
107
|
-
end
|
108
|
-
|
109
|
-
# @return [Boolean]
|
110
|
-
#
|
111
|
-
# @see https://docs.github.com/en/rest/commits/statuses#get-the-combined-status-for-a-specific-reference
|
112
|
-
def failed?
|
113
|
-
["error", "failure"].include?(status)
|
114
104
|
end
|
115
105
|
|
116
106
|
private
|
117
107
|
|
118
|
-
# Implement a queuing system in order to download log files in parallel.
|
119
|
-
#
|
120
|
-
# @return [void]
|
121
|
-
def process_queue
|
122
|
-
max_threads = 6
|
123
|
-
threads = []
|
124
|
-
|
125
|
-
max_threads.times do
|
126
|
-
threads << Thread.new do
|
127
|
-
while (element = dequeue)
|
128
|
-
process(element)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
threads.each(&:join)
|
134
|
-
end
|
135
|
-
|
136
108
|
# @param step [Step]
|
137
109
|
#
|
138
110
|
# @return [void]
|
@@ -144,15 +116,6 @@ module CIRunner
|
|
144
116
|
@tempfile.write(log_output)
|
145
117
|
end
|
146
118
|
|
147
|
-
# Dequeue a CircleCI Step from the queue.
|
148
|
-
#
|
149
|
-
# @return [Step, nil]
|
150
|
-
def dequeue
|
151
|
-
@queue.pop(true)
|
152
|
-
rescue ThreadError
|
153
|
-
nil
|
154
|
-
end
|
155
|
-
|
156
119
|
# The URL on the commit status will look something like: https://circleci.com/gh/owner/repo/1234?query_string.
|
157
120
|
# We want the `1234` which is the builder number.
|
158
121
|
#
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tempfile"
|
4
|
+
|
5
|
+
module CIRunner
|
6
|
+
module Check
|
7
|
+
# Module used to dowload multiple logfiles in parallel.
|
8
|
+
#
|
9
|
+
# Some CI providers doesn't have an API to download a single log file for the whole
|
10
|
+
# build, and instead one log file is produced per step. CI Runner needs to download
|
11
|
+
# the logfile of all steps in the build in order to rerun all test that failed.
|
12
|
+
module ConcurrentDownload
|
13
|
+
def initialize(...)
|
14
|
+
@queue = Queue.new
|
15
|
+
@tempfile = Tempfile.new
|
16
|
+
|
17
|
+
super(...)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Implement a queuing system in order to download log files in parallel.
|
23
|
+
#
|
24
|
+
# @return [void]
|
25
|
+
def process_queue
|
26
|
+
max_threads = 6
|
27
|
+
threads = []
|
28
|
+
|
29
|
+
max_threads.times do
|
30
|
+
threads << Thread.new do
|
31
|
+
while (element = dequeue)
|
32
|
+
process(element)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
threads.each(&:join)
|
38
|
+
|
39
|
+
@tempfile.tap(&:flush)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Process item in the queue.
|
43
|
+
def process
|
44
|
+
raise(NotImplementedError)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Dequeue a CircleCI Step from the queue.
|
48
|
+
#
|
49
|
+
# @return [Step, nil]
|
50
|
+
def dequeue
|
51
|
+
@queue.pop(true)
|
52
|
+
rescue ThreadError
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ci_runner/cli.rb
CHANGED
@@ -35,6 +35,7 @@ module CIRunner
|
|
35
35
|
def rerun
|
36
36
|
::CLI::UI::StdoutRouter.enable
|
37
37
|
|
38
|
+
check_for_new_version
|
38
39
|
runner = nil
|
39
40
|
|
40
41
|
::CLI::UI.frame("Preparing CI Runner") do
|
@@ -117,8 +118,56 @@ module CIRunner
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
121
|
+
desc "buildkite_token TOKEN ORGANIZATION", "Save a Buildkite token in your config."
|
122
|
+
long_desc <<~EOM
|
123
|
+
Save a personal access Buildkite token in the ~/.ci_runner/config.yml file.
|
124
|
+
Storing a Buildkite token is required to retrieve log from private Buildkite builds.
|
125
|
+
|
126
|
+
The ORGANIZATION, should be the name of the organization the token has access to.
|
127
|
+
|
128
|
+
You can get a token from Buildkite by following this link: https://buildkite.com/user/api-access-tokens/new?description=CI%20Runner&scopes[]=read_builds&scopes[]=read_build_logs
|
129
|
+
EOM
|
130
|
+
def buildkite_token(token, organization)
|
131
|
+
::CLI::UI::StdoutRouter.enable
|
132
|
+
|
133
|
+
required_scopes = ["read_builds", "read_build_logs"]
|
134
|
+
token_scopes = Client::AuthenticatedBuildkite.new(token).access_token["scopes"]
|
135
|
+
missing_scopes = required_scopes - token_scopes
|
136
|
+
|
137
|
+
if missing_scopes.empty?
|
138
|
+
Configuration::User.instance.save_buildkite_token(token, organization)
|
139
|
+
|
140
|
+
::CLI::UI.puts(<<~EOM)
|
141
|
+
{{success:Your token is valid!}}
|
142
|
+
|
143
|
+
{{info:The token has been saved in this file: #{Configuration::User.instance.config_file}}}
|
144
|
+
EOM
|
145
|
+
else
|
146
|
+
::CLI::UI.puts("{{red:\nYour token is missing required scope(s): #{missing_scopes.join(",")}")
|
147
|
+
end
|
148
|
+
rescue Client::Error => e
|
149
|
+
::CLI::UI.puts("{{red:\nYour token doesn't seem to be valid. The response from Buildkite was: #{e.message}}}")
|
150
|
+
|
151
|
+
exit(false)
|
152
|
+
end
|
153
|
+
|
120
154
|
private
|
121
155
|
|
156
|
+
# Inform the user of a possible new CI Runner version.
|
157
|
+
#
|
158
|
+
# @return [void]
|
159
|
+
def check_for_new_version
|
160
|
+
version_verifier = VersionVerifier.new
|
161
|
+
return unless version_verifier.new_ci_runner_version?
|
162
|
+
|
163
|
+
::CLI::UI.puts(<<~EOM)
|
164
|
+
{{info:A newer version of CI Runner is available (#{version_verifier.upstream_version}).}}
|
165
|
+
{{info:You can update CI Runner by running}} {{command:gem update ci_runner}}
|
166
|
+
EOM
|
167
|
+
rescue StandardError
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
122
171
|
# Retrieve all the GitHub CI checks for a given commit. Will be used to interactively prompt
|
123
172
|
# the user which one to rerun.
|
124
173
|
#
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require "stringio"
|
5
|
+
|
6
|
+
module CIRunner
|
7
|
+
module Client
|
8
|
+
# Client used to retrieve private Resources on buildkite.
|
9
|
+
#
|
10
|
+
# For public resources, the API can be used but only a limited number of users will be
|
11
|
+
# be able to access it as it requires a token scoped for the organization (most users
|
12
|
+
# working on opensource project aren't member of the organization they contribute to).
|
13
|
+
#
|
14
|
+
# @see https://forum.buildkite.community/t/api-access-to-public-builds/1425/2
|
15
|
+
# @see Client::Buildkite
|
16
|
+
#
|
17
|
+
class AuthenticatedBuildkite < Base
|
18
|
+
API_ENDPOINT = "api.buildkite.com"
|
19
|
+
|
20
|
+
# Retrieve URLs to download job logs for all steps.
|
21
|
+
#
|
22
|
+
# @param org [String] The organizatio name.
|
23
|
+
# @param pipeline [String] The pipeline name.
|
24
|
+
# @param number [Integer] The build number.
|
25
|
+
#
|
26
|
+
# @return [Array<String>] An array of URLs
|
27
|
+
#
|
28
|
+
# @see https://buildkite.com/docs/apis/rest-api/builds#get-a-build
|
29
|
+
def job_logs(org, pipeline, number)
|
30
|
+
build = get("/v2/organizations/#{org}/pipelines/#{pipeline}/builds/#{number}")
|
31
|
+
|
32
|
+
build["jobs"].map do |job|
|
33
|
+
job["raw_log_url"]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param url [String] A URL pointing to a log output resource.
|
38
|
+
#
|
39
|
+
# @return [StringIO]
|
40
|
+
#
|
41
|
+
# @see https://buildkite.com/docs/apis/rest-api/jobs#get-a-jobs-log-output
|
42
|
+
def download_log(url)
|
43
|
+
StringIO.new(get(url))
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get information about an access token. Used to check if the token has the correct scopes.
|
47
|
+
#
|
48
|
+
# @see https://buildkite.com/docs/apis/rest-api/access-token
|
49
|
+
#
|
50
|
+
# @return [Hash] See Buildkite doc
|
51
|
+
def access_token
|
52
|
+
get("/v2/access-token")
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Add authentication before making the request.
|
58
|
+
#
|
59
|
+
# @param request [Net::HTTPRequest] A subclass of Net::HTTPRequest.
|
60
|
+
#
|
61
|
+
# @return [void]
|
62
|
+
def authentication(request)
|
63
|
+
request["Authorization"] = "Bearer #{@access_token}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -24,6 +24,14 @@ module CIRunner
|
|
24
24
|
@client = client
|
25
25
|
end
|
26
26
|
|
27
|
+
# Set a new Client object.
|
28
|
+
# NET::HTTP is not threadsafe so each time we need to make requests concurrently we need to use a new client.
|
29
|
+
#
|
30
|
+
# @return [void]
|
31
|
+
def reset!
|
32
|
+
@client = self.class.default_client
|
33
|
+
end
|
34
|
+
|
27
35
|
private
|
28
36
|
|
29
37
|
# Add authentication before making the request.
|
@@ -32,7 +40,6 @@ module CIRunner
|
|
32
40
|
#
|
33
41
|
# @return [void]
|
34
42
|
def authentication(request)
|
35
|
-
raise(NotImplementedError, "Subclass responsability")
|
36
43
|
end
|
37
44
|
|
38
45
|
# Perform an authenticated GET request.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
require "open-uri"
|
5
|
+
|
6
|
+
module CIRunner
|
7
|
+
module Client
|
8
|
+
# Client used for public Buildkite resources.
|
9
|
+
# Allow any users to download log output for builds that are in organizations they
|
10
|
+
# are not a member of.
|
11
|
+
#
|
12
|
+
# This client doesn't use the official buildkite API. The data returned are not exactly the same.
|
13
|
+
class Buildkite < Base
|
14
|
+
API_ENDPOINT = "buildkite.com"
|
15
|
+
|
16
|
+
# Check if the build is public and can be accessed without authentication.
|
17
|
+
#
|
18
|
+
# @param org [String] The organizatio name.
|
19
|
+
# @param pipeline [String] The pipeline name.
|
20
|
+
# @param number [Integer] The build number.
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
def public_build?(org, pipeline, build_number)
|
24
|
+
job_logs(org, pipeline, build_number)
|
25
|
+
|
26
|
+
true
|
27
|
+
rescue Error => e
|
28
|
+
return false if e.error_code == 403
|
29
|
+
|
30
|
+
raise(e)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retrieve URL paths to download job logs for all steps.
|
34
|
+
#
|
35
|
+
# @param org [String] The organizatio name.
|
36
|
+
# @param pipeline [String] The pipeline name.
|
37
|
+
# @param number [Integer] The build number.
|
38
|
+
#
|
39
|
+
# @return [Array<String>] An array of URL paths
|
40
|
+
def job_logs(org, pipeline, build_number)
|
41
|
+
@build ||= get("/#{org}/#{pipeline}/builds/#{build_number}")
|
42
|
+
|
43
|
+
@build["jobs"].map do |job|
|
44
|
+
job["base_path"] + "/raw_log"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Download raw log output for a job.
|
49
|
+
#
|
50
|
+
# @param path [String] A URL path
|
51
|
+
#
|
52
|
+
# @return [Tempfile, IO] Depending on the size of the response. Quirk of URI.open.
|
53
|
+
def download_log(path)
|
54
|
+
redirection_url = get(path)
|
55
|
+
|
56
|
+
URI.open(redirection_url)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -22,6 +22,17 @@ module CIRunner
|
|
22
22
|
get("/user")
|
23
23
|
end
|
24
24
|
|
25
|
+
# Get the latest release of a repository.
|
26
|
+
#
|
27
|
+
# @param repository [String] The full repository name, including the owner (rails/rails)
|
28
|
+
#
|
29
|
+
# @return [Hash] See GitHub documentation.
|
30
|
+
#
|
31
|
+
# https://docs.github.com/en/rest/releases/releases#get-the-latest-release
|
32
|
+
def latest_release(repository)
|
33
|
+
get("/repos/#{repository}/releases/latest")
|
34
|
+
end
|
35
|
+
|
25
36
|
# Makes an API request to get the CI checks for the +commit+.
|
26
37
|
#
|
27
38
|
# @param repository [String] The full repository name, including the owner (rails/rails)
|
@@ -68,6 +68,36 @@ module CIRunner
|
|
68
68
|
save!(@yaml_config)
|
69
69
|
end
|
70
70
|
|
71
|
+
# Retrieve the stored Buildkite access token of the user that has access to the +organization+.
|
72
|
+
#
|
73
|
+
# @return [String, nil] Depending if the user ran the `ci_runner buildkite TOKEN ORGANIZATION` command.
|
74
|
+
def buildkite_token(organization)
|
75
|
+
@yaml_config.dig("buildkite", "tokens", organization.downcase)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Write the Buildkite token to the user configuration file.
|
79
|
+
#
|
80
|
+
# @param token [String] A valid Buildkite access token.
|
81
|
+
# @param organization [String] The name of the organization the token has access to.
|
82
|
+
#
|
83
|
+
# @return [void]
|
84
|
+
def save_buildkite_token(token, organization)
|
85
|
+
existing_tokens = @yaml_config.dig("buildkite", "tokens") || {}
|
86
|
+
existing_tokens[organization.downcase] = token
|
87
|
+
|
88
|
+
@yaml_config["buildkite"] = { "tokens" => existing_tokens }
|
89
|
+
|
90
|
+
save!(@yaml_config)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Pathname] The path of the CI Runner directory configuration.
|
94
|
+
#
|
95
|
+
# @example
|
96
|
+
# puts config_directory # ~/.ci_runner
|
97
|
+
def config_directory
|
98
|
+
config_file.dirname
|
99
|
+
end
|
100
|
+
|
71
101
|
# @return [Pathname] The path of the configuration file.
|
72
102
|
#
|
73
103
|
# @example
|
@@ -56,6 +56,9 @@ module CIRunner
|
|
56
56
|
|
57
57
|
regex = %r{.*/?(test/.*?)\Z}
|
58
58
|
unless path.to_s.match?(regex)
|
59
|
+
# TODO(on: '2022-09-17', to: "edouard-chin") Revisit this as it's too brittle.
|
60
|
+
# If a test file doesn't live the in the `test/` root folder, this will raise an error.
|
61
|
+
# I should instead warn the user and move on.
|
59
62
|
raise "Can't create a relative path."
|
60
63
|
end
|
61
64
|
|
@@ -129,6 +129,8 @@ module CIRunner
|
|
129
129
|
case uri.host
|
130
130
|
when "circleci.com"
|
131
131
|
Check::CircleCI.new(repository, commit, *commit_status.values_at("context", "state", "target_url"))
|
132
|
+
when "buildkite.com"
|
133
|
+
Check::Buildkite.new(repository, commit, *commit_status.values_at("context", "state", "target_url"))
|
132
134
|
else
|
133
135
|
Check::Unsupported.new(repository, commit, *commit_status.values_at("context", "state"))
|
134
136
|
end
|
data/lib/ci_runner/version.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module CIRunner
|
6
|
+
# Class used to check if a newer version of CI Runner has been released.
|
7
|
+
# This is used to inform the user to update its gem.
|
8
|
+
#
|
9
|
+
# The check only runs every week.
|
10
|
+
class VersionVerifier
|
11
|
+
SEVEN_DAYS = 86_400 * 7
|
12
|
+
|
13
|
+
# Check if the user is running the latest version of CI Runner.
|
14
|
+
#
|
15
|
+
# @return [Boolean]
|
16
|
+
def new_ci_runner_version?
|
17
|
+
return false unless check?
|
18
|
+
|
19
|
+
fetch_upstream_version
|
20
|
+
FileUtils.touch(last_checked)
|
21
|
+
|
22
|
+
upstream_version > Gem::Version.new(VERSION)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Makes a request to GitHub to get the latest release on the Edouard-chin/ci_runner repository
|
26
|
+
#
|
27
|
+
# @return [Gem::Version] An instance of Gem::Version
|
28
|
+
def upstream_version
|
29
|
+
@upstream_version ||= begin
|
30
|
+
release = Client::Github.new(Configuration::User.instance.github_token).latest_release("Edouard-chin/ci_runner")
|
31
|
+
|
32
|
+
Gem::Version.new(release["tag_name"].sub(/\Av/, ""))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
alias_method :fetch_upstream_version, :upstream_version
|
36
|
+
|
37
|
+
# Path of a file used to store when we last checked for a release.
|
38
|
+
#
|
39
|
+
# @return [Pathname]
|
40
|
+
def last_checked
|
41
|
+
Configuration::User.instance.config_directory.join("last-checked")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @return [Boolean] Whether we checked for a release in the 7 days.
|
47
|
+
def check?
|
48
|
+
Time.now > (File.stat(last_checked).mtime + SEVEN_DAYS)
|
49
|
+
rescue Errno::ENOENT
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/ci_runner.rb
CHANGED
@@ -6,22 +6,27 @@ require_relative "ci_runner/version"
|
|
6
6
|
module CIRunner
|
7
7
|
Error = Class.new(StandardError)
|
8
8
|
|
9
|
-
autoload :CLI,
|
10
|
-
autoload :GitHelper,
|
11
|
-
autoload :TestRunFinder,
|
12
|
-
autoload :LogDownloader,
|
13
|
-
autoload :TestFailure,
|
9
|
+
autoload :CLI, "ci_runner/cli"
|
10
|
+
autoload :GitHelper, "ci_runner/git_helper"
|
11
|
+
autoload :TestRunFinder, "ci_runner/test_run_finder"
|
12
|
+
autoload :LogDownloader, "ci_runner/log_downloader"
|
13
|
+
autoload :TestFailure, "ci_runner/test_failure"
|
14
|
+
autoload :VersionVerifier, "ci_runner/version_verifier"
|
14
15
|
|
15
16
|
module Check
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
17
|
+
autoload :Buildkite, "ci_runner/check/buildkite"
|
18
|
+
autoload :Github, "ci_runner/check/github"
|
19
|
+
autoload :CircleCI, "ci_runner/check/circle_ci"
|
20
|
+
autoload :Unsupported, "ci_runner/check/unsupported"
|
21
|
+
autoload :ConcurrentDownload, "ci_runner/check/concurrent_download"
|
19
22
|
end
|
20
23
|
|
21
24
|
module Client
|
22
|
-
autoload :Error,
|
23
|
-
autoload :Github,
|
24
|
-
autoload :CircleCI,
|
25
|
+
autoload :Error, "ci_runner/client/error"
|
26
|
+
autoload :Github, "ci_runner/client/github"
|
27
|
+
autoload :CircleCI, "ci_runner/client/circle_ci"
|
28
|
+
autoload :Buildkite, "ci_runner/client/buildkite"
|
29
|
+
autoload :AuthenticatedBuildkite, "ci_runner/client/authenticated_buildkite"
|
25
30
|
end
|
26
31
|
|
27
32
|
module Configuration
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ci_runner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Edouard Chin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cli-ui
|
@@ -113,11 +113,15 @@ files:
|
|
113
113
|
- exe/ci_runner
|
114
114
|
- lib/ci_runner.rb
|
115
115
|
- lib/ci_runner/check/base.rb
|
116
|
+
- lib/ci_runner/check/buildkite.rb
|
116
117
|
- lib/ci_runner/check/circle_ci.rb
|
118
|
+
- lib/ci_runner/check/concurrent_download.rb
|
117
119
|
- lib/ci_runner/check/github.rb
|
118
120
|
- lib/ci_runner/check/unsupported.rb
|
119
121
|
- lib/ci_runner/cli.rb
|
122
|
+
- lib/ci_runner/client/authenticated_buildkite.rb
|
120
123
|
- lib/ci_runner/client/base.rb
|
124
|
+
- lib/ci_runner/client/buildkite.rb
|
121
125
|
- lib/ci_runner/client/circle_ci.rb
|
122
126
|
- lib/ci_runner/client/error.rb
|
123
127
|
- lib/ci_runner/client/github.rb
|
@@ -131,6 +135,7 @@ files:
|
|
131
135
|
- lib/ci_runner/test_failure.rb
|
132
136
|
- lib/ci_runner/test_run_finder.rb
|
133
137
|
- lib/ci_runner/version.rb
|
138
|
+
- lib/ci_runner/version_verifier.rb
|
134
139
|
- lib/minitest/ci_runner_plugin.rb
|
135
140
|
homepage: https://github.com/Edouard-chin/ci_runner
|
136
141
|
licenses:
|