Skip to content

Commit 47084c6

Browse files
committed
move the create build action over to the bugsnag CLI
1 parent dcd85e9 commit 47084c6

File tree

3 files changed

+125
-76
lines changed

3 files changed

+125
-76
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'os'
2+
require 'rbconfig'
3+
4+
class BundledCli
5+
def self.get_path
6+
host_cpu = RbConfig::CONFIG['host_cpu']
7+
if OS.mac?
8+
if host_cpu =~ /arm|aarch64/
9+
self.bin_folder('arm64-macos-bugsnag-cli')
10+
else
11+
self.bin_folder('x86_64-macos-bugsnag-cli')
12+
end
13+
elsif OS.windows?
14+
if OS.bits == 64
15+
self.bin_folder('x86_64-windows-bugsnag-cli.exe')
16+
else
17+
self.bin_folder('i386-windows-bugsnag-cli.exe')
18+
end
19+
else
20+
if host_cpu =~ /arm|aarch64/
21+
self.bin_folder('arm64-linux-bugsnag-cli')
22+
else if OS.bits == 64
23+
self.bin_folder('x86_64-linux-bugsnag-cli')
24+
else
25+
self.bin_folder('i386-linux-bugsnag-cli')
26+
end
27+
end
28+
end
29+
end
30+
31+
def self.bin_folder(filename)
32+
File.expand_path("../../../../../bin/#{filename}", File.dirname(__FILE__))
33+
end
34+
end

tools/fastlane-plugin/lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb

Lines changed: 89 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
11
require "xmlsimple"
22
require "json"
33
require_relative "find_info_plist_path"
4+
require_relative "bundled_cli_path"
45

56
module Fastlane
67
module Actions
78
class SendBuildToBugsnagAction < Action
9+
def self.get_bugsnag_cli_path(params)
10+
bundled_bugsnag_cli_path = BundledCli.get_path
11+
bundled_bugsnag_cli_version = Gem::Version.new(`#{bundled_bugsnag_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
812

9-
BUILD_TOOL = "bugsnag-fastlane-plugin"
13+
if params[:bugsnag_cli_path]
14+
bugsnag_cli_path = params[:bugsnag_cli_path] || bundled_bugsnag_cli_path
15+
16+
bugsnag_cli_version = Gem::Version.new(`#{bugsnag_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
17+
18+
if bugsnag_cli_version < bundled_bugsnag_cli_version
19+
UI.warning("Your bugsnag-cli is outdated. The current bugsnag-cli version is: #{bundled_bugsnag_cli_version}")
20+
end
21+
bugsnag_cli_path
22+
else
23+
bundled_bugsnag_cli_path
24+
end
25+
end
1026

1127
def self.run(params)
12-
payload = {buildTool: BUILD_TOOL, sourceControl: {}}
28+
bugsnag_cli_path = get_bugsnag_cli_path(params)
29+
UI.verbose("Using bugsnag-cli from path: #{bugsnag_cli_path}")
30+
31+
payload = {}
1332

1433
# If a configuration file was found or was specified, load in the options:
1534
if params[:config_file]
@@ -19,35 +38,41 @@ def self.run(params)
1938
# for each of the config options, if it's not been overriden by any
2039
# input to the lane, write it to the payload:
2140
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]
41+
payload[:versionName] = params[:app_version] || config_options[:appVersion]
42+
payload[:versionCode] = params[:android_version_code] || config_options[:appVersionCode]
43+
payload[:bundleVersion] = params[:ios_bundle_version] || config_options[:appBundleVersion]
2544
payload[:releaseStage] = params[:release_stage] || config_options[:releaseStage] || "production"
2645
else
2746
# No configuration file was found or specified, use the input parameters:
2847
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]
48+
payload[:versionName] = params[:app_version]
49+
payload[:versionCode] = params[:android_version_code]
50+
payload[:bundleVersion] = params[:ios_bundle_version]
3251
payload[:releaseStage] = params[:release_stage] || "production"
3352
end
3453

3554
# If builder, or source control information has been provided into
3655
# Fastlane, apply it to the payload here.
3756
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]
57+
payload[:revision] = params[:revision] if params[:revision]
58+
payload[:repository] = params[:repository] if params[:repository]
59+
payload[:provider] = params[:provider] if params[:provider]
60+
61+
payload[:autoAssignRelease] = params[:auto_assign_release] if params[:auto_assign_release]
4162

4263
# If provided apply metadata to payload.
4364
payload[:metadata] = params[:metadata]
4465

66+
payload[:retries] = params[:retries] if params[:retries]
67+
payload[:timeout] = params[:timeout] if params[:timeout]
68+
payload[:buildApiRootUrl] = params[:endpoint] if params[:endpoint]
69+
4570
payload.reject! {|k,v| v == nil || (v.is_a?(Hash) && v.empty?)}
4671

4772
if payload[:apiKey].nil? || !payload[:apiKey].is_a?(String)
4873
UI.user_error! missing_api_key_message(params)
4974
end
50-
if payload[:appVersion].nil?
75+
if payload[:versionName].nil?
5176
UI.user_error! missing_app_version_message(params)
5277
end
5378

@@ -57,7 +82,7 @@ def self.run(params)
5782
UI.verbose(" #{param[0].to_s.rjust(18)}: #{param[1]}")
5883
end
5984

60-
send_notification(params[:endpoint], ::JSON.dump(payload))
85+
send_notification(bugsnag_cli_path, ::JSON.dump(payload))
6186
end
6287

