appsendr 0.0.3 → 0.0.5
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/Manifest +4 -0
- data/Rakefile +2 -2
- data/appsendr.gemspec +5 -8
- data/lib/appsendr/client.rb +105 -29
- data/lib/appsendr/command.rb +14 -3
- data/lib/appsendr/commands/app.rb +46 -33
- data/lib/appsendr/commands/auth.rb +5 -21
- data/lib/appsendr/commands/base.rb +1 -0
- data/lib/appsendr/commands/build.rb +139 -19
- data/lib/appsendr/commands/collaborators.rb +29 -1
- data/lib/appsendr/commands/deploy.rb +13 -2
- data/lib/appsendr/commands/groups.rb +63 -0
- data/lib/appsendr/commands/help.rb +10 -3
- data/lib/appsendr/commands/portal.rb +375 -0
- data/lib/appsendr/commands/testers.rb +14 -31
- data/lib/appsendr/constants.rb +1 -1
- data/lib/appsendr/helpers.rb +31 -7
- data/lib/appsendr/progressbar.rb +236 -0
- metadata +12 -20
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'zip/zip'
|
2
|
-
require '
|
2
|
+
require 'fileutils'
|
3
|
+
require 'appsendr/progressbar'
|
4
|
+
|
3
5
|
module AppSendr::Command
|
4
6
|
class Build < Base
|
5
7
|
attr_accessor :app_path, :app_dir, :app_name, :ipa_path, :provisioning_name, :provisioning_path, :configuration, :project_info
|
@@ -8,18 +10,30 @@ module AppSendr::Command
|
|
8
10
|
def index
|
9
11
|
if require_project(1,"build","your active configuration name")
|
10
12
|
@configuration = load_configuration
|
11
|
-
|
13
|
+
message "Building Xcode project with configuration #{@configuration}"
|
12
14
|
compile(@configuration)
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
appsendr.upload(read_app_id,@ipa_path, profile_path,nil,bundle_identifier,icon_path)
|
21
|
-
FileUtils.rm_rf @ipa_path
|
16
|
+
|
17
|
+
@app_dir = path_to_app_dir
|
18
|
+
@app_path = @app_dir+"/#{app_name}"
|
19
|
+
if @app_name
|
20
|
+
@notes = ask_for_notes
|
21
|
+
message "Building IPA File"
|
22
22
|
|
23
|
+
@ipa_path = build_ipa_with_app_at_path
|
24
|
+
info = app_info
|
25
|
+
bundle_identifier ||= info['CFBundleIdentifier']
|
26
|
+
icon_path = icon_path(info)
|
27
|
+
if is_file_to_large?(@ipa_path)
|
28
|
+
upload_large_ipa(read_app_id,@ipa_path, profile_path,notify,@notes,bundle_identifier,icon_path)
|
29
|
+
else
|
30
|
+
appsendr.upload(read_app_id,@ipa_path, profile_path,notify,@notes,bundle_identifier,icon_path)
|
31
|
+
message "App deployed"
|
32
|
+
end
|
33
|
+
FileUtils.rm_rf @ipa_path
|
34
|
+
else
|
35
|
+
error "No .app file was built for the configuration \"#{@configuration}\"."
|
36
|
+
end
|
23
37
|
end
|
24
38
|
end
|
25
39
|
|
@@ -50,7 +64,7 @@ module AppSendr::Command
|
|
50
64
|
|
51
65
|
def build_ipa_with_app_at_path
|
52
66
|
payload_path = @app_dir+"/Payload"
|
53
|
-
ipa_path = @app_dir+"/#{ipa_app_name(app_name)}"
|
67
|
+
ipa_path = @app_dir+"/#{ipa_app_name(app_name).delete('^A-Za-z0-9\.\_\-')}"
|
54
68
|
|
55
69
|
|
56
70
|
FileUtils.rm_rf payload_path
|
@@ -72,6 +86,94 @@ module AppSendr::Command
|
|
72
86
|
system(ditto_exec)
|
73
87
|
end
|
74
88
|
|
89
|
+
def upload_large_ipa(app_id, ipa, provisioning ,notify, notes, bundle_id, icon)
|
90
|
+
resp = appsendr.upload(app_id,ipa, provisioning,notify,notes,bundle_id,icon, true)
|
91
|
+
|
92
|
+
@upload_id = nil
|
93
|
+
@app_version_id = resp['message']['version']['id']
|
94
|
+
if @app_version_id
|
95
|
+
begin
|
96
|
+
split_file(ipa)
|
97
|
+
upload_as_multipart(app_id, @app_version_id, ipa)
|
98
|
+
rescue Exception => e
|
99
|
+
clean_split_files(File.dirname(ipa))
|
100
|
+
appsendr.abort_multipart(app_id, @app_version_id, ipa, @upload_id)
|
101
|
+
raise e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def upload_as_multipart(app_id, version_id, ipa)
|
108
|
+
|
109
|
+
ipa_name = File.basename(ipa)
|
110
|
+
ipa_dir = File.dirname(ipa)
|
111
|
+
|
112
|
+
@split_files = organize_split_files(ipa_dir)
|
113
|
+
|
114
|
+
resp = appsendr.initialize_multipart(app_id, version_id, ipa)
|
115
|
+
@upload_id = resp['message']['upload_id']
|
116
|
+
unless @upload_id
|
117
|
+
clean_split_files(ipa_dir)
|
118
|
+
raise "A error has occured with the upload"
|
119
|
+
end
|
120
|
+
|
121
|
+
parts = {}
|
122
|
+
pbar = ProgressBar.new("Uploading IPA", @split_files.length * 2)
|
123
|
+
|
124
|
+
@split_files.each_with_index{|file, i|
|
125
|
+
pbar.inc
|
126
|
+
|
127
|
+
resp = appsendr.upload_part(app_id, version_id, ipa, @upload_id, i+1, file)
|
128
|
+
etag = resp['message']['etag']
|
129
|
+
parts["#{i}"]={:part_num=>i+1, :e_tag=>etag}
|
130
|
+
|
131
|
+
pbar.inc
|
132
|
+
|
133
|
+
}
|
134
|
+
pbar.finish
|
135
|
+
|
136
|
+
appsendr.finish_multipart(app_id, version_id, ipa, @upload_id, parts)
|
137
|
+
|
138
|
+
clean_split_files(ipa_dir)
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
def clean_split_files(ipa_dir)
|
143
|
+
if File.directory? "#{ipa_dir}/appsendr_splits"
|
144
|
+
FileUtils.rm_r Dir.glob("#{ipa_dir}/appsendr_splits/*")
|
145
|
+
FileUtils.rmdir "#{ipa_dir}/appsendr_splits"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def organize_split_files(ipa_dir)
|
150
|
+
dir_contents = Dir["#{ipa_dir}/appsendr_splits/AppSendrSplit*"]
|
151
|
+
dir_contents.sort! { |a,b| a <=> b }
|
152
|
+
unless dir_contents.length > 1
|
153
|
+
|
154
|
+
clean_split_files(ipa_dir)
|
155
|
+
raise "A error has occured with the upload"
|
156
|
+
|
157
|
+
end
|
158
|
+
return dir_contents
|
159
|
+
end
|
160
|
+
|
161
|
+
def split_file(file_path)
|
162
|
+
return unless file_path
|
163
|
+
file_dir = File.dirname(file_path)
|
164
|
+
|
165
|
+
file_path = file_path.cl_escape
|
166
|
+
clean_split_files(file_dir)
|
167
|
+
|
168
|
+
file_dir << "/appsendr_splits"
|
169
|
+
|
170
|
+
FileUtils.mkdir file_dir
|
171
|
+
|
172
|
+
name = File.basename(file_path)
|
173
|
+
`split -b 5mb #{file_path} AppSendrSplit`
|
174
|
+
|
175
|
+
FileUtils.mv Dir.glob("AppSendrSplit*"), file_dir
|
176
|
+
end
|
75
177
|
|
76
178
|
|
77
179
|
#povisioning
|
@@ -142,20 +244,34 @@ module AppSendr::Command
|
|
142
244
|
if icon_files.nil? or icon_files.count == 0
|
143
245
|
icon_file = info['CFBundleIconFile']
|
144
246
|
else
|
145
|
-
|
146
|
-
if large_icon.nil?
|
147
|
-
large_icon = icon_files.first
|
148
|
-
end
|
149
|
-
icon_file = large_icon
|
247
|
+
icon_file = icon_files.grep(/@2x/i)
|
150
248
|
end
|
249
|
+
|
151
250
|
if icon_file and !icon_file.empty?
|
152
251
|
all_dir_contents = Dir["**/*"]
|
153
252
|
all_dir_contents = all_dir_contents - all_dir_contents.grep(/^build/)
|
154
|
-
|
155
253
|
icon = all_dir_contents.grep(%r{#{icon_file}}).flatten.first
|
156
254
|
if icon
|
157
255
|
icon_path = Dir.pwd+"/#{icon}"
|
158
256
|
end
|
257
|
+
elsif icon_files
|
258
|
+
|
259
|
+
all_dir_contents = Dir["**/*"]
|
260
|
+
all_dir_contents = all_dir_contents - all_dir_contents.grep(/^build/)
|
261
|
+
icon = all_dir_contents.grep(%r{#{icon_files.first}}).flatten.first
|
262
|
+
icon_dir = icon.sub(icon_files.first,"");
|
263
|
+
|
264
|
+
icon_full_path = Dir.pwd+"/#{icon_dir}"
|
265
|
+
biggest_icon_size = 0
|
266
|
+
biggest_icon = ""
|
267
|
+
for icon_file in icon_files
|
268
|
+
size = File.size(icon_full_path+icon_file)
|
269
|
+
if size > biggest_icon_size
|
270
|
+
biggest_icon = icon_full_path+icon_file
|
271
|
+
end
|
272
|
+
end
|
273
|
+
icon_path = biggest_icon
|
274
|
+
|
159
275
|
end
|
160
276
|
return icon_path
|
161
277
|
end
|
@@ -183,8 +299,12 @@ module AppSendr::Command
|
|
183
299
|
@app_dir = path_to_app_dir
|
184
300
|
end
|
185
301
|
return @app_name if @app_name
|
186
|
-
|
187
|
-
|
302
|
+
if File.directory? @app_dir
|
303
|
+
@app_name = Dir.entries(@app_dir).grep(/.+\.app$/).first
|
304
|
+
@app_name
|
305
|
+
else
|
306
|
+
error "#{@app_dir} does not exisit"
|
307
|
+
end
|
188
308
|
end
|
189
309
|
|
190
310
|
def ipa_app_name(app_name)
|
@@ -10,7 +10,7 @@ module AppSendr::Command
|
|
10
10
|
|
11
11
|
testers = appsendr.collaborators(read_app_id)
|
12
12
|
if testers["message"].size > 0
|
13
|
-
|
13
|
+
message "Your collaborators"
|
14
14
|
i = 0
|
15
15
|
display testers["message"].map {|app, id|
|
16
16
|
"#{i+=1}. #{app['name']} - #{app['email']}"
|
@@ -20,5 +20,33 @@ module AppSendr::Command
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
|
25
|
+
def add
|
26
|
+
|
27
|
+
if require_project(2,"add collaborators","an email", true)
|
28
|
+
email = args.shift.strip
|
29
|
+
|
30
|
+
begin
|
31
|
+
response = appsendr.add_collaborator(read_app_id,email)
|
32
|
+
rescue RestClient::RequestFailed => e
|
33
|
+
message "Errors"
|
34
|
+
response = JSON.parse(e.http_body)
|
35
|
+
response['message'].each{|err|
|
36
|
+
display err.join(" ")
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove
|
44
|
+
if require_project(1,"remove testers","an email")
|
45
|
+
entered_email = args.shift.strip
|
46
|
+
|
47
|
+
appsendr.remove_collaborator(read_app_id,entered_email)
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
23
51
|
end
|
24
52
|
end
|
@@ -11,20 +11,31 @@ module AppSendr::Command
|
|
11
11
|
# opts.parse!(args) rescue return false
|
12
12
|
#
|
13
13
|
#
|
14
|
+
notify = option_exists?('--notify', false)
|
14
15
|
|
15
16
|
@configuration = load_configuration
|
16
17
|
if require_project(1,"deploy","your active configuration name",true)
|
18
|
+
|
17
19
|
@app_dir = path_to_app_dir
|
18
20
|
@app_path = @app_dir+"/#{app_name}"
|
19
21
|
if @app_name
|
22
|
+
|
20
23
|
@notes = ask_for_notes
|
24
|
+
message "Deploying #{@app_name}"
|
25
|
+
|
26
|
+
message "Building IPA File"
|
21
27
|
|
22
28
|
@ipa_path = build_ipa_with_app_at_path
|
23
29
|
info = app_info
|
24
30
|
bundle_identifier ||= info['CFBundleIdentifier']
|
25
31
|
icon_path = icon_path(info)
|
26
|
-
|
27
|
-
|
32
|
+
if is_file_to_large?(@ipa_path)
|
33
|
+
upload_large_ipa(read_app_id,@ipa_path, profile_path,notify,@notes,bundle_identifier,icon_path)
|
34
|
+
else
|
35
|
+
appsendr.upload(read_app_id,@ipa_path, profile_path,notify,@notes,bundle_identifier,icon_path)
|
36
|
+
message "App deployed"
|
37
|
+
|
38
|
+
end
|
28
39
|
FileUtils.rm_rf @ipa_path
|
29
40
|
else
|
30
41
|
error "No .app file was built for the configuration \"#{@configuration}\". Compile your app or use the \"build\" command."
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module AppSendr::Command
|
2
|
+
class Groups < Base
|
3
|
+
def index #output testers
|
4
|
+
if in_project_dir?
|
5
|
+
|
6
|
+
groups = appsendr.groups(read_app_id)
|
7
|
+
if groups["message"].size > 0
|
8
|
+
message "Your groups"
|
9
|
+
i = 0
|
10
|
+
display groups["message"].map {|app, id|
|
11
|
+
"#{i+=1}. #{app['name'].ljust(35)}"
|
12
|
+
}.join("\n")
|
13
|
+
else
|
14
|
+
display "You have no groups."
|
15
|
+
end
|
16
|
+
else
|
17
|
+
error("You are not in a project directory")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add
|
22
|
+
if in_project_dir?
|
23
|
+
name = args.join(" ").strip
|
24
|
+
appsendr.add_group(read_app_id,name)
|
25
|
+
message "Group added"
|
26
|
+
else
|
27
|
+
error("You are not in a project directory")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def remove
|
32
|
+
if in_project_dir?
|
33
|
+
name = args.join(" ").strip
|
34
|
+
appsendr.remove_group(read_app_id,name)
|
35
|
+
message "Removed group"
|
36
|
+
else
|
37
|
+
error("You are not in a project directory")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_tester #<group> #<email>
|
42
|
+
|
43
|
+
if in_project_dir?
|
44
|
+
email = args.shift.strip
|
45
|
+
name = args.join(" ").strip
|
46
|
+
appsendr.add_group_tester(read_app_id,name, email)
|
47
|
+
message "Added tester to group"
|
48
|
+
else
|
49
|
+
error("You are not in a project directory")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
def remove_tester #<group> #<email>
|
53
|
+
if in_project_dir?
|
54
|
+
email = args.shift.strip
|
55
|
+
name = args.join(" ").strip
|
56
|
+
appsendr.remove_group_tester(read_app_id,name, email)
|
57
|
+
message "Added tester to group"
|
58
|
+
else
|
59
|
+
error("You are not in a project directory")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -49,12 +49,19 @@ module AppSendr::Command
|
|
49
49
|
group.command 'testers:remove <email> ', 'remove a tester'
|
50
50
|
group.command 'testers:clear', 'remove all testers'
|
51
51
|
group.command 'testers:notify <group>', 'notify testers about the latest build. group is optional.'
|
52
|
-
group.
|
53
|
-
|
52
|
+
group.command 'testers:devices', 'devices of your testers'
|
53
|
+
|
54
|
+
# group.space
|
55
|
+
# group.command 'groups', 'list groups'
|
56
|
+
# group.command 'groups:add <name>', 'add group'
|
57
|
+
# group.command 'groups:remove <name>', 'remove group'
|
58
|
+
# group.command 'groups:add_tester <email> <group name>', 'add tester to group'
|
59
|
+
# group.command 'groups:remove_tester <email> <group name>', 'remove tester from group'
|
60
|
+
|
54
61
|
group.space
|
55
62
|
group.command 'collaborators', 'list collaborators'
|
56
63
|
group.command 'collaborators:add <email>', 'add collaborators'
|
57
|
-
group.command 'collaborators:remove <email>','
|
64
|
+
group.command 'collaborators:remove <email>','remove collaborators'
|
58
65
|
end
|
59
66
|
|
60
67
|
end
|