atlantispro 0.1.2 → 0.2.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: b8c1c129d83eed952059a24255df8c32981f9ac0
4
- data.tar.gz: 120997573134d06c79c7cd7813956086374a472e
3
+ metadata.gz: deee38e26d7c80ed8bcf9aa7ff0625c34431f004
4
+ data.tar.gz: ce4b735ebb4927cf32732743a0666f8a2aa16cad
5
5
  SHA512:
6
- metadata.gz: bf28939c5e893edf9e9422f202265c4b14d2136b63f76391528b844d7d3367c350fb847c10747383bd36cf7b58f918f49625bbf533fed118e87430a02ad83806
7
- data.tar.gz: 6754d4899a3f5581e225b0765f0a31a1e37a2d72b106fe147ac62cba09e9e58fc22f53aa315d58cb7a9e5bb7f7e559a3cb3dd90000d3c95cd1dfb683b1600478
6
+ metadata.gz: a4d95d18bb76ef9cbc37576226cc5f168d56eabf022a093ad8735d766e2f27ee06ffa67f09d036b9881d3c6d538dee7655b281059da544e04ad32f197a600674
7
+ data.tar.gz: 6ef003a0b3a0ca393ee8d9eaab686b60ac405437bf97cc19510795d3f222af296d9b3eb43c3c00c7a95722a000c77a48d58bd9a2ac4fe759847a47ccf9ebc00c
data/Atlantis.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Atlantis::VERSION
9
9
  spec.authors = ["Dal Rupnik"]
10
10
  spec.email = ["legoless@gmail.com"]
11
- spec.summary = "Some random description"
12
- spec.description = "Yeees"
13
- spec.homepage = ""
11
+ spec.summary = "A command line interface to connect to distribution services such as TestFlight or Crashlytics."
12
+ spec.description = "A simple command line interface to work with multiple application distribution services, such as TestFlight or Crashlytics. Allows for downloading of device identifiers, registered testers and more."
13
+ spec.homepage = "https://github.com/legoless/Atlantis"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.add_dependency "commander", "~> 4.1.2"
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ atlantispro (0.1.2)
5
+ certified (>= 0.1.0)
6
+ commander (~> 4.1.2)
7
+ mechanize (~> 2.5.1)
8
+ nokogiri (~> 1.5.9)
9
+ security (~> 0.1.2)
10
+ term-ansicolor (~> 1.0.7)
11
+ terminal-table (~> 1.4.5)
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ certified (0.1.2)
17
+ commander (4.1.6)
18
+ highline (~> 1.6.11)
19
+ domain_name (0.5.18)
20
+ unf (>= 0.0.5, < 1.0.0)
21
+ highline (1.6.21)
22
+ mechanize (2.5.1)
23
+ domain_name (~> 0.5, >= 0.5.1)
24
+ mime-types (~> 1.17, >= 1.17.2)
25
+ net-http-digest_auth (~> 1.1, >= 1.1.1)
26
+ net-http-persistent (~> 2.5, >= 2.5.2)
27
+ nokogiri (~> 1.4)
28
+ ntlm-http (~> 0.1, >= 0.1.1)
29
+ webrobots (~> 0.0, >= 0.0.9)
30
+ mime-types (1.25.1)
31
+ net-http-digest_auth (1.4)
32
+ net-http-persistent (2.9.4)
33
+ nokogiri (1.5.11)
34
+ ntlm-http (0.1.1)
35
+ rake (10.3.2)
36
+ security (0.1.2)
37
+ term-ansicolor (1.0.7)
38
+ terminal-table (1.4.5)
39
+ unf (0.1.4)
40
+ unf_ext
41
+ unf_ext (0.0.6)
42
+ webrobots (0.1.1)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ atlantispro!
49
+ bundler (~> 1.6)
50
+ rake
@@ -16,8 +16,7 @@ Signal.trap("INT") {} # Suppress backtrace when exiting command
16
16
 
17
17
  program :name, 'atlantis'
18
18
  program :version, Atlantis::VERSION
19
- program :description, 'A command-line interface for the TestFlight Portal'
20
-
19
+ program :description, 'A command-line interface for Distribution Portals (Crashlytics, TestFlight)'
21
20
  program :help, 'Author', 'Dal Rupnik <legoless@gmail.com>'
