statistrano 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/changelog.md +161 -0
- data/doc/config/file-permissions.md +33 -0
- data/doc/config/log-files.md +32 -0
- data/doc/config/task-definitions.md +88 -0
- data/doc/getting-started.md +96 -0
- data/doc/strategies/base.md +38 -0
- data/doc/strategies/branches.md +82 -0
- data/doc/strategies/releases.md +110 -0
- data/doc/strategies.md +17 -0
- data/lib/statistrano/config/configurable.rb +53 -0
- data/lib/statistrano/config/rake_task_with_context_creation.rb +43 -0
- data/lib/statistrano/config.rb +52 -0
- data/lib/statistrano/deployment/log_file.rb +44 -0
- data/lib/statistrano/deployment/manifest.rb +88 -0
- data/lib/statistrano/deployment/rake_tasks.rb +74 -0
- data/lib/statistrano/deployment/registerable.rb +11 -0
- data/lib/statistrano/deployment/releaser/revisions.rb +163 -0
- data/lib/statistrano/deployment/releaser/single.rb +48 -0
- data/lib/statistrano/deployment/releaser.rb +2 -0
- data/lib/statistrano/deployment/strategy/base.rb +132 -0
- data/lib/statistrano/deployment/strategy/branches/index/template.html.erb +78 -0
- data/lib/statistrano/deployment/strategy/branches/index.rb +40 -0
- data/lib/statistrano/deployment/strategy/branches/release.rb +73 -0
- data/lib/statistrano/deployment/strategy/branches.rb +198 -0
- data/lib/statistrano/deployment/strategy/check_git.rb +43 -0
- data/lib/statistrano/deployment/strategy/invoke_tasks.rb +58 -0
- data/lib/statistrano/deployment/strategy/releases.rb +76 -0
- data/lib/statistrano/deployment/strategy.rb +37 -0
- data/lib/statistrano/deployment.rb +10 -0
- data/lib/statistrano/log/default_logger.rb +105 -0
- data/lib/statistrano/log.rb +33 -0
- data/lib/statistrano/remote/file.rb +79 -0
- data/lib/statistrano/remote.rb +111 -0
- data/lib/statistrano/shell.rb +17 -0
- data/lib/statistrano/util/file_permissions.rb +34 -0
- data/lib/statistrano/util.rb +27 -0
- data/lib/statistrano/version.rb +3 -0
- data/lib/statistrano.rb +55 -0
- data/readme.md +247 -0
- data/spec/integration_tests/base_integration_spec.rb +103 -0
- data/spec/integration_tests/branches_integration_spec.rb +189 -0
- data/spec/integration_tests/releases/deploy_integration_spec.rb +116 -0
- data/spec/integration_tests/releases/list_releases_integration_spec.rb +38 -0
- data/spec/integration_tests/releases/prune_releases_integration_spec.rb +86 -0
- data/spec/integration_tests/releases/rollback_release_integration_spec.rb +46 -0
- data/spec/lib/statistrano/config/configurable_spec.rb +88 -0
- data/spec/lib/statistrano/config/rake_task_with_context_creation_spec.rb +73 -0
- data/spec/lib/statistrano/config_spec.rb +34 -0
- data/spec/lib/statistrano/deployment/log_file_spec.rb +75 -0
- data/spec/lib/statistrano/deployment/manifest_spec.rb +171 -0
- data/spec/lib/statistrano/deployment/rake_tasks_spec.rb +107 -0
- data/spec/lib/statistrano/deployment/registerable_spec.rb +19 -0
- data/spec/lib/statistrano/deployment/releaser/revisions_spec.rb +486 -0
- data/spec/lib/statistrano/deployment/releaser/single_spec.rb +59 -0
- data/spec/lib/statistrano/deployment/strategy/base_spec.rb +158 -0
- data/spec/lib/statistrano/deployment/strategy/branches_spec.rb +19 -0
- data/spec/lib/statistrano/deployment/strategy/check_git_spec.rb +39 -0
- data/spec/lib/statistrano/deployment/strategy/invoke_tasks_spec.rb +66 -0
- data/spec/lib/statistrano/deployment/strategy/releases_spec.rb +257 -0
- data/spec/lib/statistrano/deployment/strategy_spec.rb +76 -0
- data/spec/lib/statistrano/deployment_spec.rb +4 -0
- data/spec/lib/statistrano/log/default_logger_spec.rb +172 -0
- data/spec/lib/statistrano/log_spec.rb +36 -0
- data/spec/lib/statistrano/remote/file_spec.rb +166 -0
- data/spec/lib/statistrano/remote_spec.rb +226 -0
- data/spec/lib/statistrano/util/file_permissions_spec.rb +25 -0
- data/spec/lib/statistrano/util_spec.rb +23 -0
- data/spec/lib/statistrano_spec.rb +52 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/support/given.rb +39 -0
- metadata +223 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
|
5
|
+
# A Base Deployment Instance
|
6
|
+
# it holds the common methods needed
|
7
|
+
# to create a deployment
|
8
|
+
class Base
|
9
|
+
extend Deployment::Registerable
|
10
|
+
extend Config::Configurable
|
11
|
+
include InvokeTasks
|
12
|
+
include CheckGit
|
13
|
+
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
register_strategy :base
|
17
|
+
|
18
|
+
options :remote_dir, :local_dir,
|
19
|
+
:hostname, :user, :password, :keys, :forward_agent,
|
20
|
+
:build_task, :post_deploy_task,
|
21
|
+
:check_git, :git_branch, :repo_url,
|
22
|
+
:dir_permissions, :file_permissions, :rsync_flags,
|
23
|
+
:log_file_path, :log_file_entry
|
24
|
+
|
25
|
+
option :remotes, []
|
26
|
+
option :dir_permissions, 755
|
27
|
+
option :file_permissions, 644
|
28
|
+
option :rsync_flags, '-aqz --delete-after'
|
29
|
+
|
30
|
+
option :verbose, false
|
31
|
+
|
32
|
+
task :deploy, :deploy, "Deploy to remote"
|
33
|
+
task :build, :invoke_build_task, "Run build task"
|
34
|
+
task :post_deploy, :invoke_post_deploy_task, "Run post deploy task"
|
35
|
+
|
36
|
+
# create a new deployment instance
|
37
|
+
# @param name [String]
|
38
|
+
# @return [Void]
|
39
|
+
def initialize name
|
40
|
+
@name = name
|
41
|
+
end
|
42
|
+
|
43
|
+
# Standard deployment flow
|
44
|
+
# @return [Void]
|
45
|
+
def deploy
|
46
|
+
unless safe_to_deploy?
|
47
|
+
Log.error "exiting due to git check failing"
|
48
|
+
abort()
|
49
|
+
end
|
50
|
+
|
51
|
+
build_data = invoke_build_task
|
52
|
+
build_data = ensure_data_is_hash build_data
|
53
|
+
|
54
|
+
unless safe_to_deploy?
|
55
|
+
Log.error "exiting due to git check failing",
|
56
|
+
"your build task modified checked in files"
|
57
|
+
abort()
|
58
|
+
end
|
59
|
+
|
60
|
+
remotes.each do |remote|
|
61
|
+
persisted_releaser.create_release remote, build_data
|
62
|
+
end
|
63
|
+
|
64
|
+
post_deploy_data = invoke_post_deploy_task
|
65
|
+
post_deploy_data = ensure_data_is_hash post_deploy_data
|
66
|
+
|
67
|
+
if config.log_file_path
|
68
|
+
log_entry = config.log_file_entry.call self, persisted_releaser,
|
69
|
+
build_data, post_deploy_data
|
70
|
+
|
71
|
+
remotes.each do |remote|
|
72
|
+
log_file(remote).append! log_entry
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
flush_persisted_releaser!
|
77
|
+
end
|
78
|
+
|
79
|
+
def register_tasks
|
80
|
+
RakeTasks.register self
|
81
|
+
end
|
82
|
+
|
83
|
+
def remotes
|
84
|
+
return @_remotes if @_remotes
|
85
|
+
|
86
|
+
@_remotes = config.options[:remotes].map do |remote_options|
|
87
|
+
Remote.new Config.new( config.options.dup.merge(remote_options) )
|
88
|
+
end
|
89
|
+
@_remotes.push Remote.new(config) if @_remotes.empty?
|
90
|
+
|
91
|
+
return @_remotes
|
92
|
+
end
|
93
|
+
|
94
|
+
def log_file remote=remotes.first
|
95
|
+
Deployment::LogFile.new config.log_file_path, remote
|
96
|
+
end
|
97
|
+
|
98
|
+
def persisted_releaser
|
99
|
+
@_persisted_releaser ||= releaser
|
100
|
+
end
|
101
|
+
|
102
|
+
def flush_persisted_releaser!
|
103
|
+
@_persisted_releaser = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def resolve_log_file_path
|
109
|
+
if config.log_file_path.start_with? '/'
|
110
|
+
config.log_file_path
|
111
|
+
else
|
112
|
+
File.join( config.remote_dir, config.log_file_path )
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def ensure_data_is_hash data
|
117
|
+
if data.respond_to? :to_hash
|
118
|
+
data = data.to_hash
|
119
|
+
else
|
120
|
+
data = {}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def releaser
|
125
|
+
Releaser::Single.new
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
<!DOCTYPE html>
|
3
|
+
<html role="document">
|
4
|
+
<head>
|
5
|
+
<meta charset=utf-8>
|
6
|
+
<meta content=en name=language>
|
7
|
+
<meta content=IE=edge,chrome=1 http-equiv=X-UA-Compatible>
|
8
|
+
<meta content=width=device-width name=viewport>
|
9
|
+
<style type=text/css>
|
10
|
+
body {
|
11
|
+
background: #f9f9f9;
|
12
|
+
color: #5f5f5f;
|
13
|
+
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
14
|
+
-webkit-font-smoothing: antialiased;
|
15
|
+
margin: 0;
|
16
|
+
padding: 0;
|
17
|
+
}
|
18
|
+
h1 {
|
19
|
+
font-size: 3em;
|
20
|
+
margin: .5em 0;
|
21
|
+
line-height: 1;
|
22
|
+
}
|
23
|
+
.content {
|
24
|
+
max-width: 640px;
|
25
|
+
padding: 3em 1em;
|
26
|
+
margin: 0 auto;
|
27
|
+
text-align: center;
|
28
|
+
}
|
29
|
+
ul {
|
30
|
+
padding: 0;
|
31
|
+
outline: 1px solid #a6a6a6;
|
32
|
+
list-style: none;
|
33
|
+
display: inline-block;
|
34
|
+
}
|
35
|
+
li {
|
36
|
+
padding: 1em 1.5em;
|
37
|
+
border-bottom: 1px solid #c2c2c2;
|
38
|
+
text-align: left;
|
39
|
+
}
|
40
|
+
li:last-child {
|
41
|
+
border-bottom: none;
|
42
|
+
}
|
43
|
+
li a {
|
44
|
+
margin: 0;
|
45
|
+
padding: 0 1em 0 0;
|
46
|
+
display: inline-block;
|
47
|
+
font-weight: bold;
|
48
|
+
text-decoration: none;
|
49
|
+
color: inherit;
|
50
|
+
-o-transition: color .2s ease;
|
51
|
+
-moz-transition: color .2s ease;
|
52
|
+
-webkit-transition: color .2s ease;
|
53
|
+
transition: color .2s ease;
|
54
|
+
}
|
55
|
+
li a:hover {
|
56
|
+
color: #449A88;
|
57
|
+
}
|
58
|
+
li small {
|
59
|
+
margin: .25em 0 0 0;
|
60
|
+
padding: 0;
|
61
|
+
font-size: .75em;
|
62
|
+
display: inline-block;
|
63
|
+
color: #999;
|
64
|
+
}
|
65
|
+
</style>
|
66
|
+
</head>
|
67
|
+
<body>
|
68
|
+
<section class="content">
|
69
|
+
<h1>Branch Index</h1>
|
70
|
+
<ul><% releases.each do |release| %>
|
71
|
+
<li>
|
72
|
+
<a href="<%= release.href %>"><%= release.name %></a>
|
73
|
+
<small>updated: <%= Time.at(release.time).strftime('%A %b %d, %Y at %l:%M %P') %></small>
|
74
|
+
</li>
|
75
|
+
<% end %></ul>
|
76
|
+
</section>
|
77
|
+
</body>
|
78
|
+
</html>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Statistrano
|
4
|
+
module Deployment
|
5
|
+
module Strategy
|
6
|
+
class Branches
|
7
|
+
|
8
|
+
class Index
|
9
|
+
|
10
|
+
attr_reader :template, :releases
|
11
|
+
|
12
|
+
def initialize releases, template_path=nil
|
13
|
+
@releases = releases
|
14
|
+
@template = IO.read(template_path || File.expand_path("../index/template.html.erb", __FILE__))
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_html
|
18
|
+
ERB.new(template).result(ERBContext.new(releases).get_binding)
|
19
|
+
end
|
20
|
+
|
21
|
+
class ERBContext
|
22
|
+
|
23
|
+
attr_reader :releases
|
24
|
+
|
25
|
+
def initialize releases
|
26
|
+
@releases = releases
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_binding
|
30
|
+
binding
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
class Branches
|
5
|
+
|
6
|
+
#
|
7
|
+
# Manages the state of a single release for the manifest
|
8
|
+
#
|
9
|
+
class Release
|
10
|
+
|
11
|
+
attr_reader :name, :config, :options
|
12
|
+
|
13
|
+
# init a release
|
14
|
+
# @param name [String] name of the release
|
15
|
+
# @param config [Obj] the config object
|
16
|
+
# @param options [Hash] :time, :commit, & :link || :repo_url
|
17
|
+
def initialize name, config, options={}
|
18
|
+
@name = name
|
19
|
+
@config = config
|
20
|
+
@options = Util.symbolize_hash_keys options
|
21
|
+
end
|
22
|
+
|
23
|
+
def time
|
24
|
+
@_time ||= options.fetch(:time) { Time.now.to_i }
|
25
|
+
end
|
26
|
+
|
27
|
+
def commit
|
28
|
+
@_commit ||= options.fetch(:commit) { Asgit.current_commit }
|
29
|
+
end
|
30
|
+
|
31
|
+
def link
|
32
|
+
@_link ||= options.fetch(:link) { (options[:repo_url]) ? "#{options[:repo_url]}/tree/#{commit}" : nil }
|
33
|
+
end
|
34
|
+
|
35
|
+
def href
|
36
|
+
"http://#{name}.#{config.base_domain}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# # convert the release to an li element
|
40
|
+
# # @return [String]
|
41
|
+
# def as_li
|
42
|
+
# "<li>" +
|
43
|
+
# "<a href=\"http://#{name}.#{config.base_domain}\">#{name}</a>" +
|
44
|
+
# "<small>updated: #{Time.at(time).strftime('%A %b %d, %Y at %l:%M %P')}</small>" +
|
45
|
+
# "</li>"
|
46
|
+
# end
|
47
|
+
|
48
|
+
# def get_binding
|
49
|
+
# binding
|
50
|
+
# end
|
51
|
+
|
52
|
+
def to_hash
|
53
|
+
hash = {
|
54
|
+
name: name,
|
55
|
+
time: time,
|
56
|
+
commit: commit
|
57
|
+
}
|
58
|
+
hash.merge({ link: link }) if link
|
59
|
+
|
60
|
+
return hash
|
61
|
+
end
|
62
|
+
|
63
|
+
# convert the release to a json object
|
64
|
+
# @return [String]
|
65
|
+
def to_json
|
66
|
+
return to_hash.to_json
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
|
5
|
+
#
|
6
|
+
# Branches is for deployments that depend upon the
|
7
|
+
# current git branch, eg. doing feature branch deployments
|
8
|
+
#
|
9
|
+
class Branches < Base
|
10
|
+
register_strategy :branches
|
11
|
+
|
12
|
+
option :base_domain
|
13
|
+
option :public_dir, :call, Proc.new { Asgit.current_branch.to_slug }
|
14
|
+
option :post_deploy_task, Proc.new { |d|
|
15
|
+
d.push_current_release_to_manifest
|
16
|
+
d.generate_index
|
17
|
+
}
|
18
|
+
|
19
|
+
task :list, :list_releases, "List branches"
|
20
|
+
task :prune, :prune_releases, "Prune a branch"
|
21
|
+
task :generate_index, :generate_index, "Generate a branch index"
|
22
|
+
task :open, :open_url, "Open the current branch URL"
|
23
|
+
|
24
|
+
# output a list of the releases in manifest
|
25
|
+
# @return [Void]
|
26
|
+
def list_releases
|
27
|
+
remotes.each do |remote|
|
28
|
+
releases_data = sorted_release_data(remote)
|
29
|
+
releases_data.map! do |release|
|
30
|
+
"#{release[:name]} created at #{Time.at(release[:time]).strftime('%a %b %d, %Y at %l:%M %P')}"
|
31
|
+
end
|
32
|
+
Log.info remote.config.hostname.to_sym, *releases_data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# trim releases not in the manifest,
|
37
|
+
# get user input for removal of other releases
|
38
|
+
# @return [Void]
|
39
|
+
def prune_releases
|
40
|
+
candidate_releases = []
|
41
|
+
remotes.each do |remote|
|
42
|
+
prune_untracked_releases(remote)
|
43
|
+
candidate_releases.push *get_releases(remote)
|
44
|
+
candidate_releases.uniq!
|
45
|
+
end
|
46
|
+
|
47
|
+
if candidate_releases.length > 0
|
48
|
+
pick_and_remove_release candidate_releases
|
49
|
+
else
|
50
|
+
Log.warn "no releases to prune"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# generate an index file for releases in the manifest
|
55
|
+
# @return [Void]
|
56
|
+
def generate_index
|
57
|
+
remotes.each do |remote|
|
58
|
+
index_dir = File.join( remote.config.remote_dir, "index" )
|
59
|
+
index_path = File.join( index_dir, "index.html" )
|
60
|
+
remote.create_remote_dir index_dir
|
61
|
+
remote.run "touch #{index_path} && echo '#{release_list_html(remote)}' > #{index_path}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# push the current release into the manifest
|
66
|
+
# @return [Void]
|
67
|
+
def push_current_release_to_manifest
|
68
|
+
remotes.each do |remote|
|
69
|
+
mnfst = manifest(remote)
|
70
|
+
mnfst.put Release.new( config.public_dir, config ).to_hash, :name
|
71
|
+
mnfst.save!
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def manifest remote
|
78
|
+
@_manifests ||= {}
|
79
|
+
|
80
|
+
@_manifests.fetch( remote ) do
|
81
|
+
Deployment::Manifest.new remote.config.remote_dir, remote
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def pick_and_remove_release candidate_releases
|
86
|
+
picked_release = pick_release_to_remove candidate_releases
|
87
|
+
if picked_release
|
88
|
+
remotes.each do |remote|
|
89
|
+
if get_releases(remote).include? picked_release
|
90
|
+
remove_release(remote, picked_release)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
generate_index
|
94
|
+
else
|
95
|
+
Log.warn "sorry, that isn't one of the releases"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def pick_release_to_remove candidate_releases
|
100
|
+
list_releases_with_index candidate_releases
|
101
|
+
|
102
|
+
picked_release = Shell.get_input("select a release to remove: ").gsub(/[^0-9]/, '')
|
103
|
+
|
104
|
+
if !picked_release.empty? && picked_release.to_i < candidate_releases.length
|
105
|
+
return candidate_releases[picked_release.to_i]
|
106
|
+
else
|
107
|
+
return false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def list_releases_with_index releases
|
112
|
+
releases.each_with_index do |release,idx|
|
113
|
+
Log.info :"[#{idx}]", "#{release}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# removes releases that are on the remote but not in the manifest
|
118
|
+
# @return [Void]
|
119
|
+
def prune_untracked_releases remote
|
120
|
+
get_actual_releases(remote).each do |release|
|
121
|
+
remove_release(remote, release) unless get_releases(remote).include? release
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def release_list_html remote
|
126
|
+
releases = sorted_release_data(remote).map do |r|
|
127
|
+
name = r.fetch(:name)
|
128
|
+
r.merge({ repo_url: config.repo_url }) if config.repo_url
|
129
|
+
Release.new( name, config, r )
|
130
|
+
end
|
131
|
+
|
132
|
+
Index.new( releases ).to_html
|
133
|
+
end
|
134
|
+
|
135
|
+
def sorted_release_data remote
|
136
|
+
manifest(remote).data.sort_by do |r|
|
137
|
+
r[:time]
|
138
|
+
end.reverse
|
139
|
+
end
|
140
|
+
|
141
|
+
# remove a release
|
142
|
+
# @param name [String]
|
143
|
+
# @return [Void]
|
144
|
+
def remove_release remote, name
|
145
|
+
Log.info remote.config.hostname.to_sym, "Removing release '#{name}'"
|
146
|
+
remote.run "rm -rf #{release_path(name, remote)}"
|
147
|
+
manifest(remote).remove_if do |r|
|
148
|
+
r[:name] == name
|
149
|
+
end
|
150
|
+
manifest(remote).save!
|
151
|
+
end
|
152
|
+
|
153
|
+
# return array of releases from the manifest
|
154
|
+
# @return [Array]
|
155
|
+
def get_releases remote
|
156
|
+
sorted_release_data(remote).map { |r| r[:name] }
|
157
|
+
end
|
158
|
+
|
159
|
+
# return array of releases on the remote
|
160
|
+
# @return [Array]
|
161
|
+
def get_actual_releases remote
|
162
|
+
remote.run("ls -mp #{remote.config.remote_dir}")
|
163
|
+
.stdout.strip.split(',')
|
164
|
+
.keep_if { |release| /\/$/.match(release) }
|
165
|
+
.map { |release| release.strip.sub(/(\/$)/, '') }
|
166
|
+
.keep_if { |release| release != "index" }
|
167
|
+
end
|
168
|
+
|
169
|
+
# path to the current release
|
170
|
+
# this is based on the git branch
|
171
|
+
# @return [String]
|
172
|
+
def current_release_path
|
173
|
+
File.join( config.remote_dir, config.public_dir )
|
174
|
+
end
|
175
|
+
|
176
|
+
# path to a specific release
|
177
|
+
# @return [String]
|
178
|
+
def release_path name, remote
|
179
|
+
File.join( remote.config.remote_dir, name )
|
180
|
+
end
|
181
|
+
|
182
|
+
# open the current checked out branch
|
183
|
+
# @return [Void]
|
184
|
+
def open_url
|
185
|
+
if config.base_domain
|
186
|
+
url = "http://#{config.public_dir}.#{config.base_domain}"
|
187
|
+
system "open #{url}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
require_relative 'branches/index'
|
198
|
+
require_relative 'branches/release'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
|
5
|
+
module CheckGit
|
6
|
+
|
7
|
+
# Check if things are safe to deploy
|
8
|
+
# @return [Boolean]
|
9
|
+
def safe_to_deploy?
|
10
|
+
|
11
|
+
# if we don't want to check git
|
12
|
+
# we're good to go
|
13
|
+
if !config.check_git
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
|
17
|
+
# are there any uncommited changes?
|
18
|
+
if !Asgit.working_tree_clean?
|
19
|
+
Log.warn "You need to commit or stash your changes before deploying"
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
# make sure you're on the branch selected to check against
|
24
|
+
if Asgit.current_branch != config.git_branch
|
25
|
+
Log.warn "You shouldn't deploy from any branch but #{config.git_branch}"
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
# make sure you're up to date
|
30
|
+
if !Asgit.remote_up_to_date?
|
31
|
+
Log.warn "You need to update or push your changes before deploying"
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
# we passed all the checks
|
36
|
+
return true
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
|
5
|
+
module InvokeTasks
|
6
|
+
|
7
|
+
# Run the post_deploy_task
|
8
|
+
# return [Void]
|
9
|
+
def invoke_post_deploy_task
|
10
|
+
if config.post_deploy_task
|
11
|
+
Log.info :post_deploy, "Running the post deploy task"
|
12
|
+
call_or_invoke_task config.post_deploy_task
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Run the build_task supplied
|
17
|
+
# return [Void]
|
18
|
+
def invoke_build_task
|
19
|
+
Log.info :build, "Running the build task"
|
20
|
+
call_or_invoke_task config.build_task
|
21
|
+
end
|
22
|
+
|
23
|
+
# Run the pre_symlink_task if supplied
|
24
|
+
# return [Void]
|
25
|
+
def invoke_pre_symlink_task remote
|
26
|
+
if !remote.config.pre_symlink_task.nil?
|
27
|
+
Log.info :pre_symlink, "Running the pre_symlink task"
|
28
|
+
resp = call_or_invoke_task remote.config.pre_symlink_task, remote
|
29
|
+
if resp == false
|
30
|
+
Log.error :pre_symlink, "exiting due to falsy return"
|
31
|
+
abort()
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def call_or_invoke_task task, arg=nil
|
37
|
+
if task.respond_to? :call
|
38
|
+
if arg && task.arity == 2
|
39
|
+
task.call self, arg
|
40
|
+
elsif task.arity == 1
|
41
|
+
task.call self
|
42
|
+
else
|
43
|
+
task.call
|
44
|
+
end
|
45
|
+
else
|
46
|
+
Rake::Task[task].invoke
|
47
|
+
end
|
48
|
+
rescue Exception => e
|
49
|
+
Log.error "exiting due to error in task",
|
50
|
+
"#{e.class}: #{e}"
|
51
|
+
abort()
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Statistrano
|
2
|
+
module Deployment
|
3
|
+
module Strategy
|
4
|
+
|
5
|
+
# deployment type for running a releases deployment
|
6
|
+
# accross multiple remotes
|
7
|
+
#
|
8
|
+
# @example:
|
9
|
+
#
|
10
|
+
# define_deployment "multi", :releases do
|
11
|
+
# build_task 'deploy:build'
|
12
|
+
# local_dir 'build'
|
13
|
+
# remote_dir '/var/www/proj'
|
14
|
+
#
|
15
|
+
# check_git true
|
16
|
+
# git_branch 'master'
|
17
|
+
#
|
18
|
+
# remotes [
|
19
|
+
# { hostname: 'web01' },
|
20
|
+
# { hostname: 'web02' }
|
21
|
+
# ]
|
22
|
+
#
|
23
|
+
# # each remote gets merged with the global
|
24
|
+
# # configs and deployed to individually
|
25
|
+
# #
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
class Releases < Base
|
29
|
+
register_strategy :releases
|
30
|
+
|
31
|
+
option :pre_symlink_task, nil
|
32
|
+
option :release_count, 5
|
33
|
+
option :release_dir, "releases"
|
34
|
+
option :public_dir, "current"
|
35
|
+
|
36
|
+
option :remotes, []
|
37
|
+
|
38
|
+
task :deploy, :deploy, "Deploy to all remotes"
|
39
|
+
task :rollback, :rollback_release, "Rollback to the previous release"
|
40
|
+
task :prune, :prune_releases, "Prune releases to release count"
|
41
|
+
task :list, :list_releases, "List releases"
|
42
|
+
|
43
|
+
def initialize name
|
44
|
+
@name = name
|
45
|
+
end
|
46
|
+
|
47
|
+
def rollback_release
|
48
|
+
remotes.each do |remote|
|
49
|
+
releaser.rollback_release remote
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def prune_releases
|
54
|
+
remotes.each do |remote|
|
55
|
+
releaser.prune_releases remote
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_releases
|
60
|
+
remotes.each do |remote|
|
61
|
+
releases = releaser.list_releases(remote).map { |rel| rel[:release] }
|
62
|
+
Log.info :"#{remote.config.hostname}", releases
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def releaser
|
69
|
+
Releaser::Revisions.new
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|