fastlane-plugin-bugsnag 1.4.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bugsnag-dsym-upload +66 -22
- data/lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb +100 -87
- data/lib/fastlane/plugin/bugsnag/actions/upload_symbols_to_bugsnag.rb +86 -38
- data/lib/fastlane/plugin/bugsnag/version.rb +1 -1
- data/spec/bugsnag_upload_dsym_action_spec.rb +57 -40
- data/spec/fixtures/ios_proj/FirstRealFolder/Info.plist +7 -6
- data/spec/fixtures/ios_proj/Project/Info.plist +7 -5
- data/spec/fixtures/ios_proj/aaTests/Info.plist +5 -3
- data/spec/fixtures/ios_proj_legacy/Project/Info.plist +47 -0
- data/spec/send_build_to_bugsnag_spec.rb +54 -38
- metadata +24 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57b215435ee483ac067b8476b40366d4040791e56311709e1e73533f356a0658
|
4
|
+
data.tar.gz: 27f72d72fff57102bc787434b2b06dc5272b7001877d4993e99122fd15a1c5d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ca750f77a93d83b0d786eff7e99a843551bdd4a59ee0585dfe83f2239c777381b33188810844ca1a227cc7e3fd489f90712cf470ab2ad19bc0c05a515caf757
|
7
|
+
data.tar.gz: a819e53c15fa5cfa35da10d4021535b91b5b52eddc56b0a22bb86f8c77ac0c21e2abc46b83d3d5ccad33cf0864ec2bc71aab97a50777058c2177e717a12968ee
|
data/bugsnag-dsym-upload
CHANGED
@@ -13,18 +13,22 @@
|
|
13
13
|
function print_usage() {
|
14
14
|
echo "Usage: $0 [--symbol-maps DIR] dSYMS_PATH"
|
15
15
|
echo
|
16
|
-
echo "-h, --help
|
17
|
-
echo "-v, --verbose
|
18
|
-
echo "--api-key API_KEY
|
19
|
-
echo "--symbol-maps DIR
|
20
|
-
echo "
|
21
|
-
echo "
|
22
|
-
echo "--upload-server URL
|
23
|
-
echo "
|
24
|
-
echo "--project-root DIR
|
25
|
-
echo "
|
26
|
-
echo "
|
27
|
-
echo "
|
16
|
+
echo "-h, --help Displays this message"
|
17
|
+
echo "-v, --verbose Print verbose logging output during execution"
|
18
|
+
echo "--api-key API_KEY The API key of the project the dSYM should be applied to"
|
19
|
+
echo "--symbol-maps DIR Path to a directory of bitcode symbol maps. The"
|
20
|
+
echo " dSYM files will be restored with symbols prior to"
|
21
|
+
echo " upload. Requires dsymutil."
|
22
|
+
echo "--upload-server URL The server receiving dSYM files. Set this value if"
|
23
|
+
echo " using an on-premise Bugsnag installation"
|
24
|
+
echo "--project-root DIR The root directory of the project. This will help to"
|
25
|
+
echo " group error reports by project"
|
26
|
+
echo "--ignore-missing-dwarf Throw warnings instead of errors when a dSYM with missing"
|
27
|
+
echo " DWARF data is found"
|
28
|
+
echo "--ignore-empty-dsym Throw warnings instead of errors when a *.dSYM file is found"
|
29
|
+
echo " rather than the expected *.dSYM directory"
|
30
|
+
echo "dSYMS_PATH A directory or .zip file containing *.dSYM bundles to"
|
31
|
+
echo " upload"
|
28
32
|
}
|
29
33
|
|
30
34
|
function exit_with_usage() {
|
@@ -50,6 +54,8 @@ upload_server=https://upload.bugsnag.com
|
|
50
54
|
unset symbol_maps
|
51
55
|
unset dsym_dir
|
52
56
|
unset verbose
|
57
|
+
unset ignore_empty_dsym
|
58
|
+
unset ignore_missing_dwarf
|
53
59
|
unset silent
|
54
60
|
unset project_root
|
55
61
|
unset api_key
|
@@ -65,6 +71,12 @@ while [[ $# -gt 0 ]]; do
|
|
65
71
|
-v|--verbose)
|
66
72
|
verbose=1
|
67
73
|
shift;;
|
74
|
+
--ignore-missing-dwarf)
|
75
|
+
ignore_missing_dwarf=1
|
76
|
+
shift;;
|
77
|
+
--ignore-empty-dsym)
|
78
|
+
ignore_empty_dsym=1
|
79
|
+
shift;;
|
68
80
|
--symbol-maps)
|
69
81
|
symbol_maps=$2
|
70
82
|
shift
|
@@ -117,11 +129,27 @@ fi
|
|
117
129
|
|
118
130
|
log_verbose "Uploading files to $upload_server"
|
119
131
|
success_count=0
|
132
|
+
warning_count=0
|
120
133
|
fail_count=0
|
121
134
|
|
122
|
-
|
135
|
+
# Find all files and directories matching *.dSYM, except those in dir __MAXCOSX generated when using macOS Archive Utility
|
136
|
+
for dsym in $(find $dsym_dir -name "*.dSYM" ! -path "*/__MACOSX/*"); do
|
123
137
|
log_verbose "Preparing to upload $dsym"
|
124
138
|
|
139
|
+
# check if the .dSYM is a file. Throw an error (warning if --ignore-empty-dsym set) if detected as such;
|
140
|
+
# it should be a directory. This can happen due to a bug in Xcode: https://developer.apple.com/forums/thread/659187
|
141
|
+
if [ -f $dsym ]; then
|
142
|
+
dsym_size=$(wc -c < "$dsym" | xargs)
|
143
|
+
if [[ $ignore_empty_dsym == 1 ]]; then
|
144
|
+
log "[WARNING] Skipping $dsym as it is a file ($dsym_size bytes), not a directory with DWARF data";
|
145
|
+
warning_count=$((warning_count+1))
|
146
|
+
else
|
147
|
+
log "[ERROR] Skipping $dsym as it is a file ($dsym_size bytes), not a directory with DWARF data";
|
148
|
+
fail_count=$((fail_count+1))
|
149
|
+
fi
|
150
|
+
continue
|
151
|
+
fi
|
152
|
+
|
125
153
|
if [[ -d $symbol_maps ]]; then
|
126
154
|
log_verbose "Updating file with bitcode symbol maps in $symbol_maps"
|
127
155
|
dsymutil "$dsym" --symbol-map "$symbol_maps"
|
@@ -129,8 +157,13 @@ for dsym in $dsym_dir/*.dSYM; do
|
|
129
157
|
|
130
158
|
dwarf_data=$dsym/Contents/Resources/DWARF
|
131
159
|
if [[ ! -d $dwarf_data ]]; then
|
132
|
-
|
133
|
-
|
160
|
+
if [[ $ignore_missing_dwarf == 1 ]]; then
|
161
|
+
log "[WARNING] Skipping file missing DWARF data: $dsym"
|
162
|
+
warning_count=$((warning_count+1))
|
163
|
+
else
|
164
|
+
log "[ERROR] Skipping file missing DWARF data: $dsym"
|
165
|
+
fail_count=$((fail_count+1))
|
166
|
+
fi
|
134
167
|
continue
|
135
168
|
fi
|
136
169
|
for file in $dwarf_data/*; do
|
@@ -141,7 +174,7 @@ for dsym in $dsym_dir/*.dSYM; do
|
|
141
174
|
# Attach the api key and project root parameters if they have been provided
|
142
175
|
args=""
|
143
176
|
if [[ ! -z $project_root ]]; then
|
144
|
-
args="-F projectRoot
|
177
|
+
args="-F projectRoot=\"$project_root\" "
|
145
178
|
fi
|
146
179
|
|
147
180
|
if [[ ! -z $api_key ]]; then
|
@@ -150,29 +183,40 @@ for dsym in $dsym_dir/*.dSYM; do
|
|
150
183
|
|
151
184
|
# We need to shell out to perform the curl as there seems to be some indirect
|
152
185
|
# wrapping of this script which causes the command to fail if called directly.
|
153
|
-
curl_cmd="curl --silent --show-error $upload_server -F dsym=@\"$file\" $args"
|
186
|
+
curl_cmd="curl --fail --silent --show-error --http1.1 $upload_server -F 'dsym=@\"$file\"' $args"
|
154
187
|
output=$(sh -c "$curl_cmd")
|
155
188
|
|
156
189
|
if [ $? -eq 0 ] && [ "$output" != "invalid apiKey" ]; then
|
157
190
|
success_count=$((success_count+1))
|
158
191
|
else
|
159
192
|
fail_count=$((fail_count+1))
|
160
|
-
|
193
|
+
log "[ERROR] Failed to upload file: $file"
|
161
194
|
fi
|
162
195
|
echo $output | grep -v '^OK$'
|
163
196
|
log
|
164
197
|
else
|
165
|
-
|
198
|
+
log "[ERROR] Skipping file without UUID: $file"
|
166
199
|
fail_count=$((fail_count+1))
|
167
200
|
fi
|
168
201
|
done
|
169
202
|
done
|
170
203
|
|
204
|
+
exit_code=0
|
171
205
|
if [ $success_count -gt 0 ]; then
|
172
|
-
log "$success_count
|
206
|
+
log "$success_count file(s) uploaded successfully"
|
207
|
+
fi
|
208
|
+
|
209
|
+
if [ $warning_count -gt 0 ]; then
|
210
|
+
log "$warning_count file(s) failed to upload with warnings"
|
173
211
|
fi
|
174
212
|
|
175
213
|
if [ $fail_count -gt 0 ]; then
|
176
|
-
|
177
|
-
|
214
|
+
exit_code=1
|
215
|
+
log "$fail_count file(s) failed to upload with errors"
|
216
|
+
fi
|
217
|
+
|
218
|
+
if [[ $fail_count -gt 0 || $warning_count -gt 0 ]] && [[ $verbose != 1 ]]; then
|
219
|
+
log "Re-run the bugsnag-dsym-upload tool with the --verbose option for more information"
|
178
220
|
fi
|
221
|
+
|
222
|
+
exit $exit_code
|
@@ -9,32 +9,31 @@ module Fastlane
|
|
9
9
|
|
10
10
|
def self.run(params)
|
11
11
|
payload = {buildTool: BUILD_TOOL, sourceControl: {}}
|
12
|
-
if lane_context[:PLATFORM_NAME] == :android
|
13
|
-
payload.merge!(options_from_android_manifest(params[:config_file])) if params[:config_file]
|
14
|
-
else
|
15
|
-
payload.merge!(options_from_info_plist(params[:config_file])) if params[:config_file]
|
16
|
-
end
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
payload[:
|
26
|
-
payload[:
|
13
|
+
# If a configuration file was found or was specified, load in the options:
|
14
|
+
if params[:config_file]
|
15
|
+
UI.message("Loading build information from #{params[:config_file]}")
|
16
|
+
config_options = load_config_file_options(params[:config_file])
|
17
|
+
|
18
|
+
# for each of the config options, if it's not been overriden by any
|
19
|
+
# input to the lane, write it to the payload:
|
20
|
+
payload[:apiKey] = params[:api_key] || config_options[:apiKey]
|
21
|
+
payload[:appVersion] = params[:app_version] || config_options[:appVersion]
|
22
|
+
payload[:appVersionCode] = params[:android_version_code] || config_options[:appVersionCode]
|
23
|
+
payload[:appBundleVersion] = params[:ios_bundle_version] || config_options[:appBundleVersion]
|
24
|
+
payload[:releaseStage] = params[:release_stage] || config_options[:releaseStage] || "production"
|
27
25
|
else
|
28
|
-
#
|
29
|
-
|
30
|
-
|
26
|
+
# No configuration file was found or specified, use the input parameters:
|
27
|
+
payload[:apiKey] = params[:api_key]
|
28
|
+
payload[:appVersion] = params[:app_version]
|
29
|
+
payload[:appVersionCode] = params[:android_version_code]
|
30
|
+
payload[:appBundleVersion] = params[:ios_bundle_version]
|
31
|
+
payload[:releaseStage] = params[:release_stage] || "production"
|
31
32
|
end
|
32
|
-
payload.delete(:config_file)
|
33
|
-
|
34
|
-
# Overwrite automated options with configured if set
|
35
|
-
payload[:releaseStage] = params[:release_stage] unless params[:release_stage].nil?
|
36
|
-
payload[:builderName] = params[:builder]
|
37
33
|
|
34
|
+
# If builder, or source control information has been provided into
|
35
|
+
# Fastlane, apply it to the payload here.
|
36
|
+
payload[:builderName] = params[:builder] if params[:builder]
|
38
37
|
payload[:sourceControl][:revision] = params[:revision] if params[:revision]
|
39
38
|
payload[:sourceControl][:repository] = params[:repository] if params[:repository]
|
40
39
|
payload[:sourceControl][:provider] = params[:provider] if params[:provider]
|
@@ -47,6 +46,13 @@ module Fastlane
|
|
47
46
|
if payload[:appVersion].nil?
|
48
47
|
UI.user_error! missing_app_version_message(params)
|
49
48
|
end
|
49
|
+
|
50
|
+
# If verbose flag is enabled (`--verbose`), display the payload debug info
|
51
|
+
UI.verbose("Sending build to Bugsnag with payload:")
|
52
|
+
payload.each do |param|
|
53
|
+
UI.verbose(" #{param[0].to_s.rjust(18)}: #{param[1]}")
|
54
|
+
end
|
55
|
+
|
50
56
|
send_notification(params[:endpoint], ::JSON.dump(payload))
|
51
57
|
end
|
52
58
|
|
@@ -60,16 +66,16 @@ module Fastlane
|
|
60
66
|
end
|
61
67
|
else
|
62
68
|
if params[:config_file]
|
63
|
-
message << "Set
|
69
|
+
message << "Set bugsnag.apiKey in your Info.plist file to detect API key automatically."
|
64
70
|
else
|
65
|
-
message << "Set the config_file option with the path to your Info.plist and set
|
71
|
+
message << "Set the config_file option with the path to your Info.plist and set bugsnag.apiKey in it to detect API key automatically."
|
66
72
|
end
|
67
73
|
end
|
68
74
|
message
|
69
75
|
end
|
70
76
|
|
71
77
|
def self.missing_app_version_message(params)
|
72
|
-
message = "An app version must be specified release a build."
|
78
|
+
message = "An app version must be specified release a build. "
|
73
79
|
if lane_context[:PLATFORM_NAME] == :android
|
74
80
|
if params[:config_file]
|
75
81
|
message << "Set com.bugsnag.android.APP_VERSION in your AndroidManifest.xml to detect this value automatically."
|
@@ -115,32 +121,27 @@ module Fastlane
|
|
115
121
|
end
|
116
122
|
|
117
123
|
def self.available_options
|
118
|
-
|
124
|
+
git_options = load_git_remote_options
|
119
125
|
[
|
120
126
|
FastlaneCore::ConfigItem.new(key: :config_file,
|
121
127
|
description: "AndroidManifest.xml/Info.plist location",
|
122
128
|
optional: true,
|
123
|
-
default_value:
|
129
|
+
default_value: default_config_file_path),
|
124
130
|
FastlaneCore::ConfigItem.new(key: :api_key,
|
125
131
|
description: "Bugsnag API Key",
|
126
|
-
optional: true,
|
127
|
-
default_value: options[:apiKey]),
|
132
|
+
optional: true),
|
128
133
|
FastlaneCore::ConfigItem.new(key: :app_version,
|
129
134
|
description: "App version being built",
|
130
|
-
optional: true,
|
131
|
-
default_value: options[:appVersion]),
|
135
|
+
optional: true),
|
132
136
|
FastlaneCore::ConfigItem.new(key: :android_version_code,
|
133
137
|
description: "Android app version code",
|
134
|
-
optional: true,
|
135
|
-
default_value: options[:appVersionCode]),
|
138
|
+
optional: true),
|
136
139
|
FastlaneCore::ConfigItem.new(key: :ios_bundle_version,
|
137
140
|
description: "iOS/macOS/tvOS bundle version",
|
138
|
-
optional: true,
|
139
|
-
default_value: options[:appBundleVersion]),
|
141
|
+
optional: true),
|
140
142
|
FastlaneCore::ConfigItem.new(key: :release_stage,
|
141
143
|
description: "Release stage being built, i.e. staging, production",
|
142
|
-
optional: true,
|
143
|
-
default_value: options[:releaseStage] || "production"),
|
144
|
+
optional: true),
|
144
145
|
FastlaneCore::ConfigItem.new(key: :builder,
|
145
146
|
description: "The name of the entity triggering the build",
|
146
147
|
optional: true,
|
@@ -148,19 +149,19 @@ module Fastlane
|
|
148
149
|
FastlaneCore::ConfigItem.new(key: :repository,
|
149
150
|
description: "The source control repository URL for this application",
|
150
151
|
optional: true,
|
151
|
-
default_value:
|
152
|
+
default_value: git_options[:repository]),
|
152
153
|
FastlaneCore::ConfigItem.new(key: :revision,
|
153
154
|
description: "The source control revision id",
|
154
155
|
optional: true,
|
155
|
-
default_value:
|
156
|
+
default_value: git_options[:revision]),
|
156
157
|
FastlaneCore::ConfigItem.new(key: :provider,
|
157
158
|
description: "The source control provider, one of 'github-enterprise', 'gitlab-onpremise', or 'bitbucket-server', if any",
|
158
159
|
optional: true,
|
159
160
|
default_value: nil,
|
160
161
|
verify_block: proc do |value|
|
161
|
-
valid = ['github-enterprise', 'gitlab-onpremise', 'bitbucket-server'].include? value
|
162
|
+
valid = ['github', 'github-enterprise', 'gitlab', 'gitlab-onpremise', 'bitbucket', 'bitbucket-server'].include? value
|
162
163
|
unless valid
|
163
|
-
UI.user_error!("Provider must be one of 'github-enterprise', 'gitlab-onpremise', 'bitbucket-server', or unspecified")
|
164
|
+
UI.user_error!("Provider must be one of 'github', 'github-enterprise', 'gitlab', 'gitlab-onpremise', 'bitbucket', 'bitbucket-server', or unspecified")
|
164
165
|
end
|
165
166
|
end),
|
166
167
|
FastlaneCore::ConfigItem.new(key: :endpoint,
|
@@ -172,69 +173,98 @@ module Fastlane
|
|
172
173
|
|
173
174
|
private
|
174
175
|
|
175
|
-
|
176
|
-
|
176
|
+
# Get any Git options (remote repo, and revision) from the directory
|
177
|
+
def self.load_git_remote_options
|
178
|
+
git_options = {repository:nil, revision:nil}
|
179
|
+
require "git"
|
180
|
+
begin
|
181
|
+
repo = Git.open(Dir.pwd)
|
182
|
+
origin = repo.remotes.detect {|r| r.name == "origin"}
|
183
|
+
origin = repo.remotes.first unless origin
|
184
|
+
if origin
|
185
|
+
git_options[:repository] = origin.url
|
186
|
+
git_options[:revision] = repo.revparse("HEAD")
|
187
|
+
end
|
188
|
+
rescue
|
189
|
+
end
|
190
|
+
return git_options
|
191
|
+
end
|
192
|
+
|
193
|
+
# Used to get a default configuration file (AndroidManifest.xml or Info.plist)
|
194
|
+
def self.default_config_file_path
|
177
195
|
case lane_context[:PLATFORM_NAME]
|
178
196
|
when nil
|
179
197
|
if file_path = default_android_manifest_path
|
180
|
-
|
198
|
+
return file_path
|
181
199
|
elsif file_path = default_info_plist_path
|
182
|
-
|
200
|
+
return file_path
|
183
201
|
end
|
184
202
|
when :android
|
185
203
|
if file_path = default_android_manifest_path
|
186
|
-
|
204
|
+
return file_path
|
187
205
|
end
|
188
206
|
else
|
189
207
|
if file_path = default_info_plist_path
|
190
|
-
|
208
|
+
return file_path
|
191
209
|
end
|
192
210
|
end
|
211
|
+
end
|
193
212
|
|
194
|
-
|
195
|
-
|
213
|
+
def self.load_config_file_options config_file
|
214
|
+
options = {}
|
215
|
+
case File.extname(config_file)
|
216
|
+
when ".xml"
|
217
|
+
options = load_options_from_xml(config_file)
|
218
|
+
return options
|
219
|
+
when ".plist"
|
220
|
+
options = load_options_from_plist(config_file)
|
221
|
+
return options
|
222
|
+
else
|
223
|
+
UI.user_error("File type of '#{config_file}' was not recognised. This should be .xml for Android and .plist for Cococa")
|
196
224
|
end
|
197
|
-
options
|
198
225
|
end
|
199
226
|
|
200
|
-
def self.
|
201
|
-
|
202
|
-
build_gradle_path = Dir.glob("{android/,}app/build.gradle").first
|
203
|
-
build_gradle_path ||= Dir.glob("build.gradle").first
|
204
|
-
options.merge!(options_from_build_gradle(build_gradle_path)) if build_gradle_path
|
205
|
-
options
|
227
|
+
def self.default_info_plist_path
|
228
|
+
Dir.glob("./{ios/,}*/Info.plist").reject {|path| path =~ /build|test/i }.sort.first
|
206
229
|
end
|
207
230
|
|
208
|
-
def self.
|
209
|
-
|
231
|
+
def self.load_options_from_plist file_path
|
232
|
+
options = {}
|
233
|
+
plist_getter = Fastlane::Actions::GetInfoPlistValueAction
|
234
|
+
bugsnag_dict = plist_getter.run(path: file_path, key: "bugsnag")
|
235
|
+
api_key = bugsnag_dict["apiKey"] unless bugsnag_dict.nil?
|
236
|
+
release_stage = bugsnag_dict["releaseStage"] unless bugsnag_dict.nil?
|
237
|
+
if api_key.nil?
|
238
|
+
api_key = plist_getter.run(path: file_path, key: "BugsnagAPIKey")
|
239
|
+
end
|
240
|
+
options[:apiKey] = api_key
|
241
|
+
options[:releaseStage] = release_stage
|
242
|
+
options[:appVersion] = plist_getter.run(path: file_path, key: "CFBundleShortVersionString")
|
243
|
+
options[:appBundleVersion] = plist_getter.run(path: file_path, key: "CFBundleVersion")
|
244
|
+
return options
|
210
245
|
end
|
211
246
|
|
212
|
-
def self.
|
213
|
-
Dir.glob("./{
|
247
|
+
def self.default_android_manifest_path
|
248
|
+
Dir.glob("./{android/,}{app,}/src/main/AndroidManifest.xml").sort.first
|
214
249
|
end
|
215
250
|
|
216
|
-
def self.
|
217
|
-
|
218
|
-
{
|
219
|
-
|
220
|
-
|
221
|
-
appBundleVersion: plist_getter.run(path: file_path, key: "CFBundleVersion"),
|
222
|
-
config_file: file_path,
|
223
|
-
}
|
251
|
+
def self.load_options_from_xml file_path
|
252
|
+
options = options_from_android_manifest(file_path)
|
253
|
+
build_gradle_path = Dir.glob("{android/,}app/build.gradle").sort.first || Dir.glob("build.gradle").sort.first
|
254
|
+
options.merge!(options_from_build_gradle(build_gradle_path)) if build_gradle_path
|
255
|
+
return options
|
224
256
|
end
|
225
257
|
|
226
258
|
def self.options_from_android_manifest file_path
|
227
259
|
options = {}
|
228
260
|
begin
|
229
261
|
meta_data = parse_android_manifest_options(XmlSimple.xml_in(file_path))
|
230
|
-
|
231
262
|
options[:apiKey] = meta_data["com.bugsnag.android.API_KEY"]
|
232
263
|
options[:appVersion] = meta_data["com.bugsnag.android.APP_VERSION"]
|
233
264
|
options[:releaseStage] = meta_data["com.bugsnag.android.RELEASE_STAGE"]
|
234
265
|
rescue ArgumentError
|
235
266
|
nil
|
236
267
|
end
|
237
|
-
options[:config_file] = file_path
|
238
268
|
options
|
239
269
|
end
|
240
270
|
|
@@ -253,23 +283,6 @@ module Fastlane
|
|
253
283
|
options
|
254
284
|
end
|
255
285
|
|
256
|
-
def self.git_remote_options
|
257
|
-
require "git"
|
258
|
-
begin
|
259
|
-
repo = Git.open(Dir.pwd)
|
260
|
-
origin = repo.remotes.detect {|r| r.name == "origin"}
|
261
|
-
origin = repo.remotes.first unless origin
|
262
|
-
if origin
|
263
|
-
return {
|
264
|
-
repository: origin.url,
|
265
|
-
revision: repo.revparse("HEAD"),
|
266
|
-
}
|
267
|
-
end
|
268
|
-
rescue
|
269
|
-
end
|
270
|
-
nil
|
271
|
-
end
|
272
|
-
|
273
286
|
def self.parse_android_manifest_options config_hash
|
274
287
|
map_meta_data(get_meta_data(config_hash))
|
275
288
|
end
|
@@ -6,21 +6,27 @@ module Fastlane
|
|
6
6
|
File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'bugsnag-dsym-upload'))
|
7
7
|
|
8
8
|
def self.run(params)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
if
|
13
|
-
|
14
|
-
|
15
|
-
else
|
16
|
-
# Print which file is populating version and API key information since the value has been
|
17
|
-
# overridden
|
18
|
-
UI.message("Loading API key from #{params[:config_file]}")
|
9
|
+
# If we have not explicitly set an API key through env, or parameter
|
10
|
+
# input in Fastfile, find an API key in the Info.plist in config_file param
|
11
|
+
api_key = params[:api_key]
|
12
|
+
if params[:config_file] && params[:api_key] == nil
|
13
|
+
UI.message("Using the API Key from #{params[:config_file]}")
|
14
|
+
api_key = options_from_info_plist(params[:config_file])[:apiKey]
|
19
15
|
end
|
20
16
|
|
17
|
+
# If verbose flag is enabled (`--verbose`), display the plugin action debug info
|
18
|
+
# Store the verbose flag for use in the upload arguments.
|
19
|
+
verbose = UI.verbose("Uploading dSYMs to Bugsnag with the following parameters:")
|
20
|
+
rjust = 30 # set justification width for keys for the list of parameters to output
|
21
|
+
params.values.each do |param|
|
22
|
+
UI.verbose(" #{param[0].to_s.rjust(rjust)}: #{param[1]}")
|
23
|
+
end
|
24
|
+
UI.verbose(" #{"SharedValues::DSYM_PATHS".to_s.rjust(rjust)}: #{gym_dsyms? ? Actions.lane_context[SharedValues::DSYM_PATHS] : "not set"}")
|
25
|
+
UI.verbose(" #{"SharedValues::DSYM_OUTPUT_PATH".to_s.rjust(rjust)}: #{download_dsym_dsyms? ? Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH] : "not set"}")
|
26
|
+
|
21
27
|
parse_dsym_paths(params[:dsym_path]).each do |dsym_path|
|
22
28
|
if dsym_path.end_with?(".zip") or File.directory?(dsym_path)
|
23
|
-
args = upload_args(dsym_path, params[:symbol_maps_path], params[:upload_url], params[:project_root],
|
29
|
+
args = upload_args(dsym_path, params[:symbol_maps_path], params[:upload_url], params[:project_root], api_key, verbose, params[:ignore_missing_dwarf], params[:ignore_empty_dsym])
|
24
30
|
success = Kernel.system(UPLOAD_SCRIPT_PATH, *args)
|
25
31
|
if success
|
26
32
|
UI.success("Uploaded dSYMs in #{dsym_path}")
|
@@ -34,11 +40,11 @@ module Fastlane
|
|
34
40
|
end
|
35
41
|
|
36
42
|
def self.description
|
37
|
-
"Uploads symbol files to Bugsnag"
|
43
|
+
"Uploads dSYM debug symbol files to Bugsnag"
|
38
44
|
end
|
39
45
|
|
40
46
|
def self.authors
|
41
|
-
["kattrali"]
|
47
|
+
["kattrali", "xander-jones"]
|
42
48
|
end
|
43
49
|
|
44
50
|
def self.return_value
|
@@ -66,33 +72,31 @@ module Fastlane
|
|
66
72
|
end
|
67
73
|
validate_symbol_maps = proc do |path|
|
68
74
|
return if path.nil?
|
69
|
-
|
70
75
|
unless File.exist?(path) and File.directory?(path)
|
71
76
|
UI.user_error!("Symbol maps file needs to be a directory containing symbol map files")
|
72
77
|
end
|
73
78
|
end
|
74
79
|
validate_api_key = proc do |key|
|
75
80
|
return if key.nil?
|
76
|
-
|
77
81
|
unless !key[/\H/] and key.length == 32
|
78
|
-
UI.user_error!("API key should be a 32 character
|
82
|
+
UI.user_error!("API key should be a 32 character hexadecimal string")
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
|
-
|
83
|
-
|
86
|
+
# If the Info.plist is in a default location, we'll get API key here
|
87
|
+
# This will be overwritten if you pass in an API key parameter in your
|
88
|
+
# Fastfile, or have an API key environment variable set.
|
84
89
|
[
|
85
90
|
FastlaneCore::ConfigItem.new(key: :api_key,
|
86
91
|
env_name: "BUGSNAG_API_KEY",
|
87
92
|
description: "Bugsnag API Key",
|
88
93
|
optional: true,
|
89
|
-
default_value: options[:apiKey],
|
90
94
|
verify_block: validate_api_key),
|
91
95
|
FastlaneCore::ConfigItem.new(key: :dsym_path,
|
92
96
|
type: Array,
|
93
97
|
env_name: "BUGSNAG_DSYM_PATH",
|
94
|
-
description: "Path to the
|
95
|
-
default_value:
|
98
|
+
description: "Path to the dSYM directory, file, or zip to upload",
|
99
|
+
default_value: default_dsym_paths,
|
96
100
|
optional: true,
|
97
101
|
verify_block: validate_dsym_path),
|
98
102
|
FastlaneCore::ConfigItem.new(key: :upload_url,
|
@@ -111,34 +115,53 @@ module Fastlane
|
|
111
115
|
description: "Root path of the project",
|
112
116
|
default_value: Dir::pwd,
|
113
117
|
optional: true),
|
118
|
+
FastlaneCore::ConfigItem.new(key: :ignore_missing_dwarf,
|
119
|
+
env_name: "BUGSNAG_IGNORE_MISSING_DWARF",
|
120
|
+
description: "Throw warnings instead of errors when a dSYM with missing DWARF data is found",
|
121
|
+
optional: true,
|
122
|
+
default_value: false,
|
123
|
+
is_string: false),
|
124
|
+
FastlaneCore::ConfigItem.new(key: :ignore_empty_dsym,
|
125
|
+
env_name: "BUGSNAG_IGNORE_EMPTY_DSYM",
|
126
|
+
description: "Throw warnings instead of errors when a *.dSYM file is found rather than the expected *.dSYM directory",
|
127
|
+
optional: true,
|
128
|
+
default_value: false,
|
129
|
+
is_string: false),
|
114
130
|
FastlaneCore::ConfigItem.new(key: :config_file,
|
131
|
+
env_name: "BUGSNAG_CONFIG_FILE",
|
115
132
|
description: "Info.plist location",
|
116
133
|
optional: true,
|
117
|
-
default_value:
|
118
|
-
FastlaneCore::ConfigItem.new(key: :verbose,
|
119
|
-
env_name: "BUGSNAG_VERBOSE",
|
120
|
-
description: "Print helpful debug info",
|
121
|
-
skip_type_validation: true,
|
122
|
-
optional: true),
|
134
|
+
default_value: default_info_plist_path)
|
123
135
|
]
|
124
136
|
end
|
125
137
|
|
126
138
|
private
|
127
139
|
|
128
140
|
def self.default_info_plist_path
|
129
|
-
|
141
|
+
# Find first 'Info.plist' in the current working directory
|
142
|
+
# ignoring any in 'build', or 'test' folders
|
143
|
+
return Dir.glob("./{ios/,}*/Info.plist").reject{|path| path =~ /build|test/i }.sort.first
|
130
144
|
end
|
131
145
|
|
132
146
|
def self.options_from_info_plist file_path
|
133
147
|
plist_getter = Fastlane::Actions::GetInfoPlistValueAction
|
148
|
+
bugsnag_dict = plist_getter.run(path: file_path, key: "bugsnag")
|
149
|
+
api_key = bugsnag_dict["apiKey"] unless bugsnag_dict.nil?
|
150
|
+
# From v6.0.0 of bugsnag-cocoa, the API key is in 'bugsnag.apiKey',
|
151
|
+
# use 'BugsnagAPIKey' as a fallback if it exists (<v6.x.x)
|
152
|
+
if api_key.nil?
|
153
|
+
api_key = plist_getter.run(path: file_path, key: "BugsnagAPIKey")
|
154
|
+
end
|
134
155
|
{
|
135
|
-
apiKey:
|
156
|
+
apiKey: api_key,
|
136
157
|
config_file: file_path,
|
137
158
|
}
|
138
159
|
end
|
139
160
|
|
140
|
-
def self.upload_args dir, symbol_maps_dir, upload_url, project_root, api_key, verbose
|
161
|
+
def self.upload_args dir, symbol_maps_dir, upload_url, project_root, api_key, verbose, ignore_missing_dwarf, ignore_empty_dsym
|
141
162
|
args = [verbose ? "--verbose" : "--silent"]
|
163
|
+
args += ["--ignore-missing-dwarf"] if ignore_missing_dwarf
|
164
|
+
args += ["--ignore-empty-dsym"] if ignore_empty_dsym
|
142
165
|
args += ["--api-key", api_key] unless api_key.nil?
|
143
166
|
args += ["--upload-server", upload_url] unless upload_url.nil?
|
144
167
|
args += ["--symbol-maps", symbol_maps_dir] unless symbol_maps_dir.nil?
|
@@ -147,19 +170,44 @@ module Fastlane
|
|
147
170
|
args
|
148
171
|
end
|
149
172
|
|
173
|
+
# returns an array of unique dSYM-containing directory paths to upload
|
150
174
|
def self.parse_dsym_paths dsym_path
|
151
|
-
|
152
|
-
dsym_paths.compact.map do |path|
|
175
|
+
dsym_path.compact.map do |path|
|
153
176
|
path.end_with?(".dSYM") ? File.dirname(path) : path
|
154
177
|
end.uniq
|
155
178
|
end
|
156
179
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
180
|
+
# if input is an Array, return that array, else coerce the input into an array
|
181
|
+
def self.coerce_array dsym_path
|
182
|
+
dsym_path.is_a?(Array) ? dsym_path : [dsym_path]
|
183
|
+
end
|
184
|
+
|
185
|
+
# returns true if `gym` created some dSYMs for us to upload
|
186
|
+
# https://docs.fastlane.tools/actions/gym/#lane-variables
|
187
|
+
def self.gym_dsyms?
|
188
|
+
if defined?(SharedValues::DSYM_PATHS)
|
189
|
+
if Actions.lane_context[SharedValues::DSYM_PATHS]
|
190
|
+
true
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# returns true if `download_dsyms` created some dSYMs for us to upload
|
196
|
+
# https://docs.fastlane.tools/actions/download_dsyms/#lane-variables
|
197
|
+
def self.download_dsym_dsyms?
|
198
|
+
if defined?(SharedValues::DSYM_OUTPUT_PATH)
|
199
|
+
if Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]
|
200
|
+
true
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.default_dsym_paths
|
206
|
+
vendor_regex = %r{\.\/vendor.*}
|
207
|
+
paths = Dir["./**/*.dSYM.zip"].reject{|f| f[vendor_regex] } + Dir["./**/*.dSYM"].reject{|f| f[vendor_regex] } # scrape the sub directories for zips and dSYMs
|
208
|
+
paths += coerce_array(Actions.lane_context[SharedValues::DSYM_PATHS]) if gym_dsyms? # set by `download_dsyms` Fastlane action
|
209
|
+
paths += coerce_array(Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH]) if download_dsym_dsyms? # set by `gym` Fastlane action
|
210
|
+
parse_dsym_paths(paths.uniq)
|
163
211
|
end
|
164
212
|
end
|
165
213
|
end
|
@@ -3,10 +3,8 @@ require 'spec_helper'
|
|
3
3
|
Action = Fastlane::Actions::UploadSymbolsToBugsnagAction
|
4
4
|
|
5
5
|
describe Action do
|
6
|
-
def
|
7
|
-
Action.
|
8
|
-
[x.key, x.default_value]
|
9
|
-
end.to_h
|
6
|
+
def run_with args
|
7
|
+
Action.run(Fastlane::ConfigurationHelper.parse(Action, args))
|
10
8
|
end
|
11
9
|
|
12
10
|
context "the packaged gem" do
|
@@ -27,51 +25,43 @@ describe Action do
|
|
27
25
|
describe '#run' do
|
28
26
|
it 'silences script output by default' do
|
29
27
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
30
|
-
"--silent",
|
31
|
-
"--project-root", Dir::pwd,
|
32
|
-
FIXTURE_PATH).and_return(true)
|
33
|
-
Action.run(load_default_opts.merge({dsym_path: FIXTURE_PATH}))
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'prints verbose if verbose flag set' do
|
37
|
-
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
38
|
-
"--verbose",
|
28
|
+
"--silent",
|
39
29
|
"--project-root", Dir::pwd,
|
40
30
|
FIXTURE_PATH).and_return(true)
|
41
|
-
|
31
|
+
run_with({dsym_path: FIXTURE_PATH})
|
42
32
|
end
|
43
33
|
|
44
34
|
it 'UI.user_error when script fails' do
|
45
35
|
expect(Fastlane::UI).to receive(:user_error!).with("Failed uploading #{FIXTURE_PATH}")
|
46
36
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
47
|
-
|
48
|
-
|
37
|
+
"--silent",
|
38
|
+
"--project-root", Dir::pwd,
|
39
|
+
FIXTURE_PATH).and_return(false)
|
40
|
+
run_with({dsym_path: FIXTURE_PATH})
|
49
41
|
end
|
50
42
|
|
51
43
|
it 'requires the dSYM file path to exist' do
|
52
|
-
expect(Fastlane::UI).to receive(:user_error!)
|
53
|
-
|
54
|
-
Action.run(load_default_opts.merge({dsym_path: 'fake/file/path'}))
|
44
|
+
expect(Fastlane::UI).to receive(:user_error!).at_least(:once)
|
45
|
+
run_with({dsym_path: 'fake/file/path'})
|
55
46
|
end
|
56
47
|
|
57
48
|
it 'rejects dSYM files which are not a .zip or a directory' do
|
58
|
-
expect(Fastlane::UI).to receive(:user_error!)
|
59
|
-
|
60
|
-
Action.run(load_default_opts.merge({dsym_path: File.join(FIXTURE_PATH, 'invalid_file')}))
|
49
|
+
expect(Fastlane::UI).to receive(:user_error!).at_least(:once)
|
50
|
+
run_with({dsym_path: File.join(FIXTURE_PATH, 'invalid_file')})
|
61
51
|
end
|
62
52
|
|
63
53
|
it 'uploads a single .dSYM file' do
|
64
54
|
directory = File.join(FIXTURE_PATH, 'dSYMs')
|
65
55
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
66
56
|
"--silent", "--project-root", Dir::pwd, directory).and_return(true)
|
67
|
-
|
57
|
+
run_with({dsym_path: File.join(FIXTURE_PATH, 'dSYMs/app.dSYM')})
|
68
58
|
end
|
69
59
|
|
70
60
|
it 'uploads a .zip of .dSYM files' do
|
71
61
|
path = File.join(FIXTURE_PATH, 'files.zip')
|
72
62
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
73
63
|
"--silent", "--project-root", Dir::pwd, path).and_return(true)
|
74
|
-
|
64
|
+
run_with({dsym_path: path})
|
75
65
|
end
|
76
66
|
|
77
67
|
it 'uploads multiple .zip files' do
|
@@ -81,7 +71,7 @@ describe Action do
|
|
81
71
|
"--silent", "--project-root", Dir::pwd, zip1).and_return(true)
|
82
72
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
83
73
|
"--silent", "--project-root", Dir::pwd, zip2).and_return(true)
|
84
|
-
|
74
|
+
run_with({dsym_path: [zip1, zip2]})
|
85
75
|
end
|
86
76
|
|
87
77
|
it 'uploads multiple .dSYM files multiple directories' do
|
@@ -94,7 +84,7 @@ describe Action do
|
|
94
84
|
"--silent", "--project-root", Dir::pwd, directories[0]).and_return(true)
|
95
85
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
96
86
|
"--silent", "--project-root", Dir::pwd, directories[1]).and_return(true)
|
97
|
-
|
87
|
+
run_with({dsym_path: [dsym1, dsym2, dsym3, dsym4]})
|
98
88
|
end
|
99
89
|
|
100
90
|
it 'uploads multiple .dSYM files in a single directory' do
|
@@ -103,7 +93,7 @@ describe Action do
|
|
103
93
|
directory = File.join(FIXTURE_PATH, 'dSYMs')
|
104
94
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
105
95
|
"--silent", "--project-root", Dir::pwd, directory).and_return(true)
|
106
|
-
|
96
|
+
run_with({dsym_path: [dsym1, dsym2]})
|
107
97
|
end
|
108
98
|
|
109
99
|
it 'accepts a project root argument' do
|
@@ -112,48 +102,75 @@ describe Action do
|
|
112
102
|
"--silent",
|
113
103
|
"--project-root", root_path,
|
114
104
|
FIXTURE_PATH).and_return(true)
|
115
|
-
|
105
|
+
run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
|
116
106
|
end
|
117
107
|
|
118
108
|
it 'accepts an API key argument' do
|
119
|
-
api_key = "
|
109
|
+
api_key = "123456789123456789001234567890FF"
|
120
110
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
121
111
|
"--silent",
|
122
112
|
"--api-key", api_key,
|
123
113
|
"--project-root", Dir::pwd,
|
124
114
|
FIXTURE_PATH).and_return(true)
|
125
|
-
|
115
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key})
|
126
116
|
end
|
127
117
|
|
128
118
|
it 'accepts an API key argument with no project root' do
|
129
|
-
api_key = "
|
119
|
+
api_key = "123456789012345678901234567890FF"
|
130
120
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
131
121
|
"--silent",
|
132
122
|
"--api-key", api_key,
|
123
|
+
"--project-root", `pwd`.chomp,
|
133
124
|
FIXTURE_PATH).and_return(true)
|
134
|
-
|
125
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key, project_root: nil})
|
135
126
|
end
|
136
127
|
|
137
128
|
it 'uses default API key argument from plist' do
|
138
129
|
root_path = "/test/test/test"
|
130
|
+
api_key = "12345678901234567890123456789AAA" # Uses the API Key from ./spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|
139
131
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
140
132
|
"--silent",
|
141
|
-
"--api-key",
|
133
|
+
"--api-key", api_key,
|
142
134
|
"--project-root", root_path,
|
143
135
|
FIXTURE_PATH).and_return(true)
|
144
136
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
145
|
-
|
137
|
+
run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
|
146
138
|
end
|
147
139
|
end
|
148
|
-
|
149
|
-
it '
|
140
|
+
|
141
|
+
it 'uses legacy API key argument from plist' do
|
142
|
+
root_path = "/test/test/test"
|
143
|
+
api_key = "12345678901234567890123456789BBB" # Uses the API Key from ./spec/fixtures/ios_proj_legacy/Project/Info.plist
|
150
144
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
151
145
|
"--silent",
|
152
|
-
"--api-key",
|
146
|
+
"--api-key", api_key,
|
147
|
+
"--project-root", root_path,
|
148
|
+
FIXTURE_PATH).and_return(true)
|
149
|
+
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj_legacy')) do
|
150
|
+
run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'allows option input to overwrite the config file api key' do
|
155
|
+
# The order of precedence is 1. option input, 2. env variable, 3. default or config file input (for api key only)
|
156
|
+
# The API key in ./spec/fixtures/ios_proj_legacy/Project/Info.plist is 12345678912345678900123456789AAA.
|
157
|
+
api_key = "12345678912345678900123456789CCC"
|
158
|
+
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
159
|
+
"--silent",
|
160
|
+
"--api-key", api_key,
|
153
161
|
"--project-root", File.join(FIXTURE_PATH, 'ios_proj'),
|
154
162
|
FIXTURE_PATH).and_return(true)
|
155
163
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
156
|
-
|
164
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key, config_file: File.join('Project', 'Info.plist')})
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'reject an invalid API key' do
|
169
|
+
api_key = "this-is-not-a-hex-key"
|
170
|
+
expect(Fastlane::UI).to receive(:user_error!).with("API key should be a 32 character hexadecimal string")
|
171
|
+
expect(Fastlane::UI).to receive(:user_error!).with("Failed uploading #{FIXTURE_PATH}")
|
172
|
+
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
173
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key, config_file: File.join('Project', 'Info.plist')})
|
157
174
|
end
|
158
175
|
end
|
159
176
|
|
@@ -164,7 +181,7 @@ describe Action do
|
|
164
181
|
"--upload-server", "http://myserver.example.com",
|
165
182
|
"--project-root", Dir::pwd,
|
166
183
|
FIXTURE_PATH).and_return(true)
|
167
|
-
|
184
|
+
run_with({dsym_path: FIXTURE_PATH, upload_url: "http://myserver.example.com"})
|
168
185
|
end
|
169
186
|
end
|
170
187
|
|
@@ -176,7 +193,7 @@ describe Action do
|
|
176
193
|
"--symbol-maps", path,
|
177
194
|
"--project-root", Dir::pwd,
|
178
195
|
FIXTURE_PATH).and_return(true)
|
179
|
-
|
196
|
+
run_with({dsym_path: FIXTURE_PATH, symbol_maps_path: path})
|
180
197
|
end
|
181
198
|
end
|
182
199
|
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
3
|
<plist version="1.0">
|
4
4
|
<dict>
|
5
|
-
|
6
|
-
|
5
|
+
<key>bugsnag</key>
|
6
|
+
<dict>
|
7
|
+
<key>apiKey</key>
|
8
|
+
<string>12345678901234567890123456789AAA</string>
|
9
|
+
</dict>
|
7
10
|
<key>CFBundleDevelopmentRegion</key>
|
8
11
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
9
12
|
<key>CFBundleExecutable</key>
|
@@ -17,9 +20,9 @@
|
|
17
20
|
<key>CFBundlePackageType</key>
|
18
21
|
<string>APPL</string>
|
19
22
|
<key>CFBundleShortVersionString</key>
|
20
|
-
<string>
|
23
|
+
<string>2.0-other</string>
|
21
24
|
<key>CFBundleVersion</key>
|
22
|
-
<string>
|
25
|
+
<string>22</string>
|
23
26
|
<key>LSRequiresIPhoneOS</key>
|
24
27
|
<true/>
|
25
28
|
<key>UILaunchStoryboardName</key>
|
@@ -45,5 +48,3 @@
|
|
45
48
|
</array>
|
46
49
|
</dict>
|
47
50
|
</plist>
|
48
|
-
|
49
|
-
|
@@ -2,8 +2,11 @@
|
|
2
2
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
3
|
<plist version="1.0">
|
4
4
|
<dict>
|
5
|
-
|
6
|
-
|
5
|
+
<key>bugsnag</key>
|
6
|
+
<dict>
|
7
|
+
<key>apiKey</key>
|
8
|
+
<string>12345678901234567890123456789DDD</string>
|
9
|
+
</dict>
|
7
10
|
<key>CFBundleDevelopmentRegion</key>
|
8
11
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
9
12
|
<key>CFBundleExecutable</key>
|
@@ -17,9 +20,9 @@
|
|
17
20
|
<key>CFBundlePackageType</key>
|
18
21
|
<string>APPL</string>
|
19
22
|
<key>CFBundleShortVersionString</key>
|
20
|
-
<string>
|
23
|
+
<string>3.0-project</string>
|
21
24
|
<key>CFBundleVersion</key>
|
22
|
-
<string>
|
25
|
+
<string>33</string>
|
23
26
|
<key>LSRequiresIPhoneOS</key>
|
24
27
|
<true/>
|
25
28
|
<key>UILaunchStoryboardName</key>
|
@@ -45,4 +48,3 @@
|
|
45
48
|
</array>
|
46
49
|
</dict>
|
47
50
|
</plist>
|
48
|
-
|
@@ -2,8 +2,11 @@
|
|
2
2
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
3
|
<plist version="1.0">
|
4
4
|
<dict>
|
5
|
-
|
6
|
-
|
5
|
+
<key>bugsnag</key>
|
6
|
+
<dict>
|
7
|
+
<key>apiKey</key>
|
8
|
+
<string>12345678901234567890123456789EEE</string>
|
9
|
+
</dict>
|
7
10
|
<key>CFBundleDevelopmentRegion</key>
|
8
11
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
9
12
|
<key>CFBundleExecutable</key>
|
@@ -45,4 +48,3 @@
|
|
45
48
|
</array>
|
46
49
|
</dict>
|
47
50
|
</plist>
|
48
|
-
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>BugsnagAPIKey</key>
|
6
|
+
<string>12345678901234567890123456789BBB</string>
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
8
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
9
|
+
<key>CFBundleExecutable</key>
|
10
|
+
<string>$(EXECUTABLE_NAME)</string>
|
11
|
+
<key>CFBundleIdentifier</key>
|
12
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
13
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
14
|
+
<string>6.0</string>
|
15
|
+
<key>CFBundleName</key>
|
16
|
+
<string>$(PRODUCT_NAME)</string>
|
17
|
+
<key>CFBundlePackageType</key>
|
18
|
+
<string>APPL</string>
|
19
|
+
<key>CFBundleShortVersionString</key>
|
20
|
+
<string>4.0-project</string>
|
21
|
+
<key>CFBundleVersion</key>
|
22
|
+
<string>44</string>
|
23
|
+
<key>LSRequiresIPhoneOS</key>
|
24
|
+
<true/>
|
25
|
+
<key>UILaunchStoryboardName</key>
|
26
|
+
<string>LaunchScreen</string>
|
27
|
+
<key>UIMainStoryboardFile</key>
|
28
|
+
<string>Main</string>
|
29
|
+
<key>UIRequiredDeviceCapabilities</key>
|
30
|
+
<array>
|
31
|
+
<string>armv7</string>
|
32
|
+
</array>
|
33
|
+
<key>UISupportedInterfaceOrientations</key>
|
34
|
+
<array>
|
35
|
+
<string>UIInterfaceOrientationPortrait</string>
|
36
|
+
<string>UIInterfaceOrientationLandscapeLeft</string>
|
37
|
+
<string>UIInterfaceOrientationLandscapeRight</string>
|
38
|
+
</array>
|
39
|
+
<key>UISupportedInterfaceOrientations~ipad</key>
|
40
|
+
<array>
|
41
|
+
<string>UIInterfaceOrientationPortrait</string>
|
42
|
+
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
43
|
+
<string>UIInterfaceOrientationLandscapeLeft</string>
|
44
|
+
<string>UIInterfaceOrientationLandscapeRight</string>
|
45
|
+
</array>
|
46
|
+
</dict>
|
47
|
+
</plist>
|
@@ -5,53 +5,66 @@ require 'fastlane/actions/get_info_plist_value'
|
|
5
5
|
BuildAction = Fastlane::Actions::SendBuildToBugsnagAction
|
6
6
|
|
7
7
|
describe BuildAction do
|
8
|
-
def
|
9
|
-
BuildAction.
|
10
|
-
[x.key, x.default_value]
|
11
|
-
end.to_h
|
8
|
+
def run_with args
|
9
|
+
BuildAction.run(Fastlane::ConfigurationHelper.parse(BuildAction, args))
|
12
10
|
end
|
13
11
|
|
14
12
|
context 'building an iOS project' do
|
15
13
|
it 'detects default Info.plist file excluding test dirs' do
|
16
14
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
17
15
|
payload = ::JSON.load(body)
|
18
|
-
expect(payload['appVersion']).to eq '
|
19
|
-
expect(payload['appBundleVersion']).to eq '
|
20
|
-
expect(payload['apiKey']).to eq '
|
16
|
+
expect(payload['appVersion']).to eq '2.0-other'
|
17
|
+
expect(payload['appBundleVersion']).to eq '22'
|
18
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789AAA'
|
21
19
|
end
|
22
20
|
|
23
21
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
24
|
-
|
22
|
+
run_with({})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'reads api key from legacy location' do
|
27
|
+
# the API key is now in `bugsnag.apiKey`, it used to be in 'BugsnagAPIKey',
|
28
|
+
# test this can be extracted correctly from the `ios_proj_legacy`
|
29
|
+
expect(BuildAction).to receive(:send_notification) do |url, body|
|
30
|
+
payload = ::JSON.load(body)
|
31
|
+
expect(payload['appVersion']).to eq '4.0-project'
|
32
|
+
expect(payload['appBundleVersion']).to eq '44'
|
33
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789BBB'
|
34
|
+
end
|
35
|
+
|
36
|
+
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj_legacy')) do
|
37
|
+
run_with({})
|
25
38
|
end
|
26
39
|
end
|
27
40
|
|
28
41
|
context 'using default config_file option' do
|
29
|
-
context 'override API key
|
42
|
+
context 'override API key from config' do
|
30
43
|
it 'reads API key from the api_key option' do
|
31
44
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
32
45
|
payload = ::JSON.load(body)
|
33
|
-
expect(payload['apiKey']).to eq '
|
46
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789FFF'
|
34
47
|
end
|
35
48
|
|
36
49
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
37
|
-
|
38
|
-
api_key: '
|
39
|
-
})
|
50
|
+
run_with({
|
51
|
+
api_key: '12345678901234567890123456789FFF'
|
52
|
+
})
|
40
53
|
end
|
41
54
|
end
|
42
55
|
|
43
|
-
it '
|
56
|
+
it 'uses input versions from options' do
|
44
57
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
45
58
|
payload = ::JSON.load(body)
|
46
|
-
expect(payload['appVersion']).to eq '
|
47
|
-
expect(payload['appBundleVersion']).to eq '
|
59
|
+
expect(payload['appVersion']).to eq '8.0.0'
|
60
|
+
expect(payload['appBundleVersion']).to eq '800'
|
48
61
|
end
|
49
62
|
|
50
63
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
51
|
-
|
52
|
-
app_version: '
|
53
|
-
ios_bundle_version: '
|
54
|
-
})
|
64
|
+
run_with({
|
65
|
+
app_version: '8.0.0',
|
66
|
+
ios_bundle_version: '800'
|
67
|
+
})
|
55
68
|
end
|
56
69
|
end
|
57
70
|
end
|
@@ -61,46 +74,49 @@ describe BuildAction do
|
|
61
74
|
it 'reads API key and version info from the config file' do
|
62
75
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
63
76
|
payload = ::JSON.load(body)
|
64
|
-
expect(payload['appVersion']).to eq '
|
65
|
-
expect(payload['appBundleVersion']).to eq '
|
66
|
-
expect(payload['apiKey']).to eq '
|
77
|
+
expect(payload['appVersion']).to eq '3.0-project'
|
78
|
+
expect(payload['appBundleVersion']).to eq '33'
|
79
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
|
67
80
|
end
|
68
81
|
|
69
82
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
70
|
-
|
83
|
+
run_with({
|
71
84
|
config_file: File.join('Project', 'Info.plist')
|
72
|
-
})
|
85
|
+
})
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
76
|
-
context 'override API key
|
77
|
-
it '
|
89
|
+
context 'override API key, and config file' do
|
90
|
+
it 'uses the input api_key to override a non default config' do
|
78
91
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
79
92
|
payload = ::JSON.load(body)
|
80
|
-
expect(payload['
|
93
|
+
expect(payload['appVersion']).to eq '3.0-project'
|
94
|
+
expect(payload['appBundleVersion']).to eq '33'
|
95
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789EEE'
|
81
96
|
end
|
82
97
|
|
83
98
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
84
|
-
|
99
|
+
run_with({
|
85
100
|
config_file: File.join('Project', 'Info.plist'),
|
86
|
-
api_key: '
|
87
|
-
})
|
101
|
+
api_key: '12345678901234567890123456789EEE'
|
102
|
+
})
|
88
103
|
end
|
89
104
|
end
|
90
105
|
|
91
|
-
it '
|
106
|
+
it 'uses the input versions to override a non default config' do
|
92
107
|
expect(BuildAction).to receive(:send_notification) do |url, body|
|
93
108
|
payload = ::JSON.load(body)
|
94
|
-
expect(payload['appVersion']).to eq '
|
95
|
-
expect(payload['appBundleVersion']).to eq '
|
109
|
+
expect(payload['appVersion']).to eq '9.0.0'
|
110
|
+
expect(payload['appBundleVersion']).to eq '900'
|
111
|
+
expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
|
96
112
|
end
|
97
113
|
|
98
114
|
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
99
|
-
|
115
|
+
run_with({
|
100
116
|
config_file: File.join('Project', 'Info.plist'),
|
101
|
-
app_version: '
|
102
|
-
ios_bundle_version: '
|
103
|
-
})
|
117
|
+
app_version: '9.0.0',
|
118
|
+
ios_bundle_version: '900'
|
119
|
+
})
|
104
120
|
end
|
105
121
|
end
|
106
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-bugsnag
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delisa Mason
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xml-simple
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: parallel
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "<"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.20.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "<"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.20.0
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: fastlane
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,7 +136,7 @@ dependencies:
|
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: 2.28.5
|
125
|
-
description:
|
139
|
+
description:
|
126
140
|
email: iskanamagus@gmail.com
|
127
141
|
executables: []
|
128
142
|
extensions: []
|
@@ -143,16 +157,17 @@ files:
|
|
143
157
|
- spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|
144
158
|
- spec/fixtures/ios_proj/Project/Info.plist
|
145
159
|
- spec/fixtures/ios_proj/aaTests/Info.plist
|
160
|
+
- spec/fixtures/ios_proj_legacy/Project/Info.plist
|
146
161
|
- spec/fixtures/more_files.zip
|
147
162
|
- spec/fixtures/stuff/app.dSYM/Contents/Resources/DWARF/app
|
148
163
|
- spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
|
149
164
|
- spec/send_build_to_bugsnag_spec.rb
|
150
165
|
- spec/spec_helper.rb
|
151
|
-
homepage: https://github.com/bugsnag/bugsnag-upload
|
166
|
+
homepage: https://github.com/bugsnag/bugsnag-dsym-upload
|
152
167
|
licenses:
|
153
168
|
- MIT
|
154
169
|
metadata: {}
|
155
|
-
post_install_message:
|
170
|
+
post_install_message:
|
156
171
|
rdoc_options: []
|
157
172
|
require_paths:
|
158
173
|
- lib
|
@@ -167,15 +182,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
182
|
- !ruby/object:Gem::Version
|
168
183
|
version: '0'
|
169
184
|
requirements: []
|
170
|
-
|
171
|
-
|
172
|
-
signing_key:
|
185
|
+
rubygems_version: 3.0.3
|
186
|
+
signing_key:
|
173
187
|
specification_version: 4
|
174
188
|
summary: Uploads dSYM files to Bugsnag
|
175
189
|
test_files:
|
176
190
|
- spec/spec_helper.rb
|
177
191
|
- spec/bugsnag_upload_dsym_action_spec.rb
|
178
192
|
- spec/send_build_to_bugsnag_spec.rb
|
193
|
+
- spec/fixtures/ios_proj_legacy/Project/Info.plist
|
179
194
|
- spec/fixtures/more_files.zip
|
180
195
|
- spec/fixtures/BCSymbolMaps/real.bcsymbolmap
|
181
196
|
- spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|