thrust 0.0.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/thrust +24 -18
- data/lib/config/example.yml +32 -25
- data/lib/tasks/cedar.rake +52 -25
- data/lib/tasks/testflight.rake +29 -52
- data/lib/thrust/android/deploy.rb +33 -0
- data/lib/thrust/android/tools.rb +33 -0
- data/lib/thrust/config.rb +39 -0
- data/lib/thrust/executor.rb +27 -0
- data/lib/thrust/git.rb +66 -0
- data/lib/thrust/ios/cedar.rb +21 -0
- data/lib/thrust/ios/deploy.rb +36 -0
- data/lib/thrust/ios/x_code_tools.rb +117 -0
- data/lib/thrust/tasks.rb +3 -0
- data/lib/thrust/testflight.rb +36 -0
- data/lib/thrust/user_prompt.rb +12 -0
- data/lib/thrust.rb +16 -0
- metadata +120 -17
- data/lib/thrust_config.rb +0 -129
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b6b885837e6c3dac116eca861cc6e772243c2d54
|
4
|
+
data.tar.gz: 625f5556939cdb39a575c648c9b85de3b4825a92
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f249ea7b0d1b30871c64de088427dccaf5a05fe66f1feb8707855c808374971b273b085ca1646b062e0358cac21866c9f9abc911425430c4c9ec2931a04f91d
|
7
|
+
data.tar.gz: 254b3ca985e6b4cee3c4d866d60e30d48fd9fe8f9fa3fdde3b665ad5b17a70eee309ec229c991ea0b64043444de9e0fcf9e1716e3079db67c84d225ae3554443
|
data/bin/thrust
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'colorize'
|
4
|
+
|
5
|
+
command = ARGV.first
|
6
|
+
|
7
|
+
if command != 'install'
|
8
|
+
puts ''
|
9
|
+
puts ' USAGE: '.yellow + 'thrust install'
|
10
|
+
exit 0
|
11
|
+
end
|
12
|
+
|
13
|
+
|
2
14
|
require 'fileutils'
|
3
15
|
project_root = Dir.pwd
|
4
16
|
thrust_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
5
17
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
Dir.mkdir(File.join(project_root, 'thrust', 'lib', 'tasks'))
|
18
|
+
rakefile = File.join(project_root, 'Rakefile')
|
19
|
+
File.open(rakefile, 'a') do |f|
|
20
|
+
f.puts "require 'thrust/tasks'"
|
10
21
|
end
|
11
22
|
|
12
|
-
FileUtils.cp(Dir.glob(File.join(thrust_root, 'lib', 'tasks', '*.rake')), File.join(project_root, 'thrust', 'lib', 'tasks'))
|
13
|
-
FileUtils.cp(File.join(thrust_root, 'lib', 'thrust_config.rb'), File.join(project_root, 'thrust', 'lib'))
|
14
23
|
FileUtils.cp(File.join(thrust_root, 'lib', 'config', 'example.yml'), File.join(project_root, 'thrust.example.yml'))
|
24
|
+
puts ''
|
25
|
+
puts " To finish installation of " + "Thrust".green + ", rename:\n\n"
|
26
|
+
puts " #{project_root}/thrust.example.yml\n".blue
|
27
|
+
puts " to:\n\n"
|
28
|
+
puts " #{project_root}/thrust.yml\n".blue
|
29
|
+
puts " and edit it for your project."
|
15
30
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
puts "Dir.glob('thrust/lib/tasks/*.rake').each { |r| import r }"
|
20
|
-
else
|
21
|
-
File.open(rakefile, 'w') do |f|
|
22
|
-
f.puts "Dir.glob('thrust/lib/tasks/*.rake').each { |r| import r }"
|
23
|
-
end
|
24
|
-
puts "a Rakefile was created for you type 'rake -T' to see a list of tasks"
|
25
|
-
end
|
26
|
-
|
27
|
-
puts "Rename #{project_root}/thrust.example.yml to #{project_root}/thrust.yml and edit it for your project."
|
31
|
+
puts ''
|
32
|
+
puts " Thrust rake tasks were generated in your Rakefile."
|
33
|
+
puts " Type 'rake -T' to see the list of tasks after you have created your thrust.yml configuration.".green
|
data/lib/config/example.yml
CHANGED
@@ -1,29 +1,36 @@
|
|
1
|
-
|
1
|
+
thrust_version: 0.2
|
2
2
|
project_name: My Great Project
|
3
|
-
app_name: My Great
|
4
|
-
|
3
|
+
app_name: My Great App
|
4
|
+
ios_distribution_certificate: 'Name of Distribution Signing Certificate'
|
5
|
+
ios_sim_binary: 'ios-sim' # or wax-sim. iOS only.
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
sdk: 6.1 # SDK version to build/run the specs with
|
10
|
-
binary: 'Specs/bin/ios-sim' # or 'Specs/bin/waxim'
|
7
|
+
testflight:
|
8
|
+
api_token: 'testflight api token'
|
9
|
+
team_token: 'testflight team token'
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
token: TestFlight Team Token
|
21
|
-
default_list: Default Distribution List to Permission for the build
|
22
|
-
configuration: iOS configuration to build with e.g. Debug, Release, etc.
|
11
|
+
deployment_targets:
|
12
|
+
staging:
|
13
|
+
distribution_list: Developers
|
14
|
+
notify: true
|
15
|
+
note_generation_method: autotag # If you set this value, it will auto-generate the deploy notes from the commit history. Optional.
|
16
|
+
ios_target: MyGreatAppTarget # Name of the build target. Optional, defaults to app name. iOS only.
|
17
|
+
ios_build_configuration: Release # iOS only
|
18
|
+
ios_provisioning_search_query: 'query to find Provisioning Profile' # iOS only. Optional.
|
23
19
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
demo:
|
21
|
+
distribution_list: Beta Testers
|
22
|
+
notify: true
|
23
|
+
|
24
|
+
ios_spec_targets:
|
25
|
+
specs:
|
26
|
+
target: UISpecs # name of the build target
|
27
|
+
build_configuration: Debug # name of the build configuration
|
28
|
+
build_sdk: 6.1 # SDK used to build the target. Optional, defaults to iphonesimulator.
|
29
|
+
runtime_sdk: 7.0 # SDK used to run the target. Not optional.
|
30
|
+
device: ipad # Device to run the specs on. Optional, defaults to iPhone.
|
31
|
+
|
32
|
+
integration:
|
33
|
+
target: IntegrationSpecs
|
34
|
+
build_configuration: Release
|
35
|
+
build_sdk: macosx
|
36
|
+
runtime_sdk: macosx
|
data/lib/tasks/cedar.rake
CHANGED
@@ -1,39 +1,66 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'tmpdir'
|
3
|
+
require File.expand_path('../../thrust', __FILE__)
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
@thrust = ThrustConfig.new(Dir.getwd, File.join(Dir.getwd, 'thrust.yml'))
|
7
|
-
|
8
|
-
task :default => [:trim, :specs]
|
5
|
+
@thrust = Thrust::Config.make(Dir.getwd, File.join(Dir.getwd, 'thrust.yml'))
|
9
6
|
|
10
7
|
desc 'Trim whitespace'
|
11
8
|
task :trim do
|
12
|
-
|
9
|
+
awk_statement = <<-AWK
|
10
|
+
{
|
11
|
+
if ($1 == "RM" || $1 == "R")
|
12
|
+
print $4;
|
13
|
+
else if ($1 != "D")
|
14
|
+
print $2;
|
15
|
+
}
|
16
|
+
AWK
|
17
|
+
awk_statement.gsub!(%r{\s+}, " ")
|
18
|
+
|
19
|
+
Thrust::Executor.system_or_exit %Q[git status --porcelain | awk '#{awk_statement}' | grep -e '.*\.[cmh]$' | xargs sed -i '' -e 's/ / /g;s/ *$//g;']
|
13
20
|
end
|
14
21
|
|
15
|
-
desc
|
16
|
-
task :
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
desc "Remove any focus from specs"
|
23
|
+
task :nof do
|
24
|
+
substitutions = focused_methods.map do |method|
|
25
|
+
unfocused_method = method.sub(/^f/, '')
|
26
|
+
"-e 's/#{method}/#{unfocused_method}/g;'"
|
27
|
+
end
|
28
|
+
|
29
|
+
Thrust::Executor.system_or_exit %Q[ rake focused_specs | xargs -I filename sed -i '' #{substitutions.join(' ')} "filename" ]
|
20
30
|
end
|
21
31
|
|
22
|
-
desc
|
23
|
-
task :
|
24
|
-
|
25
|
-
|
32
|
+
desc "Print out names of files containing focused specs"
|
33
|
+
task :focused_specs do
|
34
|
+
pattern = focused_methods.join("\\|")
|
35
|
+
directories = @thrust.app_config['ios_spec_targets'].values.map {|h| h['target']}.join(' ')
|
36
|
+
Thrust::Executor.system_or_exit %Q[ grep -l -r -e "\\(#{pattern}\\)" #{directories} | grep -v 'Frameworks' ; exit 0 ]
|
26
37
|
end
|
27
38
|
|
28
|
-
desc '
|
29
|
-
task :
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
elsif binary =~ /ios-sim$/
|
34
|
-
@thrust.grep_cmd_for_failure(%Q[#{binary} launch #{File.join(@thrust.sim_dir, "#{@thrust.spec_config['target']}.app")} --sdk #{@thrust.spec_config['sdk']} --family iphone --retina --tall --setenv CFFIXED_USER_HOME=#{Dir.tmpdir} --setenv CEDAR_HEADLESS_SPECS=1 --setenv CEDAR_REPORTER_CLASS=CDRDefaultReporter])
|
35
|
-
else
|
36
|
-
puts "Uknown binary for running specs: '#{binary}'"
|
37
|
-
exit(1)
|
39
|
+
desc 'Clean all targets'
|
40
|
+
task :clean_build do
|
41
|
+
Thrust::IOS::XCodeTools.build_configurations(@thrust.app_config['project_name']).each do |config|
|
42
|
+
xcode_tools = Thrust::IOS::XCodeTools.new($stdout, config, @thrust.build_dir, @thrust.app_config['project_name'])
|
43
|
+
xcode_tools.clean_build
|
38
44
|
end
|
39
45
|
end
|
46
|
+
|
47
|
+
(@thrust.app_config['ios_spec_targets'] || []).each do |task_name, target_info|
|
48
|
+
desc "Run the #{target_info['target']} target"
|
49
|
+
task task_name do
|
50
|
+
build_configuration = target_info['build_configuration']
|
51
|
+
target = target_info['target']
|
52
|
+
build_sdk = target_info['build_sdk'] || 'iphonesimulator' #build sdk - version you compile the code with
|
53
|
+
runtime_sdk = target_info['runtime_sdk'] #runtime sdk
|
54
|
+
|
55
|
+
xcode_tools = Thrust::IOS::XCodeTools.new($stdout, build_configuration, @thrust.build_dir, @thrust.app_config['project_name'])
|
56
|
+
xcode_tools.clean_and_build_target(target, build_sdk)
|
57
|
+
|
58
|
+
cedar_success = Thrust::IOS::Cedar.run($stdout, build_configuration, target, runtime_sdk, build_sdk, target_info['device'], @thrust.build_dir, @thrust.app_config['ios_sim_binary'])
|
59
|
+
|
60
|
+
exit(cedar_success ? 0 : 1)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def focused_methods
|
65
|
+
["fit", "fcontext", "fdescribe"].map { |method| "#{method}(@" }
|
66
|
+
end
|
data/lib/tasks/testflight.rake
CHANGED
@@ -1,63 +1,40 @@
|
|
1
1
|
require 'yaml'
|
2
|
-
require
|
2
|
+
require 'tempfile'
|
3
|
+
require File.expand_path('../../thrust', __FILE__)
|
3
4
|
|
4
|
-
@thrust =
|
5
|
+
@thrust = Thrust::Config.make(Dir.getwd, File.join(Dir.getwd, 'thrust.yml'))
|
5
6
|
|
6
|
-
namespace :
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
desc 'Bumps the patch marketing version in (major.minor.patch)'
|
26
|
-
task :patch do
|
27
|
-
@thrust.update_version(:patch)
|
7
|
+
namespace :testflight do
|
8
|
+
android_project = File.exists?('AndroidManifest.xml')
|
9
|
+
|
10
|
+
@thrust.app_config['deployment_targets'].each do |task_name, deployment_config|
|
11
|
+
if android_project
|
12
|
+
desc "Deploy Android build to #{task_name} (use NOTIFY=false to prevent team notification)"
|
13
|
+
task task_name do |_, _|
|
14
|
+
Thrust::Android::Deploy.make(@thrust, deployment_config, task_name).run
|
15
|
+
|
16
|
+
Rake::Task['autotag:create'].invoke(task_name)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
desc "Deploy iOS build to #{task_name} (use NOTIFY=false to prevent team notification)"
|
20
|
+
task task_name do |_, _|
|
21
|
+
Thrust::IOS::Deploy.make(@thrust, deployment_config, task_name).run
|
22
|
+
|
23
|
+
Rake::Task['autotag:create'].invoke(task_name)
|
24
|
+
end
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
32
|
-
|
33
|
-
task :
|
34
|
-
|
35
|
-
@thrust.system_or_exit "xcodebuild -project #{@thrust.config['project_name']}.xcodeproj -alltargets -configuration '#{args[:configuration]}' -sdk iphoneos clean", @thrust.output_file("clean")
|
36
|
-
@thrust.kill_simulator
|
37
|
-
@thrust.system_or_exit "xcodebuild -project #{@thrust.config['project_name']}.xcodeproj -target #{@thrust.config['app_name']} -configuration '#{args[:configuration]}' -sdk iphoneos build", @thrust.output_file(args[:configuration])
|
38
|
-
@thrust.system_or_exit "/usr/bin/xcrun -sdk iphoneos PackageApplication -v '#{build_prefix}.app' -o '#{build_prefix}.ipa' --sign '#{@thrust.config['identity']}'"
|
39
|
-
@thrust.system_or_exit "zip -r -T -y '#{build_prefix}.app.dSYM.zip' '#{build_prefix}.app.dSYM'"
|
40
|
-
end
|
41
|
-
|
42
|
-
namespace :testflight do
|
43
|
-
@thrust.config['distributions'].each do |task_name, info|
|
44
|
-
desc "Deploy build to testflight #{info['team']} team (use NOTIFY=false to prevent team notification)"
|
45
|
-
task task_name do
|
46
|
-
Rake::Task["testflight:deploy"].invoke(info['token'], info['default_list'], info['configuration'])
|
47
|
-
end
|
29
|
+
namespace :autotag do
|
30
|
+
task :create, :stage do |_, args|
|
31
|
+
`autotag create #{args[:stage]}`
|
48
32
|
end
|
49
33
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
-F file=@#{build_prefix}.ipa\
|
56
|
-
-F dsym=@#{build_prefix}.app.dSYM.zip\
|
57
|
-
-F api_token='#{@thrust.config['api_token']}'\
|
58
|
-
-F team_token='#{args[:team]}'\
|
59
|
-
-F notes='This build was uploaded via the upload API'\
|
60
|
-
-F notify=#{(ENV['NOTIFY'] || 'true').downcase.capitalize}\
|
61
|
-
#{"-F distribution_lists='#{args[:distribution_list]}'" if args[:distribution_list]}"
|
34
|
+
desc 'Show the commit that is currently deployed to each environment'
|
35
|
+
task :list do
|
36
|
+
@thrust.app_config['deployment_targets'].each do |deployment_target, _|
|
37
|
+
puts Thrust::Git.new($stdout).commit_summary_for_last_deploy(deployment_target)
|
38
|
+
end
|
62
39
|
end
|
63
40
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Thrust::Android::Deploy
|
2
|
+
def self.make(thrust_config, deployment_config, deployment_target)
|
3
|
+
tools = Thrust::Android::Tools.new($stdout)
|
4
|
+
git = Thrust::Git.new($stdout)
|
5
|
+
|
6
|
+
testflight_config = thrust_config.app_config['testflight']
|
7
|
+
testflight = Thrust::Testflight.new($stdout, $stdin, testflight_config['api_token'], testflight_config['team_token'])
|
8
|
+
|
9
|
+
autogenerate_notes = deployment_config['note_generation_method'] == 'autotag'
|
10
|
+
new($stdout, tools, git, testflight, deployment_config['notify'], deployment_config['distribution_list'], autogenerate_notes, deployment_target)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(out, tools, git, testflight, notify, distribution_list, autogenerate_notes, deployment_target)
|
14
|
+
@out = out
|
15
|
+
@tools = tools
|
16
|
+
@git = git
|
17
|
+
@testflight = testflight
|
18
|
+
@notify = notify
|
19
|
+
@distribution_list = distribution_list
|
20
|
+
@deployment_target = deployment_target
|
21
|
+
@autogenerate_notes = autogenerate_notes
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
@git.ensure_clean
|
26
|
+
@tools.change_build_number(Time.now.utc.strftime('%y%m%d%H%M'), @git.current_commit)
|
27
|
+
apk_path = @tools.build_signed_release
|
28
|
+
|
29
|
+
@testflight.upload(apk_path, @notify, @distribution_list, @autogenerate_notes, @deployment_target)
|
30
|
+
|
31
|
+
@git.reset
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class Thrust::Android::Tools
|
4
|
+
def initialize(out)
|
5
|
+
@out = out
|
6
|
+
|
7
|
+
if ENV['ANDROID_HOME'].nil?
|
8
|
+
if File.directory?('/usr/local/opt/android-sdk')
|
9
|
+
@out.puts 'Setting /usr/local/opt/android-sdk as ANDROID_HOME...'.magenta
|
10
|
+
ENV['ANDROID_HOME'] = '/usr/local/opt/android-sdk'
|
11
|
+
else
|
12
|
+
raise('**********Android is not installed. Run `brew install android`.**********')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def change_build_number(version_code, version_name)
|
18
|
+
Thrust::Executor.system_or_exit(
|
19
|
+
"sed -i ''" +
|
20
|
+
" -e 's/android:versionCode=\"[0-9]*\"/android:versionCode=\"#{version_code}\"/'" +
|
21
|
+
" -e 's/android:versionName=\"\\([^ \"]*\\)[^\"]*\"/android:versionName=\"\\1 (#{version_name})\"/'" +
|
22
|
+
" AndroidManifest.xml")
|
23
|
+
Thrust::Executor.system_or_exit(
|
24
|
+
"sed -i ''" +
|
25
|
+
" '1,/<version>/s/<version>\\([^- <]*\\)[^<]*<\\/version>/<version>\\1 (#{version_name})<\\/version>/'" +
|
26
|
+
" pom.xml")
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_signed_release
|
30
|
+
Thrust::Executor.system_or_exit('mvn clean package -Prelease')
|
31
|
+
Dir.glob('target/*-signed-aligned.apk').first or raise 'Signed APK was not generated'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class Thrust::Config
|
4
|
+
attr_reader :project_root, :app_config, :build_dir
|
5
|
+
THRUST_VERSION = 0.2
|
6
|
+
THRUST_ROOT = File.expand_path('../..', __FILE__)
|
7
|
+
|
8
|
+
def self.make(relative_project_root, config_file)
|
9
|
+
begin
|
10
|
+
config_file_contents = YAML.load_file(config_file)
|
11
|
+
rescue Errno::ENOENT
|
12
|
+
puts ""
|
13
|
+
puts " Missing thrust.yml. Create by running:\n".red
|
14
|
+
puts " cp thrust.example.yml thrust.yml".blue
|
15
|
+
exit 1
|
16
|
+
rescue Psych::SyntaxError
|
17
|
+
puts ""
|
18
|
+
puts " Malformed thrust.yml.".red
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
new(relative_project_root, config_file_contents)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(relative_project_root, config)
|
25
|
+
@project_root = File.expand_path(relative_project_root)
|
26
|
+
@build_dir = File.join(project_root, 'build')
|
27
|
+
@app_config = config
|
28
|
+
verify_configuration(@app_config)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def verify_configuration(config)
|
34
|
+
config['thrust_version'] ||= 0
|
35
|
+
if config['thrust_version'] != THRUST_VERSION
|
36
|
+
fail "Invalid configuration. Have you updated thrust recently? Your thrust.yml specifies version #{config['thrust_version']}, but thrust is at version #{THRUST_VERSION}. See README for details.".red
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Thrust::Executor
|
2
|
+
def self.system_or_exit(cmd, output_file = nil)
|
3
|
+
self.system(cmd, output_file) or raise '******** Build failed ********'
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.system(cmd, output_file = nil)
|
7
|
+
STDERR.puts "Executing #{cmd}"
|
8
|
+
cmd += " > #{output_file}" if output_file
|
9
|
+
Kernel::system(cmd)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.capture_output_from_system(cmd)
|
13
|
+
captured_output = `#{cmd}`
|
14
|
+
raise '******** Build failed ********' if $?.exitstatus > 0
|
15
|
+
|
16
|
+
captured_output
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.check_command_for_failure(cmd)
|
20
|
+
STDERR.puts "Executing #{cmd} and checking for FAILURE"
|
21
|
+
result = %x[#{cmd} 2>&1]
|
22
|
+
STDERR.puts "Results:"
|
23
|
+
STDERR.puts result
|
24
|
+
|
25
|
+
result.include?("Finished") && !result.include?("FAILURE") && !result.include?("EXCEPTION")
|
26
|
+
end
|
27
|
+
end
|
data/lib/thrust/git.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
class Thrust::Git
|
4
|
+
def initialize(out)
|
5
|
+
@out = out
|
6
|
+
end
|
7
|
+
|
8
|
+
def ensure_clean
|
9
|
+
if ENV['IGNORE_GIT']
|
10
|
+
@out.puts 'WARNING NOT CHECKING FOR CLEAN WORKING DIRECTORY'.red
|
11
|
+
else
|
12
|
+
@out.puts 'Checking for clean working tree...'
|
13
|
+
Thrust::Executor.system_or_exit 'git diff-index --quiet HEAD'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_commit
|
18
|
+
Thrust::Executor.capture_output_from_system('git log --format=format:%h -1').strip
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
Thrust::Executor.system_or_exit('git reset --hard')
|
23
|
+
end
|
24
|
+
|
25
|
+
def checkout_file(filename)
|
26
|
+
Thrust::Executor.system_or_exit("git checkout #{filename}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def commit_summary_for_last_deploy(deployment_target)
|
30
|
+
sha_of_latest_deployed_commit = latest_deployed_commit(deployment_target)
|
31
|
+
if sha_of_latest_deployed_commit
|
32
|
+
"#{deployment_target}:".blue + " #{summary_for_commit(sha_of_latest_deployed_commit)}"
|
33
|
+
else
|
34
|
+
"#{deployment_target}:".blue + ' Never deployed'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate_notes_for_deployment(deployment_target)
|
39
|
+
sha_of_latest_commit = Thrust::Executor.capture_output_from_system('git rev-parse HEAD').strip
|
40
|
+
sha_of_latest_deployed_commit = latest_deployed_commit(deployment_target)
|
41
|
+
|
42
|
+
notes = Tempfile.new('deployment_notes')
|
43
|
+
|
44
|
+
if sha_of_latest_deployed_commit
|
45
|
+
Thrust::Executor.system_or_exit("git log --oneline #{sha_of_latest_deployed_commit}...#{sha_of_latest_commit}", notes.path)
|
46
|
+
else
|
47
|
+
notes.puts(summary_for_commit(sha_of_latest_commit))
|
48
|
+
notes.close
|
49
|
+
end
|
50
|
+
|
51
|
+
notes.path
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def summary_for_commit(sha)
|
57
|
+
Thrust::Executor.capture_output_from_system("git log --oneline -n 1 #{sha}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def latest_deployed_commit(deployment_target)
|
61
|
+
list = Thrust::Executor.capture_output_from_system("autotag list #{deployment_target}")
|
62
|
+
unless list.strip.empty?
|
63
|
+
list.split("\n").last.split(" ").first
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Thrust::IOS::Cedar
|
2
|
+
|
3
|
+
def self.run(out, build_configuration, target, runtime_sdk, build_sdk, device, build_dir, simulator_binary)
|
4
|
+
if build_sdk == 'macosx'
|
5
|
+
build_path = File.join(build_dir, build_configuration)
|
6
|
+
app_dir = File.join(build_path, target)
|
7
|
+
Thrust::Executor.check_command_for_failure("DYLD_FRAMEWORK_PATH=#{build_path.inspect} #{app_dir}")
|
8
|
+
else
|
9
|
+
app_executable = File.join(build_dir, "#{build_configuration}-#{build_sdk}", "#{target}.app")
|
10
|
+
|
11
|
+
if simulator_binary =~ /waxim%/
|
12
|
+
Thrust::Executor.check_command_for_failure(%Q[#{simulator_binary} -s #{runtime_sdk} -f #{device} -e CFFIXED_USER_HOME=#{Dir.tmpdir} -e CEDAR_HEADLESS_SPECS=1 -e CEDAR_REPORTER_CLASS=CDRDefaultReporter #{app_executable}])
|
13
|
+
elsif simulator_binary =~ /ios-sim$/
|
14
|
+
Thrust::Executor.check_command_for_failure(%Q[#{simulator_binary} launch #{app_executable} --sdk #{runtime_sdk} --family #{device} --retina --tall --setenv CFFIXED_USER_HOME=#{Dir.tmpdir} --setenv CEDAR_HEADLESS_SPECS=1 --setenv CEDAR_REPORTER_CLASS=CDRDefaultReporter])
|
15
|
+
else
|
16
|
+
out.puts "Unknown binary for running specs: '#{simulator_binary}'"
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Thrust::IOS::Deploy
|
2
|
+
def self.make(thrust_config, deployment_config, deployment_target)
|
3
|
+
build_configuration = deployment_config['ios_build_configuration']
|
4
|
+
x_code_tools = Thrust::IOS::XCodeTools.new($stdout, build_configuration, thrust_config.build_dir, thrust_config.app_config['project_name'])
|
5
|
+
git = Thrust::Git.new($stdout)
|
6
|
+
testflight_config = thrust_config.app_config['testflight']
|
7
|
+
testflight = Thrust::Testflight.new($stdout, $stdin, testflight_config['api_token'], testflight_config['team_token'])
|
8
|
+
|
9
|
+
new($stdout, x_code_tools, git, testflight, thrust_config, deployment_config, deployment_target)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(out, x_code_tools, git, testflight, thrust_config, deployment_config, deployment_target)
|
13
|
+
@out = out
|
14
|
+
@x_code_tools = x_code_tools
|
15
|
+
@git = git
|
16
|
+
@testflight = testflight
|
17
|
+
@thrust_config = thrust_config
|
18
|
+
@deployment_config = deployment_config
|
19
|
+
@deployment_target = deployment_target
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
@git.ensure_clean
|
24
|
+
@x_code_tools.change_build_number(@git.current_commit)
|
25
|
+
|
26
|
+
app_name = @thrust_config.app_config['app_name']
|
27
|
+
target = @deployment_config['ios_target'] || app_name
|
28
|
+
ipa_file = @x_code_tools.cleanly_create_ipa(target, app_name, @thrust_config.app_config['ios_distribution_certificate'], @deployment_config['ios_provisioning_search_query'])
|
29
|
+
|
30
|
+
dsym_path = "#{@x_code_tools.build_configuration_directory}/#{app_name}.app.dSYM"
|
31
|
+
|
32
|
+
autogenerate_notes = @deployment_config['note_generation_method'] == 'autotag'
|
33
|
+
@testflight.upload(ipa_file, @deployment_config['notify'], @deployment_config['distribution_list'], autogenerate_notes, @deployment_target, dsym_path)
|
34
|
+
@git.reset
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
class Thrust::IOS::XCodeTools
|
2
|
+
ProvisioningProfileNotFound = Class.new(StandardError)
|
3
|
+
|
4
|
+
def self.build_configurations(project_name) #TODO: Backfill a test
|
5
|
+
output = Thrust::Executor.capture_output_from_system("xcodebuild -project #{project_name}.xcodeproj -list")
|
6
|
+
match = /Build Configurations:(.+?)\n\n/m.match(output)
|
7
|
+
if match
|
8
|
+
match[1].strip.split("\n").map { |line| line.strip }
|
9
|
+
else
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(out, build_configuration, build_directory, project_name)
|
15
|
+
@out = out
|
16
|
+
@git = Thrust::Git.new(out)
|
17
|
+
@build_configuration = build_configuration
|
18
|
+
@build_directory = build_directory
|
19
|
+
@project_name = project_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def change_build_number(build_number)
|
23
|
+
Thrust::Executor.system_or_exit "agvtool new-version -all '#{build_number}'"
|
24
|
+
@git.checkout_file('*.xcodeproj')
|
25
|
+
end
|
26
|
+
|
27
|
+
def cleanly_create_ipa(target, app_name, signing_identity, provision_search_query = nil)
|
28
|
+
clean_build
|
29
|
+
kill_simulator
|
30
|
+
build_target(target, 'iphoneos')
|
31
|
+
create_ipa(app_name, signing_identity, provision_search_query)
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_configuration_directory
|
35
|
+
"#{@build_directory}/#{@build_configuration}-iphoneos"
|
36
|
+
end
|
37
|
+
|
38
|
+
def clean_build
|
39
|
+
@out.puts 'Cleaning...'
|
40
|
+
run_xcode('clean')
|
41
|
+
FileUtils.rm_rf(build_configuration_directory)
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_and_build_target(target, build_sdk)
|
45
|
+
clean_build
|
46
|
+
build_target(target, build_sdk)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def build_target(target, build_sdk)
|
52
|
+
@out.puts "Building..."
|
53
|
+
run_xcode('build', build_sdk, target)
|
54
|
+
end
|
55
|
+
|
56
|
+
def kill_simulator
|
57
|
+
@out.puts('Killing simulator...')
|
58
|
+
Thrust::Executor.system %q[killall -m -KILL "gdb"]
|
59
|
+
Thrust::Executor.system %q[killall -m -KILL "otest"]
|
60
|
+
Thrust::Executor.system %q[killall -m -KILL "iPhone Simulator"]
|
61
|
+
end
|
62
|
+
|
63
|
+
def provision_path(provision_search_query)
|
64
|
+
provision_search_path = File.expand_path("~/Library/MobileDevice/Provisioning Profiles/")
|
65
|
+
command = %Q(grep -rl "#{provision_search_query}" "#{provision_search_path}")
|
66
|
+
paths = `#{command}`.split("\n")
|
67
|
+
paths.first or raise(ProvisioningProfileNotFound, "\nCouldn't find provisioning profiles matching #{provision_search_query}.\n\nThe command used was:\n\n#{command}")
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_ipa(app_name, signing_identity, provision_search_query)
|
71
|
+
@out.puts 'Packaging...'
|
72
|
+
ipa_filename = "#{build_configuration_directory}/#{app_name}.ipa"
|
73
|
+
cmd = [
|
74
|
+
"xcrun",
|
75
|
+
"-sdk iphoneos",
|
76
|
+
"-v PackageApplication",
|
77
|
+
"'#{build_configuration_directory}/#{app_name}.app'",
|
78
|
+
"-o '#{ipa_filename}'",
|
79
|
+
"--sign '#{signing_identity}'",
|
80
|
+
"--embed '#{provision_path(provision_search_query)}'"
|
81
|
+
].join(' ')
|
82
|
+
Thrust::Executor.system_or_exit(cmd)
|
83
|
+
ipa_filename
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def run_xcode(build_command, sdk = nil, target = nil)
|
88
|
+
target_flag = target ? "-target #{target}" : "-alltargets"
|
89
|
+
sdk_flag = sdk ? "-sdk #{sdk}" : ''
|
90
|
+
|
91
|
+
Thrust::Executor.system_or_exit(
|
92
|
+
[
|
93
|
+
'set -o pipefail &&',
|
94
|
+
'xcodebuild',
|
95
|
+
"-project #{@project_name}.xcodeproj",
|
96
|
+
target_flag,
|
97
|
+
"-configuration #{@build_configuration}",
|
98
|
+
sdk_flag,
|
99
|
+
"#{build_command}",
|
100
|
+
"SYMROOT=#{@build_directory.inspect}",
|
101
|
+
'2>&1',
|
102
|
+
"| grep -v 'backing file'"
|
103
|
+
].join(' '),
|
104
|
+
output_file("#{@build_configuration}-#{build_command}")
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
def output_file(target)
|
109
|
+
output_dir = if ENV['IS_CI_BOX']
|
110
|
+
ENV['CC_BUILD_ARTIFACTS']
|
111
|
+
else
|
112
|
+
File.exists?(@build_directory) ? @build_directory : FileUtils.mkdir_p(@build_directory)
|
113
|
+
end
|
114
|
+
|
115
|
+
File.join(output_dir, "#{target}.output").tap { |file| @out.puts "Output: #{file}" }
|
116
|
+
end
|
117
|
+
end
|
data/lib/thrust/tasks.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class Thrust::Testflight
|
2
|
+
def initialize(out, input, api_token, team_token)
|
3
|
+
@out = out
|
4
|
+
@in = input
|
5
|
+
@git = Thrust::Git.new(@out)
|
6
|
+
@api_token = api_token
|
7
|
+
@team_token = team_token
|
8
|
+
end
|
9
|
+
|
10
|
+
def upload(package_file, notify, distribution_list, autogenerate_deploy_notes, deployment_target, dsym_path = nil)
|
11
|
+
if dsym_path
|
12
|
+
@out.puts 'Zipping dSYM...'
|
13
|
+
zipped_dsym_path = "#{dsym_path}.zip"
|
14
|
+
Thrust::Executor.system_or_exit "zip -r -T -y '#{zipped_dsym_path}' '#{dsym_path}'"
|
15
|
+
@out.puts 'Done!'
|
16
|
+
end
|
17
|
+
|
18
|
+
if autogenerate_deploy_notes
|
19
|
+
message_file_path = @git.generate_notes_for_deployment(deployment_target)
|
20
|
+
else
|
21
|
+
message_file_path = Thrust::UserPrompt.get_user_input('Deploy Notes: ', @out, @in)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
Thrust::Executor.system_or_exit [
|
26
|
+
'curl http://testflightapp.com/api/builds.json',
|
27
|
+
"-F file=@#{package_file}",
|
28
|
+
("-F dsym=@#{zipped_dsym_path}" if dsym_path),
|
29
|
+
"-F api_token='#{@api_token}'",
|
30
|
+
"-F team_token='#{@team_token}'",
|
31
|
+
"-F notes=@#{message_file_path}",
|
32
|
+
"-F notify=#{(ENV['NOTIFY'] || notify).to_s.downcase.capitalize}",
|
33
|
+
("-F distribution_lists='#{distribution_list}'" if distribution_list)
|
34
|
+
].compact.join(' ')
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module Thrust::UserPrompt
|
4
|
+
def self.get_user_input(prompt, out, stdin = $stdin)
|
5
|
+
out.print prompt.yellow
|
6
|
+
message = stdin.gets
|
7
|
+
message_file = Tempfile.new('message')
|
8
|
+
message_file << message
|
9
|
+
message_file.close
|
10
|
+
message_file.path
|
11
|
+
end
|
12
|
+
end
|
data/lib/thrust.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Thrust; end
|
2
|
+
module Thrust::Android; end
|
3
|
+
module Thrust::IOS; end
|
4
|
+
|
5
|
+
require 'thrust/config'
|
6
|
+
require 'thrust/executor'
|
7
|
+
require 'thrust/git'
|
8
|
+
require 'thrust/testflight'
|
9
|
+
require 'thrust/user_prompt'
|
10
|
+
|
11
|
+
require 'thrust/android/deploy'
|
12
|
+
require 'thrust/android/tools'
|
13
|
+
|
14
|
+
require 'thrust/ios/cedar'
|
15
|
+
require 'thrust/ios/deploy'
|
16
|
+
require 'thrust/ios/x_code_tools'
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thrust
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Michael McCormick
|
@@ -14,45 +13,149 @@ authors:
|
|
14
13
|
- Brandon Liu
|
15
14
|
- Jeff Hui
|
16
15
|
- Philip Kuryloski
|
16
|
+
- Andrew Bruce
|
17
|
+
- Aaron Levine
|
18
|
+
- Eugenia Dellapenna
|
19
|
+
- Aaron VonderHaar
|
20
|
+
- Sheel Choksi
|
21
|
+
- Rachel Bobbins
|
22
|
+
- Molly Trombley-McCann
|
17
23
|
autorequire:
|
18
24
|
bindir: bin
|
19
25
|
cert_chain: []
|
20
26
|
date: 2013-07-24 00:00:00.000000000 Z
|
21
|
-
dependencies:
|
22
|
-
|
27
|
+
dependencies:
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: colorize
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0.6'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.6'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: auto_tagger
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.2'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.2'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '10.1'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.1'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: fakefs
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.5'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.5'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: timecop
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
description: Thrust provides a collection of rake tasks for iOS and Android projects. These
|
113
|
+
include tasks for running Cedar test suites (iOS) and for deploying apps to Testflight
|
114
|
+
(iOS and Android).
|
23
115
|
email: mc+jbritz@pivotallabs.com
|
24
116
|
executables:
|
25
117
|
- thrust
|
26
118
|
extensions: []
|
27
119
|
extra_rdoc_files: []
|
28
120
|
files:
|
29
|
-
-
|
121
|
+
- bin/thrust
|
122
|
+
- lib/config/example.yml
|
30
123
|
- lib/tasks/cedar.rake
|
31
124
|
- lib/tasks/testflight.rake
|
32
|
-
- lib/
|
33
|
-
-
|
34
|
-
|
35
|
-
|
125
|
+
- lib/thrust.rb
|
126
|
+
- lib/thrust/android/deploy.rb
|
127
|
+
- lib/thrust/android/tools.rb
|
128
|
+
- lib/thrust/config.rb
|
129
|
+
- lib/thrust/executor.rb
|
130
|
+
- lib/thrust/git.rb
|
131
|
+
- lib/thrust/ios/cedar.rb
|
132
|
+
- lib/thrust/ios/deploy.rb
|
133
|
+
- lib/thrust/ios/x_code_tools.rb
|
134
|
+
- lib/thrust/tasks.rb
|
135
|
+
- lib/thrust/testflight.rb
|
136
|
+
- lib/thrust/user_prompt.rb
|
137
|
+
homepage: http://github.com/pivotal/thrust
|
138
|
+
licenses:
|
139
|
+
- MIT
|
140
|
+
metadata: {}
|
36
141
|
post_install_message:
|
37
142
|
rdoc_options: []
|
38
143
|
require_paths:
|
39
144
|
- lib
|
40
145
|
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
146
|
requirements:
|
43
|
-
- -
|
147
|
+
- - '>='
|
44
148
|
- !ruby/object:Gem::Version
|
45
|
-
version:
|
149
|
+
version: 1.9.3
|
46
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
151
|
requirements:
|
49
|
-
- -
|
152
|
+
- - '>='
|
50
153
|
- !ruby/object:Gem::Version
|
51
154
|
version: '0'
|
52
155
|
requirements: []
|
53
156
|
rubyforge_project:
|
54
|
-
rubygems_version:
|
157
|
+
rubygems_version: 2.2.1
|
55
158
|
signing_key:
|
56
|
-
specification_version:
|
57
|
-
summary: iOS
|
159
|
+
specification_version: 4
|
160
|
+
summary: Thrust is a collection of rake tasks for iOS/Android development and deployment
|
58
161
|
test_files: []
|
data/lib/thrust_config.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
class ThrustConfig
|
2
|
-
attr_reader :project_root, :config, :spec_config, :build_dir
|
3
|
-
|
4
|
-
def initialize(proj_root, config_file)
|
5
|
-
@project_root = File.expand_path(proj_root)
|
6
|
-
@build_dir = File.join(project_root, 'build')
|
7
|
-
@config = YAML.load_file(config_file)
|
8
|
-
@spec_config = config['specs']
|
9
|
-
end
|
10
|
-
|
11
|
-
def build_prefix_for(configuration)
|
12
|
-
"#{build_dir}/#{configuration}-iphoneos/#{config['app_name']}"
|
13
|
-
end
|
14
|
-
|
15
|
-
# Xcode 4.3 stores its /Developer inside /Applications/Xcode.app, Xcode 4.2 stored it in /Developer
|
16
|
-
def xcode_developer_dir
|
17
|
-
`xcode-select -print-path`.strip
|
18
|
-
end
|
19
|
-
|
20
|
-
def sim_dir
|
21
|
-
File.join(build_dir, spec_config['configuration'] + '-iphonesimulator')
|
22
|
-
end
|
23
|
-
|
24
|
-
def system_or_exit(cmd, stdout = nil)
|
25
|
-
puts "Executing #{cmd}"
|
26
|
-
cmd += " >#{stdout}" if stdout
|
27
|
-
system(cmd) or raise '******** Build failed ********'
|
28
|
-
end
|
29
|
-
|
30
|
-
def run(cmd)
|
31
|
-
puts "Executing #{cmd}"
|
32
|
-
`#{cmd}`
|
33
|
-
end
|
34
|
-
|
35
|
-
def grep_cmd_for_failure(cmd)
|
36
|
-
1.times do
|
37
|
-
puts "Executing #{cmd} and checking for FAILURE"
|
38
|
-
%x[#{cmd} > #{Dir.tmpdir}/cmd.out 2>&1]
|
39
|
-
status = $?
|
40
|
-
result = File.read("#{Dir.tmpdir}/cmd.out")
|
41
|
-
if status.success?
|
42
|
-
puts 'Results:'
|
43
|
-
puts result
|
44
|
-
if result.include?('FAILURE')
|
45
|
-
exit(1)
|
46
|
-
else
|
47
|
-
exit(0)
|
48
|
-
end
|
49
|
-
elsif status == 256
|
50
|
-
redo
|
51
|
-
else
|
52
|
-
puts "Failed to launch: #{status}"
|
53
|
-
exit(1)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def with_env_vars(env_vars)
|
59
|
-
old_values = {}
|
60
|
-
env_vars.each do |key,new_value|
|
61
|
-
old_values[key] = ENV[key]
|
62
|
-
ENV[key] = new_value
|
63
|
-
end
|
64
|
-
|
65
|
-
yield
|
66
|
-
|
67
|
-
env_vars.each_key do |key|
|
68
|
-
ENV[key] = old_values[key]
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def output_file(target)
|
73
|
-
output_dir = if ENV['IS_CI_BOX']
|
74
|
-
ENV['CC_BUILD_ARTIFACTS']
|
75
|
-
else
|
76
|
-
Dir.mkdir(build_dir) unless File.exists?(build_dir)
|
77
|
-
build_dir
|
78
|
-
end
|
79
|
-
|
80
|
-
output_file = File.join(output_dir, "#{target}.output")
|
81
|
-
puts "Output: #{output_file}"
|
82
|
-
output_file
|
83
|
-
end
|
84
|
-
|
85
|
-
def kill_simulator
|
86
|
-
system %q[killall -m -KILL "gdb"]
|
87
|
-
system %q[killall -m -KILL "otest"]
|
88
|
-
system %q[killall -m -KILL "iPhone Simulator"]
|
89
|
-
end
|
90
|
-
|
91
|
-
def update_version(release)
|
92
|
-
run_git_with_message('Changes version to $(agvtool what-marketing-version -terse)') do
|
93
|
-
version = run "agvtool what-marketing-version -terse | head -n1 |cut -f2 -d\="
|
94
|
-
puts "version !#{version}!"
|
95
|
-
build_regex = %r{^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))$}
|
96
|
-
if (match = build_regex.match(version))
|
97
|
-
puts "found match #{match.inspect}"
|
98
|
-
v = {:major => match[:major].to_i, :minor => match[:minor].to_i, :patch => match[:patch].to_i}
|
99
|
-
case(release)
|
100
|
-
when :major then new_build_version(v[:major] + 1, 0, 0)
|
101
|
-
when :minor then new_build_version(v[:major], v[:minor] + 1, 0)
|
102
|
-
when :patch then new_build_version(v[:major], v[:minor], v[:patch] + 1)
|
103
|
-
when :clear then new_build_version(v[:major], v[:minor], v[:patch])
|
104
|
-
end
|
105
|
-
else
|
106
|
-
raise "Unknown version #{version} it should match major.minor.patch"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def new_build_version(major, minor, patch)
|
112
|
-
version = [major, minor, patch].join(".")
|
113
|
-
system_or_exit "agvtool new-marketing-version \"#{version}\""
|
114
|
-
end
|
115
|
-
|
116
|
-
def run_git_with_message(message, &block)
|
117
|
-
if ENV['IGNORE_GIT']
|
118
|
-
puts 'WARNING NOT CHECKING FOR CLEAN WORKING DIRECTORY'
|
119
|
-
block.call
|
120
|
-
else
|
121
|
-
puts 'Checking for clean working tree...'
|
122
|
-
system_or_exit 'git diff-index --quiet HEAD'
|
123
|
-
puts 'Checking that the master branch is up to date...'
|
124
|
-
system_or_exit 'git fetch && git diff --quiet HEAD origin/master'
|
125
|
-
block.call
|
126
|
-
system_or_exit "git commit -am \"#{message}\" && git push origin head"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|