fastlane-plugin-bugsnag 2.3.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee3c60f5a8e31661e3a0e828b3e792535c6b31088924f48a25f8ae935e982986
4
- data.tar.gz: 77589505109ad61b5bc91d19bfa07ff7b79f7b0a910fe7c84ebd311bdb8b8b99
3
+ metadata.gz: e8044da3301498296d3a835cdf3cae80c8c52cbb171e341c74579506b4a016da
4
+ data.tar.gz: f40fd81e58b62e35f127090c1966789ce16b26dd64e2796a2043f7aed4f39950
5
5
  SHA512:
6
- metadata.gz: 23c3d24f139d1a5249a74419bce45ac37beb6e24e94793cce45ccaf1df74ba4056f68d36b1af743cb6a9e9a3293119b8c48f9543784edcd3edbf11656f9a3093
7
- data.tar.gz: d4dba2b84a6feb931045c5777928f76c29b472206d6c1e886b5a332c32d6e9c6acec7b3ce96b23d7b7df48754a69973bc7e5d1754d279ed9cab53436c897763d
6
+ metadata.gz: 811cbcd503ab874433e2ccd4faf13b8bd9d8e40ad700a7dd13308a775d751641dbb6cf6463808c9c0c6f07c7f7718559d74c577785aa52c93e90dbb99633368a
7
+ data.tar.gz: 741164fd928f226de759a2cc6c6aa10206cd3957aad730a19171fe4983dd67e7689e47c86346f957657c496503adfdb6547f09e9338a0652444ce0a238c011bd
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,116 @@
1
+ require 'os'
2
+ require 'rbconfig'
3
+
4
+ class BugsnagCli
5
+ def self.get_bugsnag_cli_path(params)
6
+ bundled_bugsnag_cli_path = self.get_bundled_path
7
+ bundled_bugsnag_cli_version = Gem::Version.new(`#{bundled_bugsnag_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
8
+
9
+ if params[:bugsnag_cli_path]
10
+ bugsnag_cli_path = params[:bugsnag_cli_path] || bundled_bugsnag_cli_path
11
+
12
+ bugsnag_cli_version = Gem::Version.new(`#{bugsnag_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
13
+
14
+ if bugsnag_cli_version < bundled_bugsnag_cli_version
15
+ FastlaneCore::UI.warning("The installed bugsnag-cli at #{bugsnag_cli_path} is outdated (#{bugsnag_cli_version}). The current bundled version is: #{bundled_bugsnag_cli_version}. It is recommended that you either update your installed version or use the bundled version.")
16
+ end
17
+ FastlaneCore::UI.verbose("Using bugsnag-cli from path: #{bugsnag_cli_path}")
18
+ bugsnag_cli_path
19
+ else
20
+ FastlaneCore::UI.verbose("Using bundled bugsnag-cli from path: #{bundled_bugsnag_cli_path}")
21
+ bundled_bugsnag_cli_path
22
+ end
23
+ end
24
+
25
+ def self.get_bundled_path
26
+ host_cpu = RbConfig::CONFIG['host_cpu']
27
+ if OS.mac?
28
+ if host_cpu =~ /arm|aarch64/
29
+ self.bin_folder('arm64-macos-bugsnag-cli')
30
+ else
31
+ self.bin_folder('x86_64-macos-bugsnag-cli')
32
+ end
33
+ elsif OS.windows?
34
+ if OS.bits == 64
35
+ self.bin_folder('x86_64-windows-bugsnag-cli.exe')
36
+ else
37
+ self.bin_folder('i386-windows-bugsnag-cli.exe')
38
+ end
39
+ else
40
+ if host_cpu =~ /arm|aarch64/
41
+ self.bin_folder('arm64-linux-bugsnag-cli')
42
+ elsif OS.bits == 64
43
+ self.bin_folder('x86_64-linux-bugsnag-cli')
44
+ else
45
+ self.bin_folder('i386-linux-bugsnag-cli')
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.bin_folder(filename)
51
+ File.expand_path("../../../../../bin/#{filename}", File.dirname(__FILE__))
52
+ end
53
+
54
+ def self.create_build_args(api_key, version_name, version_code, bundle_version, release_stage, builder, revision, repository, provider, auto_assign_release, metadata, retries, timeout, endpoint)
55
+ args = []
56
+ args += ["--api-key", api_key] unless api_key.nil?
57
+ args += ["--version-name", version_name] unless version_name.nil?
58
+ args += ["--version-code", version_code] unless version_code.nil?
59
+ args += ["--bundle-version", bundle_version] unless bundle_version.nil?
60
+ args += ["--release-stage", release_stage] unless release_stage.nil?
61
+ args += ["--builder-name", builder] unless builder.nil?
62
+ args += ["--revision", revision] unless revision.nil?
63
+ args += ["--repository", repository] unless repository.nil?
64
+ args += ["--provider", provider] unless provider.nil?
65
+ args += ["--auto-assign-release"] if auto_assign_release
66
+ unless metadata.nil?
67
+ if metadata.is_a?(String)
68
+ #
69
+ args += ["--metadata", metadata]
70
+ elsif metadata.is_a?(Hash)
71
+ formatted_metadata = metadata.map { |k, v| %Q{"#{k}"="#{v}"} }.join(",")
72
+ args += ["--metadata", formatted_metadata]
73
+ end
74
+ end
75
+ args += ["--retries", retries] unless retries.nil?
76
+ args += ["--timeout", timeout] unless timeout.nil?
77
+ args += ["--build-api-root-url", endpoint] unless endpoint.nil?
78
+ args += ["--verbose"] if FastlaneCore::Globals.verbose?
79
+ args
80
+ end
81
+
82
+ def self.upload_args dir, upload_url, project_root, api_key, ignore_missing_dwarf, ignore_empty_dsym, dryrun, log_level, port, retries, timeout, configuration, scheme, plist, xcode_project
83
+ args = []
84
+ args += ["--verbose"] if FastlaneCore::Globals.verbose?
85
+ args += ["--ignore-missing-dwarf"] if ignore_missing_dwarf
86
+ args += ["--ignore-empty-dsym"] if ignore_empty_dsym
87
+ args += ["--api-key", api_key] unless api_key.nil?
88
+ args += ["--upload-api-root-url", upload_url] unless upload_url.nil?
89
+ args += ["--project-root", project_root] unless project_root.nil?
90
+ args += ["--dry-run"] if dryrun
91
+ args += ["--log-level", log_level] unless log_level.nil?
92
+ args += ["--port", port] unless port.nil?
93
+ args += ["--retries", retries] unless retries.nil?
94
+ args += ["--timeout", timeout] unless timeout.nil?
95
+ args += ["--configuration", configuration] unless configuration.nil?
96
+ args += ["--scheme", scheme] unless scheme.nil?
97
+ args += ["--plist", plist] unless plist.nil?
98
+ args += ["--xcode-project", xcode_project] unless xcode_project.nil?
99
+ args << dir
100
+ args
101
+ end
102
+
103
+ def self.upload_dsym cli_path, args
104
+ bugsnag_cli_command = "#{cli_path} upload dsym #{args.join(' ')}"
105
+ FastlaneCore::UI.verbose("Running command: #{bugsnag_cli_command}")
106
+ Kernel.system(bugsnag_cli_command)
107
+ end
108
+
109
+ def self.create_build cli_path, args
110
+ bugsnag_cli_command = "#{cli_path} create-build #{args.join(' ')}"
111
+ FastlaneCore::UI.verbose("Running command: #{bugsnag_cli_command}")
112
+ Kernel.system(bugsnag_cli_command)
113
+ end
114
+ end
115
+
116
+
@@ -1,63 +1,66 @@
1
1
  require "xmlsimple"
2
2
  require "json"
3
3
  require_relative "find_info_plist_path"
4
+ require_relative "bugsnag_cli"
4
5
 
5
6
  module Fastlane
6
7
  module Actions
7
8
  class SendBuildToBugsnagAction < Action
8
-
9
- BUILD_TOOL = "bugsnag-fastlane-plugin"
10
-
11
9
  def self.run(params)
12
- payload = {buildTool: BUILD_TOOL, sourceControl: {}}
10
+ bugsnag_cli_path = BugsnagCli.get_bugsnag_cli_path(params)
13
11
 
14
12
  # If a configuration file was found or was specified, load in the options:
13
+ config_options = {}
15
14
  if params[:config_file]
16
15
  UI.message("Loading build information from #{params[:config_file]}")
17
16
  config_options = load_config_file_options(params[:config_file])
18
-
19
- # for each of the config options, if it's not been overriden by any
20
- # input to the lane, write it to the payload:
21
- payload[:apiKey] = params[:api_key] || config_options[:apiKey]
22
- payload[:appVersion] = params[:app_version] || config_options[:appVersion]
23
- payload[:appVersionCode] = params[:android_version_code] || config_options[:appVersionCode]
24
- payload[:appBundleVersion] = params[:ios_bundle_version] || config_options[:appBundleVersion]
25
- payload[:releaseStage] = params[:release_stage] || config_options[:releaseStage] || "production"
26
- else
27
- # No configuration file was found or specified, use the input parameters:
28
- payload[:apiKey] = params[:api_key]
29
- payload[:appVersion] = params[:app_version]
30
- payload[:appVersionCode] = params[:android_version_code]
31
- payload[:appBundleVersion] = params[:ios_bundle_version]
32
- payload[:releaseStage] = params[:release_stage] || "production"
33
17
  end
34
18
 
35
- # If builder, or source control information has been provided into
36
- # Fastlane, apply it to the payload here.
37
- payload[:builderName] = params[:builder] if params[:builder]
38
- payload[:sourceControl][:revision] = params[:revision] if params[:revision]
39
- payload[:sourceControl][:repository] = params[:repository] if params[:repository]
40
- payload[:sourceControl][:provider] = params[:provider] if params[:provider]
41
-
42
- # If provided apply metadata to payload.
43
- payload[:metadata] = params[:metadata]
44
-
45
- payload.reject! {|k,v| v == nil || (v.is_a?(Hash) && v.empty?)}
46
-
47
- if payload[:apiKey].nil? || !payload[:apiKey].is_a?(String)
19
+ api_key = params[:api_key] || config_options[:apiKey] unless (params[:api_key].nil? && config_options[:apiKey].nil?)
20
+ version_name = params[:app_version] || config_options[:appVersion] unless (params[:app_version].nil? && config_options[:appVersion].nil?)
21
+ version_code = params[:android_version_code] || config_options[:appVersionCode] unless (params[:android_version_code].nil? && config_options[:appVersionCode].nil?)
22
+ bundle_version = params[:ios_bundle_version] || config_options[:appBundleVersion] unless (params[:ios_bundle_version].nil? && config_options[:appBundleVersion].nil?)
23
+ release_stage = params[:release_stage] || config_options[:releaseStage] || "production" unless (params[:release_stage].nil? && config_options[:releaseStage].nil?)
24
+ builder = params[:builder] unless params[:builder].nil?
25
+ revision = params[:revision] unless params[:revision].nil?
26
+ repository = params[:repository] unless params[:repository].nil?
27
+ provider = params[:provider] unless params[:provider].nil?
28
+ auto_assign_release = params[:auto_assign_release] unless params[:auto_assign_release].nil?
29
+ metadata = params[:metadata] unless params[:metadata].nil?
30
+ retries = params[:retries] unless params[:retries].nil?
31
+ timeout = params[:timeout] unless params[:timeout].nil?
32
+ endpoint = params[:endpoint] unless params[:endpoint].nil?
33
+
34
+
35
+ if api_key.nil? || !api_key.is_a?(String)
48
36
  UI.user_error! missing_api_key_message(params)
49
37
  end
50
- if payload[:appVersion].nil?
38
+ if version_name.nil?
51
39
  UI.user_error! missing_app_version_message(params)
52
40
  end
53
41
 
54
- # If verbose flag is enabled (`--verbose`), display the payload debug info
55
- UI.verbose("Sending build to Bugsnag with payload:")
56
- payload.each do |param|
57
- UI.verbose(" #{param[0].to_s.rjust(18)}: #{param[1]}")
42
+ args = BugsnagCli.create_build_args(
43
+ api_key,
44
+ version_name,
45
+ version_code,
46
+ bundle_version,
47
+ release_stage,
48
+ builder,
49
+ revision,
50
+ repository,
51
+ provider,
52
+ auto_assign_release,
53
+ metadata,
54
+ retries,
55
+ timeout,
56
+ endpoint
57
+ )
58
+ success = BugsnagCli.create_build bugsnag_cli_path, args
59
+ if success
60
+ UI.success("Build successfully sent to Bugsnag")
61
+ else
62
+ UI.user_error!("Failed to send build to Bugsnag.")
58
63
  end
59
-
60
- send_notification(params[:endpoint], ::JSON.dump(payload))
61
64
  end
62
65
 
63
66
  def self.missing_api_key_message(params)
@@ -146,6 +149,11 @@ module Fastlane
146
149
  FastlaneCore::ConfigItem.new(key: :release_stage,
147
150
  description: "Release stage being built, i.e. staging, production",
148
151
  optional: true),
152
+ FastlaneCore::ConfigItem.new(key: :auto_assign_release,
153
+ description: "Whether to automatically associate this build with any new error events and sessions that are received for",
154
+ optional: true,
155
+ default_value: false,
156
+ is_string: false),
149
157
  FastlaneCore::ConfigItem.new(key: :builder,
150
158
  description: "The name of the entity triggering the build",
151
159
  optional: true,
@@ -170,13 +178,28 @@ module Fastlane
170
178
  end),
171
179
  FastlaneCore::ConfigItem.new(key: :endpoint,
172
180
  description: "Bugsnag deployment endpoint",
173
- optional: true,
174
- default_value: "https://build.bugsnag.com"),
181
+ default_value: nil,
182
+ optional: true),
175
183
  FastlaneCore::ConfigItem.new(key: :metadata,
176
184
  description: "Metadata",
177
185
  optional:true,
178
- type: Object,
179
- default_value: nil)
186
+ type: Object,
187
+ default_value: nil),
188
+ FastlaneCore::ConfigItem.new(key: :retries,
189
+ description: "The number of retry attempts before failing an upload request",
190
+ optional: true,
191
+ is_string: false),
192
+ FastlaneCore::ConfigItem.new(key: :timeout,
193
+ description: "The number of seconds to wait before failing an upload request",
194
+ optional: true,
195
+ is_string: false),
196
+ FastlaneCore::ConfigItem.new(key: :bugsnag_cli_path,
197
+ env_name: "BUGSNAG_CLI_PATH",
198
+ description: "Path to your bugsnag-cli",
199
+ optional: true,
200
+ verify_block: proc do |value|
201
+ UI.user_error! "'#{value}' is not executable" unless FastlaneCore::Helper.executable?(value)
202
+ end)
180
203
  ]
