dk-abdeploy 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA512:
3
- data.tar.gz: e68d988dd930229c983f361d1cd75ef9c7addd22029082bdf4c9535e1790b7eb05e4ca9ef6fc72a3b6cace056bd4a2ead6c9ac4319acb02d30e7fe880efb96b2
4
- metadata.gz: 4b68931c278d1b5d7cefc95c7ddec5b816237432733e4f4fd712f27439e1e6d7a21b042aa8600db653f69bfc5624211afcbb603e7f5ee6576b5680e0c38ca6af
3
+ data.tar.gz: f3ce66fba1bb1953197ff2dab9dbe0108fbf608041f8870c6990f48006a6e44f68e142bee666207379539b3ed8a5591a2ed09a12dbcabea90040d6ef08cff635
4
+ metadata.gz: bfaa114075d524c5f51b334e799f1a388079582040bfdc0ec643d6286773f8c743637177dcb58b1fcf6ff99709907be5df17c20140bab2433ebe59d300020d8a
5
5
  SHA1:
6
- data.tar.gz: 95fa2def0135c8e828f944c2a1f796df7fe9eb92
7
- metadata.gz: 151800353f028be1d8d7f8a4988248586eed4d0a
6
+ data.tar.gz: e00eb0f3da66b17a199f313bead21ab34940271c
7
+ metadata.gz: 3888b89935192e1a09c3f6520b5cbe179bcc58c3
data/README.md CHANGED
@@ -1,29 +1,102 @@
1
1
  # Dk::ABDeploy
2
2
 
