fastlane-plugin-bugsnag 1.4.1 → 2.2.0

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