181
204
  end
182
205
 
@@ -194,7 +217,8 @@ module Fastlane
194
217
  git_options[:repository] = origin.url
195
218
  git_options[:revision] = repo.revparse("HEAD")
196
219
  end
197
- rescue
220
+ rescue => e
221
+ UI.verbose("Could not load git options: #{e.message}")
198
222
  end
199
223
  return git_options
200
224
  end
@@ -316,45 +340,6 @@ module Fastlane
316
340
  end
317
341
  output
318
342
  end
319
-
320
- def self.parse_response_body(response)
321
- begin
322
- JSON.load(response.body)
323
- rescue
324
- nil
325
- end
326
- end
327
-
328
- def self.send_notification(url, body)
329
- require "net/http"
330
- uri = URI.parse(url)
331
- http = Net::HTTP.new(uri.host, uri.port)
332
- http.read_timeout = 15
333
- http.open_timeout = 15
334
-
335
- http.use_ssl = uri.scheme == "https"
336
-
337
- uri.path == "" ? "/" : uri.path
338
- request = Net::HTTP::Post.new(uri, {"Content-Type" => "application/json"})
339
- request.body = body
340
- begin
341
- response = http.request(request)
342
- rescue => e
343
- UI.user_error! "Failed to notify Bugsnag of a new build: #{e}"
344
- end
345
- if body = parse_response_body(response)
346
- if body.has_key? "errors"
347
- errors = body["errors"].map {|error| "\n * #{error}"}.join
348
- UI.user_error! "The following errors occurred while notifying Bugsnag:#{errors}.\n\nPlease update your lane config and retry."
349
- elsif response.code != "200"
350
- UI.user_error! "Failed to notify Bugsnag of a new build. Please retry. HTTP status code: #{response.code}"
351
- end
352
- if body.has_key? "warnings"
353
- warnings = body["warnings"].map {|warn| "\n * #{warn}"}.join
354
- UI.important "Sending the build to Bugsnag succeeded with the following warnings:#{warnings}\n\nPlease update your lane config."
355
- end
356
- end
357
- end
358
343
  end
