Skip to content

Commit ecab691

Browse files
authored
Merge pull request #2 from qawolf/gen-190-update-fastlane-plugin-to-optionally-call-notify-deploy
add notify upload action
2 parents 9ecf170 + 1f3ec42 commit ecab691

File tree

8 files changed

+328
-29
lines changed

8 files changed

+328
-29
lines changed

README.md

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To
66

77
```
88
# Add this to your Gemfile
9-
gem "fastlane-plugin-qawolf", git: "https://github.com/qawolf/fastlane-plugin-qawolf", branch: "main"
9+
gem "fastlane-plugin-qawolf", git: "https://github.com/qawolf/fastlane-plugin-qawolf", tag: "0.2.0"
1010
```
1111

1212
## About qawolf
1313

1414
Fastlane plugin for QA Wolf integration.
1515

16-
Uploads build artifacts (IPA, APK, or AAB) to QA Wolf storage for automated testing.
16+
Uploads build artifacts (IPA, APK, or AAB) to QA Wolf storage for automated testing. Optionally triggers a test run on QA Wolf.
1717

1818
> [!IMPORTANT]
1919
> Testing iOS apps (IPA) on QA Wolf is not yet available.
@@ -24,17 +24,39 @@ Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plu
2424

2525
```ruby
2626
lane :build do
27-
# option 1: rely on env vars and output from other lanes
28-
# QAWOLF_API_KEY must be set in the environment
29-
# expects `gradle` to have set the APK (or AAB) output path
27+
# Step 1: Build your app
28+
# Ensure the APK/AAB file has been created. Your use case may vary.
3029
gradle
31-
qawolf # alias for upload_to_qawolf
3230

33-
# option 2: pass in the values directly
31+
# Step 2: Upload the artifact to QA Wolf
3432
upload_to_qawolf(
33+
# Must be set or available as env var QAWOLF_API_KEY
3534
qawolf_api_key: "qawolf_...",
36-
file_path: "./build/app-bundle.apk",
37-
filename: "app.apk"
35+
36+
# only set this if you have not built the artifact in the same lane
37+
# e.g. via gradle or similar, check official Fastlane docs for details
38+
file_path: "./build/app-bundle.apk"
39+
)
40+
41+
# Step 3: Trigger a test run on QA Wolf
42+
# optional, only use when deployment triggers are enabled in QA Wolf
43+
notify_deploy_qawolf(
44+
# Must be set or available as env var QAWOLF_API_KEY
45+
qawolf_api_key: "qawolf_...",
46+
47+
# These fields are dependent on how triggers are setup within QA Wolf.
48+
# Reach out to support for help. All fields are optional.
49+
branch: nil,
50+
commit_url: nil,
51+
deduplication_key: nil,
52+
deployment_type: nil,
53+
deployment_url: nil,
54+
hosting_service: nil,
55+
sha: nil,
56+
variables: nil,
57+
58+
# Only set this if your lane does not include `upload_to_qawolf`
59+
run_input_path: nil,
3860
)
3961
end
4062
```

