shenzhen 0.0.1

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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shenzhen (0.0.1)
5
+ commander (~> 4.1.2)
6
+ faraday (~> 0.8.0)
7
+ faraday_middleware (~> 0.8.7)
8
+ json (~> 1.7.3)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ commander (4.1.2)
14
+ highline (~> 1.6.11)
15
+ faraday (0.8.1)
16
+ multipart-post (~> 1.1)
17
+ faraday_middleware (0.8.7)
18
+ faraday (>= 0.7.4, < 0.9)
19
+ highline (1.6.15)
20
+ json (1.7.5)
21
+ multipart-post (1.1.5)
22
+ rake (0.9.2.2)
23
+ rspec (0.6.4)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ rake (~> 0.9.2)
30
+ rspec (~> 0.6.1)
31
+ shenzhen!
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Mattt Thompson (http://mattt.me/)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # Shenzhen
2
+ **CLI for Building & Distributing iOS Apps (.ipa Files)**
3
+
4
+ Create `.ipa` files and then distribute them to TestFlight, all from the command line!
5
+
6
+ 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.
7
+
8
+ This project is starting with TestFlight, but will move to support other popular distribution methods, such as S3, CloudApp, and /or Dropbox. Suggestions (and pull requests) are very welcome.
9
+
10
+ > `shenzhen` is named for [深圳](http://en.wikipedia.org/wiki/Shenzhen), the Chinese city famous for its role as the center of manufacturing for a majority of consumer electronics, including iPhones and iPads. Its [sister project](https://github.com/mattt/cupertino)'s namesake, [Cupertino, CA](http://en.wikipedia.org/wiki/Cupertino,_California), is home to Apple, Inc.'s world headquarters.
11
+
12
+ ## Installation
13
+
14
+ ```
15
+ $ gem install shenzhen
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Shenzhen adds the `ipa` command to your PATH,
21
+
22
+ ```
23
+ $ ipa
24
+
25
+ Build and distribute iOS apps (.ipa files)
26
+
27
+ Commands:
28
+ build Create a new .ipa file for your app
29
+ distribute:testflight Distribute an .ipa file over testflight
30
+ help Display global or [command] help documentation.
31
+
32
+ Aliases:
33
+ distribute distribute:testflight
34
+
35
+ Global Options:
36
+ -h, --help Display help documentation
37
+ -v, --version Display version information
38
+ -t, --trace Display backtrace when an error occurs
39
+ ```
40
+
41
+ ### Building & Distribution
42
+
43
+ ```
44
+ $ cd /path/to/iOS Project/
45
+ $ ipa build
46
+ $ ipa distribute
47
+ ```
48
+
49
+ ## Contact
50
+
51
+ Mattt Thompson
52
+
53
+ - http://github.com/mattt
54
+ - http://twitter.com/mattt
55
+ - m@mattt.me
56
+
57
+ ## License
58
+
59
+ Shenzhen is available under the MIT license. See the LICENSE file for more info.
@@ -0,0 +1,10 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ gemspec = eval(File.read("shenzhen.gemspec"))
5
+
6
+ task :build => "#{gemspec.full_name}.gem"
7
+
8
+ file "#{gemspec.full_name}.gem" => gemspec.files + ["shenzhen.gemspec"] do
9
+ system "gem build shenzhen.gemspec"
10
+ end
data/bin/ipa ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'commander/import'
4
+
5
+ $:.push File.expand_path("../../lib", __FILE__)
6
+ require 'shenzhen'
7
+
8
+ HighLine.track_eof = false # Fix for built-in Ruby
9
+
10
+ program :version, Shenzhen::VERSION
11
+ program :description, 'Build and distribute iOS apps (.ipa files)'
12
+
13
+ program :help, 'Author', 'Mattt Thompson <m@mattt.me>'
14
+ program :help, 'Website', 'http://mattt.me'
15
+ program :help_formatter, :compact
16
+
17
+ # global_option '--verbose'
18
+ default_command :help
19
+
20
+ require 'shenzhen/commands'
@@ -0,0 +1,6 @@
1
+ module Shenzhen
2
+ VERSION = '0.0.1'
3
+ end
4
+
5
+ require 'shenzhen/agvtool'
6
+ require 'shenzhen/xcodebuild'
@@ -0,0 +1,17 @@
1
+ module Shenzhen::Agvtool
2
+ class << self
3
+ def what_version
4
+ output = `agvtool what-version -terse`
5
+ output.length > 0 ? output : nil
6
+ end
7
+
8
+ alias :vers :what_version
9
+
10
+ def what_marketing_version
11
+ output = `agvtool what-marketing-version -terse`
12
+ output.scan(/\=(.+)$/).flatten.first
13
+ end
14
+
15
+ alias :mvers :what_marketing_version
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ $:.push File.expand_path('../', __FILE__)
2
+
3
+ require 'plugins/testflight'
4
+
5
+ require 'commands/build'
6
+ require 'commands/distribute'
7
+
@@ -0,0 +1,101 @@
1
+ command :build do |c|
2
+ c.syntax = 'ipa build [options] [output]'
3
+ c.summary = 'Create a new .ipa file for your app'
4
+ c.description = ''
5
+
6
+ c.example 'description', 'ipa release 478 --tag 3.7.2 --scheme MyApp'
7
+ c.option '-w', '--workspace WORKSPACE', 'Workspace (.xcworkspace) file to use to build app (automatically detected in current directory)'
8
+ c.option '-p', '--project PROJECT', 'Project (.xcodeproj) file to use to build app (automatically detected in current directory, overridden by --workspace option, if passed)'
9
+ c.option '-c', '--configuration CONFIGURATION', 'Configuration used to build'
10
+ c.option '-s', '--scheme SCHEME', 'Scheme used to build app'
11
+ c.option '-q', '--quiet', 'Silence warning and success messages'
12
+
13
+ c.action do |args, options|
14
+ validate_xcode_version!
15
+
16
+ @xcodebuild_info = Shenzhen::XcodeBuild.info
17
+
18
+ @workspace = options.workspace
19
+ @project = options.project
20
+ @scheme = options.scheme
21
+ @configuration = options.configuration
22
+
23
+ determine_workspace_or_project! unless @workspace || @project
24
+
25
+ determine_configuration! unless @configuration
26
+ say_error "Configuration #{@configuration} not found" and abort unless @xcodebuild_info.build_configurations.include?(@configuration)
27
+
28
+ determine_scheme! unless @scheme
29
+ say_error "Scheme #{@scheme} not found" and abort unless @xcodebuild_info.schemes.include?(@scheme)
30
+
31
+ say_warning "Building \"#{@workspace || @project}\" with Scheme \"#{@scheme}\" and Configuration \"#{@configuration}\"\n" unless options.quiet
32
+
33
+ log "xcodebuild", (@workspace || @project)
34
+
35
+ flags = []
36
+ flags << "-sdk iphoneos"
37
+ flags << "-workspace '#{@workspace}'" if @workspace
38
+ flags << "-project '#{@project}'" if @project
39
+ flags << "-scheme '#{@scheme}'" if @scheme
40
+ flags << "-configuration Release"
41
+
42
+ ENV['CC'] = nil # Fix for RVM
43
+ abort unless system "xcodebuild #{flags.join(' ')} clean build 1> /dev/null"
44
+
45
+ log "xcrun", "PackageApplication"
46
+
47
+ @xcodebuild_settings = Shenzhen::XcodeBuild.settings(flags)
48
+
49
+ @app_path = File.join(@xcodebuild_settings['BUILT_PRODUCTS_DIR'], @xcodebuild_settings['PRODUCT_NAME']) + ".app"
50
+ @dsym_path = @app_path + ".dSYM"
51
+ @ipa_path = File.join(Dir.pwd, @xcodebuild_settings['PRODUCT_NAME']) + ".ipa"
52
+ abort unless system %{xcrun -sdk iphoneos PackageApplication -v "#{@app_path}" -o "#{@ipa_path}" --embed "#{@dsym_path}" 1> /dev/null}
53
+
54
+ say_ok "#{File.basename(@ipa_path)} successfully built" unless options.quiet
55
+ end
56
+
57
+ private
58
+
59
+ def validate_xcode_version!
60
+ version = Shenzhen::XcodeBuild.version
61
+ say_error "Shenzhen requires Xcode 4 (found #{version}). Please install or switch to the latest Xcode." and abort if version < "4.0.0"
62
+ end
63
+
64
+ def determine_workspace_or_project!
65
+ workspaces, projects = Dir["*.xcworkspace"], Dir["*.xcodeproj"]
66
+
67
+ if workspaces.empty?
68
+ if projects.empty?
69
+ say_error "No Xcode projects or workspaces found in current directory" and abort
70
+ else
71
+ if projects.length == 1
72
+ @project = projects.first
73
+ else
74
+ @project = choose "Select a project:", *projects
75
+ end
76
+ end
77
+ else
78
+ if workspaces.length == 1
79
+ @workspace = workspaces.first
80
+ else
81
+ @workspace = choose "Select a workspace:", *workspaces
82
+ end
83
+ end
84
+ end
85
+
86
+ def determine_scheme!
87
+ if @xcodebuild_info.schemes.length == 1
88
+ @scheme = @xcodebuild_info.schemes.first
89
+ else
90
+ @scheme = choose "Select a scheme:", *@xcodebuild_info.schemes
91
+ end
92
+ end
93
+
94
+ def determine_configuration!
95
+ if @xcodebuild_info.build_configurations.length == 1
96
+ @configuration = @xcodebuild_info.build_configurations.first
97
+ else
98
+ @configuration = choose "Select a configuration:", *@xcodebuild_info.build_configurations
99
+ end
100
+ end
101
+ end
@@ -0,0 +1 @@
1
+ alias_command :distribute, :'distribute:testflight'
@@ -0,0 +1,104 @@
1
+ require 'openssl'
2
+ require 'faraday'
3
+ require 'faraday_middleware'
4
+
5
+ module Shenzhen::Plugins
6
+ module TestFlight
7
+ class Client
8
+ HOSTNAME = 'testflightapp.com'
9
+
10
+ def initialize(api_token, team_token)
11
+ @api_token, @team_token = api_token, team_token
12
+ @connection = Faraday.new(:url => "http://#{HOSTNAME}") do |builder|
13
+ builder.request :multipart
14
+ builder.request :json
15
+ builder.response :json, :content_type => /\bjson$/
16
+ builder.use FaradayMiddleware::FollowRedirects
17
+ builder.adapter :net_http
18
+ end
19
+ end
20
+
21
+ def upload_build(ipa, options)
22
+ options.update({
23
+ :api_token => @api_token,
24
+ :team_token => @team_token,
25
+ :file => Faraday::UploadIO.new(ipa, 'application/octet-stream')
26
+ })
27
+
28
+ @connection.post("/api/builds.json", options).on_complete do |env|
29
+ yield env[:status], env[:body] if block_given?
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ command :'distribute:testflight' do |c|
37
+ c.syntax = "ipa distribute:testflight [options]"
38
+ c.summary = "Distribute an .ipa file over testflight"
39
+ c.description = ""
40
+ c.option '-f', '--file FILE', ".ipa file for the build"
41
+ c.option '-t', '--api_token TOKEN', "API Token. Available at https://testflightapp.com/account/#api-token"
42
+ c.option '-T', '--team_token TOKEN', "Team Token. Available at https://testflightapp.com/dashboard/team/edit/"
43
+ c.option '-m', '--notes NOTES', "Release notes for the build"
44
+ c.option '-l', '--lists LISTS', "Comma separated distribution list names which will receive access to the build"
45
+ c.option '--notify', "Notify permitted teammates to install the build"
46
+ c.option '--replace', "Replace binary for an existing build if one is found with the same name/bundle version"
47
+ c.option '-q', '--quiet', "Silence warning and success messages"
48
+
49
+ c.action do |args, options|
50
+ determine_file! unless @file = options.file
51
+ say_error "Missing or unspecified .ipa file" and abort unless @file and File.exist?(@file)
52
+
53
+ determine_api_token! unless @api_token = options.api_token
54
+ say_error "Missing API Token" and abort unless @api_token
55
+
56
+ determine_team_token! unless @team_token = options.team_token
57
+
58
+ determine_notes! unless @notes = options.notes
59
+ say_error "Missing release notes" and abort unless @notes
60
+
61
+ parameters = {}
62
+ parameters[:file] = @file
63
+ parameters[:notes] = @notes
64
+ parameters[:notify] = "true" if options.notify
65
+ parameters[:replace] = "true" if options.replace
66
+ parameters[:distribution_lists] = options.lists if options.lists
67
+
68
+ client = Shenzhen::Plugins::TestFlight::Client.new(@api_token, @team_token)
69
+ response = client.upload_build(@file, parameters)
70
+ case response.status
71
+ when 200...300
72
+ say_ok "Build successfully uploaded to TestFlight"
73
+ else
74
+ say_error "Error uploading to TestFlight: #{response.body}"
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def determine_api_token!
81
+ @api_token ||= ask "API Token:"
82
+ end
83
+
84
+ def determine_team_token!
85
+ @team_token ||= ask "Team Token:"
86
+ end
87
+
88
+ def determine_file!
89
+ files = Dir['*.ipa']
90
+ @file ||= case files.length
91
+ when 0 then nil
92
+ when 1 then files.first
93
+ else
94
+ @file = choose "Select an .ipa File:", *files
95
+ end
96
+ end
97
+
98
+ def determine_notes!
99
+ placeholder = %{What's new in this release: }
100
+
101
+ @notes = ask_editor placeholder
102
+ @notes = nil if @notes == placeholder
103
+ end
104
+ end
@@ -0,0 +1,63 @@
1
+ require 'ostruct'
2
+
3
+ module Shenzhen::XcodeBuild
4
+ class Info < OpenStruct; end
5
+
6
+ class Error < StandardError; end
7
+ class NilOutputError < Error; end
8
+
9
+ class << self
10
+ def info
11
+ output = `xcodebuild -list 2> /dev/null`
12
+ raise Error.new $1 if /^xcodebuild\: error\: (.+)$/ === output
13
+ raise NilOutputError unless /\S/ === output
14
+
15
+ lines = output.split(/\n/)
16
+ hash = {}
17
+ group = nil
18
+
19
+ hash[:project] = lines.shift.match(/\"(.+)\"\:/)[1]
20
+
21
+ lines.each do |line|
22
+ if /\:$/ === line
23
+ group = line.strip[0...-1].downcase.gsub(/\s+/, '_')
24
+ hash[group] = []
25
+ next
26
+ end
27
+
28
+ unless group.nil? or /\.$/ === line
29
+ hash[group] << line.strip
30
+ end
31
+ end
32
+
33
+ hash.each do |group, values|
34
+ next unless Array === values
35
+ values.delete("") and values.uniq!
36
+ end
37
+
38
+ Info.new(hash)
39
+ end
40
+
41
+ def settings(flags = [])
42
+ output = `xcodebuild #{flags.join(' ')} -showBuildSettings 2> /dev/null`
43
+ raise Error.new $1 if /^xcodebuild\: error\: (.+)$/ === output
44
+ raise NilOutputError unless /\S/ === output
45
+
46
+ lines = output.split(/\n/)
47
+ lines.shift
48
+
49
+ hash = {}
50
+ lines.each do |line|
51
+ key, value = line.split(/\=/).collect(&:strip)
52
+ hash[key] = value
53
+ end
54
+
55
+ hash
56
+ end
57
+
58
+ def version
59
+ output = `xcodebuild -version`
60
+ output.scan(/([\d\.?]+)/).flatten.first rescue nil
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "shenzhen"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "shenzhen"
7
+ s.authors = ["Mattt Thompson"]
8
+ s.email = "m@mattt.me"
9
+ s.homepage = "http://github.com/mattt/shenzhen"
10
+ s.version = Shenzhen::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.summary = "Shenzhen"
13
+ s.description = "CLI for Building & Distributing iOS Apps (.ipa Files)"
14
+
15
+ s.add_development_dependency "rspec", "~> 0.6.1"
16
+ s.add_development_dependency "rake", "~> 0.9.2"
17
+
18
+ s.add_dependency "commander", "~> 4.1.2"
19
+ s.add_dependency "json", "~> 1.7.3"
20
+ s.add_dependency "faraday", "~> 0.8.0"
21
+ s.add_dependency "faraday_middleware", "~> 0.8.7"
22
+
23
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shenzhen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mattt Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-28 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70338229563260 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.6.1
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70338229563260
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70338229562040 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70338229562040
36
+ - !ruby/object:Gem::Dependency
37
+ name: commander
38
+ requirement: &70338229560720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 4.1.2
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70338229560720
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: &70338229557880 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.7.3
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70338229557880
58
+ - !ruby/object:Gem::Dependency
59
+ name: faraday
60
+ requirement: &70338229546680 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 0.8.0
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70338229546680
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday_middleware
71
+ requirement: &70338229545900 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.8.7
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *70338229545900
80
+ description: CLI for Building & Distributing iOS Apps (.ipa Files)
81
+ email: m@mattt.me
82
+ executables:
83
+ - ipa
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - ./Gemfile
88
+ - ./Gemfile.lock
89
+ - ./lib/shenzhen/agvtool.rb
90
+ - ./lib/shenzhen/commands/build.rb
91
+ - ./lib/shenzhen/commands/distribute.rb
92
+ - ./lib/shenzhen/commands.rb
93
+ - ./lib/shenzhen/plugins/testflight.rb
94
+ - ./lib/shenzhen/xcodebuild.rb
95
+ - ./lib/shenzhen.rb
96
+ - ./LICENSE
97
+ - ./Rakefile
98
+ - ./README.md
99
+ - ./shenzhen.gemspec
100
+ - bin/ipa
101
+ homepage: http://github.com/mattt/shenzhen
102
+ licenses: []
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ segments:
114
+ - 0
115
+ hash: 522475160615829235
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ segments:
123
+ - 0
124
+ hash: 522475160615829235
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 1.8.15
128
+ signing_key:
129
+ specification_version: 3
130
+ summary: Shenzhen
131
+ test_files: []