Class: Fastlane::Actions::UploadToTestingbotAction

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

Constant Summary collapse

UPLOAD_API_PATH =
'/v1/storage'.freeze
SUPPORTED_FILE_EXTENSIONS =
%w[apk aab ipa zip].freeze

Documentation collapse

Class Method Summary collapse

Class Method Details

.authorsObject



116
117
118
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 116

def self.authors
  ['TestingBot']
end

.available_optionsObject



124
125
126
127
128
129
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
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 124

def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :testingbot_key,
                                 env_name: 'TESTINGBOT_KEY',
                                 description: 'Your TestingBot API key (find it at https://testingbot.com/members/user/edit)',
                                 sensitive: true,
                                 optional: false,
                                 type: String,
                                 verify_block: proc do |value|
                                   UI.user_error!('No TestingBot API key given, pass using `testingbot_key:` or the TESTINGBOT_KEY environment variable') if value.to_s.empty?
                                 end),
    FastlaneCore::ConfigItem.new(key: :testingbot_secret,
                                 env_name: 'TESTINGBOT_SECRET',
                                 description: 'Your TestingBot API secret (find it at https://testingbot.com/members/user/edit)',
                                 sensitive: true,
                                 optional: false,
                                 type: String,
                                 verify_block: proc do |value|
                                   UI.user_error!('No TestingBot API secret given, pass using `testingbot_secret:` or the TESTINGBOT_SECRET environment variable') if value.to_s.empty?
                                 end),
    FastlaneCore::ConfigItem.new(key: :file_path,
                                 env_name: 'TESTINGBOT_FILE_PATH',
                                 description: 'Path to the local app file (.apk, .aab, .ipa or .zip) to upload. Defaults to the output of a preceding gym/gradle build',
                                 optional: true,
                                 type: String),
    FastlaneCore::ConfigItem.new(key: :remote_url,
                                 env_name: 'TESTINGBOT_REMOTE_URL',
                                 description: 'A publicly accessible URL to the app file; TestingBot downloads it server-side (mutually exclusive with file_path)',
                                 optional: true,
                                 type: String),
    FastlaneCore::ConfigItem.new(key: :app_url,
                                 env_name: 'TESTINGBOT_REPLACE_APP_URL',
                                 description: 'An existing TestingBot app_url (tb://...) to replace in place, keeping the same identifier',
                                 optional: true,
                                 type: String)
  ]
end

.categoryObject



170
171
172
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 170

def self.category
  :testing
end

.default_file_pathObject

Resolve a sensible default app path from earlier build actions in the same lane.



77
78
79
80
81
82
83
84
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 77

def self.default_file_path
  if Actions.lane_context[SharedValues::PLATFORM_NAME] == :ios
    Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
  else
    Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] ||
      Actions.lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
  end
end

.descriptionObject



104
105
106
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 104

def self.description
  'Uploads an app (.apk/.aab/.ipa/.zip) to TestingBot Storage'
end

.detailsObject



108
109
110
111
112
113
114
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 108

def self.details
  [
    'Uploads a local app file or a remote app URL to TestingBot Storage so it can be used in App Automate / App Live test runs.',
    'On success the returned `tb://<appkey>` identifier is stored in the lane context (`SharedValues::TESTINGBOT_APP_URL`) and in `ENV`, ready to be used as the Appium `appium:app` capability.',
    'Pass an existing `app_url` to replace the binary in place while keeping the same `tb://` identifier (handy for stable CI references).'
  ].join("\n")
end

.example_codeObject



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 174

def self.example_code
  [
    'upload_to_testingbot(
      testingbot_key: ENV["TESTINGBOT_KEY"],
      testingbot_secret: ENV["TESTINGBOT_SECRET"],
      file_path: "./app/build/outputs/apk/debug/app-debug.apk"
    )',
    'upload_to_testingbot(
      remote_url: "https://example.com/MyApp.ipa"
    )',
    '# Replace an existing build in place (keeps the same tb:// identifier)
    upload_to_testingbot(
      file_path: "./MyApp.ipa",
      app_url: "tb://abc123"
    )'
  ]
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


