fastlane-plugin-huawei_appgallery 0.1.0 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 814eeb8cd0cb8757080358a1015311e60b495356acec1e42c7f89a0f19fa03c1
4
- data.tar.gz: e4228b3f43c4fbcd648428b79fdd0b7ba776a2411474e37c36ebc9ad236e849f
3
+ metadata.gz: 9760586a7e5237109bb4691be3130957b5300b2660017fe265ae285c3fd44ae3
4
+ data.tar.gz: f45ad17400d6908fa19d0f00a0ee0d808f8486f6d345195d9390203b62ba02dc
5
5
  SHA512:
6
- metadata.gz: c9649220e125e2b92562640e9c79dc7ec3596052fc18c11f9fd5f8e2134d57996e92dc811c6f59817771a4edaa4c7d787de9676e9f6da942c10769fe6a5bcb1d
7
- data.tar.gz: 870cccb67d99ec822447dbe7a6ef1c2a8d27b15c1e6ab850c5908b98edcbb4ecc8fc07babfac8042a0483c73eb6452b1133e1d3d4e730e5110c0d4423ce9214a
6
+ metadata.gz: 77700c3d8da30622d95c083de9b8be3b3b9acbc1018afd433d3be113d67005cad95498672536e9c7314bfa2c6be831a7c4f8d1e5828741c62d767b820989a03b
7
+ data.tar.gz: e5a107781d55369756906d6126b1c7e037d197a5e875297e8318258326083fe04ecd5db9cdc22997eec8b329cc4aed28166b7ad858cbf8b202efb48ff5bfc6a7
data/README.md CHANGED
@@ -14,10 +14,8 @@ fastlane add_plugin huawei_appgallery
14
14
 
15
15
  Plugin to deploy an app to the Huawei AppGallery. Updates the release notes, uploads an APK and submits the new version for review.
16
16
 
17
- **Note:** To authenticate, you need to create the signature by your own.
18
- Example code: https://developer.huawei.com/consumer/en/service/hms/catalog/publishingAPI.html?page=hmssdk_appGalleryConnect_devguide (Authentication by secret key)
19
-
20
- Pull requests are welcome. ;)
17
+ To create client id and access token, go to [AppGalleryConnect](https://developer.huawei.com/consumer/en/service/josp/agc/index.html).
18
+ Navigate to "Users and permissions", than click on the left on "Connect API" (in section "Api Key"). Now you create and manage your client ids and secrets.
21
19
 
22
20
  ## Example
23
21
 
@@ -1,14 +1,13 @@
1
1
  require 'fastlane/action'
2
- require_relative '../helper/huawei_appgallery_helper'
3
2
 
4
3
  module Fastlane
5
4
  module Actions
6
5
  class HuaweiAppgalleryAction < Action
7
6
  def self.run(params)
8
- cookie = Helper::HuaweiAppgalleryHelper.request_cookie(params[:client_id], params[:time], params[:signature])
9
- Helper::HuaweiAppgalleryHelper.update_release_notes(cookie, params[:app_id], params[:release_notes])
10
- Helper::HuaweiAppgalleryHelper.upload_apk(cookie, params[:app_id], params[:apk_path])
11
- Helper::HuaweiAppgalleryHelper.submit_app(cookie, params[:app_id])
7
+ access_token = Helper::HuaweiAppgalleryHelperV2.request_access_token(params[:client_id], params[:client_secret])
8
+ Helper::HuaweiAppgalleryHelperV2.update_release_notes(params[:client_id], access_token, params[:app_id], params[:release_notes])
9
+ Helper::HuaweiAppgalleryHelperV2.upload_apk(params[:client_id], access_token, params[:app_id], params[:apk_path])
10
+ Helper::HuaweiAppgalleryHelperV2.submit_app(params[:client_id], access_token, params[:app_id])
12
11
  UI.message('Finished!')
13
12
  end
14
13
 
@@ -35,14 +34,9 @@ module Fastlane
35
34
  description: "Client ID of an AppGallery Connect API client",
36
35
  optional: false,
37
36
  type: String),
