statistrano 1.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/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
|