testflight 0.1.1 → 0.1.2

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.
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = Introduction
2
+
3
+ If you are an iOS developer, like me, you probably have dealt with the tedious
4
+ tasks of manually building, packaging, tagging, commiting and deploying your
5
+ iOS applications to testflightapp.com. Your probably wondered if there is an easier way to do so.
6
+
7
+ Well, you are absolutely right! This gem is going to make your life much easier. Here is what it does:
8
+
9
+ 1. Reads your XCode project defintion to get your app version and build number
10
+ 2. Commits all your changes to the git repository
11
+ 3. Builds your project (or workspace)
12
+ 4. Packages your application into an IPA file
13
+ 5. Uploads the IPA file to testflightapp.com
14
+ 6. Tags your project with the version and build
15
+ 7. Increments your app build number and commits it to the repo
16
+
17
+ All that with a simple command:
18
+
19
+ $ takeoff
20
+
21
+
22
+ = Configuration
23
+
24
+ All you have to do is add .tesflight file to your project folder with the following content:
25
+
26
+ build:
27
+ developer_name: # This must be your company name or your name as it appears in your certificate
28
+ increment_bundle: true
29
+ commit_changes: true
30
+ testflight:
31
+ api_token: # Get it from https://testflightapp.com/account/#api
32
+ team_token: # Get it from https://testflightapp.com/dashboard/team/edit/
33
+ distribution_lists: ["Internal", "Everyone"] # Configure your distribution lists on https://testflightapp.com
34
+
35
+
36
+ Then go to your project folder and simple type:
37
+
38
+ $ takeoff
39
+
40
+
41
+ Have a safe flight!
42
+
43
+
44
+ = Contributions
45
+
46
+ Clone the repository, make any changes you like and send me a pull request.
47
+
48
+
data/bin/takeoff CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'fileutils'
4
- require 'yaml'
5
3
  require 'pp'
6
- require 'plist'
4
+
5
+ require File.expand_path(File.join(File.dirname(__FILE__), "../lib/testflight/config.rb"))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "../lib/testflight/prompter.rb"))
7
7
 
8
8
  XCODE_BUILDER = "/usr/bin/xcodebuild"
9
9
  XCODE_PACKAGER = "/usr/bin/xcrun"
@@ -17,260 +17,90 @@ TESTFLIGHT_ENDPOINT = "http://testflightapp.com/api/builds.json"
17
17
  # package files
18
18
  # deploy to testflight app
19
19
 