22
21
  program :help, 'Website', 'https://github.com/legoless'
23
22
  program :help_formatter, :compact
@@ -3,26 +3,26 @@ require 'certified'
3
3
 
4
4
  module Atlantis
5
5
  module Portal
6
- HOST = "testflightapp.com"
7
-
8
6
  class UnsuccessfulAuthenticationError < RuntimeError; end
7
+ class NetworkConnectionError < RuntimeError; end
9
8
  class UnexpectedContentError < RuntimeError; end
9
+ class UnknownTeamError < RuntimeError; end
10
10
 
11
- class Person < Struct.new(:id, :name, :email, :devices)
11
+ class Person < Struct.new(:id, :name, :email, :devices, :groups)
12
12
  def to_s
13
- "#{self.id} #{self.name} #{self.email} #{self.devices}"
13
+ "#{self.id} #{self.name} #{self.email}"
14
14
  end
15
15
  end
16
16
 
17
- class Device < Struct.new(:udid, :name)
17
+ class Device < Struct.new(:manufacturer, :model, :os_version, :identifier, :name, :platform, :model_name)
18
18
  def to_s
19
- "#{self.udid} #{self.name}"
19
+ "#{self.identifier} #{self.name}"
20
20
  end
21
21
  end
22
22
 
23
- class DistributionList < Struct.new(:id, :name, :count)
23
+ class Group < Struct.new(:id, :alias, :name, :count)
24
24
  def to_s
25
- "#{self.id} #{self.name} #{self.count}"
25
+ "#{self.id} #{self.name}"
26
26
  end
27
27
  end
28
28
 
@@ -31,7 +31,16 @@ module Atlantis
31
31
  "#{self.id} #{self.name} #{self.token}"
32
32
  end
33
33
  end
34
+
35
+ class CommandArguments < Struct.new(:username, :password, :team, :format, :service, :group)
36
+ def to_s
37
+ "#{self.username} #{self.team} #{self.format} #{self.service}"
38
+ end
39
+ end
34
40
  end
35
41
  end
36
42
 
37
- require 'atlantis/portal/agent'
43
+ require 'atlantis/portal/agent'
44
+ require 'atlantis/portal/service'
45
+ require 'atlantis/portal/crashlytics/crashlyticsservice'
46
+ require 'atlantis/portal/testflight/testflightservice'
@@ -8,7 +8,7 @@ require 'nokogiri'
8
8
  module Atlantis
9
9
  module Portal
10
10
  class Agent < ::Mechanize
11
- attr_accessor :username, :password, :team, :format
11
+ attr_accessor :format, :service, :service_instance
12
12
 
13
13
  def initialize
14
14
  super
@@ -25,203 +25,7 @@ module Atlantis
25
25
 
26
26
  set_proxy(uri.host, uri.port, user || uri.user, password || uri.password)
27
27
  end
