slowlane 1.0.1 → 1.1.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
  SHA1:
3
- metadata.gz: ccbe4b06a1d413c6aab728a2ad8b27db74edd98e
4
- data.tar.gz: d9b7702e713547e036b2b980b74836f7de9629f6
3
+ metadata.gz: 83a8d79b2005d87f2b9c35e79bc9942304d0b09b
4
+ data.tar.gz: e3effd5f51ce6eab958a99b21d2fdb7012597ab5
5
5
  SHA512:
6
- metadata.gz: bf67a421c0e1aabbfb703decc1e0c2ba9caa6fe3c09102c678c511fcedb35824a9293bc364dc267dc813c9d2a19e9f892be2917011f2915cc87d3bf469b946c4
7
- data.tar.gz: 73e2586bb0b59828781d86c223538fd7244fe0478f44a9affd4343a503a1253f0ae293cd422397afe90c66b9c29569335d8dd2bdf48c214a345566f387d37ead
6
+ metadata.gz: 09a2c085299bced49a2e435ac1c400c8ac062d67c7499734c06da1bccc3732359a257ba13d3e88016ae3b4e99e40e748f33fbd7db52e0ec0fdb11e2d374f1690
7
+ data.tar.gz: 25b3805ba96923d11d22f28b6da47091c0dbbd706c2ef35097dc6744cfbb456d5f5715175948566081d3908080f46c9a60dc6c6c7be603a23fd3e794849ab1b1
data/README.md CHANGED
@@ -23,6 +23,14 @@ Let us know if you like this! [We're only one tweet away!](http://twitter.com/sl
23
23
  ## Kudos
24
24
  - [Fastlane](https://github.com/fastlane/fastlane)
25
25
  - [Shenzhen](https://github.com/nomad/shenzhen)
26
+ - [Atlantispro](https://github.com/Legoless/Atlantis)
27
+
28
+ ## Changes
29
+ 1.1.0
30
+ - added slowlane-fabric (organization, tester, apps)
31
+
32
+ 1.0.0
33
+ - first release
26
34
 
27
35
  ## Status
28
36
  Current able to list most items, moving on to create & delete
@@ -32,6 +40,7 @@ Current able to list most items, moving on to create & delete
32
40
  results in binaries:
33
41
  - `slowlane-itunes`
34
42
  - `slowlane-portal`
43
+ - `slowlane-fabric`
35
44
  - `slowlane-ipa` (NOTE: this might change to a more generic `slowlane-ios` command)
36
45
 
37
46
  ### Working
@@ -42,6 +51,9 @@ results in binaries:
42
51
  - `SLOWLANE_PORTAL_USER`
43
52
  - `SLOWLANE_PORTAL_PASSWORD`
44
53
  - `SLOWLANE_PORTAL_TEAM`
54
+ - `SLOWLANE_FABRIC_USER`
55
+ - `SLOWLANE_FABRIC_PASSWORD`
56
+ - `SLOWLANE_FABRIC_TEAM`
45
57
 
46
58
  #### Portal
47
59
  - `slowlane-portal app list`
@@ -63,6 +75,10 @@ results in binaries:
63
75
  - `slowlane-itunes build list`
64
76
  - `slowlane-itunes build upload <bundle_id> <ipa_file>`
65
77
 
78
+ #### Fabric
79
+ - `slowlane-fabric app list`
80
+ - `slowlane-fabric tester list <bundle_id>`
81
+
66
82
  #### Ipa
67
83
  - `slowlane-ipa info <ipa_file>`
68
84
 
@@ -90,5 +106,11 @@ A lot is still focusing on the happy path , we need to catch better the errors a
90
106
  - create | submit | delete app
91
107
  - all other commands
92
108
 
109
+ #### Fabric
110
+ - info organization
111
+ - create|delete|list groups
112
+ - create|delete|list testers
113
+ - list, add device
114
+
93
115
  #### Playstore
94
116
  - all commands
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require "thor"
5
+
6
+ require_relative '../lib/slowlane/fabric/app.rb'
7
+ require_relative '../lib/slowlane/fabric/tester.rb'
8
+ require_relative '../lib/slowlane/fabric/organization.rb'
9
+ require_relative '../lib/slowlane/fabric/command.rb'
10
+
11
+ SlowlaneFabric.start(ARGV)
@@ -0,0 +1,37 @@
1
+ require_relative './util.rb'
2
+ require_relative './client.rb'
3
+ require 'terminal-table'
4
+
5
+ module Slowlane
6
+ module Fabric
7
+ class App <Thor
8
+
9
+ desc "apps", "get list of apps"
10
+ def list
11
+
12
+ c=Utils.credentials(options)
13
+
14
+ fabric = Slowlane::Fabric::Client.new
15
+ fabric.username = c.username
16
+ fabric.password = c.password
17
+ fabric.team = Utils.team(options)
18
+ apps = fabric.list_apps()
19
+
20
+ headings = ['id', 'name', 'bundle_id']
21
+ rows = []
22
+
23
+ apps.each do |app|
24
+ row = []
25
+ row << app['id']
26
+ row << app['name']
27
+ row << app['bundle_identifier']
28
+ rows << row
29
+ end
30
+
31
+ table = Terminal::Table.new :headings => headings, :rows => rows
32
+ puts table
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,333 @@
1
+ require 'mechanize'
2
+ require 'uri'
3
+ require 'json'
4
+ require 'logger'
5
+
6
+ module Slowlane
7
+ module Fabric
8
+ class Client
9
+ attr_accessor :host, :agent
10
+ attr_accessor :username,:password,:team
11
+ attr_accessor :developer_token, :access_token, :csrf_token, :login_data, :team_id
12
+
13
+ def initialize
14
+ self.agent = Mechanize.new
15
+
16
+ self.host = "fabric.io"
17
+ end
18
+
19
+ def get(uri, parameters = [], referer = nil, headers = {})
20
+ uri = ::File.join("https://#{self.host}", uri) unless /^https?/ === uri
21
+
22
+ #puts "Requesting: #{uri}"
23
+
24
+ unless (self.developer_token.nil?)
25
+ headers['X-CRASHLYTICS-DEVELOPER-TOKEN'] = self.developer_token
26
+ end
27
+
28
+ unless (self.access_token.nil?)
29
+ headers['X-CRASHLYTICS-ACCESS-TOKEN'] = self.access_token
30
+ end
31
+
32
+ unless (self.csrf_token.nil?)
33
+ headers['X-CSRF-Token'] = self.csrf_token
34
+ end
35
+
36
+ headers['X-Requested-With'] = 'XMLHttpRequest'
37
+
38
+ 3.times do
39
+
40
+ self.agent.get(uri, parameters, referer, headers)
41
+
42
+ return self.agent.page
43
+ end
44
+
45
+ #raise NetworkError
46
+ raise Error
47
+ end
48
+
49
+ #
50
+ # Handles login and CSRF tokens
51
+ #
52
+
53
+ def bootstrap
54
+
55
+ if (self.csrf_token.nil?)
56
+ csrf!
57
+ end
58
+
59
+ #
60
+ # First need developer token
61
+ #
62
+ if (self.developer_token.nil?)
63
+ config!
64
+ end
65
+
66
+ #
67
+ # Need to login too
68
+ #
69
+ if (self.access_token.nil?)
70
+ login!
71
+ end
72
+ end
73
+
74
+ def list_devices(distribution_list)
75
+ people = list_people(distribution_list)
76
+
77
+ people_list = []
78
+
79
+ people.each do |person|
80
+ people_list << person.id
81
+ end
82
+
83
+ self.agent.post('/dashboard/team/export/devices/', { "members" => people_list.join('|'), "csrfmiddlewaretoken" => self.agent.page.parser.css("[name='csrfmiddlewaretoken']")[0]['value'] } )
84
+
85
+ device_list = self.agent.page.body.split( /\r?\n/ )
86
+
87
+ # Remove first one
88
+ device_list.shift
89
+
90
+ devices = []
91
+
92
+ device_list.each do |dev|
93
+ #puts dev
94
+
95
+ device = Device.new
96
+ device.udid = dev.split(/\t/)[0]
97
+ device.name = dev.split(/\t/)[1]
98
+
99
+ devices << device
100
+ end
101
+
102
+ devices
103
+ end
104
+
105
+ def list_apps
106
+ bootstrap
107
+
108
+ page = get("/api/v2/organizations/#{self.team_id}/apps")
109
+
110
+ apps = JSON.parse(page.body)
111
+
112
+ return apps
113
+ end
114
+
115
+ def list_organizations
116
+ bootstrap
117
+
118
+ page = get("/api/v2/organizations")
119
+
120
+ organizations = JSON.parse(page.body)
121
+
122
+ return organizations
123
+ end
124
+
125
+ def find_app_by_bundle_id(bundle_id)
126
+ apps = list_apps()
127
+ apps.find { |app| app['bundle_identifier'] == bundle_id }
128
+ end
129
+
130
+ def list_testers(app_id)
131
+ bootstrap
132
+ page = get("/api/v2/organizations/#{self.team_id}/apps/#{app_id}/beta_distribution/testers_in_organization?include_developers=true")
133
+
134
+ testers = JSON.parse(page.body)
135
+
136
+ return testers['testers']
137
+ end
138
+
139
+ def list_people(group)
140
+ bootstrap
141
+
142
+ apps = list_apps
143
+
144
+ all_testers = {}
145
+
146
+ apps.each do |app|
147
+ testers = list_testers (app['id'])
148
+
149
+ #
150
+ # For each tester go through it's devices and add them
151
+ #
152
+
153
+ testers.each do |tester|
154
+
155
+ #
156
+ # If tester is not yet in, create it
157
+ #
158
+
159
+ if all_testers[tester['id']].nil?
160
+
161
+ person = Person.new
162
+ person.id = tester['id']
163
+ person.name = tester['name']
164
+ person.email = tester['email']
165
+ person.groups = []
166
+ person.devices = {}
167
+
168
+ add_group = false
169
+
170
+ if tester['groups']
171
+ groups = tester['groups']
172
+
173
+ groups.each do |current_group|
174
+
175
+ person_group = Group.new
176
+ person_group.id = current_group['id']
177
+ person_group.name = current_group['name']
178
+ person_group.alias = current_group['alias']
179
+
180
+ person.groups << person_group
181
+
182
+ if person_group.name == group or person_group.alias == group
183
+ add_group = true
184
+ end
185
+ end
186
+ end
187
+
188
+ if (add_group == true or group.nil?) and !person.name.empty?
189
+ all_testers[person.id] = person
190
+ end
191
+ else
192
+ person = all_testers[tester['id']]
193
+ end
194
+
195
+ append_devices(person, tester['devices'])
196
+ end
197
+ end
198
+
199
+ return all_testers
200
+ end
201
+
202
+ def list_devices(group)
203
+ bootstrap
204
+
205
+ testers = list_people(group)
206
+
207
+ devices = {}
208
+
209
+ testers.each do |id, tester|
210
+ devices = devices.merge (tester.devices)
211
+ end
212
+
213
+ return devices.values
214
+ end
215
+
216
+ def list_groups
217
+ testers = list_people(nil)
218
+
219
+ groups = {}
220
+
221
+ testers.each do |id, tester|
222
+ tester.groups.each do |group|
223
+ groups[group.id] = group
224
+ end
225
+ end
226
+
227
+ return groups.values
228
+ end
229
+
230
+ private
231
+
232
+ def append_devices (person, devices)
233
+
234
+ if devices.nil?
235
+ return nil
236
+ end
237
+
238
+ devices.each do |device|
239
+
240
+ if person.devices[device['identifier']].nil?
241
+ current_device = Device.new
242
+ current_device.manufacturer = device['manufacturer']
243
+ current_device.model = device['model']
244
+ current_device.os_version = device['os_version']
245
+ current_device.identifier = device['identifier']
246
+ current_device.name = device['name']
247
+ current_device.platform = device['platform']
248
+ current_device.model_name = device['model_name']
249
+
250
+ person.devices[current_device.identifier] = current_device
251
+ end
252
+
253
+ end
254
+ end
255
+
256
+ def csrf!
257
+ page = get('/login')
258
+
259
+ token = page.at('meta[name="csrf-token"]')[:content]
260
+
261
+ unless token.nil?
262
+ self.csrf_token = token
263
+ end
264
+ end
265
+
266
+ def config!
267
+ page = get ('/api/v2/client_boot/config_data')
268
+
269
+ config_object = JSON.parse(page.body)
270
+
271
+ unless config_object['developer_token'].nil?
272
+ self.developer_token = config_object['developer_token']
273
+ else
274
+ raise Error
275
+ #raise UnsuccessfulAuthenticationError
276
+ end
277
+ end
278
+
279
+ def login!
280
+ begin
281
+
282
+ session = self.agent.post('https://fabric.io/api/v2/session', { "email" => self.username, "password" => self.password }, { 'X-CRASHLYTICS-DEVELOPER-TOKEN' => self.developer_token, 'X-CSRF-Token' => self.csrf_token, 'X-Requested-With' => 'XMLHttpRequest' } )
283
+
284
+ rescue Mechanize::ResponseCodeError => ex
285
+ message = JSON.parse(ex.page.body)
286
+
287
+ unless message['message'].nil?
288
+ puts message['message']
289
+ end
290
+ end
291
+
292
+ self.login_data = JSON.parse(self.agent.page.body)
293
+
294
+ unless self.login_data['token'].nil?
295
+ self.access_token = self.login_data['token']
296
+
297
+ select_team!
298
+ else
299
+ raise Error
300
+ #raise UnsuccessfulAuthenticationError
301
+ end
302
+ end
303
+
304
+ def select_team!
305
+
306
+ if self.login_data['organizations'].nil?
307
+ raise Error
308
+ # raise UnknownTeamError
309
+ end
310
+
311
+ teams = self.login_data['organizations']
312
+
313
+ teams.each do |team|
314
+ #puts team['name']
315
+
316
+ if team['alias'] == self.team or team['name'] == self.team
317
+ self.team_id = team['id']
318
+
319
+ break
320
+ end
321
+ end
322
+
323
+ if self.team_id.nil?
324
+ #raise UnknownTeamError
325
+ raise Error
326
+ end
327
+
328
+ #puts "SELECTED TEAM: #{self.team_id}"
329
+ end
330
+
331
+ end
332
+ end
333
+ end
@@ -0,0 +1,17 @@
1
+ require 'thor'
2
+ class SlowlaneFabric < Thor
3
+
4
+ class_option :username , :default => '<username>' , :required => true, :desc => 'username [SLOWLANE_FABRIC_USERNAME]'
5
+ class_option :password , :default => '<password>' , :required => true, :desc => 'password [SLOWLANE_FABRIC_PASSWORD]'
6
+ class_option :team , :default => '<team>' , :required => false, :desc => 'team [SLOWLANE_FABRIC_TEAM]'
7
+
8
+ desc "app SUBCOMMAND ...ARGS", "manage apps"
9
+ subcommand "app", Slowlane::Fabric::App
10
+
11
+ desc "organization SUBCOMMAND ...ARGS", "manage organizations"
12
+ subcommand "organization", Slowlane::Fabric::Organization
13
+
14
+ desc "tester SUBCOMMAND ...ARGS", "manage testers"
15
+ subcommand "tester", Slowlane::Fabric::Tester
16
+
17
+ end
@@ -0,0 +1,41 @@
1
+ require_relative './util.rb'
2
+ require_relative './client.rb'
3
+ require 'terminal-table'
4
+
5
+ module Slowlane
6
+ module Fabric
7
+ class Organization <Thor
8
+
9
+ desc "list", "get list of organizations"
10
+ def list
11
+
12
+ c=Utils.credentials(options)
13
+
14
+ fabric = Slowlane::Fabric::Client.new
15
+ fabric.username = c.username
16
+ fabric.password = c.password
17
+ fabric.team = Utils.team(options)
18
+ orgs = fabric.list_organizations
19
+
20
+ puts orgs
21
+ headings = ['id', 'name', 'alias','accounts_count', 'build_secret', 'api_key']
22
+ rows = []
23
+
24
+ orgs.each do |org|
25
+ row = []
26
+ row << org['id']
27
+ row << org['name']
28
+ row << org['alias']
29
+ row << org['accounts_count']
30
+ row << org['build_secret']
31
+ row << org['api_key']
32
+ rows << row
33
+ end
34
+
35
+ table = Terminal::Table.new :headings => headings, :rows => rows
36
+ puts table
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ require_relative './util.rb'
2
+ require_relative './client.rb'
3
+ require 'terminal-table'
4
+
5
+ module Slowlane
6
+ module Fabric
7
+ class Tester <Thor
8
+
9
+ desc "list", "get list of tester"
10
+ def list(bundle_id)
11
+
12
+ c=Utils.credentials(options)
13
+
14
+ fabric = Slowlane::Fabric::Client.new
15
+ fabric.username = c.username
16
+ fabric.password = c.password
17
+ fabric.team = Utils.team(options)
18
+ app = fabric.find_app_by_bundle_id(bundle_id)
19
+ app_id = app['id']
20
+ testers = fabric.list_testers(app_id)
21
+
22
+ headings = ['id', 'name', 'email','groups' ]
23
+ rows = []
24
+
25
+ testers.each do |tester|
26
+ row = []
27
+ row << tester['id']
28
+ row << tester['name']
29
+ row << tester['email']
30
+ groups = tester['groups']
31
+ if groups.nil?
32
+ row << ""
33
+ else
34
+ row << groups.map { |g| g['name'] }.join(",")
35
+ end
36
+ rows << row
37
+ end
38
+
39
+ table = Terminal::Table.new :headings => headings, :rows => rows
40
+ puts table
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,47 @@
1
+ require 'ostruct'
2
+
3
+ class Utils
4
+ def self.credentials(options)
5
+
6
+ credentials = OpenStruct.new
7
+
8
+ if ENV['SLOWLANE_FABRIC_USERNAME']
9
+ credentials.username=ENV['SLOWLANE_FABRIC_USERNAME']
10
+ else
11
+ if options[:username] == '<username>'
12
+ puts "username is required"
13
+ exit(-1)
14
+ else
15
+ credentials.username=options[:username]
16
+ end
17
+ end
18
+
19
+ if ENV['SLOWLANE_FABRIC_PASSWORD']
20
+ credentials.password=ENV['SLOWLANE_FABRIC_PASSWORD']
21
+ else
22
+ if options[:password] == '<password>'
23
+ puts "password is required"
24
+ exit(-1)
25
+ else
26
+ credentials.password=options[:username]
27
+ end
28
+ end
29
+ return credentials
30
+ end
31
+
32
+ def self.team(options)
33
+
34
+ if ENV['SLOWLANE_FABRIC_TEAM']
35
+ team=ENV['SLOWLANE_FABRIC_TEAM']
36
+ else
37
+ if options[:team] == '<team>'
38
+ puts "team is required"
39
+ exit(-1)
40
+ else
41
+ team=options[:team]
42
+ end
43
+ end
44
+ return team
45
+ end
46
+
47
+ end
data/slowlane.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "slowlane"
5
- s.version = "1.0.1"
5
+ s.version = "1.1.0"
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.license = 'MIT'
8
8
  s.authors = ["Patrick Debois"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slowlane
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Debois
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-12 00:00:00.000000000 Z
11
+ date: 2016-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -127,6 +127,7 @@ email:
127
127
  - patrick.debois@jedi.be
128
128
  executables:
129
129
  - complete.sh
130
+ - slowlane-fabric
130
131
  - slowlane-ipa
131
132
  - slowlane-itunes
132
133
  - slowlane-portal
@@ -138,9 +139,16 @@ files:
138
139
  - README.md
139
140
  - Rakefile
140
141
  - bin/complete.sh
142
+ - bin/slowlane-fabric
141
143
  - bin/slowlane-ipa
142
144
  - bin/slowlane-itunes
143
145
  - bin/slowlane-portal
146
+ - lib/slowlane/fabric/app.rb
147
+ - lib/slowlane/fabric/client.rb
148
+ - lib/slowlane/fabric/command.rb
149
+ - lib/slowlane/fabric/organization.rb
150
+ - lib/slowlane/fabric/tester.rb
151
+ - lib/slowlane/fabric/util.rb
144
152
  - lib/slowlane/itunes/app.rb
145
153
  - lib/slowlane/itunes/build.rb
146
154
  - lib/slowlane/itunes/command.rb