359
344
  end
360
345
  end
@@ -1,15 +1,16 @@
1
1
  require_relative "find_info_plist_path"
2
+ require_relative "bugsnag_cli"
3
+
2
4
 
3
5
  module Fastlane
4
6
  module Actions
5
7
  class UploadSymbolsToBugsnagAction < Action
6
-
7
- UPLOAD_SCRIPT_PATH = File.expand_path(
8
- File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'bugsnag-dsym-upload'))
9
-
10
8
  def self.run(params)
9
+ bugsnag_cli_path = BugsnagCli.get_bugsnag_cli_path(params)
10
+ UI.verbose("Using bugsnag-cli from path: #{bugsnag_cli_path}")
11
+
11
12
  # If we have not explicitly set an API key through env, or parameter
12
- # input in Fastfile, find an API key in the Info.plist in config_file param
13
+ # input in Fastfile, find an API key from the Info.plist in config_file param
13
14
  api_key = params[:api_key]
14
15
  if params[:config_file] && params[:api_key] == nil
15
16
  UI.message("Using the API Key from #{params[:config_file]}")
@@ -18,7 +19,7 @@ module Fastlane
18
19
 
19
20
  # If verbose flag is enabled (`--verbose`), display the plugin action debug info
20
21
  # Store the verbose flag for use in the upload arguments.
21
- verbose = UI.verbose("Uploading dSYMs to Bugsnag with the following parameters:")
22
+ UI.verbose("Uploading dSYMs to Bugsnag with the following parameters:")
22
23
  rjust = 30 # set justification width for keys for the list of parameters to output
23
24
  params.values.each do |param|
24
25
  UI.verbose(" #{param[0].to_s.rjust(rjust)}: #{param[1]}")
@@ -28,8 +29,24 @@ module Fastlane
28
29
 
29
30
  parse_dsym_paths(params[:dsym_path]).each do |dsym_path|
30
31
  if dsym_path.end_with?(".zip") or File.directory?(dsym_path)
31
- 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])
32
- success = Kernel.system(UPLOAD_SCRIPT_PATH, *args)
32
+ args = BugsnagCli.upload_args(
33
+ "\"#{dsym_path}\"",
34
+ params[:upload_url],
35
+ params[:project_root],
36
+ api_key,
37
+ params[:ignore_missing_dwarf],
38
+ params[:ignore_empty_dsym],
39
+ params[:dryrun],
40
+ params[:log_level],
41
+ params[:port],
42
+ params[:retries],
43
+ params[:timeout],
44
+ params[:configuration],
45
+ params[:scheme],
46
+ params[:config_file],
47
+ params[:xcode_project]
48
+ )
49
+ success = BugsnagCli.upload_dsym bugsnag_cli_path, args
33
50
  if success
34
51
  UI.success("Uploaded dSYMs in #{dsym_path}")
35
52
  else
@@ -106,12 +123,6 @@ module Fastlane
106
123
  description: "URL of the server receiving uploaded files",
107
124
  default_value: nil,
108
125
  optional: true),
109
- FastlaneCore::ConfigItem.new(key: :symbol_maps_path,
110
- env_name: "BUGSNAG_SYMBOL_MAPS_PATH",
111
- description: "Path to the BCSymbolMaps directory to build complete dSYM files",
112
- default_value: nil,
113
- optional: true,
114
- verify_block: validate_symbol_maps),
115
126
  FastlaneCore::ConfigItem.new(key: :project_root,
116
127
  env_name: "BUGSNAG_PROJECT_ROOT",
117
128
  description: "Root path of the project",
@@ -129,11 +140,55 @@ module Fastlane
129
140
  optional: true,
130
141
  default_value: false,
131
142
  is_string: false),
