Class: Fastlane::Actions::SendBuildToBugsnagAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb

Class Method Summary collapse

Class Method Details

.authorsObject



106
107
108
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 106

def self.authors
  ["cawllec"]
end

.available_optionsObject



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 130

def self.available_options
  git_options = load_git_remote_options
  [
    FastlaneCore::ConfigItem.new(key: :config_file,
                                 description: "AndroidManifest.xml/Info.plist location",
                                 optional: true,
                                 default_value: default_config_file_path),
    FastlaneCore::ConfigItem.new(key: :api_key,
                                 description: "Bugsnag API Key",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :app_version,
                                 description: "App version being built",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :android_version_code,
                                 description: "Android app version code",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :ios_bundle_version,
                                 description: "iOS/macOS/tvOS bundle version",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :release_stage,
                                 description: "Release stage being built, i.e. staging, production",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :auto_assign_release,
                                 description: "Whether to automatically associate this build with any new error events and sessions that are received for",
                                 optional: true,
                                 default_value: false,
                                 is_string: false),
    FastlaneCore::ConfigItem.new(key: :builder,
                                 description: "The name of the entity triggering the build",
                                 optional: true,
                                 default_value: `whoami`.chomp),
    FastlaneCore::ConfigItem.new(key: :repository,
                                 description: "The source control repository URL for this application",
                                 optional: true,
                                 default_value: git_options[:repository]),
    FastlaneCore::ConfigItem.new(key: :revision,
                                 description: "The source control revision id",
                                 optional: true,
                                 default_value: git_options[:revision]),
    FastlaneCore::ConfigItem.new(key: :provider,
                                 description: "The source control provider, one of 'github-enterprise', 'gitlab-onpremise', or 'bitbucket-server', if any",
                                 optional: true,
                                 default_value: nil,
                                 verify_block: proc do |value|
                                   valid = ['github', 'github-enterprise', 'gitlab', 'gitlab-onpremise', 'bitbucket', 'bitbucket-server'].include? value
                                   unless valid
                                     UI.user_error!("Provider must be one of 'github', 'github-enterprise', 'gitlab', 'gitlab-onpremise', 'bitbucket', 'bitbucket-server', or unspecified")
                                   end
                                 end),
    FastlaneCore::ConfigItem.new(key: :endpoint,
                                 description: "Bugsnag deployment endpoint",
                                 default_value: nil,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :metadata,
                                 description: "Metadata",
                                 optional:true,
                                 type: Object,
                                 default_value: nil),
    FastlaneCore::ConfigItem.new(key: :retries,
                                 description: "The number of retry attempts before failing an upload request",
                                 optional: true,
                                 is_string: false),
    FastlaneCore::ConfigItem.new(key: :timeout,
                                 description: "The number of seconds to wait before failing an upload request",
                                 optional: true,
                                 is_string: false),
    FastlaneCore::ConfigItem.new(key: :bugsnag_cli_path,
                                 env_name: "BUGSNAG_CLI_PATH",
                                 description: "Path to your bugsnag-cli",
                                 optional: true,
                                 verify_block: proc do |value|
                                   UI.user_error! "'#{value}' is not executable" unless FastlaneCore::Helper.executable?(value)
                                 end)
  ]
end

.categoryObject



114
115
116
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 114

def self.category
  :building
end

.default_android_manifest_pathObject



276
277
278
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 276

def self.default_android_manifest_path
  Dir.glob("./{android/,}{app,}/src/main/AndroidManifest.xml").sort.first
end

.default_config_file_pathObject

Used to get a default configuration file (AndroidManifest.xml or Info.plist)



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 227

def self.default_config_file_path
  case lane_context[:PLATFORM_NAME]
  when nil
    if file_path = default_android_manifest_path
      return file_path
    elsif file_path = FindInfoPlist.default_info_plist_path
      return file_path
    end
  when :android
    if file_path = default_android_manifest_path
      return file_path
    end
  else
    if file_path = FindInfoPlist.default_info_plist_path
      return file_path
    end
  end
end

.descriptionObject



102
103
104
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 102

def self.description
  "Notifies Bugsnag of a build"
end

.detailsObject



122
123
124
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 122

def self.details
  "Notifies Bugsnag of a new build being released including app version and source control details"
