recap 0.1.0 → 0.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.
Files changed (61) hide show
  1. data/.gitignore +3 -0
  2. data/.travis.yml +4 -0
  3. data/README.md +12 -3
  4. data/Rakefile +8 -0
  5. data/Vagrantfile +61 -0
  6. data/doc/index.html +45 -26
  7. data/doc/lib/recap/bootstrap.html +42 -0
  8. data/doc/lib/recap/bundler.html +36 -19
  9. data/doc/lib/recap/capistrano_extensions.html +28 -23
  10. data/doc/lib/recap/cli.html +3 -0
  11. data/doc/lib/recap/compatibility.html +6 -3
  12. data/doc/lib/recap/deploy.html +41 -86
  13. data/doc/lib/recap/env.html +6 -1
  14. data/doc/lib/recap/foreman.html +3 -0
  15. data/doc/lib/recap/namespace.html +42 -0
  16. data/doc/lib/recap/preflight.html +12 -7
  17. data/doc/lib/recap/rails.html +3 -0
  18. data/doc/lib/recap/version.html +3 -0
  19. data/doc/lib/recap.html +42 -0
  20. data/features/bundling-gems.feature +18 -0
  21. data/features/deploying-projects.feature +21 -0
  22. data/features/managing-processes.feature +17 -0
  23. data/features/setting-environment-variables.feature +21 -0
  24. data/features/steps/capistrano_steps.rb +98 -0
  25. data/features/support/project.rb +211 -0
  26. data/features/support/server.rb +53 -0
  27. data/features/templates/gem/binary.erb +43 -0
  28. data/features/templates/gem/gemspec.erb +11 -0
  29. data/features/templates/project/Capfile +21 -0
  30. data/features/templates/project/Capfile.erb +21 -0
  31. data/features/templates/project/Gemfile.erb +7 -0
  32. data/features/templates/project/Procfile.erb +1 -0
  33. data/index.rb +26 -17
  34. data/lib/recap/bootstrap.rb +47 -0
  35. data/lib/recap/bundler.rb +31 -21
  36. data/lib/recap/capistrano_extensions.rb +11 -9
  37. data/lib/recap/cli.rb +1 -1
  38. data/lib/recap/compatibility.rb +3 -3
  39. data/lib/recap/deploy.rb +45 -57
  40. data/lib/recap/env.rb +30 -26
  41. data/lib/recap/environment.rb +54 -0
  42. data/lib/recap/foreman.rb +28 -9
  43. data/lib/recap/namespace.rb +37 -0
  44. data/lib/recap/preflight.rb +10 -8
  45. data/lib/recap/rails.rb +6 -4
  46. data/lib/recap/ruby.rb +3 -0
  47. data/lib/recap/static.rb +1 -0
  48. data/lib/recap/version.rb +1 -1
  49. data/lib/recap.rb +12 -0
  50. data/recap.gemspec +8 -4
  51. data/spec/models/environment_spec.rb +143 -0
  52. data/spec/spec_helper.rb +7 -0
  53. data/spec/tasks/bootstrap_spec.rb +34 -0
  54. data/spec/tasks/bundler_spec.rb +126 -0
  55. data/spec/tasks/deploy_spec.rb +209 -0
  56. data/spec/tasks/env_spec.rb +38 -0
  57. data/spec/tasks/foreman_spec.rb +154 -0
  58. data/test-vm/manifests/base.pp +17 -0
  59. data/test-vm/share/.gitkeep +0 -0
  60. metadata +138 -19
  61. /data/bin/{tomafro-deploy → recap} +0 -0
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
5
+ <title>namespace.rb</title>
6
+ <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
7
+ </head>
8
+ <body>
9
+ <div id='container'>
10
+ <div id="background"></div>
11
+ <div id="jump_to">
12
+ Jump To &hellip;
13
+ <div id="jump_wrapper">
14
+ <div id="jump_page">
15
+ <a class="source" href="../../index.html">index.rb</a>
16
+ <a class="source" href="../recap.html">recap.rb</a>
17
+ <a class="source" href="bootstrap.html">bootstrap.rb</a>
18
+ <a class="source" href="bundler.html">bundler.rb</a>
19
+ <a class="source" href="capistrano_extensions.html">capistrano_extensions.rb</a>
20
+ <a class="source" href="cli.html">cli.rb</a>
21
+ <a class="source" href="compatibility.html">compatibility.rb</a>
22
+ <a class="source" href="deploy.html">deploy.rb</a>
23
+ <a class="source" href="env.html">env.rb</a>
24
+ <a class="source" href="foreman.html">foreman.rb</a>
25
+ <a class="source" href="namespace.html">namespace.rb</a>
26
+ <a class="source" href="preflight.html">preflight.rb</a>
27
+ <a class="source" href="rails.html">rails.rb</a>
28
+ <a class="source" href="version.html">version.rb</a>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <table cellspacing=0 cellpadding=0>
33
+ <thead>
34
+ <tr>
35
+ <th class=docs><h1>namespace.rb</h1></th>
36
+ <th class=code></th>
37
+ </tr>
38
+ </thead>
39
+ <tbody>
40
+ </table>
41
+ </div>
42
+ </body>
@@ -13,6 +13,8 @@
13
13
  <div id="jump_wrapper">