143
+ FastlaneCore::ConfigItem.new(key: :dryrun,
144
+ env_name: "BUGSNAG_DRYRUN",
145
+ description: "Performs a dry-run of the command without sending any information to BugSnag",
146
+ optional: true,
147
+ default_value: false,
148
+ is_string: false),
149
+ FastlaneCore::ConfigItem.new(key: :log_level,
150
+ env_name: "BUGSNAG_LOG_LEVEL",
151
+ description: "Sets the level of logging to debug, info, warn or fatal",
152
+ optional: true),
153
+ FastlaneCore::ConfigItem.new(key: :port,
154
+ env_name: "BUGSNAG_PORT",
155
+ description: "The port number for the BugSnag upload server",
156
+ optional: true,
157
+ is_string: false),
158
+ FastlaneCore::ConfigItem.new(key: :retries,
159
+ env_name: "BUGSNAG_RETRIES",
160
+ description: "The number of retry attempts before failing an upload request",
161
+ optional: true,
162
+ is_string: false),
163
+ FastlaneCore::ConfigItem.new(key: :timeout,
164
+ env_name: "BUGSNAG_TIMEOUT",
165
+ description: "The number of seconds to wait before failing an upload request",
166
+ optional: true,
167
+ is_string: false),
168
+ FastlaneCore::ConfigItem.new(key: :configuration,
169
+ env_name: "BUGSNAG_CONFIGURATION",
170
+ description: "The configuration used to build the application",
171
+ optional: true),
172
+ FastlaneCore::ConfigItem.new(key: :scheme,
173
+ env_name: "BUGSNAG_SCHEME",
174
+ description: "The name of the Xcode options.Scheme used to build the application",
175
+ optional: true),
132
176
  FastlaneCore::ConfigItem.new(key: :config_file,
133
177
  env_name: "BUGSNAG_CONFIG_FILE",
134
178
  description: "Info.plist location",
135
179
  optional: true,
136
- default_value: FindInfoPlist.default_info_plist_path)
180
+ default_value: FindInfoPlist.default_info_plist_path),
181
+ FastlaneCore::ConfigItem.new(key: :xcode_project,
182
+ env_name: "BUGSNAG_XCODE_PROJECT",
183
+ description: "The path to an Xcode project, workspace or containing directory from which to obtain build information",
184
+ optional: true),
185
+ FastlaneCore::ConfigItem.new(key: :bugsnag_cli_path,
186
+ env_name: "BUGSNAG_CLI_PATH",
187
+ description: "Path to your bugsnag-cli",
188
+ optional: true,
189
+ verify_block: proc do |value|
190
+ UI.user_error! "'#{value}' is not executable" unless FastlaneCore::Helper.executable?(value)
191
+ end)
137
192
  ]
138
193
  end
139
194
 
@@ -154,17 +209,7 @@ module Fastlane
154
209
  }
155
210
  end
156
211
 
157
- def self.upload_args dir, symbol_maps_dir, upload_url, project_root, api_key, verbose, ignore_missing_dwarf, ignore_empty_dsym
158
- args = [verbose ? "--verbose" : "--silent"]
159
- args += ["--ignore-missing-dwarf"] if ignore_missing_dwarf
160
- args += ["--ignore-empty-dsym"] if ignore_empty_dsym
161
- args += ["--api-key", api_key] unless api_key.nil?
162
- args += ["--upload-server", upload_url] unless upload_url.nil?
163
- args += ["--symbol-maps", symbol_maps_dir] unless symbol_maps_dir.nil?
164
- args += ["--project-root", project_root] unless project_root.nil?
165
- args << dir
166
- args
167
- end
212
+
168
213
 
169
214
  # returns an array of unique dSYM-containing directory paths to upload
170
215
  def self.parse_dsym_paths dsym_path
@@ -207,4 +252,4 @@ module Fastlane
207
252
  end
208
253
  end
209
254
  end
210
- end
255
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Bugsnag
3
- VERSION = "2.3.1"
3
+ VERSION = "3.0.0"
4
4
  end
5
5
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require_relative '../lib/fastlane/plugin/bugsnag/actions/bugsnag_cli'
2
3
 
3
4
  Action = Fastlane::Actions::UploadSymbolsToBugsnagAction
4
5
 
@@ -15,29 +16,24 @@ describe Action do
15
16
  FileUtils.rm_rf("#{gem_name}.gem")
16
17
  end
17
18
 
18
- it 'has an executable upload script' do
19
+ it 'has bundled CLI binaries' do
19
20
  system('rake build')
20
21
  system("gem unpack #{gem_name}.gem")
21
- expect(File.exist?(File.join("#{gem_name}/bugsnag-dsym-upload"))).to be true
22
+ expect(File.exist?(File.join("#{gem_name}", "bin", "arm64-linux-bugsnag-cli"))).to be true
23
+ expect(File.exist?(File.join("#{gem_name}", "bin", "arm64-macos-bugsnag-cli"))).to be true
24
+ expect(File.exist?(File.join("#{gem_name}", "bin", "i386-linux-bugsnag-cli"))).to be true
25
+ expect(File.exist?(File.join("#{gem_name}", "bin", "i386-windows-bugsnag-cli.exe"))).to be true
26
+ expect(File.exist?(File.join("#{gem_name}", "bin", "x86_64-linux-bugsnag-cli"))).to be true
27
+ expect(File.exist?(File.join("#{gem_name}", "bin", "x86_64-macos-bugsnag-cli"))).to be true
28
+ expect(File.exist?(File.join("#{gem_name}", "bin", "x86_64-windows-bugsnag-cli.exe"))).to be true
22
29
  end
23
30
  end
24
31
 
25
32
  describe '#run' do
26
- it 'silences script output by default' do
27
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
28
- "--silent",
29
- "--project-root", Dir::pwd,
30
- FIXTURE_PATH).and_return(true)
31
- run_with({dsym_path: FIXTURE_PATH})
32
- end
33
-
34
33
  it 'UI.user_error when script fails' do
35
- expect(Fastlane::UI).to receive(:user_error!).with("Failed uploading #{FIXTURE_PATH}")
36
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
37
- "--silent",
38
- "--project-root", Dir::pwd,
39
- FIXTURE_PATH).and_return(false)
40
- run_with({dsym_path: FIXTURE_PATH})
34
+ expect(Fastlane::UI).to receive(:user_error!).with("Failed uploading #{File.join(FIXTURE_PATH, "ios_proj/Project")}")
35
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{File.join(FIXTURE_PATH, "ios_proj/Project")}\"").and_return(false)
36
+ run_with({dsym_path: File.join(FIXTURE_PATH, "ios_proj/Project")})
41
37
  end
42
38
 
43
39
  it 'requires the dSYM file path to exist' do
@@ -52,25 +48,21 @@ describe Action do
52
48
 
53
49
  it 'uploads a single .dSYM file' do
54
50
  directory = File.join(FIXTURE_PATH, 'dSYMs')
55
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
56
- "--silent", "--project-root", Dir::pwd, directory).and_return(true)
51
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{directory}\"").and_return(true)
57
52
  run_with({dsym_path: File.join(FIXTURE_PATH, 'dSYMs/app.dSYM')})
58
53
  end
59
54
 
60
55
  it 'uploads a .zip of .dSYM files' do
61
56
  path = File.join(FIXTURE_PATH, 'files.zip')
62
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
63
- "--silent", "--project-root", Dir::pwd, path).and_return(true)
57
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{path}\"").and_return(true)
64
58
  run_with({dsym_path: path})
65
59
  end
66
60
 
67
61
  it 'uploads multiple .zip files' do
68
62
  zip1 = File.join(FIXTURE_PATH, 'files.zip')
69
63
  zip2 = File.join(FIXTURE_PATH, 'more_files.zip')
70
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
71
- "--silent", "--project-root", Dir::pwd, zip1).and_return(true)
72
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
73
- "--silent", "--project-root", Dir::pwd, zip2).and_return(true)
64
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{zip1}\"").and_return(true)
65
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{zip2}\"").and_return(true)
74
66
  run_with({dsym_path: [zip1, zip2]})
75
67
  end
76
68
 
@@ -80,10 +72,8 @@ describe Action do
80
72
  dsym3 = File.join(FIXTURE_PATH, 'dSYMs/app2.dSYM')
