fastlane-plugin-bugsnag 1.3.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|