fastlane-plugin-applivery 2.4.0 → 2.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 896d1a5cf3e8bd2ca4026553c73b1260341495dc78ad9768e9e51f36f88a8ebe
4
- data.tar.gz: 88b8923a87645ae48eaf99c6b9d542a5b4685aa66cb0b59cd9cab08b913a6ad5
3
+ metadata.gz: 2a52ad4e11f20fe0e95a88314e0ff3ffe9d76df86d5b170e8b5c8252ab3e1d79
4
+ data.tar.gz: b1e28178c7d60a27aad0105d5acace27afa759db510f5351a584e8b6922dc2b5
5
5
  SHA512:
6
- metadata.gz: e4e5bbeed4475c9f4c5b01dcac9c780adcc24c8c018059f0dff02cc5fbfe8bed96ae2509f539471d547f32fe8e42e85cc07e4787a0ab43c1b4fdeb72dd1f0a0c
7
- data.tar.gz: e7c02526e3305c6d27e733f4ebda50c8729a0102ced57c4cf609d86b7ab5f316557f8ba61ff2efa0fdb2cceb53347842a1e110530e10ed64e43a600e6717e581
6
+ metadata.gz: 11e6e6eb7fa83a5d83dedeb0be90b481ffa5b7e7044fe42fa531b5b2da36d4b9f8cf49b43e2f716572a86af17ce80b1f17cce1ab5e62dab8dba6718c4ce51723
7
+ data.tar.gz: 65a65cd114bbf6438244a5075901fd8c2729a74a95bd644beebcf4d5bf436a2db5cf2e0d76a5858d2a0b89f7a11d8140cadf5991760faaf5e45760c135715da7
data/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog
2
+
3
+ All notable changes to this plugin are documented in this file.
4
+
5
+ ## 2.5.0
6
+
7
+ ### Fixed
8
+
9
+ - **Compatibility with fastlane 2.238 and Faraday 2** ([#20](https://github.com/fastlane-community/fastlane-plugin-applivery/issues/20)). The
10
+ upload crashed with `uninitialized constant Faraday::UploadIO (NameError)`, because Faraday 2 moved
11
+ multipart support to the `faraday-multipart` gem. The plugin now works with Faraday 1 and Faraday 2, and
12
+ no longer depends on the unmaintained `faraday_middleware` gem (`FaradayMiddleware::ParseJson`).
13
+ - **No more `fatal:` lines in the console** ([#18](https://github.com/fastlane-community/fastlane-plugin-applivery/issues/18)). Repositories
14
+ without tags (or folders that are not a repository at all) made git print
15
+ `fatal: No names found, cannot describe anything.` and
16
+ `fatal: empty string is not a valid pathspec...`, which looked like the upload had failed. Git commands
17
+ are now run without leaking their output, and the details are only shown with `--verbose`.
18
+ - The git tag, commit hash and repository URL are no longer sent with a trailing line break.
19
+ - The branch and the commit message are now read from git when fastlane cannot resolve them, instead of
20
+ being reported as empty.
21
+ - The tag of the current commit is now resolved comparing full commit hashes, instead of abbreviated ones.
22
+ - A failure with an unexpected response (an HTML error page from a proxy, an empty body, ...) reported
23
+ `undefined local variable or method 'response'` instead of the HTTP status code.
24
+ - The detection of the build generated by the previous step of the lane no longer relies on an
25
+ always-true condition (`elsif :android`), and falls back to the IPA generated by `gym` when the lane
26
+ has no platform (`fastlane run applivery`).
27
+
28
+ ### Added
29
+
30
+ - New `timeout` option (`APPLIVERY_TIMEOUT`, 600 seconds by default) so uploading big builds over a slow
31
+ network does not fail after the 60 seconds Net::HTTP defaults to.
32
+ - The build number is also detected on GitHub Actions, GitLab CI, CircleCI, Bitrise and Azure Pipelines,
33
+ in addition to Xcode Server, Jenkins and Travis CI.
34
+ - Missing or wrong `build_path` values now fail immediately with an actionable message, instead of
35
+ uploading an empty request and letting the API reject it. Timeouts and connection errors are also
36
+ reported with a readable message.
37
+ - The `applivery` action returns the id of the new build, in addition to setting the
38
+ `APPLIVERY_BUILD_ID` shared value.
39
+
40
+ ### Changed
41
+
42
+ - Unit tests (they no longer need a real app token to run) and a GitHub Actions workflow running them on
43
+ Ruby 3.1 to 3.4. The Travis CI and CircleCI configurations were removed.
44
+ - A Docker based end-to-end suite (`e2e/`) that uploads a real build with the combinations users
45
+ actually have, from Ruby 2.6 + Faraday 1 to the newest fastlane on Faraday 2. See
46
+ [e2e/README.md](e2e/README.md).
47
+ - `rubocop` configuration updated, the previous one referenced cops that no longer exist.
48
+
49
+ ## 2.4.0
50
+
51
+ - Support for private tenants through the `tenant` option.
52
+
53
+ ## 2.3.1
54
+
55
+ - Fixed the git tag interpolation.
56
+
57
+ ## 2.3.0
58
+
59
+ - The build is uploaded to `upload.applivery.io`.
60
+ - Added the `filter` option to choose which groups get notified.
61
+
62
+ Older releases are listed in
63
+ [GitHub](https://github.com/fastlane-community/fastlane-plugin-applivery/releases).
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Alejandro Jimenez <alejandro.jimenez@gigigo.com>
3
+ Copyright (c) 2026 Applivery <info@applivery.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -11,18 +11,17 @@ This project is a [fastlane](https://github.com/fastlane/fastlane) plugin. To ge
11
11
  fastlane add_plugin applivery
12
12
  ```
13
13
 
14
+ It works with Faraday 1 and Faraday 2, so any fastlane version from 2.x on is supported.
15
+
14
16
  ## About Applivery.com
15
17
 
16
18
  With [Applivery.com](https://www.applivery.com) you can easily distribute your iOS and Android Apps throughout a customizable platform with no need of your users have to be registered on it.
17
19
 
18
20
  **The main purpose of this plugin is to upload a new iOS or Android build to [Applivery.com](https://www.applivery.com).**
19
21
 
20
- If you usually use Fastlane tools to automate the most common development tasks now you can start using out Fastlane Plugin to easily deploy new iOS and Android versions of your Apps to Applivery and close your development the cycle: Build, Test & deploy like a pro!
21
-
22
- This **fastlane** plugin will also help you to have more context about the build, attaching and displaying the most relevant information: Direct link to the repository(GitHub & Bitbucket), commit hash, Branch, Tag, etc:
22
+ If you usually use Fastlane tools to automate the most common development tasks now you can start using our Fastlane Plugin to easily deploy new iOS and Android versions of your Apps to Applivery and close your development cycle: Build, Test & deploy like a pro!
23
23
 
24
- <!--![List of builds](http://www.applivery.com/wp-content/uploads/2016/08/BuildsList.png)-->
25
- <!--![Build details](http://www.applivery.com/wp-content/uploads/2016/08/BuilInfo.png)-->
24
+ This **fastlane** plugin will also help you to have more context about the build, attaching and displaying the most relevant information: Direct link to the repository (GitHub & Bitbucket), commit hash, branch, tag, etc.
26
25
 
27
26
  ## Examples
28
27
 
@@ -52,26 +51,27 @@ lane :applivery_android do
52
51
  end
53
52
  ```
54
53
 
55
- Please check out the [example `Fastfile`](fastlane/Fastfile) to see additional examples of how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
54
+ Please check out the [example `Fastfile`](fastlane/Fastfile) to see additional examples of how to use this plugin.
56
55
 
57
56
  ## Additional Parameters
58
- The above examples are the most simple configuration you can have but you can add additional parameters to fully customize the deployment process. They are:
59
-
60
- | Param | Description | Mandatory | Values |
61
- |--------------------------|--------------------------------------|-----------|--------------|
62
- | `app_token` | Applivery App Token | YES | string -> Available in the App Settings |
63
- | `name` | Applivery Build name | NO | string-> i.e.: "RC 1.0" |
64
- | `notify_collaborators` | Notify Collaborators after deploy | NO | boolean -> i.e.: `true` / `false` |
65
- | `notify_employees` | Notify Employees after deploy | NO | boolean -> i.e.: `true` / `false` |
66
- | `notify_message` | Notification message | NO | string -> i.e.: "Enjoy the new version!" |
67
- | `changelog` | Release notes | NO | string -> i.e.: "Bug fixing" |
68
- | `tags` | Tags to identify the build | NO | string -> comma separated. i.e.: `"RC1, QA"` |
69
- | `filter` | List of groups that will be notified | NO | string -> comma separated + special chars. i.e.: `"group1,group2\|group3"` = (grupo1 AND grupo2) OR (grupo3) |
70
- | `build_path` | Build path to the APK/AAB/IPA file | NO | string -> by default it takes the IPA/APK build path |
71
- | `tenant` | Private tenant name or base domain | NO | string -> i.e.: `mycompany` or `mycompany-apps.com` |
57
+ The above examples are the most simple configuration you can have but you can add additional parameters to fully customize the deployment process. Every parameter can also be set with its environment variable, which is handy on CI. They are:
58
+
59
+ | Param | Environment variable | Description | Mandatory | Values |
60
+ |--------------------------|-----------------------------------|--------------------------------------|-----------|--------------|
61
+ | `app_token` | `APPLIVERY_APP_TOKEN` | Applivery App Token | YES | string -> Available in the App Settings |
62
+ | `name` | `APPLIVERY_BUILD_NAME` | Applivery Build name | NO | string-> i.e.: "RC 1.0" |
63
+ | `notify_collaborators` | `APPLIVERY_NOTIFY_COLLABORATORS` | Notify Collaborators after deploy | NO | boolean -> i.e.: `true` / `false` |
64
+ | `notify_employees` | `APPLIVERY_NOTIFY_EMPLOYEES` | Notify Employees after deploy | NO | boolean -> i.e.: `true` / `false` |
65
+ | `notify_message` | `APPLIVERY_NOTIFY_MESSAGE` | Notification message | NO | string -> i.e.: "Enjoy the new version!" |
66
+ | `changelog` | `APPLIVERY_BUILD_CHANGELOG` | Release notes | NO | string -> i.e.: "Bug fixing" |
67
+ | `tags` | `APPLIVERY_BUILD_TAGS` | Tags to identify the build | NO | string -> comma separated. i.e.: `"RC1, QA"` |
68
+ | `filter` | `APPLIVERY_FILTER` | List of groups that will be notified | NO | string -> comma separated + special chars. i.e.: `"group1,group2\|group3"` = (grupo1 AND grupo2) OR (grupo3) |
69
+ | `build_path` | `APPLIVERY_BUILD_PATH` | Build path to the APK/AAB/IPA file | NO | string -> by default it takes the IPA/APK/AAB build path |
70
+ | `tenant` | `APPLIVERY_TENANT` | Private tenant name or base domain | NO | string -> i.e.: `mycompany` or `mycompany-apps.com` |
71
+ | `timeout` | `APPLIVERY_TIMEOUT` | Upload timeout in seconds | NO | integer -> `600` by default |
72
72
 
73
73
  ## Shared Value
74
- Once your build is uploaded successfuly, the new generated build ID is provided by a Shared Value `APPLIVERY_BUILD_ID` that can be accesed in your lane with `lane_context[SharedValues::APPLIVERY_BUILD_ID]`
74
+ Once your build is uploaded successfully, the new generated build ID is provided by a Shared Value `APPLIVERY_BUILD_ID` that can be accessed in your lane with `lane_context[SharedValues::APPLIVERY_BUILD_ID]`. The action returns that same id, so it can also be assigned directly.
75
75
 
76
76
  Example:
77
77
 
@@ -80,8 +80,9 @@ lane :applivery_ios do
80
80
  gym(
81
81
  scheme: "YOUR_APP_SCHEME", # Your App Scheme
82
82
  export_method: 'enterprise') # Choose between: enterprise or ad-hoc
83
- applivery(
84
- app_token: "YOUR_APP_TOKEN" # Your Applivery App Token)
83
+ build_id = applivery(
84
+ app_token: "YOUR_APP_TOKEN") # Your Applivery App Token
85
+ puts "BUILD ID: #{build_id}"
85
86
  puts "BUILD ID: #{lane_context[SharedValues::APPLIVERY_BUILD_ID]}"
86
87
  end
87
88
  ```
@@ -98,19 +99,48 @@ Or to create a direct link to a specific build in your enterprise store:
98
99
  "https://{YOUR_WORKSPACE_SLUG}.applivery.io/#{YOUR_APP_SLUG}?os={YOUR_APP_OS}&build=#{THIS_BUILD_ID}"
99
100
  ```
100
101
 
102
+ ## Build information
103
+
104
+ Along with the build, the plugin sends the context of the deployment so you can identify every build in the Applivery dashboard: the build number of the CI job (Xcode Server, Jenkins, Travis CI, GitHub Actions, GitLab CI, CircleCI, Bitrise and Azure Pipelines are detected automatically), and the branch, commit, commit message, tag and repository URL of the current git checkout. All of them are optional: nothing is reported when the build is not made from a git repository.
105
+
101
106
  ## Run tests for this plugin
102
107
 
103
108
  To run both the tests, and code style validation, run
104
109
 
105
110
  ```
106
- rake
111
+ bundle install
112
+ bundle exec rake
113
+ ```
114
+
115
+ To only run the tests, or to only check the code style:
116
+
117
+ ```
118
+ bundle exec rake spec
119
+ bundle exec rubocop
107
120
  ```
108
121
 
109
- To automatically fix many of the styling issues, use
122
+ To automatically fix many of the styling issues, use
123
+
124
+ ```
125
+ bundle exec rubocop -a
126
+ ```
127
+
128
+ ### End-to-end tests
129
+
130
+ There is also a Docker based end-to-end suite that installs real fastlane and
131
+ faraday combinations (from ruby 2.6 + faraday 1 to the newest release on faraday
132
+ 2) and uploads a real build to a real Applivery app in each of them:
133
+
110
134
  ```
111
- rubocop -a
135
+ cp e2e/config.env.example e2e/config.env # your token, tenant and build file
136
+ ./e2e/run.sh --list
137
+ ./e2e/run.sh
112
138
  ```
113
139
 
140
+ It needs Docker and real credentials, so it is not part of `rake` nor of CI.
141
+ See [e2e/README.md](e2e/README.md) for the version matrix, the scenarios and
142
+ what each run costs.
143
+
114
144
  ## Issues and Feedback
115
145
 
116
146
  For any other issues and feedback about this plugin, please submit it to this repository or contact us at [support@applivery.com](mailto:support@applivery.com)
@@ -119,6 +149,12 @@ For any other issues and feedback about this plugin, please submit it to this re
119
149
 
120
150
  If you have trouble using plugins, check out the [Plugins Troubleshooting](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/PluginsTroubleshooting.md) doc in the main `fastlane` repo.
121
151
 
152
+ **`uninitialized constant Faraday::UploadIO`**: you are running a plugin version older than 2.5.0 with a fastlane version that uses Faraday 2 (2.238.0 and newer). Update the plugin with `bundle update fastlane-plugin-applivery`.
153
+
154
+ **Upload timeouts**: big builds on a slow network may need more time than the 600 seconds used by default. Increase it with the `timeout` option.
155
+
156
+ Run `fastlane` with `--verbose` to see the upload URL, the request body and the raw API response.
157
+
122
158
  ## Using `fastlane` Plugins
123
159
 
124
160
  For more information about how the `fastlane` plugin system works, check out the [Plugins documentation](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Plugins.md).
@@ -2,94 +2,126 @@ require 'faraday'
2
2
 
3
3
  module Fastlane
4
4
  module Actions
5
-
6
5
  module SharedValues
7
6
  APPLIVERY_BUILD_ID = :APPLIVERY_BUILD_ID
8
7
  end
9
8
 
10
9
  class AppliveryAction < Action
10
+ UPLOAD_PATH = "/v1/integrations/builds".freeze
11
+
12
+ # Default timeout for the whole upload request. Big builds on a slow
13
+ # network need way more than the 60 seconds Net::HTTP defaults to.
14
+ DEFAULT_TIMEOUT = 600
11
15
 
12
16
  def self.run(params)
13
- build_path = params[:build_path]
14
- build = Faraday::UploadIO.new(build_path, 'application/octet-stream') if build_path && File.exist?(build_path)
15
-
16
- base_domain = Helper::AppliveryHelper.get_base_domain(params[:tenant])
17
- upload_api_url = "https://upload.#{base_domain}"
18
-
19
- conn = Faraday.new(url: upload_api_url) do |faraday|
20
- faraday.request :multipart
21
- faraday.request :url_encoded
22
- # faraday.response :logger
23
- faraday.use FaradayMiddleware::ParseJson
24
- faraday.adapter :net_http
25
- end
17
+ build_path = validated_build_path(params[:build_path])
18
+ request_body = build_request_body(params, build_path)
19
+ connection = Helper::AppliveryHelper.upload_connection(params[:tenant], params[:timeout])
20
+
21
+ UI.message("Uploading to Applivery... 🛫")
22
+ response = upload(connection, request_body, params)
26
23
 
27
- response = conn.post do |req|
28
- req.url '/v1/integrations/builds'
24
+ handle_response(response)
25
+ end
26
+
27
+ def self.upload(connection, request_body, params)
28
+ connection.post do |req|
29
+ req.url(UPLOAD_PATH)
29
30
  req.headers['Content-Type'] = 'multipart/form-data'
30
31
  req.headers['Accept'] = 'application/json'
31
32
  req.headers['Authorization'] = "bearer #{params[:app_token]}"
32
- request_body = {
33
- changelog: params[:changelog],
34
- notifyCollaborators: params[:notify_collaborators],
35
- notifyEmployees: params[:notify_employees],
36
- notifyMessage: params[:notify_message],
37
- filter: params[:filter],
38
- build: build,
39
- deployer: {
40
- name: "fastlane",
41
- info: {
42
- buildNumber: Helper::AppliveryHelper.get_integration_number,
43
- branch: Helper::AppliveryHelper.git_branch,
44
- commit: Helper::AppliveryHelper.git_commit,
45
- commitMessage: Helper::AppliveryHelper.git_message,
46
- repositoryUrl: Helper::AppliveryHelper.add_git_remote,
47
- tag: Helper::AppliveryHelper.git_tag,
48
- triggerTimestamp: Time.now.getutc.to_i
49
- }
50
- }
51
- }
52
- request_body[:versionName] = params[:name] if !params[:name].nil?
53
- request_body[:tags] = params[:tags] if !params[:tags].nil?
54
-
55
33
  req.body = request_body
56
- UI.message "Uploading to Applivery... 🛫"
57
34
  UI.verbose("Request Body: #{req.body}")
58
35
  end
59
- UI.verbose "Response Body: #{response.body}"
60
- status = response.body["status"]
61
- if status
62
- UI.success "Build uploaded successfully! 💪"
63
- Actions.lane_context[SharedValues::APPLIVERY_BUILD_ID] = response.body["data"]["id"]
36
+ rescue Faraday::TimeoutError
37
+ UI.user_error!("Timed out while uploading the build to Applivery. You can increase the `timeout` option (currently #{params[:timeout] || DEFAULT_TIMEOUT} seconds) and try again")
38
+ rescue Faraday::ConnectionFailed => e
39
+ UI.user_error!("Could not connect to Applivery: #{e.message}. Please check your network connection#{params[:tenant] ? " and the `tenant` option (#{params[:tenant]})" : ''}")
40
+ end
41
+
42
+ def self.handle_response(response)
43
+ body = Helper::AppliveryHelper.parse_response_body(response)
44
+ UI.verbose("Response Body: #{body}")
45
+
46
+ if body["status"]
47
+ UI.success("Build uploaded successfully! 💪")
48
+ build_id = (body["data"] || {})["id"]
49
+ Actions.lane_context[SharedValues::APPLIVERY_BUILD_ID] = build_id
50
+ return build_id
64
51
  else
65
- UI.error "Oops! Something went wrong.... 🔥"
66
- error = response.body["error"]
67
- Helper::AppliveryHelper.parse_error(error)
52
+ UI.error("Oops! Something went wrong.... 🔥")
53
+ Helper::AppliveryHelper.parse_error(body["error"], response.status)
54
+ end
55
+ end
56
+
57
+ # Fails early with an actionable message instead of uploading an empty
58
+ # request and letting the API reject it.
59
+ def self.validated_build_path(build_path)
60
+ build_path = build_path.to_s.strip
61
+ if build_path.empty?
62
+ UI.user_error!("No build to upload. Please set the `build_path` option (or the APPLIVERY_BUILD_PATH environment variable) with the path to your IPA, APK or AAB file")
63
+ end
64
+ unless File.file?(build_path)
65
+ UI.user_error!("Build not found at '#{build_path}'. Please double-check the `build_path` option")
68
66
  end
69
67
 
68
+ return build_path
70
69
  end
71
70
 
71
+ def self.build_request_body(params, build_path)
72
+ request_body = {
73
+ changelog: params[:changelog],
74
+ notifyCollaborators: params[:notify_collaborators],
75
+ notifyEmployees: params[:notify_employees],
76
+ notifyMessage: params[:notify_message],
77
+ filter: params[:filter],
78
+ build: Helper::AppliveryHelper.file_part(build_path),
79
+ deployer: {
80
+ name: "fastlane",
81
+ info: {
82
+ buildNumber: Helper::AppliveryHelper.get_integration_number,
83
+ branch: Helper::AppliveryHelper.git_branch,
84
+ commit: Helper::AppliveryHelper.git_commit,
85
+ commitMessage: Helper::AppliveryHelper.git_message,
86
+ repositoryUrl: Helper::AppliveryHelper.add_git_remote,
87
+ tag: Helper::AppliveryHelper.git_tag,
88
+ triggerTimestamp: Time.now.getutc.to_i
89
+ }
90
+ }
91
+ }
92
+ request_body[:versionName] = params[:name] unless params[:name].nil?
93
+ request_body[:tags] = params[:tags] unless params[:tags].nil?
94
+
95
+ return request_body
96
+ end
97
+
98
+ # Build generated by the previous step of the lane, if any.
72
99
  def self.build_path
73
100
  platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
74
101
  ipa_path = Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]
75
102
  aab_path = Actions.lane_context[Actions::SharedValues::GRADLE_AAB_OUTPUT_PATH]
76
103
  apk_path = Actions.lane_context[Actions::SharedValues::GRADLE_APK_OUTPUT_PATH]
77
104
 
78
- if platform == :ios
79
- return ipa_path
80
- elsif :android and !aab_path.nil?
81
- return aab_path
82
- else
83
- return apk_path
84
- end
105
+ return ipa_path if platform == :ios
106
+ return aab_path unless aab_path.nil?
107
+ return apk_path unless apk_path.nil?
108
+ return ipa_path
85
109
  end
86
110
 
87
111
  def self.description
88
112
  "Upload new iOS or Android build to Applivery"
89
113
  end
90
114
 
115
+ def self.details
116
+ [
117
+ "Uploads an IPA, APK or AAB file to Applivery and attaches the information of the current build:",
118
+ "build number, git branch, commit, commit message, tag and repository URL.",
119
+ "By default it takes the build generated by `gym` or `gradle` in the same lane."
120
+ ].join(" ")
121
+ end
122
+
91
123
  def self.authors
92
- ["Alejandro Jimenez", "Cesar Trigo"]
124
+ ["Applivery"]
93
125
  end
94
126
 
95
127
  def self.available_options
@@ -98,6 +130,7 @@ module Fastlane
98
130
  env_name: "APPLIVERY_APP_TOKEN",
99
131
  description: "Your application identifier",
100
132
  optional: false,
133
+ sensitive: true,
101
134
  type: String),
102
135
 
103
136
  FastlaneCore::ConfigItem.new(key: :name,
@@ -123,6 +156,7 @@ module Fastlane
123
156
  env_name: "APPLIVERY_BUILD_PATH",
124
157
  description: "Your build path",
125
158
  default_value: self.build_path,
159
+ default_value_dynamic: true,
126
160
  optional: true,
127
161
  type: String),
128
162
 
@@ -146,7 +180,7 @@ module Fastlane
146
180
  default_value: "New version uploaded!",
147
181
  optional: true,
148
182
  type: String),
149
-
183
+
150
184
  FastlaneCore::ConfigItem.new(key: :filter,
151
185
  env_name: "APPLIVERY_FILTER",
152
186
  description: "List of groups that will be notified",
@@ -158,6 +192,16 @@ module Fastlane
158
192
  description: "Your private tenant name or base domain",
159
193
  optional: true,
160
194
  type: String),
195
+
196
+ FastlaneCore::ConfigItem.new(key: :timeout,
197
+ env_name: "APPLIVERY_TIMEOUT",
198
+ description: "Timeout in seconds for the upload request",
199
+ default_value: DEFAULT_TIMEOUT,
200
+ optional: true,
201
+ type: Integer,
202
+ verify_block: proc do |value|
203
+ UI.user_error!("`timeout` must be greater than 0") unless value.to_i.positive?
204
+ end)
161
205
  ]
162
206
  end
163
207
 
@@ -167,11 +211,13 @@ module Fastlane
167
211
  ]
168
212
  end
169
213
 
214
+ def self.return_value
215
+ "The id of the build created in Applivery"
216
+ end
217
+
170
218
  def self.is_supported?(platform)
171
- # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
172
- # See: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md
173
-
174
- [:ios, :android].include?(platform)
219
+ # The action uploads any IPA, APK or AAB file, so it also works when the
220
+ # lane has no platform defined (for example with `fastlane run applivery`)
175
221
  true
176
222
  end
177
223
 
@@ -181,10 +227,24 @@ module Fastlane
181
227
 
182
228
  def self.example_code
183
229
  [
184
- 'applivery(app_token: "YOUR_APP_TOKEN")'
230
+ 'applivery(app_token: "YOUR_APP_TOKEN")',
231
+ 'applivery(
232
+ app_token: "YOUR_APP_TOKEN",
233
+ build_path: "./app/build/outputs/bundle/release/app-release.aab",
234
+ name: "RC 1.0",
235
+ changelog: "Bug fixing",
236
+ tags: "RC1, QA",
237
+ notify_collaborators: true,
238
+ notify_employees: false,
239
+ notify_message: "Enjoy the new version!",
240
+ filter: "group1,group2|group3"
241
+ )',
242
+ 'applivery(
243
+ app_token: "YOUR_APP_TOKEN",
244
+ tenant: "mycompany"
245
+ )'
185
246
  ]
186
247
  end
187
-
188
248
  end
189
249
  end
190
250
  end
@@ -1,12 +1,40 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'open3'
4
+
5
+ begin
6
+ # Faraday 2 extracted multipart support into the `faraday-multipart` gem and
7
+ # it has to be required explicitly. Faraday 1 ships it in the core gem.
8
+ require 'faraday/multipart'
9
+ rescue LoadError
10
+ # Nothing else to load, Faraday 1 already provides multipart support.
11
+ end
12
+
1
13
  module Fastlane
2
14
  module Helper
3
15
  class AppliveryHelper
4
16
  # class methods that you define here become available in your action
5
17
  # as `Helper::AppliveryHelper.your_method`
6
18
  #
7
- def self.show_message
8
- UI.message("Hello from the applivery plugin helper!")
9
- end
19
+ DEFAULT_DOMAIN = "applivery.io".freeze
20
+ UPLOAD_MIME_TYPE = "application/octet-stream".freeze
21
+
22
+ # Seconds we wait for the connection to be established. The upload itself
23
+ # is limited by the `timeout` option of the action instead.
24
+ OPEN_TIMEOUT = 60
25
+
26
+ # Environment variables holding the build number of the current CI job,
27
+ # ordered by precedence.
28
+ INTEGRATION_NUMBER_ENV_VARS = [
29
+ "XCS_INTEGRATION_NUMBER", # Xcode Server
30
+ "BUILD_NUMBER", # Jenkins
31
+ "TRAVIS_BUILD_NUMBER", # Travis CI
32
+ "GITHUB_RUN_NUMBER", # GitHub Actions
33
+ "CI_PIPELINE_IID", # GitLab CI
34
+ "CIRCLE_BUILD_NUM", # CircleCI
35
+ "BITRISE_BUILD_NUMBER", # Bitrise
36
+ "BUILD_BUILDID" # Azure Pipelines
37
+ ].freeze
10
38
 
11
39
  def self.platform
12
40
  platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
@@ -18,83 +46,154 @@ module Fastlane
18
46
  end
19
47
 
20
48
  def self.get_base_domain(tenant = nil)
21
- default_domain = "applivery.io"
22
- return default_domain if tenant.nil? || tenant.strip.empty?
49
+ tenant = tenant.to_s.strip
50
+ return DEFAULT_DOMAIN if tenant.empty?
23
51
  return tenant if tenant.include?(".")
24
- return "#{tenant}.#{default_domain}"
52
+ return "#{tenant}.#{DEFAULT_DOMAIN}"
25
53
  end
26
54
 
27
55
  def self.get_integration_number
28
- xcodeIntegrationNumber = ENV["XCS_INTEGRATION_NUMBER"] # XCode Server
29
- jenkinsIntegrationNumber = ENV["BUILD_NUMBER"] # Jenkins
30
- travisIntegrationNumber = ENV["TRAVIS_BUILD_NUMBER"] # Travis
31
- integrationNumber = ""
32
-
33
- if !xcodeIntegrationNumber.nil?
34
- integrationNumber += xcodeIntegrationNumber
35
- elsif !jenkinsIntegrationNumber.nil?
36
- integrationNumber += jenkinsIntegrationNumber
37
- elsif !travisIntegrationNumber.nil?
38
- integrationNumber += travisIntegrationNumber
56
+ INTEGRATION_NUMBER_ENV_VARS.each do |env_var|
57
+ integration_number = ENV[env_var].to_s.strip
58
+ return integration_number unless integration_number.empty?
39
59
  end
40
60
 
41
- return integrationNumber
61
+ return ""
62
+ end
63
+
64
+ ### HTTP Methods ###
65
+
66
+ # Connection used to upload the build. Both the read and the write
67
+ # timeouts are set from `timeout` so uploading big builds over a slow
68
+ # network doesn't fail after Net::HTTP's default 60 seconds.
69
+ def self.upload_connection(tenant = nil, timeout = nil)
70
+ url = "https://upload.#{get_base_domain(tenant)}"
71
+ UI.verbose("Applivery upload URL: #{url}")
72
+
73
+ request_options = { open_timeout: OPEN_TIMEOUT }
74
+ request_options[:timeout] = timeout if timeout
75
+
76
+ Faraday.new(url: url, request: request_options) do |faraday|
77
+ faraday.request(:multipart)
78
+ faraday.request(:url_encoded)
79
+ faraday.adapter(:net_http)
80
+ end
81
+ rescue Faraday::Error => e
82
+ UI.user_error!("Could not prepare the upload request: #{e.message}. Please add `gem 'faraday-multipart'` to your Gemfile and run `bundle install`")
83
+ end
84
+
85
+ # Wraps the build in the multipart class provided by the installed Faraday
86
+ # version: `Faraday::Multipart::FilePart` (Faraday 2 and Faraday >= 1.10),
87
+ # `Faraday::FilePart` or the long deprecated `Faraday::UploadIO`.
88
+ def self.file_part(path, mime_type = UPLOAD_MIME_TYPE)
89
+ if defined?(Faraday::Multipart::FilePart)
90
+ Faraday::Multipart::FilePart.new(path, mime_type)
91
+ elsif defined?(Faraday::FilePart)
92
+ Faraday::FilePart.new(path, mime_type)
93
+ elsif defined?(Faraday::UploadIO)
94
+ Faraday::UploadIO.new(path, mime_type)
95
+ else
96
+ UI.user_error!("Faraday #{Faraday::VERSION} does not provide multipart support. Please add `gem 'faraday-multipart'` to your Gemfile and run `bundle install`")
97
+ end
42
98
  end
43
99
 
100
+ # Parses the response body, which is a Hash when a JSON middleware is
101
+ # installed in the connection and a String otherwise.
102
+ def self.parse_response_body(response)
103
+ body = response.body
104
+ return body if body.kind_of?(Hash)
105
+
106
+ parsed_body = JSON.parse(body.to_s)
107
+ parsed_body.kind_of?(Hash) ? parsed_body : {}
108
+ rescue JSON::ParserError
109
+ UI.verbose("Response body is not valid JSON: #{response.body}")
110
+ return {}
111
+ end
44
112
 
45
113
  ### GIT Methods ###
46
114
 
115
+ # fastlane resolves the branch from the environment of the CI when possible,
116
+ # git is only asked when it has nothing to report.
47
117
  def self.git_branch
48
- return Actions.git_branch
49
- rescue
50
- return ""
118
+ branch = fastlane_git_value { Actions.git_branch }
119
+ return branch unless branch.empty?
120
+ return git_command("rev-parse", "--abbrev-ref", "HEAD")
51
121
  end
52
122
 
53
123
  def self.git_commit
54
- return `git rev-parse --short HEAD`
55
- rescue
56
- return ""
124
+ return git_command("rev-parse", "--short", "HEAD")
57
125
  end
58
126
 
59
127
  def self.git_message
60
- return Actions.last_git_commit_message
61
- rescue
62
- return ""
128
+ message = fastlane_git_value { Actions.last_git_commit_message }
129
+ return message unless message.empty?
130
+ return git_command("log", "-1", "--pretty=%B")
63
131
  end
64
132
 
65
133
  def self.add_git_remote
66
- return `git config --get remote.origin.url`
67
- rescue
68
- return ""
134
+ return git_command("config", "--get", "remote.origin.url")
69
135
  end
70
136
 
137
+ # Returns the tag pointing at HEAD, or an empty String when the current
138
+ # commit is not tagged (or the repository has no tags at all).
71
139
  def self.git_tag
72
- gitTag = `git describe --abbrev=0 --tags`
73
- gitTagCommit = `git rev-list -n 1 --abbrev-commit '#{gitTag.strip}'`
74
- gitCommit = `git rev-parse --short HEAD`
75
- return gitTag if gitTagCommit == gitCommit
140
+ tag = git_command("describe", "--abbrev=0", "--tags")
141
+ return "" if tag.empty?
142
+
143
+ tag_commit = git_command("rev-list", "-n", "1", tag)
144
+ head_commit = git_command("rev-parse", "HEAD")
145
+ return tag if !tag_commit.empty? && tag_commit == head_commit
76
146
  return ""
77
- rescue
147
+ end
148
+
149
+ # Values that fastlane can provide itself. It raises when there is no git
150
+ # information available, and then git is asked directly.
151
+ def self.fastlane_git_value
152
+ yield.to_s.strip
153
+ rescue StandardError => e
154
+ UI.verbose("Applivery: fastlane could not provide the git information: #{e.message}")
78
155
  return ""
79
156
  end
157
+ private_class_method :fastlane_git_value
80
158
 
81
- def self.parse_error(error)
82
- if error
83
- case error["code"]
84
- when 5006
85
- UI.user_error! "Upload fail. The build path seems to be wrong or file is invalid"
86
- when 4004
87
- UI.user_error! "The app_token is not valid. Please, go to your app settings and doble-check the integration tokens"
88
- when 4002
89
- UI.user_error! "The app_token is empty. Please, go to your app Settings->Integrations to generate a token"
90
- else
91
- UI.user_error! "Upload fail. [#{error["code"]}]: #{error["message"]}"
92
- end
93
- else
94
- UI.crash! "Upload fails unexpectedly. [#{response.status}]"
159
+ # Runs a git command without leaking anything to the console. Git writes
160
+ # to stderr when there are no tags, when the folder is not a repository,
161
+ # etc. and those `fatal:` lines used to look like the upload had failed.
162
+ def self.git_command(*args)
163
+ output, status = Open3.capture2("git", *args, err: File::NULL)
164
+ unless status.success?
165
+ UI.verbose("Applivery: `git #{args.join(' ')}` exited with #{status.exitstatus}")
166
+ return ""
95
167
  end
168
+
169
+ return output.strip
170
+ rescue StandardError => e
171
+ UI.verbose("Applivery: `git #{args.join(' ')}` failed: #{e.message}")
172
+ return ""
96
173
  end
174
+ private_class_method :git_command
175
+
176
+ # `error` is the `error` object returned by the API. Anything else (a
177
+ # missing body, an HTML error page from a proxy, ...) ends up in the
178
+ # generic message including the HTTP status code.
179
+ def self.parse_error(error, status = nil)
180
+ unless error.kind_of?(Hash)
181
+ details = error.to_s.strip
182
+ details = details.empty? ? "" : ": #{details}"
183
+ UI.user_error!("Upload failed unexpectedly. [HTTP #{status || 'unknown'}]#{details}")
184
+ end
97
185
 
186
+ case error["code"].to_i
187
+ when 5006
188
+ UI.user_error!("Upload failed. The build path seems to be wrong or the file is invalid")
189
+ when 4004
190
+ UI.user_error!("The app_token is not valid. Please, go to your app settings and double-check the integration tokens")
191
+ when 4002
192
+ UI.user_error!("The app_token is empty. Please, go to your app Settings->Integrations to generate a token")
193
+ else
194
+ UI.user_error!("Upload failed. [#{error['code']}]: #{error['message']}")
195
+ end
196
+ end
98
197
  end
99
198
  end
100
199
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module Applivery
3
- VERSION = "2.4.0"
3
+ VERSION = "2.5.0".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,105 +1,145 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-applivery
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
- - Alejandro Jimenez
8
- autorequire:
7
+ - Applivery
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: pry
13
+ name: faraday
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
18
+ version: '1.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - ">="
25
27
  - !ruby/object:Gem::Version
26
- version: '0'
28
+ version: '1.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3.0'
27
32
  - !ruby/object:Gem::Dependency
28
- name: bundler
33
+ name: faraday-multipart
29
34
  requirement: !ruby/object:Gem::Requirement
30
35
  requirements:
31
36
  - - ">="
32
37
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
38
+ version: '1.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
42
+ type: :runtime
35
43
  prerelease: false
36
44
  version_requirements: !ruby/object:Gem::Requirement
37
45
  requirements:
38
46
  - - ">="
39
47
  - !ruby/object:Gem::Version
40
- version: '0'
48
+ version: '1.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '2.0'
41
52
  - !ruby/object:Gem::Dependency
42
- name: rspec
53
+ name: fastlane
43
54
  requirement: !ruby/object:Gem::Requirement
44
55
  requirements:
45
- - - ">="
56
+ - - "~>"
46
57
  - !ruby/object:Gem::Version
47
- version: '0'
58
+ version: '2.0'
48
59
  type: :development
49
60
  prerelease: false
50
61
  version_requirements: !ruby/object:Gem::Requirement
51
62
  requirements:
52
- - - ">="
63
+ - - "~>"
53
64
  - !ruby/object:Gem::Version
54
- version: '0'
65
+ version: '2.0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: pry
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '0.14'
73
+ type: :development
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.14'
55
80
  - !ruby/object:Gem::Dependency
56
81
  name: rake
57
82
  requirement: !ruby/object:Gem::Requirement
58
83
  requirements:
59
- - - ">="
84
+ - - "~>"
60
85
  - !ruby/object:Gem::Version
61
- version: '0'
86
+ version: '13.0'
62
87
  type: :development
63
88
  prerelease: false
64
89
  version_requirements: !ruby/object:Gem::Requirement
65
90
  requirements:
66
- - - ">="
91
+ - - "~>"
67
92
  - !ruby/object:Gem::Version
68
- version: '0'
93
+ version: '13.0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '3.0'
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '3.0'
69
108
  - !ruby/object:Gem::Dependency
70
109
  name: rubocop
71
110
  requirement: !ruby/object:Gem::Requirement
72
111
  requirements:
73
- - - ">="
112
+ - - "~>"
74
113
  - !ruby/object:Gem::Version
75
- version: '0'
114
+ version: 1.50.2
76
115
  type: :development
77
116
  prerelease: false
78
117
  version_requirements: !ruby/object:Gem::Requirement
79
118
  requirements:
80
- - - ">="
119
+ - - "~>"
81
120
  - !ruby/object:Gem::Version
82
- version: '0'
121
+ version: 1.50.2
83
122
  - !ruby/object:Gem::Dependency
84
- name: fastlane
123
+ name: webmock
85
124
  requirement: !ruby/object:Gem::Requirement
86
125
  requirements:
87
- - - ">="
126
+ - - "~>"
88
127
  - !ruby/object:Gem::Version
89
- version: 1.98.0
128
+ version: '3.0'
90
129
  type: :development
91
130
  prerelease: false
92
131
  version_requirements: !ruby/object:Gem::Requirement
93
132
  requirements:
94
- - - ">="
133
+ - - "~>"
95
134
  - !ruby/object:Gem::Version
96
- version: 1.98.0
97
- description:
98
- email: a.j.agudo@gmail.com
135
+ version: '3.0'
136
+ email:
137
+ - info@applivery.com
99
138
  executables: []
100
139
  extensions: []
101
140
  extra_rdoc_files: []
102
141
  files:
142
+ - CHANGELOG.md
103
143
  - LICENSE
104
144
  - README.md
105
145
  - lib/fastlane/plugin/applivery.rb
@@ -109,8 +149,11 @@ files:
109
149
  homepage: https://github.com/fastlane-community/fastlane-plugin-applivery
110
150
  licenses:
111
151
  - MIT
112
- metadata: {}
113
- post_install_message:
152
+ metadata:
153
+ homepage_uri: https://github.com/fastlane-community/fastlane-plugin-applivery
154
+ source_code_uri: https://github.com/fastlane-community/fastlane-plugin-applivery
155
+ changelog_uri: https://github.com/fastlane-community/fastlane-plugin-applivery/blob/master/CHANGELOG.md
156
+ bug_tracker_uri: https://github.com/fastlane-community/fastlane-plugin-applivery/issues
114
157
  rdoc_options: []
115
158
  require_paths:
116
159
  - lib
@@ -118,15 +161,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
161
  requirements:
119
162
  - - ">="
120
163
  - !ruby/object:Gem::Version
121
- version: '0'
164
+ version: '2.6'
122
165
  required_rubygems_version: !ruby/object:Gem::Requirement
123
166
  requirements:
124
167
  - - ">="
125
168
  - !ruby/object:Gem::Version
126
169
  version: '0'
127
170
  requirements: []
128
- rubygems_version: 3.0.3.1
129
- signing_key:
171
+ rubygems_version: 4.0.16
130
172
  specification_version: 4
131
173
  summary: Upload new build to Applivery
132
174
  test_files: []