fastlane-plugin-bugsnag 1.3.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +20 -0
- data/bugsnag-dsym-upload +178 -0
- data/lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb +100 -87
- data/lib/fastlane/plugin/bugsnag/actions/upload_symbols_to_bugsnag.rb +65 -12
- data/lib/fastlane/plugin/bugsnag/version.rb +1 -1
- data/spec/bugsnag_upload_dsym_action_spec.rb +114 -34
- 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 +15 -14
- data/LICENSE.txt +0 -1
- data/bugsnag-dsym-upload +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f2f91641496dae26549b47215b4cf137e6c03d7307f38b491903a8505cb26c1
|
4
|
+
data.tar.gz: eca880b9cd9aa3afaca45892e01ba63130fb29d069a40eb2f2a8fef98c5dee51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56875d0eb5b5d644030466ab85c80817524d953c3286593fdbc7c2d9db4e81d919f90d6875a40d6d4adce8d81846db0b85069fbf07cc470d927bc6530f4b2836
|
7
|
+
data.tar.gz: 456b9b1d0ece3dcedd9302654ac25be698b075a44bb108c6c11257f6b8c4d23ebd4e6c641bccf41d40aaabf2d83c074a13d92ceca9647dfffbfb0e7c488f496b
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2016 Bugsnag, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bugsnag-dsym-upload
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
#
|
3
|
+
# Given a directory, uploads any *.dSYM bundles with the directory to
|
4
|
+
# Bugsnag. As a pre-upload step, bitcode symbol maps can be combined with
|
5
|
+
# dSYM files to ensure symbols are sent for bitcode-enabled binaries.
|
6
|
+
#
|
7
|
+
# Depends on:
|
8
|
+
# * curl
|
9
|
+
# * dwarfdump
|
10
|
+
# * dsymutil (for --symbol-maps, optional)
|
11
|
+
# * unzip (for uploading a .zip file, optional)
|
12
|
+
|
13
|
+
function print_usage() {
|
14
|
+
echo "Usage: $0 [--symbol-maps DIR] dSYMS_PATH"
|
15
|
+
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 "dSYMS_PATH A directory or .zip file containing *.dSYM bundles to"
|
27
|
+
echo " upload"
|
28
|
+
}
|
29
|
+
|
30
|
+
function exit_with_usage() {
|
31
|
+
echo $1
|
32
|
+
echo
|
33
|
+
print_usage
|
34
|
+
exit 1
|
35
|
+
}
|
36
|
+
|
37
|
+
function log() {
|
38
|
+
if [[ $silent != 1 ]]; then
|
39
|
+
echo $@
|
40
|
+
fi
|
41
|
+
}
|
42
|
+
|
43
|
+
function log_verbose() {
|
44
|
+
if [[ $verbose == 1 ]]; then
|
45
|
+
log $@
|
46
|
+
fi
|
47
|
+
}
|
48
|
+
|
49
|
+
upload_server=https://upload.bugsnag.com
|
50
|
+
unset symbol_maps
|
51
|
+
unset dsym_dir
|
52
|
+
unset verbose
|
53
|
+
unset silent
|
54
|
+
unset project_root
|
55
|
+
unset api_key
|
56
|
+
|
57
|
+
while [[ $# -gt 0 ]]; do
|
58
|
+
case $1 in
|
59
|
+
-h|--help)
|
60
|
+
print_usage
|
61
|
+
exit 0;;
|
62
|
+
-s|--silent)
|
63
|
+
silent=1
|
64
|
+
shift;;
|
65
|
+
-v|--verbose)
|
66
|
+
verbose=1
|
67
|
+
shift;;
|
68
|
+
--symbol-maps)
|
69
|
+
symbol_maps=$2
|
70
|
+
shift
|
71
|
+
shift;;
|
72
|
+
--upload-server)
|
73
|
+
upload_server=$2
|
74
|
+
shift
|
75
|
+
shift;;
|
76
|
+
--api-key)
|
77
|
+
api_key=$2
|
78
|
+
shift
|
79
|
+
shift;;
|
80
|
+
--project-root)
|
81
|
+
project_root=$2
|
82
|
+
shift
|
83
|
+
shift;;
|
84
|
+
-*)
|
85
|
+
exit_with_usage "Invalid parameter provided: $1";;
|
86
|
+
*)
|
87
|
+
dsym_dir=$1
|
88
|
+
break;;
|
89
|
+
esac
|
90
|
+
done
|
91
|
+
|
92
|
+
# Set IFS to ensure that file paths with spaces in get processed correctly
|
93
|
+
IFS=$'\n'
|
94
|
+
|
95
|
+
if [[ ! -z $symbol_maps ]]; then
|
96
|
+
if [[ ! -d $symbol_maps ]]; then
|
97
|
+
exit_with_usage "Bitcode symbol map parameter is not a directory"
|
98
|
+
elif [[ ! -x "$(command -v dsymutil 2>/dev/null)" ]]; then
|
99
|
+
exit_with_usage "dsymutil command not found."
|
100
|
+
fi
|
101
|
+
fi
|
102
|
+
if [[ -z $dsym_dir ]]; then
|
103
|
+
exit_with_usage "No dSYM directory provided"
|
104
|
+
fi
|
105
|
+
if [[ ! -d $dsym_dir ]]; then
|
106
|
+
if [[ $dsym_dir == *".zip" ]]; then
|
107
|
+
if [[ ! -x "$(command -v unzip 2>/dev/null)" ]]; then
|
108
|
+
exit_with_usage "unzip command not found."
|
109
|
+
fi
|
110
|
+
temp_dir=$(mktemp -dt "bugsnag-dsym-upload.XXX")
|
111
|
+
unzip -qq $dsym_dir -d $temp_dir
|
112
|
+
dsym_dir=$temp_dir
|
113
|
+
else
|
114
|
+
exit_with_usage "'$dsym_dir' is not a directory or a zip file"
|
115
|
+
fi
|
116
|
+
fi
|
117
|
+
|
118
|
+
log_verbose "Uploading files to $upload_server"
|
119
|
+
success_count=0
|
120
|
+
fail_count=0
|
121
|
+
|
122
|
+
for dsym in $dsym_dir/*.dSYM; do
|
123
|
+
log_verbose "Preparing to upload $dsym"
|
124
|
+
|
125
|
+
if [[ -d $symbol_maps ]]; then
|
126
|
+
log_verbose "Updating file with bitcode symbol maps in $symbol_maps"
|
127
|
+
dsymutil "$dsym" --symbol-map "$symbol_maps"
|
128
|
+
fi
|
129
|
+
|
130
|
+
dwarf_data=$dsym/Contents/Resources/DWARF
|
131
|
+
if [[ ! -d $dwarf_data ]]; then
|
132
|
+
log_verbose "Skipping file missing DWARF data: $dsym"
|
133
|
+
fail_count=$((fail_count+1))
|
134
|
+
continue
|
135
|
+
fi
|
136
|
+
for file in $dwarf_data/*; do
|
137
|
+
uuid=$(dwarfdump -u $file 2>/dev/null)
|
138
|
+
if [[ $uuid == UUID* ]]; then
|
139
|
+
log Uploading $uuid
|
140
|
+
|
141
|
+
# Attach the api key and project root parameters if they have been provided
|
142
|
+
args=""
|
143
|
+
if [[ ! -z $project_root ]]; then
|
144
|
+
args="-F projectRoot=\"$project_root\" "
|
145
|
+
fi
|
146
|
+
|
147
|
+
if [[ ! -z $api_key ]]; then
|
148
|
+
args="$args-F apiKey=$api_key"
|
149
|
+
fi
|
150
|
+
|
151
|
+
# We need to shell out to perform the curl as there seems to be some indirect
|
152
|
+
# wrapping of this script which causes the command to fail if called directly.
|
153
|
+
curl_cmd="curl --fail --silent --show-error --http1.1 $upload_server -F dsym=@\"$file\" $args"
|
154
|
+
output=$(sh -c "$curl_cmd")
|
155
|
+
|
156
|
+
if [ $? -eq 0 ] && [ "$output" != "invalid apiKey" ]; then
|
157
|
+
success_count=$((success_count+1))
|
158
|
+
else
|
159
|
+
fail_count=$((fail_count+1))
|
160
|
+
log_verbose "Failed to upload file: $file"
|
161
|
+
fi
|
162
|
+
echo $output | grep -v '^OK$'
|
163
|
+
log
|
164
|
+
else
|
165
|
+
log_verbose "Skipping file without UUID: $file"
|
166
|
+
fail_count=$((fail_count+1))
|
167
|
+
fi
|
168
|
+
done
|
169
|
+
done
|
170
|
+
|
171
|
+
if [ $success_count -gt 0 ]; then
|
172
|
+
log "$success_count files uploaded successfully"
|
173
|
+
fi
|
174
|
+
|
175
|
+
if [ $fail_count -gt 0 ]; then
|
176
|
+
log "$fail_count files failed to upload"
|
177
|
+
exit 1
|
178
|
+
fi
|
@@ -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 }.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").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").first || Dir.glob("build.gradle").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,9 +6,24 @@ module Fastlane
|
|
6
6
|
File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'bugsnag-dsym-upload'))
|
7
7
|
|
8
8
|
def self.run(params)
|
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]
|
15
|
+
end
|
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
|
+
params.values.each do |param|
|
21
|
+
UI.verbose(" #{param[0].to_s.rjust(18)}: #{param[1]}")
|
22
|
+
end
|
23
|
+
|
9
24
|
parse_dsym_paths(params[:dsym_path]).each do |dsym_path|
|
10
25
|
if dsym_path.end_with?(".zip") or File.directory?(dsym_path)
|
11
|
-
args = upload_args(dsym_path, params[:symbol_maps_path], params[:upload_url], params[:project_root],
|
26
|
+
args = upload_args(dsym_path, params[:symbol_maps_path], params[:upload_url], params[:project_root], api_key, verbose)
|
12
27
|
success = Kernel.system(UPLOAD_SCRIPT_PATH, *args)
|
13
28
|
if success
|
14
29
|
UI.success("Uploaded dSYMs in #{dsym_path}")
|
@@ -22,7 +37,7 @@ module Fastlane
|
|
22
37
|
end
|
23
38
|
|
24
39
|
def self.description
|
25
|
-
"Uploads symbol files to Bugsnag"
|
40
|
+
"Uploads dSYM debug symbol files to Bugsnag"
|
26
41
|
end
|
27
42
|
|
28
43
|
def self.authors
|
@@ -54,12 +69,26 @@ module Fastlane
|
|
54
69
|
end
|
55
70
|
validate_symbol_maps = proc do |path|
|
56
71
|
return if path.nil?
|
57
|
-
|
58
72
|
unless File.exist?(path) and File.directory?(path)
|
59
73
|
UI.user_error!("Symbol maps file needs to be a directory containing symbol map files")
|
60
74
|
end
|
61
75
|
end
|
76
|
+
validate_api_key = proc do |key|
|
77
|
+
return if key.nil?
|
78
|
+
unless !key[/\H/] and key.length == 32
|
79
|
+
UI.user_error!("API key should be a 32 character hexadecimal string")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# If the Info.plist is in a default location, we'll get API key here
|
84
|
+
# This will be overwritten if you pass in an API key parameter in your
|
85
|
+
# Fastfile, or have an API key environment variable set.
|
62
86
|
[
|
87
|
+
FastlaneCore::ConfigItem.new(key: :api_key,
|
88
|
+
env_name: "BUGSNAG_API_KEY",
|
89
|
+
description: "Bugsnag API Key",
|
90
|
+
optional: true,
|
91
|
+
verify_block: validate_api_key),
|
63
92
|
FastlaneCore::ConfigItem.new(key: :dsym_path,
|
64
93
|
type: Array,
|
65
94
|
env_name: "BUGSNAG_DSYM_PATH",
|
@@ -83,18 +112,40 @@ module Fastlane
|
|
83
112
|
description: "Root path of the project",
|
84
113
|
default_value: Dir::pwd,
|
85
114
|
optional: true),
|
86
|
-
FastlaneCore::ConfigItem.new(key: :
|
87
|
-
env_name: "
|
88
|
-
description: "
|
89
|
-
|
90
|
-
|
115
|
+
FastlaneCore::ConfigItem.new(key: :config_file,
|
116
|
+
env_name: "BUGSNAG_CONFIG_FILE",
|
117
|
+
description: "Info.plist location",
|
118
|
+
optional: true,
|
119
|
+
default_value: default_info_plist_path)
|
91
120
|
]
|
92
121
|
end
|
93
122
|
|
94
123
|
private
|
95
124
|
|
96
|
-
def self.
|
125
|
+
def self.default_info_plist_path
|
126
|
+
# Find first 'Info.plist' in the current working directory
|
127
|
+
# ignoring any in 'build', or 'test' folders
|
128
|
+
return Dir.glob("./{ios/,}*/Info.plist").reject{|path| path =~ /build|test/i }.first
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.options_from_info_plist file_path
|
132
|
+
plist_getter = Fastlane::Actions::GetInfoPlistValueAction
|
133
|
+
bugsnag_dict = plist_getter.run(path: file_path, key: "bugsnag")
|
134
|
+
api_key = bugsnag_dict["apiKey"] unless bugsnag_dict.nil?
|
135
|
+
# From v6.0.0 of bugsnag-cocoa, the API key is in 'bugsnag.apiKey',
|
136
|
+
# use 'BugsnagAPIKey' as a fallback if it exists (<v6.x.x)
|
137
|
+
if api_key.nil?
|
138
|
+
api_key = plist_getter.run(path: file_path, key: "BugsnagAPIKey")
|
139
|
+
end
|
140
|
+
{
|
141
|
+
apiKey: api_key,
|
142
|
+
config_file: file_path,
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.upload_args dir, symbol_maps_dir, upload_url, project_root, api_key, verbose
|
97
147
|
args = [verbose ? "--verbose" : "--silent"]
|
148
|
+
args += ["--api-key", api_key] unless api_key.nil?
|
98
149
|
args += ["--upload-server", upload_url] unless upload_url.nil?
|
99
150
|
args += ["--symbol-maps", symbol_maps_dir] unless symbol_maps_dir.nil?
|
100
151
|
args += ["--project-root", project_root] unless project_root.nil?
|
@@ -110,9 +161,11 @@ module Fastlane
|
|
110
161
|
end
|
111
162
|
|
112
163
|
def self.default_dsym_path
|
113
|
-
|
114
|
-
|
115
|
-
|
164
|
+
path = Dir["./**/*.dSYM.zip"] + Dir["./**/*.dSYM"]
|
165
|
+
dsym_paths = Actions.lane_context[SharedValues::DSYM_PATHS] if defined? SharedValues::DSYM_PATHS
|
166
|
+
dsyms_output_path = Actions.lane_context[SharedValues::DSYM_OUTPUT_PATH] if defined? SharedValues::DSYM_OUTPUT_PATH
|
167
|
+
default_value = dsyms_output_path || dsym_paths || path
|
168
|
+
parse_dsym_paths(default_value)
|
116
169
|
end
|
117
170
|
end
|
118
171
|
end
|
@@ -3,65 +3,75 @@ require 'spec_helper'
|
|
3
3
|
Action = Fastlane::Actions::UploadSymbolsToBugsnagAction
|
4
4
|
|
5
5
|
describe Action do
|
6
|
+
def run_with args
|
7
|
+
Action.run(Fastlane::ConfigurationHelper.parse(Action, args))
|
8
|
+
end
|
9
|
+
|
10
|
+
context "the packaged gem" do
|
11
|
+
gem_name = "fastlane-plugin-bugsnag-#{Fastlane::Bugsnag::VERSION}"
|
12
|
+
|
13
|
+
after do
|
14
|
+
FileUtils.rm_rf(gem_name)
|
15
|
+
FileUtils.rm_rf("#{gem_name}.gem")
|
16
|
+
end
|
6
17
|
|
7
|
-
|
8
|
-
|
18
|
+
it 'has an executable upload script' do
|
19
|
+
system('rake build')
|
20
|
+
system("gem unpack #{gem_name}.gem")
|
21
|
+
expect(File.exist?(File.join("#{gem_name}/bugsnag-dsym-upload"))).to be true
|
22
|
+
end
|
9
23
|
end
|
10
24
|
|
11
25
|
describe '#run' do
|
12
26
|
it 'silences script output by default' do
|
13
27
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
14
|
-
"--silent",
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
it 'prints verbose if verbose flag set' do
|
19
|
-
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
20
|
-
"--verbose", FIXTURE_PATH).and_return(true)
|
21
|
-
Action.run({dsym_path: FIXTURE_PATH, verbose: true})
|
28
|
+
"--silent",
|
29
|
+
"--project-root", Dir::pwd,
|
30
|
+
FIXTURE_PATH).and_return(true)
|
31
|
+
run_with({dsym_path: FIXTURE_PATH})
|
22
32
|
end
|
23
33
|
|
24
34
|
it 'UI.user_error when script fails' do
|
25
35
|
expect(Fastlane::UI).to receive(:user_error!).with("Failed uploading #{FIXTURE_PATH}")
|
26
36
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
27
|
-
|
28
|
-
|
37
|
+
"--silent",
|
38
|
+
"--project-root", Dir::pwd,
|
39
|
+
FIXTURE_PATH).and_return(false)
|
40
|
+
run_with({dsym_path: FIXTURE_PATH})
|
29
41
|
end
|
30
42
|
|
31
43
|
it 'requires the dSYM file path to exist' do
|
32
|
-
expect(Fastlane::UI).to receive(:user_error!)
|
33
|
-
|
34
|
-
Action.run({dsym_path: 'fake/file/path'})
|
44
|
+
expect(Fastlane::UI).to receive(:user_error!).at_least(:once)
|
45
|
+
run_with({dsym_path: 'fake/file/path'})
|
35
46
|
end
|
36
47
|
|
37
48
|
it 'rejects dSYM files which are not a .zip or a directory' do
|
38
|
-
expect(Fastlane::UI).to receive(:user_error!)
|
39
|
-
|
40
|
-
Action.run({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')})
|
41
51
|
end
|
42
52
|
|
43
53
|
it 'uploads a single .dSYM file' do
|
44
54
|
directory = File.join(FIXTURE_PATH, 'dSYMs')
|
45
55
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
46
|
-
"--silent", directory).and_return(true)
|
47
|
-
|
56
|
+
"--silent", "--project-root", Dir::pwd, directory).and_return(true)
|
57
|
+
run_with({dsym_path: File.join(FIXTURE_PATH, 'dSYMs/app.dSYM')})
|
48
58
|
end
|
49
59
|
|
50
60
|
it 'uploads a .zip of .dSYM files' do
|
51
61
|
path = File.join(FIXTURE_PATH, 'files.zip')
|
52
62
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
53
|
-
"--silent", path).and_return(true)
|
54
|
-
|
63
|
+
"--silent", "--project-root", Dir::pwd, path).and_return(true)
|
64
|
+
run_with({dsym_path: path})
|
55
65
|
end
|
56
66
|
|
57
67
|
it 'uploads multiple .zip files' do
|
58
68
|
zip1 = File.join(FIXTURE_PATH, 'files.zip')
|
59
69
|
zip2 = File.join(FIXTURE_PATH, 'more_files.zip')
|
60
70
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
61
|
-
"--silent", zip1).and_return(true)
|
71
|
+
"--silent", "--project-root", Dir::pwd, zip1).and_return(true)
|
62
72
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
63
|
-
"--silent", zip2).and_return(true)
|
64
|
-
|
73
|
+
"--silent", "--project-root", Dir::pwd, zip2).and_return(true)
|
74
|
+
run_with({dsym_path: [zip1, zip2]})
|
65
75
|
end
|
66
76
|
|
67
77
|
it 'uploads multiple .dSYM files multiple directories' do
|
@@ -71,10 +81,10 @@ describe Action do
|
|
71
81
|
dsym4 = File.join(FIXTURE_PATH, 'stuff/app2.dSYM')
|
72
82
|
directories = [File.join(FIXTURE_PATH, 'dSYMs'), File.join(FIXTURE_PATH, 'stuff')]
|
73
83
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
74
|
-
"--silent", directories[0]).and_return(true)
|
84
|
+
"--silent", "--project-root", Dir::pwd, directories[0]).and_return(true)
|
75
85
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
76
|
-
"--silent", directories[1]).and_return(true)
|
77
|
-
|
86
|
+
"--silent", "--project-root", Dir::pwd, directories[1]).and_return(true)
|
87
|
+
run_with({dsym_path: [dsym1, dsym2, dsym3, dsym4]})
|
78
88
|
end
|
79
89
|
|
80
90
|
it 'uploads multiple .dSYM files in a single directory' do
|
@@ -82,8 +92,8 @@ describe Action do
|
|
82
92
|
dsym2 = File.join(FIXTURE_PATH, 'dSYMs/app2.dSYM')
|
83
93
|
directory = File.join(FIXTURE_PATH, 'dSYMs')
|
84
94
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
85
|
-
"--silent", directory).and_return(true)
|
86
|
-
|
95
|
+
"--silent", "--project-root", Dir::pwd, directory).and_return(true)
|
96
|
+
run_with({dsym_path: [dsym1, dsym2]})
|
87
97
|
end
|
88
98
|
|
89
99
|
it 'accepts a project root argument' do
|
@@ -92,7 +102,76 @@ describe Action do
|
|
92
102
|
"--silent",
|
93
103
|
"--project-root", root_path,
|
94
104
|
FIXTURE_PATH).and_return(true)
|
95
|
-
|
105
|
+
run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'accepts an API key argument' do
|
109
|
+
api_key = "123456789123456789001234567890FF"
|
110
|
+
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
111
|
+
"--silent",
|
112
|
+
"--api-key", api_key,
|
113
|
+
"--project-root", Dir::pwd,
|
114
|
+
FIXTURE_PATH).and_return(true)
|
115
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key})
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'accepts an API key argument with no project root' do
|
119
|
+
api_key = "123456789012345678901234567890FF"
|
120
|
+
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
121
|
+
"--silent",
|
122
|
+
"--api-key", api_key,
|
123
|
+
"--project-root", `pwd`.chomp,
|
124
|
+
FIXTURE_PATH).and_return(true)
|
125
|
+
run_with({dsym_path: FIXTURE_PATH, api_key: api_key, project_root: nil})
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'uses default API key argument from plist' do
|
129
|
+
root_path = "/test/test/test"
|
130
|
+
api_key = "12345678901234567890123456789AAA" # Uses the API Key from ./spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|
131
|
+
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
132
|
+
"--silent",
|
133
|
+
"--api-key", api_key,
|
134
|
+
"--project-root", root_path,
|
135
|
+
FIXTURE_PATH).and_return(true)
|
136
|
+
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
137
|
+
run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
|
138
|
+
end
|
139
|
+
end
|
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
|
144
|
+
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
145
|
+
"--silent",
|
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,
|
161
|
+
"--project-root", File.join(FIXTURE_PATH, 'ios_proj'),
|
162
|
+
FIXTURE_PATH).and_return(true)
|
163
|
+
Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
|
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')})
|
174
|
+
end
|
96
175
|
end
|
97
176
|
|
98
177
|
context 'using a private server' do
|
@@ -100,9 +179,9 @@ describe Action do
|
|
100
179
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
101
180
|
"--silent",
|
102
181
|
"--upload-server", "http://myserver.example.com",
|
182
|
+
"--project-root", Dir::pwd,
|
103
183
|
FIXTURE_PATH).and_return(true)
|
104
|
-
|
105
|
-
upload_url: "http://myserver.example.com"})
|
184
|
+
run_with({dsym_path: FIXTURE_PATH, upload_url: "http://myserver.example.com"})
|
106
185
|
end
|
107
186
|
end
|
108
187
|
|
@@ -112,8 +191,9 @@ describe Action do
|
|
112
191
|
expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
|
113
192
|
"--silent",
|
114
193
|
"--symbol-maps", path,
|
194
|
+
"--project-root", Dir::pwd,
|
115
195
|
FIXTURE_PATH).and_return(true)
|
116
|
-
|
196
|
+
run_with({dsym_path: FIXTURE_PATH, symbol_maps_path: path})
|
117
197
|
end
|
118
198
|
end
|
119
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.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delisa Mason
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: xml-simple
|
@@ -143,12 +143,13 @@ files:
|
|
143
143
|
- spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|
144
144
|
- spec/fixtures/ios_proj/Project/Info.plist
|
145
145
|
- spec/fixtures/ios_proj/aaTests/Info.plist
|
146
|
+
- spec/fixtures/ios_proj_legacy/Project/Info.plist
|
146
147
|
- spec/fixtures/more_files.zip
|
147
148
|
- spec/fixtures/stuff/app.dSYM/Contents/Resources/DWARF/app
|
148
149
|
- spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
|
149
150
|
- spec/send_build_to_bugsnag_spec.rb
|
150
151
|
- spec/spec_helper.rb
|
151
|
-
homepage: https://github.com/bugsnag/bugsnag-upload
|
152
|
+
homepage: https://github.com/bugsnag/bugsnag-dsym-upload
|
152
153
|
licenses:
|
153
154
|
- MIT
|
154
155
|
metadata: {}
|
@@ -167,23 +168,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
168
|
- !ruby/object:Gem::Version
|
168
169
|
version: '0'
|
169
170
|
requirements: []
|
170
|
-
|
171
|
-
rubygems_version: 2.7.7
|
171
|
+
rubygems_version: 3.0.3
|
172
172
|
signing_key:
|
173
173
|
specification_version: 4
|
174
174
|
summary: Uploads dSYM files to Bugsnag
|
175
175
|
test_files:
|
176
|
+
- spec/spec_helper.rb
|
176
177
|
- spec/bugsnag_upload_dsym_action_spec.rb
|
178
|
+
- spec/send_build_to_bugsnag_spec.rb
|
179
|
+
- spec/fixtures/ios_proj_legacy/Project/Info.plist
|
180
|
+
- spec/fixtures/more_files.zip
|
177
181
|
- spec/fixtures/BCSymbolMaps/real.bcsymbolmap
|
178
|
-
- spec/fixtures/dSYMs/app.dSYM/Contents/Resources/DWARF/app
|
179
|
-
- spec/fixtures/dSYMs/app2.dSYM/Contents/Resources/DWARF/app2
|
180
|
-
- spec/fixtures/files.zip
|
181
|
-
- spec/fixtures/invalid_file
|
182
|
-
- spec/fixtures/ios_proj/aaTests/Info.plist
|
183
182
|
- spec/fixtures/ios_proj/FirstRealFolder/Info.plist
|
183
|
+
- spec/fixtures/ios_proj/aaTests/Info.plist
|
184
184
|
- spec/fixtures/ios_proj/Project/Info.plist
|
185
|
-
- spec/fixtures/more_files.zip
|
186
|
-
- spec/fixtures/stuff/app.dSYM/Contents/Resources/DWARF/app
|
187
185
|
- spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
|
188
|
-
- spec/
|
189
|
-
- spec/
|
186
|
+
- spec/fixtures/stuff/app.dSYM/Contents/Resources/DWARF/app
|
187
|
+
- spec/fixtures/invalid_file
|
188
|
+
- spec/fixtures/files.zip
|
189
|
+
- spec/fixtures/dSYMs/app2.dSYM/Contents/Resources/DWARF/app2
|
190
|
+
- spec/fixtures/dSYMs/app.dSYM/Contents/Resources/DWARF/app
|
data/LICENSE.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
./../../LICENSE.txt
|
data/bugsnag-dsym-upload
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
./../../bin/bugsnag-dsym-upload
|