fastlane-plugin-airwatch_workspaceone 1.0.2 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ require 'fastlane/action'
2
+ require_relative '../helper/airwatch_workspaceone_helper'
3
+
4
+ module Fastlane
5
+ module Actions
6
+ class DeleteSpecificVersionAction < Action
7
+
8
+ APP_VERSIONS_LIST_SUFFIX = "/API/mam/apps/search?applicationtype=Internal&bundleid=%s"
9
+ INTERNAL_APP_DELETE_SUFFIX = "/API/mam/apps/internal/%d"
10
+
11
+ $is_debug = false
12
+
13
+ def self.run(params)
14
+ UI.message("The airwatch_workspaceone plugin is working!")
15
+
16
+ # check if debug is enabled
17
+ $is_debug = params[:debug]
18
+
19
+ if debug
20
+ UI.message("---------------------------------------------")
21
+ UI.message("DeleteSpecificVersionAction debug information")
22
+ UI.message("---------------------------------------------")
23
+ UI.message(" host_url: #{params[:host_url]}")
24
+ UI.message(" aw_tenant_code: #{params[:aw_tenant_code]}")
25
+ UI.message(" b64_encoded_auth: #{params[:b64_encoded_auth]}")
26
+ UI.message(" app_identifier: #{params[:app_identifier]}")
27
+ UI.message(" version_number: #{params[:version_number]}")
28
+ end
29
+
30
+ $host_url = params[:host_url]
31
+ $aw_tenant_code = params[:aw_tenant_code]
32
+ $b64_encoded_auth = params[:b64_encoded_auth]
33
+ app_identifier = params[:app_identifier]
34
+ version_number = params[:version_number]
35
+
36
+ # step 1: find app
37
+ UI.message("-----------------------")
38
+ UI.message("1. Finding app versions")
39
+ UI.message("-----------------------")
40
+
41
+ app_versions = find_app(app_identifier)
42
+ app_version_numbers = app_versions.map {|app_version| app_version.values[1]}
43
+ UI.success("Found %d app version(s)" % [app_versions.count])
44
+ UI.success("Version number(s): %s" % [app_version_numbers])
45
+
46
+ # step 2: delete specific version
47
+ UI.message("--------------------------------")
48
+ UI.message("2. Deleting specific app version")
49
+ UI.message("--------------------------------")
50
+
51
+ if app_version_numbers.include? version_number
52
+ version_index = app_version_numbers.index(version_number)
53
+ app_version_to_delete = app_versions[version_index]
54
+ delete_app(app_version_to_delete)
55
+ else
56
+ UI.user_error!("A version with the given version number: %s does not exist on the console for this application or is already deleted." % [version_number])
57
+ end
58
+
59
+ UI.success("Version %s successfully deleted" % [version_number])
60
+ end
61
+
62
+ def self.find_app(app_identifier)
63
+ # get the list of apps
64
+ data = list_app_versions(app_identifier)
65
+ app_versions = Array.new
66
+
67
+ data['Application'].each do |app|
68
+ app_version = Hash.new
69
+ app_version['Id'] = app['Id']['Value']
70
+ app_version['Version'] = app['AppVersion']
71
+ app_versions << app_version
72
+ end
73
+
74
+ app_versions.sort_by! { |app_version| app_version["Id"] }
75
+ return app_versions
76
+ end
77
+
78
+ def self.list_app_versions(app_identifier)
79
+ require 'rest-client'
80
+ require 'json'
81
+
82
+ response = RestClient.get($host_url + APP_VERSIONS_LIST_SUFFIX % [app_identifier], {accept: :json, 'aw-tenant-code': $aw_tenant_code, 'Authorization': "Basic " + $b64_encoded_auth})
83
+
84
+ if debug
85
+ UI.message("Response code: %d" % [response.code])
86
+ UI.message("Response body:")
87
+ UI.message(response.body)
88
+ end
89
+
90
+ if response.code != 200
91
+ UI.user_error!("There was an error in finding app versions. One possible reason is that an app with the bundle identifier given does not exist on Console.")
92
+ exit
93
+ end
94
+
95
+ json = JSON.parse(response.body)
96
+ return json
97
+ end
98
+
99
+ def self.delete_app(app_version)
100
+ require 'rest-client'
101
+ require 'json'
102
+
103
+ UI.message("Starting to delete app version: %s" % [app_version['Version']])
104
+ response = RestClient.delete($host_url + INTERNAL_APP_DELETE_SUFFIX % [app_version['Id']], {accept: :json, 'aw-tenant-code': $aw_tenant_code, 'Authorization': "Basic " + $b64_encoded_auth})
105
+
106
+ if debug
107
+ UI.message("Response code: %d" % [response.code])
108
+ end
109
+
110
+ if response.code == 204
111
+ UI.message("Successfully deleted app version: %s" % [app_version['Version']])
112
+ else
113
+ UI.message("Failed to delete app version: %s" % [app_version['Version']])
114
+ end
115
+ end
116
+
117
+ def self.description
118
+ "The main purpose of this action is to delete a specific version of an application. This action takes a string parameter where you can specify the version number to delete."
119
+ end
120
+
121
+ def self.authors
122
+ ["Ram Awadhesh Sharan"]
123
+ end
124
+
125
+ def self.return_value
126
+ # If your method provides a return value, you can describe here what it does
127
+ end
128
+
129
+ def self.details
130
+ # Optional:
131
+ "delete_specific_version - To delete specific version of an application on the AirWatch/Workspace ONE console."
132
+ end
133
+
134
+ def self.available_options
135
+ [
136
+ FastlaneCore::ConfigItem.new(key: :host_url,
137
+ env_name: "AIRWATCH_HOST_API_URL",
138
+ description: "Host API URL of the AirWatch/Workspace ONE instance without /API/ at the end",
139
+ optional: false,
140
+ type: String,
141
+ verify_block: proc do |value|
142
+ UI.user_error!("No AirWatch/Workspace ONE Host API URl given, pass using `host_url: 'https://yourhost.com'`") unless value and !value.empty?
143
+ end),
144
+
145
+ FastlaneCore::ConfigItem.new(key: :aw_tenant_code,
146
+ env_name: "AIRWATCH_API_KEY",
147
+ description: "API key or the tenant code to access AirWatch/Workspace ONE Rest APIs",
148
+ optional: false,
149
+ type: String,
150
+ verify_block: proc do |value|
151
+ UI.user_error!("Api tenant code header is missing, pass using `aw_tenant_code: 'yourapikey'`") unless value and !value.empty?
152
+ end),
153
+
154
+ FastlaneCore::ConfigItem.new(key: :b64_encoded_auth,
155
+ env_name: "AIRWATCH_BASE64_ENCODED_BASIC_AUTH_STRING",
156
+ description: "The base64 encoded Basic Auth string generated by authorizing username and password to the AirWatch/Workspace ONE instance",
157
+ optional: false,
158
+ type: String,
159
+ verify_block: proc do |value|
160
+ UI.user_error!("The authorization header is empty or the scheme is not basic, pass using `b64_encoded_auth: 'yourb64encodedauthstring'`") unless value and !value.empty?
161
+ end),
162
+
163
+ FastlaneCore::ConfigItem.new(key: :app_identifier,
164
+ env_name: "APP_IDENTIFIER",
165
+ description: "Bundle identifier of your app",
166
+ optional: false,
167
+ type: String,
168
+ verify_block: proc do |value|
169
+ UI.user_error!("No app identifier given, pass using `app_identifier: 'com.example.app'`") unless value and !value.empty?
170
+ end),
171
+
172
+ FastlaneCore::ConfigItem.new(key: :version_number,
173
+ env_name: "AIRWATCH_VERSION_NUMBER",
174
+ description: "Version number of the application to delete",
175
+ optional: false,
176
+ type: String,
177
+ verify_block: proc do |value|
178
+ UI.user_error!("No version number given, pass using `version_number: '1.0'`") unless value and !value.empty?
179
+ end),
180
+
181
+ FastlaneCore::ConfigItem.new(key: :debug,
182
+ env_name: "AIRWATCH_DEBUG",
183
+ description: "Debug flag, set to true to show extended output. default: false",
184
+ optional: true,
185
+ is_string: false,
186
+ default_value: false)
187
+ ]
188
+ end
189
+
190
+ def self.is_supported?(platform)
191
+ # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
192
+ # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
193
+
194
+ [:ios, :android].include?(platform)
195
+ true
196
+ end
197
+
198
+ # helpers
199
+
200
+ def self.debug
201
+ $is_debug
202
+ end
203
+
204
+ end
205
+ end
206
+ end
@@ -5,12 +5,12 @@ module Fastlane
5
5
  module Actions
