xcjobs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 091108d90cf5ed6a1cec28b8bec082b7aab8ccfd
4
+ data.tar.gz: 919b0dae45768817875b8450ab5a2546f1af9048
5
+ SHA512:
6
+ metadata.gz: 7372e441dbdf4f86b2eddc7a49bb985d260c008b23cf108ed1b3bdd07af32288c545eb132b6f49bfc7c93193bb20f1e3c5bd3ea2ba75a5abc1e326262e1097bc
7
+ data.tar.gz: a8a54a041606803fbe4f5f685ec9ef8c5ce3a023660376242ec06938003e6d42741867475138e68444ab0c0b14dec9fd8a709254397308be59b6363be745028f
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ cache: bundler
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 kishikawa katsumi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # XCJobs [![Build Status](https://travis-ci.org/kishikawakatsumi/xcjobs.svg?branch=master)](https://travis-ci.org/kishikawakatsumi/xcjobs) [![Coverage Status](https://img.shields.io/coveralls/kishikawakatsumi/xcjobs.svg)](https://coveralls.io/r/kishikawakatsumi/xcjobs?branch=master)
2
+
3
+ Support the automation of release process of iOS/OSX apps with CI
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'xcjobs'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install xcjobs
20
+
21
+ ## Usage
22
+
23
+ ### Test application
24
+
25
+ ```ruby
26
+ XCJobs::Test.new do |t|
27
+ t.workspace = "Example.xcworkspace"
28
+ t.scheme = "Example"
29
+ t.configuration = "Release"
30
+ t.add_destination("name=iPad 2,OS=7.1")
31
+ t.add_destination("name=iPad Air,OS=8.1")
32
+ t.formatter = "xcpretty -c"
33
+ end
34
+ ```
35
+
36
+ ```shell
37
+ $ rake -T
38
+
39
+ rake test # test application
40
+ ```
41
+
42
+ ```shell
43
+ $ rake test
44
+
45
+ xcodebuild test -workspace Example.xcworkspace -scheme Example -sdk iphonesimulator -configuration Release -destination name=iPad 2,OS=7.1 -destination -destination name=iPad Air,OS=8.1 CODE_SIGN_IDENTITY="" GCC_SYMBOLS_PRIVATE_EXTERN=NO
46
+ ```
47
+
48
+ ### Build application
49
+
50
+ ```ruby
51
+ XCJobs::Build.new do |t|
52
+ t.workspace = "Example.xcworkspace"
53
+ t.scheme = "Example"
54
+ t.configuration = "Release"
55
+ t.signing_identity = "iPhone Distribution: kishikawa katsumi"
56
+ t.build_dir = "build"
57
+ t.formatter = "xcpretty -c"
58
+ end
59
+ ```
60
+
61
+ ```shell
62
+ $ rake -T
63
+
64
+ rake build # build application
65
+ ```
66
+
67
+ ```shell
68
+ $ rake build
69
+
70
+ xcodebuild build -workspace Example.xcworkspace -scheme Example -configuration Release -derivedDataPath build CONFIGURATION_TEMP_DIR=build/temp CODE_SIGN_IDENTITY=iPhone Distribution: kishikawa katsumi
71
+ ```
72
+
73
+ ### Export IPA from xcarchive
74
+
75
+ ```ruby
76
+ XCJobs::Archive.new do |t|
77
+ t.workspace = "Example.xcworkspace"
78
+ t.scheme = "Example"
79
+ t.configuration = "Release"
80
+ t.signing_identity = "iPhone Distribution: kishikawa katsumi"
81
+ t.build_dir = "build"
82
+ t.formatter = "xcpretty -c"
83
+ end
84
+
85
+ XCJobs::Export.new do |t|
86
+ t.archivePath = File.join("build", "Example.xcarchive")
87
+ t.exportPath = File.join("build", "Example.ipa")
88
+ t.exportProvisioningProfile = "Ad_Hoc.mobileprovision"
89
+ t.formatter = "xcpretty -c"
90
+ end
91
+ ```
92
+
93
+ ```shell
94
+ $ rake -T
95
+
96
+ rake build:archive # make xcarchive
97
+ rake build:export # export from an archive
98
+ ```
99
+
100
+ ```shell
101
+ $ bundle exec rake build:archive
102
+
103
+ xcodebuild archive -workspace Example.xcworkspace -scheme Example -configuration Release -archivePath build/Example -derivedDataPath build CONFIGURATION_TEMP_DIR=build/temp CODE_SIGN_IDENTITY=iPhone Distribution: kishikawa katsumi
104
+ ```
105
+
106
+ ```shell
107
+ $ bundle exec rake build:export
108
+
109
+ xcodebuild -exportArchive -exportFormat IPA -archivePath build/Example.xcarchive -exportPath build/Example.ipa -exportProvisioningProfile Ad Hoc
110
+ ```
111
+
112
+ ### Distribute (Upload to Testfligh/Crittercism)
113
+
114
+ ```ruby
115
+ XCJobs::Distribute::Crittercism.new do |t|
116
+ t.app_id = "xxx..."
117
+ t.key = "xxx..."
118
+ t.dsym = File.join("build", "dSYMs.zip")
119
+ end
120
+
121
+ XCJobs::Distribute::TestFlight.new do |t|
122
+ t.file = File.join("build", "#{Example}.ipa")
123
+ t.api_token = "xxx..."
124
+ t.team_token = "xxx..."
125
+ t.notify = true
126
+ t.replace = true
127
+ t.distribution_lists = "Dev"
128
+ t.notes = "Uploaded: #{DateTime.now.strftime("%Y/%m/%d %H:%M:%S")}"
129
+ end
130
+ ```
131
+
132
+ ```shell
133
+ $ rake -T
134
+
135
+ rake distribute:crittercism # upload dSYMs to Crittercism
136
+ rake distribute:testflight # upload IPA to TestFlight
137
+ ```
138
+
139
+ ### Install/Remove certificates (For Travis CI)
140
+
141
+ ```ruby
142
+ XCJobs::Certificate.new do |t|
143
+ passphrase = "password1234"
144
+
145
+ t.add_certificate('./certificates/apple.cer')
146
+ t.add_certificate('./certificates/appstore.cer')
147
+ t.add_certificate('./certificates/appstore.p12', passphrase)
148
+ t.add_certificate('./certificates/adhoc.cer')
149
+ t.add_certificate('./certificates/adhoc.p12', passphrase)
150
+
151
+ t.add_profile("AppStore")
152
+ t.add_profile("Ad Hoc")
153
+ end
154
+ ```
155
+
156
+ ```shell
157
+ $ rake -T
158
+
159
+ rake profiles:install # install provisioning profiles
160
+
161
+ rake certificates:install # install certificates
162
+ rake certificates:remove # remove certificates
163
+ ```
164
+
165
+ ### Bumping version
166
+
167
+ ```ruby
168
+ XCJobs::InfoPlist::Version.new do |t|
169
+ t.path = File.join("Example", "Info.plist")
170
+ end
171
+ ```
172
+
173
+ ```shell
174
+ $ rake -T
175
+
176
+ rake version # Print the current version
177
+ rake version:bump:major # Bump major version (X.0.0)
178
+ rake version:bump:minor # Bump minor version (0.X.0)
179
+ rake version:bump:patch # Bump patch version (0.0.X)
180
+ rake version:current # Print the current version
181
+ rake version:set_build_number # Sets build version to number of commits
182
+ rake version:set_build_version # Sets build version to last git commit hash
183
+ ```
184
+
185
+ ## Automate with Travis CI
186
+
187
+ ```ruby
188
+ # Gemfile
189
+ source 'https://rubygems.org'
190
+
191
+ gem 'rake'
192
+ gem 'cocoapods'
193
+ gem 'xcpretty'
194
+ gem 'xcjobs', :github => 'kishikawakatsumi/xcjobs'
195
+ ```
196
+
197
+ ```yaml
198
+ # .travis.yml
199
+ language: objective-c
200
+ osx_image: xcode61
201
+ cache:
202
+ directories:
203
+ - vendor/bundle
204
+ - Pods
205
+ install:
206
+ - bundle install --path=vendor/bundle --binstubs=vendor/bin
207
+ - bundle exec pod install
208
+ script:
209
+ - bundle exec rake ${ACTION}
210
+ env:
211
+ global:
212
+ - LANG=en_US.UTF-8
213
+ - LC_ALL=en_US.UTF-8
214
+ matrix:
215
+ - ACTION=test
216
+ - ACTION="profiles:install certificates:install version:set_build_version build:archive build:export distribute:crittercism distribute:testflight certificates:remove"
217
+ ```
218
+
219
+ ## Contributing
220
+
221
+ 1. Fork it ( https://github.com/[my-github-username]/xcjobs/fork )
222
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
223
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
224
+ 4. Push to the branch (`git push origin my-new-feature`)
225
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,65 @@
1
+ require 'rake/tasklib'
2
+
3
+ module XCJobs
4
+ class Certificate < Rake::TaskLib
5
+ include Rake::DSL if defined?(Rake::DSL)
6
+
7
+ attr_accessor :keychain_name
8
+
9
+ def initialize()
10
+ @certificates = {}
11
+ @profiles = []
12
+ yield self if block_given?
13
+ define
14
+ end
15
+
16
+ def keychain_name
17
+ @keychain_name || 'build.keychain'
18
+ end
19
+
20
+ def profile_dir
21
+ @profile_dir || '$HOME/Library/MobileDevice/Provisioning Profiles'
22
+ end
23
+
24
+ def add_certificate(certificate, passphrase='')
25
+ @certificates[certificate] = passphrase
26
+ end
27
+
28
+ def add_profile(profile)
29
+ @profiles << profile
30
+ end
31
+
32
+ private
33
+
34
+ def define
35
+ namespace :certificates do
36
+ desc 'install certificates'
37
+ task :install do
38
+ sh %[security create-keychain -p "" "#{keychain_name}"]
39
+
40
+ @certificates.each do |certificate, passphrase|
41
+ sh %[security import "#{certificate}" -k "#{keychain_name}" -P "#{passphrase}" -T /usr/bin/codesign]
42
+ end
43
+
44
+ sh %[security default-keychain -s "#{keychain_name}"]
45
+ end
46
+
47
+ desc 'remove certificates'
48
+ task :remove do
49
+ sh %[security delete-keychain #{keychain_name}]
50
+ end
51
+ end
52
+
53
+ namespace :profiles do
54
+ desc 'install provisioning profiles'
55
+ task :install do
56
+ sh %[mkdir -p "#{profile_dir}"]
57
+
58
+ @profiles.each do |profile|
59
+ sh %[cp "#{profile}" "#{profile_dir}"]
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,108 @@
1
+ require 'rake/tasklib'
2
+
3
+ module XCJobs
4
+ module Distribute
5
+ def upload(url, form_data = {})
6
+ @before_action.call if @before_action
7
+
8
+ curl_options = ['curl', '-sSf', "#{url}"]
9
+ form_fields = form_data.flat_map { |k, v| ['-F', "#{k}=#{v}"] }
10
+ puts (curl_options + form_fields).join(' ')
11
+ Open3.popen2e(*(curl_options + form_fields)) do |stdin, stdout_err, wait_thr|
12
+ output = ''
13
+ while line = stdout_err.gets
14
+ puts line
15
+ output << line
16
+ end
17
+
18
+ status = wait_thr.value
19
+ if status.success?
20
+ @after_action.call(output, status) if @after_action
21
+ else
22
+ fail "upload failed (exited with status: #{status.exitstatus})"
23
+ end
24
+ end
25
+ end
26
+
27
+ def before_action(&block)
28
+ @before_action = block
29
+ end
30
+
31
+ def after_action(&block)
32
+ @after_action = block
33
+ end
34
+
35
+ class TestFlight < Rake::TaskLib
36
+ include Rake::DSL if defined?(Rake::DSL)
37
+ include Distribute
38
+
39
+ attr_accessor :file
40
+ attr_accessor :api_token
41
+ attr_accessor :team_token
42
+ attr_accessor :notify
43
+ attr_accessor :replace
44
+ attr_accessor :distribution_lists
45
+ attr_accessor :notes
46
+
47
+ def initialize()
48
+ yield self if block_given?
49
+ define
50
+ end
51
+
52
+ private
53
+
54
+ def define
55
+ namespace :distribute do
56
+ desc 'upload IPA to TestFlight'
57
+ task :testflight do
58
+ upload('http://testflightapp.com/api/builds.json', form_data)
59
+ end
60
+ end
61
+ end
62
+
63
+ def form_data
64
+ {}.tap do |fields|
65
+ fields[:file] = "@#{file}" if file
66
+ fields[:api_token] = api_token if api_token
67
+ fields[:team_token] = team_token if team_token
68
+ fields[:notify] = notify if notify
69
+ fields[:replace] = replace if replace
70
+ fields[:distribution_lists] = distribution_lists if distribution_lists
71
+ fields[:notes] = notes if notes
72
+ end
73
+ end
74
+ end
75
+
76
+ class Crittercism < Rake::TaskLib
77
+ include Rake::DSL if defined?(Rake::DSL)
78
+ include Distribute
79
+
80
+ attr_accessor :app_id
81
+ attr_accessor :dsym
82
+ attr_accessor :key
83
+
84
+ def initialize(name=:export)
85
+ yield self if block_given?
86
+ define
87
+ end
88
+
89
+ private
90
+
91
+ def define
92
+ namespace :distribute do
93
+ desc 'upload dSYMs to Crittercism'
94
+ task :crittercism do
95
+ upload("https://api.crittercism.com/api_beta/dsym/#{app_id}", form_data)
96
+ end
97
+ end
98
+ end
99
+
100
+ def form_data
101
+ {}.tap do |fields|
102
+ fields[:dsym] = "@#{dsym}" if dsym
103
+ fields[:key] = key if key
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,114 @@
1
+ require "rake/tasklib"
2
+
3
+ module XCJobs
4
+ module InfoPlist
5
+ extend self
6
+ attr_accessor :path
7
+
8
+ def [](key)
9
+ output = %x[/usr/libexec/PlistBuddy -c "Print #{key}" #{path}].strip
10
+ raise "The key `#{key}' does not exist in `#{path}'." if output.include?('Does Not Exist')
11
+ output
12
+ end
13
+
14
+ def set(key, value, file = "#{path}")
15
+ %x[/usr/libexec/PlistBuddy -c 'Set :#{key} "#{value}"' '#{file}'].strip
16
+ end
17
+
18
+ def []=(key, value)
19
+ set(key, value)
20
+ end
21
+
22
+ def build_version
23
+ self['CFBundleVersion']
24
+ end
25
+
26
+ def build_version=(revision)
27
+ self['CFBundleVersion'] = revision
28
+ end
29
+
30
+ def marketing_version
31
+ self['CFBundleShortVersionString']
32
+ end
33
+
34
+ def marketing_version=(version)
35
+ self['CFBundleShortVersionString'] = version
36
+ end
37
+
38
+ def bump_marketing_version_segment(segment_index)
39
+ segments = Gem::Version.new(marketing_version).segments
40
+ segments[segment_index] = segments[segment_index].to_i + 1
41
+ (segment_index+1..segments.size - 1).each { |i| segments[i] = 0 }
42
+ version = segments.map(&:to_i).join('.')
43
+
44
+ puts "Setting marketing version to: #{version}"
45
+ self.marketing_version = version
46
+ end
47
+
48
+ def marketing_and_build_version
49
+ "#{marketing_version} (#{build_version})"
50
+ end
51
+ end
52
+
53
+ module InfoPlist
54
+ class Version < Rake::TaskLib
55
+ include Rake::DSL if defined?(Rake::DSL)
56
+
57
+ def initialize()
58
+ yield self if block_given?
59
+ define
60
+ end
61
+
62
+ def path
63
+ InfoPlist.path
64
+ end
65
+
66
+ def path=(path)
67
+ InfoPlist.path = path
68
+ end
69
+
70
+ def define
71
+ namespace :version do
72
+ desc "Print the current version"
73
+ task :current do
74
+ puts InfoPlist.marketing_and_build_version
75
+ end
76
+
77
+ desc "Sets build version to last git commit hash"
78
+ task :set_build_version do
79
+ rev = `git rev-parse --short HEAD`.strip
80
+ puts "Setting build version to: #{rev}"
81
+ InfoPlist.build_version = rev
82
+ end
83
+
84
+ desc "Sets build version to number of commits"
85
+ task :set_build_number do
86
+ rev = `git rev-list --count HEAD`.strip
87
+ puts "Setting build version to: #{rev}"
88
+ InfoPlist.build_version = rev
89
+ end
90
+
91
+ namespace :bump do
92
+ desc "Bump patch version (0.0.X)"
93
+ task :patch do
94
+ InfoPlist.bump_marketing_version_segment(2)
95
+ end
96
+
97
+ desc "Bump minor version (0.X.0)"
98
+ task :minor do
99
+ InfoPlist.bump_marketing_version_segment(1)
100
+ end
101
+
102
+ desc "Bump major version (X.0.0)"
103
+ task :major do
104
+ InfoPlist.bump_marketing_version_segment(0)
105
+ end
106
+ end
107
+ end
108
+
109
+ desc "Print the current version"
110
+ task :version => "version:current"
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ module XCJobs
2
+ VERSION = "0.0.1"
3
+ end