14
14
  <div id="jump_page">
15
15
  <a class="source" href="../../index.html">index.rb</a>
16
+ <a class="source" href="../recap.html">recap.rb</a>
17
+ <a class="source" href="bootstrap.html">bootstrap.rb</a>
16
18
  <a class="source" href="bundler.html">bundler.rb</a>
17
19
  <a class="source" href="capistrano_extensions.html">capistrano_extensions.rb</a>
18
20
  <a class="source" href="cli.html">cli.rb</a>
@@ -20,6 +22,7 @@
20
22
  <a class="source" href="deploy.html">deploy.rb</a>
21
23
  <a class="source" href="env.html">env.rb</a>
22
24
  <a class="source" href="foreman.html">foreman.rb</a>
25
+ <a class="source" href="namespace.html">namespace.rb</a>
23
26
  <a class="source" href="preflight.html">preflight.rb</a>
24
27
  <a class="source" href="rails.html">rails.rb</a>
25
28
  <a class="source" href="version.html">version.rb</a>
@@ -59,7 +62,10 @@ application specific files. Each deploying user should be added to the applicat
59
62
  should a check fail.</p>
60
63
  </td>
61
64
  <td class=code>
62
- <div class='highlight'><pre><span class="no">Capistrano</span><span class="o">::</span><span class="no">Configuration</span><span class="o">.</span><span class="n">instance</span><span class="p">(</span><span class="ss">:must_exist</span><span class="p">)</span><span class="o">.</span><span class="n">load</span> <span class="k">do</span></pre></div>
65
+ <div class='highlight'><pre><span class="k">module</span> <span class="nn">Recap::Preflight</span>
66
+ <span class="kp">extend</span> <span class="no">Recap</span><span class="o">::</span><span class="no">Namespace</span>
67
+
68
+ <span class="n">namespace</span> <span class="ss">:preflight</span> <span class="k">do</span></pre></div>
63
69
  </td>
64
70
  </tr>
65
71
  <tr id='section-2'>
@@ -70,12 +76,11 @@ should a check fail.</p>
70
76
  <p>The preflight check is pretty quick, so run it before every <code>deploy:setup</code> and <code>deploy</code></p>
71
77
  </td>
72
78
  <td class=code>
