fastlane-plugin-rustored 0.1.0 → 0.2.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 +4 -4
- data/README.md +35 -13
- data/lib/fastlane/plugin/rustored/actions/rustored_publish_aab_action.rb +4 -3
- data/lib/fastlane/plugin/rustored/actions/rustored_publish_apk_action.rb +9 -4
- data/lib/fastlane/plugin/rustored/helper/rustored_helper.rb +33 -20
- data/lib/fastlane/plugin/rustored/helper/rustored_options.rb +13 -4
- data/lib/fastlane/plugin/rustored/helper/rustored_token_helper.rb +58 -0
- data/lib/fastlane/plugin/rustored/version.rb +1 -1
- metadata +31 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1147f69aca62793f1507a37818bf8dab5568b33e37df2a8ddfc74cefa73dcc8
|
|
4
|
+
data.tar.gz: 2689e265f60f6147cdf3c5daa87f61bcc226d3360c67a85103a01f7bd3545e55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe622044ff5b889921a385f1fecee80816d5290b3e420edc60e941a324a0dd9dbb96051b0d777c67e696dee88f64af593f4178b69d1d8986c1d931372abfa843
|
|
7
|
+
data.tar.gz: a56d20e1fa4728b51e878b1e423965f8640ca5389b765becb017fa29ea742ee538ecdb1542753860323c57cb0c152cabd2dd53b5b81f9009bd88f817a53436cc
|
data/README.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# rustored
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://rubygems.org/gems/fastlane-plugin-rustored)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
Fastlane plugin for uploading APK and AAB files to an existing RuStore draft version through the public API.
|
|
7
|
+
|
|
8
|
+
The plugin does not create a draft, send it to moderation, or publish it. Pass the `version_id` of a draft created in the RuStore Console or through the [RuStore API](https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app/create-draft-version).
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- The application must have at least one active version in RuStore.
|
|
13
|
+
- The API key must have access to the application and the required APK/AAB upload method.
|
|
14
|
+
- A draft version and its `version_id` must already exist.
|
|
15
|
+
- Before uploading an AAB, configure its publication signature in the RuStore Console.
|
|
4
16
|
|
|
5
17
|
## Install
|
|
6
18
|
|
|
@@ -12,39 +24,48 @@ fastlane add_plugin rustored
|
|
|
12
24
|
|
|
13
25
|
### `rustored_publish_aab`
|
|
14
26
|
|
|
15
|
-
|
|
27
|
+
Uploads an AAB file to the draft.
|
|
16
28
|
|
|
17
29
|
```ruby
|
|
18
30
|
rustored_publish_aab(
|
|
19
|
-
|
|
31
|
+
key_id: ENV["RUSTORE_KEY_ID"],
|
|
32
|
+
private_key: ENV["RUSTORE_PRIVATE_KEY"],
|
|
20
33
|
package_name: "com.example.app",
|
|
21
|
-
version_id:
|
|
34
|
+
version_id: 123456,
|
|
22
35
|
aab_path: "app/build/outputs/bundle/release/app-release.aab"
|
|
23
36
|
)
|
|
24
37
|
```
|
|
25
38
|
|
|
26
39
|
### `rustored_publish_apk`
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
Uploads an APK file to the draft.
|
|
29
42
|
|
|
30
43
|
```ruby
|
|
31
44
|
rustored_publish_apk(
|
|
32
|
-
|
|
45
|
+
key_id: ENV["RUSTORE_KEY_ID"],
|
|
46
|
+
private_key: ENV["RUSTORE_PRIVATE_KEY"],
|
|
33
47
|
package_name: "com.example.app",
|
|
34
|
-
version_id:
|
|
48
|
+
version_id: 123456,
|
|
35
49
|
apk_path: "app/build/outputs/apk/release/app-release.apk",
|
|
36
50
|
services_type: "Unknown",
|
|
37
51
|
is_main_apk: true
|
|
38
52
|
)
|
|
39
53
|
```
|
|
40
54
|
|
|
55
|
+
The plugin signs the auth request and receives the RuStore public token automatically. Pass `private_key` exactly as copied from the RuStore Console: it is already Base64-encoded and must not be encoded again.
|
|
56
|
+
|
|
57
|
+
`services_type` accepts `Unknown` (the default) or `HMS`. `is_main_apk` is required by the RuStore API.
|
|
58
|
+
|
|
59
|
+
RuStore does not show an API-uploaded artifact in the Console until the draft is sent to moderation. Builds containing new sensitive permissions must be uploaded and declared through the Console instead.
|
|
60
|
+
|
|
41
61
|
## Options
|
|
42
62
|
|
|
43
63
|
Shared:
|
|
44
64
|
|
|
45
65
|
| Option | Env | Required |
|
|
46
66
|
| --- | --- | --- |
|
|
47
|
-
| `
|
|
67
|
+
| `key_id` | `RUSTORE_KEY_ID` | yes |
|
|
68
|
+
| `private_key` | `RUSTORE_PRIVATE_KEY` | yes |
|
|
48
69
|
| `package_name` | `RUSTORE_PACKAGE_NAME` | yes |
|
|
49
70
|
| `version_id` | `RUSTORE_VERSION_ID` | yes |
|
|
50
71
|
|
|
@@ -59,12 +80,13 @@ APK:
|
|
|
59
80
|
| Option | Env | Required |
|
|
60
81
|
| --- | --- | --- |
|
|
61
82
|
| `apk_path` | `RUSTORE_APK_PATH` | yes |
|
|
62
|
-
| `services_type` | `RUSTORE_SERVICES_TYPE` | no |
|
|
83
|
+
| `services_type` | `RUSTORE_SERVICES_TYPE` | no (`Unknown`) |
|
|
63
84
|
| `is_main_apk` | `RUSTORE_IS_MAIN_APK` | yes |
|
|
64
85
|
|
|
65
|
-
##
|
|
86
|
+
## Development
|
|
66
87
|
|
|
67
88
|
```bash
|
|
68
|
-
bundle exec
|
|
69
|
-
bundle exec rubocop
|
|
89
|
+
bundle exec rake
|
|
70
90
|
```
|
|
91
|
+
|
|
92
|
+
API reference: [authorization](https://www.rustore.ru/help/work-with-rustore-api/api-authorization-token), [APK upload](https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app/apk-file-upload/file-upload-apk), and [AAB upload](https://www.rustore.ru/help/work-with-rustore-api/api-upload-publication-app/apk-file-upload/file-upload-aab).
|
|
@@ -9,7 +9,8 @@ module Fastlane
|
|
|
9
9
|
class RustoredPublishAabAction < Action
|
|
10
10
|
def self.run(params)
|
|
11
11
|
Fastlane::Helper::RustoredHelper.publish_aab(
|
|
12
|
-
|
|
12
|
+
key_id: params[:key_id],
|
|
13
|
+
private_key: params[:private_key],
|
|
13
14
|
package_name: params[:package_name],
|
|
14
15
|
version_id: params[:version_id],
|
|
15
16
|
aab_path: params[:aab_path]
|
|
@@ -17,7 +18,7 @@ module Fastlane
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def self.description
|
|
20
|
-
"
|
|
21
|
+
"Upload an AAB file to an existing RuStore draft version"
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def self.authors
|
|
@@ -25,7 +26,7 @@ module Fastlane
|
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def self.details
|
|
28
|
-
"Uploads an Android App Bundle to an existing RuStore
|
|
29
|
+
"Uploads an Android App Bundle to an existing RuStore draft version."
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def self.available_options
|
|
@@ -9,7 +9,8 @@ module Fastlane
|
|
|
9
9
|
class RustoredPublishApkAction < Action
|
|
10
10
|
def self.run(params)
|
|
11
11
|
Fastlane::Helper::RustoredHelper.publish_apk(
|
|
12
|
-
|
|
12
|
+
key_id: params[:key_id],
|
|
13
|
+
private_key: params[:private_key],
|
|
13
14
|
package_name: params[:package_name],
|
|
14
15
|
version_id: params[:version_id],
|
|
15
16
|
services_type: params[:services_type],
|
|
@@ -19,7 +20,7 @@ module Fastlane
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def self.description
|
|
22
|
-
"
|
|
23
|
+
"Upload an APK file to an existing RuStore draft version"
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def self.authors
|
|
@@ -27,7 +28,7 @@ module Fastlane
|
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def self.details
|
|
30
|
-
"Uploads an APK to an existing RuStore
|
|
31
|
+
"Uploads an APK to an existing RuStore draft version."
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def self.available_options
|
|
@@ -44,7 +45,11 @@ module Fastlane
|
|
|
44
45
|
env_name: "RUSTORE_SERVICES_TYPE",
|
|
45
46
|
description: "The APK services type",
|
|
46
47
|
optional: true,
|
|
47
|
-
type: String
|
|
48
|
+
type: String,
|
|
49
|
+
default_value: "Unknown",
|
|
50
|
+
verify_block: proc do |value|
|
|
51
|
+
UI.user_error!("services_type must be HMS or Unknown") unless %w[HMS Unknown].include?(value)
|
|
52
|
+
end
|
|
48
53
|
),
|
|
49
54
|
FastlaneCore::ConfigItem.new(
|
|
50
55
|
key: :is_main_apk,
|
|
@@ -1,46 +1,51 @@
|
|
|
1
1
|
require 'fastlane_core/ui/ui'
|
|
2
2
|
require 'faraday'
|
|
3
|
-
require '
|
|
3
|
+
require 'faraday_middleware'
|
|
4
|
+
require_relative 'rustored_token_helper'
|
|
4
5
|
|
|
5
6
|
module Fastlane
|
|
6
7
|
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
|
7
8
|
|
|
8
9
|
module Helper
|
|
9
10
|
class RustoredHelper
|
|
10
|
-
BASE_URL = 'https://public-api.rustore.ru
|
|
11
|
+
BASE_URL = 'https://public-api.rustore.ru'
|
|
11
12
|
|
|
12
13
|
def self.client
|
|
13
14
|
@client ||= Faraday.new(url: BASE_URL) do |f|
|
|
14
|
-
f.request(:json)
|
|
15
15
|
f.request(:multipart)
|
|
16
16
|
f.response(:json)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def self.publish_aab(
|
|
21
|
-
UI.
|
|
20
|
+
def self.publish_aab(key_id:, private_key:, package_name:, version_id:, aab_path:)
|
|
21
|
+
UI.user_error!("AAB file not found: #{aab_path}") unless File.file?(aab_path)
|
|
22
|
+
UI.message("Uploading AAB to RuStore...")
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
token = RustoredTokenHelper.get_token(key_id: key_id, private_key: private_key)
|
|
25
|
+
|
|
26
|
+
response = client.post("/public/v1/application/#{package_name}/version/#{version_id}/aab") do |req|
|
|
24
27
|
req.headers['Public-Token'] = token
|
|
25
28
|
req.body = {
|
|
26
29
|
file: Faraday::UploadIO.new(aab_path, 'application/octet-stream')
|
|
27
30
|
}
|
|
28
31
|
end
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
end
|
|
33
|
+
check_response!(response, 'AAB')
|
|
34
|
+
UI.success("AAB uploaded successfully!")
|
|
35
|
+
rescue Faraday::Error => e
|
|
36
|
+
UI.user_error!("Failed to upload AAB: #{e.message}")
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
def self.publish_apk(
|
|
38
|
-
UI.
|
|
39
|
+
def self.publish_apk(key_id:, private_key:, package_name:, version_id:, services_type:, is_main_apk:, apk_path:)
|
|
40
|
+
UI.user_error!("APK file not found: #{apk_path}") unless File.file?(apk_path)
|
|
41
|
+
UI.message("Uploading APK to RuStore...")
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
token = RustoredTokenHelper.get_token(key_id: key_id, private_key: private_key)
|
|
44
|
+
|
|
45
|
+
response = client.post("/public/v1/application/#{package_name}/version/#{version_id}/apk") do |req|
|
|
41
46
|
req.headers['Public-Token'] = token
|
|
42
47
|
|
|
43
|
-
req.params['servicesType'] = services_type
|
|
48
|
+
req.params['servicesType'] = services_type if services_type
|
|
44
49
|
req.params['isMainApk'] = is_main_apk
|
|
45
50
|
|
|
46
51
|
req.body = {
|
|
@@ -48,12 +53,20 @@ module Fastlane
|
|
|
48
53
|
}
|
|
49
54
|
end
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
check_response!(response, 'APK')
|
|
57
|
+
UI.success("APK uploaded successfully!")
|
|
58
|
+
rescue Faraday::Error => e
|
|
59
|
+
UI.user_error!("Failed to upload APK: #{e.message}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.check_response!(response, artifact)
|
|
63
|
+
body = response.body.kind_of?(Hash) ? response.body : {}
|
|
64
|
+
return if response.success? && body['code'] == 'OK'
|
|
65
|
+
|
|
66
|
+
error = body['message'] || body['code'] || 'Unknown RuStore response'
|
|
67
|
+
UI.user_error!("Failed to upload #{artifact}: #{response.status} - #{error}")
|
|
56
68
|
end
|
|
69
|
+
private_class_method(:check_response!)
|
|
57
70
|
end
|
|
58
71
|
end
|
|
59
72
|
end
|
|
@@ -6,12 +6,21 @@ module Fastlane
|
|
|
6
6
|
def self.common_options
|
|
7
7
|
[
|
|
8
8
|
FastlaneCore::ConfigItem.new(
|
|
9
|
-
key: :
|
|
10
|
-
env_name: "
|
|
11
|
-
description: "The
|
|
9
|
+
key: :key_id,
|
|
10
|
+
env_name: "RUSTORE_KEY_ID",
|
|
11
|
+
description: "The key id for authenticating with RuStore",
|
|
12
12
|
optional: false,
|
|
13
13
|
type: String
|
|
14
14
|
),
|
|
15
|
+
FastlaneCore::ConfigItem.new(
|
|
16
|
+
key: :private_key,
|
|
17
|
+
env_name: "RUSTORE_PRIVATE_KEY",
|
|
18
|
+
description: "The private key for authenticating with RuStore",
|
|
19
|
+
optional: false,
|
|
20
|
+
type: String,
|
|
21
|
+
sensitive: true,
|
|
22
|
+
display_in_shell: false
|
|
23
|
+
),
|
|
15
24
|
FastlaneCore::ConfigItem.new(
|
|
16
25
|
key: :package_name,
|
|
17
26
|
env_name: "RUSTORE_PACKAGE_NAME",
|
|
@@ -24,7 +33,7 @@ module Fastlane
|
|
|
24
33
|
env_name: "RUSTORE_VERSION_ID",
|
|
25
34
|
description: "The version id of the app you want to publish",
|
|
26
35
|
optional: false,
|
|
27
|
-
type:
|
|
36
|
+
type: Integer
|
|
28
37
|
)
|
|
29
38
|
]
|
|
30
39
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'time'
|
|
6
|
+
require 'faraday'
|
|
7
|
+
require 'faraday_middleware'
|
|
8
|
+
|
|
9
|
+
module Fastlane
|
|
10
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?(:UI)
|
|
11
|
+
|
|
12
|
+
module Helper
|
|
13
|
+
class RustoredTokenHelper
|
|
14
|
+
BASE_URL = 'https://public-api.rustore.ru'
|
|
15
|
+
|
|
16
|
+
def self.client
|
|
17
|
+
@client ||= Faraday.new(url: BASE_URL) do |f|
|
|
18
|
+
f.request(:json)
|
|
19
|
+
f.response(:json)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.gen_token_signature(key_id:, private_key:, timestamp:)
|
|
24
|
+
private_key_data = Base64.strict_decode64(private_key)
|
|
25
|
+
rsa_private_key = OpenSSL::PKey::RSA.new(private_key_data)
|
|
26
|
+
|
|
27
|
+
message_to_sign = key_id + timestamp
|
|
28
|
+
|
|
29
|
+
digest = OpenSSL::Digest.new('SHA512')
|
|
30
|
+
signature_bytes = rsa_private_key.sign(digest, message_to_sign)
|
|
31
|
+
Base64.strict_encode64(signature_bytes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.get_token(key_id:, private_key:)
|
|
35
|
+
timestamp = Time.now.utc.iso8601(3)
|
|
36
|
+
sig = gen_token_signature(key_id: key_id, private_key: private_key, timestamp: timestamp)
|
|
37
|
+
|
|
38
|
+
response = client.post('/public/auth') do |req|
|
|
39
|
+
req.body = {
|
|
40
|
+
keyId: key_id,
|
|
41
|
+
signature: sig,
|
|
42
|
+
timestamp: timestamp
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
body = response.body.kind_of?(Hash) ? response.body : {}
|
|
47
|
+
token = body.dig('body', 'jwe')
|
|
48
|
+
return token if response.success? && body['code'] == 'OK' && !token.to_s.empty?
|
|
49
|
+
|
|
50
|
+
UI.user_error!("Failed to get token: #{response.status} - #{body['message'] || body['code'] || 'Unknown RuStore response'}")
|
|
51
|
+
rescue ArgumentError, OpenSSL::PKey::PKeyError => e
|
|
52
|
+
UI.user_error!("Invalid RuStore private key: #{e.message}")
|
|
53
|
+
rescue Faraday::Error => e
|
|
54
|
+
UI.user_error!("Failed to get RuStore token: #{e.message}")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fastlane-plugin-rustored
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 06ED
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.8'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.8'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: faraday_middleware
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.2'
|
|
12
40
|
email: hsbest123@gmail.com
|
|
13
41
|
executables: []
|
|
14
42
|
extensions: []
|
|
@@ -21,6 +49,7 @@ files:
|
|
|
21
49
|
- lib/fastlane/plugin/rustored/actions/rustored_publish_apk_action.rb
|
|
22
50
|
- lib/fastlane/plugin/rustored/helper/rustored_helper.rb
|
|
23
51
|
- lib/fastlane/plugin/rustored/helper/rustored_options.rb
|
|
52
|
+
- lib/fastlane/plugin/rustored/helper/rustored_token_helper.rb
|
|
24
53
|
- lib/fastlane/plugin/rustored/version.rb
|
|
25
54
|
homepage: https://github.com/06ED/fastlane-rustored
|
|
26
55
|
licenses:
|