emerge 0.3.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/lib/commands/autofixes/exported_symbols.rb +62 -0
- data/lib/commands/autofixes/minify_strings.rb +99 -0
- data/lib/commands/autofixes/strip_binary_symbols.rb +116 -0
- data/lib/commands/build_distribution/download_and_install.rb +107 -0
- data/lib/commands/build_distribution/validate_app.rb +164 -0
- data/lib/commands/order_files/download_order_files.rb +77 -0
- data/lib/commands/order_files/validate_linkmaps.rb +55 -0
- data/lib/commands/order_files/validate_xcode_project.rb +71 -0
- data/lib/commands/snapshots/validate_app.rb +64 -0
- data/lib/commands/upload/build.rb +140 -0
- data/lib/emerge_cli.rb +33 -0
- data/lib/reaper/ast_parser.rb +17 -16
- data/lib/reaper/code_deleter.rb +3 -1
- data/lib/utils/git.rb +21 -1
- data/lib/utils/macho_parser.rb +325 -0
- data/lib/utils/network.rb +12 -10
- data/lib/version.rb +1 -1
- data/parsers/libtree-sitter-objc-darwin-arm64.dylib +0 -0
- data/parsers/libtree-sitter-objc-linux-x86_64.so +0 -0
- metadata +66 -5
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'dry/cli'
|
2
|
+
require 'xcodeproj'
|
3
|
+
|
4
|
+
module EmergeCLI
|
5
|
+
module Commands
|
6
|
+
class ValidateXcodeProject < EmergeCLI::Commands::GlobalOptions
|
7
|
+
desc 'Validate xcodeproject for order files'
|
8
|
+
|
9
|
+
option :path, type: :string, required: true, desc: 'Path to the xcodeproject to validate'
|
10
|
+
option :target, type: :string, required: false, desc: 'Target to validate'
|
11
|
+
option :build_configuration, type: :string, required: false,
|
12
|
+
desc: 'Build configuration to validate (Release by default)'
|
13
|
+
|
14
|
+
# Constants
|
15
|
+
LINK_MAPS_CONFIG = 'LD_GENERATE_MAP_FILE'.freeze
|
16
|
+
LINK_MAPS_PATH = 'LD_MAP_FILE_PATH'.freeze
|
17
|
+
PATH_TO_LINKMAP = '$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt'.freeze
|
18
|
+
|
19
|
+
def call(**options)
|
20
|
+
@options = options
|
21
|
+
before(options)
|
22
|
+
|
23
|
+
raise 'Path must be an xcodeproject' unless @options[:path].end_with?('.xcodeproj')
|
24
|
+
raise 'Path does not exist' unless File.exist?(@options[:path])
|
25
|
+
|
26
|
+
@options[:build_configuration] ||= 'Release'
|
27
|
+
|
28
|
+
Sync do
|
29
|
+
project = Xcodeproj::Project.open(@options[:path])
|
30
|
+
|
31
|
+
validate_xcproj(project)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def validate_xcproj(project)
|
38
|
+
project.targets.each do |target|
|
39
|
+
next if @options[:target] && target.name != @options[:target]
|
40
|
+
next unless target.product_type == 'com.apple.product-type.application'
|
41
|
+
|
42
|
+
target.build_configurations.each do |config|
|
43
|
+
next if config.name != @options[:build_configuration]
|
44
|
+
validate_target_config(target, config)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_target_config(target, config)
|
50
|
+
has_error = false
|
51
|
+
if config.build_settings[LINK_MAPS_CONFIG] != 'YES'
|
52
|
+
has_error = true
|
53
|
+
Logger.error "❌ Write Link Map File (#{LINK_MAPS_CONFIG}) is not set to YES"
|
54
|
+
end
|
55
|
+
if config.build_settings[LINK_MAPS_PATH] != ''
|
56
|
+
has_error = true
|
57
|
+
Logger.error "❌ Path to Link Map File (#{LINK_MAPS_PATH}) is not set, we recommend \
|
58
|
+
setting it to '#{PATH_TO_LINKMAP}'"
|
59
|
+
end
|
60
|
+
|
61
|
+
if has_error
|
62
|
+
Logger.error "❌ Target '#{target.name}' has errors, this means \
|
63
|
+
that the linkmaps will not be generated as expected"
|
64
|
+
Logger.error "Use `emerge configure order-files-ios --project-path '#{@options[:path]}'` to fix this"
|
65
|
+
else
|
66
|
+
Logger.info "✅ Target '#{target.name}' is valid"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'dry/cli'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
require 'yaml'
|
5
|
+
require 'cfpropertylist'
|
6
|
+
|
7
|
+
module EmergeCLI
|
8
|
+
module Commands
|
9
|
+
module Snapshots
|
10
|
+
class ValidateApp < EmergeCLI::Commands::GlobalOptions
|
11
|
+
desc 'Validate app for snapshot testing [iOS, macOS]'
|
12
|
+
|
13
|
+
# Optional options
|
14
|
+
option :path, type: :string, required: true, desc: 'Path to the app binary or xcarchive'
|
15
|
+
|
16
|
+
# Mangled names are deterministic, no need to demangle them
|
17
|
+
SWIFT_PREVIEWS_MANGLED_NAMES = [
|
18
|
+
'_$s21DeveloperToolsSupport15PreviewRegistryMp',
|
19
|
+
'_$s7SwiftUI15PreviewProviderMp'
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
def call(**options)
|
23
|
+
@options = options
|
24
|
+
before(options)
|
25
|
+
|
26
|
+
Sync do
|
27
|
+
binary_path = get_binary_path
|
28
|
+
Logger.info "Found binary: #{binary_path}"
|
29
|
+
|
30
|
+
Logger.info "Loading binary: #{binary_path}"
|
31
|
+
macho_parser = MachOParser.new
|
32
|
+
macho_parser.load_binary(binary_path)
|
33
|
+
|
34
|
+
use_chained_fixups, imported_symbols = macho_parser.read_linkedit_data_command
|
35
|
+
bound_symbols = macho_parser.read_dyld_info_only_command
|
36
|
+
|
37
|
+
found = macho_parser.find_protocols_in_swift_proto(use_chained_fixups, imported_symbols, bound_symbols,
|
38
|
+
SWIFT_PREVIEWS_MANGLED_NAMES)
|
39
|
+
|
40
|
+
if found
|
41
|
+
Logger.info '✅ Found SwiftUI previews'
|
42
|
+
else
|
43
|
+
Logger.error '❌ No SwiftUI previews found'
|
44
|
+
end
|
45
|
+
found
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def get_binary_path
|
52
|
+
return @options[:path] unless @options[:path].end_with?('.xcarchive')
|
53
|
+
app_path = Dir.glob("#{@options[:path]}/Products/Applications/*.app").first
|
54
|
+
info_path = File.join(app_path, 'Info.plist')
|
55
|
+
plist_data = File.read(info_path)
|
56
|
+
plist = CFPropertyList::List.new(data: plist_data)
|
57
|
+
parsed_data = CFPropertyList.native_types(plist.value)
|
58
|
+
|
59
|
+
File.join(app_path, parsed_data['CFBundleExecutable'])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'dry/cli'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
require 'async'
|
5
|
+
require 'async/barrier'
|
6
|
+
require 'async/semaphore'
|
7
|
+
require 'async/http/internet/instance'
|
8
|
+
|
9
|
+
module EmergeCLI
|
10
|
+
module Commands
|
11
|
+
module Upload
|
12
|
+
class Build < EmergeCLI::Commands::GlobalOptions
|
13
|
+
desc 'Upload a build to Emerge'
|
14
|
+
|
15
|
+
option :path, type: :string, required: true, desc: 'Path to the build artifact'
|
16
|
+
|
17
|
+
# Optional options
|
18
|
+
option :api_token, type: :string, required: false,
|
19
|
+
desc: 'API token for authentication, defaults to ENV[EMERGE_API_TOKEN]'
|
20
|
+
option :sha, type: :string, required: false, desc: 'SHA of the commit'
|
21
|
+
option :branch, type: :string, required: false, desc: 'Branch name'
|
22
|
+
option :repo_name, type: :string, required: false, desc: 'Repository name'
|
23
|
+
option :base_sha, type: :string, required: false, desc: 'Base SHA'
|
24
|
+
option :previous_sha, type: :string, required: false, desc: 'Previous SHA'
|
25
|
+
option :pr_number, type: :string, required: false, desc: 'PR number'
|
26
|
+
|
27
|
+
def initialize(network: nil, git_info_provider: nil)
|
28
|
+
@network = network
|
29
|
+
@git_info_provider = git_info_provider
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(**options)
|
33
|
+
@options = options
|
34
|
+
@profiler = EmergeCLI::Profiler.new(enabled: options[:profile])
|
35
|
+
before(options)
|
36
|
+
|
37
|
+
start_time = Time.now
|
38
|
+
|
39
|
+
file_path = options[:path]
|
40
|
+
file_exists = File.exist?(file_path)
|
41
|
+
raise "File not found at path: #{file_path}" unless file_exists
|
42
|
+
|
43
|
+
file_extension = File.extname(file_path)
|
44
|
+
raise "Unsupported file type: #{file_extension}" unless ['.ipa', '.apk', '.aab',
|
45
|
+
'.zip'].include?(file_extension)
|
46
|
+
|
47
|
+
api_token = @options[:api_token] || ENV.fetch('EMERGE_API_TOKEN', nil)
|
48
|
+
raise 'API token is required and cannot be blank' if api_token.nil? || api_token.strip.empty?
|
49
|
+
|
50
|
+
@network ||= EmergeCLI::Network.new(api_token:)
|
51
|
+
@git_info_provider ||= GitInfoProvider.new
|
52
|
+
|
53
|
+
Sync do
|
54
|
+
upload_url, upload_id = fetch_upload_url
|
55
|
+
|
56
|
+
file_size = File.size(file_path)
|
57
|
+
Logger.info("Uploading file... (#{file_size} bytes)")
|
58
|
+
|
59
|
+
File.open(file_path, 'rb') do |file|
|
60
|
+
headers = {
|
61
|
+
'Content-Type' => 'application/zip',
|
62
|
+
'Content-Length' => file_size.to_s
|
63
|
+
}
|
64
|
+
|
65
|
+
response = @network.put(
|
66
|
+
path: upload_url,
|
67
|
+
body: file.read,
|
68
|
+
headers: headers
|
69
|
+
)
|
70
|
+
|
71
|
+
unless response.status == 200
|
72
|
+
Logger.error("Upload failed with status #{response.status}")
|
73
|
+
Logger.error("Response body: #{response.body}")
|
74
|
+
raise "Uploading file failed with status #{response.status}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Logger.info('Upload complete successfully!')
|
79
|
+
Logger.info "Time taken: #{(Time.now - start_time).round(2)} seconds"
|
80
|
+
Logger.info("✅ You can view the build analysis at https://emergetools.com/build/#{upload_id}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def fetch_upload_url
|
87
|
+
git_result = @git_info_provider.fetch_git_info
|
88
|
+
sha = @options[:sha] || git_result.sha
|
89
|
+
branch = @options[:branch] || git_result.branch
|
90
|
+
base_sha = @options[:base_sha] || git_result.base_sha
|
91
|
+
previous_sha = @options[:previous_sha] || git_result.previous_sha
|
92
|
+
pr_number = @options[:pr_number] || git_result.pr_number
|
93
|
+
|
94
|
+
# TODO: Make optional
|
95
|
+
raise 'SHA is required' unless sha
|
96
|
+
raise 'Branch is required' unless branch
|
97
|
+
|
98
|
+
payload = {
|
99
|
+
sha:,
|
100
|
+
branch:,
|
101
|
+
repo_name: @options[:repo_name],
|
102
|
+
# Optional
|
103
|
+
base_sha:,
|
104
|
+
previous_sha:,
|
105
|
+
pr_number: pr_number&.to_s
|
106
|
+
}.compact
|
107
|
+
|
108
|
+
upload_response = @network.post(
|
109
|
+
path: '/upload',
|
110
|
+
body: payload,
|
111
|
+
headers: { 'Content-Type' => 'application/json' }
|
112
|
+
)
|
113
|
+
upload_json = parse_response(upload_response)
|
114
|
+
upload_id = upload_json.fetch('upload_id')
|
115
|
+
upload_url = upload_json.fetch('uploadURL')
|
116
|
+
Logger.debug("Got upload ID: #{upload_id}")
|
117
|
+
|
118
|
+
warning = upload_json['warning']
|
119
|
+
Logger.warn(warning) if warning
|
120
|
+
|
121
|
+
[upload_url, upload_id]
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_response(response)
|
125
|
+
case response.status
|
126
|
+
when 200
|
127
|
+
JSON.parse(response.read)
|
128
|
+
when 400
|
129
|
+
error_message = JSON.parse(response.read)['errorMessage']
|
130
|
+
raise "Invalid parameters: #{error_message}"
|
131
|
+
when 401, 403
|
132
|
+
raise 'Invalid API token'
|
133
|
+
else
|
134
|
+
raise "Creating upload failed with status #{response.status}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/emerge_cli.rb
CHANGED
@@ -10,6 +10,16 @@ require_relative 'commands/integrate/fastlane'
|
|
10
10
|
require_relative 'commands/config/snapshots/snapshots_ios'
|
11
11
|
require_relative 'commands/config/orderfiles/orderfiles_ios'
|
12
12
|
require_relative 'commands/reaper/reaper'
|
13
|
+
require_relative 'commands/snapshots/validate_app'
|
14
|
+
require_relative 'commands/order_files/download_order_files'
|
15
|
+
require_relative 'commands/order_files/validate_linkmaps'
|
16
|
+
require_relative 'commands/order_files/validate_xcode_project'
|
17
|
+
require_relative 'commands/upload/build'
|
18
|
+
require_relative 'commands/build_distribution/validate_app'
|
19
|
+
require_relative 'commands/build_distribution/download_and_install'
|
20
|
+
require_relative 'commands/autofixes/minify_strings'
|
21
|
+
require_relative 'commands/autofixes/strip_binary_symbols'
|
22
|
+
require_relative 'commands/autofixes/exported_symbols'
|
13
23
|
|
14
24
|
require_relative 'reaper/ast_parser'
|
15
25
|
require_relative 'reaper/code_deleter'
|
@@ -22,6 +32,7 @@ require_relative 'utils/logger'
|
|
22
32
|
require_relative 'utils/network'
|
23
33
|
require_relative 'utils/profiler'
|
24
34
|
require_relative 'utils/project_detector'
|
35
|
+
require_relative 'utils/macho_parser'
|
25
36
|
require_relative 'utils/version_check'
|
26
37
|
|
27
38
|
require 'dry/cli'
|
@@ -30,6 +41,7 @@ module EmergeCLI
|
|
30
41
|
extend Dry::CLI::Registry
|
31
42
|
|
32
43
|
register 'upload', aliases: ['u'] do |prefix|
|
44
|
+
prefix.register 'build', Commands::Upload::Build
|
33
45
|
prefix.register 'snapshots', Commands::Upload::Snapshots
|
34
46
|
end
|
35
47
|
|
@@ -43,6 +55,27 @@ module EmergeCLI
|
|
43
55
|
end
|
44
56
|
|
45
57
|
register 'reaper', Commands::Reaper
|
58
|
+
|
59
|
+
register 'snapshots' do |prefix|
|
60
|
+
prefix.register 'validate-app-ios', Commands::Snapshots::ValidateApp
|
61
|
+
end
|
62
|
+
|
63
|
+
register 'order-files' do |prefix|
|
64
|
+
prefix.register 'download', Commands::DownloadOrderFiles
|
65
|
+
prefix.register 'validate-linkmaps', Commands::ValidateLinkmaps
|
66
|
+
prefix.register 'validate-xcode-project', Commands::ValidateXcodeProject
|
67
|
+
end
|
68
|
+
|
69
|
+
register 'build-distribution' do |prefix|
|
70
|
+
prefix.register 'validate-app', Commands::BuildDistribution::ValidateApp
|
71
|
+
prefix.register 'install', Commands::BuildDistribution::DownloadAndInstall
|
72
|
+
end
|
73
|
+
|
74
|
+
register 'autofix' do |prefix|
|
75
|
+
prefix.register 'minify-strings', Commands::Autofixes::MinifyStrings
|
76
|
+
prefix.register 'strip-binary-symbols', Commands::Autofixes::StripBinarySymbols
|
77
|
+
prefix.register 'exported-symbols', Commands::Autofixes::ExportedSymbols
|
78
|
+
end
|
46
79
|
end
|
47
80
|
|
48
81
|
# By default the log level is INFO, but can be overridden by the --debug flag
|
data/lib/reaper/ast_parser.rb
CHANGED
@@ -9,19 +9,22 @@ module EmergeCLI
|
|
9
9
|
DECLARATION_NODE_TYPES = {
|
10
10
|
'swift' => %i[class_declaration protocol_declaration],
|
11
11
|
'kotlin' => %i[class_declaration protocol_declaration interface_declaration object_declaration],
|
12
|
-
'java' => %i[class_declaration protocol_declaration interface_declaration]
|
12
|
+
'java' => %i[class_declaration protocol_declaration interface_declaration],
|
13
|
+
'objc' => %i[class_declaration protocol_declaration class_implementation class_interface]
|
13
14
|
}.freeze
|
14
15
|
|
15
16
|
IDENTIFIER_NODE_TYPES = {
|
16
17
|
'swift' => %i[simple_identifier qualified_name identifier type_identifier],
|
17
18
|
'kotlin' => %i[simple_identifier qualified_name identifier type_identifier],
|
18
|
-
'java' => %i[simple_identifier qualified_name identifier type_identifier]
|
19
|
+
'java' => %i[simple_identifier qualified_name identifier type_identifier],
|
20
|
+
'objc' => %i[simple_identifier qualified_name identifier type_identifier]
|
19
21
|
}.freeze
|
20
22
|
|
21
23
|
COMMENT_AND_IMPORT_NODE_TYPES = {
|
22
24
|
'swift' => %i[comment import_declaration],
|
23
25
|
'kotlin' => %i[comment import_header],
|
24
|
-
'java' => %i[comment import_declaration]
|
26
|
+
'java' => %i[comment import_declaration],
|
27
|
+
'objc' => %i[comment import_declaration preproc_include]
|
25
28
|
}.freeze
|
26
29
|
|
27
30
|
attr_reader :parser, :language
|
@@ -52,17 +55,9 @@ module EmergeCLI
|
|
52
55
|
extension = platform == 'darwin' ? 'dylib' : 'so'
|
53
56
|
parser_file = "libtree-sitter-#{language}-#{platform}-#{arch}.#{extension}"
|
54
57
|
parser_path = File.join('parsers', parser_file)
|
58
|
+
raise "No language grammar found for #{language}" unless File.exist?(parser_path)
|
55
59
|
|
56
|
-
|
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
|
60
|
+
@parser.language = TreeSitter::Language.load(language, parser_path)
|
66
61
|
end
|
67
62
|
|
68
63
|
# Deletes a type from the given file contents.
|
@@ -127,6 +122,7 @@ module EmergeCLI
|
|
127
122
|
|
128
123
|
while (node = nodes_to_process.shift)
|
129
124
|
identifier_type = identifier_node_types.include?(node.type)
|
125
|
+
Logger.debug "Processing node: #{node.type} #{node_text(node)}"
|
130
126
|
declaration_type = if node == tree.root_node
|
131
127
|
false
|
132
128
|
else
|
@@ -136,6 +132,11 @@ module EmergeCLI
|
|
136
132
|
usages << { line: node.start_point.row, usage_type: 'declaration' }
|
137
133
|
elsif identifier_type && node_text(node) == type_name
|
138
134
|
usages << { line: node.start_point.row, usage_type: 'identifier' }
|
135
|
+
elsif node.type == :@implementation
|
136
|
+
next_sibling = node.next_named_sibling
|
137
|
+
if next_sibling.type == :identifier && node_text(next_sibling) == type_name
|
138
|
+
usages << { line: next_sibling.start_point.row, usage_type: 'declaration' }
|
139
|
+
end
|
139
140
|
end
|
140
141
|
|
141
142
|
node.each { |child| nodes_to_process.push(child) }
|
@@ -172,14 +173,14 @@ module EmergeCLI
|
|
172
173
|
|
173
174
|
return file_contents if nodes_to_remove.empty?
|
174
175
|
|
175
|
-
Logger.debug "Found #{nodes_to_remove.length} nodes to remove"
|
176
|
+
Logger.debug "✅ Found #{nodes_to_remove.length} nodes to remove"
|
176
177
|
remove_nodes_from_content(file_contents, nodes_to_remove)
|
177
178
|
end
|
178
179
|
|
179
180
|
private
|
180
181
|
|
181
182
|
def remove_node(node, lines_to_remove)
|
182
|
-
Logger.debug "Removing node: #{node.type}"
|
183
|
+
Logger.debug "✅ Removing node: #{node.type}"
|
183
184
|
start_position = node.start_point.row
|
184
185
|
end_position = node.end_point.row
|
185
186
|
lines_to_remove << { start: start_position, end: end_position }
|
@@ -287,7 +288,7 @@ module EmergeCLI
|
|
287
288
|
when :navigation_expression # NetworkDebugger.printStats
|
288
289
|
result = handle_navigation_expression(current)
|
289
290
|
return result if result
|
290
|
-
when :class_declaration, :function_declaration, :method_declaration
|
291
|
+
when :class_declaration, :function_declaration, :method_declaration, :@implementation
|
291
292
|
Logger.debug "Reached structural element, stopping at: #{current.type}"
|
292
293
|
break
|
293
294
|
end
|
data/lib/reaper/code_deleter.rb
CHANGED
@@ -163,7 +163,8 @@ module EmergeCLI
|
|
163
163
|
found_usages = []
|
164
164
|
source_patterns = case @platform&.downcase
|
165
165
|
when 'ios'
|
166
|
-
{ 'swift' => '**/*.swift'
|
166
|
+
{ 'swift' => '**/*.swift',
|
167
|
+
'objc' => '**/*.{m,h}' }
|
167
168
|
when 'android'
|
168
169
|
{
|
169
170
|
'kotlin' => '**/*.kt',
|
@@ -253,6 +254,7 @@ module EmergeCLI
|
|
253
254
|
when '.swift' then 'swift'
|
254
255
|
when '.kt' then 'kotlin'
|
255
256
|
when '.java' then 'java'
|
257
|
+
when '.m', '.h' then 'objc'
|
256
258
|
else
|
257
259
|
raise "Unsupported file type for #{file_path}"
|
258
260
|
end
|
data/lib/utils/git.rb
CHANGED
@@ -3,6 +3,7 @@ require 'open3'
|
|
3
3
|
module EmergeCLI
|
4
4
|
module Git
|
5
5
|
def self.branch
|
6
|
+
Logger.debug 'Getting current branch name'
|
6
7
|
command = 'git rev-parse --abbrev-ref HEAD'
|
7
8
|
Logger.debug command
|
8
9
|
stdout, _, status = Open3.capture3(command)
|
@@ -39,6 +40,7 @@ module EmergeCLI
|
|
39
40
|
end
|
40
41
|
|
41
42
|
def self.sha
|
43
|
+
Logger.debug 'Getting current SHA'
|
42
44
|
command = 'git rev-parse HEAD'
|
43
45
|
Logger.debug command
|
44
46
|
stdout, _, status = Open3.capture3(command)
|
@@ -46,6 +48,7 @@ module EmergeCLI
|
|
46
48
|
end
|
47
49
|
|
48
50
|
def self.base_sha
|
51
|
+
Logger.debug 'Getting base SHA'
|
49
52
|
current_branch = branch
|
50
53
|
remote_head = remote_head_branch
|
51
54
|
return nil if current_branch.nil? || remote_head.nil?
|
@@ -59,19 +62,34 @@ module EmergeCLI
|
|
59
62
|
end
|
60
63
|
|
61
64
|
def self.previous_sha
|
65
|
+
Logger.debug 'Getting previous SHA'
|
66
|
+
command = 'git rev-list --count HEAD'
|
67
|
+
Logger.debug command
|
68
|
+
count_stdout, _, count_status = Open3.capture3(command)
|
69
|
+
|
70
|
+
if !count_status.success? || count_stdout.strip.to_i <= 1
|
71
|
+
Logger.error 'Detected shallow clone while trying to get the previous commit. ' \
|
72
|
+
'Please clone with full history using: git clone --no-single-branch ' \
|
73
|
+
'or configure CI with fetch-depth: 0'
|
74
|
+
return nil
|
75
|
+
end
|
76
|
+
|
62
77
|
command = 'git rev-parse HEAD^'
|
63
78
|
Logger.debug command
|
64
|
-
stdout,
|
79
|
+
stdout, stderr, status = Open3.capture3(command)
|
80
|
+
Logger.error "Failed to get previous SHA: #{stdout}, #{stderr}" if !status.success?
|
65
81
|
stdout.strip if status.success?
|
66
82
|
end
|
67
83
|
|
68
84
|
def self.primary_remote
|
85
|
+
Logger.debug 'Getting primary remote'
|
69
86
|
remote = remote()
|
70
87
|
return nil if remote.nil?
|
71
88
|
remote.include?('origin') ? 'origin' : remote.first
|
72
89
|
end
|
73
90
|
|
74
91
|
def self.remote_head_branch(remote = primary_remote)
|
92
|
+
Logger.debug 'Getting remote head branch'
|
75
93
|
return nil if remote.nil?
|
76
94
|
command = "git remote show #{remote}"
|
77
95
|
Logger.debug command
|
@@ -86,6 +104,7 @@ module EmergeCLI
|
|
86
104
|
end
|
87
105
|
|
88
106
|
def self.remote_url(remote = primary_remote)
|
107
|
+
Logger.debug 'Getting remote URL'
|
89
108
|
return nil if remote.nil?
|
90
109
|
command = "git config --get remote.#{remote}.url"
|
91
110
|
Logger.debug command
|
@@ -94,6 +113,7 @@ module EmergeCLI
|
|
94
113
|
end
|
95
114
|
|
96
115
|
def self.remote
|
116
|
+
Logger.debug 'Getting remote'
|
97
117
|
command = 'git remote'
|
98
118
|
Logger.debug command
|
99
119
|
stdout, _, status = Open3.capture3(command)
|