20
-
21
- def project_dir
22
- Dir.pwd
23
- end
24
-
25
- def project_files
26
- @project_files ||= Dir.entries(project_dir)
27
- end
28
-
29
- def file_name_by_ext(ext)
30
- project_files.select{|file| file.match(/#{ext}$/)}.first
31
- end
32
-
33
-
34
- def workspace_name
35
- @workspace_name ||= file_name_by_ext('xcworkspace')
36
- end
37
-
38
- def project_name
39
- @project_name ||= file_name_by_ext('xcodeproj')
40
- end
41
-
42
- def type
43
- return "unknown" if workspace_name.nil? and project_name.nil?
44
- @type ||= workspace_name ? 'workspace' : 'project'
45
- end
46
-
47
- def application_name
48
- @application_name ||= begin
49
- name = workspace_name || project_name
50
- name = name.split(".")[0..-2].join(".") if name
51
- name
52
- end
53
- end
54
-
55
- def default_config
56
- {
57
- "build" => {
58
- "developer_name" => "As it appears in your Apple certificate",
59
- "increment_bundle" => true,
60
- "commit_changes" => true
61
- },
62
- "testflight" => {
63
- "api_token" => "Get it from https://testflightapp.com/account/#api",
64
- "team_token" => "Get it from https://testflightapp.com/dashboard/team/edit/",
65
- "distribution_lists" => [""]
66
- }
67
- }
68
- end
69
-
70
- def valid_config?
71
- return false unless config
72
- return false if config.empty?
73
- return false if config["build"].nil?
74
- return false if config["build"]["developer_name"].nil?
75
- return false if config["testflight"].nil?
76
- return false if config["testflight"]["api_token"].nil?
77
- return false if config["testflight"]["team_token"].nil?
78
- return false if config["testflight"]["distribution_lists"].nil?
79
- true
80
- end
81
-
82
- def config_file
83
- '.testflight'
84
- end
85
-
86
- def config
87
- @config ||= begin
88
- if File.exist?(config_file)
89
- YAML::load(File.open(config_file))
90
- else
91
- File.open(config_file, "w") do |f|
92
- f.write(default_config.to_yaml)
93
- end
94
- YAML::load(File.open(config_file))
95
- end
96
- end
97
- end
98
-
99
- def build_dir
100
- "#{project_dir}/build"
101
- end
102
-
103
- def provisioning_dir
104
- "#{project_dir}/Provisioning"
105
- end
106
-
107
- def distributions_dir
108
- "#{project_dir}/Distributions"
109
- end
20
+ ####################################################################################
21
+ ## Building Project
22
+ ####################################################################################
110
23
 
111
24
  def build_workspace
112
- cmd = "#{XCODE_BUILDER} -workspace '#{workspace_name}' "
113
- cmd << "-scheme '#{application_name}' "
25
+ cmd = "#{XCODE_BUILDER} -workspace '#{Testflight::Config.workspace_name}' "
26
+ cmd << "-scheme '#{Testflight::Config.application_name}' "
114
27
  cmd << "-sdk 'iphoneos6.0' "
115
28
  cmd << "-configuration 'AdHoc' "
116
29
  cmd << "-arch 'armv6 armv7' "
117
- cmd << "CONFIGURATION_BUILD_DIR='#{build_dir}' "
30
+ cmd << "CONFIGURATION_BUILD_DIR='#{Testflight::Config.build_dir}' "
118
31
  pp "Building Workspace..."
119
32
  pp cmd
120
33
  unless system(cmd)
121
- pp "Failed to package the workspace"
34
+ pp "Failed to build workspace"
122
35
  exit 1
123
36
  end
124
37
  end
125
38
 
126
39
  def build_project
127
- cmd = "#{XCODE_BUILDER} -target '#{application_name}' "
40
+ cmd = "#{XCODE_BUILDER} -target '#{Testflight::Config.application_name}' "
128
41
  cmd << "-sdk 'iphoneos6.0' "
129
42
  cmd << "-configuration 'AdHoc' "
43
+
130
44
  pp "Building Project..."
131
45
  pp cmd
132
46
  unless system(cmd)
133
- pp "Failed to build the project"
47
+ pp "Failed to build project"
134
48
  exit 1
135
49
  end
136
50
  end
137
51
 
138
- def ad_hoc_provisioning_name
139
- @ad_hoc_provisioning_name ||= begin
140
- files = Dir.entries(provisioning_dir)
141
- files.select{|file| file.match(/mobileprovision$/)}.first
142
- end
143
- end
144
-
145
- def distribution_file
146
- "#{distributions_dir}/#{application_name}.ipa"
147
- end
52
+ ####################################################################################
53
+ ## Packaging Project
54
+ ####################################################################################
148
55
 
149
56
  def package_workspace
150
57
  cmd = "#{XCODE_PACKAGER} -sdk iphoneos PackageApplication "
151
- cmd << "-v '#{build_dir}/#{application_name}.app' "
152
- cmd << "-o '#{distribution_file}' "
153
- cmd << "--sign '#{config["build"]["developer_name"]}' "
154
- cmd << "--embed '#{provisioning_dir}/#{ad_hoc_provisioning_name}'"
58
+ cmd << "-v '#{Testflight::Config.build_dir}/#{Testflight::Config.application_name}.app' "
59
+ cmd << "-o '#{Testflight::Config.distribution_file}' "
60
+ cmd << "--sign '#{Testflight::Config.developer_name}' "
61
+ cmd << "--embed '#{Testflight::Config.provisioning_dir}/#{Testflight::Config.ad_hoc_provisioning_name}'"
155
62
 
156
63
  pp "Packaging Workspace..."
157
64
  pp cmd
158
65
  unless system(cmd)
159
- pp "Failed to package the workspace"
66
+ pp "Failed to package workspace"
160
67
  exit 1
161
68
  end
162
69
  end
163
70
 
164
71
  def package_project
165
72
  cmd = "#{XCODE_PACKAGER} -sdk iphoneos PackageApplication "
166
- cmd << "-v '#{build_dir}/AdHoc-iphoneos/#{application_name}.app' "
167
- cmd << "-o '#{distribution_file}' "
168
- cmd << "--sign '#{config["build"]["developer_name"]}' "
169
- cmd << "--embed '#{provisioning_dir}/#{ad_hoc_provisioning_name}'"
73
+ cmd << "-v '#{Testflight::Config.build_dir}/AdHoc-iphoneos/#{Testflight::Config.application_name}.app' "
74
+ cmd << "-o '#{Testflight::Config.distribution_file}' "
75
+ cmd << "--sign '#{Testflight::Config.developer_name}' "
76
+ cmd << "--embed '#{Testflight::Config.provisioning_dir}/#{Testflight::Config.ad_hoc_provisioning_name}'"
170
77
 
171
78
  pp "Packaging Project..."
172
79
  pp cmd
173
80
  unless system(cmd)
174
- pp "Failed to package the project"
81
+ pp "Failed to package project"
175
82
  exit 1
176
83
  end
177
84
  end
178
85
 
179
- def setup
180
- unless application_name
181
- pp "This folder does not contain an xCode project or a workspace."
182
- exit 1
183
- end
184
-
185
- unless valid_config?
186
- pp "Ensure that you have provided all of the information in the #{config_file} config file"
187
- exit 1
188
- end
189
-
190
- unless project_files.include?("Distributions")
191
- FileUtils.mkdir("Distributions")
192
- @project_files = nil
193
- end
194
-
195
- unless project_files.include?("Provisioning")
196
- FileUtils.mkdir("Provisioning")
197
- @project_files = nil
198
- end
199
-
200
- unless ad_hoc_provisioning_name
201
- pp "Please copy your Ad Hoc Provisioning Profile into the provisioning folder."
202
- exit 1
203
- end
204
- end
205
-
206
- def read_options(question, opts=["Yes", "No"], vals=["y", "yes", "n", "no"], joiner = "/")
207
- prompt = "(#{opts.join(joiner)})? "
208
- pp question
209
-
210
- $stdout.print(prompt)
211
-
212
- $stdin.each_line do |line|
213
- value = line.strip.downcase
214
- return value if vals.include?(value)
215
- $stdout.print(prompt)
216
- end
217
- end
218
-
219
- def read_info(question, prompt="> ", allow_blank=false)
220
- pp(question) if question
221
-
222
- $stdout.print(prompt)
223
-
224
- $stdin.each_line do |line|
225
- value = line.strip.downcase
226
- return value if allow_blank
227
- return value unless value.empty?
228
- $stdout.print(prompt)
229
- end
230
- end
231
-
232
- def read_distribution_lists
233
- lists = []
234
- vals = read_info(nil, prompt="? ")
235
- vals.split(",").each do |index|
236
- index = index.to_i - 1
237
- return nil if index<0 or index>=config["testflight"]["distribution_lists"].size
238
- lists << config["testflight"]["distribution_lists"][index]
239
- end
240
- lists
241
- end
242
-
243
- def select_distribution_lists
244
- return [] unless config["testflight"]["distribution_lists"]
245
-
246
- pp "Which distribution lists would you like to send this build to? (Select one or more lists, separated with comma.)"
247
- config["testflight"]["distribution_lists"].each_with_index do |list, index|
248
- pp " #{index+1}) #{list}"
249
- end
250
-
251
- lists = read_distribution_lists
252
- while lists.nil?
253
- pp "Invalid selection, please try again."
254
- lists = read_distribution_lists
255
- end
256
-
257
- lists
258
- end
86
+ ####################################################################################
87
+ ## Uploading Project
88
+ ####################################################################################
259
89
 
260
90
  def upload_to_testflightapp
261
91
  pp "Uploading to TestFlightApp..."
262
92
 
263
- notes = read_info("What has changed in this build?")
93
+ notes = Testflight::Prompter.read_info("What has changed in this build?")
264
94
 
265
- lists = select_distribution_lists
95
+ lists = Testflight::Prompter.select_set("Which distribution lists would you like to send this build to? Select one or more lists, separated with comma.", Testflight::Config.distribution_lists)
266
96
 
267
- notify = read_options("Would you like to notify your team members by email about this build?")
97
+ notify = Testflight::Prompter.read_options("Would you like to notify your team members by email about this build?")
268
98
  ["y", "yes"].include?(notify) ? notify = "true" : notify = "false"
269
99
 
270
100
  cmd = "curl #{TESTFLIGHT_ENDPOINT} "
271
- cmd << "-F file=@#{distribution_file} "
272
- cmd << "-F api_token=#{config["testflight"]["api_token"]} "
273
- cmd << "-F team_token=#{config["testflight"]["team_token"]} "
101
+ cmd << "-F file=@#{Testflight::Config.distribution_file} "
102
+ cmd << "-F api_token=#{Testflight::Config.api_token} "
103
+ cmd << "-F team_token=#{Testflight::Config.team_token} "
274
104
  cmd << "-F notify=#{notify} "
275
105
  cmd << "-F distribution_lists=#{lists.join(",")} "
276
106
  cmd << "-F notes='#{notes}'"
@@ -286,35 +116,13 @@ end
286
116
  ## Project Versioning
287
117
  ####################################################################################
288
118
 
289
- def project_info_path
290
- files = Dir["**/#{application_name}-Info.plist"]
291
- if files.empty?
292
- pp "Cannot locate #{application_name}-Info.plist file. Please make sure such file exists in your project."
293
- exit 1
294
- end
295
- files.first
296
- end
297
-
298
- def project_info
299
- @project_info ||= Plist::parse_xml(project_info_path)
300
- end
301
-
302
- def project_version
303
- "#{project_info["CFBundleShortVersionString"]} (#{project_info["CFBundleVersion"]})"
304
- end
305
-
306
- def project_version_short
307
- "#{project_info["CFBundleShortVersionString"]}.#{project_info["CFBundleVersion"]}"
308
- end
309
-
310
119
  def increment_bundle_version
311
- project_info["CFBundleVersion"] = (project_info["CFBundleVersion"].to_i + 1).to_s
312
- pp "Incrementing bundle version to #{project_info["CFBundleVersion"]}..."
120
+ Testflight::Config.project_info["CFBundleVersion"] = (Testflight::Config.build_number.to_i + 1).to_s
121
+ pp "Incrementing bundle version to #{Testflight::Config.build_number}..."
313
122
 
314
- File.open(project_info_path, "w") do |f|
315
- f.write(project_info.to_plist)
123
+ File.open(Testflight::Config.project_info_path, "w") do |f|
124
+ f.write(Testflight::Config.project_info.to_plist)
316
125
  end
317
- @project_info = nil
318
126
  end
319
127
 
320
128
  ####################################################################################
@@ -333,7 +141,7 @@ def commit_changes(msg)
333
141
  system("git commit -m '#{msg}'")
334
142
  system("git push")
335
143
 
336
- system("git tag -a #{project_version_short} -m 'Release #{project_version}'")
144
+ system("git tag -a #{Testflight::Config.project_version_short} -m 'Release #{Testflight::Config.project_version}'")
337
145
  end
338
146
 
339
147
  ####################################################################################
@@ -341,13 +149,13 @@ end
341
149
  ####################################################################################
342
150
 
343
151
  def deploy
344
- setup
152
+ Testflight::Config.setup
345
153
 
346
- if config["build"]["commit_changes"]
347
- commit_changes("Preparing build #{project_version}")
154
+ if Testflight::Config.commit_changes?
155
+ commit_changes("Preparing build #{Testflight::Config.project_version}")
348
156
  end
349
-
350
- if workspace_name
157
+
158
+ if Testflight::Config.workspace?
351
159
  build_workspace
352
160
  package_workspace
353
161
  else
@@ -359,10 +167,10 @@ def deploy
359
167
 
360
168
  pp ""
361
169
 
362
- if config["build"]["increment_bundle"]
170
+ if Testflight::Config.increment_bundle?
363
171
  increment_bundle_version
364
- if config["build"]["commit_changes"]
365
- commit_changes("Incrementing build number to #{project_version}")
172
+ if Testflight::Config.commit_changes?
173
+ commit_changes("Incrementing build number to #{Testflight::Config.project_version}")
366
174
  end
367
175
  end
368
176
 
@@ -0,0 +1,221 @@
1
+ #--
2
+ # Copyright (c) 2012 Michael Berkovich
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'fileutils'
25
+ require 'yaml'
26
+ require 'pp'
27
+ require 'plist'
28
+
29
+ module Testflight
30
+ class Config
31
+
32
+ def self.path
33
+ '.testflight'
34
+ end
35
+
36
+ def self.defaults
37
+ {
38
+ "build" => {
39
+ "developer_name" => "As it appears in your Apple certificate",
40
+ "increment_bundle" => true,
41
+ "commit_changes" => true
42
+ },
43
+ "testflight" => {
44
+ "api_token" => "Get it from https://testflightapp.com/account/#api",
45
+ "team_token" => "Get it from https://testflightapp.com/dashboard/team/edit/",
46
+ "distribution_lists" => [""]
47
+ }
48
+ }
49
+ end
50
+
51
+ def self.config
52
+ @config ||= begin
53
+ if File.exist?(path)
54
+ YAML::load(File.open(path))
55
+ else
56
+ File.open(path, "w") do |f|
57
+ f.write(defaults.to_yaml)
58
+ end
59
+ raise "Please update #{path} with all necessary properties."
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.commit_changes?
65
+ config["build"]["commit_changes"]
66
+ end
67
+
68
+ def self.increment_bundle?
69
+ config["build"]["increment_bundle"]
70
+ end
71
+
72
+ def self.developer_name
73
+ config["build"]["developer_name"]
74
+ end
75
+
76
+ def self.distribution_lists
77
+ config["testflight"]["distribution_lists"]
78
+ end
79
+
80
+ def self.api_token
81
+ config["testflight"]["api_token"]
82
+ end
83
+
84
+ def self.team_token
85
+ config["testflight"]["team_token"]
86
+ end
87
+
88
+ def self.valid?
89
+ return false unless config
90
+ return false if config.empty?
91
+ return false if config["build"].nil?
92
+ return false if config["build"]["developer_name"].nil?
93
+ return false if config["testflight"].nil?
94
+ return false if config["testflight"]["api_token"].nil?
95
+ return false if config["testflight"]["team_token"].nil?
96
+ return false if config["testflight"]["distribution_lists"].nil?
97
+ true
98
+ end
99
+
100
+ def self.project_dir
101
+ Dir.pwd
102
+ end
103
+
104
+ def self.project_files
105
+ @project_files ||= Dir.entries(project_dir)
106
+ end
107
+
108
+ def self.file_name_by_ext(ext)
109
+ project_files.select{|file| file.match(/#{ext}$/)}.first
110
+ end
111
+
112
+ def self.workspace_name
113
+ @workspace_name ||= file_name_by_ext('xcworkspace')
114
+ end
115
+
116
+ def self.project_name
117
+ @project_name ||= file_name_by_ext('xcodeproj')
118
+ end
119
+
120
+ def self.type
121
+ return "unknown" if workspace_name.nil? and project_name.nil?
122
+ @type ||= workspace_name ? 'workspace' : 'project'
123
+ end
124
+
125
+ def self.workspace?
126
+ type == "workspace"
127
+ end
128
+
129
+ def self.project?
130
+ type == "project"
131
+ end
132
+
133
+ def self.application_name
134
+ @application_name ||= begin
135
+ name = workspace_name || project_name
136
+ name = name.split(".")[0..-2].join(".") if name
137
+ name
138
+ end
139
+ end
140
+
141
+ def self.build_dir
142
+ "#{project_dir}/build"
143
+ end
144
+
145
+ def self.provisioning_dir
146
+ "#{project_dir}/Provisioning"
147
+ end
148
+
149
+ def self.distributions_dir
150
+ "#{project_dir}/Distributions"
151
+ end
152
+
153
+ def self.ad_hoc_provisioning_name
154
+ @ad_hoc_provisioning_name ||= begin
155
+ files = Dir.entries(provisioning_dir)
156
+ files.select{|file| file.match(/mobileprovision$/)}.first
157
+ end
158
+ end
159
+
160
+ def self.distribution_file
161
+ "#{distributions_dir}/#{application_name}.ipa"
162
+ end
163
+
164
+ def self.setup
165
+ unless application_name
166
+ pp "This folder does not contain an xCode project or a workspace."
167
+ exit 1
168
+ end
169
+
170
+ unless valid?
171
+ pp "Ensure that you have provided all of the information in the #{config_file} config file"
172
+ exit 1
173
+ end
174
+
175
+ unless project_files.include?("Distributions")
176
+ FileUtils.mkdir("Distributions")
177
+ @project_files = nil
178
+ end
179
+
180
+ unless project_files.include?("Provisioning")
181
+ FileUtils.mkdir("Provisioning")
182
+ @project_files = nil
183
+ end
184
+
185
+ unless ad_hoc_provisioning_name
186
+ pp "Please copy your Ad Hoc Provisioning Profile into the provisioning folder."
187
+ exit 1
188
+ end
189
+ end
190
+
191
+ def self.project_info_path
192
+ files = Dir["**/#{application_name}-Info.plist"]
193
+ if files.empty?
194
+ pp "Cannot locate #{application_name}-Info.plist file. Please make sure such file exists in your project."
195
+ exit 1
196
+ end
197
+ files.first
198
+ end
199
+
200
+ def self.project_info
201
+ @project_info ||= Plist::parse_xml(project_info_path)
202
+ end
203
+
204
+ def self.version
205
+ project_info["CFBundleShortVersionString"]
206
+ end
207
+
208
+ def self.build_number
209
+ project_info["CFBundleVersion"]
210
+ end
211
+
212
+ def self.project_version
213
+ "#{version} (#{build_number})"
214
+ end
215
+
216
+ def self.project_version_short
217
+ "#{version}.#{build_number}"
218
+ end
219
+
220
+ end
221
+ end
@@ -0,0 +1,81 @@
1
+ #--
2
+ # Copyright (c) 2012 Michael Berkovich
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module Testflight
25
+ class Prompter
26
+
27
+ def self.read_options(question=nil, opts=["Yes", "No"], vals=["y", "yes", "n", "no"], joiner = "/")
28
+ prompt = "(#{opts.join(joiner)})? "
29
+ pp question if question
30
+
31
+ $stdout.print(prompt)
32
+
33
+ $stdin.each_line do |line|
34
+ value = line.strip.downcase
35
+ return value if vals.include?(value)
36
+ $stdout.print(prompt)
37
+ end
38
+ end
39
+
40
+ def self.read_info(question=nil, prompt="> ", allow_blank=false)
41
+ pp question if question
42
+
43
+ $stdout.print(prompt)
44
+
45
+ $stdin.each_line do |line|
46
+ value = line.strip.downcase
47
+ return value if allow_blank
48
+ return value unless value.empty?
49
+ $stdout.print(prompt)
50
+ end
51
+ end
52
+
53
+ def self.read_set_values(opts)
54
+ lists = []
55
+ vals = read_info(nil, prompt="? ")
56
+ vals.split(",").each do |index|
57
+ index = index.to_i - 1
58
+ return nil if index<0 or index>=opts.size
59
+ lists << opts[index]
60
+ end
61
+ lists
62
+ end
63
+
64
+ def self.select_set(question, opts = [])
65
+ pp question
66
+
67
+ opts.each_with_index do |opt, index|
68
+ pp " #{index+1}) #{opt}"
69
+ end
70
+
71
+ vals = read_set_values(opts)
72
+ while vals.nil?
73
+ pp "Invalid selection, please try again."
74
+ vals = read_set_values(opts)
75
+ end
76
+
77
+ vals
78
+ end
79
+
80
+ end
81
+ end
@@ -22,7 +22,7 @@
22
22
  #++
23
23
 
24
24
  module Testflight
25
- VERSION = "0.1.1"
25
+ VERSION = "0.1.2"
26
26
  end
27
27
 
28
28
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testflight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -37,15 +37,18 @@ extensions: []
37
37
  extra_rdoc_files: []
38
38
  files:
39
39
  - bin/takeoff
40
+ - lib/testflight/config.rb
41
+ - lib/testflight/prompter.rb
40
42
  - lib/testflight/version.rb
41
43
  - LICENSE
42
- - README.md
44
+ - README.rdoc
43
45
  homepage: https://github.com/berk/testflight
44
46
  licenses: []
45
47
  post_install_message:
46
48
  rdoc_options: []
47
49
  require_paths:
48
- - lib
50
+ - - lib
51
+ - lib/testflight
49
52
  required_ruby_version: !ruby/object:Gem::Requirement
50
53
  none: false
51
54
  requirements:
data/README.md DELETED
File without changes