shenzhen 0.9.0 → 0.10.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.
- 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
|

|
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
|