11require "xmlsimple"
22require "json"
33require_relative "find_info_plist_path"
4+ require_relative "bundled_cli_path"
45
56module 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 \n Please 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 \n Please 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
0 commit comments