end

.example_codeObject



110
111
112
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 110

def self.example_code
  ['send_build_to_bugsnag']
end

.get_meta_data(object, output = []) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 330

def self.(object, output = [])
  if object.is_a?(Array)
    object.each do |item|
      output = (item, output)
    end
  elsif object.is_a?(Hash)
    object.each do |key, value|
      if key === "meta-data"
        output << value
      elsif value.is_a?(Array) || value.is_a?(Hash)
        output = (value, output)
      end
    end
  end
  output.flatten
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 126

def self.is_supported?(platform)
  [:ios, :mac, :android].include?(platform)
end

.load_config_file_options(config_file) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 246

def self.load_config_file_options config_file
  options = {}
  case File.extname(config_file)
  when ".xml"
    options = load_options_from_xml(config_file)
    return options
  when ".plist"
    options = load_options_from_plist(config_file)
    return options
  else
    UI.user_error("File type of '#{config_file}' was not recognised. This should be .xml for Android and .plist for Cococa")
  end
end

.load_git_remote_optionsObject

Get any Git options (remote repo, and revision) from the directory



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 209

def self.load_git_remote_options
  git_options = {repository:nil, revision:nil}
  require "git"
  begin
    repo = Git.open(Dir.pwd)
    origin = repo.remotes.detect {|r| r.name == "origin"}
    origin = repo.remotes.first unless origin
    if origin
      git_options[:repository] = origin.url
      git_options[:revision] = repo.revparse("HEAD")
    end
  rescue => e
    UI.verbose("Could not load git options: #{e.message}")
  end
  return git_options
end

.load_options_from_plist(file_path) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 260

def self.load_options_from_plist file_path
  options = {}
  plist_getter = Fastlane::Actions::GetInfoPlistValueAction
  bugsnag_dict = plist_getter.run(path: file_path, key: "bugsnag")
  api_key = bugsnag_dict["apiKey"] unless bugsnag_dict.nil?
  release_stage = bugsnag_dict["releaseStage"] unless bugsnag_dict.nil?
  if api_key.nil?
    api_key = plist_getter.run(path: file_path, key: "BugsnagAPIKey")
  end
  options[:apiKey] = api_key
  options[:releaseStage] = release_stage
  options[:appVersion] = plist_getter.run(path: file_path, key: "CFBundleShortVersionString")
  options[:appBundleVersion] = plist_getter.run(path: file_path, key: "CFBundleVersion")
  return options
end

.load_options_from_xml(file_path) ⇒ Object



280
281
282
283
284
285
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 280

def self.load_options_from_xml file_path
  options = options_from_android_manifest(file_path)
  build_gradle_path = Dir.glob("{android/,}app/build.gradle").sort.first || Dir.glob("build.gradle").sort.first
  options.merge!(options_from_build_gradle(build_gradle_path)) if build_gradle_path
  return options
end

.map_meta_data(meta_data) ⇒ Object



347
348
349
350
351
352
353
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 347

def self.()
  output = {}
  .each do |hash|
    output[hash["android:name"]] = hash["android:value"]
  end
  output
end

.missing_api_key_message(params) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 66

def self.missing_api_key_message(params)
  message = "A Bugsnag API key is required to release a build. "
  if lane_context[:PLATFORM_NAME] == :android
    if params[:config_file]
      message << "Set com.bugsnag.android.API_KEY in your AndroidManifest.xml to detect API key automatically."
    else
      message << "Set the config_file option with the path to your AndroidManifest.xml and set com.bugsnag.android.API_KEY in it to detect API key automatically."
    end
  else
    if params[:config_file]
      message << "Set bugsnag.apiKey in your Info.plist file to detect API key automatically."
    else
      message << "Set the config_file option with the path to your Info.plist and set bugsnag.apiKey in it to detect API key automatically."
    end
  end
  message
end

.missing_app_version_message(params) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 84

def self.missing_app_version_message(params)
  message = "An app version must be specified release a build. "
  if lane_context[:PLATFORM_NAME] == :android
    if params[:config_file]
      message << "Set com.bugsnag.android.APP_VERSION in your AndroidManifest.xml to detect this value automatically."
    else
      message << "Set the config_file option with the path to your AndroidManifest.xml and set com.bugsnag.android.APP_VERSION in it to detect this value automatically."
    end
  else
    if params[:config_file]
      message << "Set the app_version option with your app version or set config_file to update the path to your Info.plist"
    else
      message << "Set the config_file option with the path to your Info.plist"
    end
  end
  message
