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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2745f53de64c6a957f3a244a80ed2bba9158bef2
4
- data.tar.gz: 07d9564be28ee258ca0a34fd455d6cde603d6458
3
+ metadata.gz: cd6f780fc6f948207a05137f7601aa343f853748
4
+ data.tar.gz: 5d2c3f534324b6e693ab7e506d5f1d1fb2aef0b6
5
5
  SHA512:
6
- metadata.gz: 9c66500a86a7a9ffa9120f565a0d151b4fa9dc87bad995730d1d186d8f06b4b03602e9ac0f5f577f4c86d818b95f4639968e7ddec82053c511079d2a8ff9ad8a
7
- data.tar.gz: 3fab0ee6067d5f030bbed3de04d6ee3dd7e12aec3887b59da1e4a5a0266846a2b1b842e2923141015d6a4d52754d42e59736bfd13105a25c03dfc085c91d6b95
6
+ metadata.gz: c15377a72c05879235ee17772e26d06fa20d49e6dbed6131e740704834bd586a664a425f446750eb9daa8db209595f2445df815d31b93b889b6c89bda9a3e31f
7
+ data.tar.gz: 8a38072bf6fc2311e48acce05d99439daac70335bb4edac49c9acd1d956c358a06b20565c34e14ff60584b6cad856807ca730104ee007ba2dc5747ffe5258648
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shenzhen (0.9.0)
4
+ shenzhen (0.10.0)
5
5
  aws-sdk (~> 1.0)
6
6
  commander (~> 4.1)
7
7
  dotenv (~> 0.7)
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 then distribute them with [TestFlight](https://testflightapp.com/), [HockeyApp](http://www.hockeyapp.net), [Crashlytics Beta](http://try.crashlytics.com/beta/), [DeployGate](https://deploygate.com), or even through [iTunes Connect](https://itunesconnect.apple.com)—all from the command line!
3
+ Create `.ipa` files and distribute them from the command line, using any of the following methods:
4
4
 
5
- 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.
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
- Build and distribute iOS apps (.ipa files)
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
  ```
@@ -7,6 +7,8 @@ require 'plugins/itunesconnect'
7
7
  require 'plugins/ftp'
8
8
  require 'plugins/s3'
9
9
  require 'plugins/crashlytics'
10
+ require 'plugins/fir'
11
+ require 'plugins/pgyer'
10
12
 
11
13
  require 'commands/build'
12
14
  require 'commands/distribute'
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Shenzhen
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
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.9.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-17 00:00:00.000000000 Z
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