fastlane/Fastfile

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
lane :test do
2-
qawolf(
3-
file_path: "./fastlane/fastlane-test-app-debug.apk",
4-
filename: "app.apk"
2+
upload_to_qawolf(
3+
file_path: "./fastlane/fastlane-test-app-debug.apk"
4+
)
5+
notify_deploy_qawolf(
6+
deployment_type: "android",
7+
variables: {
8+
HELLO: "WORLD"
9+
}
510
)
6-
# upload_to_qawolf(
7-
# qawolf_api_key: ENV["QAWOLF_API_KEY"],
8-
# file_path: "./fastlane/fastlane-test-app-debug.apk"
9-
# )
1011
end
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
require 'fastlane/action'
2+
require 'fastlane_core'
3+
require_relative '../helper/qawolf_helper'
4+
5+
module Fastlane
6+
module Actions
7+
module SharedValues
8+
QAWOLF_RUN_ID = :QAWOLF_RUN_ID
9+
end
10+
11+
# Casing is important for the action name!
12+
class NotifyDeployQawolfAction < Action
13+
def self.run(params)
14+
qawolf_api_key = params[:qawolf_api_key] # Required
15+
qawolf_base_url = params[:qawolf_base_url]
16+
17+
UI.message("🐺 Calling QA Wolf deploy success webhook...")
18+
19+
variables = params[:variables] || {}
20+
21+
options = {
22+
branch: params[:branch],
23+
commit_url: params[:commit_url],
24+
deployment_type: params[:deployment_type],
25+
deployment_url: params[:deployment_url],
26+
deduplication_key: params[:deduplication_key],
27+
hosting_service: params[:hosting_service],
28+
sha: params[:sha],
29+
variables: variables.merge({
30+
RUN_INPUT_PATH: run_input_path(params)
31+
})
32+
}
33+
34+
run_id = Helper::QawolfHelper.notify_deploy(qawolf_api_key, qawolf_base_url, options)
35+
36+
ENV["QAWOLF_RUN_ID"] = run_id
37+
38+
UI.success("🐺 QA Wolf triggered run: #{run_id}")
39+
UI.success("🐺 Setting environment variable QAWOLF_RUN_ID = #{run_id}")
40+
41+
Actions.lane_context[SharedValues::QAWOLF_RUN_ID] = run_id
42+
end
43+
44+
def self.run_input_path(params)
45+
if params[:run_input_path].nil?
46+
UI.user_error!("🐺 No run input path found. Please run the `upload_to_qawolf` action first or set the `run_input_path` option.")
47+
end
48+
49+
return params[:run_input_path]
50+
end
51+
52+
def self.description
53+
"Fastlane plugin for QA Wolf integration to trigger test runs."
54+
end
55+
56+
def self.authors
57+
["QA Wolf"]
58+
end
59+
60+
def self.details
61+
"Calls the QA Wolf deployment success webhook to trigger test runs. Requires the `upload_to_qawolf` action to be run first."
62+
end
63+
64+
def self.output
65+
[
66+
['QAWOLF_RUN_ID', 'The ID of the run triggered in QA Wolf.']
67+
]
68+
end
69+
70+
def self.available_options
71+
[
72+
FastlaneCore::ConfigItem.new(key: :qawolf_api_key,
73+
env_name: "QAWOLF_API_KEY",
74+
description: "Your QA Wolf API key",
75+
optional: false,
76+
type: String),
77+
FastlaneCore::ConfigItem.new(key: :qawolf_base_url,
78+
env_name: "QAWOLF_BASE_URL",
79+
description: "Your QA Wolf base URL",
80+
optional: true,
81+
type: String),
82+
FastlaneCore::ConfigItem.new(key: :branch,
83+
description: "If using Git, set this to the branch name so it can be displayed in the QA Wolf UI and find any pull requests in the linked repo",
84+
optional: true,
85+
type: String),
86+
FastlaneCore::ConfigItem.new(key: :commit_url,
87+
description: "If you do not specify a hosting service, include this and the `sha` option to ensure the commit hash is a clickable link in QA Wolf",
88+
optional: true,
89+
type: String),
90+
FastlaneCore::ConfigItem.new(key: :deduplication_key,
91+
description: "By default, new runs will cancel ongoing runs if the `branch` and `environment` combination is matched, so setting this will instead cancel runs that have the same key",
92+
optional: true,
93+
type: String),
94+
FastlaneCore::ConfigItem.new(key: :deployment_type,
95+
description: "Arbitrary string to describe the deployment type. Configured in the QA Wolf UI when creating deployment triggers",
96+
optional: true,
97+
type: String),
98+
FastlaneCore::ConfigItem.new(key: :deployment_url,
99+
description: "When set, will be available as `process.env.URL` in tests",
100+
optional: true,
101+
type: String),
102+
FastlaneCore::ConfigItem.new(key: :hosting_service,
103+
description: "GitHub, GitLab, etc. Must be configured in QA Wolf",
104+
optional: true,
105+
type: String),
106+
FastlaneCore::ConfigItem.new(key: :sha,
107+
description: "If a Git commit triggered this, include the commit hash so that we can create commit checks if you also have a GitHub repo linked. Also displayed in the QA Wolf UI",
108+
optional: true,
109+
type: String),
110+
FastlaneCore::ConfigItem.new(key: :variables,
111+
description: "Optional key-value pairs to pass to the test run. These will be available as `process.env` in tests",
112+
optional: true,
113+
type: Object),
114+
FastlaneCore::ConfigItem.new(key: :run_input_path,
115+
env_name: "QAWOLF_RUN_INPUT_PATH",
116+
description: "The path of the run input file to run in QA Wolf. Set by the `upload_to_qawolf` action",
117+
optional: true,
118+
type: String)
119+
]
120+
end
121+
122+
def self.is_supported?(platform)
123+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
124+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
125+
[:ios, :android].include?(platform)
126+
end
127+
128+
def self.example_code
129+
[
130+
'notify_deploy_qawolf',
131+
'notify_deploy_qawolf(
132+
qawolf_api_key: ENV["QAWOLF_API_KEY"],
133+
branch: "<BRANCH_NAME>",
134+
commit_url: "<URL>",
135+
deployment_type: "<DEPLOYMENT_TYPE>",
136+
deployment_url: "<URL>",
137+
hosting_service: "GitHub|GitLab",
138+
sha: "<SHA>"
139+
)'
140+
]
141+
end
142+
end
143+
end
144+
end