6388
def self.missing_api_key_message(params)
@@ -146,6 +171,11 @@ def self.available_options
146171
FastlaneCore::ConfigItem.new(key: :release_stage,
147172
description: "Release stage being built, i.e. staging, production",
148173
optional: true),
174+
FastlaneCore::ConfigItem.new(key: :auto_assign_release,
175+
description: "Whether to automatically associate this build with any new error events and sessions that are received for",
176+
optional: true,
177+
default_value: false,
178+
is_string: false),
149179
FastlaneCore::ConfigItem.new(key: :builder,
150180
description: "The name of the entity triggering the build",
151181
optional: true,
@@ -175,8 +205,23 @@ def self.available_options
175205
FastlaneCore::ConfigItem.new(key: :metadata,
176206
description: "Metadata",
177207
optional:true,
178-
type: Object,
179-
default_value: nil)
208+
type: Object,
209+
default_value: nil),
210+
FastlaneCore::ConfigItem.new(key: :retries,
211+
description: "The number of retry attempts before failing an upload request",
212+
optional: true,
213+
is_string: false),
214+
FastlaneCore::ConfigItem.new(key: :timeout,
215+
description: "The number of seconds to wait before failing an upload request",
216+
optional: true,
217+
is_string: false),
218+
FastlaneCore::ConfigItem.new(key: :bugsnag_cli_path,
219+
env_name: "BUGSNAG_CLI_PATH",
220+
description: "Path to your bugsnag-cli",
221+
optional: true,
222+
verify_block: proc do |value|
223+
UI.user_error! "'#{value}' is not executable" unless FastlaneCore::Helper.executable?(value)
224+
end)
180225
]
181226
end
182227

@@ -324,35 +369,36 @@ def self.parse_response_body(response)
324369
nil
325370
end
326371
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."
372+
373+
def self.to_kebab_case(str)
374+
str.gsub(/([a-z])([A-Z])/, '\1-\2').downcase
375+
end
376+
377+
def self.json_to_cli_args(json_payload)
378+
data = JSON.parse(json_payload)
379+
380+
data.map do |k, v|
381+
key = to_kebab_case(k)
382+
if v.is_a?(Hash)
383+
# Convert nested hash into key=value pairs joined by commas
384+
nested = v.map { |nk, nv| "#{to_kebab_case(nk)}=#{nv}" }.join(",")
385+
"--#{key}=#{nested}"
386+
else
387+
"--#{key}=#{v}"
355388
end
389+
end.join(" ")
390+
end
391+
392+
def self.send_notification(cli_path, body)
393+
args = self.json_to_cli_args(body)
394+
bugsnag_cli_command = "#{cli_path} create-build #{args}"
395+
396+
UI.verbose("Running command: #{bugsnag_cli_command}")
397+
success = Kernel.system(bugsnag_cli_command)
398+
if success
399+
UI.success("Build successfully sent to Bugsnag")
400+
else
401+
UI.user_error!("Failed to send build to Bugsnag.")
356402
end
357403
end
358404
end

tools/fastlane-plugin/lib/fastlane/plugin/bugsnag/actions/upload_symbols_to_bugsnag.rb

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
require_relative "find_info_plist_path"
2-
require 'os'
3-
require 'rbconfig'
2+
require_relative "bundled_cli_path"
43

54

65
module Fastlane
76
module Actions
87
class UploadSymbolsToBugsnagAction < Action
98
def self.get_bugsnag_cli_path(params)
10-
bundled_bugsnag_cli_path = self.bundled_bugsnag_cli_path
9+
bundled_bugsnag_cli_path = BundledCli.get_path
1110
bundled_bugsnag_cli_version = Gem::Version.new(`#{bundled_bugsnag_cli_path} --version`.scan(/(?:\d+\.?){3}/).first)
1211

1312
if params[:bugsnag_cli_path]
@@ -291,36 +290,6 @@ def self.default_dsym_paths
291290
paths += coerce_array(Actions.lane_context[SharedValues::DSYM_PATHS]) if download_dsym_dsyms? # set by `download_dsyms` Fastlane action
292291
parse_dsym_paths(paths.uniq)
293292
end
294-
295-
def self.bundled_bugsnag_cli_path
296-
host_cpu = RbConfig::CONFIG['host_cpu']
297-
if OS.mac?
298-
if host_cpu =~ /arm|aarch64/
299-
self.bin_folder('arm64-macos-bugsnag-cli')
300-
else
301-
self.bin_folder('x86_64-macos-bugsnag-cli')
302-
end
303-
elsif OS.windows?
304-
if OS.bits == 64
305-
self.bin_folder('x86_64-windows-bugsnag-cli.exe')
306-
else
307-
self.bin_folder('i386-windows-bugsnag-cli.exe')
308-
end
309-
else
310-
if host_cpu =~ /arm|aarch64/
311-
self.bin_folder('arm64-linux-bugsnag-cli')
312-
else if OS.bits == 64
313-
self.bin_folder('x86_64-linux-bugsnag-cli')
314-
else
315-
self.bin_folder('i386-linux-bugsnag-cli')
316-
end
317-
end
318-
end
319-
end
320-
321-
def self.bin_folder(filename)
322-
File.expand_path("../../../../../bin/#{filename}", File.dirname(__FILE__))
323-
end
324293
end
325294
end
326295
end

0 commit comments

Comments
 (0)