end

.options_from_android_manifest(file_path) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 287

def self.options_from_android_manifest file_path
  options = {}
  begin
    xml_content = File.read(file_path)
    doc = REXML::Document.new(xml_content)
     = {}
    REXML::XPath.each(doc, "//meta-data") do |element|
      name = element.attributes["android:name"]
      value = element.attributes["android:value"]
      [name] = value if name
    end
    options[:apiKey] = ["com.bugsnag.android.API_KEY"]
    options[:appVersion] = ["com.bugsnag.android.APP_VERSION"]
    options[:releaseStage] = ["com.bugsnag.android.RELEASE_STAGE"]
  rescue ArgumentError, REXML::ParseException, Errno::ENOENT, Errno::EACCES => e
    nil
  end
  options
end

.options_from_build_gradle(file_path) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 307

def self.options_from_build_gradle file_path
  options = {}
  begin
    content = File.read(file_path)
    if content =~ /versionCode (\d+)/
      options[:appVersionCode] = $1
    end
    if content =~ /versionName \W(.*)\W[\s]*\n/
      options[:appVersion] = $1
    end
  rescue
  end
  options
end

.parse_android_manifest_options(config_hash) ⇒ Object

NOTE: The following methods are no longer used. XML parsing is now handled directly via REXML::XPath in options_from_android_manifest. Kept for reference only.



326
327
328
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 326

def self.parse_android_manifest_options config_hash
  ((config_hash))
end

.return_valueObject



118
119
120
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 118

def self.return_value
  nil
end

.run(params) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/fastlane/plugin/bugsnag/actions/send_build_to_bugsnag.rb', line 9

def self.run(params)
  bugsnag_cli_path = BugsnagCli.get_bugsnag_cli_path(params)

  # If a configuration file was found or was specified, load in the options:
  config_options = {}
  if params[:config_file]
    UI.message("Loading build information from #{params[:config_file]}")
    config_options = load_config_file_options(params[:config_file])
  end

  api_key = params[:api_key] || config_options[:apiKey] unless (params[:api_key].nil? && config_options[:apiKey].nil?)
  version_name = params[:app_version] || config_options[:appVersion] unless (params[:app_version].nil? && config_options[:appVersion].nil?)
  version_code = params[:android_version_code] || config_options[:appVersionCode] unless (params[:android_version_code].nil? && config_options[:appVersionCode].nil?)
  bundle_version = params[:ios_bundle_version] || config_options[:appBundleVersion] unless (params[:ios_bundle_version].nil? && config_options[:appBundleVersion].nil?)
  release_stage = params[:release_stage] || config_options[:releaseStage] || "production" unless (params[:release_stage].nil? && config_options[:releaseStage].nil?)
  builder = params[:builder] unless params[:builder].nil?
  revision = params[:revision] unless params[:revision].nil?
  repository = params[:repository] unless params[:repository].nil?
  provider = params[:provider] unless params[:provider].nil?
  auto_assign_release = params[:auto_assign_release] unless params[:auto_assign_release].nil?
   = params[:metadata] unless params[:metadata].nil?
  retries = params[:retries] unless params[:retries].nil?
  timeout = params[:timeout] unless params[:timeout].nil?
  endpoint = params[:endpoint] unless params[:endpoint].nil?


  if api_key.nil? || !api_key.is_a?(String)
    UI.user_error! missing_api_key_message(params)
  end
  if version_name.nil?
    UI.user_error! missing_app_version_message(params)
  end

  args = BugsnagCli.create_build_args(
    api_key,
    version_name,
    version_code,
    bundle_version,
    release_stage,
    builder,
    revision,
    repository,
    provider,
    auto_assign_release,
    ,
    retries,
    timeout,
    endpoint
  )
  success = BugsnagCli.create_build bugsnag_cli_path, args
  if success
    UI.success("Build successfully sent to Bugsnag")
  else
    UI.user_error!("Failed to send build to Bugsnag.")
  end
end