192
193
194
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 192

def self.is_supported?(platform)
  %i[ios android mac].include?(platform)
end

.outputObject



162
163
164
165
166
167
168
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 162

def self.output
  [
    ['TESTINGBOT_APP_URL', 'The tb:// app_url of the uploaded app'],
    ['TESTINGBOT_APP_KEY', 'The app key (app_url without the tb:// scheme)'],
    ['TESTINGBOT_STORAGE_RESPONSE', 'The full parsed JSON response from TestingBot Storage']
  ]
end

.presence(value) ⇒ Object



96
97
98
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 96

def self.presence(value)
  value.to_s.empty? ? nil : value
end

.publish_result(response) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 50

def self.publish_result(response)
  app_url = response['app_url']
  UI.user_error!("TestingBot did not return an app_url. Response: #{response}") if presence(app_url).nil?

  app_key = strip_scheme(app_url)
  Actions.lane_context[SharedValues::TESTINGBOT_APP_URL] = app_url
  Actions.lane_context[SharedValues::TESTINGBOT_APP_KEY] = app_key
  Actions.lane_context[SharedValues::TESTINGBOT_STORAGE_RESPONSE] = response
  ENV['TESTINGBOT_APP_URL'] = app_url
  ENV['TESTINGBOT_APP_KEY'] = app_key

  UI.success("Successfully uploaded app to TestingBot: #{app_url}")
  app_url
end

.resolve_source(file_path, remote_url) ⇒ Object

Decide what to upload, enforcing that exactly one source is used. An explicit remote_url takes precedence over the auto-resolved default file path.



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 37

def self.resolve_source(file_path, remote_url)
  file_path = presence(file_path)
  remote_url = presence(remote_url)

  UI.user_error!('Provide either `file_path` or `remote_url`, not both.') if file_path && remote_url

  file_path = presence(default_file_path) if file_path.nil? && remote_url.nil?

  UI.user_error!('No app to upload. Provide `file_path` (a local .apk/.aab/.ipa/.zip) or `remote_url`.') if file_path.nil? && remote_url.nil?

  [file_path, remote_url]
end

.return_valueObject



120
121
122
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 120

def self.return_value
  'The TestingBot app_url of the uploaded app (e.g. "tb://abc123"). Also exposed via lane_context[SharedValues::TESTINGBOT_APP_URL] and ENV["TESTINGBOT_APP_URL"].'
end

.run(params) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 16

def self.run(params)
  file_path, remote_url = resolve_source(params[:file_path], params[:remote_url])
  validate_file_path!(file_path) if file_path

  path = upload_path(params[:app_url])

  UI.message("Uploading to TestingBot Storage #{file_path ? "(#{File.basename(file_path)})" : "from #{remote_url}"} ...")

  response = Helper::TestingbotHelper.upload(
    params[:testingbot_key],
    params[:testingbot_secret],
    path,
    file_path: file_path,
    remote_url: remote_url
  )

  publish_result(response)
end

.strip_scheme(app_url) ⇒ Object

Strip only the leading "tb://" scheme, preserving any remaining path.



72
73
74
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 72

def self.strip_scheme(app_url)
  app_url.to_s.sub(%r{\Atb://}, '')
end

.upload_path(app_url) ⇒ Object



65
66
67
68
69
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 65

def self.upload_path(app_url)
  return UPLOAD_API_PATH if presence(app_url).nil?

  "#{UPLOAD_API_PATH}/#{strip_scheme(app_url)}"
end

.validate_file_path!(file_path) ⇒ Object



86
87
88
89
90
91
92
93
94
# File 'lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb', line 86

def self.validate_file_path!(file_path)
  UI.user_error!("Couldn't find file at path '#{file_path}'") unless File.exist?(file_path)

  extension = File.extname(file_path).delete('.').downcase
  return if SUPPORTED_FILE_EXTENSIONS.include?(extension)

  supported = SUPPORTED_FILE_EXTENSIONS.map { |ext| ".#{ext}" }.join(', ')
  UI.user_error!("Unsupported file type '.#{extension}'. TestingBot supports: #{supported}")
end