appsendr 0.0.6 → 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.
- data/.gitignore +17 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -16
- data/appsendr.gemspec +18 -43
- data/bin/appsendr +103 -8
- data/lib/appsendr.rb +6 -26
- data/lib/appsendr/app.rb +60 -0
- data/lib/appsendr/version.rb +3 -0
- data/test/units/app_test.rb +9 -0
- metadata +51 -168
- data/Manifest +0 -25
- data/README.rdoc +0 -49
- data/lib/appsendr/binary_plist.rb +0 -494
- data/lib/appsendr/client.rb +0 -258
- data/lib/appsendr/command.rb +0 -81
- data/lib/appsendr/commands/app.rb +0 -241
- data/lib/appsendr/commands/auth.rb +0 -139
- data/lib/appsendr/commands/base.rb +0 -78
- data/lib/appsendr/commands/build.rb +0 -320
- data/lib/appsendr/commands/collaborators.rb +0 -52
- data/lib/appsendr/commands/common.rb +0 -36
- data/lib/appsendr/commands/deploy.rb +0 -56
- data/lib/appsendr/commands/groups.rb +0 -63
- data/lib/appsendr/commands/help.rb +0 -103
- data/lib/appsendr/commands/portal.rb +0 -375
- data/lib/appsendr/commands/testers.rb +0 -118
- data/lib/appsendr/commands/version.rb +0 -7
- data/lib/appsendr/constants.rb +0 -5
- data/lib/appsendr/helpers.rb +0 -144
- data/lib/appsendr/progressbar.rb +0 -236
- data/portal.rb +0 -197
- data/terms +0 -0
@@ -1,139 +0,0 @@
|
|
1
|
-
module AppSendr::Command
|
2
|
-
class Auth < Base
|
3
|
-
attr_accessor :credentials
|
4
|
-
|
5
|
-
def client
|
6
|
-
@client ||= init_for_credentials
|
7
|
-
end
|
8
|
-
|
9
|
-
def init_for_credentials
|
10
|
-
client = AppSendr::Client.new(api_key, host)
|
11
|
-
#client.on_warning { |msg| self.display("\n#{msg}\n\n") }
|
12
|
-
client
|
13
|
-
end
|
14
|
-
|
15
|
-
def auth_credentials(user,pass)
|
16
|
-
AppSendr::Client.auth(user,pass,host)
|
17
|
-
end
|
18
|
-
|
19
|
-
# just a stub; will raise if not authenticated
|
20
|
-
def check
|
21
|
-
client.list
|
22
|
-
end
|
23
|
-
|
24
|
-
def host
|
25
|
-
ENV['APPDROPPR_HOST'] || 'appsendr.com'
|
26
|
-
#ENV['APPDROPPR_HOST'] || '0.0.0.0:3000'
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def login
|
31
|
-
user_pass = ask_for_credentials
|
32
|
-
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
33
|
-
write_credentials
|
34
|
-
end
|
35
|
-
|
36
|
-
def api_key
|
37
|
-
get_credentials
|
38
|
-
@credentials[0]
|
39
|
-
end
|
40
|
-
|
41
|
-
# def user # :nodoc:
|
42
|
-
# get_credentials
|
43
|
-
# @credentials[0]
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# def password # :nodoc:
|
47
|
-
# get_credentials
|
48
|
-
# @credentials[1]
|
49
|
-
# end
|
50
|
-
|
51
|
-
def get_credentials # :nodoc:
|
52
|
-
return if @credentials
|
53
|
-
unless @credentials = read_credentials
|
54
|
-
user_pass = ask_for_credentials
|
55
|
-
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
56
|
-
|
57
|
-
#@credentials = ask_for_credentials
|
58
|
-
save_credentials
|
59
|
-
end
|
60
|
-
@credentials
|
61
|
-
end
|
62
|
-
|
63
|
-
def read_credentials
|
64
|
-
File.exists?(credentials_file) and File.read(credentials_file).split("\n")
|
65
|
-
end
|
66
|
-
|
67
|
-
def echo_off
|
68
|
-
system "stty -echo"
|
69
|
-
end
|
70
|
-
|
71
|
-
def echo_on
|
72
|
-
system "stty echo"
|
73
|
-
end
|
74
|
-
|
75
|
-
def ask_for_credentials
|
76
|
-
puts "Enter your AppSendr credentials."
|
77
|
-
|
78
|
-
print "Email: "
|
79
|
-
user = ask
|
80
|
-
|
81
|
-
print "Password: "
|
82
|
-
password = ask_for_password
|
83
|
-
|
84
|
-
[ user, password ]
|
85
|
-
end
|
86
|
-
|
87
|
-
def ask_for_password
|
88
|
-
echo_off
|
89
|
-
password = ask
|
90
|
-
puts
|
91
|
-
echo_on
|
92
|
-
return password
|
93
|
-
end
|
94
|
-
|
95
|
-
def save_credentials
|
96
|
-
begin
|
97
|
-
write_credentials
|
98
|
-
#command = args.any? { |a| a == '--ignore-keys' } ? 'auth:check' : 'keys:add'
|
99
|
-
command = 'auth:check'
|
100
|
-
AppSendr::Command.run_internal(command, args)
|
101
|
-
rescue RestClient::Unauthorized => e
|
102
|
-
delete_credentials
|
103
|
-
raise e unless retry_login?
|
104
|
-
|
105
|
-
display "\nAuthentication failed"
|
106
|
-
user_pass = ask_for_credentials
|
107
|
-
@credentials = auth_credentials(user_pass[0],user_pass[1])
|
108
|
-
@client = init_for_credentials
|
109
|
-
retry
|
110
|
-
rescue Exception => e
|
111
|
-
delete_credentials
|
112
|
-
raise e
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def retry_login?
|
117
|
-
@login_attempts ||= 0
|
118
|
-
@login_attempts += 1
|
119
|
-
@login_attempts < 3
|
120
|
-
end
|
121
|
-
|
122
|
-
def write_credentials
|
123
|
-
FileUtils.mkdir_p(File.dirname(credentials_file))
|
124
|
-
File.open(credentials_file, 'w') do |f|
|
125
|
-
f.puts self.credentials
|
126
|
-
end
|
127
|
-
set_credentials_permissions
|
128
|
-
end
|
129
|
-
|
130
|
-
def set_credentials_permissions
|
131
|
-
FileUtils.chmod 0700, File.dirname(credentials_file)
|
132
|
-
FileUtils.chmod 0600, credentials_file
|
133
|
-
end
|
134
|
-
|
135
|
-
def delete_credentials
|
136
|
-
FileUtils.rm_f(credentials_file)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
|
3
|
-
class String
|
4
|
-
def cl_escape
|
5
|
-
self.gsub('&','\\\\&').gsub(" ","\\ ")
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
|
10
|
-
module AppSendr::Command
|
11
|
-
class Base
|
12
|
-
include AppSendr::Helpers
|
13
|
-
|
14
|
-
attr_accessor :args
|
15
|
-
attr_reader :autodetected_app
|
16
|
-
def initialize(args, appsendr=nil)
|
17
|
-
@args = args
|
18
|
-
@appsendr = appsendr
|
19
|
-
@autodetected_app = false
|
20
|
-
end
|
21
|
-
|
22
|
-
def confirm(message="Are you sure you wish to continue? (y/n)?")
|
23
|
-
display("#{message} ", false)
|
24
|
-
ask.downcase == 'y'
|
25
|
-
end
|
26
|
-
|
27
|
-
def format_date(date)
|
28
|
-
date = Time.parse(date) if date.is_a?(String)
|
29
|
-
date.strftime("%Y-%m-%d %H:%M %Z")
|
30
|
-
end
|
31
|
-
|
32
|
-
def ask
|
33
|
-
gets.strip
|
34
|
-
end
|
35
|
-
|
36
|
-
def appsendr
|
37
|
-
@appsendr ||= AppSendr::Command.run_internal('auth:client', args)
|
38
|
-
end
|
39
|
-
|
40
|
-
def extract_app(force=true)
|
41
|
-
app = extract_option('--app', false)
|
42
|
-
raise(CommandFailed, "You must specify an app name after --app") if app == false
|
43
|
-
unless app
|
44
|
-
app = extract_app_in_dir(Dir.pwd) ||
|
45
|
-
raise(CommandFailed, "No app specified.\nRun this command from app folder or set it adding --app <app name>") if force
|
46
|
-
@autodetected_app = true
|
47
|
-
end
|
48
|
-
app
|
49
|
-
end
|
50
|
-
|
51
|
-
def option_exists?(options, default=true)
|
52
|
-
values = options.is_a?(Array) ? options : [options]
|
53
|
-
return false unless opt_index = args.select { |a| values.include? a }.first
|
54
|
-
args.delete(opt_index)
|
55
|
-
return true
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
def extract_option(options, default=true)
|
60
|
-
values = options.is_a?(Array) ? options : [options]
|
61
|
-
return unless opt_index = args.select { |a| values.include? a }.first
|
62
|
-
opt_position = args.index(opt_index) + 1
|
63
|
-
if args.size > opt_position && opt_value = args[opt_position]
|
64
|
-
if opt_value.include?('--')
|
65
|
-
opt_value = nil
|
66
|
-
else
|
67
|
-
args.delete_at(opt_position)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
opt_value ||= default
|
71
|
-
args.delete(opt_index)
|
72
|
-
block_given? ? yield(opt_value) : opt_value
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
end
|
@@ -1,320 +0,0 @@
|
|
1
|
-
require 'zip/zip'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'appsendr/progressbar'
|
4
|
-
|
5
|
-
module AppSendr::Command
|
6
|
-
class Build < Base
|
7
|
-
attr_accessor :app_path, :app_dir, :app_name, :ipa_path, :provisioning_name, :provisioning_path, :configuration, :project_info
|
8
|
-
|
9
|
-
|
10
|
-
def index
|
11
|
-
notify = option_exists?('--notify', false)
|
12
|
-
is_public = option_exists?('--public', false)
|
13
|
-
|
14
|
-
if require_project(1,"build","your active configuration name")
|
15
|
-
@configuration = load_configuration
|
16
|
-
message "Building Xcode project with configuration #{@configuration}"
|
17
|
-
compile(@configuration)
|
18
|
-
|
19
|
-
|
20
|
-
@app_dir = path_to_app_dir
|
21
|
-
@app_path = @app_dir+"/#{app_name}"
|
22
|
-
if @app_name
|
23
|
-
@notes = ask_for_notes
|
24
|
-
message "Building IPA File"
|
25
|
-
|
26
|
-
@ipa_path = build_ipa_with_app_at_path
|
27
|
-
info = app_info
|
28
|
-
bundle_identifier ||= info['CFBundleIdentifier']
|
29
|
-
icon_path = icon_path(info)
|
30
|
-
if is_file_to_large?(@ipa_path)
|
31
|
-
upload_large_ipa(read_app_id,@ipa_path, profile_path,is_public,notify,@notes,bundle_identifier,icon_path)
|
32
|
-
else
|
33
|
-
message "Uploading IPA"
|
34
|
-
appsendr.upload(read_app_id,@ipa_path, profile_path,is_public,notify,@notes,bundle_identifier,icon_path)
|
35
|
-
message "App deployed"
|
36
|
-
end
|
37
|
-
FileUtils.rm_rf @ipa_path
|
38
|
-
else
|
39
|
-
error "No .app file was built for the configuration \"#{@configuration}\"."
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
def clean
|
46
|
-
require_project_dir("clean")
|
47
|
-
require_project_droppr
|
48
|
-
config = args.join(" ").strip
|
49
|
-
if !config.empty?
|
50
|
-
config = "-configuration #{config} "
|
51
|
-
end
|
52
|
-
%x[xcodebuild #{config} clean]
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def compile(build_style, ossdk=nil, target=nil)
|
58
|
-
iossdk = "iphoneos4.1"
|
59
|
-
iossdk ||= ossdk
|
60
|
-
sdk_str = "-sdk "+iossdk
|
61
|
-
|
62
|
-
if target
|
63
|
-
target_str = "-target "+target
|
64
|
-
end
|
65
|
-
xcode = "xcodebuild #{sdk_str} #{target_str}-configuration #{build_style} > /dev/null 2>&1"
|
66
|
-
system(xcode)
|
67
|
-
end
|
68
|
-
|
69
|
-
def build_ipa_with_app_at_path
|
70
|
-
payload_path = @app_dir+"/Payload"
|
71
|
-
ipa_path = @app_dir+"/#{ipa_app_name(app_name).delete('^A-Za-z0-9\.\_\-')}"
|
72
|
-
|
73
|
-
|
74
|
-
FileUtils.rm_rf payload_path
|
75
|
-
Dir.mkdir(payload_path)
|
76
|
-
`cp -r #{@app_path.cl_escape} #{payload_path.cl_escape+"/"+app_name.cl_escape}`
|
77
|
-
build_ipa(payload_path.cl_escape,ipa_path.cl_escape)
|
78
|
-
|
79
|
-
FileUtils.rm_rf payload_path
|
80
|
-
|
81
|
-
return ipa_path
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
def build_ipa(payload_path,ipa_path)
|
86
|
-
ditto_exec = "ditto -c -k --keepParent --sequesterRsrc #{payload_path} #{ipa_path}"
|
87
|
-
system(ditto_exec)
|
88
|
-
end
|
89
|
-
|
90
|
-
def upload_large_ipa(app_id, ipa, provisioning ,is_public, notify, notes, bundle_id, icon)
|
91
|
-
resp = appsendr.upload(app_id,ipa, provisioning,is_public,notify,notes,bundle_id,icon, true)
|
92
|
-
|
93
|
-
@upload_id = nil
|
94
|
-
@app_version_id = resp['message']['version']['id']
|
95
|
-
if @app_version_id
|
96
|
-
begin
|
97
|
-
split_file(ipa)
|
98
|
-
upload_as_multipart(app_id, @app_version_id, ipa, notify)
|
99
|
-
rescue Exception => e
|
100
|
-
clean_split_files(File.dirname(ipa))
|
101
|
-
appsendr.abort_multipart(app_id, @app_version_id, ipa, @upload_id)
|
102
|
-
raise e
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
def upload_as_multipart(app_id, version_id, ipa, notify)
|
109
|
-
|
110
|
-
ipa_name = File.basename(ipa)
|
111
|
-
ipa_dir = File.dirname(ipa)
|
112
|
-
|
113
|
-
@split_files = organize_split_files(ipa_dir)
|
114
|
-
|
115
|
-
resp = appsendr.initialize_multipart(app_id, version_id, ipa)
|
116
|
-
@upload_id = resp['message']['upload_id']
|
117
|
-
unless @upload_id
|
118
|
-
clean_split_files(ipa_dir)
|
119
|
-
raise "A error has occured with the upload"
|
120
|
-
end
|
121
|
-
|
122
|
-
parts = {}
|
123
|
-
pbar = ProgressBar.new("Uploading IPA", @split_files.length * 2)
|
124
|
-
|
125
|
-
@split_files.each_with_index{|file, i|
|
126
|
-
pbar.inc
|
127
|
-
|
128
|
-
resp = appsendr.upload_part(app_id, version_id, ipa, @upload_id, i+1, file)
|
129
|
-
etag = resp['message']['etag']
|
130
|
-
parts["#{i}"]={:part_num=>i+1, :e_tag=>etag}
|
131
|
-
|
132
|
-
pbar.inc
|
133
|
-
|
134
|
-
}
|
135
|
-
pbar.finish
|
136
|
-
|
137
|
-
appsendr.finish_multipart(app_id, version_id, ipa, @upload_id, parts, notify)
|
138
|
-
|
139
|
-
clean_split_files(ipa_dir)
|
140
|
-
|
141
|
-
end
|
142
|
-
|
143
|
-
def clean_split_files(ipa_dir)
|
144
|
-
if File.directory? "#{ipa_dir}/appsendr_splits"
|
145
|
-
FileUtils.rm_r Dir.glob("#{ipa_dir}/appsendr_splits/*")
|
146
|
-
FileUtils.rmdir "#{ipa_dir}/appsendr_splits"
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def organize_split_files(ipa_dir)
|
151
|
-
dir_contents = Dir["#{ipa_dir}/appsendr_splits/AppSendrSplit*"]
|
152
|
-
dir_contents.sort! { |a,b| a <=> b }
|
153
|
-
unless dir_contents.length > 1
|
154
|
-
|
155
|
-
clean_split_files(ipa_dir)
|
156
|
-
raise "A error has occured with the upload"
|
157
|
-
|
158
|
-
end
|
159
|
-
return dir_contents
|
160
|
-
end
|
161
|
-
|
162
|
-
def split_file(file_path)
|
163
|
-
return unless file_path
|
164
|
-
file_dir = File.dirname(file_path)
|
165
|
-
|
166
|
-
file_path = file_path.cl_escape
|
167
|
-
clean_split_files(file_dir)
|
168
|
-
|
169
|
-
file_dir << "/appsendr_splits"
|
170
|
-
|
171
|
-
FileUtils.mkdir file_dir
|
172
|
-
|
173
|
-
name = File.basename(file_path)
|
174
|
-
`split -b 5mb #{file_path} AppSendrSplit`
|
175
|
-
|
176
|
-
FileUtils.mv Dir.glob("AppSendrSplit*"), file_dir
|
177
|
-
end
|
178
|
-
|
179
|
-
|
180
|
-
#povisioning
|
181
|
-
|
182
|
-
def profile_path
|
183
|
-
return @provisioning_path if @provisioning_path
|
184
|
-
@provisioning_path = provisioning_profiles_dir + "/#{profile_name}"
|
185
|
-
@provisioning_path
|
186
|
-
end
|
187
|
-
|
188
|
-
def provisioning_profiles_dir
|
189
|
-
File.expand_path("~/Library/MobileDevice/Provisioning\ Profiles/")
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
def profile_name
|
194
|
-
return @provisioning_name if @provisioning_name
|
195
|
-
error "Build is not valid for AdHoc distribution" unless valid_build?
|
196
|
-
|
197
|
-
mem_buf = File.open(@app_path+"/embedded.mobileprovision","rb") {|io| io.read}
|
198
|
-
name = mem_buf.scan(/UUID<\/key>[\s]+<string>(.*)<\/string>/).flatten.first
|
199
|
-
@provisioning_name = name + ".mobileprovision"
|
200
|
-
@provisioning_name
|
201
|
-
end
|
202
|
-
|
203
|
-
|
204
|
-
## helpers
|
205
|
-
def load_configuration
|
206
|
-
@configuration = args.join(" ").strip
|
207
|
-
error "That configuration does not exist.\nAvailable configurations:\n#{project_info[:configurations].join("\n")}" unless valid_configuration?
|
208
|
-
@configuration
|
209
|
-
end
|
210
|
-
|
211
|
-
def valid_configuration?
|
212
|
-
return false unless @configuration
|
213
|
-
info = project_info
|
214
|
-
info[:configurations].include?(@configuration)
|
215
|
-
end
|
216
|
-
|
217
|
-
def project_info
|
218
|
-
return @project_info if @project_info
|
219
|
-
result = %x[xcodebuild -list]
|
220
|
-
project_info = result.split("\n")
|
221
|
-
name = project_info.shift.scan(/\"(.*)\"/).flatten.first
|
222
|
-
project_info.shift
|
223
|
-
project_info.pop #remove last line
|
224
|
-
project_info.pop #remove
|
225
|
-
on_targets = true
|
226
|
-
targets = []
|
227
|
-
configs = []
|
228
|
-
project_info.each{|ln|
|
229
|
-
if ln.strip == "Build Configurations:"
|
230
|
-
on_targets = false
|
231
|
-
end
|
232
|
-
if ln.strip != "Build Configurations:" and ln.strip != "Targets:" and ln.strip!=""
|
233
|
-
ln = ln.gsub("(Active)","").strip
|
234
|
-
on_targets ? targets.push(ln) : configs.push(ln)
|
235
|
-
end
|
236
|
-
}
|
237
|
-
|
238
|
-
@project_info = {:name => name, :targets=>targets, :configurations=>configs}
|
239
|
-
@project_info
|
240
|
-
end
|
241
|
-
def icon_path(info)
|
242
|
-
icon_files = info['CFBundleIconFiles']
|
243
|
-
icon_path = nil
|
244
|
-
icon_file = nil
|
245
|
-
if icon_files.nil? or icon_files.count == 0
|
246
|
-
icon_file = info['CFBundleIconFile']
|
247
|
-
else
|
248
|
-
icon_file = icon_files.grep(/@2x/i)
|
249
|
-
end
|
250
|
-
|
251
|
-
if icon_file and !icon_file.empty?
|
252
|
-
all_dir_contents = Dir["**/*"]
|
253
|
-
all_dir_contents = all_dir_contents - all_dir_contents.grep(/^build/)
|
254
|
-
icon = all_dir_contents.grep(%r{#{icon_file}}).flatten.first
|
255
|
-
if icon
|
256
|
-
icon_path = Dir.pwd+"/#{icon}"
|
257
|
-
end
|
258
|
-
elsif icon_files
|
259
|
-
|
260
|
-
all_dir_contents = Dir["**/*"]
|
261
|
-
all_dir_contents = all_dir_contents - all_dir_contents.grep(/^build/)
|
262
|
-
icon = all_dir_contents.grep(%r{#{icon_files.first}}).flatten.first
|
263
|
-
icon_dir = icon.sub(icon_files.first,"");
|
264
|
-
|
265
|
-
icon_full_path = Dir.pwd+"/#{icon_dir}"
|
266
|
-
biggest_icon_size = 0
|
267
|
-
biggest_icon = ""
|
268
|
-
for icon_file in icon_files
|
269
|
-
size = File.size(icon_full_path+icon_file)
|
270
|
-
if size > biggest_icon_size
|
271
|
-
biggest_icon = icon_full_path+icon_file
|
272
|
-
end
|
273
|
-
end
|
274
|
-
icon_path = biggest_icon
|
275
|
-
|
276
|
-
end
|
277
|
-
return icon_path
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
def valid_build?
|
282
|
-
return unless @app_path
|
283
|
-
File.exist?(@app_path+"/embedded.mobileprovision")
|
284
|
-
end
|
285
|
-
|
286
|
-
def app_info
|
287
|
-
return unless @app_path
|
288
|
-
mem_buf = File.open(@app_path+"/Info.plist","rb") {|io| io.read}
|
289
|
-
plist = AppSendr::Plist::Binary.decode_binary_plist(mem_buf)
|
290
|
-
plist
|
291
|
-
end
|
292
|
-
|
293
|
-
def path_to_app_dir
|
294
|
-
Dir.pwd+"/build/#{@configuration}-iphoneos"
|
295
|
-
end
|
296
|
-
|
297
|
-
def app_name
|
298
|
-
if @app_dir.nil?
|
299
|
-
return unless @configuration
|
300
|
-
@app_dir = path_to_app_dir
|
301
|
-
end
|
302
|
-
return @app_name if @app_name
|
303
|
-
if File.directory? @app_dir
|
304
|
-
@app_name = Dir.entries(@app_dir).grep(/.+\.app$/).first
|
305
|
-
@app_name
|
306
|
-
else
|
307
|
-
error "#{@app_dir} does not exisit"
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
def ipa_app_name(app_name)
|
312
|
-
app_name.gsub(/\.app$/,".ipa")
|
313
|
-
end
|
314
|
-
|
315
|
-
def project_name
|
316
|
-
return Dir.entries(Dir.pwd).grep(/.+\.xcodeproj/).first.sub("",".xcodeproj")
|
317
|
-
end
|
318
|
-
|
319
|
-
end
|
320
|
-
end
|