emerge 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -1
- data/lib/commands/config/orderfiles/orderfiles_ios.rb +0 -2
- data/lib/commands/config/snapshots/snapshots_ios.rb +5 -8
- data/lib/commands/integrate/fastlane.rb +2 -2
- data/lib/commands/upload/snapshots/snapshots.rb +10 -4
- data/lib/emerge_cli.rb +25 -19
- data/lib/reaper/ast_parser.rb +234 -0
- data/lib/utils/git.rb +1 -1
- data/lib/utils/github.rb +3 -3
- data/lib/utils/logger.rb +2 -2
- data/lib/utils/network.rb +23 -8
- data/lib/version.rb +2 -2
- metadata +25 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a44399f9df18a4ce4d32984e12eaadb2fa921c34409d8e4daa0dd1b4acddd2c
|
4
|
+
data.tar.gz: 855d965c1478cbc23eee27f5f99281d1a144b7c40197bdb693fac7d93f0be72d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d005834cf05f3d463df75af935e5e5ad075fe98cac3a05169df0e9f29c50366731f99b606af924ec33afce6fe332cd0e0ebccdd4768d52fa2dff59628e28b68
|
7
|
+
data.tar.gz: 184aad1844eacdf8aa3693ab5ff45acb36f11a16d039f258137f807f355f822b5f31565450927f5b6db453300785df287dcb5dc7c68227d635ccc7dc498a83ba
|
data/README.md
CHANGED
@@ -14,7 +14,7 @@ gem install emerge
|
|
14
14
|
|
15
15
|
## API Key
|
16
16
|
|
17
|
-
Follow our guide to obtain an [API key](https://docs.emergetools.com/docs/uploading-basics#obtain-an-api-key) for your organization. The API Token is used by the CLI to authenticate with the Emerge API. The CLI will automatically pick up the API key if configured as an `EMERGE_API_TOKEN` environment variable, or you can manually pass it into individual commands.
|
17
|
+
Follow our guide to obtain an [API key](https://docs.emergetools.com/docs/uploading-basics#obtain-an-api-key) for your organization. The API Token is used by the CLI to authenticate with the Emerge API. The CLI will automatically pick up the API key if configured as an `EMERGE_API_TOKEN` environment variable, or you can manually pass it into individual commands with the `--api-token` option.
|
18
18
|
|
19
19
|
## Snapshots
|
20
20
|
|
@@ -92,3 +92,24 @@ emerge upload snapshots \
|
|
92
92
|
--client-library paparazzi \
|
93
93
|
--project-root /my/awesomeapp/android/repo
|
94
94
|
```
|
95
|
+
|
96
|
+
### Using with Roborazzi
|
97
|
+
|
98
|
+
Snapshots generated via [Roborazzi](https://github.com/takahirom/roborazzi) are natively supported by the CLI by setting `--client-library roborazzi` and a `--project-root` directory. This will scan your project for all images found in `**/build/outputs/roborazzi` directories.
|
99
|
+
|
100
|
+
Example:
|
101
|
+
|
102
|
+
```shell
|
103
|
+
emerge upload snapshots \
|
104
|
+
--name "AwesomeApp Roborazzi" \
|
105
|
+
--id "com.emerge.awesomeapp.roborazzi" \
|
106
|
+
--repo-name "EmergeTools/AwesomeApp" \
|
107
|
+
--client-library roborazzi \
|
108
|
+
--project-root /my/awesomeapp/android/repo
|
109
|
+
```
|
110
|
+
|
111
|
+
## Building
|
112
|
+
|
113
|
+
This depends on [Tree Sitter](https://tree-sitter.github.io/tree-sitter/) for part of its functionality.
|
114
|
+
|
115
|
+
In order to parse language grammars for Swift and Kotlin, both of which are third-party language grammars, we also depend on [tsdl](https://github.com/stackmystack/tsdl). This downloads and compiles the language grammars into dylibs for us to use.
|
@@ -39,8 +39,6 @@ module EmergeCLI
|
|
39
39
|
format KEY=VALUE".freeze
|
40
40
|
AVAILABLE_OS_VERSIONS = ['17.2', '17.5', '18.0'].freeze
|
41
41
|
|
42
|
-
def initialize; end
|
43
|
-
|
44
42
|
def call(**options)
|
45
43
|
@options = options
|
46
44
|
before(options)
|
@@ -204,12 +202,11 @@ format KEY=VALUE".freeze
|
|
204
202
|
end
|
205
203
|
|
206
204
|
def get_parsed_previews(previews_exact, previews_regex)
|
207
|
-
excluded =
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
})
|
205
|
+
excluded = previews_exact.map do |preview|
|
206
|
+
{
|
207
|
+
'type' => 'exact',
|
208
|
+
'value' => preview
|
209
|
+
}
|
213
210
|
end
|
214
211
|
previews_regex.each do |preview|
|
215
212
|
excluded.push({
|
@@ -80,7 +80,7 @@ module EmergeCLI
|
|
80
80
|
|
81
81
|
# Add app_size lane if not present
|
82
82
|
unless current_content.match?(/^\s*lane\s*:app_size\s*do/)
|
83
|
-
app_size_lane = <<~
|
83
|
+
app_size_lane = <<~RUBY.gsub(/^/, ' ')
|
84
84
|
lane :app_size do
|
85
85
|
# NOTE: If you already have a lane setup to build your app, then you can that instead of this and call emerge() after it.
|
86
86
|
build_app(scheme: ENV["SCHEME_NAME"], export_method: "development")
|
@@ -92,7 +92,7 @@ module EmergeCLI
|
|
92
92
|
|
93
93
|
# Add snapshots lane if not present
|
94
94
|
unless current_content.match?(/^\s*lane\s*:build_upload_emerge_snapshot\s*do/)
|
95
|
-
snapshot_lane = <<~
|
95
|
+
snapshot_lane = <<~RUBY.gsub(/^/, ' ')
|
96
96
|
desc 'Build and upload snapshot build to Emerge Tools'
|
97
97
|
lane :build_upload_emerge_snapshot do
|
98
98
|
emerge_snapshot(scheme: ENV["SCHEME_NAME"])
|
@@ -48,10 +48,11 @@ module EmergeCLI
|
|
48
48
|
|
49
49
|
start_time = Time.now
|
50
50
|
run_id = nil
|
51
|
+
success = false
|
51
52
|
|
52
53
|
begin
|
53
|
-
api_token = @options[:api_token] || ENV
|
54
|
-
raise 'API token is required'
|
54
|
+
api_token = @options[:api_token] || ENV.fetch('EMERGE_API_TOKEN', nil)
|
55
|
+
raise 'API token is required and cannot be blank' if api_token.nil? || api_token.strip.empty?
|
55
56
|
|
56
57
|
@network ||= EmergeCLI::Network.new(api_token:)
|
57
58
|
@git_info_provider ||= GitInfoProvider.new
|
@@ -76,12 +77,16 @@ module EmergeCLI
|
|
76
77
|
Logger.info "Time taken: #{(Time.now - start_time).round(2)} seconds"
|
77
78
|
@profiler.report
|
78
79
|
Logger.info "✅ View your snapshots at https://emergetools.com/snapshot/#{run_id}"
|
80
|
+
success = true
|
79
81
|
rescue StandardError => e
|
80
82
|
Logger.error "CLI Error: #{e.message}"
|
81
83
|
Sync { report_error(run_id, e.message, 'crash') } if run_id
|
84
|
+
raise e # Re-raise the error to dry-cli
|
82
85
|
ensure
|
83
86
|
@network&.close
|
84
87
|
end
|
88
|
+
|
89
|
+
success
|
85
90
|
end
|
86
91
|
|
87
92
|
private
|
@@ -116,6 +121,7 @@ module EmergeCLI
|
|
116
121
|
|
117
122
|
def find_image_files(client)
|
118
123
|
found_images = client.image_files
|
124
|
+
raise 'No images found. Please check your image paths or project configuration.' if found_images.empty?
|
119
125
|
Logger.info "Found #{found_images.size} images"
|
120
126
|
found_images
|
121
127
|
end
|
@@ -127,8 +133,8 @@ module EmergeCLI
|
|
127
133
|
|
128
134
|
if seen_files[file_name]
|
129
135
|
Logger.warn "Duplicate file name detected: '#{file_name}'. " \
|
130
|
-
|
131
|
-
|
136
|
+
"Previous occurrence: '#{seen_files[file_name]}'. " \
|
137
|
+
'This upload will overwrite the previous one.'
|
132
138
|
end
|
133
139
|
seen_files[file_name] = image_path
|
134
140
|
end
|
data/lib/emerge_cli.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
require_relative '
|
2
|
-
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
6
|
-
require_relative '
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
10
|
-
|
11
|
-
require_relative '
|
12
|
-
|
13
|
-
require_relative '
|
14
|
-
|
15
|
-
require_relative '
|
16
|
-
require_relative '
|
17
|
-
require_relative '
|
18
|
-
require_relative '
|
1
|
+
require_relative 'version'
|
2
|
+
|
3
|
+
require_relative 'commands/global_options'
|
4
|
+
require_relative 'commands/upload/snapshots/snapshots'
|
5
|
+
require_relative 'commands/upload/snapshots/client_libraries/swift_snapshot_testing'
|
6
|
+
require_relative 'commands/upload/snapshots/client_libraries/paparazzi'
|
7
|
+
require_relative 'commands/upload/snapshots/client_libraries/roborazzi'
|
8
|
+
require_relative 'commands/upload/snapshots/client_libraries/default'
|
9
|
+
require_relative 'commands/integrate/fastlane'
|
10
|
+
require_relative 'commands/config/snapshots/snapshots_ios'
|
11
|
+
require_relative 'commands/config/orderfiles/orderfiles_ios'
|
12
|
+
|
13
|
+
require_relative 'reaper/ast_parser'
|
14
|
+
|
15
|
+
require_relative 'utils/git_info_provider'
|
16
|
+
require_relative 'utils/git_result'
|
17
|
+
require_relative 'utils/github'
|
18
|
+
require_relative 'utils/git'
|
19
|
+
require_relative 'utils/logger'
|
20
|
+
require_relative 'utils/network'
|
21
|
+
require_relative 'utils/profiler'
|
22
|
+
require_relative 'utils/project_detector'
|
19
23
|
|
20
24
|
require 'dry/cli'
|
25
|
+
require 'pry'
|
26
|
+
require 'pry-byebug'
|
21
27
|
|
22
28
|
module EmergeCLI
|
23
29
|
extend Dry::CLI::Registry
|
@@ -37,4 +43,4 @@ module EmergeCLI
|
|
37
43
|
end
|
38
44
|
|
39
45
|
# By default the log level is INFO, but can be overridden by the --debug flag
|
40
|
-
EmergeCLI::Logger.configure(
|
46
|
+
EmergeCLI::Logger.configure(Logger::INFO)
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'tree_sitter'
|
2
|
+
|
3
|
+
module Emerge
|
4
|
+
module Reaper
|
5
|
+
# Parses the AST of a given file using Tree Sitter and allows us to find usages or delete types.
|
6
|
+
# This does have a lot of limitations since it only looks at a single file at a time,
|
7
|
+
# but can get us most of the way there.
|
8
|
+
class AstParser
|
9
|
+
DECLARATION_NODE_TYPES = {
|
10
|
+
'swift' => %i[class_declaration protocol_declaration],
|
11
|
+
'kotlin' => %i[class_declaration protocol_declaration interface_declaration],
|
12
|
+
'java' => %i[class_declaration protocol_declaration interface_declaration]
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
IDENTIFIER_NODE_TYPES = {
|
16
|
+
'swift' => %i[simple_identifier qualified_name identifier type_identifier],
|
17
|
+
'kotlin' => %i[simple_identifier qualified_name identifier type_identifier],
|
18
|
+
'java' => %i[simple_identifier qualified_name identifier type_identifier]
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
COMMENT_AND_IMPORT_NODE_TYPES = {
|
22
|
+
'swift' => %i[comment import_declaration],
|
23
|
+
'kotlin' => %i[comment import_header],
|
24
|
+
'java' => %i[comment import_declaration]
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
attr_reader :parser, :language
|
28
|
+
|
29
|
+
def initialize(language)
|
30
|
+
@parser = TreeSitter::Parser.new
|
31
|
+
@language = language
|
32
|
+
@current_file_contents = nil
|
33
|
+
|
34
|
+
platform = case RUBY_PLATFORM
|
35
|
+
when /darwin/
|
36
|
+
'darwin'
|
37
|
+
when /linux/
|
38
|
+
'linux'
|
39
|
+
else
|
40
|
+
raise "Unsupported platform: #{RUBY_PLATFORM}"
|
41
|
+
end
|
42
|
+
|
43
|
+
arch = case RUBY_PLATFORM
|
44
|
+
when /x86_64|amd64/
|
45
|
+
'x86_64'
|
46
|
+
when /arm64|aarch64/
|
47
|
+
'arm64'
|
48
|
+
else
|
49
|
+
raise "Unsupported architecture: #{RUBY_PLATFORM}"
|
50
|
+
end
|
51
|
+
|
52
|
+
extension = platform == 'darwin' ? 'dylib' : 'so'
|
53
|
+
parser_file = "libtree-sitter-#{language}-#{platform}-#{arch}.#{extension}"
|
54
|
+
parser_path = File.join('parsers', parser_file)
|
55
|
+
|
56
|
+
case language
|
57
|
+
when 'swift'
|
58
|
+
@parser.language = TreeSitter::Language.load('swift', parser_path)
|
59
|
+
when 'kotlin'
|
60
|
+
@parser.language = TreeSitter::Language.load('kotlin', parser_path)
|
61
|
+
when 'java'
|
62
|
+
@parser.language = TreeSitter::Language.load('java', parser_path)
|
63
|
+
else
|
64
|
+
raise "Unsupported language: #{language}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Deletes a type from the given file contents.
|
69
|
+
# Returns the modified file contents if successful, otherwise nil.
|
70
|
+
# TODO(telkins): Look into the tree-sitter query API to see if it simplifies this.
|
71
|
+
def delete_type(file_contents:, type_name:)
|
72
|
+
@current_file_contents = file_contents
|
73
|
+
tree = @parser.parse_string(nil, file_contents)
|
74
|
+
cursor = TreeSitter::TreeCursor.new(tree.root_node)
|
75
|
+
nodes_to_process = [cursor.current_node]
|
76
|
+
lines_to_remove = []
|
77
|
+
|
78
|
+
while (node = nodes_to_process.shift)
|
79
|
+
if declaration_node_types.include?(node.type)
|
80
|
+
type_identifier_node = find_type_identifier(node)
|
81
|
+
if type_identifier_node && fully_qualified_type_name(type_identifier_node) == type_name
|
82
|
+
remove_node(node, lines_to_remove)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if extension?(node)
|
87
|
+
user_type_nodes = node.select { |n| n.type == :user_type }
|
88
|
+
if user_type_nodes.length >= 1 && fully_qualified_type_name(user_type_nodes[0]) == type_name
|
89
|
+
remove_node(node, lines_to_remove)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
node.each_named { |child| nodes_to_process.push(child) }
|
94
|
+
end
|
95
|
+
|
96
|
+
lines = file_contents.split("\n")
|
97
|
+
lines_to_remove.each do |range|
|
98
|
+
(range[:start]..range[:end]).each { |i| lines[i] = nil }
|
99
|
+
|
100
|
+
# Remove extra newline after class declaration, but only if it's blank
|
101
|
+
if range[:end] + 1 < lines.length && !lines[range[:end] + 1].nil? && lines[range[:end] + 1].match?(/^\s*$/)
|
102
|
+
lines[range[:end] + 1] = nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
modified_source = lines.compact.join("\n")
|
107
|
+
new_tree = @parser.parse_string(nil, modified_source)
|
108
|
+
|
109
|
+
return nil if only_comments_and_imports?(TreeSitter::TreeCursor.new(new_tree.root_node))
|
110
|
+
modified_source
|
111
|
+
end
|
112
|
+
|
113
|
+
# Finds all usages of a given type in a file.
|
114
|
+
# TODO(telkins): Look into the tree-sitter query API to see if it simplifies this.
|
115
|
+
def find_usages(file_contents:, type_name:)
|
116
|
+
@current_file_contents = file_contents
|
117
|
+
tree = @parser.parse_string(nil, file_contents)
|
118
|
+
cursor = TreeSitter::TreeCursor.new(tree.root_node)
|
119
|
+
usages = []
|
120
|
+
nodes_to_process = [cursor.current_node]
|
121
|
+
|
122
|
+
while (node = nodes_to_process.shift)
|
123
|
+
identifier_type = identifier_node_types.include?(node.type)
|
124
|
+
declaration_type = if node == tree.root_node
|
125
|
+
false
|
126
|
+
else
|
127
|
+
declaration_node_types.include?(node.parent&.type)
|
128
|
+
end
|
129
|
+
if declaration_type && fully_qualified_type_name(node) == type_name
|
130
|
+
usages << { line: node.start_point.row, usage_type: 'declaration' }
|
131
|
+
elsif identifier_type && node_text(node) == type_name
|
132
|
+
usages << { line: node.start_point.row, usage_type: 'identifier' }
|
133
|
+
end
|
134
|
+
|
135
|
+
node.each { |child| nodes_to_process.push(child) }
|
136
|
+
end
|
137
|
+
|
138
|
+
usages
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def remove_node(node, lines_to_remove)
|
144
|
+
start_position = node.start_point.row
|
145
|
+
end_position = node.end_point.row
|
146
|
+
lines_to_remove << { start: start_position, end: end_position }
|
147
|
+
|
148
|
+
# Remove comments preceding the class declaration
|
149
|
+
predecessor = node.prev_named_sibling
|
150
|
+
return unless predecessor && predecessor.type == :comment
|
151
|
+
lines_to_remove << { start: predecessor.start_point.row, end: predecessor.end_point.row }
|
152
|
+
end
|
153
|
+
|
154
|
+
def extension?(node)
|
155
|
+
if node.type == :class_declaration
|
156
|
+
!node.find { |n| n.type == :extension }.nil?
|
157
|
+
else
|
158
|
+
false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def only_comments_and_imports?(root)
|
163
|
+
types = comment_and_import_types
|
164
|
+
root.current_node.all? do |child|
|
165
|
+
types.include?(child.type)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Reaper expects a fully qualified type name, so we need to extract it from the AST.
|
170
|
+
# E.g. `MyModule.MyClass`
|
171
|
+
def fully_qualified_type_name(node)
|
172
|
+
class_name = node_text(node)
|
173
|
+
current_node = node
|
174
|
+
parent = find_parent_type_declaration(current_node)
|
175
|
+
|
176
|
+
while parent
|
177
|
+
type_identifier = find_type_identifier(parent)
|
178
|
+
user_type = find_user_type(parent)
|
179
|
+
|
180
|
+
if type_identifier && type_identifier != current_node
|
181
|
+
class_name = "#{node_text(type_identifier)}.#{class_name}"
|
182
|
+
current_node = type_identifier
|
183
|
+
elsif user_type && user_type != current_node
|
184
|
+
class_name = "#{node_text(user_type)}.#{class_name}"
|
185
|
+
current_node = user_type
|
186
|
+
end
|
187
|
+
|
188
|
+
parent = find_parent_type_declaration(parent)
|
189
|
+
end
|
190
|
+
|
191
|
+
class_name
|
192
|
+
end
|
193
|
+
|
194
|
+
def find_parent_type_declaration(node)
|
195
|
+
return nil unless node&.parent
|
196
|
+
|
197
|
+
current = node.parent
|
198
|
+
while current && !current.null?
|
199
|
+
return current if current.type && declaration_node_types.include?(current.type)
|
200
|
+
break unless current.parent && !current.parent.null?
|
201
|
+
current = current.parent
|
202
|
+
end
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
|
206
|
+
def find_type_identifier(node)
|
207
|
+
node.find { |n| identifier_node_types.include?(n.type) }
|
208
|
+
end
|
209
|
+
|
210
|
+
def find_user_type(node)
|
211
|
+
node.find { |n| n.type == :user_type }
|
212
|
+
end
|
213
|
+
|
214
|
+
def declaration_node_types
|
215
|
+
DECLARATION_NODE_TYPES[language]
|
216
|
+
end
|
217
|
+
|
218
|
+
def identifier_node_types
|
219
|
+
IDENTIFIER_NODE_TYPES[language]
|
220
|
+
end
|
221
|
+
|
222
|
+
def comment_and_import_types
|
223
|
+
COMMENT_AND_IMPORT_NODE_TYPES[language]
|
224
|
+
end
|
225
|
+
|
226
|
+
def node_text(node)
|
227
|
+
return '' unless @current_file_contents
|
228
|
+
start_byte = node.start_byte
|
229
|
+
end_byte = node.end_byte
|
230
|
+
@current_file_contents[start_byte...end_byte]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
data/lib/utils/git.rb
CHANGED
data/lib/utils/github.rb
CHANGED
@@ -4,7 +4,7 @@ module EmergeCLI
|
|
4
4
|
GITHUB_EVENT_PUSH = 'push'.freeze
|
5
5
|
|
6
6
|
def self.event_name
|
7
|
-
ENV
|
7
|
+
ENV.fetch('GITHUB_EVENT_NAME', nil)
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.supported_github_event?
|
@@ -22,7 +22,7 @@ module EmergeCLI
|
|
22
22
|
|
23
23
|
def self.sha
|
24
24
|
if push?
|
25
|
-
ENV
|
25
|
+
ENV.fetch('GITHUB_SHA', nil)
|
26
26
|
elsif pull_request?
|
27
27
|
github_event_data.dig(:pull_request, :head, :sha)
|
28
28
|
end
|
@@ -55,7 +55,7 @@ module EmergeCLI
|
|
55
55
|
|
56
56
|
def self.github_event_data
|
57
57
|
@github_event_data ||= begin
|
58
|
-
github_event_path = ENV
|
58
|
+
github_event_path = ENV.fetch('GITHUB_EVENT_PATH', nil)
|
59
59
|
Logger.error 'GITHUB_EVENT_PATH is not set' if github_event_path.nil?
|
60
60
|
|
61
61
|
Logger.error "File #{github_event_path} doesn't exist" unless File.exist?(github_event_path)
|
data/lib/utils/logger.rb
CHANGED
data/lib/utils/network.rb
CHANGED
@@ -9,7 +9,7 @@ module EmergeCLI
|
|
9
9
|
public_constant :EMERGE_API_PROD_URL
|
10
10
|
|
11
11
|
RETRY_DELAY = 5
|
12
|
-
MAX_RETRIES =
|
12
|
+
MAX_RETRIES = 3
|
13
13
|
|
14
14
|
def initialize(api_token:, base_url: EMERGE_API_PROD_URL)
|
15
15
|
@base_url = base_url
|
@@ -43,7 +43,10 @@ module EmergeCLI
|
|
43
43
|
end
|
44
44
|
absolute_uri = uri.to_s
|
45
45
|
|
46
|
-
headers = {
|
46
|
+
headers = {
|
47
|
+
'X-API-Token' => @api_token,
|
48
|
+
'User-Agent' => "emerge-cli/#{EmergeCLI::VERSION}"
|
49
|
+
}
|
47
50
|
headers['Content-Type'] = 'application/json' if method == :post && body.is_a?(Hash)
|
48
51
|
headers.merge!(custom_headers)
|
49
52
|
|
@@ -62,20 +65,32 @@ module EmergeCLI
|
|
62
65
|
|
63
66
|
response
|
64
67
|
rescue StandardError => e
|
65
|
-
|
66
|
-
if
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
retries += 1
|
69
|
+
if retries <= MAX_RETRIES
|
70
|
+
delay = RETRY_DELAY * retries
|
71
|
+
error_message = e.message
|
72
|
+
Logger.warn "Request failed (attempt #{retries}/#{MAX_RETRIES}): #{error_message}"
|
73
|
+
Logger.warn "Retrying in #{delay} seconds..."
|
74
|
+
|
75
|
+
begin
|
76
|
+
@internet.close
|
77
|
+
rescue StandardError
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
@internet = Async::HTTP::Internet.new
|
81
|
+
|
82
|
+
sleep delay
|
70
83
|
retry
|
71
84
|
else
|
72
|
-
Logger.error "Request failed: #{absolute_uri} #{e.message}"
|
85
|
+
Logger.error "Request failed after #{MAX_RETRIES} attempts: #{absolute_uri} #{e.message}"
|
73
86
|
raise e
|
74
87
|
end
|
75
88
|
end
|
76
89
|
end
|
77
90
|
|
78
91
|
def perform_request(method, absolute_uri, headers, body)
|
92
|
+
headers ||= {}
|
93
|
+
|
79
94
|
case method
|
80
95
|
when :get
|
81
96
|
@internet.get(absolute_uri, headers:)
|
data/lib/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = '0.2.
|
1
|
+
module EmergeCLI
|
2
|
+
VERSION = '0.2.2'.freeze
|
3
3
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: emerge
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emerge Tools
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.
|
19
|
+
version: 2.21.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.
|
26
|
+
version: 2.21.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: async-http
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.86.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.86.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: chunky_png
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.4.0
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: colorize
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 1.1.0
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 1.1.0
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: dry-cli
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,145 +81,75 @@ dependencies:
|
|
95
81
|
- !ruby/object:Gem::Version
|
96
82
|
version: 0.2.1
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
84
|
+
name: pry-byebug
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
87
|
- - "~>"
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
89
|
+
version: '3.8'
|
104
90
|
type: :runtime
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
96
|
+
version: '3.8'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
98
|
+
name: ruby_tree_sitter
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
114
100
|
requirements:
|
115
101
|
- - "~>"
|
116
102
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
103
|
+
version: '1.9'
|
118
104
|
type: :runtime
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
107
|
requirements:
|
122
108
|
- - "~>"
|
123
109
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
110
|
+
version: '1.9'
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
112
|
+
name: tty-prompt
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
128
114
|
requirements:
|
129
115
|
- - "~>"
|
130
116
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
117
|
+
version: 0.23.1
|
132
118
|
type: :runtime
|
133
119
|
prerelease: false
|
134
120
|
version_requirements: !ruby/object:Gem::Requirement
|
135
121
|
requirements:
|
136
122
|
- - "~>"
|
137
123
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
139
|
-
- !ruby/object:Gem::Dependency
|
140
|
-
name: minitest
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
142
|
-
requirements:
|
143
|
-
- - "~>"
|
144
|
-
- !ruby/object:Gem::Version
|
145
|
-
version: 5.25.1
|
146
|
-
type: :development
|
147
|
-
prerelease: false
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
requirements:
|
150
|
-
- - "~>"
|
151
|
-
- !ruby/object:Gem::Version
|
152
|
-
version: 5.25.1
|
153
|
-
- !ruby/object:Gem::Dependency
|
154
|
-
name: minitest-reporters
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
156
|
-
requirements:
|
157
|
-
- - "~>"
|
158
|
-
- !ruby/object:Gem::Version
|
159
|
-
version: 1.7.1
|
160
|
-
type: :development
|
161
|
-
prerelease: false
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
163
|
-
requirements:
|
164
|
-
- - "~>"
|
165
|
-
- !ruby/object:Gem::Version
|
166
|
-
version: 1.7.1
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: pry
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - "~>"
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 0.15.0
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - "~>"
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 0.15.0
|
181
|
-
- !ruby/object:Gem::Dependency
|
182
|
-
name: rake
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
184
|
-
requirements:
|
185
|
-
- - "~>"
|
186
|
-
- !ruby/object:Gem::Version
|
187
|
-
version: 13.2.1
|
188
|
-
type: :development
|
189
|
-
prerelease: false
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
191
|
-
requirements:
|
192
|
-
- - "~>"
|
193
|
-
- !ruby/object:Gem::Version
|
194
|
-
version: 13.2.1
|
195
|
-
- !ruby/object:Gem::Dependency
|
196
|
-
name: rspec
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
198
|
-
requirements:
|
199
|
-
- - "~>"
|
200
|
-
- !ruby/object:Gem::Version
|
201
|
-
version: 3.13.0
|
202
|
-
type: :development
|
203
|
-
prerelease: false
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
205
|
-
requirements:
|
206
|
-
- - "~>"
|
207
|
-
- !ruby/object:Gem::Version
|
208
|
-
version: 3.13.0
|
124
|
+
version: 0.23.1
|
209
125
|
- !ruby/object:Gem::Dependency
|
210
|
-
name:
|
126
|
+
name: tty-table
|
211
127
|
requirement: !ruby/object:Gem::Requirement
|
212
128
|
requirements:
|
213
129
|
- - "~>"
|
214
130
|
- !ruby/object:Gem::Version
|
215
|
-
version:
|
216
|
-
type: :
|
131
|
+
version: 0.12.0
|
132
|
+
type: :runtime
|
217
133
|
prerelease: false
|
218
134
|
version_requirements: !ruby/object:Gem::Requirement
|
219
135
|
requirements:
|
220
136
|
- - "~>"
|
221
137
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
138
|
+
version: 0.12.0
|
223
139
|
- !ruby/object:Gem::Dependency
|
224
|
-
name:
|
140
|
+
name: xcodeproj
|
225
141
|
requirement: !ruby/object:Gem::Requirement
|
226
142
|
requirements:
|
227
143
|
- - "~>"
|
228
144
|
- !ruby/object:Gem::Version
|
229
|
-
version:
|
230
|
-
type: :
|
145
|
+
version: 1.27.0
|
146
|
+
type: :runtime
|
231
147
|
prerelease: false
|
232
148
|
version_requirements: !ruby/object:Gem::Requirement
|
233
149
|
requirements:
|
234
150
|
- - "~>"
|
235
151
|
- !ruby/object:Gem::Version
|
236
|
-
version:
|
152
|
+
version: 1.27.0
|
237
153
|
description: The official CLI for Emerge Tools
|
238
154
|
email:
|
239
155
|
- support@emergetools.com
|
@@ -255,6 +171,7 @@ files:
|
|
255
171
|
- lib/commands/upload/snapshots/client_libraries/swift_snapshot_testing.rb
|
256
172
|
- lib/commands/upload/snapshots/snapshots.rb
|
257
173
|
- lib/emerge_cli.rb
|
174
|
+
- lib/reaper/ast_parser.rb
|
258
175
|
- lib/utils/git.rb
|
259
176
|
- lib/utils/git_info_provider.rb
|
260
177
|
- lib/utils/git_result.rb
|
@@ -271,6 +188,7 @@ metadata:
|
|
271
188
|
homepage_uri: https://github.com/EmergeTools/emerge-cli
|
272
189
|
source_code_uri: https://github.com/EmergeTools/emerge-cli
|
273
190
|
changelog_uri: https://github.com/EmergeTools/emerge-cli/blob/main/CHANGELOG.md
|
191
|
+
rubygems_mfa_required: 'true'
|
274
192
|
post_install_message:
|
275
193
|
rdoc_options: []
|
276
194
|
require_paths:
|