shenzhen 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +32 -3
- data/lib/shenzhen/commands.rb +2 -0
- data/lib/shenzhen/plugins/fir.rb +129 -0
- data/lib/shenzhen/plugins/pgyer.rb +135 -0
- data/lib/shenzhen/version.rb +1 -1
- metadata +4 -5
- data/shenzhen-0.8.0.gem +0 -0
- data/shenzhen-0.8.1.gem +0 -0
- data/shenzhen-0.8.2.gem +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd6f780fc6f948207a05137f7601aa343f853748
|
4
|
+
data.tar.gz: 5d2c3f534324b6e693ab7e506d5f1d1fb2aef0b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c15377a72c05879235ee17772e26d06fa20d49e6dbed6131e740704834bd586a664a425f446750eb9daa8db209595f2445df815d31b93b889b6c89bda9a3e31f
|
7
|
+
data.tar.gz: 8a38072bf6fc2311e48acce05d99439daac70335bb4edac49c9acd1d956c358a06b20565c34e14ff60584b6cad856807ca730104ee007ba2dc5747ffe5258648
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,18 @@
|
|
1
1
|
![Shenzhen](https://raw.github.com/nomad/nomad.github.io/assets/shenzhen-banner.png)
|
2
2
|
|
3
|
-
Create `.ipa` files and
|
3
|
+
Create `.ipa` files and distribute them from the command line, using any of the following methods:
|
4
4
|
|
5
|
-
|
5
|
+
- [iTunes Connect](https://itunesconnect.apple.com)
|
6
|
+
- [TestFlight](https://testflightapp.com/)
|
7
|
+
- [HockeyApp](http://www.hockeyapp.net)
|
8
|
+
- [Crashlytics Beta](http://try.crashlytics.com/beta/)
|
9
|
+
- [DeployGate](https://deploygate.com)
|
10
|
+
- [Fly It Remotely (FIR.im)](http://fir.im)
|
11
|
+
- [蒲公英 (PGYER)](http://www.pgyer.com)
|
12
|
+
- [Amazon S3](http://aws.amazon.com/s3/)
|
13
|
+
- FTP / SFTP
|
14
|
+
|
15
|
+
Less cumbersome than clicking around in Xcode, and less hassle than rolling your own build script, Shenzhen radically improves the process of getting new builds out to testers and enterprises.
|
6
16
|
|
7
17
|
> `shenzhen` is named for [深圳](http://en.wikipedia.org/wiki/Shenzhen), the Chinese city famous for being the center of manufacturing for a majority of consumer electronics, including iPhones and iPads.
|
8
18
|
> It's part of a series of world-class command-line utilities for iOS development, which includes [Cupertino](https://github.com/mattt/cupertino) (Apple Dev Center management), [Houston](https://github.com/mattt/houston) (Push Notifications), [Venice](https://github.com/mattt/venice) (In-App Purchase Receipt Verification), [Dubai](https://github.com/mattt/dubai) (Passbook pass generation), and [Nashville](https://github.com/nomad/nashville) (iTunes Store API).
|
@@ -34,7 +44,7 @@ Shenzhen adds the `ipa` command to your PATH:
|
|
34
44
|
```
|
35
45
|
$ ipa
|
36
46
|
|
37
|
-
|
47
|
+
Build and distribute iOS apps (.ipa files)
|
38
48
|
|
39
49
|
Commands:
|
40
50
|
build Create a new .ipa file for your app
|
@@ -42,7 +52,9 @@ $ ipa
|
|
42
52
|
distribute:hockeyapp Distribute an .ipa file over HockeyApp
|
43
53
|
distribute:crashlytics Distribute an .ipa file over Crashlytics
|
44
54
|
distribute:deploygate Distribute an .ipa file over deploygate
|
55
|
+
distribute:firim Distribute an .ipa file over fir.im
|
45
56
|
distribute:itunesconnect Upload an .ipa file to iTunes Connect for review
|
57
|
+
distribute:pgyer Distribute an .ipa file over Pgyer
|
46
58
|
distribute:ftp Distribute an .ipa file over FTP
|
47
59
|
distribute:s3 Distribute an .ipa file over Amazon S3
|
48
60
|
info Show mobile provisioning information about an .ipa file
|
@@ -118,6 +130,23 @@ $ ipa distribute:s3 -a ACCESS_KEY_ID -s SECRET_ACCESS_KEY -b BUCKET
|
|
118
130
|
|
119
131
|
> Shenzhen will load credentials from the environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` unless otherwise specified.
|
120
132
|
|
133
|
+
#### FIR (Fly it Remotely)
|
134
|
+
|
135
|
+
```
|
136
|
+
$ ipa distribute:fir -u USER_TOKEN -a APP_ID
|
137
|
+
```
|
138
|
+
|
139
|
+
> Shenzhen will load credentials from the environment variables `FIR_USER_TOKEN`, `FIR_APP_ID` unless otherwise specified.
|
140
|
+
|
141
|
+
#### 蒲公英 (PGYER)
|
142
|
+
|
143
|
+
```
|
144
|
+
$ ipa distribute:pgyer -u USER_KEY -a APP_KEY
|
145
|
+
```
|
146
|
+
|
147
|
+
> Shenzhen will load credentials from the environment variables `PGYER_USER_KEY`, `PGYER_APP_KEY` unless otherwise specified.
|
148
|
+
|
149
|
+
|
121
150
|
#### iTunes Connect Distribution
|
122
151
|
|
123
152
|
```
|
data/lib/shenzhen/commands.rb
CHANGED
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Shenzhen::Plugins
|
7
|
+
module Fir
|
8
|
+
class Client
|
9
|
+
HOSTNAME = 'fir.im'
|
10
|
+
VERSION = 'v2'
|
11
|
+
|
12
|
+
def initialize(user_token)
|
13
|
+
@user_token = user_token
|
14
|
+
|
15
|
+
@connection = Faraday.new(:url => "http://#{HOSTNAME}") do |builder|
|
16
|
+
builder.request :url_encoded
|
17
|
+
builder.response :json
|
18
|
+
builder.use FaradayMiddleware::FollowRedirects
|
19
|
+
builder.adapter :net_http
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_app_info(app_id)
|
24
|
+
options = {
|
25
|
+
:type => 'ios',
|
26
|
+
:token => @user_token,
|
27
|
+
}
|
28
|
+
|
29
|
+
@connection.get("/api/#{VERSION}/app/info/#{app_id}", options) do |env|
|
30
|
+
yield env[:status], env[:body] if block_given?
|
31
|
+
end
|
32
|
+
rescue Faraday::Error::TimeoutError
|
33
|
+
say_error "Timed out while geting app info." and abort
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_app_info(app_id, options)
|
37
|
+
@connection.put("/api/#{VERSION}/app/#{app_id}?token=#{@user_token}", options) do |env|
|
38
|
+
yield env[:status], env[:body] if block_given?
|
39
|
+
end
|
40
|
+
rescue Faraday::Error::TimeoutError
|
41
|
+
say_error "Timed out while geting app info." and abort
|
42
|
+
end
|
43
|
+
|
44
|
+
def upload_build(ipa, options)
|
45
|
+
connection = Faraday.new(:url => options['url'], :request => { :timeout => 360 }) do |builder|
|
46
|
+
builder.request :multipart
|
47
|
+
builder.response :json
|
48
|
+
builder.use FaradayMiddleware::FollowRedirects
|
49
|
+
builder.adapter :net_http
|
50
|
+
end
|
51
|
+
|
52
|
+
options = {
|
53
|
+
:key => options['type'],
|
54
|
+
:token => options['token'],
|
55
|
+
:file => Faraday::UploadIO.new(ipa, 'application/octet-stream')
|
56
|
+
}
|
57
|
+
|
58
|
+
connection.post('/', options).on_complete do |env|
|
59
|
+
yield env[:status], env[:body] if block_given?
|
60
|
+
end
|
61
|
+
rescue Errno::EPIPE
|
62
|
+
say_error "Upload failed. Check internet connection is ok." and abort
|
63
|
+
rescue Faraday::Error::TimeoutError
|
64
|
+
say_error "Timed out while uploading build. Check https://fir.im// to see if the upload was completed." and abort
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
command :'distribute:fir' do |c|
|
71
|
+
c.syntax = "ipa distribute:fir [options]"
|
72
|
+
c.summary = "Distribute an .ipa file over fir.im"
|
73
|
+
c.description = ""
|
74
|
+
c.option '-f', '--file FILE', ".ipa file for the build"
|
75
|
+
c.option '-u', '--user_token TOKEN', "User Token. Available at http://fir.im/user/info"
|
76
|
+
c.option '-a', '--app_id APPID', "App Id (iOS Bundle identifier)"
|
77
|
+
c.option '-n', '--notes NOTES', "Release notes for the build"
|
78
|
+
|
79
|
+
c.action do |args, options|
|
80
|
+
determine_file! unless @file = options.file
|
81
|
+
say_error "Missing or unspecified .ipa file" and abort unless @file and File.exist?(@file)
|
82
|
+
|
83
|
+
determine_fir_user_token! unless @user_token = options.user_token || ENV['FIR_USER_TOKEN']
|
84
|
+
say_error "Missing User Token" and abort unless @user_token
|
85
|
+
|
86
|
+
determine_fir_app_id! unless @app_id = options.app_id || ENV['FIR_APP_ID']
|
87
|
+
say_error "Missing App Id" and abort unless @app_id
|
88
|
+
|
89
|
+
determine_notes! unless @notes = options.notes
|
90
|
+
say_error "Missing release notes" and abort unless @notes
|
91
|
+
|
92
|
+
client = Shenzhen::Plugins::Fir::Client.new(@user_token)
|
93
|
+
app_response = client.get_app_info(@app_id)
|
94
|
+
if app_response.status == 200
|
95
|
+
upload_response = client.upload_build(@file, app_response.body['bundle']['pkg'])
|
96
|
+
|
97
|
+
if upload_response.status == 200
|
98
|
+
oid = upload_response.body['appOid']
|
99
|
+
today = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
100
|
+
@notes ||= "Upload on #{today}"
|
101
|
+
|
102
|
+
app_response = client.update_app_info(oid, {
|
103
|
+
:changelog => @notes,
|
104
|
+
})
|
105
|
+
|
106
|
+
if app_response.status == 200
|
107
|
+
app_short_uri = app_response.body['short']
|
108
|
+
say_ok "Build successfully uploaded to Fir, visit url: http://fir.im/#{app_short_uri}"
|
109
|
+
else
|
110
|
+
say_error "Error updating build information: #{app_response.body[:error]}" and abort
|
111
|
+
end
|
112
|
+
else
|
113
|
+
say_error "Error uploading to Fir: #{upload_response.body[:error]}" and abort
|
114
|
+
end
|
115
|
+
else
|
116
|
+
say_error "Error getting app information: #{response.body[:error]}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def determine_fir_user_token!
|
123
|
+
@user_token ||= ask "User Token:"
|
124
|
+
end
|
125
|
+
|
126
|
+
def determine_fir_app_id!
|
127
|
+
@app_id ||= ask "App Id:"
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'faraday'
|
4
|
+
require 'faraday_middleware'
|
5
|
+
|
6
|
+
module Shenzhen::Plugins
|
7
|
+
module Pgyer
|
8
|
+
class Client
|
9
|
+
HOSTNAME = 'www.pgyer.com'
|
10
|
+
|
11
|
+
def initialize(user_key, api_key)
|
12
|
+
@user_key, @api_key = user_key, api_key
|
13
|
+
@connection = Faraday.new(:url => "http://#{HOSTNAME}", :request => { :timeout => 120 }) do |builder|
|
14
|
+
builder.request :multipart
|
15
|
+
builder.request :json
|
16
|
+
builder.response :json, :content_type => /\bjson$/
|
17
|
+
builder.use FaradayMiddleware::FollowRedirects
|
18
|
+
builder.adapter :net_http
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload_build(ipa, options)
|
23
|
+
options.update({
|
24
|
+
:uKey => @user_key,
|
25
|
+
:_api_key => @api_key,
|
26
|
+
:file => Faraday::UploadIO.new(ipa, 'application/octet-stream')
|
27
|
+
})
|
28
|
+
|
29
|
+
@connection.post("/apiv1/app/upload", options).on_complete do |env|
|
30
|
+
yield env[:status], env[:body] if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
rescue Faraday::Error::TimeoutError
|
34
|
+
say_error "Timed out while uploading build. Check http://www.pgyer.com/my to see if the upload was completed." and abort
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_app_info(options)
|
38
|
+
connection = Faraday.new(:url => "http://#{HOSTNAME}", :request => { :timeout => 120 }) do |builder|
|
39
|
+
builder.request :url_encoded
|
40
|
+
builder.request :json
|
41
|
+
builder.response :logger
|
42
|
+
builder.response :json, :content_type => /\bjson$/
|
43
|
+
builder.use FaradayMiddleware::FollowRedirects
|
44
|
+
builder.adapter :net_http
|
45
|
+
end
|
46
|
+
|
47
|
+
options.update({
|
48
|
+
:uKey => @user_key,
|
49
|
+
:_api_key => @api_key,
|
50
|
+
})
|
51
|
+
|
52
|
+
connection.post("/apiv1/app/update", options) do |env|
|
53
|
+
yield env[:status], env[:body] if block_given?
|
54
|
+
end
|
55
|
+
|
56
|
+
rescue Faraday::Error::TimeoutError
|
57
|
+
say_error "Timed out while uploading build. Check http://www.pgyer.com/my to see if the upload was completed." and abort
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
command :'distribute:pgyer' do |c|
|
64
|
+
c.syntax = "ipa distribute:pgyer [options]"
|
65
|
+
c.summary = "Distribute an .ipa file over Pgyer"
|
66
|
+
c.description = ""
|
67
|
+
c.option '-f', '--file FILE', ".ipa file for the build"
|
68
|
+
c.option '-a', '--api_key KEY', "API KEY. Available at http://www.pgyer.com/doc/api#uploadApp"
|
69
|
+
c.option '-u', '--user_key KEY', "USER KEY. Available at http://www.pgyer.com/doc/api#uploadApp/"
|
70
|
+
c.option '--range RANGE', "Publish range. e.g. 1 (default), 2, 3"
|
71
|
+
c.option '--[no-]public', "Allow build app on public to download. it is not public default."
|
72
|
+
c.option '--password PASSWORD', "Set password to allow visit app web page."
|
73
|
+
|
74
|
+
c.action do |args, options|
|
75
|
+
determine_file! unless @file = options.file
|
76
|
+
say_error "Missing or unspecified .ipa file" and abort unless @file and File.exist?(@file)
|
77
|
+
|
78
|
+
determine_testflight_user_key! unless @user_key = options.user_key || ENV['PGYER_USER_KEY']
|
79
|
+
say_error "Missing User Key" and abort unless @user_key
|
80
|
+
|
81
|
+
determine_testflight_api_key! unless @api_key = options.api_key || ENV['PGYER_API_KEY']
|
82
|
+
say_error "Missing API Key" and abort unless @api_key
|
83
|
+
|
84
|
+
determine_publish_range! unless @publish_range = options.range
|
85
|
+
say_error "Missing Publish Range" and abort unless @publish_range
|
86
|
+
|
87
|
+
determine_is_public! unless @is_public = !!options.public
|
88
|
+
@is_public = @is_public ? 1 : 2
|
89
|
+
|
90
|
+
parameters = {}
|
91
|
+
parameters[:publishRange] = @publish_range
|
92
|
+
parameters[:isPublishToPublic] = @is_public
|
93
|
+
parameters[:password] = options.password.chomp if options.password
|
94
|
+
|
95
|
+
client = Shenzhen::Plugins::Pgyer::Client.new(@user_key, @api_key)
|
96
|
+
response = client.upload_build(@file, parameters)
|
97
|
+
case response.status
|
98
|
+
when 200...300
|
99
|
+
app_id = response.body['data']['appKey']
|
100
|
+
app_short_uri = response.body['data']['appShortcutUrl']
|
101
|
+
|
102
|
+
app_response = client.update_app_info({
|
103
|
+
:aKey => app_id,
|
104
|
+
:appUpdateDescription => @notes
|
105
|
+
})
|
106
|
+
|
107
|
+
if app_response.status == 200
|
108
|
+
say_ok "Build successfully uploaded to Pgyer, visit url: http://www.pgyer.com/#{app_short_uri}"
|
109
|
+
else
|
110
|
+
say_error "Error update build information: #{response.body}" and abort
|
111
|
+
end
|
112
|
+
else
|
113
|
+
say_error "Error uploading to Pgyer: #{response.body}" and abort
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def determine_pgyer_api_key!
|
120
|
+
@api_key ||= ask "API Key:"
|
121
|
+
end
|
122
|
+
|
123
|
+
def determine_pgyer_user_key!
|
124
|
+
@user_key ||= ask "User Key:"
|
125
|
+
end
|
126
|
+
|
127
|
+
def determine_publish_range!
|
128
|
+
@publish_range ||= "1"
|
129
|
+
end
|
130
|
+
|
131
|
+
def determine_is_public!
|
132
|
+
@is_public ||= false
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
data/lib/shenzhen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shenzhen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattt Thompson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -209,9 +209,11 @@ files:
|
|
209
209
|
- ./lib/shenzhen/plistbuddy.rb
|
210
210
|
- ./lib/shenzhen/plugins/crashlytics.rb
|
211
211
|
- ./lib/shenzhen/plugins/deploygate.rb
|
212
|
+
- ./lib/shenzhen/plugins/fir.rb
|
212
213
|
- ./lib/shenzhen/plugins/ftp.rb
|
213
214
|
- ./lib/shenzhen/plugins/hockeyapp.rb
|
214
215
|
- ./lib/shenzhen/plugins/itunesconnect.rb
|
216
|
+
- ./lib/shenzhen/plugins/pgyer.rb
|
215
217
|
- ./lib/shenzhen/plugins/s3.rb
|
216
218
|
- ./lib/shenzhen/plugins/testflight.rb
|
217
219
|
- ./lib/shenzhen/version.rb
|
@@ -220,9 +222,6 @@ files:
|
|
220
222
|
- ./LICENSE
|
221
223
|
- ./Rakefile
|
222
224
|
- ./README.md
|
223
|
-
- ./shenzhen-0.8.0.gem
|
224
|
-
- ./shenzhen-0.8.1.gem
|
225
|
-
- ./shenzhen-0.8.2.gem
|
226
225
|
- ./shenzhen.gemspec
|
227
226
|
- bin/ipa
|
228
227
|
homepage: http://nomad-cli.com
|
data/shenzhen-0.8.0.gem
DELETED
Binary file
|
data/shenzhen-0.8.1.gem
DELETED
Binary file
|
data/shenzhen-0.8.2.gem
DELETED
Binary file
|