lib/fastlane/plugin/qawolf/actions/upload_to_qawolf_action.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
module Fastlane
66
module Actions
77
module SharedValues
8-
QAWOLF_PLAYGROUND_FILE_LOCATION = :QAWOLF_PLAYGROUND_FILE_LOCATION
8+
QAWOLF_RUN_INPUT_PATH = :QAWOLF_RUN_INPUT_PATH
99
end
1010

1111
# Casing is important for the action name!
@@ -22,14 +22,14 @@ def self.run(params)
2222

2323
UI.message("🐺 Uploading to QA Wolf...")
2424

25-
playground_file_location = Helper::QawolfHelper.upload_file(qawolf_api_key, qawolf_base_url, file_path, filename)
25+
run_input_path = Helper::QawolfHelper.upload_file(qawolf_api_key, qawolf_base_url, file_path, filename)
2626

27-
ENV["QAWOLF_PLAYGROUND_FILE_LOCATION"] = playground_file_location
27+
ENV["QAWOLF_RUN_INPUT_PATH"] = run_input_path
2828

29-
UI.success("🐺 Uploaded #{file_path} to QA Wolf successfully. Playground file location: #{playground_file_location}")
30-
UI.success("🐺 Setting environment variable QAWOLF_PLAYGROUND_FILE_LOCATION = #{playground_file_location}")
29+
UI.success("🐺 Uploaded #{file_path} to QA Wolf successfully. Run input path: #{run_input_path}")
30+
UI.success("🐺 Setting environment variable QAWOLF_RUN_INPUT_PATH = #{run_input_path}")
3131

32-
Actions.lane_context[SharedValues::QAWOLF_PLAYGROUND_FILE_LOCATION] = playground_file_location
32+
Actions.lane_context[SharedValues::QAWOLF_RUN_INPUT_PATH] = run_input_path
3333
end
3434

3535
# Validate file_path.
@@ -45,7 +45,7 @@ def self.validate_file_path(file_path)
4545
end
4646

4747
def self.description
48-
"Fastlane plugin for QA Wolf integration."
48+
"Fastlane plugin for QA Wolf integration to upload executable artifacts."
4949
end
5050

5151
def self.authors
@@ -58,7 +58,7 @@ def self.details
5858

5959
def self.output
6060
[
61-
['QAWOLF_PLAYGROUND_FILE_LOCATION', 'Uploaded file location in playgrounds.']
61+
['QAWOLF_RUN_INPUT_PATH', 'Uploaded file location for the executable artifact.']
6262
]
6363
end
6464

lib/fastlane/plugin/qawolf/helper/qawolf_helper.rb

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module Helper
99
class QawolfHelper
1010
BASE_URL = "https://app.qawolf.com"
1111
SIGNED_URL_ENDPOINT = "/api/v0/run-inputs-executables-signed-urls"
12+
WEBHOOK_DEPLOY_SUCCESS_ENDPOINT = "/api/webhooks/deploy_success"
1213

