xcodebuilder 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES.md +82 -0
- data/LICENSE +20 -0
- data/README.md +248 -0
- data/lib/xcode_builder.rb +299 -0
- data/lib/xcode_builder/build_output_parser.rb +56 -0
- data/lib/xcode_builder/configuration.rb +151 -0
- data/lib/xcode_builder/deployment_strategies.rb +39 -0
- data/lib/xcode_builder/deployment_strategies/testflight.rb +92 -0
- data/lib/xcode_builder/deployment_strategies/web.rb +104 -0
- data/lib/xcode_builder/release_strategies.rb +34 -0
- data/lib/xcode_builder/release_strategies/git.rb +116 -0
- data/lib/xcodebuilder.rb +1 -0
- metadata +141 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module XcodeBuilder
|
|
2
|
+
class BuildOutputParser
|
|
3
|
+
|
|
4
|
+
def initialize(output)
|
|
5
|
+
@output = output
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def build_output_dir
|
|
9
|
+
# yes, this is truly horrible, but unless somebody else can find a better way...
|
|
10
|
+
found = @output.split("\n").grep(/^Validate(.*)\/Xcode\/DerivedData\/(.*)-(.*)/).first
|
|
11
|
+
if found && found =~ /Validate [\"]?([^\"|$]*)/
|
|
12
|
+
reference = $1
|
|
13
|
+
else
|
|
14
|
+
raise "Cannot parse build_dir from build output."
|
|
15
|
+
end
|
|
16
|
+
derived_data_directory = reference.split("/Build/Products/").first
|
|
17
|
+
"#{derived_data_directory}/Build/Products/"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def failed?
|
|
21
|
+
@output.split("\n").any? {|line| line.include? "** BUILD FAILED **"}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# quick testing
|
|
27
|
+
if __FILE__ == $0
|
|
28
|
+
|
|
29
|
+
require 'test/unit'
|
|
30
|
+
class BuildOutputTest < Test::Unit::TestCase
|
|
31
|
+
|
|
32
|
+
def test_parses_output_with_unquoted_build_path
|
|
33
|
+
bop = BetaBuilder::BuildOutputParser.new(<<eos)
|
|
34
|
+
Validate /Users/johnsmith/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/Distribution-iphoneos/Application.app
|
|
35
|
+
cd /Users/user/app/ios
|
|
36
|
+
setenv PATH \"/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Developer/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin\"
|
|
37
|
+
setenv PRODUCT_TYPE com.apple.product-type.application
|
|
38
|
+
/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/Validation /Users/user/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/Distribution-iphoneos/Application.app
|
|
39
|
+
eos
|
|
40
|
+
assert_equal "/Users/johnsmith/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/", bop.build_output_dir
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_parses_output_with_quoted_build_path
|
|
44
|
+
bop = BetaBuilder::BuildOutputParser.new(<<eos)
|
|
45
|
+
Validate \"/Users/john smith/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/Distribution-iphoneos/Application.app\"
|
|
46
|
+
cd /Users/user/app/ios
|
|
47
|
+
setenv PATH \"/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Developer/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin\"
|
|
48
|
+
setenv PRODUCT_TYPE com.apple.product-type.application
|
|
49
|
+
/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/Validation /Users/user/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/Distribution-iphoneos/Application.app
|
|
50
|
+
eos
|
|
51
|
+
assert_equal "/Users/john smith/Library/Developer/Xcode/DerivedData/Application-hegpgdbpjylesafhkxnsymrzjavl/Build/Products/", bop.build_output_dir
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require File.dirname(__FILE__) + '/deployment_strategies'
|
|
3
|
+
require File.dirname(__FILE__) + '/release_strategies'
|
|
4
|
+
|
|
5
|
+
module XcodeBuilder
|
|
6
|
+
class Configuration < OpenStruct
|
|
7
|
+
def release_notes_text
|
|
8
|
+
return release_notes.call if release_notes.is_a? Proc
|
|
9
|
+
release_notes
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build_arguments
|
|
13
|
+
args = []
|
|
14
|
+
if workspace_file_path
|
|
15
|
+
raise "A scheme is required if building from a workspace" unless scheme
|
|
16
|
+
args << "-workspace '#{workspace_file_path}'"
|
|
17
|
+
args << "-scheme '#{scheme}'"
|
|
18
|
+
else
|
|
19
|
+
args << "-target '#{target}'"
|
|
20
|
+
args << "-project '#{project_file_path}'" if project_file_path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
args << "-sdk #{sdk}"
|
|
24
|
+
|
|
25
|
+
args << "-configuration '#{configuration}'"
|
|
26
|
+
|
|
27
|
+
if xcodebuild_extra_args
|
|
28
|
+
args.concat xcodebuild_extra_args if xcodebuild_extra_args.is_a? Array
|
|
29
|
+
args << "#{xcodebuild_extra_args}" if xcodears.is_a? String
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
args
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def app_file_name
|
|
36
|
+
raise ArgumentError, "app_name or target must be set in the BetaBuilder configuration block" if app_name.nil?
|
|
37
|
+
"#{app_name}.#{app_extension}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def info_plist_path
|
|
41
|
+
if info_plist != nil then
|
|
42
|
+
File.expand_path info_plist
|
|
43
|
+
else
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_number
|
|
49
|
+
# no plist is found, return a nil version
|
|
50
|
+
if (info_plist_path == nil) || (!File.exists? info_plist_path) then
|
|
51
|
+
return nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# read the plist and extract data
|
|
55
|
+
plist = CFPropertyList::List.new(:file => info_plist_path)
|
|
56
|
+
data = CFPropertyList.native_types(plist.value)
|
|
57
|
+
data["CFBundleVersion"]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def next_build_number
|
|
61
|
+
# if we don't have a current version, we don't have a next version :)
|
|
62
|
+
if build_number == nil then
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# get a hold on the build number and increment it
|
|
67
|
+
version_components = build_number.split(".")
|
|
68
|
+
new_build_number = version_components.pop.to_i + 1
|
|
69
|
+
version_components.push new_build_number.to_s
|
|
70
|
+
version_components.join "."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def built_app_long_version_suffix
|
|
74
|
+
if build_number == nil then
|
|
75
|
+
""
|
|
76
|
+
else
|
|
77
|
+
"-#{build_number}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def ipa_name
|
|
82
|
+
prefix = app_name == nil ? target : app_name
|
|
83
|
+
"#{prefix}#{built_app_long_version_suffix}.ipa"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def built_app_path
|
|
87
|
+
sdk_extension = if sdk.eql? "macosx" then "" else "-#{sdk}" end
|
|
88
|
+
if build_dir == :derived
|
|
89
|
+
File.join("#{derived_build_dir}", "#{configuration}#{sdk_extension}", "#{app_file_name}")
|
|
90
|
+
else
|
|
91
|
+
File.join("#{build_dir}", "#{configuration}#{sdk_extension}", "#{app_file_name}")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def built_dsym_path
|
|
96
|
+
"#{built_app_path}.dSYM"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def derived_build_dir
|
|
100
|
+
workspace_name = Pathname.new(workspace_file_path).basename.to_s.split(".")[0]
|
|
101
|
+
for dir in Dir[File.join(File.expand_path("~/Library/Developer/Xcode/DerivedData"), "#{workspace_name}-*")]
|
|
102
|
+
return "#{dir}/Build/Products" if File.read( File.join(dir, "info.plist") ).match workspace_file_path
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def derived_build_dir_from_build_output
|
|
108
|
+
output = BuildOutputParser.new(File.read("build.output"))
|
|
109
|
+
output.build_output_dir
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def zipped_package_name
|
|
113
|
+
"#{app_name}#{built_app_long_version_suffix}.zip"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def ipa_path
|
|
117
|
+
File.join(File.expand_path(package_destination_path), ipa_name)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def dsym_name
|
|
121
|
+
"#{app_name}#{built_app_long_version_suffix}.dSYM.zip"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def dsym_path
|
|
125
|
+
File.join(File.expand_path(package_destination_path), dsym_name)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def app_bundle_path
|
|
129
|
+
"#{package_destination_path}/#{app_name}.#{app_extension}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def deploy_using(strategy_name, &block)
|
|
133
|
+
if DeploymentStrategies.valid_strategy?(strategy_name.to_sym)
|
|
134
|
+
self.deployment_strategy = DeploymentStrategies.build(strategy_name, self)
|
|
135
|
+
self.deployment_strategy.configure(&block)
|
|
136
|
+
else
|
|
137
|
+
raise "Unknown deployment strategy '#{strategy_name}'."
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def release_using(strategy_name, &block)
|
|
142
|
+
if ReleaseStrategies.valid_strategy?(strategy_name.to_sym)
|
|
143
|
+
self.release_strategy = ReleaseStrategies.build(strategy_name, self)
|
|
144
|
+
self.release_strategy.configure(&block)
|
|
145
|
+
self.release_strategy.prepare
|
|
146
|
+
else
|
|
147
|
+
raise "Unknown release strategy '#{strategy_name}'."
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module XcodeBuilder
|
|
2
|
+
module DeploymentStrategies
|
|
3
|
+
def self.valid_strategy?(strategy_name)
|
|
4
|
+
strategies.keys.include?(strategy_name.to_sym)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.build(strategy_name, configuration)
|
|
8
|
+
strategies[strategy_name.to_sym].new(configuration)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Strategy
|
|
12
|
+
def initialize(configuration)
|
|
13
|
+
@configuration = configuration
|
|
14
|
+
|
|
15
|
+
if respond_to?(:extended_configuration_for_strategy)
|
|
16
|
+
@configuration.instance_eval(&extended_configuration_for_strategy)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def configure(&block)
|
|
21
|
+
yield @configuration
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def prepare
|
|
25
|
+
puts "Nothing to prepare!" if @configuration.verbose
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def self.strategies
|
|
32
|
+
{:web => Web, :testflight => TestFlight}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
require File.dirname(__FILE__) + '/deployment_strategies/web'
|
|
38
|
+
require File.dirname(__FILE__) + '/deployment_strategies/testflight'
|
|
39
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'rest_client'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module XcodeBuilder
|
|
7
|
+
module DeploymentStrategies
|
|
8
|
+
class TestFlight < Strategy
|
|
9
|
+
include Rake::DSL
|
|
10
|
+
include FileUtils
|
|
11
|
+
ENDPOINT = "https://testflightapp.com/api/builds.json"
|
|
12
|
+
|
|
13
|
+
def extended_configuration_for_strategy
|
|
14
|
+
proc do
|
|
15
|
+
def generate_release_notes(&block)
|
|
16
|
+
self.release_notes = block if block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def deploy
|
|
22
|
+
release_notes = get_notes
|
|
23
|
+
payload = {
|
|
24
|
+
:api_token => @configuration.api_token,
|
|
25
|
+
:team_token => @configuration.team_token,
|
|
26
|
+
:file => File.new(@configuration.ipa_path, 'rb'),
|
|
27
|
+
:notes => release_notes,
|
|
28
|
+
:distribution_lists => (@configuration.distribution_lists || []).join(","),
|
|
29
|
+
:notify => @configuration.notify || false,
|
|
30
|
+
:replace => @configuration.replace || false
|
|
31
|
+
}
|
|
32
|
+
if @configuration.verbose
|
|
33
|
+
puts "ipa path: #{@configuration.ipa_path}"
|
|
34
|
+
puts "release notes: #{release_notes}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if @configuration.dry_run
|
|
38
|
+
puts '** Dry Run - No action here! **'
|
|
39
|
+
return
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
print "Uploading build to TestFlight..."
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
response = RestClient.post(ENDPOINT, payload, :accept => :json)
|
|
46
|
+
rescue => e
|
|
47
|
+
response = e.response
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if (response.code == 201) || (response.code == 200)
|
|
51
|
+
puts "Done."
|
|
52
|
+
else
|
|
53
|
+
puts "Failed."
|
|
54
|
+
puts "#{response}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def get_notes
|
|
61
|
+
notes = @configuration.release_notes_text
|
|
62
|
+
notes || get_notes_using_editor || get_notes_using_prompt
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get_notes_using_editor
|
|
66
|
+
return unless (editor = ENV["EDITOR"])
|
|
67
|
+
|
|
68
|
+
dir = Dir.mktmpdir
|
|
69
|
+
begin
|
|
70
|
+
filepath = "#{dir}/release_notes"
|
|
71
|
+
system("#{editor} #{filepath}")
|
|
72
|
+
@configuration.release_notes = File.read(filepath)
|
|
73
|
+
ensure
|
|
74
|
+
rm_rf(dir)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def get_notes_using_prompt
|
|
79
|
+
puts "Enter the release notes for this build (hit enter twice when done):\n"
|
|
80
|
+
@configuration.release_notes = gets_until_match(/\n{2}$/).strip
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def gets_until_match(pattern, string = "")
|
|
84
|
+
if (string += STDIN.gets) =~ pattern
|
|
85
|
+
string
|
|
86
|
+
else
|
|
87
|
+
gets_until_match(pattern, string)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
module XcodeBuilder
|
|
2
|
+
module DeploymentStrategies
|
|
3
|
+
class Web < Strategy
|
|
4
|
+
def extended_configuration_for_strategy
|
|
5
|
+
proc do
|
|
6
|
+
def deployment_url
|
|
7
|
+
File.join(deploy_to, ipa_name)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def manifest_url
|
|
11
|
+
File.join(deploy_to, "manifest.plist")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def remote_installation_path
|
|
15
|
+
File.join(remote_directory)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def prepare
|
|
21
|
+
plist = CFPropertyList::List.new(:file => "#{@configuration.built_app_path}/Info.plist")
|
|
22
|
+
plist_data = CFPropertyList.native_types(plist.value)
|
|
23
|
+
File.open("pkg/dist/manifest.plist", "w") do |io|
|
|
24
|
+
io << %{<?xml version="1.0" encoding="UTF-8"?>
|
|
25
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
26
|
+
<plist version="1.0">
|
|
27
|
+
<dict>
|
|
28
|
+
<key>items</key>
|
|
29
|
+
<array>
|
|
30
|
+
<dict>
|
|
31
|
+
<key>assets</key>
|
|
32
|
+
<array>
|
|
33
|
+
<dict>
|
|
34
|
+
<key>kind</key>
|
|
35
|
+
<string>software-package</string>
|
|
36
|
+
<key>url</key>
|
|
37
|
+
<string>#{@configuration.deployment_url}</string>
|
|
38
|
+
</dict>
|
|
39
|
+
</array>
|
|
40
|
+
<key>metadata</key>
|
|
41
|
+
<dict>
|
|
42
|
+
<key>bundle-identifier</key>
|
|
43
|
+
<string>#{plist_data['CFBundleIdentifier']}</string>
|
|
44
|
+
<key>bundle-version</key>
|
|
45
|
+
<string>#{plist_data['CFBundleVersion']}</string>
|
|
46
|
+
<key>kind</key>
|
|
47
|
+
<string>software</string>
|
|
48
|
+
<key>title</key>
|
|
49
|
+
<string>#{plist_data['CFBundleDisplayName']}</string>
|
|
50
|
+
</dict>
|
|
51
|
+
</dict>
|
|
52
|
+
</array>
|
|
53
|
+
</dict>
|
|
54
|
+
</plist>
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
File.open("pkg/dist/index.html", "w") do |io|
|
|
58
|
+
io << %{<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
59
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
60
|
+
<head>
|
|
61
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
62
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
|
63
|
+
<title>Beta Download</title>
|
|
64
|
+
<style type="text/css">
|
|
65
|
+
body {background:#fff;margin:0;padding:0;font-family:arial,helvetica,sans-serif;text-align:center;padding:10px;color:#333;font-size:16px;}
|
|
66
|
+
#container {width:300px;margin:0 auto;}
|
|
67
|
+
h1 {margin:0;padding:0;font-size:14px;}
|
|
68
|
+
p {font-size:13px;}
|
|
69
|
+
.link {background:#ecf5ff;border-top:1px solid #fff;border:1px solid #dfebf8;margin-top:.5em;padding:.3em;}
|
|
70
|
+
.link a {text-decoration:none;font-size:15px;display:block;color:#069;}
|
|
71
|
+
</style>
|
|
72
|
+
</head>
|
|
73
|
+
<body>
|
|
74
|
+
<div id="container">
|
|
75
|
+
<div class="link"><a href="itms-services://?action=download-manifest&url=#{@configuration.manifest_url}">Tap Here to Install<br />#{@configuration.target} #{plist_data['CFBundleVersion']}<br />On Your Device</a></div>
|
|
76
|
+
<p><strong>Link didn't work?</strong><br />
|
|
77
|
+
Make sure you're visiting this page on your device, not your computer.</p>
|
|
78
|
+
</div>
|
|
79
|
+
</body>
|
|
80
|
+
</html>
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def deploy
|
|
86
|
+
cmd = []
|
|
87
|
+
|
|
88
|
+
cmd.push "scp"
|
|
89
|
+
|
|
90
|
+
if @configuration.remote_port
|
|
91
|
+
cmd.push "-P #{@configuration.remote_port}"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
cmd.push "pkg/dist/*"
|
|
95
|
+
cmd.push "#{@configuration.remote_host}:#{@configuration.remote_installation_path}"
|
|
96
|
+
|
|
97
|
+
cmd = cmd.join(" ")
|
|
98
|
+
|
|
99
|
+
puts "* Running `#{cmd}`"
|
|
100
|
+
system(cmd)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module XcodeBuilder
|
|
2
|
+
module ReleaseStrategies
|
|
3
|
+
def self.valid_strategy?(strategy_name)
|
|
4
|
+
strategies.keys.include?(strategy_name.to_sym)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.build(strategy_name, configuration)
|
|
8
|
+
strategies[strategy_name.to_sym].new(configuration)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class ReleaseStrategy
|
|
12
|
+
def initialize(configuration)
|
|
13
|
+
@configuration = configuration
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def configure(&block)
|
|
17
|
+
yield self
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def prepare
|
|
22
|
+
puts "Nothing to prepare!" if @configuration.verbose
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def self.strategies
|
|
28
|
+
{:git => Git}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
require File.dirname(__FILE__) + '/release_strategies/git'
|
|
34
|
+
|