thrust 0.0.5 → 0.2.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 +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
|