3
- [Dk](https://github.com/redding/dk) tasks that implement the A/B deploy pattern.
3
+ [Dk](https://github.com/redding/dk) tasks that implement the A/B deploy scheme.
4
4
 
5
5
  ## Deploy scheme design
6
6
 
7
7
  Two release dirs, A and B, each have a clone of the app's git repo. On deploy, whichever release dir is not current is fetched and reset to the deploy commit. After the source is updated and ready, the current pointer is updated to the new release dir.
8
8
 
9
- This allows for the current running processes to remain running against the previous source while the new source is updated, migrated, built, etc. Once the new source is ready, the point is updated and everything can be restarted.
9
+ This allows for the current running processes to remain running against the previous source while the new source is updated, migrated, built, etc. Once the new source is ready, the pointer is updated and everything can be restarted.
10
10
 
11
- This has a few advantages over a more tradition Cap deploy (for example):
11
+ This has a few advantages over a more tradition single-clone deploy scheme:
12
12
 
13
- * This removes the need to copy the entire source tree after a single common repo has been updated.
13
+ * This removes the need to copy the entire source tree after the single common repo has been updated.
14
14
  * There is no need to calculate release dir names with dates/etc and manage them (ie clean out previous old release dirs)
15
- * This works will with branch deploys as there is no need for any complex rollback logic - just deploy a previous commit to rollback.
15
+ * This works well with branch deploys as there is no need for any complex rollback logic - just deploy a previous commit to rollback.
16
16
  * You retain all the benefits of a current pointer that is set to the previous deploy source while the new source is being built/setup.
17
17
 
18
18
  ## Usage
19
19
 
20
- TODO: Write code samples and usage instructions here
20
+ Here is an example "deploy" task that uses dk-abdeploy tasks to compose it's logic:
21
+
22
+ ```ruby
23
+ # in config/dk.rb or whatever
24
+ require 'dk'
25
+ require 'dk-abdeploy'
26
+
27
+ class MyDeployTask
28
+ incude Dk::Task
29
+
30
+ desc "deploy my code"
31
+
32
+ ssh_hosts 'my_servers'
33
+
34
+ def run!
35
+ # set any required dk-abdeploy params before validating
36
+ set_param(Dk::ABDeploy::ROOT_PARAM_NAME, params['deploy_dir'])
37
+ set_param(Dk::ABDeploy::REPO_PARAM_NAME, params['repo_url'])
38
+ set_param(Dk::ABDeploy::REF_PARAM_NAME, 'origin/master')
39
+ set_param(
40
+ Dk::ABDeploy::PRIMARY_SSH_HOST_PARAM_NAME,
41
+ ssh_hosts('my_servers').first
42
+ )
43
+
44
+ # validate the deploy params, config, etc (safe to run on each deploy)
45
+ run_task Dk::ABDeploy::Validate
46
+
47
+ # do post-validate custom logic (like maybe setting more friendly param names?)
48
+ set_param('shared_dir', params[Dk::ABDeploy::SHARED_DIR_PARAM_NAME])
49
+ set_param('current_dir', params[Dk::ABDeploy::CURRENT_DIR_PARAM_NAME])
50
+
51
+ # setup the deploy dirs, etc (safe to run on each deploy)
52
+ run_task Dk::ABDeploy::Setup
53
+
54
+ # update the source in either the A or B release dir (whichever is not current)
55
+ run_task Dk::ABDeploy::Update
56
+
57
+ # do any custom post-source-update logic like:
58
+ # - set more "friendly" param names
59
+ # - bundle
60
+ # - build assets
61
+ # - etc
62
+ set_param('deploy_release_dir', release_dir)
63
+
64
+ # symlink the release dir that was just updated as the "current"
65
+ run_task Dk::ABDeploy::Link
66
+
67
+ # do any post-symlink logic like (restarting processes, etc)
68
+
69
+ # cleanup the deploy
70
+ # (gets the non-updated release dir on the same commit as the updated release dir)
71
+ run_task Dk::ABDeploy::Cleanup
72
+ end
73
+
74
+ end
75
+
76
+ Dk.configure do
77
+ task 'deploy', MyDeployTask
78
+
79
+ set_param 'repo_url', 'some-repo-rul'
80
+ set_param 'deploy_dir', '/path/to/my/code'
81
+
82
+ ssh_hosts 'my_servers', 'myhost1.example.com',
83
+ 'myhost2.example.com'
84
+ end
85
+ ```
86
+
87
+ then...
88
+
89
+ ```
90
+ $ dk -T
91
+ deploy # deploy my code
92
+ $ dk deploy
93
+ ```
21
94
 
22
95
  ## Notes
23
96
 
24
- The scope of the tasks provided here are fairly limited. This makes no assumptions about your app ie. whether it is Rails or not, whether it is a web app or not, what subdirs it has, etc. This just covers the basics of setting up the pattern, updating the source and resetting the current pointer. That's it.
97
+ The scope of the tasks provided here is fairly limited. This makes no assumptions about your app ie. whether it is Rails or not, whether it is a web app or not, what subdirs it has, etc. This just covers the basics of setting up the scheme, updating the source and resetting the current pointer. That's it.
25
98
 
26
- Layer these tasks as callbacks in your app's specific deploy scheme and do all app-specific logic in those tasks.
99
+ Layer these tasks in your app's specific deploy scheme as callbacks and do all app-specific logic in those tasks.
27
100
 
28
101
  ## Installation
29
102
 
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Dk::ABDeploy::VERSION
9
9
  gem.authors = ["Kelly Redding", "Collin Redding"]
10
10
  gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
11
- gem.summary = "Dk tasks that implement the A/B deploy pattern"
12
- gem.description = "Dk tasks that implement the A/B deploy pattern"
11
+ gem.summary = "Dk tasks that implement the A/B deploy scheme"
12
+ gem.description = "Dk tasks that implement the A/B deploy scheme"
13
13
  gem.homepage = "https://github.com/redding/dk-abdeploy"
14
14
  gem.license = 'MIT'
15
15
 
@@ -18,6 +18,9 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_development_dependency("assert", ["~> 2.16.1"])
21
+ gem.add_development_dependency("assert", ["~> 2.16.3"])
22
+
23
+ gem.add_dependency("dk", ["~> 0.1.0"])
24
+ gem.add_dependency("much-plugin", ["~> 0.2.0"])
22
25
 
23
26
  end
@@ -1,5 +1,9 @@
1
1
  require "dk-abdeploy/version"
2
+ require "dk-abdeploy/constants"
2
3
 
3
- module Dk; end
4
- module Dk::ABDeploy
5
- end
4
+ # require the tasks for convenience
5
+ require 'dk-abdeploy/cleanup'
6
+ require 'dk-abdeploy/link'
7
+ require 'dk-abdeploy/update'
8
+ require 'dk-abdeploy/setup'
9
+ require 'dk-abdeploy/validate'
@@ -0,0 +1,40 @@
1
+ require 'much-plugin'
2
+ require 'dk/task'
3
+ require "dk-abdeploy/constants"
4
+ require "dk-abdeploy/update"
5
+ require 'dk-abdeploy/validate'
6
+
7
+ module Dk::ABDeploy
8
+
9
+ class Cleanup
10
+ include Dk::Task
11
+
12
+ desc "(dk-abdeploy) update the non-deploy release's source post-update"
13
+
14
+ before Validate
15
+
16
+ ssh_hosts SSH_HOSTS_GROUP_NAME
17
+
18
+ def run!
19
+ # current release dir is the one that was current pre-update - the
20
+ # non-deploy release dir
21
+ if params[CURRENT_RELEASE_DIR_PARAM_NAME].to_s.empty?
22
+ raise ArgumentError, "no #{CURRENT_RELEASE_DIR_PARAM_NAME.inspect} param set"
23
+ end
24
+ if params[REF_PARAM_NAME].to_s.empty?
25
+ raise ArgumentError, "no #{REF_PARAM_NAME.inspect} param set"
26
+ end
27
+
28
+ # reset the non-deploy release git repo
29
+ ssh! git_reset_cmd_str(params[CURRENT_RELEASE_DIR_PARAM_NAME], params[REF_PARAM_NAME])
30
+ end
31
+
32
+ private
33
+
34
+ def git_reset_cmd_str(repo_dir, ref)
35
+ Update.git_reset_cmd_str(repo_dir, ref)
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,25 @@
1
+ module Dk; end
2
+ module Dk::ABDeploy
3
+
4
+ SHARED_DIR_NAME = 'shared'.freeze
5
+ RELEASES_DIR_NAME = 'releases'.freeze
6
+ RELEASE_A_DIR_NAME = 'A'.freeze
7
+ RELEASE_B_DIR_NAME = 'B'.freeze
8
+ CURRENT_LINK_NAME = 'current'.freeze
9
+
10
+ ROOT_PARAM_NAME = 'dk_abdeploy_root'.freeze
11
+ SHARED_DIR_PARAM_NAME = 'dk_abdeploy_shared_dir'.freeze
12
+ CURRENT_DIR_PARAM_NAME = 'dk_abdeploy_current_dir'.freeze
13
+ RELEASES_DIR_PARAM_NAME = 'dk_abdeploy_releases_dir'.freeze
14
+ RELEASE_A_DIR_PARAM_NAME = 'dk_abdeploy_release_a_dir'.freeze
15
+ RELEASE_B_DIR_PARAM_NAME = 'dk_abdeploy_release_b_dir'.freeze
16
+ CURRENT_RELEASE_DIR_PARAM_NAME = 'dk_abdeploy_current_release_dir'.freeze
17
+ DEPLOY_RELEASE_DIR_PARAM_NAME = 'dk_abdeploy_deploy_release_dir'.freeze
18
+ REPO_PARAM_NAME = 'dk_abdeploy_repo'.freeze
19
+ REF_PARAM_NAME = 'dk_abdeploy_ref'.freeze
20
+ PRIMARY_SSH_HOST_PARAM_NAME = 'dk_abdeploy_primary_ssh_host'.freeze
21
+
22
+ SSH_HOSTS_GROUP_NAME = 'dk_abdeploy_servers'.freeze
23
+
24
+ end
25
+
@@ -0,0 +1,30 @@
1
+ require 'dk/task'
2
+ require "dk-abdeploy/constants"
3
+ require 'dk-abdeploy/validate'
4
+
5
+ module Dk::ABDeploy
6
+
7
+ class Link
8
+ include Dk::Task
9
+
10
+ desc "(dk-abdeploy) link the deploy release dir as the current dir"
11
+
12
+ before Validate
13
+
14
+ ssh_hosts SSH_HOSTS_GROUP_NAME
15
+
16
+ def run!
17
+ # validate required params are set
18
+ if params[DEPLOY_RELEASE_DIR_PARAM_NAME].to_s.empty?
19
+ raise ArgumentError, "no #{DEPLOY_RELEASE_DIR_PARAM_NAME.inspect} param set"
20
+ end
21
+
22
+ # link the deploy release dir as the current dir
23
+ curr_dir = params[CURRENT_DIR_PARAM_NAME]
24
+ release_dir = params[DEPLOY_RELEASE_DIR_PARAM_NAME]
25
+ ssh! "rm -f #{curr_dir} && ln -s #{release_dir} #{curr_dir}"
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,43 @@
1
+ require 'dk/task'
2
+ require "dk-abdeploy/constants"
3
+ require 'dk-abdeploy/validate'
4
+
5
+ module Dk::ABDeploy
6
+
7
+ class Setup
8
+ include Dk::Task
9
+
10
+ desc "(dk-abdeploy) create the dirs and clone the repos for the A/B deploy scheme"
11
+
12
+ before Validate
13
+
14
+ ssh_hosts SSH_HOSTS_GROUP_NAME
15
+
16
+ def run!
17
+ # make the expected dirs if not already made
18
+ mkdirs = [
19
+ params[ROOT_PARAM_NAME].to_s,
20
+ params[SHARED_DIR_PARAM_NAME],
21
+ params[RELEASES_DIR_PARAM_NAME],
22
+ params[RELEASE_A_DIR_PARAM_NAME],
23
+ params[RELEASE_B_DIR_PARAM_NAME]
24
+ ]
25
+ ssh! "mkdir -p #{mkdirs.join(' ')}"
26
+
27
+ # clone the A/B release repos if not already cloned
28
+ ssh! clone_cmd_str(params[REPO_PARAM_NAME], params[RELEASE_A_DIR_PARAM_NAME])
29
+ ssh! clone_cmd_str(params[REPO_PARAM_NAME], params[RELEASE_B_DIR_PARAM_NAME])
30
+ end
31
+
32
+ private
33
+
34
+ def clone_cmd_str(repo, release_dir)
35
+ "if [ -d #{release_dir}/.git ]; " \
36
+ "then echo 'repo already cloned'; " \
37
+ "else git clone -q #{repo} #{release_dir}; " \
38
+ "fi"
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,90 @@
1
+ require 'much-plugin'
2
+ require 'dk/task'
3
+ require "dk-abdeploy/constants"
4
+ require 'dk-abdeploy/validate'
5
+
6
+ module Dk::ABDeploy
7
+
8
+ class Update
9
+ include Dk::Task
10
+
11
+ desc "(dk-abdeploy) update the non-current release's source"
12
+
13
+ before Validate
14
+
15
+ ssh_hosts SSH_HOSTS_GROUP_NAME
16
+
17
+ def self.readlink_cmd_str(link)
18
+ "readlink #{link}"
19
+ end
20
+
21
+ def self.git_reset_cmd_str(repo_dir, ref)
22
+ "cd #{repo_dir} && " \
23
+ "git fetch -q origin && " \
24
+ "git reset -q --hard #{ref} && " \
25
+ "git clean -q -d -x -f"
26
+ end
27
+
28
+ def run!
29
+ # validate required params are set
30
+ if params[REF_PARAM_NAME].to_s.empty?
31
+ raise ArgumentError, "no #{REF_PARAM_NAME.inspect} param set"
32
+ end
33
+ if params[PRIMARY_SSH_HOST_PARAM_NAME].to_s.empty?
34
+ raise ArgumentError, "no #{PRIMARY_SSH_HOST_PARAM_NAME.inspect} param set"
35
+ end
36
+
37
+ # lookup the current release dir; set current/deploy release dir params
38
+ release_dirs = [
39
+ params[RELEASE_A_DIR_PARAM_NAME],
40
+ params[RELEASE_B_DIR_PARAM_NAME]
41
+ ]
42
+
43
+ rl_ssh = ssh(readlink_cmd_str(params[CURRENT_DIR_PARAM_NAME]), {
44
+ :hosts => params[PRIMARY_SSH_HOST_PARAM_NAME]
45
+ })
46
+ current_dir = (o = rl_ssh.stdout.strip).empty? ? release_dirs.last : o
47
+ set_param(CURRENT_RELEASE_DIR_PARAM_NAME, current_dir)
48
+
49
+ release_dirs.delete(params[CURRENT_RELEASE_DIR_PARAM_NAME])
50
+ set_param(DEPLOY_RELEASE_DIR_PARAM_NAME, release_dirs.first)
51
+
52
+ # reset the deploy release git repo
53
+ ssh! git_reset_cmd_str(params[DEPLOY_RELEASE_DIR_PARAM_NAME], params[REF_PARAM_NAME])
54
+ end
55
+
56
+ private
57
+
58
+ def readlink_cmd_str(link)
59
+ self.class.readlink_cmd_str(link)
60
+ end
61
+
62
+ def git_reset_cmd_str(repo_dir, ref)
63
+ self.class.git_reset_cmd_str(repo_dir, ref)
64
+ end
65
+
66
+ module TestHelpers
67
+ include MuchPlugin
68
+
69
+ plugin_included do
70
+ include Dk::Task::TestHelpers
71
+ include Dk::ABDeploy::Validate::TestHelpers
72
+
73
+ setup do
74
+ release_dirs = [
75
+ @params[Dk::ABDeploy::RELEASE_A_DIR_PARAM_NAME],
76
+ @params[Dk::ABDeploy::RELEASE_B_DIR_PARAM_NAME]
77
+ ]
78
+
79
+ @params[Dk::ABDeploy::CURRENT_RELEASE_DIR_PARAM_NAME] ||= release_dirs.sample
80
+ release_dirs.delete(@params[Dk::ABDeploy::CURRENT_RELEASE_DIR_PARAM_NAME])
81
+ @params[Dk::ABDeploy::DEPLOY_RELEASE_DIR_PARAM_NAME] ||= release_dirs.first
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,24 @@
1
+ module Dk; end
2
+ module Dk::ABDeploy; end
3
+ module Dk::ABDeploy::Utils
4
+
5
+ module CurrentGitBranch
6
+
7
+ def self.new(&block)
8
+ git_cmd_str = "git symbolic-ref HEAD"
9
+
10
+ # returns the cmd str if no block given
11
+ return git_cmd_str if block.nil?
12
+
13
+ # to get the value pass a block that runs the yielded cmd_str cmd, ex:
14
+ # current_branch_name = CurrentGitBranch.new do |cmd_str|
15
+ # log_info "Fetching current git branch from HEAD"
16
+ # cmd! cmd_str
17
+ # end
18
+ cmd = block.call(git_cmd_str)
19
+ cmd.stdout.split('/').last.strip
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,87 @@
1
+ require 'much-plugin'
2
+ require 'pathname'
3
+ require 'dk/task'
4
+ require "dk-abdeploy/constants"
5
+
6
+ module Dk::ABDeploy
7
+
8
+ class Validate
9
+ include Dk::Task
10
+
11
+ desc "(dk-abdeploy) validate the required dk-abdeploy params"
12
+
13
+ run_only_once true
14
+
15
+ def run!
16
+ # validate required params are set
17
+ if params[ROOT_PARAM_NAME].to_s.empty?
18
+ raise ArgumentError, "no #{ROOT_PARAM_NAME.inspect} param set"
19
+ end
20
+ if params[REPO_PARAM_NAME].to_s.empty?
21
+ raise ArgumentError, "no #{REPO_PARAM_NAME.inspect} param set"
22
+ end
23
+
24
+ # make sure the hosts group has been set
25
+ if (h = ssh_hosts(SSH_HOSTS_GROUP_NAME)).nil? || h.empty?
26
+ raise ArgumentError, "no #{SSH_HOSTS_GROUP_NAME.inspect} have been set"
27
+ end
28
+
29
+ # set common required params for downstream tasks
30
+ deploy_root = Pathname.new(params[ROOT_PARAM_NAME])
31
+ set_param(SHARED_DIR_PARAM_NAME, deploy_root.join(SHARED_DIR_NAME).to_s)
32
+ set_param(CURRENT_DIR_PARAM_NAME, deploy_root.join(CURRENT_LINK_NAME).to_s)
33
+
34
+ releases_dir = deploy_root.join(RELEASES_DIR_NAME)
35
+ set_param(RELEASES_DIR_PARAM_NAME, releases_dir.to_s)
36
+ set_param(RELEASE_A_DIR_PARAM_NAME, releases_dir.join(RELEASE_A_DIR_NAME).to_s)
37
+ set_param(RELEASE_B_DIR_PARAM_NAME, releases_dir.join(RELEASE_B_DIR_NAME).to_s)
38
+ end
39
+
40
+ module TestHelpers
41
+ include MuchPlugin
42
+
43
+ plugin_included do
44
+ include Dk::Task::TestHelpers
45
+
46
+ setup do
47
+ @dk_abdeploy_root ||= Factory.path
48
+ @dk_abdeploy_repo ||= Factory.string
49
+
50
+ @dk_abdeploy_shared ||= File.join(
51
+ @dk_abdeploy_root,
52
+ Dk::ABDeploy::SHARED_DIR_NAME
53
+ )
54
+ @dk_abdeploy_current ||= File.join(
55
+ @dk_abdeploy_root,
56
+ Dk::ABDeploy::CURRENT_LINK_NAME
57
+ )
58
+ @dk_abdeploy_releases ||= File.join(
59
+ @dk_abdeploy_root,
60
+ Dk::ABDeploy::RELEASES_DIR_NAME
61
+ )
62
+ @dk_abdeploy_release_a ||= File.join(
63
+ @dk_abdeploy_releases,
64
+ Dk::ABDeploy::RELEASE_A_DIR_NAME
65
+ )
66
+ @dk_abdeploy_release_b ||= File.join(
67
+ @dk_abdeploy_releases,
68
+ Dk::ABDeploy::RELEASE_B_DIR_NAME
69
+ )
70
+
71
+ @params ||= {}
72
+ @params[Dk::ABDeploy::ROOT_PARAM_NAME] ||= @dk_abdeploy_root
73
+ @params[Dk::ABDeploy::REPO_PARAM_NAME] ||= @dk_abdeploy_repo
74
+ @params[Dk::ABDeploy::SHARED_DIR_PARAM_NAME] ||= @dk_abdeploy_shared
75
+ @params[Dk::ABDeploy::CURRENT_DIR_PARAM_NAME] ||= @dk_abdeploy_current
76
+ @params[Dk::ABDeploy::RELEASES_DIR_PARAM_NAME] ||= @dk_abdeploy_releases
77
+ @params[Dk::ABDeploy::RELEASE_A_DIR_PARAM_NAME] ||= @dk_abdeploy_release_a
78
+ @params[Dk::ABDeploy::RELEASE_B_DIR_PARAM_NAME] ||= @dk_abdeploy_release_b
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ end