fastlane-plugin-amazon_app_submission 0.2.0 → 0.4.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: 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: {}