6
6
  class DeployBuildAction < Action
7
7
 
8
- UPLOAD_BLOB_SUFFIX = "/API/mam/blobs/uploadblob?fileName=%s&organizationGroupId=%s"
9
- BEGIN_INSTALL_SUFFIX = "/API/mam/apps/internal/begininstall"
8
+ UPLOAD_BLOB_SUFFIX = "/API/mam/blobs/uploadblob?fileName=%s&organizationGroupId=%s"
9
+ BEGIN_INSTALL_SUFFIX = "/API/mam/apps/internal/begininstall"
10
10
 
11
- $is_debug = false
12
- $device_type = "Apple"
13
- $supported_device_models = Hash.new
11
+ $is_debug = false
12
+ $device_type = "Apple"
13
+ $supported_device_models = Hash.new
14
14
 
15
15
  def self.run(params)
16
16
  UI.message("The airwatch_workspaceone plugin is working!")
@@ -19,27 +19,28 @@ module Fastlane
19
19
  $is_debug = params[:debug]
20
20
 
21
21
  if debug
22
- UI.message("---------------------------------")
23
- UI.message("AirWatch plugin debug information")
24
- UI.message("---------------------------------")
22
+ UI.message("-----------------------------------")
23
+ UI.message("DeployBuildAction debug information")
24
+ UI.message("-----------------------------------")
25
25
  UI.message(" host_url: #{params[:host_url]}")