81
73
  dsym4 = File.join(FIXTURE_PATH, 'stuff/app2.dSYM')
82
74
  directories = [File.join(FIXTURE_PATH, 'dSYMs'), File.join(FIXTURE_PATH, 'stuff')]
83
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
84
- "--silent", "--project-root", Dir::pwd, directories[0]).and_return(true)
85
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
86
- "--silent", "--project-root", Dir::pwd, directories[1]).and_return(true)
75
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{directories[0]}\"").and_return(true)
76
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{directories[1]}\"").and_return(true)
87
77
  run_with({dsym_path: [dsym1, dsym2, dsym3, dsym4]})
88
78
  end
89
79
 
@@ -91,48 +81,32 @@ describe Action do
91
81
  dsym1 = File.join(FIXTURE_PATH, 'dSYMs/app.dSYM')
92
82
  dsym2 = File.join(FIXTURE_PATH, 'dSYMs/app2.dSYM')
93
83
  directory = File.join(FIXTURE_PATH, 'dSYMs')
94
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
95
- "--silent", "--project-root", Dir::pwd, directory).and_return(true)
84
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{Dir::pwd} \"#{directory}\"").and_return(true)
96
85
  run_with({dsym_path: [dsym1, dsym2]})
97
86
  end
98
87
 
99
88
  it 'accepts a project root argument' do
100
89
  root_path = "/test/test/test"
101
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
102
- "--silent",
103
- "--project-root", root_path,
104
- FIXTURE_PATH).and_return(true)
90
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --project-root #{root_path} \"#{FIXTURE_PATH}\"").and_return(true)
105
91
  run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
106
92
  end
107
93
 
108
94
  it 'accepts an API key argument' do
109
95
  api_key = "123456789123456789001234567890FF"
110
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
111
- "--silent",
112
- "--api-key", api_key,
113
- "--project-root", Dir::pwd,
114
- FIXTURE_PATH).and_return(true)
96
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --api-key #{api_key} --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
115
97
  run_with({dsym_path: FIXTURE_PATH, api_key: api_key})
116
98
  end
117
99
 
118
100
  it 'accepts an API key argument with no project root' do
119
101
  api_key = "123456789012345678901234567890FF"
120
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
121
- "--silent",
122
- "--api-key", api_key,
123
- "--project-root", `pwd`.chomp,
124
- FIXTURE_PATH).and_return(true)
102
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --api-key #{api_key} --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
125
103
  run_with({dsym_path: FIXTURE_PATH, api_key: api_key, project_root: nil})
126
104
  end
127
105
 
128
106
  it 'uses default API key argument from plist' do
129
107
  root_path = "/test/test/test"
130
108
  api_key = "12345678901234567890123456789AAA" # Uses the API Key from ./spec/fixtures/ios_proj/FirstRealFolder/Info.plist
131
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
132
- "--silent",
133
- "--api-key", api_key,
134
- "--project-root", root_path,
135
- FIXTURE_PATH).and_return(true)
109
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --api-key #{api_key} --project-root #{root_path} --plist ./FirstRealFolder/Info.plist \"#{FIXTURE_PATH}\"").and_return(true)
136
110
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
137
111
  run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
138
112
  end
@@ -141,11 +115,7 @@ describe Action do
141
115
  it 'uses legacy API key argument from plist' do
142
116
  root_path = "/test/test/test"
143
117
  api_key = "12345678901234567890123456789BBB" # Uses the API Key from ./spec/fixtures/ios_proj_legacy/Project/Info.plist
144
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
145
- "--silent",
146
- "--api-key", api_key,
147
- "--project-root", root_path,
148
- FIXTURE_PATH).and_return(true)
118
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --api-key #{api_key} --project-root #{root_path} --plist ./Project/Info.plist \"#{FIXTURE_PATH}\"").and_return(true)
149
119
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj_legacy')) do
150
120
  run_with({dsym_path: FIXTURE_PATH, project_root: root_path})
151
121
  end
@@ -155,11 +125,7 @@ describe Action do
155
125
  # The order of precedence is 1. option input, 2. env variable, 3. default or config file input (for api key only)
156
126
  # The API key in ./spec/fixtures/ios_proj_legacy/Project/Info.plist is 12345678912345678900123456789AAA.
157
127
  api_key = "12345678912345678900123456789CCC"
158
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
159
- "--silent",
160
- "--api-key", api_key,
161
- "--project-root", File.join(FIXTURE_PATH, 'ios_proj'),
162
- FIXTURE_PATH).and_return(true)
128
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --api-key #{api_key} --project-root #{File.join(FIXTURE_PATH, 'ios_proj')} --plist Project/Info.plist \"#{FIXTURE_PATH}\"").and_return(true)
163
129
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
164
130
  run_with({dsym_path: FIXTURE_PATH, api_key: api_key, config_file: File.join('Project', 'Info.plist')})
165
131
  end
@@ -176,25 +142,47 @@ describe Action do
176
142
 
177
143
  context 'using a private server' do
178
144
  it 'uploads to the private server' do
179
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
180
- "--silent",
181
- "--upload-server", "http://myserver.example.com",
182
- "--project-root", Dir::pwd,
183
- FIXTURE_PATH).and_return(true)
145
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} upload dsym --upload-api-root-url http://myserver.example.com --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
184
146
  run_with({dsym_path: FIXTURE_PATH, upload_url: "http://myserver.example.com"})
185
147
  end
186
148
  end
187
149
 
