jay_api 27.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 +7 -0
- data/CHANGELOG.md +786 -0
- data/README.md +61 -0
- data/jay_api.gemspec +38 -0
- data/lib/jay_api/abstract/connection.rb +50 -0
- data/lib/jay_api/abstract/constant_wait.rb +17 -0
- data/lib/jay_api/abstract/geometric_wait.rb +35 -0
- data/lib/jay_api/abstract/wait_strategy.rb +43 -0
- data/lib/jay_api/configuration.rb +115 -0
- data/lib/jay_api/elasticsearch/async.rb +72 -0
- data/lib/jay_api/elasticsearch/batch_counter.rb +76 -0
- data/lib/jay_api/elasticsearch/client.rb +96 -0
- data/lib/jay_api/elasticsearch/client_factory.rb +100 -0
- data/lib/jay_api/elasticsearch/errors/elasticsearch_error.rb +13 -0
- data/lib/jay_api/elasticsearch/errors/end_of_query_results_error.rb +22 -0
- data/lib/jay_api/elasticsearch/errors/query_execution_error.rb +15 -0
- data/lib/jay_api/elasticsearch/errors/query_execution_failure.rb +17 -0
- data/lib/jay_api/elasticsearch/errors/query_execution_timeout.rb +13 -0
- data/lib/jay_api/elasticsearch/errors/search_after_error.rb +13 -0
- data/lib/jay_api/elasticsearch/index.rb +223 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/aggregation.rb +66 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/avg.rb +56 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/errors/aggregations_error.rb +17 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/errors.rb +14 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/filter.rb +67 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/max.rb +51 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/scripted_metric.rb +72 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/sum.rb +57 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/terms.rb +73 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/top_hits.rb +49 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations/value_count.rb +50 -0
- data/lib/jay_api/elasticsearch/query_builder/aggregations.rb +168 -0
- data/lib/jay_api/elasticsearch/query_builder/errors/query_builder_error.rb +16 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/bool.rb +179 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/exists.rb +33 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/match_all.rb +22 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/match_clauses.rb +140 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/match_none.rb +22 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/match_phrase.rb +35 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/negator.rb +42 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/query_clause.rb +17 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/query_string.rb +50 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/range.rb +49 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/regexp.rb +39 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/term.rb +37 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/terms.rb +37 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses/wildcard.rb +37 -0
- data/lib/jay_api/elasticsearch/query_builder/query_clauses.rb +163 -0
- data/lib/jay_api/elasticsearch/query_builder/script.rb +36 -0
- data/lib/jay_api/elasticsearch/query_builder.rb +196 -0
- data/lib/jay_api/elasticsearch/query_results.rb +111 -0
- data/lib/jay_api/elasticsearch/response.rb +43 -0
- data/lib/jay_api/elasticsearch/search_after_results.rb +58 -0
- data/lib/jay_api/elasticsearch/tasks.rb +36 -0
- data/lib/jay_api/elasticsearch/time.rb +18 -0
- data/lib/jay_api/errors/configuration_error.rb +22 -0
- data/lib/jay_api/errors/error.rb +8 -0
- data/lib/jay_api/git/errors/invalid_repository_error.rb +11 -0
- data/lib/jay_api/git/errors/missing_url_error.rb +13 -0
- data/lib/jay_api/git/gerrit/gitiles_helper.rb +58 -0
- data/lib/jay_api/git/repository.rb +356 -0
- data/lib/jay_api/id_builder.rb +52 -0
- data/lib/jay_api/mergeables/merge_selector/configuration.rb +29 -0
- data/lib/jay_api/mergeables/merge_selector/merger.rb +58 -0
- data/lib/jay_api/mergeables/merge_selector.rb +15 -0
- data/lib/jay_api/prior_version_fetcher_base.rb +66 -0
- data/lib/jay_api/properties_fetcher.rb +196 -0
- data/lib/jay_api/rspec/configuration.rb +46 -0
- data/lib/jay_api/rspec/git.rb +60 -0
- data/lib/jay_api/rspec/test_data_collector.rb +189 -0
- data/lib/jay_api/rspec.rb +9 -0
- data/lib/jay_api/version.rb +6 -0
- data/lib/jay_api.rb +9 -0
- metadata +215 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
|
6
|
+
module JayAPI
|
7
|
+
module Elasticsearch
|
8
|
+
# Represents Elasticsearch tasks. Returns information about the tasks
|
9
|
+
# currently executing in the cluster.
|
10
|
+
# TODO: Add #all [JAY-593]
|
11
|
+
class Tasks
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
# @param [JayAPI::Elasticsearch::Client] client The Elasticsearch Client
|
15
|
+
# object
|
16
|
+
def initialize(client:)
|
17
|
+
@client = client
|
18
|
+
end
|
19
|
+
|
20
|
+
# Retrieves info about the task with the passed +task_id+
|
21
|
+
# For more information on how to build the query please refer to the
|
22
|
+
# Elasticsearch DSL documentation:
|
23
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
|
24
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html#tasks-api-query-params
|
25
|
+
# @param [String] task_id The ID of the task whose info is needed
|
26
|
+
# @return [Hash] A Hash that details the results of the operation defined
|
27
|
+
# by +task_id+
|
28
|
+
# @example Returned Hash can be found in this method's unit tests
|
29
|
+
# @raise [Elasticsearch::Transport::Transport::ServerError] If the
|
30
|
+
# query fails.
|
31
|
+
def by_id(task_id)
|
32
|
+
client.task_by_id(task_id: task_id, wait_for_completion: true).deep_symbolize_keys
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JayAPI
|
4
|
+
module Elasticsearch
|
5
|
+
module Time
|
6
|
+
# Time format accepted by elasticsearch
|
7
|
+
TIME_FORMAT = '%Y/%m/%d %H:%M:%S'
|
8
|
+
|
9
|
+
# Transforms a Time object to a time format that is recognized by
|
10
|
+
# elasticsearch
|
11
|
+
# @param [Time] time The time to convert
|
12
|
+
# @return [String] The time converter to a string, parseable by ES
|
13
|
+
def format_time(time)
|
14
|
+
time.getutc.strftime(TIME_FORMAT)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'error'
|
4
|
+
|
5
|
+
module JayAPI
|
6
|
+
module Errors
|
7
|
+
# An error to be raised when there is an issue with the configuration of
|
8
|
+
# one of Jay's modules.
|
9
|
+
class ConfigurationError < JayAPI::Errors::Error
|
10
|
+
attr_reader :source_string
|
11
|
+
|
12
|
+
# Creates a new instance of the class.
|
13
|
+
# @param [String] message The error message
|
14
|
+
# @param [String] source_string The string from which the configuration
|
15
|
+
# was loaded.
|
16
|
+
def initialize(message, source_string = nil)
|
17
|
+
@source_string = source_string
|
18
|
+
super(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JayAPI
|
4
|
+
module Git
|
5
|
+
module Errors
|
6
|
+
# An error to be raised when an attempt is made to perform an action on a
|
7
|
+
# repository that is not yet initialized.
|
8
|
+
class InvalidRepositoryError < StandardError; end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../errors/error'
|
4
|
+
|
5
|
+
module JayAPI
|
6
|
+
module Git
|
7
|
+
module Errors
|
8
|
+
# An error to be raised when an attempt is made to execute a Git operation
|
9
|
+
# which requires a URL without providing one.
|
10
|
+
class MissingURLError < JayAPI::Errors::Error; end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module JayAPI
|
6
|
+
module Git
|
7
|
+
module Gerrit
|
8
|
+
# Offers a set of utility methods to work with Gerrit's Gitiles URLs.
|
9
|
+
module GitilesHelper
|
10
|
+
GITILES_PATH = '/plugins/gitiles/'
|
11
|
+
GITILES_REFSPEC = '/+/%<refspec>s/'
|
12
|
+
|
13
|
+
# Returns a Gitiles URL for the given parameters
|
14
|
+
# @param [String] repository The URL of the git repository.
|
15
|
+
# @param [String] refspec The name of a branch or the SHA1 of a particular
|
16
|
+
# commit.
|
17
|
+
# @param [String] path The path to the source file.
|
18
|
+
# @param [Integer, String] line_number The line number
|
19
|
+
# @return [String] The corresponding Gitiles URL.
|
20
|
+
def gitiles_url(repository:, refspec:, path:, line_number: nil)
|
21
|
+
# NOTE: Here File.join is being used because it takes care of cases in
|
22
|
+
# which both strings have slash (/) at their tips and removes the double
|
23
|
+
# slash, for example:
|
24
|
+
#
|
25
|
+
# ['https://example.com/', '/path/to/file'].join('/') => https://example.com///path/to/file
|
26
|
+
# File.join('https://example.com/', '/path/to/file') => https://example.com/path/to/file
|
27
|
+
#
|
28
|
+
# Do not use URL.join because it interprets a slash at the beginning
|
29
|
+
# of the second string as a reference to the URL's root:
|
30
|
+
#
|
31
|
+
# URI.join('https://www.example.com/hello/world', '/again') => https://www.example.com/again
|
32
|
+
|
33
|
+
@gitiles_urls ||= {}
|
34
|
+
base_url = @gitiles_urls[repository] ||= translate_gerrit_url(repository)
|
35
|
+
|
36
|
+
File.join(
|
37
|
+
base_url,
|
38
|
+
format(GITILES_REFSPEC, refspec: refspec),
|
39
|
+
[path, line_number].compact.join('#') # If there is no line number the # will not appear in the URL
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Translates a Gerrit repository URL into a Gerrit Gitiles URL for that
|
44
|
+
# repository, for example:
|
45
|
+
#
|
46
|
+
# ssh://jenkins@gerrit.local:29418/tools/elite becomes
|
47
|
+
# https://gerrit.local/plugins/gitiles/tools/elite
|
48
|
+
# @param [String] url Gerrit's repository URL
|
49
|
+
# @return [String] The corresponding Gitiles URL
|
50
|
+
def translate_gerrit_url(url)
|
51
|
+
uri = URI.parse(url)
|
52
|
+
path = uri.path.sub(%r{^/a/}, '/') # Removes the /a/ at the beginning of HTTP/S repository URLs
|
53
|
+
URI::HTTPS.build(host: uri.host, path: File.join(GITILES_PATH, path)).to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,356 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'git'
|
4
|
+
require 'logging'
|
5
|
+
require 'pathname'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
require_relative 'errors/invalid_repository_error'
|
9
|
+
require_relative 'errors/missing_url_error'
|
10
|
+
|
11
|
+
module JayAPI
|
12
|
+
module Git
|
13
|
+
# :reek:MissingSafeMethod safe method `update` is defined as alias of clone
|
14
|
+
|
15
|
+
# Represents a Git repository. Offers a set of methods to lazy clone a
|
16
|
+
# repository and update it when necessary. As well as a set of methods to
|
17
|
+
# work with the repository's branches and files.
|
18
|
+
class Repository
|
19
|
+
attr_reader :url, :clone_location, :clone_path
|
20
|
+
|
21
|
+
# Creates a new instance of the class.
|
22
|
+
# @param [String, nil] url The URL of the git repository. This parameter
|
23
|
+
# can be +nil+ *ONLY* for repositories that already exist.
|
24
|
+
# @param [String] clone_location The path to the location where the
|
25
|
+
# repository should be cloned. (A new folder with the Repository's name
|
26
|
+
# will be created INSIDE the given directory).
|
27
|
+
# @param [String] clone_path The path to the directory where the
|
28
|
+
# repository should be cloned. (The repository will be cloned DIRECTLY
|
29
|
+
# in this directory).
|
30
|
+
# @param [Logging::Logger] logger The logger for the class (as well as for
|
31
|
+
# the Git client).
|
32
|
+
def initialize(url:, clone_location: nil, clone_path: nil, logger: nil)
|
33
|
+
raise ArgumentError, 'Either clone_location or clone_path must be given' unless clone_location || clone_path
|
34
|
+
raise ArgumentError, 'Either clone_path or url must be given' unless url || clone_path
|
35
|
+
|
36
|
+
@url = url
|
37
|
+
@logger = logger || Logging.logger[self]
|
38
|
+
@clone_path = Pathname.new(clone_path || File.join(clone_location, name))
|
39
|
+
@clone_path = Pathname.pwd.join(@clone_path) unless @clone_path.absolute?
|
40
|
+
@clone_location = @clone_path.dirname
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Boolean] True if the repository directory exists on the disk,
|
44
|
+
# false otherwise
|
45
|
+
def exist?
|
46
|
+
clone_path.exist?
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Boolean] True if the repository directory directory exists and
|
50
|
+
# is a valid git repository, false otherwise.
|
51
|
+
def valid?
|
52
|
+
exist? && clone_path.directory? && git_dir.exist? && git_dir.directory?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Clones the repository or updates it, if it already exists.
|
56
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If no repository URL was
|
57
|
+
# provided.
|
58
|
+
def clone
|
59
|
+
valid? ? update! : clone!
|
60
|
+
end
|
61
|
+
|
62
|
+
alias update clone
|
63
|
+
|
64
|
+
# Clones the repository. If the repository already exists the directory is
|
65
|
+
# completely removed and the repository cloned again.
|
66
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
67
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If no repository URL was
|
68
|
+
# provided.
|
69
|
+
def clone!
|
70
|
+
clone_path.rmtree if exist?
|
71
|
+
clone_location.mkpath
|
72
|
+
clone_repo
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Updates the repository (without checking if it already exists or cloning
|
77
|
+
# it beforehand).
|
78
|
+
# @raise [ArgumentError] If the repository path doesn't exist.
|
79
|
+
# @raise [Git::GitExecuteError] If the repository path exists but it does
|
80
|
+
# not contain a valid git repository.
|
81
|
+
def update!
|
82
|
+
open_repo
|
83
|
+
repository.fetch(remote = repository.remotes.first.name)
|
84
|
+
repository.pull(remote, repository.current_branch)
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
# Checks out the specified commit. If the Repository is not yet initialized, then
|
89
|
+
# it will be opened or cloned depending on whether the repository exists.
|
90
|
+
# @param [String] commit The SHA1 representing the commit.
|
91
|
+
def checkout(commit)
|
92
|
+
open_or_clone
|
93
|
+
checkout!(commit)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Checks out the specified commit.
|
97
|
+
# @param [String] commit The SHA1 representing the commit.
|
98
|
+
def checkout!(commit)
|
99
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
100
|
+
|
101
|
+
repository.checkout commit
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [String] The name of the directory in which the repository
|
105
|
+
# was/will be cloned.
|
106
|
+
def name
|
107
|
+
@name ||= url ? directory_from_url : File.basename(clone_path)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns the Git object that correspond to the given reference (normally
|
111
|
+
# a Git::Object::Commit). If the Repository is not yet initialized then,
|
112
|
+
# if the repository exists it will be open if not it will be cloned.
|
113
|
+
# @param [String] objectish The reference to the object whose information
|
114
|
+
# should be retrieved. Normally this would be a SHA1 for a specific
|
115
|
+
# commit but it could also be a branch, a tag or any other valid git
|
116
|
+
# reference.
|
117
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
118
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned, or the
|
119
|
+
# given reference doesn't exist or is not a valid git object reference.
|
120
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
121
|
+
# exist yet and no repository URL was provided for the cloning.
|
122
|
+
def object(objectish)
|
123
|
+
open_or_clone
|
124
|
+
object!(objectish)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns the Git object that correspond to the given reference (normally
|
128
|
+
# a Git::Object::Commit).
|
129
|
+
# @param [String] objectish The reference to the object whose information
|
130
|
+
# should be retrieved. Normally this would be a SHA1 for a specific
|
131
|
+
# commit but it could also be a branch, a tag or any other valid git
|
132
|
+
# reference.
|
133
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
134
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
135
|
+
# open).
|
136
|
+
# @raise [Git::GitExecuteError] If the given reference doesn't exist or is
|
137
|
+
# not a valid git object reference.
|
138
|
+
def object!(objectish)
|
139
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
140
|
+
|
141
|
+
repository.object(objectish)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a Git::Branches object with the collection of branches in the
|
145
|
+
# repository.
|
146
|
+
# @return [Git::Branches] The collection of branches in the repository.
|
147
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
148
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
149
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
150
|
+
# exist yet and no repository URL was provided for the cloning.
|
151
|
+
def branches
|
152
|
+
open_or_clone
|
153
|
+
branches!
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns a Git::Branches object with the collection of branches in the
|
157
|
+
# repository.
|
158
|
+
# @return [Git::Branches] The collection of branches in the repository.
|
159
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
160
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
161
|
+
# open).
|
162
|
+
def branches!
|
163
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
164
|
+
|
165
|
+
repository.branches
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns an +Array+ of objects representing the Remote Repositories
|
169
|
+
# linked to the repository. The array may be empty but normally it
|
170
|
+
# contains at least one element (origin).
|
171
|
+
# @return [Array<Git::Remote>] The collection of remote repositories
|
172
|
+
# linked to the repository.
|
173
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
174
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
175
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
176
|
+
# exist yet and no repository URL was provided for the cloning.
|
177
|
+
def remotes
|
178
|
+
open_or_clone
|
179
|
+
remotes!
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns an +Array+ of objects representing the Remote Repositories
|
183
|
+
# linked to the repository. The array may be empty but normally it
|
184
|
+
# contains at least one element (origin).
|
185
|
+
# @return [Array<Git::Remote>] The collection of remote repositories
|
186
|
+
# linked to the repository.
|
187
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
188
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
189
|
+
# open).
|
190
|
+
def remotes!
|
191
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
192
|
+
|
193
|
+
repository.remotes
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [String, nil] The URL of the remote repository.
|
197
|
+
def remote_url
|
198
|
+
@remote_url ||= remotes.first&.url
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns a Git::Log object: The collection of commits in the current
|
202
|
+
# branch or under the specified reference (if given).
|
203
|
+
# @param [String] objectish A git reference. Normally this would be a
|
204
|
+
# branch's name but it could also be a tag, the SHA1 for a specific
|
205
|
+
# commit or any other valid git reference.
|
206
|
+
# @param [Integer] count The maximum number of commits to return in the
|
207
|
+
# collection, if omitted all the commits will be returned.
|
208
|
+
# @return [Git::Log] A Git::Log object with the collection of commits.
|
209
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
210
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
211
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
212
|
+
# exist yet and no repository URL was provided for the cloning.
|
213
|
+
def log(objectish: nil, count: nil)
|
214
|
+
open_or_clone
|
215
|
+
log!(objectish: objectish, count: count)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Returns a Git::Log object: The collection of commits in the current
|
219
|
+
# branch or under the specified reference (if given).
|
220
|
+
# @param [String] objectish A git reference. Normally this would be a
|
221
|
+
# branch's name but it could also be a tag, the SHA1 for a specific
|
222
|
+
# commit or any other valid git reference.
|
223
|
+
# @param [Integer] count The maximum number of commits to return in the
|
224
|
+
# collection, if omitted all the commits will be returned.
|
225
|
+
# @return [Git::Log] A Git::Log object with the collection of commits.
|
226
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
227
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
228
|
+
# open).
|
229
|
+
def log!(objectish: nil, count: nil)
|
230
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
231
|
+
|
232
|
+
objectish ? repository.gblob(objectish).log(count) : repository.log(count)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Adds a new worktree to the repository.
|
236
|
+
# @param [String] path The path where the worktree should be created.
|
237
|
+
# @param [String] branch The branch that should be checked out in the
|
238
|
+
# working tree. If no branch is provided the current HEAD will be
|
239
|
+
# checked out in a new branch.
|
240
|
+
# @return [Git::Worktree] The object representing the newly created
|
241
|
+
# worktree.
|
242
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
243
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned or if
|
244
|
+
# the worktree cannot be created.
|
245
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
246
|
+
# exist yet and no repository URL was provided for the cloning.
|
247
|
+
def add_worktree(path:, branch: nil)
|
248
|
+
open_or_clone
|
249
|
+
add_worktree!(path: path, branch: branch)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Adds a new worktree to the repository.
|
253
|
+
# @param [String] path The path where the worktree should be created.
|
254
|
+
# @param [String] branch The branch that should be checked out in the
|
255
|
+
# working tree. If no branch is provided the current HEAD will be
|
256
|
+
# checked out in a new branch.
|
257
|
+
# @return [Git::Worktree] The object representing the newly created
|
258
|
+
# worktree.
|
259
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
260
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
261
|
+
# open).
|
262
|
+
# @raise [Git::GitExecuteError] If the worktree cannot be created.
|
263
|
+
def add_worktree!(path:, branch: nil)
|
264
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
265
|
+
|
266
|
+
repository.worktree(path, branch).add
|
267
|
+
repository.worktree(path)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Returns the collection of worktrees linked to the repository.
|
271
|
+
# @return [Git::Worktrees] The collection of worktrees linked to the
|
272
|
+
# repository.
|
273
|
+
# @raise [ArgumentError] If the Repository cannot be opened.
|
274
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
275
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
276
|
+
# exist yet and no repository URL was provided for the cloning.
|
277
|
+
def worktrees
|
278
|
+
open_or_clone
|
279
|
+
worktrees!
|
280
|
+
end
|
281
|
+
|
282
|
+
# Returns the collection of worktrees linked to the repository.
|
283
|
+
# @return [Git::Worktrees] The collection of worktrees linked to the
|
284
|
+
# repository.
|
285
|
+
# @raise [JayAPI::Git::Errors::InvalidRepositoryError] If the repository
|
286
|
+
# is not a valid repository (it hasn't been initialized: cloned or
|
287
|
+
# open).
|
288
|
+
def worktrees!
|
289
|
+
raise JayAPI::Git::Errors::InvalidRepositoryError unless repository
|
290
|
+
|
291
|
+
repository.worktrees
|
292
|
+
end
|
293
|
+
|
294
|
+
# Opens the repository if it is valid or clones it if it isn't
|
295
|
+
# @return [JayAPI::Git::Repository] Self.
|
296
|
+
# @raise [ArgumentError] If the Repository doesn't exist.
|
297
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
298
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
299
|
+
# exist yet and no repository URL was provided for the cloning.
|
300
|
+
def open_or_clone!
|
301
|
+
open_or_clone
|
302
|
+
self
|
303
|
+
end
|
304
|
+
|
305
|
+
private
|
306
|
+
|
307
|
+
attr_reader :repository, :logger
|
308
|
+
|
309
|
+
# Opens the repository if it is valid or clones it if it isn't
|
310
|
+
# @raise [ArgumentError] If the Repository doesn't exist.
|
311
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
312
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If the repository doesn't
|
313
|
+
# exist yet and no repository URL was provided for the cloning.
|
314
|
+
def open_or_clone
|
315
|
+
return if repository
|
316
|
+
|
317
|
+
valid? ? open_repo : clone!
|
318
|
+
end
|
319
|
+
|
320
|
+
# Opens the repository and returns the Git::Base object
|
321
|
+
# @return [Git::Base] The Git::Base object representing the repository.
|
322
|
+
# @raise [ArgumentError] If the Repository doesn't exist.
|
323
|
+
def open_repo
|
324
|
+
@repository = ::Git.open(clone_path.to_s, log: logger)
|
325
|
+
end
|
326
|
+
|
327
|
+
# Clones the repository and returns the Git::Base object
|
328
|
+
# @return [Git::Base] The Git::Base object representing the repository.
|
329
|
+
# @raise [Git::GitExecuteError] If the repository cannot be cloned.
|
330
|
+
# @raise [JayAPI::Git::Errors::MissingURLError] If no repository URL was
|
331
|
+
# provided.
|
332
|
+
def clone_repo
|
333
|
+
raise JayAPI::Git::Errors::MissingURLError, 'A repository URL is required to perform this operation' unless url
|
334
|
+
|
335
|
+
# name needs to be passed as an empty string, otherwise the Git gem will
|
336
|
+
# add it again at the end of the path, and we want the repository to be
|
337
|
+
# cloned exactly in the path that we have designated.
|
338
|
+
@repository = ::Git.clone(url, '', path: clone_path.to_s, log: logger)
|
339
|
+
end
|
340
|
+
|
341
|
+
def git_dir
|
342
|
+
@git_dir ||= clone_path.join('.git')
|
343
|
+
end
|
344
|
+
|
345
|
+
# Derives a directory name from the Repository URL, for example:
|
346
|
+
#
|
347
|
+
# git://git.local/tools/jay/jay_api.git -> jay_api
|
348
|
+
#
|
349
|
+
# @return [String] A directory name
|
350
|
+
def directory_from_url
|
351
|
+
path = URI.parse(url).path
|
352
|
+
File.basename(path).sub('.git', '')
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/string'
|
5
|
+
require 'digest'
|
6
|
+
require 'securerandom'
|
7
|
+
|
8
|
+
module JayAPI
|
9
|
+
# Provides methods to calculate special identifiers for Test Case for Jay.
|
10
|
+
class IDBuilder
|
11
|
+
attr_reader :test_case_id, :project, :software_version, :result
|
12
|
+
|
13
|
+
def initialize(test_case_id: nil, project: nil, software_version: nil, result: nil)
|
14
|
+
@test_case_id = test_case_id
|
15
|
+
@project = project
|
16
|
+
@software_version = software_version
|
17
|
+
@result = result
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] The Sort ID for the Test Case (composed from the
|
21
|
+
# Project's name and a clean version of the full Test Case Identifier)
|
22
|
+
# noinspection RubyNilAnalysis
|
23
|
+
def short_id
|
24
|
+
unless test_case_id && project
|
25
|
+
raise ArgumentError,
|
26
|
+
"The Test Case ID (test_case_id) and the Project's name " \
|
27
|
+
'(project) are required to calculate the Short ID'
|
28
|
+
end
|
29
|
+
|
30
|
+
clean_id = test_case_id.downcase.gsub(/[^a-z0-9-]/, '')
|
31
|
+
"#{project.underscore}_#{Digest::SHA1.new.update(clean_id).hexdigest[0...12]}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Array] An array with two elements:
|
35
|
+
# - The secure Seed (A Version 4 UUID)
|
36
|
+
# - The secure Hash composed by concatenating the Software Version, the
|
37
|
+
# secure Seed and the result of the Test Case.
|
38
|
+
# This Secure ID is meant for end-to-end verification of the test results.
|
39
|
+
def secure_id
|
40
|
+
unless software_version && result
|
41
|
+
raise ArgumentError,
|
42
|
+
'The Software Version (software_version) and the Result ' \
|
43
|
+
'(result) are required to calculate the Secure ID'
|
44
|
+
end
|
45
|
+
|
46
|
+
[
|
47
|
+
uuid = SecureRandom.uuid,
|
48
|
+
Digest::MD5.hexdigest("#{software_version}:#{uuid}:#{result}")
|
49
|
+
]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'merger'
|
4
|
+
require_relative '../../configuration'
|
5
|
+
|
6
|
+
module JayAPI
|
7
|
+
module Mergeables
|
8
|
+
module MergeSelector
|
9
|
+
# A child class of Configuration, that contains the functionality that
|
10
|
+
# allows the Configuration objects 'merge' with each other.
|
11
|
+
class Configuration < JayAPI::Configuration
|
12
|
+
public_class_method :from_hash
|
13
|
+
|
14
|
+
# @param [JayAPI::Configuration, Hash] other The element with which the
|
15
|
+
# 'self' object will be 'merged'.
|
16
|
+
# @return [JayAPI::Mergeables::MergeSelector::Configuration] The result of the 'merge' between 'self'
|
17
|
+
# and 'other'.
|
18
|
+
def merge_select(other)
|
19
|
+
self.class.from_hash(
|
20
|
+
Merger.new(
|
21
|
+
with_indifferent_access,
|
22
|
+
other.with_indifferent_access
|
23
|
+
).to_h
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
5
|
+
|
6
|
+
module JayAPI
|
7
|
+
module Mergeables
|
8
|
+
module MergeSelector
|
9
|
+
# This class is responsible for merging two hashes together into a new one.
|
10
|
+
# The merge behaviour here differs from the standard Hash merging, and can be
|
11
|
+
# summarized with the following rules:
|
12
|
+
# (Let A be a 'mergee' Hash and B a 'merger' Hash. This would be equivalent to
|
13
|
+
# A.merge(B))
|
14
|
+
# * All nodes in Hash B will completely overwrite nodes in Hash A.
|
15
|
+
# * All nodes in Hash A that are not found in Hash B will be ignored in the result.
|
16
|
+
# * If a node value is 'nil' in Hash B and the node matches a node in Hash A then
|
17
|
+
# the matching node in Hash A will be 'selected' to be in the result.
|
18
|
+
# @see documentation/unit tests for details and examples.
|
19
|
+
class Merger
|
20
|
+
attr_reader :mergee, :merger
|
21
|
+
|
22
|
+
# @param [HashWithIndifferentAccess] merger
|
23
|
+
# @param [HashWithIndifferentAccess] mergee
|
24
|
+
def initialize(mergee, merger)
|
25
|
+
@merger = merger
|
26
|
+
@mergee = mergee
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [HashWithIndifferentAccess] The merged result.
|
30
|
+
def to_h
|
31
|
+
{}.with_indifferent_access.tap { |hash| deep_merge(merger, hash) }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Recursively merges 'merger' and 'mergee' into the 'new_hash'.
|
37
|
+
# @param [HashWithIndifferentAccess] merger The Hash 'B' in class documentation.
|
38
|
+
# @param [HashWithIndifferentAccess] new_hash The placeholder Hash that is being
|
39
|
+
# constructed and will be eventually returned as part of the merge result.
|
40
|
+
# @param [Array<String>] path A succession of keys leading to some value of the
|
41
|
+
# mergee hash (to be used in #dig).
|
42
|
+
# This method smells like :reek:FeatureEnvy
|
43
|
+
def deep_merge(merger, new_hash, *path)
|
44
|
+
merger.each do |key, value|
|
45
|
+
case value
|
46
|
+
when NilClass
|
47
|
+
new_hash[key] = mergee.dig(*path, key)
|
48
|
+
when Hash
|
49
|
+
deep_merge(value, new_hash[key] = {}.with_indifferent_access, *path, key)
|
50
|
+
else
|
51
|
+
new_hash[key] = value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|