38
- FastlaneCore::ConfigItem.new(key: :time,
39
- env_name: "HUAWEI_APPGALLERY_TIME",
40
- description: "Time in milliseconds since 1970, which was used to create the signature",
41
- optional: false,
42
- type: String),
43
- FastlaneCore::ConfigItem.new(key: :signature,
44
- env_name: "HUAWEI_APPGALLERY_SIGNATURE",
45
- description: "Signature which needs to be created by your own. Example code: https://developer.huawei.com/consumer/en/service/hms/catalog/publishingAPI.html?page=hmssdk_appGalleryConnect_devguide",
37
+ FastlaneCore::ConfigItem.new(key: :client_secret,
38
+ env_name: "HUAWEI_APPGALLERY_CLIENT_SECRET",
39
+ description: "Client Secret of an AppGallery Connect API client",
46
40
  optional: false,
47
41
  type: String),
48
42
  FastlaneCore::ConfigItem.new(key: :app_id,
@@ -0,0 +1,225 @@
1
+ require 'fastlane_core/ui/ui'
2
+
3
+ module Fastlane
4
+ UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
+
6
+ module Helper
7
+ class HuaweiAppgalleryHelperV2
8
+
9
+
10
+ def self.request_access_token(client_id, client_secret)
11
+ UI.message("Requesting access token from Huawei AppGallery ...")
12
+
13
+ content = {'grant_type' => 'client_credentials', 'client_id' => client_id, 'client_secret' => client_secret}
14
+
15
+ result = Net::HTTP.post(
16
+ URI("https://connect-api.cloud.huawei.com/api/oauth2/v1/token"),
17
+ content.to_json.encode('utf-8'),
18
+ "Content-Type" => "application/json"
19
+ )
20
+
21
+ if result.code.to_i != 200
22
+ UI.user_error!("Could not get access token from app gallery api. (HTTP #{result.code} - #{result.message})")
23
+ end
24
+
25
+ result_json = JSON.parse(result.body)
26
+ access_token = result_json['access_token']
27
+ access_token
28
+ end
29
+
30
+
31
+ def self.update_release_notes(client_id, access_token, app_id, release_notes)
32
+ release_notes.each do |lang, notes|
33
+ UI.message("Updating release notes for language #{lang} ...")
34
+
35
+ content = {'lang' => lang, 'newFeatures' => notes}
36
+
37
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-language-info?appId=#{app_id}")
38
+ http = Net::HTTP.new(uri.host, uri.port)
39
+ http.use_ssl = true
40
+ request = Net::HTTP::Put.new(uri)
41
+ request.body = content.to_json.encode('utf-8')
42
+ request['Content-Type'] = "application/json"
43
+ request['Authorization'] = "Bearer #{access_token}"
44
+ request['client_id'] = client_id
45
+ result = http.request(request)
46
+
47
+ if result.code.to_i != 200
48
+ UI.user_error!("Could not update release notes for #{lang}. (HTTP #{result.code} - #{result.message})")
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ def self.upload_apk(client_id, access_token, app_id, apk_path)
55
+ # get upload url and auth code
56
+ upload_url_result = self.get_upload_url(client_id, access_token, app_id)
57
+ auth_code = upload_url_result['authCode']
58
+ upload_url = upload_url_result['uploadUrl']
59
+
60
+ # upload apk
61
+ server_apk_url = self.upload_apk_to_api(auth_code, upload_url, apk_path)
62
+
63
+ # update app file information
64
+ self.update_app_file_information(client_id, access_token, app_id, server_apk_url)
65
+ end
66
+
67
+
68
+ def self.get_upload_url(client_id, access_token, app_id)
69
+ UI.message("Obtaining upload url ...")
70
+
71
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/upload-url?appId=#{app_id}&suffix=apk")
72
+ http = Net::HTTP.new(uri.host, uri.port)
73
+ http.use_ssl = true
74
+ request = Net::HTTP::Get.new(uri)
75
+ request['Authorization'] = "Bearer #{access_token}"
76
+ request['client_id'] = client_id
77
+ result = http.request(request)
78
+
79
+ if result.code.to_i != 200
80
+ UI.user_error!("Could not apk upload url from app gallery api. (HTTP #{result.code} - #{result.message})")
81
+ end
82
+
83
+ # result json containing "authCode", "uploadUrl"
84
+ result_json = JSON.parse(result.body)
85
+ result_json
86
+ end
87
+
88
+
89
+ def self.upload_apk_to_api(auth_code, upload_url, apk_path)
90
+ UI.message("Uploading apk to #{upload_url} ...")
91
+
92
+ boundary = "-----------------755754302457647"
93
+
94
+ uri = URI(upload_url)
95
+ http = Net::HTTP.new(uri.host, uri.port)
96
+ http.use_ssl = true
97
+ request = Net::HTTP::Post.new(uri)
98
+ request['Accept'] = 'application/json'
99
+ request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
100
+
101
+ post_body = []
102
+ # add auth code
103
+ post_body << "--#{boundary}\r\n"
104
+ post_body << "Content-Disposition: form-data; name=\"authCode\"\r\n\r\n"
105
+ post_body << auth_code
106
+ post_body << "\r\n"
107
+ # add file count
108
+ post_body << "--#{boundary}\r\n"
109
+ post_body << "Content-Disposition: form-data; name=\"fileCount\"\r\n\r\n"
110
+ post_body << "1"
111
+ post_body << "\r\n"
112
+ # add parse type
113
+ post_body << "--#{boundary}\r\n"
114
+ post_body << "Content-Disposition: form-data; name=\"parseType\"\r\n\r\n"
115
+ post_body << "0"
116
+ post_body << "\r\n"
117
+ # add apk
118
+ post_body << "--#{boundary}\r\n"
119
+ post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"release.apk\"\r\n"
120
+ post_body << "Content-Type: multipart/form-data\r\n\r\n"
121
+ post_body << File.read(apk_path).encode('utf-8')
122
+ post_body << "\r\n"
123
+
124
+ post_body << "--#{boundary}--\r\n"
125
+ request.body = post_body.join
126
+ result = http.request(request)
127
+
128
+ if result.code.to_i != 200
129
+ UI.user_error!("Could not upload apk to gallery api. (HTTP #{result.code} - #{result.message})")
130
+ end
131
+
132
+ # example response
133
+ # {"result":{"UploadFileRsp":{"fileInfoList":[{"fileDestUlr":"https://developerfile7.hicloud.com/FileServer/getFile/7/appapktemp/20200727/appapk/000/000/410/0890049000000000410.20200727174607.32489040188051103716016542322538:20200727174634:2500:AD10C3C4138E988C7A1C3680440C84559E2DD6184DF5A2E1C457C23868E5F277.apk","size":86803266}],"ifSuccess":1},"resultCode":"0"}}
134
+ result_json = JSON.parse(result.body)
135
+ json_result_obj = result_json['result']
136
+ json_upload_file_rsp = json_result_obj['UploadFileRsp']
137
+ json_file_info_list = json_upload_file_rsp['fileInfoList']
138
+ json_file_info = json_file_info_list.first()
139
+ file_dest_url = json_file_info['fileDestUlr'] # ulr is correct
140
+
141
+ file_dest_url
142
+ end
143
+
144
+
145
+ def self.update_app_file_information(client_id, access_token, app_id, apk_server_path)
146
+ UI.message("Updating app file information ...")
147
+
148
+ content = {
149
+ 'fileType' => 5, # type 5 = RPK or APK
150
+ 'files' => [{
151
+ 'fileName' => 'naviki-release.apk',
152
+ 'fileDestUrl' => apk_server_path
153
+ }]
154
+ }
155
+
156
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-file-info?appId=#{app_id}")
157
+ http = Net::HTTP.new(uri.host, uri.port)
158
+ http.use_ssl = true
159
+ request = Net::HTTP::Put.new(uri)
160
+ request.body = content.to_json.encode('utf-8')
161
+ request['Content-Type'] = "application/json"
162
+ request['Authorization'] = "Bearer #{access_token}"
163
+ request['client_id'] = client_id
164
+ result = http.request(request)
165
+
166
+ if result.code.to_i != 200
167
+ UI.user_error!("Could not update app file information. (HTTP #{result.code} - #{result.message})")
168
+ end
169
+
170
+ result_json = JSON.parse(result.body)
171
+ json_ret = result_json['ret']
172
+ UI.message("app-file-info ret: #{json_ret}")
173
+ end
174
+
175
+
176
+ def self.submit_app(client_id, access_token, app_id)
177
+ UI.message('Submitting app for review ...')
178
+ result_code = launch_submit_request(client_id, access_token, app_id)
179
+ if result_code == 204144660 # https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-References/agcapi-returncode_v2
180
+ UI.message('Submission failed, waiting some time and try again.')
181
+ sleep(120)
182
+ if launch_submit_request(client_id, access_token, app_id) != 0
183
+ UI.user_error!('Submission failed again.')
184
+ end
185
+ elsif result_code != 0
186
+ UI.user_error!('Submission failed.')
187
+ end
188
+ end
189
+
190
+
191
+ def self.launch_submit_request(client_id, access_token, app_id)
192
+ # should be in format yyyy-MM-dd'T'HH:mm:ssZZ, must be escaped to be GET param
193
+ # https://apidock.com/ruby/DateTime/strftime
194
+ # https://www.shortcutfoo.com/app/dojos/ruby-date-format-strftime/cheatsheet
195
+ release_time = CGI::escape(Time.now.utc.strftime("%FT%T%z")) # as soon as possible
196
+ UI.message("Use release time #{release_time}")
197
+
198
+ uri = URI("https://connect-api.cloud.huawei.com/api/publish/v2/app-submit?appId=#{app_id}&releaseTime=#{release_time}")
199
+ http = Net::HTTP.new(uri.host, uri.port)
200
+ http.use_ssl = true
201
+ request = Net::HTTP::Post.new(uri)
202
+ request['Content-Type'] = "application/json"
203
+ request['Accept'] = 'application/json'
204
+ request['Authorization'] = "Bearer #{access_token}"
205
+ request['client_id'] = client_id
206
+ result = http.request(request)
207
+
208
+ if result.code.to_i != 200
209
+ UI.user_error!("Could not submit app for review. (HTTP #{result.code} - #{result.message})")
210
+ end
211
+
212
+ result_json = JSON.parse(result.body)
213
+ json_ret = result_json['ret']
214
+ UI.message("app-submit ret: #{json_ret}")
215
+ result_code = json_ret['code']
216
+
217
+ if result_code == 0
218
+ UI.message('Submission was successfull!')
219
+ end
220
+
221
+ return result_code
222
+ end
223
+ end
224
+ end
225
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module HuaweiAppgallery
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-huawei_appgallery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arne Kaiser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-15 00:00:00.000000000 Z
11
+ date: 2021-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -146,7 +146,7 @@ files:
146
146
  - README.md
147
147
  - lib/fastlane/plugin/huawei_appgallery.rb
148
148
  - lib/fastlane/plugin/huawei_appgallery/actions/huawei_appgallery_action.rb
149
- - lib/fastlane/plugin/huawei_appgallery/helper/huawei_appgallery_helper.rb
149
+ - lib/fastlane/plugin/huawei_appgallery/helper/huawei_appgallery_helper_v2.rb
150
150
  - lib/fastlane/plugin/huawei_appgallery/version.rb
151
151
  homepage: https://github.com/arnekaiser/fastlane-plugin-huawei_appgallery
152
152
  licenses:
@@ -167,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubyforge_project:
171
- rubygems_version: 2.7.6.2
170
+ rubygems_version: 3.1.2
172
171
  signing_key:
173
172
  specification_version: 4
174
173
  summary: Plugin to deploy an app to the Huawei AppGallery
@@ -1,158 +0,0 @@
1
- require 'fastlane_core/ui/ui'
2
-
3
- module Fastlane
4
- UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
5
-
6
- module Helper
7
- class HuaweiAppgalleryHelper
8
- def self.request_cookie(client_id, time, signature)
9
- UI.message("Requesting cookie from Huawei AppGallery ...")
10
- content = { 'key_string' => { 'clientId' => client_id, 'time' => time, 'sign' => signature } }
11
- result = Net::HTTP.post(
12
- URI('https://connect-api.cloud.huawei.com/api/common/v1/connect'),
13
- content.to_json.encode('utf-8'),
14
- "Accept" => "application/json"
15
- )
16
- cookie = result['set-cookie']
17
- if cookie.nil?
18
- UI.user_error!("Authentication failed: #{result.body}")
19
- end
20
- cookie.split('; ')[0]
21
- end
22
-
23
- def self.update_release_notes(cookie, app_id, release_notes)
24
- release_notes.each do |lang, notes|
25
- UI.message("Updating release notes for language #{lang} ...")
26
-
27
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/appInfo/#{app_id}?lang=#{lang}")
28
- http = Net::HTTP.new(uri.host, uri.port)
29
- http.use_ssl = true
30
- request = Net::HTTP::Get.new(uri)
31
- request['Cookie'] = cookie
32
- request['Accept'] = 'application/json'
33
-
34
- result = http.request(request)
35
-
36
- if result.code.to_i != 200
37
- UI.user_error!("Cannot retrieve language information for language #{lang}!")
38
- end
39
-
40
- # get values for mandatory parameters
41
- result_json = JSON.parse(result.body)
42
- app_name = result_json['languages'][0]['appName']
43
- app_desc = result_json['languages'][0]['appDesc']
44
- brief_info = result_json['languages'][0]['briefInfo']
45
-
46
- if app_name.nil? || app_desc.nil? || brief_info.nil?
47
- UI.user_error!("Cannot retrieve language information for language #{lang}!")
48
- end
49
-
50
- # set new release notes
51
- content = {
52
- 'appName' => app_name,
53
- 'appDesc' => app_desc,
54
- 'briefInfo' => brief_info,
55
- 'newFeatures' => notes
56
- }
57
-
58
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/languageInfo/#{app_id}/#{lang}")
59
- http = Net::HTTP.new(uri.host, uri.port)
60
- http.use_ssl = true
61
- request = Net::HTTP::Put.new(uri)
62
- request.body = content.to_json.encode('utf-8')
63
- request['Cookie'] = cookie
64
- request['Content-Type'] = 'application/json'
65
- result = http.request(request)
66
- if result.code.to_i != 200
67
- UI.user_error!("Cannot update language information for language #{lang}!")
68
- end
69
- end
70
- end
71
-
72
- def self.upload_apk(cookie, app_id, apk_path)
73
- unless File.file?(apk_path)
74
- UI.user_error!("Cannot read apk at: #{apk_path}")
75
- end
76
-
77
- # obtain upload url
78
- UI.message("Obtaining upload url ...")
79
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/uploadUrl?suffix=apk")
80
- http = Net::HTTP.new(uri.host, uri.port)
81
- http.use_ssl = true
82
- request = Net::HTTP::Get.new(uri)
83
- request['Cookie'] = cookie
84
- request['Accept'] = 'application/json'
85
- result = http.request(request)
86
- result_json = JSON.parse(result.body)
87
- upload_url = result_json['uploadUrl'] # this is the upload server
88
- auth_code = result_json['authCode']
89
- if result.code.to_i != 200 || upload_url.nil? || auth_code.nil?
90
- UI.user_error!("Cannot obtain upload url!")
91
- end
92
-
93
- # upload apk
94
- UI.message("Uploading apk to #{upload_url} ...")
95
- boundary = "755754302457647"
96
- uri = URI("https://#{upload_url}/api/publish/v1/uploadFile")
97
- http = Net::HTTP.new(uri.host, uri.port)
98
- http.use_ssl = true
99
- request = Net::HTTP::Post.new(uri)
100
- request['Cookie'] = cookie
101
- request['Accept'] = 'application/json'
102
- request['Content-Type'] = "multipart/form-data, boundary=#{boundary}"
103
-
104
- post_body = []
105
- # add the auth code
106
- post_body << "--#{boundary}\r\n"
107
- post_body << "Content-Disposition: form-data; name=\"authCode\"\r\n\r\n"
108
- post_body << auth_code
109
- # add the apk
110
- post_body << "\r\n--#{boundary}\r\n"
111
- post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"release.apk\"\r\n"
112
- post_body << "Content-Type: multipart/form-data\r\n\r\n"
113
- post_body << File.read(apk_path).encode('utf-8')
114
- post_body << "\r\n--#{boundary}--\r\n"
115
- request.body = post_body.join
116
-
117
- result = http.request(request)
118
- result_json = JSON.parse(result.body)
119
- upload_url = result_json['uploadUrl']
120
-
121
- # update app file informnation
122
- UI.message("Updating app file information ...")
123
- content = {
124
- 'type' => 5, # type 5 = RPK or APK
125
- 'data' => upload_url
126
- }
127
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/mediaInfo/#{app_id}/en-US") # assumes that en-US is the default language
128
- http = Net::HTTP.new(uri.host, uri.port)
129
- http.use_ssl = true
130
- request = Net::HTTP::Put.new(uri)
131
- request.body = content.to_json.encode('utf-8')
132
- request['Cookie'] = cookie
133
- request['Content-Type'] = 'application/json'
134
- result = http.request(request)
135
- if result.code.to_i != 200
136
- UI.user_error!("Cannot upload apk!")
137
- end
138
- end
139
-
140
- def self.submit_app(cookie, app_id)
141
- UI.message('Submitting app for review ...')
142
- uri = URI("https://connect-api.cloud.huawei.com/api/publish/v1/submit/#{app_id}")
143
- http = Net::HTTP.new(uri.host, uri.port)
144
- http.use_ssl = true
145
- request = Net::HTTP::Post.new(uri)
146
- request['Cookie'] = cookie
147
- request['Accept'] = 'application/json'
148
- request['Content-Type'] = "application/json"
149
- content = { 'releaseTime' => (Time.now.to_f * 1000).to_i.to_s } # as soon as possible
150
- request.body = content.to_json.encode('utf-8')
151
- result = http.request(request)
152
- if result.code.to_i != 200
153
- UI.user_error!("Cannot submit app!")
154
- end
155
- end
156
- end
157
- end
158
- end