188
- context 'using bitcode' do
189
- it 'combines dSYM files with symbols' do
190
- path = File.join(FIXTURE_PATH, 'BCSymbolMaps')
191
- expect(Kernel).to receive(:system).with(Action::UPLOAD_SCRIPT_PATH,
192
- "--silent",
193
- "--symbol-maps", path,
194
- "--project-root", Dir::pwd,
195
- FIXTURE_PATH).and_return(true)
196
- run_with({dsym_path: FIXTURE_PATH, symbol_maps_path: path})
197
- end
150
+ it 'accepts a custom bugsnag_cli_path as an option' do
151
+ custom_cli_path = File.join(ENV['HOME'], ".local/bugsnag/bin/bugsnag-cli")
152
+ expect(Kernel).to receive(:system).with("#{custom_cli_path} upload dsym --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
153
+ run_with({dsym_path: FIXTURE_PATH, bugsnag_cli_path: custom_cli_path})
154
+ end
155
+
156
+ it 'accepts a custom bugsnag_cli_path with an API key' do
157
+ custom_cli_path = File.join(ENV['HOME'], ".local/bugsnag/bin/bugsnag-cli")
158
+ api_key = "123456789012345678901234567890FF"
159
+ expect(Kernel).to receive(:system).with("#{custom_cli_path} upload dsym --api-key #{api_key} --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
160
+ run_with({dsym_path: FIXTURE_PATH, api_key: api_key, bugsnag_cli_path: custom_cli_path})
161
+ end
162
+
163
+ it 'logs a warning when using an outdated CLI version' do
164
+ cli_path = File.join(FIXTURE_PATH, 'dummy_bugsnag_cli.sh')
165
+ bundled_bugsnag_cli_version = `#{BUGSNAG_CLI_PATH} --version`.strip
166
+ allow(Fastlane::Actions::UploadSymbolsToBugsnagAction).to receive(:version_from_cli).and_return("1.0.0")
167
+ allow(Fastlane::Actions::UploadSymbolsToBugsnagAction).to receive(:bundled_bugsnag_cli_version).and_return(bundled_bugsnag_cli_version)
168
+
169
+ expect(Fastlane::UI).to receive(:warning).with("The installed bugsnag-cli at #{cli_path} is outdated (1.0.0). The current bundled version is: #{bundled_bugsnag_cli_version}. It is recommended that you either update your installed version or use the bundled version.")
170
+ expect(Kernel).to receive(:system).with("#{cli_path} upload dsym --project-root #{Dir::pwd} \"#{FIXTURE_PATH}\"").and_return(true)
171
+
172
+ run_with({dsym_path: FIXTURE_PATH, bugsnag_cli_path: cli_path})
173
+ end
174
+
175
+ it 'logs doesnt log a warning when using an newer CLI version' do
176
+ cli_version = "9.9.9"
177
+ cli_path = File.join(FIXTURE_PATH, 'dummy_bugsnag_cli.sh')
178
+ bundled_bugsnag_cli_version = `#{BUGSNAG_CLI_PATH} --version`.strip
179
+ allow(Fastlane::Actions::UploadSymbolsToBugsnagAction).to receive(:version_from_cli).and_return(cli_version)
180
+ allow(Fastlane::Actions::UploadSymbolsToBugsnagAction).to receive(:bundled_bugsnag_cli_version).and_return(bundled_bugsnag_cli_version)
181
+
182
+ expect(Fastlane::UI).not_to receive(:warning)
183
+
184
+ ENV['BUGSNAG_CLI_VERSION'] = cli_version
185
+ run_with({dsym_path: FIXTURE_PATH, bugsnag_cli_path: cli_path})
198
186
  end
199
187
  end
200
188
  end
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+
3
+ VERSION="1.0.0"
4
+
5
+ if [[ -n "$BUGSNAG_CLI_VERSION" ]]; then
6
+ VERSION="$BUGSNAG_CLI_VERSION"
7
+ fi
8
+
9
+ if [[ "$1" == "--version" ]]; then
10
+ echo "Version: $VERSION"
11
+ else
12
+ echo "Usage: $0 [VERSION=x.y.z] --version"
13
+ fi
@@ -1,8 +1,13 @@
1
1
  require 'spec_helper'
2
2
  require 'json'
3
3
  require 'fastlane/actions/get_info_plist_value'
4
+ require_relative '../lib/fastlane/plugin/bugsnag/actions/bugsnag_cli'
5
+
4
6
 
5
7
  BuildAction = Fastlane::Actions::SendBuildToBugsnagAction
8
+ GIT_REVISION = `git rev-parse HEAD`.strip
9
+ BUILDER = `whoami`.strip
10
+ GIT_REPO_URL = `git config --get remote.origin.url`.strip
6
11
 
7
12
  describe BuildAction do
8
13
  def run_with args
@@ -11,13 +16,7 @@ describe BuildAction do
11
16
 
12
17
  context 'building an iOS project' do
13
18
  it 'detects default Info.plist file excluding test dirs' do
14
- expect(BuildAction).to receive(:send_notification) do |url, body|
15
- payload = ::JSON.load(body)
16
- expect(payload['appVersion']).to eq '2.0-other'
17
- expect(payload['appBundleVersion']).to eq '22'
18
- expect(payload['apiKey']).to eq '12345678901234567890123456789AAA'
19
- end
20
-
19
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789AAA --version-name 2.0-other --bundle-version 22 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
21
20
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
22
21
  run_with({})
23
22
  end
@@ -26,13 +25,7 @@ describe BuildAction do
26
25
  it 'reads api key from legacy location' do
27
26
  # the API key is now in `bugsnag.apiKey`, it used to be in 'BugsnagAPIKey',
28
27
  # 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
-
28
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789BBB --version-name 4.0-project --bundle-version 44 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
36
29
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj_legacy')) do
37
30
  run_with({})
38
31
  end
@@ -41,11 +34,7 @@ describe BuildAction do
41
34
  context 'using default config_file option' do
42
35
  context 'override API key from config' do
43
36
  it 'reads API key from the api_key option' do
44
- expect(BuildAction).to receive(:send_notification) do |url, body|
45
- payload = ::JSON.load(body)
46
- expect(payload['apiKey']).to eq '12345678901234567890123456789FFF'
47
- end
48
-
37
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789FFF --version-name 2.0-other --bundle-version 22 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
49
38
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
50
39
  run_with({
51
40
  api_key: '12345678901234567890123456789FFF'
@@ -54,12 +43,7 @@ describe BuildAction do
54
43
  end
55
44
 
56
45
  it 'uses input versions from options' do
57
- expect(BuildAction).to receive(:send_notification) do |url, body|
58
- payload = ::JSON.load(body)
59
- expect(payload['appVersion']).to eq '8.0.0'
60
- expect(payload['appBundleVersion']).to eq '800'
61
- end
62
-
46
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789AAA --version-name 8.0.0 --bundle-version 800 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
63
47
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
64
48
  run_with({
65
49
  app_version: '8.0.0',
@@ -72,13 +56,7 @@ describe BuildAction do
72
56
 
73
57
  context 'override config_file option' do
74
58
  it 'reads API key and version info from the config file' do
75
- expect(BuildAction).to receive(:send_notification) do |url, body|
76
- payload = ::JSON.load(body)
77
- expect(payload['appVersion']).to eq '3.0-project'
78
- expect(payload['appBundleVersion']).to eq '33'
79
- expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
80
- end
81
-
59
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789DDD --version-name 3.0-project --bundle-version 33 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
82
60
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
83
61
  run_with({
84
62
  config_file: File.join('Project', 'Info.plist')
@@ -88,13 +66,7 @@ describe BuildAction do
88
66
 
89
67
  context 'override API key, and config file' do
90
68
  it 'uses the input api_key to override a non default config' do
91
- expect(BuildAction).to receive(:send_notification) do |url, body|
92
- payload = ::JSON.load(body)
93
- expect(payload['appVersion']).to eq '3.0-project'
94
- expect(payload['appBundleVersion']).to eq '33'
95
- expect(payload['apiKey']).to eq '12345678901234567890123456789EEE'
96
- end
97
-
69
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789EEE --version-name 3.0-project --bundle-version 33 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
98
70
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
99
71
  run_with({
100
72
  config_file: File.join('Project', 'Info.plist'),
@@ -104,13 +76,7 @@ describe BuildAction do
104
76
  end
105
77
 
106
78
  it 'uses the input versions to override a non default config' do
107
- expect(BuildAction).to receive(:send_notification) do |url, body|
108
- payload = ::JSON.load(body)
109
- expect(payload['appVersion']).to eq '9.0.0'
110
- expect(payload['appBundleVersion']).to eq '900'
111
- expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
112
- end
113
-
79
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789DDD --version-name 9.0.0 --bundle-version 900 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL}").and_return(true)
114
80
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
115
81
  run_with({
116
82
  config_file: File.join('Project', 'Info.plist'),
@@ -122,15 +88,9 @@ describe BuildAction do
122
88
  end
123
89
  end
124
90
 
125
- context 'metadata added to payload' do
91
+ context 'metadata added to args' do
126
92
  it "single key:value pair added" do
127
- expect(BuildAction).to receive(:send_notification) do |url, body|
128
- payload = ::JSON.load(body)
129
- expect(payload['appVersion']).to eq '4.0-project'
130
- expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
131
- expect(payload['metadata']).to eq '"test1": "First test"'
132
- end
133
-
93
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789DDD --version-name 4.0-project --bundle-version 22 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL} --metadata \"test1\": \"First test\"").and_return(true)
134
94
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
135
95
  run_with({
136
96
  app_version: '4.0-project',
@@ -139,21 +99,29 @@ describe BuildAction do
139
99
  })
140
100
  end
141
101
  end
142
-
102
+
143
103
  it "multiple key:value pairs added" do
144
- expect(BuildAction).to receive(:send_notification) do |url, body|
145
- payload = ::JSON.load(body)
146
- expect(payload['appVersion']).to eq '4.0-project'
147
- expect(payload['apiKey']).to eq '12345678901234567890123456789DDD'
148
- expect(payload['metadata']).to eq '"test1": "First test", "test2": "Second test", "test3": "Third test"'
104
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789DDD --version-name 4.0-project --bundle-version 22 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL} --metadata \"test1\": \"First test\", \"test2\": \"Second test\", \"test3\": \"Third test\"").and_return(true)
105
+ Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
106
+ run_with({
107
+ app_version: '4.0-project',
108
+ api_key: '12345678901234567890123456789DDD',
109
+ metadata: '"test1": "First test", "test2": "Second test", "test3": "Third test"'
110
+ })
149
111
  end
112
+ end
150
113
 
114
+ it "multiple key:value pairs added as a hash" do
115
+ expect(Kernel).to receive(:system).with("#{BUGSNAG_CLI_PATH} create-build --api-key 12345678901234567890123456789DDD --version-name 4.0-project --bundle-version 22 --builder-name #{BUILDER} --revision #{GIT_REVISION} --repository #{GIT_REPO_URL} --metadata \"custom_field_1\"=\"value1\",\"custom_field_2\"=\"value2\"").and_return(true)
151
116
  Dir.chdir(File.join(FIXTURE_PATH, 'ios_proj')) do
152
117
  run_with({
153
- app_version: '4.0-project',
154
- api_key: '12345678901234567890123456789DDD',
155
- metadata: '"test1": "First test", "test2": "Second test", "test3": "Third test"'
156
- })
118
+ app_version: '4.0-project',
119
+ api_key: '12345678901234567890123456789DDD',
120
+ metadata: {
121
+ "custom_field_1": "value1",
122
+ "custom_field_2": "value2"
123
+ }
124
+ })
157
125
  end
158
126
  end
159
127
  end
data/spec/spec_helper.rb CHANGED
@@ -8,3 +8,11 @@ require 'fastlane' # to import the Action super class
8
8
  require 'fastlane/plugin/bugsnag' # import the actual plugin
9
9
 
10
10
  FIXTURE_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures'))
11
+ BUGSNAG_CLI_PATH = BugsnagCli.get_bundled_path
12
+
13
+ # expands the `got` and `expected` output in the RSpec output
14
+ RSpec.configure do |config|
15
+ config.expect_with :rspec do |expectations|
16
+ expectations.max_formatted_output_length = nil # unlimited
17
+ end
18
+ 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: 2.3.1
4
+ version: 3.0.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: 2024-09-16 00:00:00.000000000 Z
11
+ date: 2025-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xml-simple
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: abbrev
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: pry
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -136,15 +150,22 @@ dependencies:
136
150
  - - ">="
137
151
  - !ruby/object:Gem::Version
138
152
  version: 2.28.5
139
- description:
153
+ description:
140
154
  email: iskanamagus@gmail.com
141
155
  executables: []
142
156
  extensions: []
143
157
  extra_rdoc_files: []
144
158
  files:
145
159
  - LICENSE.txt
146
- - bugsnag-dsym-upload
160
+ - bin/arm64-linux-bugsnag-cli
161
+ - bin/arm64-macos-bugsnag-cli
162
+ - bin/i386-linux-bugsnag-cli
163
+ - bin/i386-windows-bugsnag-cli.exe
164
+ - bin/x86_64-linux-bugsnag-cli
165
+ - bin/x86_64-macos-bugsnag-cli
166
+ - bin/x86_64-windows-bugsnag-cli.exe
147
167
  - lib/fastlane/plugin/bugsnag.rb
168
+ - lib/fastlane/plugin/bugsnag/actions/bugsnag_cli.rb
148
169
  - lib/fastlane/plugin/bugsnag/actions/find_info_plist_path.rb
149
170
  - lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb
150
171
  - lib/fastlane/plugin/bugsnag/actions/upload_symbols_to_bugsnag.rb
@@ -153,6 +174,7 @@ files:
153
174
  - spec/fixtures/BCSymbolMaps/real.bcsymbolmap
154
175
  - spec/fixtures/dSYMs/app.dSYM/Contents/Resources/DWARF/app
155
176
  - spec/fixtures/dSYMs/app2.dSYM/Contents/Resources/DWARF/app2
177
+ - spec/fixtures/dummy_bugsnag_cli.sh
156
178
  - spec/fixtures/files.zip
157
179
  - spec/fixtures/invalid_file
158
180
  - spec/fixtures/ios_proj/FirstRealFolder/Info.plist
@@ -164,11 +186,11 @@ files:
164
186
  - spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
165
187
  - spec/send_build_to_bugsnag_spec.rb
166
188
  - spec/spec_helper.rb
167
- homepage: https://github.com/bugsnag/bugsnag-dsym-upload
189
+ homepage: https://github.com/bugsnag/fastlane-plugin-bugsnag
168
190
  licenses:
169
191
  - MIT
170
192
  metadata: {}
171
- post_install_message:
193
+ post_install_message:
172
194
  rdoc_options: []
173
195
  require_paths:
174
196
  - lib
@@ -183,23 +205,24 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
205
  - !ruby/object:Gem::Version
184
206
  version: '0'
185
207
  requirements: []
186
- rubygems_version: 3.4.18
187
- signing_key:
208
+ rubygems_version: 3.5.3
209
+ signing_key:
188
210
  specification_version: 4
189
211
  summary: Uploads dSYM files to Bugsnag
190
212
  test_files:
191
- - spec/spec_helper.rb
192
213
  - spec/bugsnag_upload_dsym_action_spec.rb
193
- - spec/send_build_to_bugsnag_spec.rb
194
- - spec/fixtures/ios_proj_legacy/Project/Info.plist
195
- - spec/fixtures/more_files.zip
196
214
  - spec/fixtures/BCSymbolMaps/real.bcsymbolmap
215
+ - spec/fixtures/dSYMs/app.dSYM/Contents/Resources/DWARF/app
216
+ - spec/fixtures/dSYMs/app2.dSYM/Contents/Resources/DWARF/app2
217
+ - spec/fixtures/dummy_bugsnag_cli.sh
218
+ - spec/fixtures/files.zip
219
+ - spec/fixtures/invalid_file
197
220
  - spec/fixtures/ios_proj/FirstRealFolder/Info.plist
198
- - spec/fixtures/ios_proj/aaTests/Info.plist
199
221
  - spec/fixtures/ios_proj/Project/Info.plist
200
- - spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
222
+ - spec/fixtures/ios_proj/aaTests/Info.plist
223
+ - spec/fixtures/ios_proj_legacy/Project/Info.plist
224
+ - spec/fixtures/more_files.zip
201
225
  - spec/fixtures/stuff/app.dSYM/Contents/Resources/DWARF/app
202
- - spec/fixtures/invalid_file
203
- - spec/fixtures/files.zip
204
- - spec/fixtures/dSYMs/app2.dSYM/Contents/Resources/DWARF/app2
205
- - spec/fixtures/dSYMs/app.dSYM/Contents/Resources/DWARF/app
226
+ - spec/fixtures/stuff/app2.dSYM/Contents/Resources/DWARF/app2
227
+ - spec/send_build_to_bugsnag_spec.rb
228
+ - spec/spec_helper.rb
data/bugsnag-dsym-upload DELETED
@@ -1,222 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # Given a directory, uploads any *.dSYM bundles with the directory to
4
- # Bugsnag. As a pre-upload step, bitcode symbol maps can be combined with
5
- # dSYM files to ensure symbols are sent for bitcode-enabled binaries.
6
- #
7
- # Depends on:
8
- # * curl
9
- # * dwarfdump
10
- # * dsymutil (for --symbol-maps, optional)
11
- # * unzip (for uploading a .zip file, optional)
12
-
13
- function print_usage() {
14
- echo "Usage: $0 [--symbol-maps DIR] dSYMS_PATH"
15
- echo
16
- echo "-h, --help Displays this message"
17
- echo "-v, --verbose Print verbose logging output during execution"
18
- echo "--api-key API_KEY The API key of the project the dSYM should be applied to"
19
- echo "--symbol-maps DIR Path to a directory of bitcode symbol maps. The"
20
- echo " dSYM files will be restored with symbols prior to"
21
- echo " upload. Requires dsymutil."
22
- echo "--upload-server URL The server receiving dSYM files. Set this value if"
23
- echo " using an on-premise Bugsnag installation"
24
- echo "--project-root DIR The root directory of the project. This will help to"
25
- echo " group error reports by project"
26
- echo "--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"
32
- }
33
-
34
- function exit_with_usage() {
35
- echo $1
36
- echo
37
- print_usage
38
- exit 1
39
- }
40
-
41
- function log() {
42
- if [[ $silent != 1 ]]; then
43
- echo $@
44
- fi
45
- }
46
-
47
- function log_verbose() {
48
- if [[ $verbose == 1 ]]; then
49
- log $@
50
- fi
51
- }
52
-
53
- upload_server=https://upload.bugsnag.com
54
- unset symbol_maps
55
- unset dsym_dir
56
- unset verbose
57
- unset ignore_empty_dsym
58
- unset ignore_missing_dwarf
59
- unset silent
60
- unset project_root
61
- unset api_key
62
-
63
- while [[ $# -gt 0 ]]; do
64
- case $1 in
65
- -h|--help)
66
- print_usage
67
- exit 0;;
68
- -s|--silent)
69
- silent=1
70
- shift;;
71
- -v|--verbose)
72
- verbose=1
73
- shift;;
74
- --ignore-missing-dwarf)
75
- ignore_missing_dwarf=1
76
- shift;;
77
- --ignore-empty-dsym)
78
- ignore_empty_dsym=1
79
- shift;;
80
- --symbol-maps)
81
- symbol_maps=$2
82
- shift
83
- shift;;
84
- --upload-server)
85
- upload_server=$2
86
- shift
87
- shift;;
88
- --api-key)
89
- api_key=$2
90
- shift
91
- shift;;
92
- --project-root)
93
- project_root=$2
94
- shift
95
- shift;;
96
- -*)
97
- exit_with_usage "Invalid parameter provided: $1";;
98
- *)
99
- dsym_dir=$1
100
- break;;
101
- esac
102
- done
103
-
104
- # Set IFS to ensure that file paths with spaces in get processed correctly
105
- IFS=$'\n'
106
-
107
- if [[ ! -z $symbol_maps ]]; then
108
- if [[ ! -d $symbol_maps ]]; then
109
- exit_with_usage "Bitcode symbol map parameter is not a directory"
110
- elif [[ ! -x "$(command -v dsymutil 2>/dev/null)" ]]; then
111
- exit_with_usage "dsymutil command not found."
112
- fi
113
- fi
114
- if [[ -z $dsym_dir ]]; then
115
- exit_with_usage "No dSYM directory provided"
116
- fi
117
- if [[ ! -d $dsym_dir ]]; then
118
- if [[ $dsym_dir == *".zip" ]]; then
119
- if [[ ! -x "$(command -v unzip 2>/dev/null)" ]]; then
120
- exit_with_usage "unzip command not found."
121
- fi
122
- temp_dir=$(mktemp -dt "bugsnag-dsym-upload.XXX")
123
- unzip -qq $dsym_dir -d $temp_dir
124
- dsym_dir=$temp_dir
125
- else
126
- exit_with_usage "'$dsym_dir' is not a directory or a zip file"
127
- fi
128
- fi
129
-
130
- log_verbose "Uploading files to $upload_server"
131
- success_count=0
132
- warning_count=0
133
- fail_count=0
134
-
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
137
- log_verbose "Preparing to upload $dsym"
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
-
153
- if [[ -d $symbol_maps ]]; then
154
- log_verbose "Updating file with bitcode symbol maps in $symbol_maps"
155
- dsymutil "$dsym" --symbol-map "$symbol_maps"
156
- fi
157
-
158
- dwarf_data=$dsym/Contents/Resources/DWARF
159
- if [[ ! -d $dwarf_data ]]; then
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
167
- continue
168
- fi
169
- for file in $dwarf_data/*; do
170
- uuid=$(dwarfdump -u $file 2>/dev/null)
171
- if [[ $uuid == UUID* ]]; then
172
- log Uploading $uuid
173
-
174
- # Attach the api key and project root parameters if they have been provided
175
- args=""
176
- if [[ ! -z $project_root ]]; then
177
- args="-F projectRoot=\"$project_root\" "
178
- fi
179
-
180
- if [[ ! -z $api_key ]]; then
181
- args="$args-F apiKey=$api_key"
182
- fi
183
-
184
- # We need to shell out to perform the curl as there seems to be some indirect
185
- # wrapping of this script which causes the command to fail if called directly.
186
- curl_cmd="curl --fail --silent --show-error --http1.1 $upload_server -F 'dsym=@\"$file\"' $args"
187
- output=$(sh -c "$curl_cmd")
188
-
189
- if [ $? -eq 0 ] && [ "$output" != "invalid apiKey" ]; then
190
- success_count=$((success_count+1))
191
- else
192
- fail_count=$((fail_count+1))
193
- log "[ERROR] Failed to upload file: $file"
194
- fi
195
- echo $output | grep -v '^OK$'
196
- log
197
- else
198
- log "[ERROR] Skipping file without UUID: $file"
199
- fail_count=$((fail_count+1))
200
- fi
201
- done
202
- done
203
-
204
- exit_code=0
205
- if [ $success_count -gt 0 ]; then
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"
211
- fi
212
-
213
- if [ $fail_count -gt 0 ]; then
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"
220
- fi
221
-
222
- exit $exit_code