hercules 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +10 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +20 -0
- data/README.md +71 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/bin/hercules +6 -0
- data/hdi/README.md +4 -0
- data/hdi/config.rb +7 -0
- data/hdi/site/index.html +267 -0
- data/hdi/site/stylesheets/style.css +1 -0
- data/hdi/src/configuration.rb +5 -0
- data/hdi/src/layouts/application.haml +12 -0
- data/hdi/src/pages/_hdi.haml +71 -0
- data/hdi/src/pages/_header.haml +8 -0
- data/hdi/src/pages/_jquery.haml +155 -0
- data/hdi/src/pages/index.haml +10 -0
- data/hdi/src/stylesheets/style.sass +173 -0
- data/hercules.gemspec +120 -0
- data/lib/command_runner.rb +73 -0
- data/lib/config.rb +82 -0
- data/lib/deployer.rb +95 -0
- data/lib/git_handler.rb +78 -0
- data/lib/hercules.rb +167 -0
- data/lib/http_handler.rb +41 -0
- data/lib/request_handler.rb +142 -0
- data/tests/command_runner_test.rb +35 -0
- data/tests/config_test.rb +39 -0
- data/tests/fixtures/Gemfile +1 -0
- data/tests/fixtures/Gemfile.lock +8 -0
- data/tests/fixtures/Gemfile.with_git_gem +2 -0
- data/tests/fixtures/Gemfile.with_git_gem.lock +10 -0
- data/tests/fixtures/bogus_config.yml +9 -0
- data/tests/fixtures/bogus_deployer.rb +2 -0
- data/tests/fixtures/config.yml +12 -0
- data/tests/fixtures/config_empty.yml +1 -0
- data/tests/fixtures/config_empty_branches.yml +8 -0
- data/tests/fixtures/config_empty_projects.yml +2 -0
- data/tests/fixtures/config_global.yml +14 -0
- data/tests/fixtures/config_partial_1.yml +7 -0
- data/tests/fixtures/config_partial_2.yml +7 -0
- data/tests/fixtures/config_partial_3.yml +7 -0
- data/tests/fixtures/deployer_branch.rb +11 -0
- data/tests/fixtures/deployer_exception.rb +11 -0
- data/tests/fixtures/deployer_false.rb +11 -0
- data/tests/fixtures/deployer_path.rb +11 -0
- data/tests/fixtures/deployer_true.rb +11 -0
- data/tests/fixtures/deployer_undefined_variable.rb +11 -0
- data/tests/fixtures/startup_checkout_config.yml +12 -0
- data/tests/fixtures/startup_checkout_error_config.yml +12 -0
- data/tests/git_handler_test.rb +95 -0
- data/tests/git_setup.rb +70 -0
- data/tests/hercules_test.rb +128 -0
- data/tests/http_handler_test.rb +88 -0
- data/tests/request_handler_test.rb +242 -0
- data/tests/startup.rb +36 -0
- metadata +251 -0
data/hercules.gemspec
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{hercules}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Diogo Biazus"]
|
12
|
+
s.date = %q{2010-10-14}
|
13
|
+
s.default_executable = %q{hercules}
|
14
|
+
s.description = %q{Very simple deployment tool. It was made to deploy rails applications using github, bundler.}
|
15
|
+
s.email = %q{diogob@gmail.com}
|
16
|
+
s.executables = ["hercules"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".gitignore",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"VERSION",
|
29
|
+
"bin/hercules",
|
30
|
+
"hdi/README.md",
|
31
|
+
"hdi/config.rb",
|
32
|
+
"hdi/site/index.html",
|
33
|
+
"hdi/site/stylesheets/style.css",
|
34
|
+
"hdi/src/configuration.rb",
|
35
|
+
"hdi/src/layouts/application.haml",
|
36
|
+
"hdi/src/pages/_hdi.haml",
|
37
|
+
"hdi/src/pages/_header.haml",
|
38
|
+
"hdi/src/pages/_jquery.haml",
|
39
|
+
"hdi/src/pages/index.haml",
|
40
|
+
"hdi/src/stylesheets/style.sass",
|
41
|
+
"hercules.gemspec",
|
42
|
+
"lib/command_runner.rb",
|
43
|
+
"lib/config.rb",
|
44
|
+
"lib/deployer.rb",
|
45
|
+
"lib/git_handler.rb",
|
46
|
+
"lib/hercules.rb",
|
47
|
+
"lib/http_handler.rb",
|
48
|
+
"lib/request_handler.rb",
|
49
|
+
"tests/command_runner_test.rb",
|
50
|
+
"tests/config_test.rb",
|
51
|
+
"tests/fixtures/Gemfile",
|
52
|
+
"tests/fixtures/Gemfile.lock",
|
53
|
+
"tests/fixtures/Gemfile.with_git_gem",
|
54
|
+
"tests/fixtures/Gemfile.with_git_gem.lock",
|
55
|
+
"tests/fixtures/bogus_config.yml",
|
56
|
+
"tests/fixtures/bogus_deployer.rb",
|
57
|
+
"tests/fixtures/config.yml",
|
58
|
+
"tests/fixtures/config_empty.yml",
|
59
|
+
"tests/fixtures/config_empty_branches.yml",
|
60
|
+
"tests/fixtures/config_empty_projects.yml",
|
61
|
+
"tests/fixtures/config_global.yml",
|
62
|
+
"tests/fixtures/config_partial_1.yml",
|
63
|
+
"tests/fixtures/config_partial_2.yml",
|
64
|
+
"tests/fixtures/config_partial_3.yml",
|
65
|
+
"tests/fixtures/deployer_branch.rb",
|
66
|
+
"tests/fixtures/deployer_exception.rb",
|
67
|
+
"tests/fixtures/deployer_false.rb",
|
68
|
+
"tests/fixtures/deployer_path.rb",
|
69
|
+
"tests/fixtures/deployer_true.rb",
|
70
|
+
"tests/fixtures/deployer_undefined_variable.rb",
|
71
|
+
"tests/fixtures/startup_checkout_config.yml",
|
72
|
+
"tests/fixtures/startup_checkout_error_config.yml",
|
73
|
+
"tests/git_handler_test.rb",
|
74
|
+
"tests/git_setup.rb",
|
75
|
+
"tests/hercules_test.rb",
|
76
|
+
"tests/http_handler_test.rb",
|
77
|
+
"tests/request_handler_test.rb",
|
78
|
+
"tests/startup.rb"
|
79
|
+
]
|
80
|
+
s.homepage = %q{http://github.com/diogob/hercules}
|
81
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
82
|
+
s.require_paths = ["lib"]
|
83
|
+
s.rubygems_version = %q{1.3.7}
|
84
|
+
s.summary = %q{Simple deploy solution for ruby applications (using github+bundler).}
|
85
|
+
|
86
|
+
if s.respond_to? :specification_version then
|
87
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
88
|
+
s.specification_version = 3
|
89
|
+
|
90
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
91
|
+
s.add_runtime_dependency(%q<eventmachine>, ["= 0.12.10"])
|
92
|
+
s.add_runtime_dependency(%q<eventmachine_httpserver>, ["= 0.2.0"])
|
93
|
+
s.add_runtime_dependency(%q<git>, ["= 1.2.5"])
|
94
|
+
s.add_runtime_dependency(%q<json>, ["= 1.4.6"])
|
95
|
+
s.add_runtime_dependency(%q<bundler>, ["~> 1.0.0"])
|
96
|
+
s.add_development_dependency(%q<haml>, ["= 3.0.18"])
|
97
|
+
s.add_development_dependency(%q<compass>, ["= 0.10.5"])
|
98
|
+
s.add_development_dependency(%q<staticmatic>, ["= 0.10.8"])
|
99
|
+
else
|
100
|
+
s.add_dependency(%q<eventmachine>, ["= 0.12.10"])
|
101
|
+
s.add_dependency(%q<eventmachine_httpserver>, ["= 0.2.0"])
|
102
|
+
s.add_dependency(%q<git>, ["= 1.2.5"])
|
103
|
+
s.add_dependency(%q<json>, ["= 1.4.6"])
|
104
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
105
|
+
s.add_dependency(%q<haml>, ["= 3.0.18"])
|
106
|
+
s.add_dependency(%q<compass>, ["= 0.10.5"])
|
107
|
+
s.add_dependency(%q<staticmatic>, ["= 0.10.8"])
|
108
|
+
end
|
109
|
+
else
|
110
|
+
s.add_dependency(%q<eventmachine>, ["= 0.12.10"])
|
111
|
+
s.add_dependency(%q<eventmachine_httpserver>, ["= 0.2.0"])
|
112
|
+
s.add_dependency(%q<git>, ["= 1.2.5"])
|
113
|
+
s.add_dependency(%q<json>, ["= 1.4.6"])
|
114
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
115
|
+
s.add_dependency(%q<haml>, ["= 3.0.18"])
|
116
|
+
s.add_dependency(%q<compass>, ["= 0.10.5"])
|
117
|
+
s.add_dependency(%q<staticmatic>, ["= 0.10.8"])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Hercules
|
2
|
+
# Class to run the shell commands and store their output.
|
3
|
+
# Yes, this class was kindly provided by Integrity.
|
4
|
+
# A very nice CI solution built with ruby: http://github.com/integrity/integrity
|
5
|
+
# I've made some modifications to store all the commands ran in an output log.
|
6
|
+
class CommandRunner
|
7
|
+
attr_reader :output
|
8
|
+
|
9
|
+
class Error < StandardError # :nodoc:
|
10
|
+
end
|
11
|
+
|
12
|
+
Result = Struct.new(:success, :output)
|
13
|
+
|
14
|
+
# We need to inform the logger object to initialize a CommandRunner
|
15
|
+
def initialize(logger)
|
16
|
+
@logger = logger
|
17
|
+
@output = ""
|
18
|
+
end
|
19
|
+
|
20
|
+
# This method will store the output of every command ran by this
|
21
|
+
# instance on a file.
|
22
|
+
# * path is the file path where we want to store the log.
|
23
|
+
def store_output path
|
24
|
+
File.open(path, 'a+'){|f| f.write @output }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Change the working directory.
|
28
|
+
# * dir is the new working directory.
|
29
|
+
def cd(dir)
|
30
|
+
@dir = dir
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Run a command using IO.popen, append output to @output
|
35
|
+
# * command is the string containing the shell command that will be run.
|
36
|
+
def run(command)
|
37
|
+
cmd = normalize(command)
|
38
|
+
|
39
|
+
@logger.debug(cmd)
|
40
|
+
|
41
|
+
output = ""
|
42
|
+
IO.popen(cmd, "r") { |io| output = io.read }
|
43
|
+
|
44
|
+
@output += output
|
45
|
+
Result.new($?.success?, output.chomp)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Run a command using IO.popen, append output to @output
|
49
|
+
# But we raise an error in case the command is not successful.
|
50
|
+
# * command is the string containing the shell command that will be run.
|
51
|
+
def run!(command)
|
52
|
+
result = run(command)
|
53
|
+
|
54
|
+
unless result.success
|
55
|
+
@logger.error(result.output.inspect)
|
56
|
+
raise Error, "Failed to run '#{command}'"
|
57
|
+
end
|
58
|
+
@logger.debug(result.output.inspect)
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
# We change the working directory befor executing anything.
|
64
|
+
# * cmd is the command to be executed.
|
65
|
+
def normalize(cmd)
|
66
|
+
if @dir
|
67
|
+
"(cd #{@dir} && #{cmd} 2>&1)"
|
68
|
+
else
|
69
|
+
"(#{cmd} 2>&1)"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/config.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Hercules
|
5
|
+
class InvalidConfig < Exception; end
|
6
|
+
class Config
|
7
|
+
def initialize(path)
|
8
|
+
@config = nil
|
9
|
+
@path = path
|
10
|
+
reload
|
11
|
+
validate
|
12
|
+
end
|
13
|
+
|
14
|
+
def reload
|
15
|
+
@config = YAML.load_file(@path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](k)
|
19
|
+
@config[k]
|
20
|
+
end
|
21
|
+
|
22
|
+
def each
|
23
|
+
@config.each do |k,v|
|
24
|
+
yield(k,v)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def include?(k)
|
29
|
+
@config.include?(k)
|
30
|
+
end
|
31
|
+
|
32
|
+
def host
|
33
|
+
@config['host'] || "0.0.0.0"
|
34
|
+
end
|
35
|
+
|
36
|
+
def port
|
37
|
+
@config['port'] || 8080
|
38
|
+
end
|
39
|
+
|
40
|
+
def projects
|
41
|
+
p = {}
|
42
|
+
@config.each do |k,v|
|
43
|
+
p[k] = v unless self.class.global_attributes.include?(k)
|
44
|
+
end
|
45
|
+
p
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.global_attributes
|
49
|
+
['host', 'port']
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.project_attributes
|
53
|
+
['target_directory', 'repository', 'token']
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.branch_attributes
|
57
|
+
['checkout_on_startup', 'checkouts_to_keep']
|
58
|
+
end
|
59
|
+
|
60
|
+
def branches
|
61
|
+
r = {}
|
62
|
+
projects.each do |k,v|
|
63
|
+
r[k] = v.keys.find_all{|e| e unless self.class.project_attributes.include?(e)}
|
64
|
+
end
|
65
|
+
r
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def validate
|
70
|
+
# We need to test projects.empty? to cover cases where the config has only global attributes set.
|
71
|
+
raise InvalidConfig.new("Empty config file.") if @config.nil? or projects.empty?
|
72
|
+
projects.each do |k,v|
|
73
|
+
raise InvalidConfig.new("Config file error. #{k} expects a hash of options but got #{v}") unless v.is_a?(Hash)
|
74
|
+
# Every project attribute is mandatory
|
75
|
+
raise InvalidConfig.new("Config file lacks some project attribute, every project must have #{self.class.project_attributes.inspect}") unless !v.nil? and self.class.project_attributes & v.keys == self.class.project_attributes
|
76
|
+
branches[k].each do |branch|
|
77
|
+
raise InvalidConfig.new("Branch #{branch} in project #{k} lacks some branch attribute, every branch must have #{self.class.branch_attributes.inspect}") unless !@config[k][branch].nil? and self.class.branch_attributes & @config[k][branch].keys == self.class.branch_attributes
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/deployer.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/git_handler'
|
4
|
+
require File.dirname(__FILE__) + '/command_runner'
|
5
|
+
require 'bundler'
|
6
|
+
|
7
|
+
module Hercules
|
8
|
+
# The Deployer is responsible for clonning the repository,
|
9
|
+
# copying the code, and calling the deploy triggers.
|
10
|
+
class Deployer
|
11
|
+
# * logger is the object of Logger class that will log the deploy actions.
|
12
|
+
# * config is the hash configuration of the project we want to deploy.
|
13
|
+
# Will be a subtree of the configuration YAML
|
14
|
+
# * branch is the name of the branch we are deploying.
|
15
|
+
def initialize(logger, config, branch)
|
16
|
+
@log = logger
|
17
|
+
@config = config
|
18
|
+
@branch = branch
|
19
|
+
@cmd = CommandRunner.new(@log)
|
20
|
+
@trigger_class = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# This method will do the deploy: git clone, run bundle install and call the triggers callbacks.
|
25
|
+
def deploy
|
26
|
+
git = GitHandler.new @config
|
27
|
+
git.deploy_branch(@branch) do |dir, branch|
|
28
|
+
Bundler.with_clean_env do
|
29
|
+
@cmd.cd(dir)
|
30
|
+
bundle_path = "#{dir}/../../../bundles/#{@branch}"
|
31
|
+
FileUtils.mkdir_p(bundle_path)
|
32
|
+
FileUtils.mkdir_p("#{dir}/vendor")
|
33
|
+
FileUtils.ln_s(bundle_path, "#{dir}/vendor/bundle", :force => true)
|
34
|
+
@cmd.run!("bundle install --deployment")
|
35
|
+
@trigger_class = look_for_triggers(dir)
|
36
|
+
before_trigger(dir) if has_before_trigger?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
@log.warn "Branch #{@branch} deployed"
|
40
|
+
dir = "#{git.branches_path}/#{@branch}"
|
41
|
+
Bundler.with_clean_env do
|
42
|
+
after_trigger(dir) if has_after_trigger?
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
# Now we must store the deploy output
|
46
|
+
output_dir = "#{@config['target_directory']}/logs/#{@branch}/"
|
47
|
+
FileUtils.mkdir_p output_dir
|
48
|
+
@cmd.store_output "#{output_dir}/#{git.last_commit}.log"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
# This method will execute the before trigger
|
53
|
+
# * dir is the working directory for trigger execution.
|
54
|
+
def before_trigger(dir)
|
55
|
+
@log.debug "Executing before_trigger"
|
56
|
+
Dir.chdir(dir) do
|
57
|
+
raise "before_deploy returned false." unless @trigger_class.before_deploy({:path => dir, :branch => @branch, :shell => @cmd})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# This method will execute the after trigger
|
62
|
+
# * dir is the working directory for trigger execution.
|
63
|
+
def after_trigger(dir)
|
64
|
+
@log.debug "Executing after_trigger"
|
65
|
+
Dir.chdir(dir) do
|
66
|
+
@trigger_class.after_deploy({:path => dir, :branch => @branch, :shell => @cmd})
|
67
|
+
end
|
68
|
+
@log.info "After deploy script executed"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Check if the project has a before trigger
|
72
|
+
def has_before_trigger?; (!@trigger_class.nil? and @trigger_class.methods.include?("before_deploy")) ;end
|
73
|
+
# Check if the project has an after trigger
|
74
|
+
def has_after_trigger?; (!@trigger_class.nil? and @trigger_class.methods.include?("after_deploy")) ;end
|
75
|
+
|
76
|
+
# Look for triggers in <dir>/lib/hercules_triggers.rb
|
77
|
+
# The triggers should be inside a Hercules module in the Triggers class.
|
78
|
+
# * dir is the root dir where we will look for the triggers.
|
79
|
+
def look_for_triggers(dir)
|
80
|
+
if File.exists? "#{dir}/lib/hercules_triggers.rb"
|
81
|
+
require "#{dir}/lib/hercules_triggers.rb"
|
82
|
+
begin
|
83
|
+
@log.info "Looking for trigger in #{dir}/lib/hercules_triggers.rb"
|
84
|
+
return ::Hercules::Triggers
|
85
|
+
rescue NameError => e
|
86
|
+
# We have to allow the use of a lib/hercules_triggers.rb unrelated to Hercules
|
87
|
+
if e.message =~ /uninitialized constant .*Hercules.*/
|
88
|
+
@log.warn "File lib/deployer.rb without Hercules::Triggers: #{e.inspect}"
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/git_handler.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'git'
|
3
|
+
|
4
|
+
module Hercules
|
5
|
+
# Class that handles the git operations.
|
6
|
+
class GitHandler
|
7
|
+
attr_reader :last_commit
|
8
|
+
# We pass an options hash that should contain:
|
9
|
+
# {
|
10
|
+
# 'target_directory' => '/home/hercules/hercules.com',
|
11
|
+
# 'repository' => 'git://github.com/diogob/hercules.git',
|
12
|
+
# 'master' => { 'checkouts_to_keep' => 2 },
|
13
|
+
# }
|
14
|
+
def initialize(options)
|
15
|
+
@options = options
|
16
|
+
@last_commit = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
# Will export the branch to @options['target_directory']/checkouts/
|
20
|
+
# And link it in @options['target_directory']/branches/
|
21
|
+
# It uses the commit's sha1 as directory name.
|
22
|
+
# * branch is the branch to be deployed, defaults to master.
|
23
|
+
def export_branch(branch = 'master')
|
24
|
+
tmp_dir = "#{@options['target_directory']}/checkouts/#{branch}/.tmp_#{Time.now.strftime("%Y%m%d%H%M%S")}"
|
25
|
+
begin
|
26
|
+
repo = Git.clone(@options['repository'], tmp_dir, {:depth => 1})
|
27
|
+
repo.checkout("origin/#{branch}")
|
28
|
+
rescue Exception => e
|
29
|
+
FileUtils.rm_rf repo.dir.to_s unless repo.nil?
|
30
|
+
raise "Error while cloning #{@options['repository']}: #{e}"
|
31
|
+
end
|
32
|
+
@last_commit = repo.gcommit('HEAD').sha
|
33
|
+
commit_dir = "#{@options['target_directory']}/checkouts/#{branch}/#{@last_commit}"
|
34
|
+
Dir.chdir(repo.dir.to_s) { FileUtils.rm_r '.git' }
|
35
|
+
FileUtils.mv repo.dir.to_s, commit_dir
|
36
|
+
commit_dir
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the path to branches' link directory.
|
40
|
+
def branches_path
|
41
|
+
"#{@options['target_directory']}/branches"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Creates and then returns the path to branches' link directory.
|
45
|
+
def create_branches_dir
|
46
|
+
FileUtils.mkdir_p branches_path
|
47
|
+
branches_path
|
48
|
+
end
|
49
|
+
|
50
|
+
# Deploys the branch.
|
51
|
+
# This means it exports it and removes old checkouts upon a successful completion.
|
52
|
+
# It also creates the links' directory and links the checkout.
|
53
|
+
# * branch is the branch name to be deployed. Defaults to master.
|
54
|
+
def deploy_branch(branch = 'master')
|
55
|
+
checkout = export_branch(branch)
|
56
|
+
#@todo here we must call the before deploy script
|
57
|
+
yield(checkout, branch) if block_given?
|
58
|
+
remove_old_checkouts branch
|
59
|
+
FileUtils.rm_f("#{create_branches_dir}/#{branch}")
|
60
|
+
FileUtils.ln_sf(checkout, "#{branches_path}/#{branch}")
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def remove_old_checkouts(branch) # :nodoc:
|
66
|
+
max = @options[branch]['checkouts_to_keep']
|
67
|
+
dir = "#{@options['target_directory']}/checkouts/#{branch}"
|
68
|
+
if (Dir.glob("#{dir}/*").size) > max
|
69
|
+
# Here we must delete the oldest checkout
|
70
|
+
checkout_to_delete = Dir.glob("#{dir}/*").sort{|a,b| File.new(a).mtime.strftime("%Y%m%d%H%M%S") <=> File.new(b).mtime.strftime("%Y%m%d%H%M%S") }.shift
|
71
|
+
FileUtils.rm_r "#{checkout_to_delete}"
|
72
|
+
# Remove log file if it exists
|
73
|
+
# To achieve consistency we must remove the log when and only when we remove the checkout
|
74
|
+
FileUtils.rm_f "#{@options['target_directory']}/logs/#{checkout_to_delete.split('/').pop}.log"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|