73
- <div class='highlight'><pre> <span class="n">before</span> <span class="s1">&#39;deploy:setup&#39;</span><span class="p">,</span> <span class="s1">&#39;preflight:check&#39;</span>
74
- <span class="n">before</span> <span class="s1">&#39;deploy&#39;</span><span class="p">,</span> <span class="s1">&#39;preflight:check&#39;</span>
79
+ <div class='highlight'><pre> <span class="n">before</span> <span class="s1">&#39;deploy:setup&#39;</span><span class="p">,</span> <span class="s1">&#39;preflight:check&#39;</span>
80
+ <span class="n">before</span> <span class="s1">&#39;deploy&#39;</span><span class="p">,</span> <span class="s1">&#39;preflight:check&#39;</span>
75
81
 
76
- <span class="n">set</span><span class="p">(</span><span class="ss">:remote_username</span><span class="p">)</span> <span class="p">{</span> <span class="n">capture</span><span class="p">(</span><span class="s1">&#39;whoami&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> <span class="p">}</span>
82
+ <span class="n">set</span><span class="p">(</span><span class="ss">:remote_username</span><span class="p">)</span> <span class="p">{</span> <span class="n">capture</span><span class="p">(</span><span class="s1">&#39;whoami&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> <span class="p">}</span>
77
83
 
78
- <span class="n">namespace</span> <span class="ss">:preflight</span> <span class="k">do</span>
79
84
  <span class="n">task</span> <span class="ss">:check</span> <span class="k">do</span></pre></div>
80
85
  </td>
81
86
  </tr>
@@ -87,7 +92,7 @@ should a check fail.</p>
87
92
  <p>First check the <code>application_user</code> exists</p>
88
93
  </td>
89
94
  <td class=code>
90
- <div class='highlight'><pre> <span class="k">if</span> <span class="n">capture</span><span class="p">(</span><span class="s2">&quot;id </span><span class="si">#{</span><span class="n">application_user</span><span class="si">}</span><span class="s2"> &gt; /dev/null 2&gt;&amp;1; echo $?&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> <span class="o">!=</span> <span class="s2">&quot;0&quot;</span>
95
+ <div class='highlight'><pre> <span class="k">if</span> <span class="n">exit_code</span><span class="p">(</span><span class="s2">&quot;id </span><span class="si">#{</span><span class="n">application_user</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> <span class="o">!=</span> <span class="s2">&quot;0&quot;</span>
91
96
  <span class="nb">abort</span> <span class="sx">%{</span>
92
97
  <span class="sx">The application user &#39;</span><span class="si">#{</span><span class="n">application_user</span><span class="si">}</span><span class="sx">&#39; doesn&#39;t exist. You can create this user by logging into the server and running:</span>
93
98
 
@@ -104,7 +109,7 @@ should a check fail.</p>
104
109
  <p>Then the <code>application_group</code></p>
105
110
  </td>
106
111
  <td class=code>
107
- <div class='highlight'><pre> <span class="k">if</span> <span class="n">capture</span><span class="p">(</span><span class="s2">&quot;id -g </span><span class="si">#{</span><span class="n">application_group</span><span class="si">}</span><span class="s2"> &gt; /dev/null 2&gt;&amp;1; echo $?&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span> <span class="o">!=</span> <span class="s2">&quot;0&quot;</span>
112
+ <div class='highlight'><pre> <span class="k">if</span> <span class="n">exit_code</span><span class="p">(</span><span class="s2">&quot;id -g </span><span class="si">#{</span><span class="n">application_group</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> <span class="o">!=</span> <span class="s2">&quot;0&quot;</span>
108
113
  <span class="nb">abort</span> <span class="sx">%{</span>
109
114
  <span class="sx">The application group &#39;</span><span class="si">#{</span><span class="n">application_group</span><span class="si">}</span><span class="sx">&#39; doesn&#39;t exist. You can create this group by logging into the server and running:</span>
110
115
 
@@ -13,6 +13,8 @@
13
13
  <div id="jump_wrapper">
14
14
  <div id="jump_page">
15
15
  <a class="source" href="../../index.html">index.rb</a>
16
+ <a class="source" href="../recap.html">recap.rb</a>
17
+ <a class="source" href="bootstrap.html">bootstrap.rb</a>
16
18
  <a class="source" href="bundler.html">bundler.rb</a>
17
19
  <a class="source" href="capistrano_extensions.html">capistrano_extensions.rb</a>
18
20
  <a class="source" href="cli.html">cli.rb</a>
@@ -20,6 +22,7 @@
20
22
  <a class="source" href="deploy.html">deploy.rb</a>
21
23
  <a class="source" href="env.html">env.rb</a>
22
24
  <a class="source" href="foreman.html">foreman.rb</a>
25
+ <a class="source" href="namespace.html">namespace.rb</a>
23
26
  <a class="source" href="preflight.html">preflight.rb</a>
24
27
  <a class="source" href="rails.html">rails.rb</a>
25
28
  <a class="source" href="version.html">version.rb</a>
@@ -13,6 +13,8 @@
13
13
  <div id="jump_wrapper">
14
14
  <div id="jump_page">
15
15
  <a class="source" href="../../index.html">index.rb</a>
16
+ <a class="source" href="../recap.html">recap.rb</a>
17
+ <a class="source" href="bootstrap.html">bootstrap.rb</a>
16
18
  <a class="source" href="bundler.html">bundler.rb</a>
17
19
  <a class="source" href="capistrano_extensions.html">capistrano_extensions.rb</a>
18
20
  <a class="source" href="cli.html">cli.rb</a>
@@ -20,6 +22,7 @@
20
22
  <a class="source" href="deploy.html">deploy.rb</a>
21
23
  <a class="source" href="env.html">env.rb</a>
22
24
  <a class="source" href="foreman.html">foreman.rb</a>
25
+ <a class="source" href="namespace.html">namespace.rb</a>
23
26
  <a class="source" href="preflight.html">preflight.rb</a>
24
27
  <a class="source" href="rails.html">rails.rb</a>
25
28
  <a class="source" href="version.html">version.rb</a>
@@ -0,0 +1,42 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
5
+ <title>recap.rb</title>
6
+ <link rel="stylesheet" href="http://jashkenas.github.com/docco/resources/docco.css">
7
+ </head>
8
+ <body>
9
+ <div id='container'>
10
+ <div id="background"></div>
11
+ <div id="jump_to">
12
+ Jump To &hellip;
13
+ <div id="jump_wrapper">
14
+ <div id="jump_page">
15
+ <a class="source" href="../index.html">index.rb</a>
16
+ <a class="source" href="recap.html">recap.rb</a>
17
+ <a class="source" href="recap/bootstrap.html">bootstrap.rb</a>
18
+ <a class="source" href="recap/bundler.html">bundler.rb</a>
19
+ <a class="source" href="recap/capistrano_extensions.html">capistrano_extensions.rb</a>
20
+ <a class="source" href="recap/cli.html">cli.rb</a>
21
+ <a class="source" href="recap/compatibility.html">compatibility.rb</a>
22
+ <a class="source" href="recap/deploy.html">deploy.rb</a>
23
+ <a class="source" href="recap/env.html">env.rb</a>
24
+ <a class="source" href="recap/foreman.html">foreman.rb</a>
25
+ <a class="source" href="recap/namespace.html">namespace.rb</a>
26
+ <a class="source" href="recap/preflight.html">preflight.rb</a>
27
+ <a class="source" href="recap/rails.html">rails.rb</a>
28
+ <a class="source" href="recap/version.html">version.rb</a>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <table cellspacing=0 cellpadding=0>
33
+ <thead>
34
+ <tr>
35
+ <th class=docs><h1>recap.rb</h1></th>
36
+ <th class=code></th>
37
+ </tr>
38
+ </thead>
39
+ <tbody>
40
+ </table>
41
+ </div>
42
+ </body>
@@ -0,0 +1,18 @@
1
+ Feature: Bundling gems in ruby projects
2
+
3
+ Scenario: Deploying a project with a Gemfile
4
+
5
+ Given a new ruby project and a bootstrapped server
6
+ And a bundle requiring version "1.0" of "example-gem"
7
+ When I run "cap deploy:setup deploy"
8
+ Then the project should be deployed
9
+ And the deployed project should include version "1.0" of "example-gem"
10
+
11
+ Scenario: Updating a project bundle
12
+
13
+ Given a new ruby project and a bootstrapped server
14
+ And a bundle requiring version "1.0" of "example-gem"
15
+ And I run "cap deploy:setup deploy"
16
+ When I update the bundle to require version "1.1" of "example-gem"
17
+ And I run "cap deploy"
18
+ Then the deployed project should include version "1.1" of "example-gem"
@@ -0,0 +1,21 @@
1
+ Feature: Deploying and rolling back deployments
2
+
3
+ Scenario: Deploying for the first time
4
+
5
+ Given a new project and a bootstrapped server
6
+ When I run "cap deploy:setup deploy"
7
+ Then the project should be deployed
8
+
9
+ Scenario: Deploying after changes
10
+
11
+ Given a deployed project
12
+ When I commit changes to the project
13
+ And I run "cap deploy"
14
+ Then the latest version of the project should be deployed
15
+
16
+ Scenario: Rolling back to the previous version
17
+
18
+ Given a deployed project
19
+ When I commit and deploy changes to the project
20
+ And I run "cap deploy:rollback"
21
+ Then the previous project version should be deployed
@@ -0,0 +1,17 @@
1
+
2
+ Feature: Managing processes with foreman
3
+
4
+ Scenario: Running an application process
5
+ Given a new ruby project and a bootstrapped server
6
+ And the project has an application process defined in a Procfile
7
+ When I run "cap deploy:setup deploy"
8
+ Then the project should own the running application process
9
+
10
+ Scenario: Running processes can read environment variables
11
+ Given a new ruby project and a bootstrapped server
12
+ And the project has an application process defined in a Procfile
13
+ And the variable "MONSTER" is set to "tricorn"
14
+ When I run "cap deploy:setup deploy"
15
+ And I wait for the server to start
16
+ Then the variable "MONSTER" should be set to "tricorn"
17
+ Then the running application process should know that "MONSTER" is set to "tricorn"
@@ -0,0 +1,21 @@
1
+ Feature: Setting and unsetting environment config variables
2
+
3
+ Scenario: Setting an environment variable
4
+
5
+ Given a new project and a bootstrapped server
6
+ When I run "cap env:set SECRET=very-secure"
7
+ Then the variable "SECRET" should be set to "very-secure"
8
+
9
+ @wip
10
+ Scenario: Setting an environment variable based on an existing variable
11
+
12
+ Given a new project and a bootstrapped server
13
+ When I run "cap env:set SUPER_PATH=\$PATH"
14
+ Then the variable "SUPER_PATH" should be set to the application's PATH
15
+
16
+ Scenario: Unsetting a variable
17
+
18
+ Given a new project and a bootstrapped server
19
+ And the variable "SECRET" is set to "very-secure"
20
+ When I run "cap env:set SECRET="
21
+ Then the variable "SECRET" should have no value
@@ -0,0 +1,98 @@
1
+ Before do
2
+ FileUtils.rm_rf 'test-vm/share/projects'
3
+ FileUtils.rm_rf 'test-vm/share/gems'
4
+ `bundle exec vagrant sandbox on` unless ENV['SKIP_ROLLBACK']
5
+ end
6
+
7
+ After do
8
+ `bundle exec vagrant sandbox rollback` unless ENV['SKIP_ROLLBACK']
9
+ if project
10
+ project.run_on_server "sudo stop #{project.name} || true"
11
+ project.run_on_server "sudo rm -rf /etc/init/#{project.name}* || true"
12
+ end
13
+ end
14
+
15
+ Given /^a new (ruby )?project and a bootstrapped server$/ do |project_type|
16
+ type = (project_type || 'static').strip
17
+ start_project server: server, capfile: { recap_require: "recap/#{type}" }
18
+ project.run_cap 'bootstrap'
19
+ end
20
+
21
+ Given /^a deployed project$/ do
22
+ start_project server: server
23
+ project.run_cap 'bootstrap'
24
+ project.run_cap 'deploy:setup deploy'
25
+ end
26
+
27
+ Given /^a bundle requiring version "([^"]*)" of "([^"]*)"$/ do |version, gem|
28
+ project.add_gem_to_bundle(gem, version)
29
+ end
30
+
31
+ Given /^the variable "([^"]*)" is set to "([^"]*)"$/ do |name, value|
32
+ project.run_cap "env:set #{name}=#{value}"
33
+ end
34
+
35
+ Given /^the project has an application process defined in a Procfile$/ do
36
+ @application_process = 'an-application-process'
37
+ project.add_foreman_to_bundle
38
+ project.add_gem_to_bundle @application_process, '1.0.0'
39
+ project.add_command_to_procfile 'process', "bin/#{@application_process} --server"
40
+ end
41
+
42
+ When /^I update the bundle to require version "([^"]*)" of "([^"]*)"$/ do |version, gem|
43
+ project.add_gem_to_bundle(gem, version)
44
+ end
45
+
46
+ When /^I run "cap ([^"]*)"$/ do |command|
47
+ project.run_cap command
48
+ end
49
+
50
+ When /^I commit changes to the project$/ do
51
+ project.commit_changes
52
+ end
53
+
54
+ When /^I commit and deploy changes to the project$/ do
55
+ project.commit_changes
56
+ project.run_cap 'deploy'
57
+ end
58
+
59
+ When /^I wait for the server to start$/ do
60
+ sleep(5)
61
+ end
62
+
63
+ Then /^the project should be deployed$/ do
64
+ project.deployed_version.should eql(project.latest_version)
65
+ end
66
+
67
+ Then /^the latest version of the project should be deployed$/ do
68
+ project.deployed_version.should eql(project.latest_version)
69
+ end
70
+
71
+ Then /^the previous project version should be deployed$/ do
72
+ project.deployed_version.should eql(project.previous_version)
73
+ end
74
+
75
+ Then /^the deployed project should include version "([^"]*)" of "([^"]*)"$/ do |version, gem|
76
+ project.run_on_server("bin/#{gem} --version").strip.should eql(version)
77
+ end
78
+
79
+ Then /^the variable "([^"]*)" should be set to "([^"]*)"$/ do |name, value|
80
+ project.run_on_server("sudo su - #{project.name} -c 'env | grep #{name}'").strip.should eql("#{name}=#{value}")
81
+ end
82
+
83
+ Then /^the variable "([^"]*)" should be set to the application's PATH$/ do |name|
84
+ path = project.run_on_server("echo $PATH").strip
85
+ project.run_on_server("sudo su - #{project.name} -c 'env | grep #{name}'").strip.should eql("#{name}=#{path}")
86
+ end
87
+
88
+ Then /^the variable "([^"]*)" should have no value$/ do |name|
89
+ project.run_on_server("sudo su - #{project.name} -c 'env'").include?("#{name}=").should be_false
90
+ end
91
+
92
+ Then /^the project should own the running application process$/ do
93
+ project.run_on_server("ps -U #{project.name} u").include?(@application_process).should be_true
94
+ end
95
+
96
+ Then /^the running application process should know that "([^"]*)" is set to "([^"]*)"$/ do |name, value|
97
+ project.run_on_server("/usr/bin/curl localhost:3500/env | grep #{name}").strip.should eql("#{name}=#{value}")
98
+ end
@@ -0,0 +1,211 @@
1
+ require 'fileutils'
2
+ require 'faker'
3
+ require 'erb'
4
+
5
+ module ProjectSupport
6
+ def project
7
+ @project
8
+ end
9
+
10
+ def start_project(options = {})
11
+ @project = Project.new(options)
12
+ end
13
+
14
+ class Template
15
+ def initialize(template_name)
16
+ @template_name = template_name
17
+ end
18
+
19
+ def template_root
20
+ File.expand_path("../../templates/", __FILE__)
21
+ end
22
+
23
+ def to_s
24
+ ERB.new(read_template).result(binding)
25
+ end
26
+
27
+ def write_to(path)
28
+ full_path = File.expand_path(path)
29
+ FileUtils.mkdir_p File.dirname(full_path)
30
+ File.write(full_path, to_s)
31
+ end
32
+
33
+ def read_template
34
+ template_path = File.join(template_root, @template_name)
35
+ File.read(template_path)
36
+ end
37
+ end
38
+
39
+ class Capfile < Template
40
+ attr_reader :project, :recap_require
41
+
42
+ def initialize(project, options = {})
43
+ super('project/Capfile.erb')
44
+ @project = project
45
+ @recap_require = options[:recap_require] || 'recap/static'
46
+ end
47
+ end
48
+
49
+ class Gemfile < Template
50
+ attr_accessor :foreman
51
+ attr_accessor :gems
52
+
53
+ def initialize(gems = {})
54
+ super('project/Gemfile.erb')
55
+ @gems = gems
56
+ end
57
+ end
58
+
59
+ class Procfile < Template
60
+ attr_reader :name, :command
61
+
62
+ def initialize(name, command)
63
+ super('project/Procfile.erb')
64
+ @name = name
65
+ @command = command
66
+ end
67
+ end
68
+
69
+ class BundledGem
70
+ def initialize(gem, version)
71
+ @gem = gem
72
+ @version = version
73
+ @output_path = File.expand_path("../../../test-vm/share/gems/#{gem}", __FILE__)
74
+ end
75
+
76
+ def generate
77
+ FileUtils.mkdir_p @output_path
78
+ FileUtils.chdir @output_path do
79
+ GemBinary.new(@gem, @version).write_to "bin/#{@gem}"
80
+ Gemspec.new(@gem, @version).write_to "#{@name}.gemspec"
81
+
82
+ `git init`
83
+ `git add --all`
84
+ `git commit -m 'Committed version #{@version}'`
85
+ `git tag #{@version}`
86
+ end
87
+ end
88
+
89
+ class Gemspec < Template
90
+ attr_reader :gem, :version
91
+
92
+ def initialize(gem, version)
93
+ super 'gem/gemspec.erb'
94
+ @gem = gem
95
+ @version = version
96
+ end
97
+ end
98
+
99
+ class GemBinary < Template
100
+ attr_reader :gem, :version
101
+
102
+ def initialize(name, version)
103
+ super 'gem/binary.erb'
104
+ @gem = name
105
+ @version = version
106
+ end
107
+ end
108
+ end
109
+
110
+ class Project
111
+ def initialize(options = {})
112
+ @server = options[:server]
113
+ @gems = {}
114
+ FileUtils.rm_rf repository_path
115
+ git 'init'
116
+ write_and_commit_file 'Capfile', Capfile.new(self, options[:capfile] || {})
117
+ end
118
+
119
+ def name
120
+ @name ||= Faker::Name.first_name.downcase
121
+ end
122
+
123
+ def private_key_path
124
+ @server.private_key_path
125
+ end
126
+
127
+ def latest_version
128
+ committed_versions[0]
129
+ end
130
+
131
+ def previous_version
132
+ committed_versions[1]
133
+ end
134
+
135
+ def committed_versions
136
+ `cd #{repository_path} && git log --pretty=format:"%H"`.split("\n")
137
+ end
138
+
139
+ def write_and_commit_file(path, content = "")
140
+ full_path = File.join(repository_path, path)
141
+ FileUtils.mkdir_p File.dirname(full_path)
142
+ File.write(full_path, content)
143
+ commit_files(path)
144
+ end
145
+
146
+ def commit_files(*paths)
147
+ git "add #{paths.join(' ')}"
148
+ git "commit -m 'Added #{paths.join(' ')}'"
149
+ end
150
+
151
+ def repository_path(path = "")
152
+ File.join('test-vm/share/projects/', name, path)
153
+ end
154
+
155
+ def deployment_path(path = "")
156
+ File.join("/home/#{name}/apps/#{name}", path)
157
+ end
158
+
159
+ def deployed_version
160
+ (@server.run "cd #{deployment_path} && git rev-parse HEAD").strip
161
+ end
162
+
163
+ def run_cap(command)
164
+ `cap -l capistrano.log -f #{repository_path('Capfile')} #{command}`
165
+ raise "Exit code returned running 'cap #{command}'" if $?.exitstatus != 0
166
+ end
167
+
168
+ def run_on_server(cmd)
169
+ @server.run("cd #{deployment_path} && #{cmd}")
170
+ end
171
+
172
+ def git(command)
173
+ FileUtils.mkdir_p repository_path
174
+ FileUtils.chdir repository_path do
175
+ `git #{command}`
176
+ end
177
+ end
178
+
179
+ def gemfile
180
+ @gemfile ||= Gemfile.new
181
+ end
182
+
183
+ def add_gem_to_bundle(gem, version)
184
+ gemfile.gems[gem] = version
185
+ BundledGem.new(gem, version).generate
186
+ regenerate_bundle
187
+ end
188
+
189
+ def add_foreman_to_bundle
190
+ gemfile.foreman = true
191
+ regenerate_bundle
192
+ end
193
+
194
+ def regenerate_bundle
195
+ write_and_commit_file 'Gemfile', gemfile
196
+ # Nasty hack to generate a Gemfile.lock
197
+ @server.run "cd /recap/share/projects/#{name} && bundle install"
198
+ commit_files 'Gemfile.lock'
199
+ end
200
+
201
+ def add_command_to_procfile(name, command)
202
+ write_and_commit_file 'Procfile', Procfile.new(name, command)
203
+ end
204
+
205
+ def commit_changes
206
+ write_and_commit_file 'project-file', Faker::Lorem.sentence
207
+ end
208
+ end
209
+ end
210
+
211
+ World(ProjectSupport)
@@ -0,0 +1,53 @@
1
+ require 'vagrant'
2
+
3
+ module ServerSupport
4
+ def server
5
+ @server ||= Server.instance
6
+ end
7
+
8
+ class Server
9
+ class << self
10
+ def instance
11
+ Server.new
12
+ end
13
+ end
14
+
15
+ def private_key_path
16
+ env.default_private_key_path
17
+ end
18
+
19
+ def run(command, user = 'vagrant')
20
+ output = nil
21
+ env.primary_vm.channel.sudo("su - #{user} -c '#{command}'") do |type, data|
22
+ output = data if type == :stdout
23
+ end
24
+ output
25
+ end
26
+
27
+ def has_user?(name)
28
+ test? "id -u #{name}"
29
+ end
30
+
31
+ def has_group?(name)
32
+ test? "id -g #{name}"
33
+ end
34
+
35
+ def has_directory?(path)
36
+ test? "[ -d #{path} ]"
37
+ end
38
+
39
+ def has_file?(path)
40
+ test? "[ -f #{path} ]"
41
+ end
42
+
43
+ def test?(command)
44
+ env.primary_vm.channel.test(command)
45
+ end
46
+
47
+ def env
48
+ @env ||= Vagrant::Environment.new
49
+ end
50
+ end
51
+ end
52
+
53
+ World(ServerSupport)