28
-
29
- pw = Security::InternetPassword.find(:server => Atlantis::Portal::HOST)
30
- @username, @password = pw.attributes['acct'], pw.password if pw
31
- end
32
-
33
- def username=(value)
34
- @username = value
35
-
36
- pw = Security::InternetPassword.find(:a => self.username, :server => Atlantis::Portal::HOST)
37
- @password = pw.password if pw
38
- end
39
-
40
- def get(uri, parameters = [], referer = nil, headers = {})
41
- uri = ::File.join("https://#{Atlantis::Portal::HOST}", uri) unless /^https?/ === uri
42
-
43
- 3.times do
44
- super(uri, parameters, referer, headers)
45
-
46
- #puts page.body
47
-
48
- return page unless page.respond_to?(:title)
49
-
50
- unless page.parser.at_css('#id_username').nil?
51
- #puts "Logging in"
52
- login! and next
53
- else
54
- if !@team.nil? && !@team.empty?
55
- select_team! and next
56
- else
57
- return page
58
- end
59
- end
60
- end
61
-
62
- raise UnsuccessfulAuthenticationError
63
- end
64
-
65
- def list_people(distribution_list)
66
-
67
- if (distribution_list == 'all')
68
- get('https://testflightapp.com/dashboard/team')
69
- else
70
- lists = list_lists
71
-
72
- list = lists.find{|p| p.name == distribution_list}
73
-
74
- say_warning "No #{distribution_list} distribution list found." and abort if list.nil?
75
-
76
- get('https://testflightapp.com/dashboard/team/list/' + list.id)
77
- end
78
-
79
- people = []
80
-
81
- page.parser.css("table#member-table tr").each do |row|
82
- cols = row.css('td')
83
-
84
- #puts cols.length
85
-
86
- if (cols.length > 0)
87
- person = Person.new
88
-
89
- person.id = cols[0].css("input")[0]['value'];
90
- person.name = cols[1].text.strip.split.map(&:capitalize).join(' ')
91
- person.email = cols[2].text.strip
92
- person.devices = cols[3].text.strip
93
-
94
- people << person
95
-
96
- end
97
- end
98
-
99
- people
100
- end
101
-
102
- def list_devices(distribution_list)
103
- people = list_people(distribution_list)
104
-
105
- people_list = []
106
-
107
- people.each do |person|
108
- people_list << person.id
109
- end
110
-
111
- post('https://testflightapp.com/dashboard/team/export/devices/', { "members" => people_list.join('|'), "csrfmiddlewaretoken" => page.parser.css("[name='csrfmiddlewaretoken']")[0]['value'] } )
112
-
113
- device_list = page.body.split( /\r?\n/ )
114
-
115
- # Remove first one
116
- device_list.shift
117
-
118
- devices = []
119
-
120
- device_list.each do |dev|
121
- #puts dev
122
-
123
- device = Device.new
124
- device.udid = dev.split(/\t/)[0]
125
- device.name = dev.split(/\t/)[1]
126
-
127
- devices << device
128
- end
129
-
130
- devices
131
28
  end
132
-
133
- def list_lists
134
- get('https://testflightapp.com/dashboard/team')
135
-
136
- lists = []
137
-
138
- page.parser.css("nav.vert-nav li a").each do |row|
139
-
140
- unless (row['href'].nil?)
141
- url = row['href'].split('/')
142
-
143
- #puts url.length
144
-
145
- if (url.length >= 5) && (url[4].is_i?)
146
- #puts url[3]
147
-
148
- list = DistributionList.new
149
- list.id = url[4]
150
- list.name = row.css('> text()').text.strip
151
- list.count = row.css('span').text.strip
152
-
153
- lists << list
154
- end
155
-
156
- end
157
- end
158
-
159
- lists
160
- end
161
-
162
- def list_teams
163
- teams = []
164
-
165
- # Selected team
166
-
167
- selected_team = page.parser.css('ul.dropdown-menu li div div.textcontain180 text()')
168
-
169
- team = Team.new
170
- team.name = selected_team
171
-
172
- teams << team
173
-
174
- all_teams = page.parser.css('ul.ddteamlist li a')
175
- all_teams.each do |row|
176
- team = Team.new
177
- team.id = row['data-team-id']
178
- team.name = row.text
179
-
180
- teams << team
181
- end
182
-
183
- teams
184
- end
185
-
186
- private
187
-
188
- def login!
189
- if form = page.forms.first
190
- #puts "LOGGING IN" + self.username
191
-
192
- form.fields_with(type: 'email').first.value = self.username
193
- form.fields_with(type: 'password').first.value = self.password
194
-
195
- form.submit
196
- end
197
- end
198
-
199
- def select_team!
200
- teams = list_teams
201
-
202
- #puts teams
203
-
204
- # Check if desired team is different to selected team
205
-
206
- if @team != teams[0].name
207
- #puts "Selecting team..."
208
-
209
- if form = page.form_with(:id => 'team-change')
210
-
211
- team = teams.find{|t| t.name == @team}
212
-
213
- #puts team
214
-
215
- unless (team.nil?)
216
- form.team = team.id
217
- form.submit
218
- end
219
- end
220
- end
221
-
222
- @team = ''
223
- end
224
-
225
29
  end
226
30
  end
227
31
  end
@@ -3,15 +3,17 @@ include Atlantis::Portal
3
3
  require 'atlantis/portal/helpers'
4
4
  include Atlantis::Portal::Helpers
5
5
 