26
26
  UI.message(" aw_tenant_code: #{params[:aw_tenant_code]}")
27
27
  UI.message(" b64_encoded_auth: #{params[:b64_encoded_auth]}")
28
+ UI.message(" organization_group_id: #{params[:org_group_id]}")
28
29
  UI.message(" app_name: #{params[:app_name]}")
30
+ UI.message(" app_version: #{params[:app_version]}")
29
31
  UI.message(" file_name: #{params[:file_name]}")
30
32
  UI.message(" path_to_file: #{params[:path_to_file]}")
31
- UI.message(" organization_group_id: #{params[:org_group_id]}")
32
33
  UI.message(" push_mode: #{params[:push_mode]}")
33
- UI.message("---------------------------------")
34
34
  end
35
35
 
36
- host_url = params[:host_url]
37
- aw_tenant_code = params[:aw_tenant_code]
38
- b64_encoded_auth = params[:b64_encoded_auth]
36
+ $host_url = params[:host_url]
37
+ $aw_tenant_code = params[:aw_tenant_code]
38
+ $b64_encoded_auth = params[:b64_encoded_auth]
39
+ $org_group_id = params[:org_group_id]
39
40
  app_name = params[:app_name]
41
+ app_version = params[:app_version]
40
42
  file_name = params[:file_name]
41
43
  path_to_file = params[:path_to_file]
42
- org_group_id = params[:org_group_id]
43
44
  push_mode = params[:push_mode]
44
45
 
45
46
  # step 1: determining device type
@@ -54,7 +55,7 @@ module Fastlane
54
55
  UI.message("2. Setting supported device models")
55
56
  UI.message("----------------------------------")
56
57
  $supported_device_models = find_supported_device_models(path_to_file)
57
- UI.success("Supported Device Models are: %s" % [$supported_device_models.to_json])
58
+ UI.success("Supported Device Model(s): %s" % [$supported_device_models.to_json])
58
59
 
59
60
  # step 3: uploading app blob file
60
61
  UI.message("---------------------")
@@ -64,7 +65,7 @@ module Fastlane
64
65
  UI.message("3. Uploading IPA file")
65
66
  end
66
67
  UI.message("---------------------")
67
- blobID = upload_blob(host_url, aw_tenant_code, b64_encoded_auth, file_name, path_to_file, org_group_id)
68
+ blobID = upload_blob(file_name, path_to_file)
68
69
 
69
70
  if $device_type == "Android"
70
71
  UI.success("Successfully uploaded apk blob")
@@ -77,10 +78,10 @@ module Fastlane
77
78
  end
78
79
 
79
80
  # step 4: deploying app version
80
- UI.message("------------------------------------")
81
+ UI.message("-----------------------------------")
81
82
  UI.message("4. Deploying app version on console")
82
- UI.message("------------------------------------")
83
- deploy_app(host_url, aw_tenant_code, b64_encoded_auth, blobID, app_name, push_mode, org_group_id)
83
+ UI.message("-----------------------------------")
84
+ deploy_app(blobID, app_name, app_version, push_mode)
84
85
  UI.success("Successfully deployed the app version")
85
86
  end
86
87
 
@@ -134,21 +135,21 @@ module Fastlane
134
135
  return model_hash
135
136
  end
136
137
 
137
- def self.upload_blob(host_url, aw_tenant_code, b64_encoded_auth, file_name, path_to_file, org_group_id)
138
+ def self.upload_blob(file_name, path_to_file)
138
139
  require 'rest-client'
