fastlane-plugin-bugsnag 1.4.2 → 2.0.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: e181711b183200492623830bb7769c7d7939ec4c5c712ba7f0c40893f55e5e87
4
- data.tar.gz: b1f4993b0a3dc8e3bc8ad57e1f28c05fc88ebe760405c7322641aa92cb940335
3
+ metadata.gz: 0f2f91641496dae26549b47215b4cf137e6c03d7307f38b491903a8505cb26c1
4
+ data.tar.gz: eca880b9cd9aa3afaca45892e01ba63130fb29d069a40eb2f2a8fef98c5dee51
5
5
  SHA512:
6
- metadata.gz: d4cbb540fcaa9e5ff405e792416208bc520c7c99bca291f781e354fdf4f8bfacc4360cf2eba1f6f32a5eec4f1749fd5120e14907b505ec13d88d95716d2c7a6f
7
- data.tar.gz: 0bcc646b32248169a424b581cd5e85ba2da983f3d66ac372496eea90fcf34b0014704809f4e782a6fa7d0df65b9c04de560a296acb2993ee444bfb3956af4337
6
+ metadata.gz: 56875d0eb5b5d644030466ab85c80817524d953c3286593fdbc7c2d9db4e81d919f90d6875a40d6d4adce8d81846db0b85069fbf07cc470d927bc6530f4b2836
7
+ data.tar.gz: 456b9b1d0ece3dcedd9302654ac25be698b075a44bb108c6c11257f6b8c4d23ebd4e6c641bccf41d40aaabf2d83c074a13d92ceca9647dfffbfb0e7c488f496b
@@ -150,7 +150,7 @@ for dsym in $dsym_dir/*.dSYM; do
150
150
 
151
151
  # We need to shell out to perform the curl as there seems to be some indirect
152
152
  # wrapping of this script which causes the command to fail if called directly.
153
- curl_cmd="curl --silent --show-error --http1.1 $upload_server -F dsym=@\"$file\" $args"
153
+ curl_cmd="curl --fail --silent --show-error --http1.1 $upload_server -F dsym=@\"$file\" $args"
154
154
  output=$(sh -c "$curl_cmd")
155
155
 
156
156
  if [ $? -eq 0 ] && [ "$output" != "invalid apiKey" ]; then
@@ -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 }.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").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").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,21 +6,24 @@ 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]
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]}")
19
22
  end
20
23
 
21
24
  parse_dsym_paths(params[:dsym_path]).each do |dsym_path|
22
25
  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])
26
+ args = upload_args(dsym_path, params[:symbol_maps_path], params[:upload_url], params[:project_root], api_key, verbose)
24
27
  success = Kernel.system(UPLOAD_SCRIPT_PATH, *args)
25
28
  if success
26
29
  UI.success("Uploaded dSYMs in #{dsym_path}")
@@ -34,7 +37,7 @@ module Fastlane
34
37
  end
35
38
 
36
39
  def self.description
37
- "Uploads symbol files to Bugsnag"
40
+ "Uploads dSYM debug symbol files to Bugsnag"
38
41
  end
39
42
 
40
43
  def self.authors
@@ -66,27 +69,25 @@ module Fastlane
66
69
  end
67
70
  validate_symbol_maps = proc do |path|
68
71
  return if path.nil?
69
-
70
72
  unless File.exist?(path) and File.directory?(path)
71
73
  UI.user_error!("Symbol maps file needs to be a directory containing symbol map files")
72
74
  end
73
75
  end
74
76
  validate_api_key = proc do |key|
75
77
  return if key.nil?
76
-
77
78
  unless !key[/\H/] and key.length == 32
78
79
  UI.user_error!("API key should be a 32 character hexadecimal string")
79
80
  end
80
81
  end
81
82
 
82
- options = {}
83
- options = options_from_info_plist(default_info_plist_path) if file_path = default_info_plist_path
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.
84
86
  [
85
87
  FastlaneCore::ConfigItem.new(key: :api_key,
86
88
  env_name: "BUGSNAG_API_KEY",
87
89
  description: "Bugsnag API Key",
88
90
  optional: true,
89
- default_value: options[:apiKey],
90
91
  verify_block: validate_api_key),
91
92
  FastlaneCore::ConfigItem.new(key: :dsym_path,
92
93
  type: Array,
@@ -112,27 +113,32 @@ module Fastlane
112
113
  default_value: Dir::pwd,
113
114
  optional: true),
114
115
  FastlaneCore::ConfigItem.new(key: :config_file,
116
+ env_name: "BUGSNAG_CONFIG_FILE",
115
117
  description: "Info.plist location",
116
118
  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),
119
+ default_value: default_info_plist_path)
123
120
  ]
124
121
  end
125
122
 
126
123
  private
127
124
 
128
125
  def self.default_info_plist_path
129
- Dir.glob("./{ios/,}*/Info.plist").reject {|path| path =~ /build|test/i }.first
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
130
129
  end
131
130
 
132
131
  def self.options_from_info_plist file_path
133
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
134
140
  {
135
- apiKey: plist_getter.run(path: file_path, key: "BugsnagAPIKey"),
141
+ apiKey: api_key,
136
142
  config_file: file_path,
137
143
  }
138
144
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Bugsnag
3
- VERSION = "1.4.2"
3
+ VERSION = "2.0.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.2
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: 2020-04-02 00:00:00.000000000 Z
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,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
168
  - !ruby/object:Gem::Version
168
169
  version: '0'
169
170
  requirements: []
170
- rubyforge_project:
171
- rubygems_version: 2.7.6.2
171
+ rubygems_version: 3.0.3
172
172
  signing_key:
173
173
  specification_version: 4
174
174
  summary: Uploads dSYM files to Bugsnag
@@ -176,6 +176,7 @@ test_files:
176
176
  - spec/spec_helper.rb
177
177
  - spec/bugsnag_upload_dsym_action_spec.rb
178
178
  - spec/send_build_to_bugsnag_spec.rb
179
+ - spec/fixtures/ios_proj_legacy/Project/Info.plist
179
180
  - spec/fixtures/more_files.zip
180
181
  - spec/fixtures/BCSymbolMaps/real.bcsymbolmap
181
182
  - spec/fixtures/ios_proj/FirstRealFolder/Info.plist