6
- global_option('-u', '--username USER', 'Username') { |arg| agent.username = arg unless arg.nil? }
7
- global_option('-p', '--password PASSWORD', 'Password') { |arg| agent.password = arg unless arg.nil? }
8
- global_option('--team TEAM', 'Team') { |arg| agent.team = arg if arg }
6
+ global_option('-u', '--username USER', 'Username') { |arg| arguments.username = arg unless arg.nil? }
7
+ global_option('-p', '--password PASSWORD', 'Password') { |arg| arguments.password = arg unless arg.nil? }
8
+ global_option('--team TEAM', 'Team or Organisation') { |arg| arguments.team = arg if arg }
9
9
  global_option('--info', 'Set log level to INFO') { agent.log.level = Logger::INFO }
10
10
  global_option('--debug', 'Set log level to DEBUG') { agent.log.level = Logger::DEBUG }
11
- global_option('--format [table|csv]', 'Set output format (default: table)') { |arg| agent.format = arg if arg }
11
+ global_option('--format [table|csv|json]', 'Set output format (default: table)') { |arg| arguments.format = arg if arg }
12
+ global_option('--group GROUP', 'Set group or distribution list') { |arg| arguments.group = arg if arg }
13
+ global_option('--service [crashlytics/testflight]', 'Set service (default: testflight)') { |arg| arguments.service = arg if arg }
12
14
 
13
15
  require 'atlantis/portal/commands/login'
14
16
  require 'atlantis/portal/commands/logout'
15
17
  require 'atlantis/portal/commands/people'
16
18
  require 'atlantis/portal/commands/devices'
17
- require 'atlantis/portal/commands/lists'
19
+ require 'atlantis/portal/commands/groups'
@@ -1,43 +1,12 @@
1
1
  command :'devices' do |c|
2
- c.syntax = 'testflight devices'
3
- c.summary = 'Lists devices on TestFlight'
2
+ c.syntax = 'distribution devices'
3
+ c.summary = 'Lists devices registered on specified service'
4
4
  c.description = ''
5
5
 
6
6
  c.action do |args, options|
7
- distribution_list = args.join(" ").strip
8
7
 
9
- if (distribution_list.empty?)
10
- distribution_list = 'all'
11
- end
12
- devices = try{agent.list_devices(distribution_list)}
8
+ devices = try{service.list_devices(arguments.group)}
13
9
 
14
- #devices = []
15
-
16
- if (agent.format == "csv")
17
- csv_string = CSV.generate do |csv|
18
- csv << ["Name", "UDID"]
19
-
20
- devices.each do |device|
21
- csv << [device.name, device.udid]
22
- end
23
- end
24
-
25
- puts csv_string
26
- else
27
-
28
- title = "Listing devices"
29
-
30
- table = Terminal::Table.new :title => title do |t|
31
- t << ["Name", "UDID"]
32
- t.add_separator
33
- devices.each do |device|
34
- t << [device.name, device.udid]
35
- end
36
- end
37
-
38
- #table.align_column 2, :center
39
-
40
- puts table
41
- end
10
+ output('Devices', ["Name", "Identifier"], devices)
42
11
  end
43
12
  end
@@ -0,0 +1,11 @@
1
+ command :'groups' do |c|
2
+ c.syntax = 'distribution groups'
3
+ c.summary = 'Lists all groups on service'
4
+ c.description = ''
5
+
6
+ c.action do |args, options|
7
+ lists = try{service.list_groups}
8
+
9
+ output('Groups', ["ID", "Name"], lists)
10
+ end
11
+ end
@@ -1,15 +1,15 @@
1
1
  command :login do |c|
2
- c.syntax = 'testflight login'
3
- c.summary = 'Save account credentials'
2
+ c.syntax = 'distribution login'
3
+ c.summary = 'Save account credentials for specific service'
4
4
  c.description = ''
5
5
 
6
6
  c.action do |args, options|
7
- say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Atlantis::Portal::HOST)
7
+ say_warning "You are already authenticated" if Security::InternetPassword.find(:server => service.host)
8
8
 
9
9
  user = ask "Username:"
10
10
  pass = password "Password:"
11
11
 
12
- Security::InternetPassword.add(Atlantis::Portal::HOST, user, pass)
12
+ Security::InternetPassword.add(service.host, user, pass)
13
13
 
14
14
  say_ok "Account credentials saved"
15
15
  end