fastlane-plugin-testingbot 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +161 -0
- data/lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb +197 -0
- data/lib/fastlane/plugin/testingbot/helper/testingbot_helper.rb +68 -0
- data/lib/fastlane/plugin/testingbot/version.rb +5 -0
- data/lib/fastlane/plugin/testingbot.rb +16 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 69119bf112788b514fc5b4a0b2eb3c24505f1f5b30cd2017355843ad8a844b47
|
|
4
|
+
data.tar.gz: 581d283e00ca2e607bb31d816f57fce730ab15dde7c406c4bbff998c31f2af23
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 89d617bbd6961baf8f8e82aa72502e7bd5ff2797c0ced4c53e196d4dd15f6bd14a31614f2b6bfc20195bc03f5e8c81a897b6c0e54e0025cfa48fc993f505476e
|
|
7
|
+
data.tar.gz: 19c19c0dc321c48939e2ba6342c4506e64b352127abd8c948df5a11190bf7e33594be57cfdc0f50884e69eff2362c70ac6dbb3056e10e13691532edf7a358f49
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TestingBot
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# fastlane-plugin-testingbot
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/fastlane-plugin-testingbot)
|
|
4
|
+
|
|
5
|
+
A [_fastlane_](https://fastlane.tools) plugin that uploads your mobile app builds to [TestingBot Storage](https://testingbot.com/support/api#upload),
|
|
6
|
+
so they can be used for automated (App Automate) and manual (App Live) testing on TestingBot's real devices and emulators/simulators.
|
|
7
|
+
|
|
8
|
+
## Getting Started
|
|
9
|
+
|
|
10
|
+
Add the plugin to your project:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
fastlane add_plugin testingbot
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This adds the plugin to your `fastlane/Pluginfile` and installs it. Commit the updated `Gemfile`, `Gemfile.lock`, and `fastlane/Pluginfile`.
|
|
17
|
+
|
|
18
|
+
## Credentials
|
|
19
|
+
|
|
20
|
+
The plugin authenticates with your **TestingBot API Key** and **API Secret**, which you can find in your
|
|
21
|
+
[TestingBot account](https://testingbot.com/members/user/edit). The recommended way to provide them is via environment variables,
|
|
22
|
+
which the plugin picks up automatically:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export TESTINGBOT_KEY="your-api-key"
|
|
26
|
+
export TESTINGBOT_SECRET="your-api-secret"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Upload a local app file
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
upload_to_testingbot(
|
|
35
|
+
file_path: "./app/build/outputs/apk/debug/app-debug.apk"
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`file_path` is optional — if omitted, the plugin uses the output of a preceding `gym` (iOS `IPA_OUTPUT_PATH`) or
|
|
40
|
+
`gradle` (`GRADLE_APK_OUTPUT_PATH` / `GRADLE_AAB_OUTPUT_PATH`) action in the same lane:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
gradle(task: "assembleDebug")
|
|
44
|
+
upload_to_testingbot # automatically uploads the freshly built APK
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Supported formats: `.apk` and `.aab` (Android), `.ipa` (iOS real devices), and `.zip` (iOS simulator builds).
|
|
48
|
+
|
|
49
|
+
### Upload from a remote URL
|
|
50
|
+
|
|
51
|
+
Have TestingBot fetch the binary server-side instead of uploading bytes from the CI machine:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
upload_to_testingbot(
|
|
55
|
+
remote_url: "https://example.com/MyApp.ipa"
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Replace a build in place (stable identifier)
|
|
60
|
+
|
|
61
|
+
Pass an existing `app_url` to overwrite the binary while keeping the same `tb://` identifier — useful when your test
|
|
62
|
+
configuration references a fixed id across CI runs:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
upload_to_testingbot(
|
|
66
|
+
file_path: "./MyApp.ipa",
|
|
67
|
+
app_url: "tb://abc123"
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Output
|
|
72
|
+
|
|
73
|
+
On success the action returns the `tb://<appkey>` app URL and exposes it for downstream lanes/processes:
|
|
74
|
+
|
|
75
|
+
| Source | Value |
|
|
76
|
+
|---|---|
|
|
77
|
+
| return value | `tb://<appkey>` |
|
|
78
|
+
| `lane_context[SharedValues::TESTINGBOT_APP_URL]` | `tb://<appkey>` |
|
|
79
|
+
| `lane_context[SharedValues::TESTINGBOT_APP_KEY]` | `<appkey>` (scheme stripped) |
|
|
80
|
+
| `lane_context[SharedValues::TESTINGBOT_STORAGE_RESPONSE]` | full parsed JSON response |
|
|
81
|
+
| `ENV["TESTINGBOT_APP_URL"]` / `ENV["TESTINGBOT_APP_KEY"]` | same values, for external test processes |
|
|
82
|
+
|
|
83
|
+
Use the `tb://` app URL as the Appium `appium:app` (legacy `app`) capability when starting your test session, e.g.:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
app = lane_context[SharedValues::TESTINGBOT_APP_URL]
|
|
87
|
+
# capabilities["appium:app"] = app
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
> **Note:** Uploaded apps are automatically deleted by TestingBot after 62 days. For a long-lived, stable reference,
|
|
91
|
+
> re-upload using the `app_url` (replace) option so the `tb://` identifier stays the same.
|
|
92
|
+
|
|
93
|
+
## Action options
|
|
94
|
+
|
|
95
|
+
| Option | Environment variable | Required | Description |
|
|
96
|
+
|---|---|---|---|
|
|
97
|
+
| `testingbot_key` | `TESTINGBOT_KEY` | yes | Your TestingBot API key |
|
|
98
|
+
| `testingbot_secret` | `TESTINGBOT_SECRET` | yes | Your TestingBot API secret |
|
|
99
|
+
| `file_path` | `TESTINGBOT_FILE_PATH` | no | Local app file to upload (auto-resolved from gym/gradle if omitted) |
|
|
100
|
+
| `remote_url` | `TESTINGBOT_REMOTE_URL` | no | Remote URL for TestingBot to fetch (mutually exclusive with `file_path`) |
|
|
101
|
+
| `app_url` | `TESTINGBOT_REPLACE_APP_URL` | no | Existing `tb://...` app to replace in place |
|
|
102
|
+
|
|
103
|
+
Run `fastlane action upload_to_testingbot` to see this list from the command line.
|
|
104
|
+
|
|
105
|
+
## Run tests for this plugin
|
|
106
|
+
|
|
107
|
+
To run both the tests, and code style validation, run
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bundle install
|
|
111
|
+
bundle exec rake
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Releasing
|
|
115
|
+
|
|
116
|
+
Releases are published to [RubyGems.org](https://rubygems.org/gems/fastlane-plugin-testingbot) automatically by the
|
|
117
|
+
[`Publish gem`](.github/workflows/publish.yml) GitHub Action whenever a `v*` tag is pushed. It uses
|
|
118
|
+
[RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) (OIDC), so **no API key secret is stored**
|
|
119
|
+
— which is required because this gem sets `rubygems_mfa_required`.
|
|
120
|
+
|
|
121
|
+
**One-time setup on RubyGems.org** (per gem):
|
|
122
|
+
|
|
123
|
+
1. Sign in to RubyGems.org and open **Trusted Publishers** (under your profile, or the gem's settings once it exists).
|
|
124
|
+
2. Add a new GitHub Actions trusted publisher with:
|
|
125
|
+
- **Repository owner / name:** `testingbot/testingbot-fastlane-plugin`
|
|
126
|
+
- **Workflow filename:** `publish.yml`
|
|
127
|
+
- **Environment:** `rubygems.org`
|
|
128
|
+
For a brand-new gem that isn't on RubyGems yet, create it as a *pending* trusted publisher.
|
|
129
|
+
|
|
130
|
+
**To cut a release:**
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# bump VERSION in lib/fastlane/plugin/testingbot/version.rb first
|
|
134
|
+
git commit -am "Release v0.1.0"
|
|
135
|
+
git tag v0.1.0
|
|
136
|
+
git push origin main --tags
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The workflow runs the test suite + RuboCop, then builds and pushes the gem.
|
|
140
|
+
|
|
141
|
+
## Contributing
|
|
142
|
+
|
|
143
|
+
Bug reports and pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) to get started, and note
|
|
144
|
+
that this project follows a [Code of Conduct](CODE_OF_CONDUCT.md). For security issues, follow the private process in
|
|
145
|
+
[SECURITY.md](SECURITY.md).
|
|
146
|
+
|
|
147
|
+
## Issues and Feedback
|
|
148
|
+
|
|
149
|
+
For any other issues and feedback about this plugin, please submit it to this repository.
|
|
150
|
+
|
|
151
|
+
## Using _fastlane_ Plugins
|
|
152
|
+
|
|
153
|
+
For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://docs.fastlane.tools/plugins/create-plugin/).
|
|
154
|
+
|
|
155
|
+
## About _fastlane_
|
|
156
|
+
|
|
157
|
+
_fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
require 'fastlane/action'
|
|
2
|
+
require_relative '../helper/testingbot_helper'
|
|
3
|
+
|
|
4
|
+
module Fastlane
|
|
5
|
+
module Actions
|
|
6
|
+
module SharedValues
|
|
7
|
+
TESTINGBOT_APP_URL ||= :TESTINGBOT_APP_URL
|
|
8
|
+
TESTINGBOT_APP_KEY ||= :TESTINGBOT_APP_KEY
|
|
9
|
+
TESTINGBOT_STORAGE_RESPONSE ||= :TESTINGBOT_STORAGE_RESPONSE
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class UploadToTestingbotAction < Action
|
|
13
|
+
UPLOAD_API_PATH = '/v1/storage'.freeze
|
|
14
|
+
SUPPORTED_FILE_EXTENSIONS = %w[apk aab ipa zip].freeze
|
|
15
|
+
|
|
16
|
+
def self.run(params)
|
|
17
|
+
file_path, remote_url = resolve_source(params[:file_path], params[:remote_url])
|
|
18
|
+
validate_file_path!(file_path) if file_path
|
|
19
|
+
|
|
20
|
+
path = upload_path(params[:app_url])
|
|
21
|
+
|
|
22
|
+
UI.message("Uploading to TestingBot Storage #{file_path ? "(#{File.basename(file_path)})" : "from #{remote_url}"} ...")
|
|
23
|
+
|
|
24
|
+
response = Helper::TestingbotHelper.upload(
|
|
25
|
+
params[:testingbot_key],
|
|
26
|
+
params[:testingbot_secret],
|
|
27
|
+
path,
|
|
28
|
+
file_path: file_path,
|
|
29
|
+
remote_url: remote_url
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
publish_result(response)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Decide what to upload, enforcing that exactly one source is used.
|
|
36
|
+
# An explicit remote_url takes precedence over the auto-resolved default file path.
|
|
37
|
+
def self.resolve_source(file_path, remote_url)
|
|
38
|
+
file_path = presence(file_path)
|
|
39
|
+
remote_url = presence(remote_url)
|
|
40
|
+
|
|
41
|
+
UI.user_error!('Provide either `file_path` or `remote_url`, not both.') if file_path && remote_url
|
|
42
|
+
|
|
43
|
+
file_path = presence(default_file_path) if file_path.nil? && remote_url.nil?
|
|
44
|
+
|
|
45
|
+
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?
|
|
46
|
+
|
|
47
|
+
[file_path, remote_url]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.publish_result(response)
|
|
51
|
+
app_url = response['app_url']
|
|
52
|
+
UI.user_error!("TestingBot did not return an app_url. Response: #{response}") if presence(app_url).nil?
|
|
53
|
+
|
|
54
|
+
app_key = strip_scheme(app_url)
|
|
55
|
+
Actions.lane_context[SharedValues::TESTINGBOT_APP_URL] = app_url
|
|
56
|
+
Actions.lane_context[SharedValues::TESTINGBOT_APP_KEY] = app_key
|
|
57
|
+
Actions.lane_context[SharedValues::TESTINGBOT_STORAGE_RESPONSE] = response
|
|
58
|
+
ENV['TESTINGBOT_APP_URL'] = app_url
|
|
59
|
+
ENV['TESTINGBOT_APP_KEY'] = app_key
|
|
60
|
+
|
|
61
|
+
UI.success("Successfully uploaded app to TestingBot: #{app_url}")
|
|
62
|
+
app_url
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.upload_path(app_url)
|
|
66
|
+
return UPLOAD_API_PATH if presence(app_url).nil?
|
|
67
|
+
|
|
68
|
+
"#{UPLOAD_API_PATH}/#{strip_scheme(app_url)}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Strip only the leading "tb://" scheme, preserving any remaining path.
|
|
72
|
+
def self.strip_scheme(app_url)
|
|
73
|
+
app_url.to_s.sub(%r{\Atb://}, '')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Resolve a sensible default app path from earlier build actions in the same lane.
|
|
77
|
+
def self.default_file_path
|
|
78
|
+
if Actions.lane_context[SharedValues::PLATFORM_NAME] == :ios
|
|
79
|
+
Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
|
|
80
|
+
else
|
|
81
|
+
Actions.lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] ||
|
|
82
|
+
Actions.lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.validate_file_path!(file_path)
|
|
87
|
+
UI.user_error!("Couldn't find file at path '#{file_path}'") unless File.exist?(file_path)
|
|
88
|
+
|
|
89
|
+
extension = File.extname(file_path).delete('.').downcase
|
|
90
|
+
return if SUPPORTED_FILE_EXTENSIONS.include?(extension)
|
|
91
|
+
|
|
92
|
+
supported = SUPPORTED_FILE_EXTENSIONS.map { |ext| ".#{ext}" }.join(', ')
|
|
93
|
+
UI.user_error!("Unsupported file type '.#{extension}'. TestingBot supports: #{supported}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.presence(value)
|
|
97
|
+
value.to_s.empty? ? nil : value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
#####################################################
|
|
101
|
+
# @!group Documentation
|
|
102
|
+
#####################################################
|
|
103
|
+
|
|
104
|
+
def self.description
|
|
105
|
+
'Uploads an app (.apk/.aab/.ipa/.zip) to TestingBot Storage'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.details
|
|
109
|
+
[
|
|
110
|
+
'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.',
|
|
111
|
+
'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.',
|
|
112
|
+
'Pass an existing `app_url` to replace the binary in place while keeping the same `tb://` identifier (handy for stable CI references).'
|
|
113
|
+
].join("\n")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.authors
|
|
117
|
+
['TestingBot']
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def self.return_value
|
|
121
|
+
'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"].'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def self.available_options
|
|
125
|
+
[
|
|
126
|
+
FastlaneCore::ConfigItem.new(key: :testingbot_key,
|
|
127
|
+
env_name: 'TESTINGBOT_KEY',
|
|
128
|
+
description: 'Your TestingBot API key (find it at https://testingbot.com/members/user/edit)',
|
|
129
|
+
sensitive: true,
|
|
130
|
+
optional: false,
|
|
131
|
+
type: String,
|
|
132
|
+
verify_block: proc do |value|
|
|
133
|
+
UI.user_error!('No TestingBot API key given, pass using `testingbot_key:` or the TESTINGBOT_KEY environment variable') if value.to_s.empty?
|
|
134
|
+
end),
|
|
135
|
+
FastlaneCore::ConfigItem.new(key: :testingbot_secret,
|
|
136
|
+
env_name: 'TESTINGBOT_SECRET',
|
|
137
|
+
description: 'Your TestingBot API secret (find it at https://testingbot.com/members/user/edit)',
|
|
138
|
+
sensitive: true,
|
|
139
|
+
optional: false,
|
|
140
|
+
type: String,
|
|
141
|
+
verify_block: proc do |value|
|
|
142
|
+
UI.user_error!('No TestingBot API secret given, pass using `testingbot_secret:` or the TESTINGBOT_SECRET environment variable') if value.to_s.empty?
|
|
143
|
+
end),
|
|
144
|
+
FastlaneCore::ConfigItem.new(key: :file_path,
|
|
145
|
+
env_name: 'TESTINGBOT_FILE_PATH',
|
|
146
|
+
description: 'Path to the local app file (.apk, .aab, .ipa or .zip) to upload. Defaults to the output of a preceding gym/gradle build',
|
|
147
|
+
optional: true,
|
|
148
|
+
type: String),
|
|
149
|
+
FastlaneCore::ConfigItem.new(key: :remote_url,
|
|
150
|
+
env_name: 'TESTINGBOT_REMOTE_URL',
|
|
151
|
+
description: 'A publicly accessible URL to the app file; TestingBot downloads it server-side (mutually exclusive with file_path)',
|
|
152
|
+
optional: true,
|
|
153
|
+
type: String),
|
|
154
|
+
FastlaneCore::ConfigItem.new(key: :app_url,
|
|
155
|
+
env_name: 'TESTINGBOT_REPLACE_APP_URL',
|
|
156
|
+
description: 'An existing TestingBot app_url (tb://...) to replace in place, keeping the same identifier',
|
|
157
|
+
optional: true,
|
|
158
|
+
type: String)
|
|
159
|
+
]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def self.output
|
|
163
|
+
[
|
|
164
|
+
['TESTINGBOT_APP_URL', 'The tb:// app_url of the uploaded app'],
|
|
165
|
+
['TESTINGBOT_APP_KEY', 'The app key (app_url without the tb:// scheme)'],
|
|
166
|
+
['TESTINGBOT_STORAGE_RESPONSE', 'The full parsed JSON response from TestingBot Storage']
|
|
167
|
+
]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def self.category
|
|
171
|
+
:testing
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def self.example_code
|
|
175
|
+
[
|
|
176
|
+
'upload_to_testingbot(
|
|
177
|
+
testingbot_key: ENV["TESTINGBOT_KEY"],
|
|
178
|
+
testingbot_secret: ENV["TESTINGBOT_SECRET"],
|
|
179
|
+
file_path: "./app/build/outputs/apk/debug/app-debug.apk"
|
|
180
|
+
)',
|
|
181
|
+
'upload_to_testingbot(
|
|
182
|
+
remote_url: "https://example.com/MyApp.ipa"
|
|
183
|
+
)',
|
|
184
|
+
'# Replace an existing build in place (keeps the same tb:// identifier)
|
|
185
|
+
upload_to_testingbot(
|
|
186
|
+
file_path: "./MyApp.ipa",
|
|
187
|
+
app_url: "tb://abc123"
|
|
188
|
+
)'
|
|
189
|
+
]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def self.is_supported?(platform)
|
|
193
|
+
%i[ios android mac].include?(platform)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
|
2
|
+
require 'rest-client'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Fastlane
|
|
6
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
|
7
|
+
|
|
8
|
+
module Helper
|
|
9
|
+
class TestingbotHelper
|
|
10
|
+
API_BASE_URL = 'https://api.testingbot.com'.freeze
|
|
11
|
+
|
|
12
|
+
# App binaries can be large; mirror the official TestingBot Ruby client's generous read timeout.
|
|
13
|
+
UPLOAD_TIMEOUT = 600
|
|
14
|
+
OPEN_TIMEOUT = 30
|
|
15
|
+
|
|
16
|
+
# Uploads (or replaces) an app on TestingBot Storage.
|
|
17
|
+
#
|
|
18
|
+
# @param key [String] TestingBot API key (used as the HTTP Basic auth username)
|
|
19
|
+
# @param secret [String] TestingBot API secret (used as the HTTP Basic auth password)
|
|
20
|
+
# @param path [String] the request path, e.g. "/v1/storage" or "/v1/storage/<appkey>"
|
|
21
|
+
# @param file_path [String, nil] a local app file to upload via multipart/form-data
|
|
22
|
+
# @param remote_url [String, nil] a publicly reachable URL for TestingBot to fetch server-side
|
|
23
|
+
# @return [Hash] the parsed JSON response, e.g. {"app_url" => "tb://<appkey>"}
|
|
24
|
+
def self.upload(key, secret, path, file_path: nil, remote_url: nil)
|
|
25
|
+
file = file_path ? File.new(file_path, 'rb') : nil
|
|
26
|
+
payload = file ? { multipart: true, file: file } : { url: remote_url }
|
|
27
|
+
|
|
28
|
+
response = RestClient::Request.execute(
|
|
29
|
+
method: :post,
|
|
30
|
+
url: "#{API_BASE_URL}#{path}",
|
|
31
|
+
user: key,
|
|
32
|
+
password: secret,
|
|
33
|
+
payload: payload,
|
|
34
|
+
headers: { 'User-Agent' => "fastlane-plugin-testingbot/#{Fastlane::Testingbot::VERSION}" },
|
|
35
|
+
timeout: UPLOAD_TIMEOUT,
|
|
36
|
+
open_timeout: OPEN_TIMEOUT
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
parse_success(response)
|
|
40
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
41
|
+
UI.user_error!("App upload to TestingBot failed: #{error_reason(e.response)}")
|
|
42
|
+
rescue StandardError => e
|
|
43
|
+
UI.user_error!("App upload to TestingBot failed: #{e.message}")
|
|
44
|
+
ensure
|
|
45
|
+
file&.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.parse_success(response)
|
|
49
|
+
body = parse_json(response.body)
|
|
50
|
+
UI.user_error!('App upload to TestingBot failed: could not parse the server response') unless body.is_a?(Hash)
|
|
51
|
+
|
|
52
|
+
body
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.error_reason(response)
|
|
56
|
+
body = response ? parse_json(response.body) : nil
|
|
57
|
+
message = body.is_a?(Hash) && (body['error'] || body['message'])
|
|
58
|
+
message || (response && "HTTP #{response.code}") || 'unknown error'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.parse_json(raw)
|
|
62
|
+
JSON.parse(raw.to_s)
|
|
63
|
+
rescue JSON::ParserError
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'fastlane/plugin/testingbot/version'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Testingbot
|
|
5
|
+
# Return all .rb files inside the "actions" and "helper" directory
|
|
6
|
+
def self.all_classes
|
|
7
|
+
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# By default we want to import all available actions and helpers
|
|
13
|
+
# A plugin can contain any number of actions and plugins
|
|
14
|
+
Fastlane::Testingbot.all_classes.each do |current|
|
|
15
|
+
require current
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fastlane-plugin-testingbot
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- TestingBot
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-06-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rest-client
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 2.0.2
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '2.0'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.0.2
|
|
33
|
+
description:
|
|
34
|
+
email: info@testingbot.com
|
|
35
|
+
executables: []
|
|
36
|
+
extensions: []
|
|
37
|
+
extra_rdoc_files: []
|
|
38
|
+
files:
|
|
39
|
+
- LICENSE
|
|
40
|
+
- README.md
|
|
41
|
+
- lib/fastlane/plugin/testingbot.rb
|
|
42
|
+
- lib/fastlane/plugin/testingbot/actions/upload_to_testingbot_action.rb
|
|
43
|
+
- lib/fastlane/plugin/testingbot/helper/testingbot_helper.rb
|
|
44
|
+
- lib/fastlane/plugin/testingbot/version.rb
|
|
45
|
+
homepage: https://github.com/testingbot/testingbot-fastlane-plugin
|
|
46
|
+
licenses:
|
|
47
|
+
- MIT
|
|
48
|
+
metadata:
|
|
49
|
+
rubygems_mfa_required: 'true'
|
|
50
|
+
source_code_uri: https://github.com/testingbot/testingbot-fastlane-plugin
|
|
51
|
+
bug_tracker_uri: https://github.com/testingbot/testingbot-fastlane-plugin/issues
|
|
52
|
+
changelog_uri: https://github.com/testingbot/testingbot-fastlane-plugin/blob/main/CHANGELOG.md
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.0'
|
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0'
|
|
67
|
+
requirements: []
|
|
68
|
+
rubygems_version: 3.5.3
|
|
69
|
+
signing_key:
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: Uploads .apk/.aab/.ipa/.zip files to TestingBot Storage for mobile app testing
|
|
72
|
+
test_files: []
|