atlantispro 0.1.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 +7 -0
- data/.gitignore +36 -0
- data/Atlantis.gemspec +31 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/testflight +28 -0
- data/lib/Atlantis/portal/agent.rb +227 -0
- data/lib/Atlantis/portal/commands/devices.rb +55 -0
- data/lib/Atlantis/portal/commands/invites.rb +16 -0
- data/lib/Atlantis/portal/commands/lists.rb +36 -0
- data/lib/Atlantis/portal/commands/login.rb +16 -0
- data/lib/Atlantis/portal/commands/logout.rb +13 -0
- data/lib/Atlantis/portal/commands/people.rb +43 -0
- data/lib/Atlantis/portal/commands/teams.rb +23 -0
- data/lib/Atlantis/portal/commands.rb +17 -0
- data/lib/Atlantis/portal/helpers.rb +53 -0
- data/lib/Atlantis/portal.rb +37 -0
- data/lib/Atlantis/version.rb +3 -0
- data/lib/Atlantis.rb +1 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d26ef74cf6841c913d9ba2fcc506c6e087f74cb2
|
4
|
+
data.tar.gz: 16fe9c2f4fd36bea89cb48fb9758beff361aa4df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9edfae2b2f3df6148fed90d537a3bb067a9116d9be1d597a79c17b54ab92d8b110b2b16992d788aa371fe47de1350cddda4a9d9ef107f8fcb975b3a5879e0898
|
7
|
+
data.tar.gz: 5ec42412019c8eac076d9c378ebc656089b0ef259b1957047654d77ec3423afff01e895cc4d9256ecdd69dc11203ea8a0e33c2be171a21108267f8b7663e630f
|
data/.gitignore
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
35
|
+
|
36
|
+
.idea/
|
data/Atlantis.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'atlantis/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "atlantispro"
|
8
|
+
spec.version = Atlantis::VERSION
|
9
|
+
spec.authors = ["Dal Rupnik"]
|
10
|
+
spec.email = ["legoless@gmail.com"]
|
11
|
+
spec.summary = "Some random description"
|
12
|
+
spec.description = "Yeees"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.add_dependency "commander", "~> 4.1.2"
|
17
|
+
spec.add_dependency "terminal-table", "~> 1.4.5"
|
18
|
+
spec.add_dependency "term-ansicolor", "~> 1.0.7"
|
19
|
+
spec.add_dependency "mechanize", "~> 2.5.1"
|
20
|
+
spec.add_dependency "nokogiri", "~> 1.5.9"
|
21
|
+
spec.add_dependency "security", "~> 0.1.2"
|
22
|
+
spec.add_dependency "certified", ">= 0.1.0"
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0")
|
25
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
26
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
30
|
+
spec.add_development_dependency "rake"
|
31
|
+
end
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 arvystate.net
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Atlantis
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'Atlantis'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install Atlantis
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/Atlantis/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/testflight
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'commander/import'
|
4
|
+
require 'terminal-table'
|
5
|
+
require 'term/ansicolor'
|
6
|
+
require 'csv'
|
7
|
+
|
8
|
+
#$:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
|
9
|
+
|
10
|
+
require 'atlantis'
|
11
|
+
|
12
|
+
#puts Atlantis::VERSION
|
13
|
+
|
14
|
+
HighLine.track_eof = false # Fix for built-in Ruby
|
15
|
+
Signal.trap("INT") {} # Suppress backtrace when exiting command
|
16
|
+
|
17
|
+
program :name, 'atlantis'
|
18
|
+
program :version, Atlantis::VERSION
|
19
|
+
program :description, 'A command-line interface for the TestFlight Portal'
|
20
|
+
|
21
|
+
program :help, 'Author', 'Dal Rupnik <legoless@gmail.com>'
|
22
|
+
program :help, 'Website', 'https://github.com/legoless'
|
23
|
+
program :help_formatter, :compact
|
24
|
+
|
25
|
+
default_command :help
|
26
|
+
|
27
|
+
require 'atlantis/portal'
|
28
|
+
require 'atlantis/portal/commands'
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'security'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
require 'nokogiri'
|
7
|
+
|
8
|
+
module Atlantis
|
9
|
+
module Portal
|
10
|
+
class Agent < ::Mechanize
|
11
|
+
attr_accessor :username, :password, :team, :format
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
|
16
|
+
self.user_agent_alias = 'Mac Safari'
|
17
|
+
|
18
|
+
self.log ||= Logger.new(STDOUT)
|
19
|
+
self.log.level = Logger::ERROR
|
20
|
+
|
21
|
+
if ENV['HTTP_PROXY']
|
22
|
+
uri = URI.parse(ENV['HTTP_PROXY'])
|
23
|
+
user = ENV['HTTP_PROXY_USER'] if ENV['HTTP_PROXY_USER']
|
24
|
+
password = ENV['HTTP_PROXY_PASSWORD'] if ENV['HTTP_PROXY_PASSWORD']
|
25
|
+
|
26
|
+
set_proxy(uri.host, uri.port, user || uri.user, password || uri.password)
|
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
|
+
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
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
command :'devices' do |c|
|
2
|
+
c.syntax = 'testflight devices'
|
3
|
+
c.summary = 'Lists devices on TestFlight'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
distribution_list = args.join(" ").strip
|
8
|
+
|
9
|
+
if (distribution_list.empty?)
|
10
|
+
distribution_list = 'all'
|
11
|
+
end
|
12
|
+
devices = try{agent.list_devices(distribution_list)}
|
13
|
+
|
14
|
+
device = Device.new
|
15
|
+
device.udid = "aaaaaasdasdsadsaddsa"
|
16
|
+
device.name = "Test device cock!"
|
17
|
+
|
18
|
+
devices << device
|
19
|
+
|
20
|
+
device = Device.new
|
21
|
+
device.udid = "aafffffa"
|
22
|
+
device.name = "Test device 2 cock!"
|
23
|
+
|
24
|
+
devices << device
|
25
|
+
|
26
|
+
#devices = []
|
27
|
+
|
28
|
+
if (agent.format == "csv")
|
29
|
+
csv_string = CSV.generate do |csv|
|
30
|
+
csv << ["Name", "UDID"]
|
31
|
+
|
32
|
+
devices.each do |device|
|
33
|
+
csv << [device.name, device.udid]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
puts csv_string
|
38
|
+
else
|
39
|
+
|
40
|
+
title = "Listing devices"
|
41
|
+
|
42
|
+
table = Terminal::Table.new :title => title do |t|
|
43
|
+
t << ["Name", "UDID"]
|
44
|
+
t.add_separator
|
45
|
+
devices.each do |device|
|
46
|
+
t << [device.name, device.udid]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#table.align_column 2, :center
|
51
|
+
|
52
|
+
puts table
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
command :login do |c|
|
2
|
+
c.syntax = 'testflight login'
|
3
|
+
c.summary = 'Save account credentials'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Atlantis::Portal::HOST)
|
8
|
+
|
9
|
+
user = ask "Username:"
|
10
|
+
pass = password "Password:"
|
11
|
+
|
12
|
+
Security::InternetPassword.add(Atlantis::Portal::HOST, user, pass)
|
13
|
+
|
14
|
+
say_ok "Account credentials saved"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
command :'lists' do |c|
|
2
|
+
c.syntax = 'testflight lists'
|
3
|
+
c.summary = 'Lists all distribution lists on TestFlight'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
lists = try{agent.list_lists}
|
8
|
+
|
9
|
+
if (agent.format == "csv")
|
10
|
+
csv_string = CSV.generate do |csv|
|
11
|
+
csv << ["ID", "Name", "Count"]
|
12
|
+
|
13
|
+
lists.each do |list|
|
14
|
+
csv << [list.id, list.name, list.count]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
puts csv_string
|
19
|
+
|
20
|
+
else
|
21
|
+
title = "Listing distribution lists"
|
22
|
+
|
23
|
+
table = Terminal::Table.new :title => title do |t|
|
24
|
+
t << ["ID", "Name", "Count"]
|
25
|
+
t.add_separator
|
26
|
+
lists.each do |list|
|
27
|
+
t << [list.id, list.name, list.count]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#table.align_column 2, :center
|
32
|
+
|
33
|
+
puts table
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
command :login do |c|
|
2
|
+
c.syntax = 'testflight login'
|
3
|
+
c.summary = 'Save account credentials'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
say_warning "You are already authenticated" if Security::InternetPassword.find(:server => Atlantis::Portal::HOST)
|
8
|
+
|
9
|
+
user = ask "Username:"
|
10
|
+
pass = password "Password:"
|
11
|
+
|
12
|
+
Security::InternetPassword.add(Atlantis::Portal::HOST, user, pass)
|
13
|
+
|
14
|
+
say_ok "Account credentials saved"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
command :logout do |c|
|
2
|
+
c.syntax = 'testflight logout'
|
3
|
+
c.summary = 'Remove account credentials'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
say_error "You are not authenticated" and abort unless Security::InternetPassword.find(:server => Atlantis::Portal::HOST)
|
8
|
+
|
9
|
+
Security::InternetPassword.delete(:server => Atlantis::Portal::HOST)
|
10
|
+
|
11
|
+
say_ok "Account credentials removed"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
command :'people' do |c|
|
2
|
+
c.syntax = 'testflight people [DISTRIBUTION_LIST]'
|
3
|
+
c.summary = 'Lists people currently signed up to TestFlight'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
|
8
|
+
distribution_list = args.join(" ").strip
|
9
|
+
|
10
|
+
if (distribution_list.empty?)
|
11
|
+
distribution_list = 'all'
|
12
|
+
end
|
13
|
+
|
14
|
+
people = try{agent.list_people(distribution_list)}
|
15
|
+
|
16
|
+
if (agent.format == "csv")
|
17
|
+
csv_string = CSV.generate do |csv|
|
18
|
+
csv << ["ID", "User", "Email", "Devices"]
|
19
|
+
|
20
|
+
people.each do |person|
|
21
|
+
csv << [person.id, person.name, person.email, person.devices]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
puts csv_string
|
26
|
+
|
27
|
+
else
|
28
|
+
title = "Listing people"
|
29
|
+
|
30
|
+
table = Terminal::Table.new :title => title do |t|
|
31
|
+
t << ["ID", "User", "Email", "Devices"]
|
32
|
+
t.add_separator
|
33
|
+
people.each do |person|
|
34
|
+
t << [person.id, person.name, person.email, person.devices]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#table.align_column 2, :center
|
39
|
+
|
40
|
+
puts table
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
command :'teams' do |c|
|
2
|
+
c.syntax = 'testflight teams'
|
3
|
+
c.summary = 'Lists all teams on TestFlight'
|
4
|
+
c.description = ''
|
5
|
+
|
6
|
+
c.action do |args, options|
|
7
|
+
lists = try{agent.list_lists}
|
8
|
+
|
9
|
+
title = "Listing distribution lists"
|
10
|
+
|
11
|
+
table = Terminal::Table.new :title => title do |t|
|
12
|
+
t << ["ID", "Name", "Count"]
|
13
|
+
t.add_separator
|
14
|
+
lists.each do |list|
|
15
|
+
t << [list.id, list.name, list.count]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#table.align_column 2, :center
|
20
|
+
|
21
|
+
puts table
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
include Atlantis::Portal
|
2
|
+
|
3
|
+
require 'atlantis/portal/helpers'
|
4
|
+
include Atlantis::Portal::Helpers
|
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 }
|
9
|
+
global_option('--info', 'Set log level to INFO') { agent.log.level = Logger::INFO }
|
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 }
|
12
|
+
|
13
|
+
require 'atlantis/portal/commands/login'
|
14
|
+
require 'atlantis/portal/commands/logout'
|
15
|
+
require 'atlantis/portal/commands/people'
|
16
|
+
require 'atlantis/portal/commands/devices'
|
17
|
+
require 'atlantis/portal/commands/lists'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Monkey Patch Commander::UI to alias password to avoid conflicts
|
2
|
+
module Commander::UI
|
3
|
+
alias :pw :password
|
4
|
+
end
|
5
|
+
|
6
|
+
class String
|
7
|
+
include Term::ANSIColor
|
8
|
+
end
|
9
|
+
|
10
|
+
class String
|
11
|
+
def is_i?
|
12
|
+
!!(self =~ /\A[-+]?[0-9]+\z/)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Atlantis
|
17
|
+
module Portal
|
18
|
+
module Helpers
|
19
|
+
def agent
|
20
|
+
unless @agent
|
21
|
+
@agent = Atlantis::Portal::Agent.new
|
22
|
+
|
23
|
+
@agent.instance_eval do
|
24
|
+
def username
|
25
|
+
@username ||= ask "Username:"
|
26
|
+
end
|
27
|
+
|
28
|
+
def password
|
29
|
+
@password ||= pw "Password:"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@agent
|
35
|
+
end
|
36
|
+
|
37
|
+
def pluralize(n, singular, plural = nil)
|
38
|
+
n.to_i == 1 ? "1 #{singular}" : "#{n} #{plural || singular + 's'}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def try
|
42
|
+
return unless block_given?
|
43
|
+
|
44
|
+
begin
|
45
|
+
yield
|
46
|
+
rescue UnsuccessfulAuthenticationError
|
47
|
+
say_error "Could not authenticate with TestFlight. Check that your username & password are correct, and that your membership is valid and all pending Terms of Service & agreements are accepted. If this problem continues, try logging into https://testflightapp.com/ from a browser to see what's going on." and abort
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'certified'
|
3
|
+
|
4
|
+
module Atlantis
|
5
|
+
module Portal
|
6
|
+
HOST = "testflightapp.com"
|
7
|
+
|
8
|
+
class UnsuccessfulAuthenticationError < RuntimeError; end
|
9
|
+
class UnexpectedContentError < RuntimeError; end
|
10
|
+
|
11
|
+
class Person < Struct.new(:id, :name, :email, :devices)
|
12
|
+
def to_s
|
13
|
+
"#{self.id} #{self.name} #{self.email} #{self.devices}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Device < Struct.new(:udid, :name)
|
18
|
+
def to_s
|
19
|
+
"#{self.udid} #{self.name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class DistributionList < Struct.new(:id, :name, :count)
|
24
|
+
def to_s
|
25
|
+
"#{self.id} #{self.name} #{self.count}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Team < Struct.new(:id, :name, :token)
|
30
|
+
def to_s
|
31
|
+
"#{self.id} #{self.name} #{self.token}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'atlantis/portal/agent'
|
data/lib/Atlantis.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "atlantis/version"
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: atlantispro
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dal Rupnik
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: commander
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.1.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.1.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: terminal-table
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.4.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.4.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: term-ansicolor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.0.7
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.0.7
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mechanize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.5.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.5.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: nokogiri
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.5.9
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.5.9
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: security
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.1.2
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.1.2
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: certified
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.1.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.1.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: bundler
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.6'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.6'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rake
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Yeees
|
140
|
+
email:
|
141
|
+
- legoless@gmail.com
|
142
|
+
executables:
|
143
|
+
- testflight
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- ".gitignore"
|
148
|
+
- Atlantis.gemspec
|
149
|
+
- Gemfile
|
150
|
+
- LICENSE
|
151
|
+
- README.md
|
152
|
+
- Rakefile
|
153
|
+
- bin/testflight
|
154
|
+
- lib/Atlantis.rb
|
155
|
+
- lib/Atlantis/portal.rb
|
156
|
+
- lib/Atlantis/portal/agent.rb
|
157
|
+
- lib/Atlantis/portal/commands.rb
|
158
|
+
- lib/Atlantis/portal/commands/devices.rb
|
159
|
+
- lib/Atlantis/portal/commands/invites.rb
|
160
|
+
- lib/Atlantis/portal/commands/lists.rb
|
161
|
+
- lib/Atlantis/portal/commands/login.rb
|
162
|
+
- lib/Atlantis/portal/commands/logout.rb
|
163
|
+
- lib/Atlantis/portal/commands/people.rb
|
164
|
+
- lib/Atlantis/portal/commands/teams.rb
|
165
|
+
- lib/Atlantis/portal/helpers.rb
|
166
|
+
- lib/Atlantis/version.rb
|
167
|
+
homepage: ''
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.0.14
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: Some random description
|
191
|
+
test_files: []
|