fastlane-plugin-amazon_app_submission 0.2.0 → 0.4.0

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: 91d110e787d9b378a8ee45632bc6437a3527c373be5e20a3af723fed43e71737
4
- data.tar.gz: eb8965c6f69a51b561dd42bcda3775f531597118bcd978ecb1832a67bc9402b4
3
+ metadata.gz: e0e6f5db2e79b36447f13ff25a3e8f8573cbdd273578ed7ede2f645f4a534b33
4
+ data.tar.gz: c76ce77ad9e439cf5d68a8b672c8e714488f5606f2cd27ff2958ba551a2d4e2a
5
5
  SHA512:
6
- metadata.gz: d38c40afad80cde84cd62c6a2a25b166f4ff56963072325638ca1bf720f417a0ad2d397f3c8d8384986f9cc9aa075e05948a45f2585f5157dc569582342aeebe
7
- data.tar.gz: bc499eda4df532380def700a045bbb186a80cf7a5c6a91232698301ac2671ad29d68ad9e05b8ccec4d264351488af704fe765796e51d27f98c053a0e3ae7a213
6
+ metadata.gz: 3996ee47b85cb300b2d5ea07216960aa7fce672ff0dab042216018d83895b127f50b04bff9dcac283a2766a3ae3b17bd4ce475b21ba23a687d88f30313c06211
7
+ data.tar.gz: a9ebb48964cade0bfa36a415a888629ee51710575b99baf731bffffaab9e580ddf5f0ea6107c34803c1283473f6a1e6ceff15f940aabc90215df3f5201998bbf
data/README.md CHANGED
@@ -1 +1,70 @@
1
- # amazon-app-store-submission
1
+ # fastlane-plugin-amazon-app-submission
2
+
3
+ ## Getting Started
4
+
5
+ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `amazon_app_submission`, add it to your project by running:
6
+
7
+ ```bash
8
+ fastlane add_plugin amazon_app_submission
9
+ ```
10
+
11
+ ## About amazon_app_submission
12
+
13
+ * Project link on rubygems [fastlane-plugin-amazon_app_submission](https://rubygems.org/gems/fastlane-plugin-amazon_app_submission)
14
+ * Upload the apk to the Amazon Appstore using the [App Submission API](https://developer.amazon.com/docs/app-submission-api/overview.html).
15
+ * App Submission API Reference is [App Submission RESTFUL API](https://developer.amazon.com/docs/app-submission-api/appsub-api-ref.html).
16
+
17
+ ## Usage
18
+
19
+ Following the [guide](https://developer.amazon.com/docs/app-submission-api/auth.html), you will need to generate `client_id` and `client_secret` to access the console in advance.
20
+
21
+ For `app_id` you can get it from Amazon app dashboard
22
+ Please set the apk path to `apk_path` field
23
+
24
+ Call `amazon_app_submission` in your Fastfile.
25
+
26
+ ```ruby
27
+ amazon_app_submission(
28
+ client_id: "<CLIENT_ID>",
29
+ client_secret: "<CLIENT_SECRET>",
30
+ app_id: "<APP_ID>",
31
+ # Optional
32
+ apk_path: "<APK_PATH>",
33
+ upload_apk: true,
34
+ changelogs_path: "<CHANGELOG_PATH>",
35
+ upload_changelogs: false,
36
+ submit_for_review: false
37
+ )
38
+ ```
39
+
40
+ | param | default value | optional | description
41
+ |:----------|:-----------:|:-----------:|:-----------:|
42
+ client_id | - | false | getting client id from Amazon developer console dashboard
43
+ client_secret | - | false | getting client secret from Amazon developer console dashboard
44
+ app_id | - | false | getting app id from Amazon developer console dashboard
45
+ apk_path | - | true | link where you storing the release apk
46
+ upload_apk | true | true | set this to false to not upload an apk. can be used to only upload changelogs
47
+ changelogs_path | "" | true | setting the folder path where you have the change logs with different file for each language, if language file not found it will use default.txt
48
+ upload_changelogs | false | true | updating the change logs for the upcoming version
49
+ submit_for_review | false | true | submit the uploaded APK to the store
50
+
51
+ * changelogs folder files name should be:
52
+
53
+ | Language | File name
54
+ |:----------|:-----------:|
55
+ English-US | en-US.txt
56
+ English-British | en-GB.txt
57
+ English-Australia | en-AU.txt
58
+ English-India | en-IN.txt
59
+ Italian |it-IT.txt
60
+ French | fr-FR.txt
61
+ Spanish | es-ES.txt
62
+ Spanish-Mexican | es-MX.txt
63
+ Other | default.txt
64
+
65
+ ## Testing
66
+
67
+ For testing the plugin locally you have to get `client_id`, `client_secret`, `app_id` and `apk_path` in fastlane/Fastfile
68
+ please check Usage step to see how you can get them.
69
+
70
+ Then call `bundle exec fastlane test` in your terminal
@@ -1,4 +1,4 @@
1
- require 'fastlane/action'
1
+ require 'fastlane/action'
2
2
  require_relative '../helper/amazon_app_submission_helper'
3
3
 
4
4
  module Fastlane
@@ -11,35 +11,58 @@ module Fastlane
11
11
 
12
12
  if token.nil?
13
13
  UI.message("Cannot retrieve token, please check your client ID and client secret")
14
- else
15
- UI.message("the token is #{token}")
16
- end
14
+ end
17
15
 
16
+ UI.message("Getting current edit")
18
17
  current_edit_id, edit_eTag = Helper::AmazonAppSubmissionHelper.open_edit(token, params[:app_id])
19
-
18
+
20
19
  if current_edit_id.nil?
20
+ UI.message("Current edit not found, creating a new edit")
21
21
  Helper::AmazonAppSubmissionHelper.create_new_edit(token, params[:app_id])
22
22
  current_edit_id, edit_eTag = Helper::AmazonAppSubmissionHelper.open_edit(token, params[:app_id])
23
23
  end
24
-
25
- current_apk_id = Helper::AmazonAppSubmissionHelper.get_current_apk_id(token, params[:app_id], current_edit_id)
26
24
 
27
- current_apk_eTag = Helper::AmazonAppSubmissionHelper.get_current_apk_etag(token, params[:app_id], current_edit_id, current_apk_id)
25
+ if current_edit_id.nil?
26
+ UI.error("Creating new edit failed!")
27
+ return
28
+ end
29
+
30
+ if params[:upload_apk]
31
+ UI.message("Get current apk id")
32
+ current_apk_id = Helper::AmazonAppSubmissionHelper.get_current_apk_id(token, params[:app_id], current_edit_id)
28
33
 
29
- replace_apk_response_code = Helper::AmazonAppSubmissionHelper.replaceExistingApk(token, params[:app_id], current_edit_id, current_apk_id, current_apk_eTag, params[:apk_path])
30
-
31
- if replace_apk_response_code === 200
32
- Helper::AmazonAppSubmissionHelper.commit_edit(token, params[:app_id], current_edit_id, edit_eTag)
33
- end
34
+ UI.message("Get current apk ETag")
35
+ current_apk_eTag = Helper::AmazonAppSubmissionHelper.get_current_apk_etag(token, params[:app_id], current_edit_id, current_apk_id)
34
36
 
37
+ UI.message("Replacing the apk with apk from #{params[:apk_path]}")
38
+ replace_apk_response_code, replace_apk_response = Helper::AmazonAppSubmissionHelper.replaceExistingApk(token, params[:app_id], current_edit_id, current_apk_id, current_apk_eTag, params[:apk_path])
39
+ end
40
+
41
+ if params[:upload_changelogs]
42
+ UI.message("Updating the changelogs")
43
+ Helper::AmazonAppSubmissionHelper.update_listings( token, params[:app_id],current_edit_id, params[:changelogs_path], params[:changelogs_path])
44
+ end
45
+
46
+ if params[:upload_apk]
47
+ if replace_apk_response_code == '200'
48
+ if params[:submit_for_review]
49
+ UI.message("Submitting to Amazon app store")
50
+ Helper::AmazonAppSubmissionHelper.commit_edit(token, params[:app_id], current_edit_id, edit_eTag)
51
+ end
52
+ else
53
+ UI.message("Amazon app submission failed at replacing the apk error code #{replace_apk_response_code} and error respones #{replace_apk_response}")
54
+ return
55
+ end
56
+ end
57
+ UI.message("Amazon app submission finished successfully!")
35
58
  end
36
59
 
37
60
  def self.description
38
- "test"
61
+ "Fastlane plugin for Amazon App Submissions"
39
62
  end
40
63
 
41
64
  def self.authors
42
- ["mohammedhemaid"]
65
+ ["mohammedhemaid", "saschagraeff"]
43
66
  end
44
67
 
45
68
  def self.return_value
@@ -48,7 +71,7 @@ module Fastlane
48
71
 
49
72
  def self.details
50
73
  # Optional:
51
- "test"
74
+ "Fastlane plugin for Amazon App Submissions"
52
75
  end
53
76
 
54
77
  def self.available_options
@@ -58,7 +81,7 @@ module Fastlane
58
81
  description: "Amazon App Submission Client ID",
59
82
  optional: false,
60
83
  type: String),
61
-
84
+
62
85
  FastlaneCore::ConfigItem.new(key: :client_secret,
63
86
  env_name: "AMAZON_APP_SUBMISSION_CLIENT_SECRET",
64
87
  description: "Amazon App Submission Client Secret",
@@ -71,12 +94,41 @@ module Fastlane
71
94
  optional: false,
72
95
  type: String),
73
96
 
97
+ FastlaneCore::ConfigItem.new(key: :upload_apk,
98
+ env_name: "AMAZON_APP_SUBMISSION_UPLOAD_APK",
99
+ description: "Amazon App Submission upload apk file",
100
+ default_value: true,
101
+ optional: true,
102
+ type: Boolean),
103
+
74
104
  FastlaneCore::ConfigItem.new(key: :apk_path,
75
105
  env_name: "AMAZON_APP_SUBMISSION_APK_PATH",
76
106
  description: "Amazon App Submission APK Path",
77
- optional: false,
78
- type: String)
79
-
107
+ default_value: "",
108
+ optional: true,
109
+ type: String),
110
+
111
+ FastlaneCore::ConfigItem.new(key: :changelogs_path,
112
+ env_name: "AMAZON_APP_SUBMISSION_CHANGELOGS_PATH",
113
+ description: "Amazon App Submission changelogs path",
114
+ default_value: "",
115
+ optional: true,
116
+ type: String),
117
+
118
+ FastlaneCore::ConfigItem.new(key: :upload_changelogs,
119
+ env_name: "AMAZON_APP_SUBMISSION_SKIP_UPLOAD_CHANGELOGS",
120
+ description: "Amazon App Submission upload changelogs",
121
+ default_value: false,
122
+ optional: true,
123
+ type: Boolean),
124
+
125
+ FastlaneCore::ConfigItem.new(key: :submit_for_review,
126
+ env_name: "AMAZON_APP_SUBMISSION_SUBMIT_FOR_REVIEW",
127
+ description: "Amazon App Submission submit for review",
128
+ default_value: false,
129
+ optional: true,
130
+ type: Boolean)
131
+
80
132
  # FastlaneCore::ConfigItem.new(key: :your_option,
81
133
  # env_name: "AMAZON_APP_SUBMISSION_YOUR_OPTION",
82
134
  # description: "A description of your option",
@@ -13,10 +13,6 @@ module Fastlane
13
13
 
14
14
  def self.get_token(client_id, client_secret)
15
15
  UI.important("Fetching app access token")
16
-
17
- UI.important("client_id #{client_id}")
18
- UI.important("client_secret #{client_secret}")
19
-
20
16
  uri = URI('https://api.amazon.com/auth/o2/token')
21
17
  http = Net::HTTP.new(uri.host, uri.port)
22
18
  http.use_ssl = true
@@ -26,14 +22,17 @@ module Fastlane
26
22
 
27
23
  res = http.request(req)
28
24
  result_json = JSON.parse(res.body)
29
-
30
25
  auth_token = "Bearer #{result_json['access_token']}"
31
26
 
27
+ if result_json['error'] == 'invalid_scope'
28
+ UI.message("It seems that the provided security profile is not attached to the App Submission API")
29
+ end
30
+
32
31
  return auth_token
33
32
  end
34
33
 
35
34
  def self.create_new_edit(token, app_id)
36
-
35
+
37
36
  create_edit_path = "/v1/applications/#{app_id}/edits"
38
37
  create_edit_url = BASE_URL + create_edit_path
39
38
 
@@ -48,12 +47,12 @@ module Fastlane
48
47
 
49
48
  res = http.request(req)
50
49
  current_edit = JSON.parse(res.body)
51
-
50
+
52
51
  return current_edit['id']
53
52
  end
54
-
53
+
55
54
  def self.open_edit(token, app_id)
56
-
55
+
57
56
  get_edit_path = "/v1/applications/#{app_id}/edits"
58
57
  get_edit_url = BASE_URL + get_edit_path
59
58
 
@@ -69,17 +68,15 @@ module Fastlane
69
68
  res = http.request(req)
70
69
  current_edit = JSON.parse(res.body)
71
70
 
72
- UI.message("eTag #{res.header['ETag']}")
73
-
74
71
  return current_edit['id'], res.header['ETag']
75
72
  end
76
73
 
77
- def self.get_current_apk_etag(token, app_id, edit_id, apk_id)
74
+ def self.get_current_apk_id(token, app_id, edit_id)
78
75
 
79
- get_apks_etag = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/#{apk_id}"
80
- get_apks_etag_url = BASE_URL + get_apks_etag
76
+ get_apks_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks"
77
+ get_apks_url = BASE_URL + get_apks_path
81
78
 
82
- uri = URI(get_apks_etag_url)
79
+ uri = URI(get_apks_url)
83
80
  http = Net::HTTP.new(uri.host, uri.port)
84
81
  http.use_ssl = true
85
82
  req = Net::HTTP::Get.new(
@@ -89,15 +86,20 @@ module Fastlane
89
86
  )
90
87
 
91
88
  res = http.request(req)
92
- return res.header['ETag']
89
+ if !res.body.nil?
90
+ apks = JSON.parse(res.body)
91
+ firstAPK = apks.kind_of?(Array) ? apks[0] : apks
92
+ apk_id = firstAPK['id']
93
+ return apk_id
94
+ end
93
95
  end
94
96
 
95
- def self.get_current_apk_id(token, app_id, edit_id)
97
+ def self.get_current_apk_etag(token, app_id, edit_id, apk_id)
96
98
 
97
- get_apks_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks"
98
- get_apks_url = BASE_URL + get_apks_path
99
+ get_apks_etag = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/#{apk_id}"
100
+ get_apks_etag_url = BASE_URL + get_apks_etag
99
101
 
100
- uri = URI(get_apks_url)
102
+ uri = URI(get_apks_etag_url)
101
103
  http = Net::HTTP.new(uri.host, uri.port)
102
104
  http.use_ssl = true
103
105
  req = Net::HTTP::Get.new(
@@ -107,43 +109,47 @@ module Fastlane
107
109
  )
108
110
 
109
111
  res = http.request(req)
110
- if !res.body.nil?
111
- apks = JSON.parse(res.body)
112
- firstAPK = apks[0]
113
- apk_id = firstAPK['id']
114
- return apk_id
115
- end
112
+ return res.header['ETag']
116
113
  end
117
114
 
118
- def self.replaceExistingApk(token, app_id, edit_id, apk_id, eTag, apk_path)
115
+ def self.replaceExistingApk(token, app_id, edit_id, apk_id, eTag, apk_path, should_retry = true)
119
116
 
120
117
  replace_apk_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/#{apk_id}/replace"
121
118
  local_apk = File.open(apk_path, "r").read
122
-
119
+
120
+ apk_uri = URI.parse(apk_path)
121
+ apk_name = apk_uri.path.split('/').last
122
+
123
123
  replace_apk_url = BASE_URL + replace_apk_path
124
124
  uri = URI(replace_apk_url)
125
125
  http = Net::HTTP.new(uri.host, uri.port)
126
126
  http.use_ssl = true
127
- http.write_timeout = 10000
127
+ http.write_timeout = 1000
128
128
  req = Net::HTTP::Put.new(
129
129
  uri.path,
130
130
  'Authorization' => token,
131
131
  'Content-Type' => 'application/vnd.android.package-archive',
132
+ 'fileName' => apk_name,
132
133
  'If-Match' => eTag
133
134
  )
134
135
 
135
136
  req.body = local_apk
136
137
  res = http.request(req)
137
- return res.code
138
+ replace_apk_response = JSON.parse(res.body)
139
+ # Retry again if replace failed
140
+ if res.code == '412' && should_retry
141
+ UI.message("replacing the apk failed, retrying uploading it again...")
142
+ replaceExistingApk(token, app_id, edit_id, apk_id, eTag, apk_path, false)
143
+ return
144
+ end
145
+ return res.code, replace_apk_response
138
146
  end
139
147
 
140
148
  def self.delete_apk(token, app_id, edit_id, apk_id, eTag)
141
149
 
142
- delete_apk_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/#{apk_id}"
150
+ delete_apk_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/#{apk_id}"
143
151
  delete_apk_url = BASE_URL + delete_apk_path
144
152
 
145
- UI.message("delete_apk_url #{delete_apk_url}")
146
-
147
153
  uri = URI(delete_apk_url)
148
154
  http = Net::HTTP.new(uri.host, uri.port)
149
155
  http.use_ssl = true
@@ -160,15 +166,15 @@ module Fastlane
160
166
 
161
167
  def self.uploadNewApk(token, app_id, edit_id, apk_path)
162
168
 
163
- add_apk_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/upload"
169
+ add_apk_path = "/v1/applications/#{app_id}/edits/#{edit_id}/apks/upload"
164
170
  add_apk_url = BASE_URL + add_apk_path
165
171
  local_apk = File.open(apk_path, 'r').read
166
172
 
167
173
  uri = URI(add_apk_url)
168
174
  http = Net::HTTP.new(uri.host, uri.port)
169
175
  http.use_ssl = true
170
- http.write_timeout = 10000
171
- http.read_timeout = 10000
176
+ http.write_timeout = 1000
177
+ http.read_timeout = 1000
172
178
  req = Net::HTTP::Post.new(
173
179
  uri.path,
174
180
  'Authorization' => token,
@@ -180,9 +186,89 @@ module Fastlane
180
186
  result_json = JSON.parse(res.body)
181
187
  end
182
188
 
189
+ def self.update_listings(token, app_id, edit_id, changelogs_path, upload_changelogs)
190
+
191
+ listings_path = "/v1/applications/#{app_id}/edits/#{edit_id}/listings"
192
+ listings_url = BASE_URL + listings_path
193
+
194
+ uri = URI(listings_url)
195
+ http = Net::HTTP.new(uri.host, uri.port)
196
+ http.use_ssl = true
197
+ req = Net::HTTP::Get.new(
198
+ uri.path,
199
+ 'Authorization' => token,
200
+ 'Content-Type' => 'application/json'
201
+ )
202
+
203
+ res = http.request(req)
204
+ listings_response = JSON.parse(res.body)
205
+
206
+ # Iterating over the languages for getting the ETag.
207
+ listings_response['listings'].each do |lang, listing|
208
+ lang_path = "/v1/applications/#{app_id}/edits/#{edit_id}/listings/#{lang}"
209
+ lang_url = BASE_URL + lang_path
210
+
211
+ uri = URI(lang_url)
212
+ http = Net::HTTP.new(uri.host, uri.port)
213
+ http.use_ssl = true
214
+ req = Net::HTTP::Get.new(
215
+ uri.path,
216
+ 'Authorization' => token,
217
+ 'Content-Type' => 'application/json'
218
+ )
219
+ etag_response = http.request(req)
220
+ etag = etag_response.header['Etag']
221
+
222
+ recent_changes = find_changelog(
223
+ changelogs_path,
224
+ lang,
225
+ upload_changelogs,
226
+ )
227
+
228
+ listing[:recentChanges] = recent_changes
229
+
230
+ update_listings_path = "/v1/applications/#{app_id}/edits/#{edit_id}/listings/#{lang}"
231
+ update_listings_url = BASE_URL + update_listings_path
232
+
233
+ uri = URI(update_listings_url)
234
+ http = Net::HTTP.new(uri.host, uri.port)
235
+ http.use_ssl = true
236
+ req = Net::HTTP::Put.new(
237
+ uri.path,
238
+ 'Authorization' => token,
239
+ 'Content-Type' => 'application/json',
240
+ 'If-Match' => etag
241
+ )
242
+
243
+ req.body = listing.to_json
244
+ res = http.request(req)
245
+ listings_response = JSON.parse(res.body)
246
+ end
247
+ end
248
+
249
+ def self.find_changelog(changelogs_path, language, upload_changelogs)
250
+ # The Amazon appstore requires you to enter changelogs before reviewing.
251
+ # Therefore, if there is no metadata, hyphen text is returned.
252
+ changelog_text = '-'
253
+ return changelog_text if !upload_changelogs
254
+
255
+ path = File.join(changelogs_path, "#{language}.txt")
256
+ if File.exist?(path) && !File.zero?(path)
257
+ changelog_text = File.read(path, encoding: 'UTF-8')
258
+ else
259
+ defalut_changelog_path = File.join(changelogs_path, 'default.txt')
260
+ if File.exist?(defalut_changelog_path) && !File.zero?(defalut_changelog_path)
261
+ changelog_text = File.read(defalut_changelog_path, encoding: 'UTF-8')
262
+ else
263
+ UI.message("Could not find changelog for language '#{language}' at path #{path}...")
264
+ end
265
+ end
266
+ changelog_text
267
+ end
268
+
183
269
  def self.commit_edit(token, app_id, edit_id, eTag)
184
270
 
185
- commit_edit_path = "/v1/applications/#{app_id}/edits/#{edit_id}/commit"
271
+ commit_edit_path = "/v1/applications/#{app_id}/edits/#{edit_id}/commit"
186
272
  commit_edit_url = BASE_URL + commit_edit_path
187
273
 
188
274
  uri = URI(commit_edit_url)
@@ -197,10 +283,6 @@ module Fastlane
197
283
  res = http.request(req)
198
284
  result_json = JSON.parse(res.body)
199
285
  end
200
-
201
- def self.show_message
202
- UI.message("Hello from the amazon_app_submission plugin helper!")
203
- end
204
286
  end
205
287
  end
206
288
  end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module AmazonAppSubmission
3
- VERSION = "0.2.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-amazon_app_submission
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mohammedhemaid
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-08 00:00:00.000000000 Z
11
+ date: 2022-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -162,7 +162,7 @@ files:
162
162
  - lib/fastlane/plugin/amazon_app_submission/actions/amazon_app_submission_action.rb
163
163
  - lib/fastlane/plugin/amazon_app_submission/helper/amazon_app_submission_helper.rb
164
164
  - lib/fastlane/plugin/amazon_app_submission/version.rb
165
- homepage:
165
+ homepage: https://github.com/ugroupmedia/fastlane-plugin-amazon-app-store-submission
166
166
  licenses:
167
167
  - MIT
168
168
  metadata: {}