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