1314
def self.get_signed_url(qawolf_api_key, qawolf_base_url, filename)
1415
headers = {
@@ -29,7 +30,7 @@ def self.get_signed_url(qawolf_api_key, qawolf_base_url, filename)
2930
]
3031
end
3132

32-
# Uploads file to BrowserStack
33+
# Uploads file to QA Wolf
3334
# Params :
3435
# +qawolf_api_key+:: QA Wolf API key
3536
# +qawolf_base_url+:: QA Wolf API base URL
@@ -43,11 +44,11 @@ def self.upload_file(qawolf_api_key, qawolf_base_url, file_path, filename = nil)
4344
content_type: "application/octet-stream"
4445
}
4546

46-
signed_url, playground_file_location = get_signed_url(qawolf_api_key, qawolf_base_url, filename || File.basename(file_path))
47+
signed_url, run_input_path = get_signed_url(qawolf_api_key, qawolf_base_url, filename || File.basename(file_path))
4748

4849
RestClient.put(signed_url, file_content, headers)
4950

50-
return playground_file_location
51+
return run_input_path
5152
rescue RestClient::ExceptionWithResponse => e
5253
begin
5354
error_response = e.response.to_s
@@ -59,6 +60,65 @@ def self.upload_file(qawolf_api_key, qawolf_base_url, file_path, filename = nil)
5960
rescue StandardError => e
6061
UI.user_error!("App upload failed!!! Reason : #{e.message}")
6162
end
63+
64+
def self.notify_deploy_body(options)
65+
{
66+
'branch' => options[:branch],
67+
'commit_url' => options[:commit_url],
68+
'deduplication_key' => options[:deduplication_key],
69+
'deployment_type' => options[:deployment_type],
70+
'deployment_url' => options[:deployment_url],
71+
'hosting_service' => options[:hosting_service],
72+
'sha' => options[:sha],
73+
'variables' => options[:variables]
74+
}.to_json
75+
end
76+
77+
def self.process_notify_response(response)
78+
response_json = JSON.parse(response.to_s)
79+
80+
results = response_json["results"]
81+
82+
failed_trigger = results.find { |result| result["failure_reason"].nil? == false }
83+
success_trigger = results.find { |result| result["created_suite_id"].nil? == false }
84+
85+
if failed_trigger.nil? && success_trigger.nil?
86+
raise "no matched trigger, reach out to QA Wolf support"
87+
elsif failed_trigger.nil? == false
88+
raise failed_trigger["failure_reason"]
89+
end
90+
91+
return success_trigger["created_suite_id"]
92+
end
93+
94+
# Triggers QA Wolf deploy success webhook to start test runs.
95+
# Params :
96+
# +qawolf_api_key+:: QA Wolf API key
97+
# +qawolf_base_url+:: QA Wolf API base URL
98+
# +options+:: Options hash containing deployment details.
99+
def self.notify_deploy(qawolf_api_key, qawolf_base_url, options)
100+
headers = {
101+
authorization: "Bearer #{qawolf_api_key}",
102+
user_agent: "qawolf_fastlane_plugin",
103+
content_type: "application/json"
104+
}
105+
106+
url = URI.join(qawolf_base_url || BASE_URL, WEBHOOK_DEPLOY_SUCCESS_ENDPOINT)
107+
108+
response = RestClient.post(url.to_s, notify_deploy_body(options), headers)
109+
110+
return process_notify_response(response)
111+
rescue RestClient::ExceptionWithResponse => e
112+
begin
113+
error_response = e.response.to_s
114+
rescue StandardError
115+
error_response = "Internal server error"
116+
end
117+
# Give error if request failed.
118+
UI.user_error!("Failed to notify deploy!!! Request failed. Reason : #{error_response}")
119+
rescue StandardError => e
120+
UI.user_error!("Failed to notify deploy!!! Something went wrong. Reason : #{e.message}")
121+
end
62122
end
63123
end
64124
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Fastlane
22
module Qawolf
3-
VERSION = "0.1.0"
3+
VERSION = "0.2.0"
44
end
55
end

0 commit comments

Comments
 (0)