139
140
  require 'json'
140
141
 
141
142
  response = RestClient::Request.execute(
142
- :url => host_url + UPLOAD_BLOB_SUFFIX % [file_name, org_group_id],
143
- :method => :post,
144
- :headers => {
145
- 'Authorization' => "Basic " + b64_encoded_auth,
146
- 'aw-tenant-code' => aw_tenant_code,
147
- 'Accept' => 'application/json',
148
- 'Content-Type' => 'application/octet-stream',
149
- 'Expect' => '100-continue'
143
+ :url => $host_url + UPLOAD_BLOB_SUFFIX % [file_name, $org_group_id],
144
+ :method => :post,
145
+ :headers => {
146
+ 'Authorization' => "Basic " + $b64_encoded_auth,
147
+ 'aw-tenant-code' => $aw_tenant_code,
148
+ 'Accept' => 'application/json',
149
+ 'Content-Type' => 'application/octet-stream',
150
+ 'Expect' => '100-continue'
150
151
  },
151
- :payload => File.open(path_to_file, "rb")
152
+ :payload => File.open(path_to_file, "rb")
152
153
  )
153
154
 
154
155
  if debug
@@ -161,17 +162,18 @@ module Fastlane
161
162
  return json['Value']
162
163
  end
163
164
 
164
- def self.deploy_app(host_url, aw_tenant_code, b64_encoded_auth, blobID, app_name, push_mode, org_group_id)
165
+ def self.deploy_app(blobID, app_name, app_version, push_mode)
165
166
  require 'rest-client'
166
167
  require 'json'
167
168
 
168
169
  body = {
169
- "BlobId" => blobID.to_s,
170
- "DeviceType" => $device_type,
170
+ "BlobId" => blobID.to_s,
171
+ "DeviceType" => $device_type,
171
172
  "ApplicationName" => app_name,
173
+ "AppVersion" => app_version,
172
174
  "SupportedModels" => $supported_device_models,
173
- "PushMode" => push_mode,
174
- "LocationGroupId" => org_group_id
175
+ "PushMode" => push_mode,
176
+ "LocationGroupId" => $org_group_id
175
177
  }
176
178
 
177
179
  if debug
@@ -179,7 +181,14 @@ module Fastlane
179
181
  UI.message(body.to_json)
180
182
  end
181
183
 
182
- response = RestClient.post(host_url + BEGIN_INSTALL_SUFFIX, body.to_json, {content_type: :json, accept: :json, 'aw-tenant-code': aw_tenant_code, 'Authorization': "Basic " + b64_encoded_auth})
184
+ begin
185
+ response = RestClient.post($host_url + BEGIN_INSTALL_SUFFIX, body.to_json, {content_type: :json, accept: :json, 'aw-tenant-code': $aw_tenant_code, 'Authorization': "Basic " + $b64_encoded_auth})
186
+ rescue RestClient::ExceptionWithResponse => e
187
+ UI.message("ERROR! Response code: %d" % [e.response.code])
188
+ UI.message("Response body:")
189
+ UI.message(e.response.body)
190
+ raise
191
+ end
183
192
 
184
193
  if debug
185
194
  UI.message("Response code: %d" % [response.code])
@@ -192,7 +201,7 @@ module Fastlane
192
201
  end
193
202
 
194
203
  def self.description
195
- "The main purpose of this action is to upload an IPA or an APK file to an AirWatch or Workspace ONE enterprise instance/console."
204
+ "The main purpose of this action is to upload an IPA or an APK file to an AirWatch or Workspace ONE enterprise console."
196
205
  end
197
206
 
198
207
  def self.authors
@@ -205,36 +214,45 @@ module Fastlane
205
214
 
206
215
  def self.details
207
216
  # Optional:
208
- "deploy_app - To upload an iOS ipa OR Android APK to AirWatch/WorkspaceOne console."
217
+ "deploy_build - To upload an iOS ipa OR Android APK to AirWatch/Workspace One console."
209
218
  end
210
219
 
211
220
  def self.available_options
212
221
  [
213
222
  FastlaneCore::ConfigItem.new(key: :host_url,
214
223
  env_name: "AIRWATCH_HOST_API_URL",
215
- description: "Host API URL of the AirWatch instance",
224
+ description: "Host API URL of the AirWatch/Workspace ONE instance without /API/ at the end",
216
225
  optional: false,
217
226
  type: String,
218
227
  verify_block: proc do |value|
219
- UI.user_error!("No AirWatch Host API URl given.") unless value and !value.empty?
228
+ UI.user_error!("No AirWatch/Workspace ONE Host API URl given, pass using `host_url: 'https://yourhost.com'`") unless value and !value.empty?
220
229
  end),
221
230
 
222
231
  FastlaneCore::ConfigItem.new(key: :aw_tenant_code,
223
232
  env_name: "AIRWATCH_API_KEY",
224
- description: "API key or the tenant code to access AirWatch Rest APIs",
233
+ description: "API key or the tenant code to access AirWatch/Workspace ONE Rest APIs",
225
234
  optional: false,
226
235
  type: String,
227
236
  verify_block: proc do |value|
228
- UI.user_error!("Api tenant code header is missing.") unless value and !value.empty?
237
+ UI.user_error!("Api tenant code header is missing, pass using `aw_tenant_code: 'yourapikey'`") unless value and !value.empty?
229
238
  end),
230
239
 
231
240
  FastlaneCore::ConfigItem.new(key: :b64_encoded_auth,
232
241
  env_name: "AIRWATCH_BASE64_ENCODED_BASIC_AUTH_STRING",
233
- description: "The base64 encoded Basic Auth string generated by authorizing username and password to the AirWatch instance",
242
+ description: "The base64 encoded Basic Auth string generated by authorizing username and password to the AirWatch/Workspace ONE instance",
243
+ optional: false,
244
+ type: String,
245
+ verify_block: proc do |value|
246
+ UI.user_error!("The authorization header is empty or the scheme is not basic, pass using `b64_encoded_auth: 'yourb64encodedauthstring'`") unless value and !value.empty?
247
+ end),
248
+
249
+ FastlaneCore::ConfigItem.new(key: :org_group_id,
250
+ env_name: "AIRWATCH_ORGANIZATION_GROUP_ID",
251
+ description: "Organization Group ID integer identifying the customer or container",
234
252
  optional: false,
235
253
  type: String,
236
254
  verify_block: proc do |value|
237
- UI.user_error!("The authorization header is empty or the scheme is not basic") unless value and !value.empty?
255
+ UI.user_error!("No Organization Group ID integer given, pass using `org_group_id: 'yourorggrpintid'`") unless value and !value.empty?
238
256
  end),
239
257
 
240
258
  FastlaneCore::ConfigItem.new(key: :app_name,
@@ -245,6 +263,13 @@ module Fastlane
245
263
  verify_block: proc do |value|
246
264
  UI.user_error!("No app name given, pass using `app_name: 'My sample app'`") unless value and !value.empty?
247
265
  end),
266
+
267
+ FastlaneCore::ConfigItem.new(key: :app_version,
268
+ env_name: "AIRWATCH_APPLICATION_VERSION",
269
+ description: "Airwatch Internal App Version",
270
+ optional: true,
271
+ type: String,
272
+ default_value: nil),
248
273
 
249
274
  FastlaneCore::ConfigItem.new(key: :file_name,
250
275
  env_name: "AIRWATCH_FILE_NAME",
@@ -264,22 +289,13 @@ module Fastlane
264
289
  UI.user_error!("Path to the file not given, pass using `path_to_file: '/path/to/the/file/on/disk'`") unless value and !value.empty?
265
290
  end),
266
291
 
267
- FastlaneCore::ConfigItem.new(key: :org_group_id,
268
- env_name: "AIRWATCH_ORGANIZATION_GROUP_ID",
269
- description: "Organization Group ID integer identifying the customer or container",
270
- optional: false,
271
- type: String,
272
- verify_block: proc do |value|
273
- UI.user_error!("No Organization Group ID integer given, pass using `org_group_id: MyOrgGroupId`") unless value and !value.empty?
274
- end),
275
-
276
292
  FastlaneCore::ConfigItem.new(key: :push_mode,
277
293
  env_name: "AIRWATCH_APP_PUSH_MODE",
278
- description: "Push mode for the application",
294
+ description: "Push mode for the application. Values are Auto or On demand",
279
295
  optional: false,
280
296
  type: String,
281
297
  verify_block: proc do |value|
282
- UI.user_error!("No push mode given, pass using `push_mode: 'Auto'` or pass using `push_mode: 'On demand'`") unless value and !value.empty?
298
+ UI.user_error!("No push mode given, pass using `push_mode: 'Auto'` or `push_mode: 'On demand'`") unless value and !value.empty?
283
299
  end),
284
300
 
285
301
  FastlaneCore::ConfigItem.new(key: :debug,