TestFlightExporter 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 47abbef6ad6404d51f0b614e50de932db2717d82
4
+ data.tar.gz: 29e77927e625b7d47870f4e5c8d2452135e7ef87
5
+ SHA512:
6
+ metadata.gz: 2ea24bf47268d11890ef98c6e3b97d18296f18da34e0bbe17f3ca7f6a1a8c2940ef4444ec2e348136aa8adc45654eb7855bde03e350012621357666a095f3e68
7
+ data.tar.gz: b4e15239a2ba25da69590ae2a3446b3bdc4079eb87860e436b5d334791412372cf1ff9cfa1ff114c3d76b268c2a17303f54baa86b416aa7efa1fde5ad4cb04aa
data/.gitignore ADDED
@@ -0,0 +1,29 @@
1
+ ### Ruby template
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ ## Documentation cache and generated files:
14
+ /.yardoc/
15
+ /_yardoc/
16
+ /doc/
17
+ /rdoc/
18
+
19
+ ## Environment normalisation:
20
+ /.bundle/
21
+ /lib/bundler/man/
22
+
23
+ # for a library or gem, you might want to ignore these files since the code is
24
+ # intended to run in multiple environments; otherwise, check them in:
25
+ Gemfile.lock
26
+ # .ruby-version
27
+ # .ruby-gemset
28
+
29
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in osiris.gemspec
4
+ gemspec
5
+
6
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Touchwonders B.V.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # TestFlight Exporter
2
+ **TL;DR**: TestFlight Exporter is a simple CLI tool that downloads archived builds from your TestFlightapp.com account.
3
+
4
+ After years of faithful service, the original TestFlight is closing down on February 26th 2015. To ease the transition to a new beta distribution system, TestFlight provides a way to export existing teams and testers. A way to download your .ipa files, however, is lacking. That's where TestFlightExporter comes in!
5
+
6
+ TestFlight Exporter can download the builds, including release notes, from your account and stores them in a nice folder structure on your local machine.
7
+
8
+ ## Installation
9
+
10
+ TestFlight Exporter is available as a gem, which you can easily install by executing the following command in your terminal:
11
+
12
+ $ gem install testflight_exporter
13
+
14
+ ## Usage
15
+
16
+ TestFlight Exporter is available as a simple CLI tool.
17
+ You can invoke it by typing `tfexporter` on your terminal.
18
+
19
+ Follow the setup assistent, which will configure the current TestFlight Exporter run to your needs. All selected builds and release notes will be saved in a folder named `out`.
20
+
21
+ **Warning**: Depending on the number of builds you have in your TestFlight account, TestFlight Exporter could consume a lot of data/bandwidth.
22
+
23
+ ## How does this thing work?
24
+
25
+ TestFlight doesn't provide any API to perform such task like exporting your .ipa's. TestFlight Exporter uses [mechanize](https://github.com/sparklemotion/mechanize) to automatically find and follow the download links on the TestFlight website.
26
+
27
+ TestFlight Exporter uses your credentials to access your TestFlight account (over https). Other than that, your credentials do not leave your machine.
28
+
29
+ ## Incorporating TestFlight Exporter into your project
30
+ If you want to use the functionality of TestFlight Exporter directly rather than as a command-line tool, you can include it into your ruby product and use it from your code.
31
+
32
+ To include it in your project, add this line to your application's Gemfile:
33
+
34
+ ```ruby
35
+ gem 'testflight_exporter'
36
+ ```
37
+
38
+ And then execute:
39
+
40
+ $ bundle install
41
+
42
+ ## License
43
+
44
+ TestFlight exporter is available under the MIT license. See the LICENSE file for more info.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+
5
+ require 'commander'
6
+ require 'highline'
7
+ require_relative '../lib/testflight_exporter/version'
8
+ require_relative '../lib/testflight_exporter'
9
+
10
+ HighLine.track_eof = false
11
+
12
+
13
+ class TestFlightExporterApplication
14
+ include Commander::Methods
15
+
16
+ def run
17
+ program :version, Osiris::VERSION
18
+ program :description, 'CLI for \'Test Flight Exporter\' - Migrate all your IPA automatically on your Mac'
19
+ program :help, 'Author', 'Fabio Milano'
20
+ program :help, 'Website', 'http://www.touchwonders.com'
21
+ program :help_formatter, :compact
22
+
23
+ always_trace!
24
+
25
+ command :migrate do |c|
26
+ c.syntax = 'tfexporter migrate'
27
+ c.description = 'Helps you setting up all requirements to run a migration.'
28
+
29
+ c.action do |args, options|
30
+ TestFlightExporter::Setup.new.setup
31
+ end
32
+ end
33
+
34
+ default_command :migrate
35
+
36
+ run!
37
+ end
38
+ end
39
+
40
+ TestFlightExporterApplication.new.run
data/lib/helpers.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'logger'
2
+
3
+
4
+ class String
5
+ def classify
6
+ self.split('_').collect!{ |w| w.capitalize }.join
7
+ end
8
+ end
9
+
10
+
11
+
12
+ module TestFlightExporter
13
+ module Helper
14
+
15
+ # Logging happens using this method
16
+ def self.log
17
+ @@log ||= Logger.new(STDOUT)
18
+
19
+ @@log.formatter = proc do |severity, datetime, progname, msg|
20
+ string = "#{severity} [#{datetime.strftime('%Y-%m-%d %H:%M:%S.%2N')}]: "
21
+ second = "#{msg}\n"
22
+
23
+ if severity == "DEBUG"
24
+ string = string.magenta
25
+ elsif severity == "INFO"
26
+ string = string.white
27
+ elsif severity == "WARN"
28
+ string = string.yellow
29
+ elsif severity == "ERROR"
30
+ string = string.red
31
+ elsif severity == "FATAL"
32
+ string = string.red.bold
33
+ end
34
+
35
+
36
+ [string, second].join("")
37
+ end
38
+
39
+ @@log
40
+ end
41
+
42
+ # EXIT HANDLERS
43
+
44
+ # Print error text with error format and exit with in input error_code (default=1)
45
+ def self.exit_with_error (message, error_code=1)
46
+ log.error message.red
47
+ exit (error_code)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module Osiris
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,264 @@
1
+ require 'mechanize'
2
+ require 'fileutils'
3
+ require 'colored'
4
+
5
+ require_relative 'helpers'
6
+
7
+ module TestFlightExporter
8
+
9
+ include Helper
10
+
11
+ class Setup
12
+
13
+ def initialize
14
+ @agent = Mechanize.new
15
+ @team_list = Hash.new
16
+ end
17
+
18
+ def setup
19
+ "Welcome to Osiris Migration Tool."
20
+ puts "Before we start I need some information about your TestFlight account."
21
+
22
+ @username = ask("Enter your TestFlight username: ") { |q| q.echo = true }
23
+ @password = ask("Enter your TestFlight password: ") { |q| q.echo = "*" }
24
+ @path = ask("Enter your output folder where all the IPAs will be downloaded: "){ |q| q.echo = true }
25
+
26
+ # Validate ouput folder
27
+ if File.directory?(@path)
28
+ # The in input path already exist we prefer to fail instead of using overriding policies
29
+ Helper.exit_with_error "\"#{@path}\" is an existing directory. Please specify another output folder".red
30
+ return
31
+ end
32
+
33
+ # Initialize Test Flight login page
34
+ login_page_url = "https://testflightapp.com/login/"
35
+
36
+ @agent.get(login_page_url) { |page| process_login_page page }
37
+ end
38
+
39
+ def process_login_page page
40
+
41
+ # Init login process
42
+ Helper.log.debug 'Login...'.yellow
43
+
44
+ login_form = page.forms.first # by pretty printing the page this is safe catch
45
+
46
+ login_field = login_form.field_with(:name => 'username')
47
+ password_field = login_form.field_with(:name => 'password')
48
+
49
+ login_field.value = @username
50
+ password_field.value = @password
51
+
52
+ login_form.submit
53
+
54
+ @agent.get("https://testflightapp.com/dashboard/applications/") do |dashboard_page|
55
+
56
+ @dashboard_page = dashboard_page
57
+
58
+ @dashboard_page.links.each do |ll|
59
+ # Retrieve current team
60
+ if ll.attributes.attributes['class']
61
+ @current_team = ll if ll.attributes.attributes['class'].text.eql? "dropdown-toggle team-menu"
62
+ end
63
+
64
+ # Retrieve other team id list
65
+ @team_list.merge!({ll.text=>ll.attributes['data-team-id']}) if ll.attributes['data-team-id']
66
+ end
67
+
68
+ # If we don't have any current team something went wrong with the authentication process
69
+ if (@current_team.nil? || @current_team.text.empty?)
70
+ Helper.exit_with_error "Something went wrong during authentication process."
71
+ end
72
+
73
+ if (@team_list.count > 1)
74
+
75
+ # We have multiple teams for current account hence we present a nice menu selection to the user
76
+ choose do |menu|
77
+ menu.prompt = "Please choose your team? "
78
+
79
+ @team_list.each do |team_name, team_id|
80
+ menu.choice(team_name) do |choice|
81
+ process_teams false, choice
82
+ end
83
+ end
84
+
85
+ menu.choice("All teams") { process_teams true }
86
+ end
87
+ else
88
+ # process current team
89
+ puts ""
90
+ Helper.log.info "This could take a while... ☕️".green
91
+ Helper.log.info "Processing team: #{@current_team}".blue
92
+
93
+ process_dashboard_page dashboard_page
94
+ end
95
+
96
+ end
97
+ end
98
+
99
+ def process_teams process_all_teams, team_name=nil
100
+
101
+ if process_all_teams
102
+ # process current team
103
+ puts ""
104
+ Helper.log.info "This could take a while... ☕️".green
105
+ Helper.log.info "Processing team: #{@current_team}".blue
106
+
107
+ process_dashboard_page @dashboard_page
108
+
109
+ # process other teams
110
+ @team_list.each do |team_name, team_id|
111
+ switch_to_team_id team_id, team_name
112
+ process_dashboard_page @agent.get("https://testflightapp.com/dashboard/applications/")
113
+ end
114
+ else
115
+ if @current_team.eql? team_name
116
+ # process current team
117
+ Helper.log.info "Processing team: #{@current_team}".blue
118
+
119
+ process_dashboard_page @dashboard_page
120
+ return
121
+ else
122
+ if @team_list["#{team_name}"].nil?
123
+ Helper.exit_with_error "Sorry, I can\'t find #{team_name} in your teams.".red
124
+ return
125
+ end
126
+
127
+ switch_to_team_id @team_list["#{team_name}"], team_name
128
+
129
+ # process current team
130
+
131
+ process_dashboard_page @agent.get("https://testflightapp.com/dashboard/applications/")
132
+ return
133
+ end
134
+ end
135
+ end
136
+
137
+
138
+ def switch_to_team_id team_id, team_name
139
+ team_switch = @dashboard_page.forms.first
140
+ team_id_field = team_switch.field_with(:name => 'team')
141
+ team_id_field.value = team_id
142
+
143
+ begin
144
+ team_switch.submit
145
+ @current_team = team_name
146
+ Helper.log.info "Processing team: #{@current_team}".blue
147
+ rescue Exception => e
148
+ Helper.exit_with_error e
149
+ end
150
+
151
+
152
+ end
153
+
154
+ def process_dashboard_page dashboard_page
155
+ app_link_pattern = /\/dashboard\/applications\/(.*?)\/token\//
156
+ dashboard_page.links.each do |link|
157
+ link.href =~ app_link_pattern
158
+ if $1 != nil
159
+ Helper.log.debug "Builds page for #{$1}...".magenta
160
+
161
+ @agent.get "https://testflightapp.com/dashboard/applications/#{$1}/builds/" do |builds_page|
162
+
163
+ # Processing app information to retrieve additional information on current application
164
+ app_information_link = builds_page.link_with(:text => "App Information")
165
+
166
+ process_app_information app_information_link.href
167
+
168
+ # Collection of all pages for current build
169
+ inner_pages = Array.new
170
+
171
+ builds_page.links.each do |ll|
172
+ inner_pages.push(ll.href) if ll.href =~ /\?page=*/ unless inner_pages.include?(ll.href)
173
+ end
174
+
175
+ number_of_pages = inner_pages.count + 1
176
+ Helper.log.debug "Page 1 of #{number_of_pages}".magenta
177
+ puts ""
178
+
179
+ # Process current build page
180
+ process_builds_page builds_page
181
+
182
+ # Cycle over remaning build pages
183
+ i = 2
184
+ inner_pages.each do |page|
185
+ puts ""
186
+ Helper.log.debug "Page #{i} of #{number_of_pages}".magenta
187
+
188
+ process_builds_page @agent.get "https://testflightapp.com#{page}"
189
+
190
+ i = i+1
191
+ end
192
+
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+
199
+ def process_app_information app_information_page
200
+ @agent.get "https://testflightapp.com#{app_information_page}" do |app_information|
201
+ # Looking up the bundle identifier
202
+ @current_bundle_identifier = app_information.at("strong:contains('BundleID')").parent.parent.at("td").next.text
203
+ end
204
+ end
205
+
206
+ def process_builds_page page
207
+ body = page.body
208
+ build_pages = body.scan /<tr class="goversion pointer" id="\/dashboard\/builds\/report\/(.*?)\/">/
209
+
210
+ build_pages.each do |build_id|
211
+ @agent.get "https://testflightapp.com/dashboard/builds/complete/#{build_id.first}/" do |build_page|
212
+ # Retrieve current app name
213
+ @app_name = page.at("h2").text
214
+
215
+ process_build_page build_page
216
+ end
217
+ end
218
+ end
219
+
220
+ def process_build_page page
221
+ build_link = page.links_with(:dom_class => 'bitly').first
222
+ @agent.get("https://www.testflightapp.com#{build_link.href}") { |install_page| process_install_page install_page}
223
+ end
224
+
225
+ def process_install_page page
226
+ # we need to figure out what kind of build is that
227
+ release_note = page.search('.clearfix').at("p").text
228
+
229
+ Helper.log.debug "RELEASE NOTE".magenta
230
+ Helper.log.debug release_note.magenta
231
+
232
+ ipa_link = page.link_with(:text => "download the IPA.")
233
+ ipa_link = page.link_with(:text => "download the IPA") if ipa_link.nil?
234
+
235
+ if ipa_link.nil?
236
+ Helper.log.warn "No IPA link found. Do you have permission for current application?".yellow
237
+ else
238
+ download_build(ipa_link, "ipa", release_note)
239
+ end
240
+
241
+ puts ""
242
+ end
243
+
244
+ def download_build link, file_ext, release_note
245
+
246
+ link.href =~ /\/dashboard\/ipa\/(.*?)\//
247
+ filename = "#{$1}.#{file_ext}"
248
+
249
+ file_url = "https://www.testflightapp.com#{link.href}"
250
+ Helper.log.debug "Downloading #{file_url}...".magenta
251
+
252
+ dirname = File.dirname("#{@path}/#{@current_team}")
253
+
254
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
255
+
256
+ dirname = File.dirname("#{@path}/#{@current_team}/#{@current_bundle_identifier} builds")
257
+
258
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
259
+
260
+ @agent.get(file_url).save("#{@path}/#{@current_team}/#{@current_bundle_identifier} builds/#{filename}")
261
+ File.open("#{@path}/#{@current_team}/#{@current_bundle_identifier} builds/#{$1}.txt", 'w') {|f| f.write(release_note) }
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'testflight_exporter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "TestFlightExporter"
8
+ spec.version = Osiris::VERSION
9
+ spec.authors = ["Fabio Milano"]
10
+ spec.email = ["fabio@touchwonders.com"]
11
+ spec.summary = %q{A simple tool that helps you migrating your TestFlight build to your local environment}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "mechanize", '~> 2.7' # to parse www pages
20
+ spec.add_dependency 'highline', '~> 1.6' # user inputs (e.g. passwords)
21
+ spec.add_dependency 'colored' # coloured terminal output
22
+ spec.add_dependency 'commander', '~> 4.2' # CLI parser
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
data/tfexplorer.rb ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mechanize'
4
+ require 'fileutils'
5
+
6
+ @agent = Mechanize.new
7
+
8
+ def process_login_page page
9
+
10
+ puts 'Login...'
11
+
12
+ login_form = page.forms.first # by pretty printing the page this is safe catch
13
+
14
+ login_field = login_form.field_with(:name => 'username')
15
+ password_field = login_form.field_with(:name => 'password')
16
+
17
+ login_field.value = 'fabio@touchwonders.com'
18
+ password_field.value = 'touchwondersworld'
19
+
20
+ login_form.submit
21
+
22
+ puts 'Dashboard...'
23
+
24
+ team_list = Hash.new
25
+ @current_team = String.new
26
+
27
+ @agent.get("https://testflightapp.com/dashboard/applications/") do |dashboard_page|
28
+ dashboard_page.links.each do |ll|
29
+ # Retrieve current team
30
+ if ll.attributes.attributes['class']
31
+ @current_team = ll if ll.attributes.attributes['class'].text.eql? "dropdown-toggle team-menu"
32
+ end
33
+
34
+ # Retrieve other team id list
35
+ team_list.merge!({ll.attributes['data-team-id']=>ll.text}) if ll.attributes['data-team-id']
36
+ end
37
+
38
+ # process current team
39
+ puts "Processing team: #{@current_team}"
40
+
41
+ process_dashboard_page dashboard_page
42
+
43
+ # process other teams
44
+ team_list.each do |team_id, team_name|
45
+ team_switch = dashboard_page.forms.first
46
+ team_id_field = team_switch.field_with(:name => 'team')
47
+ team_id_field.value = team_id
48
+ team_switch.submit
49
+ @current_team = team_name
50
+
51
+ process_dashboard_page @agent.get("https://testflightapp.com/dashboard/applications/")
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ def process_dashboard_page dashboard_page
58
+ app_link_pattern = /\/dashboard\/applications\/(.*?)\/token\//
59
+ dashboard_page.links.each do |link|
60
+ link.href =~ app_link_pattern
61
+ if $1 != nil
62
+ puts "Builds page for #{$1}..."
63
+ @agent.get "https://testflightapp.com/dashboard/applications/#{$1}/builds/" do |builds_page|
64
+
65
+ # Collection of all pages for current build
66
+ inner_pages = Array.new
67
+
68
+ builds_page.links.each do |ll|
69
+ inner_pages.push(ll.href) if ll.href =~ /\?page=*/ unless inner_pages.include?(ll.href)
70
+ end
71
+
72
+ number_of_pages = inner_pages.count + 1
73
+ puts "Processing page 1 of #{number_of_pages}"
74
+ puts ""
75
+
76
+ # Process current build page
77
+ process_builds_page builds_page
78
+
79
+ # Cycle over remaning build pages
80
+ i = 2
81
+ inner_pages.each do |page|
82
+ puts ""
83
+ puts "Processing page #{i} of #{number_of_pages}"
84
+
85
+ process_builds_page @agent.get "https://testflightapp.com#{page}"
86
+
87
+ i = i+1
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def process_builds_page page
96
+ body = page.body
97
+ build_pages = body.scan /<tr class="goversion pointer" id="\/dashboard\/builds\/report\/(.*?)\/">/
98
+
99
+ build_pages.each do |build_id|
100
+ @agent.get "https://testflightapp.com/dashboard/builds/complete/#{build_id.first}/" do |build_page|
101
+ # Retrieve current app name
102
+ @app_name = page.at("h2").text
103
+
104
+ process_build_page build_page
105
+ end
106
+ end
107
+ end
108
+
109
+ def process_build_page page
110
+ build_link = page.links_with(:dom_class => 'bitly').first
111
+ @agent.get("https://www.testflightapp.com#{build_link.href}") { |install_page| process_install_page install_page}
112
+ end
113
+
114
+ def process_install_page page
115
+ # we need to figure out what kind of build is that
116
+ release_note = page.search('.clearfix').at("p").text
117
+
118
+ puts "RELEASE NOTE"
119
+ puts release_note
120
+
121
+ ipa_link = page.link_with(:text => "download the IPA.")
122
+ ipa_link = ipa_link = page.link_with(:text => "download the IPA") if ipa_link.nil?
123
+
124
+ if ipa_link.nil?
125
+ puts "No IPA link found. Do you have permission for current application?"
126
+ else
127
+ download_build(ipa_link, "ipa", release_note)
128
+ end
129
+
130
+ puts ""
131
+ end
132
+
133
+ def download_build link, file_ext, release_note
134
+
135
+ link.href =~ /\/dashboard\/ipa\/(.*?)\//
136
+ filename = "#{$1}.#{file_ext}"
137
+
138
+ file_url = "https://www.testflightapp.com#{link.href}"
139
+ puts "Downloading #{file_url}..."
140
+
141
+ dirname = File.dirname("out/#{@current_team}")
142
+
143
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
144
+
145
+ dirname = File.dirname("out/#{@current_team}/#{@app_name}")
146
+
147
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
148
+
149
+ @agent.get(file_url).save("out/#{@current_team}/#{@app_name}/#{filename}")
150
+ File.open("out/#{@current_team}/#{@app_name}/#{$1}.txt", 'w') {|f| f.write(release_note) }
151
+ end
152
+
153
+ FileUtils.rm_rf "out"
154
+ Dir.mkdir "out"
155
+
156
+ login_page_url = "https://testflightapp.com/login/"
157
+ @agent.get(login_page_url) { |page| process_login_page page }
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: TestFlightExporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Fabio Milano
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mechanize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colored
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: commander
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ description:
98
+ email:
99
+ - fabio@touchwonders.com
100
+ executables:
101
+ - testflight_exporter
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/testflight_exporter
111
+ - lib/helpers.rb
112
+ - lib/testflight_exporter.rb
113
+ - lib/testflight_exporter/version.rb
114
+ - testflight_exporter.gemspec
115
+ - tfexplorer.rb
116
+ homepage: ''
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.4.4
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: A simple tool that helps you migrating your TestFlight build to your local
140
+ environment
141
+ test_files: []