pulsar 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,16 +1,240 @@
1
- # Pulsar
1
+ # Pulsar [![Gem Version](https://badge.fury.io/rb/pulsar.png)](http://badge.fury.io/rb/pulsar) [![Build Status](https://secure.travis-ci.org/nebulab/pulsar.png?branch=master)](http://travis-ci.org/nebulab/pulsar) [![Coverage Status](https://coveralls.io/repos/nebulab/pulsar/badge.png?branch=master)](https://coveralls.io/r/nebulab/pulsar) [![Code Climate](https://codeclimate.com/github/nebulab/pulsar.png)](https://codeclimate.com/github/nebulab/pulsar)
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/pulsar.png)](http://badge.fury.io/rb/pulsar)
4
- [![Build Status](https://secure.travis-ci.org/nebulab/pulsar.png?branch=master)](http://travis-ci.org/nebulab/pulsar)
5
- [![Coverage Status](https://coveralls.io/repos/nebulab/pulsar/badge.png?branch=master)](https://coveralls.io/r/nebulab/pulsar)
6
- [![Code Climate](https://codeclimate.com/github/nebulab/pulsar.png)](https://codeclimate.com/github/nebulab/pulsar)
3
+ The easy [Capistrano](https://rubygems.org/gems/capistrano) deploy and configuration manager.
7
4
 
8
- Pulsar is a little tool that helps with deploys. Its main purpose is building capfiles for [Capistrano](https://rubygems.org/gems/capistrano)
9
- to run. It makes it easy to manage a large number of apps via a separate configuration repository.
5
+ Pulsar allows you to run capistrano tasks via a separate repository where all your deploy configurations are stored.
6
+ Once you have your own repository, you can gradully add configurations and recipes so that you never have to duplicate code again.
10
7
 
11
- ## Installation & Usage
8
+ The way Pulsar works means that you can deploy without actually having the application on your local machine (and neither
9
+ have all your application dependencies installed). This lets you integrate Pulsar with nearly any deploy strategy you can think of.
12
10
 
13
- Refer to the [GitHub Page](http://pulsar.nebulab.it).
11
+ Some of the benefits of using Pulsar:
12
+ * No capistrano configurations in the application code
13
+ * No need to have the application locally to deploy
14
+ * Multistage support by default
15
+ * Every recipe can be shared between all applications
16
+ * Can easily be integrated with other tools
17
+ * Write the least possible code to deploy
18
+
19
+ ## Installation
20
+
21
+ The most useful way of installing Pulsar is as a system gem:
22
+
23
+ ```bash
24
+ $ gem install pulsar
25
+ ```
26
+
27
+ This will install two commands: `pulsar` and `pulsar-utils`. The first command is required to run capistrano,
28
+ the other is for everything else.
29
+
30
+ ---
31
+
32
+ You'll need to create your own configuration repo:
33
+
34
+ ```bash
35
+ $ pulsar-utils init ~/Desktop/pulsar-conf
36
+ ```
37
+
38
+ This will create a basic start point for building your configuration repository. As soon as you're done configuring
39
+ you should consider storing this folder as an actual git repository.
40
+
41
+ Here it is possible to see how this configuration repository will look like: [Pulsar Conf Demo](http://github.com/nebulab/pulsar-conf-demo)
42
+
43
+ **NOTE**: Pulsar only supports git and *nix systems.
44
+
45
+ ## Configuration
46
+
47
+ This is an example repository configuration layout:
48
+
49
+ ```bash
50
+ pulsar-conf/
51
+ |── Gemfile
52
+ ├── Gemfile.lock
53
+ ├── apps
54
+ │   ├── base.rb
55
+ │   └── my_application
56
+ │      ├── defaults.rb
57
+ │      ├── production.rb
58
+ │      ├── recipes
59
+ │      │   └── custom_recipe.rb
60
+ │      └── staging.rb
61
+ └── recipes
62
+ ├── generic
63
+ │   ├── cleanup.rb
64
+ │   ├── maintenance_mode.rb
65
+ │   ├── notify.rb
66
+ │   └── rake.rb
67
+ ├── rails
68
+ │   ├── passenger.rb
69
+ │   ├── repair_permissions.rb
70
+ │   ├── symlink_configs.rb
71
+ │   ├── unicorn.rb
72
+ │   └── whenever.rb
73
+ └── spree_1
74
+ └── symlink_assets.rb
75
+ ```
76
+
77
+ Pulsar uses these files to build capistrano configurations on the fly, depending on how you invoke the `pulsar` command.
78
+ Since Pulsar it's basically a capistrano wrapper, the content of these files is plain old capistrano syntax.
79
+
80
+ ### _apps_ directory
81
+
82
+ This directory contains your application configurations. You'll have one directory per application.
83
+
84
+ * `base.rb` has configurations that are shared by all applications
85
+ * `my_application/defaults.rb` includes configuration shared by every stage of the application
86
+ * `my_application/staging.rb` and `my_application/production.rb` files include stage configurations
87
+ * `my_application/recipes/` are recipes that are always included for that application (no need to use `load_recipes`)
88
+
89
+ ### _recipes_ directory
90
+
91
+ This directory contains your recipes. You can create any number of directories to organize your recipes.
92
+ To load a recipe from your configurations you can use the `load_recipes` helper:
93
+
94
+ ```ruby
95
+ #
96
+ # Somewhere inside apps/
97
+ #
98
+ load_recipes do
99
+ rails :repair_permissions, :unicorn
100
+ generic :cleanup, :rake
101
+ end
102
+ ```
103
+
104
+ This will use capistrano's `load` method to include recipes from `rails/` and `generic/`.
105
+
106
+ ---
107
+
108
+ Another way to include your recipes is by using the `Gemfile`. Many gems already include custom recipes for capistrano so
109
+ you just need to require those. An example with [Whenever](https://github.com/javan/whenever):
110
+
111
+ ```ruby
112
+ #
113
+ # Inside Gemfile
114
+ #
115
+ gem 'whenever'
116
+
117
+ #
118
+ # Inside recipes/rails/whenever.rb
119
+ #
120
+ require 'whenever/capistrano'
121
+
122
+ set :whenever_command, "bundle exec whenever"
123
+
124
+ #
125
+ # Somewhere inside apps/
126
+ #
127
+ load_recipes do
128
+ rails :whenever
129
+ end
130
+ ```
131
+
132
+ ### Loading the repository
133
+
134
+ Once the repository is ready, you'll need to tell Pulsar where it is. The repository location can be specified either
135
+ as a full git path or a github repository path (`gh-user/pulsar-conf`).
136
+
137
+ Since Pulsar requires the repository for everything, there are multiple ways to store this information so that
138
+ you don't have to type it everytime.
139
+
140
+ You have three possibilities:
141
+
142
+ * `-c` command line option
143
+ * `PULSAR_CONF_REPO` environment variable
144
+ * `~/.pulsar` configuration file
145
+
146
+ The fastest way is probably the `.pulsar` hidden file inside your home directory:
147
+
148
+ ```bash
149
+ #
150
+ # Inside ~/.pulsar
151
+ #
152
+ PULSAR_CONF_REPO="gh-user/pulsar-conf"
153
+
154
+ #
155
+ # Also supported
156
+ #
157
+ # PULSAR_CONF_REPO="git://github.com/gh-user/pulsar-conf.git"
158
+ ```
159
+
160
+ Pulsar will read this file and set the environment variables properly.
161
+
162
+ ---
163
+
164
+ If you don't want to add another file to your home directory you can export the variables yourself:
165
+
166
+ ```bash
167
+ #
168
+ # Inside ~/.bash_profile or ~/.zshrc
169
+ #
170
+ export PULSAR_CONF_REPO="gh-user/pulsar-conf"
171
+ ```
172
+
173
+ ## Usage
174
+
175
+ After the repository is ready, running Pulsar is straightforward. To deploy `my_application` to `production`:
176
+
177
+ ```bash
178
+ $ pulsar my_application production
179
+ ```
180
+
181
+ As a rule of thumb, anything that's added at the end of the command is passed to capistrano. Some examples:
182
+
183
+ ```bash
184
+ $ pulsar my_application production --tasks
185
+
186
+ $ pulsar my_application staging deploy:migrations
187
+
188
+ $ pulsar my_application staging shell
189
+
190
+ #
191
+ # Deploy multiple apps by using commas
192
+ #
193
+ $ pulsar my_app1,my_app2,my_app3 production
194
+ ```
195
+
196
+ ### Running inside a Rack application (e.g. Ruby on Rails application)
197
+
198
+ In case you frequently work from a Rack application and would like a workflow similar to that of capistrano, Pulsar
199
+ supports running from inside a Rack application directory. If you use this a lot, you should consider installing
200
+ Pulsar via the application `Gemfile`.
201
+
202
+ When deploying from inside a Rack application you can omit the application name:
203
+
204
+ ```bash
205
+ $ cd /path/to/my_application
206
+
207
+ $ pulsar production
208
+
209
+ $ pulsar staging deploy:migrations
210
+ ```
211
+
212
+ ---
213
+
214
+ If you need a particular configuration for an application you can store a `.pulsar` file inside the application
215
+ directory:
216
+
217
+ ```bash
218
+ #
219
+ # Inside /path/to/my_application/.pulsar
220
+ #
221
+ PULSAR_CONF_REPO="gh-user/pulsar-conf"
222
+
223
+ #
224
+ # If the application directory name is different than what
225
+ # you configured inside the Pulsar configuration repository
226
+ #
227
+ # PULSAR_APP_NAME="my-application"
228
+ ```
229
+
230
+ ## Integrations
231
+
232
+ Pulsar is easy to integrate, you just need access to the configurations repository and the ability to
233
+ run a command.
234
+
235
+ ### Hubot
236
+
237
+ https://gist.github.com/mtylty/5324075
14
238
 
15
239
  ## Contributing
16
240
 
@@ -1,4 +1,5 @@
1
1
  module Pulsar::Helpers
2
2
  autoload :Capistrano, "pulsar/helpers/capistrano"
3
3
  autoload :Clamp, "pulsar/helpers/clamp"
4
+ autoload :Shell, "pulsar/helpers/shell"
4
5
  end
@@ -1,17 +1,18 @@
1
1
  module Pulsar
2
2
  module Helpers
3
3
  module Capistrano
4
- require "pulsar/helpers/clamp"
5
-
6
4
  class DSL
7
5
  include Pulsar::Helpers::Clamp
8
6
 
9
- def initialize(cap_conf, &dsl_code)
10
- @cap_conf = cap_conf
7
+ def initialize(capistrano, opts, &dsl_code)
8
+ @capistrano = capistrano
9
+ @options = opts
11
10
  instance_eval(&dsl_code)
12
11
  end
13
12
 
14
13
  def method_missing(meth, *args, &block)
14
+ return if !@capistrano.from_application_path? && @options[:app_only]
15
+
15
16
  recipes = "#{ENV['CONFIG_PATH']}/recipes/#{meth}"
16
17
 
17
18
  File.directory?(recipes) || raise("There are no recipes of type #{meth}")
@@ -20,18 +21,27 @@ module Pulsar
20
21
  recipe = "#{recipes}/#{arg}.rb"
21
22
  File.exists?(recipe) || raise("There is no #{arg} recipe")
22
23
 
23
- @cap_conf.send(:load, recipe)
24
+ @capistrano.send(:load, recipe)
24
25
  end
25
26
  end
26
27
  end
27
28
 
28
- def load_recipes(&block)
29
- DSL.new(self, &block)
30
- end
31
-
29
+ #
30
+ # This method can be called directly inside
31
+ # capistrano configurations but needs:
32
+ #
33
+ # defer { from_application_path? }
34
+ #
35
+ # otherwise capistrano will interpret it
36
+ # as a variable and error out
37
+ #
32
38
  def from_application_path?
33
39
  ENV.has_key?('APP_PATH')
34
40
  end
41
+
42
+ def load_recipes(opts = {}, &block)
43
+ DSL.new(self, opts, &block)
44
+ end
35
45
  end
36
46
  end
37
47
  end
@@ -1,9 +1,7 @@
1
- require 'fileutils'
2
-
3
1
  module Pulsar
4
2
  module Helpers
5
3
  module Clamp
6
- include FileUtils
4
+ include Pulsar::Helpers::Shell
7
5
 
8
6
  def self.included(base)
9
7
  base.extend(InstanceAndClassMethods)
@@ -22,7 +20,7 @@ module Pulsar
22
20
  def application_path
23
21
  Dir.pwd if from_application_path?
24
22
  end
25
-
23
+
26
24
  def build_capfile(args)
27
25
  app, stage = args.split(":")
28
26
 
@@ -45,11 +43,6 @@ module Pulsar
45
43
  @capfile_name ||= "#{tmp_dir}/capfile-#{time_to_deploy}"
46
44
  end
47
45
 
48
- def cd(path, opts, &block)
49
- puts "Directory: #{path.white}".yellow if opts[:verbose]
50
- FileUtils.cd(path) { yield }
51
- end
52
-
53
46
  def config_path
54
47
  @configuration_path ||= "#{tmp_dir}/conf-repo-#{time_to_deploy}"
55
48
  end
@@ -58,6 +51,12 @@ module Pulsar
58
51
  touch(capfile_path, :verbose => verbose?)
59
52
  end
60
53
 
54
+ def each_app
55
+ Dir["#{config_path}/apps/*"].each do |path|
56
+ yield(File.basename(path)) if File.directory?(path)
57
+ end
58
+ end
59
+
61
60
  def fetch_repo
62
61
  if File.directory?(conf_repo)
63
62
  fetch_directory_repo(conf_repo)
@@ -115,31 +114,14 @@ module Pulsar
115
114
  end
116
115
 
117
116
  def list_apps
118
- apps = Dir["#{config_path}/apps/*"].each do |app|
119
- if File.directory?(app)
120
- app_name = File.basename(app)
121
- app_envs = []
122
-
123
- Dir["#{app}/*"].each do |env|
124
- environments = %w(development staging production)
125
- env_name = File.basename(env, '.rb')
126
-
127
- if environments.include?(env_name)
128
- app_envs << env_name
129
- end
130
- end
131
-
132
- puts "#{app_name.cyan}: #{app_envs.map(&:magenta).join(', ')}"
133
- end
117
+ each_app do |name|
118
+ puts "#{name.cyan}: #{stages_for(name).map(&:magenta).join(', ')}"
134
119
  end
135
120
  end
136
121
 
137
122
  def load_configuration
138
- conf_path = application_path || Dir.home
139
- conf_file = File.join(conf_path, ".pulsar")
140
-
141
- if File.file?(conf_file)
142
- File.readlines(conf_file).each do |line|
123
+ unless pulsar_configuration.nil?
124
+ File.readlines(pulsar_configuration).each do |line|
143
125
  conf, value = line.split("=")
144
126
 
145
127
  ENV[conf] = value.chomp.gsub('"', '') if !conf.nil? && !value.nil?
@@ -147,6 +129,15 @@ module Pulsar
147
129
  end
148
130
  end
149
131
 
132
+ def pulsar_configuration
133
+ conf_file = ".pulsar"
134
+ inside_app = File.join(application_path, conf_file) rescue nil
135
+ inside_home = File.join(Dir.home, conf_file) rescue nil
136
+
137
+ return inside_app if inside_app && File.file?(inside_app)
138
+ return inside_home if inside_home && File.file?(inside_home)
139
+ end
140
+
150
141
  def remove_capfile
151
142
  rm_rf(capfile_path, :verbose => verbose?)
152
143
  end
@@ -171,18 +162,6 @@ module Pulsar
171
162
  end
172
163
  end
173
164
 
174
- def rm_rf(path, opts)
175
- puts "Remove: #{path.white}".yellow if opts[:verbose]
176
- FileUtils.rm_rf(path)
177
- end
178
-
179
- def run_cmd(cmd, opts)
180
- puts "Command: #{cmd.white}".yellow if opts[:verbose]
181
- system(cmd)
182
-
183
- raise "Command #{cmd} Failed" if $? != 0
184
- end
185
-
186
165
  def set_log_level
187
166
  level = log_level.upcase
188
167
  levels = %w(IMPORTANT INFO DEBUG)
@@ -194,13 +173,18 @@ module Pulsar
194
173
  run_cmd(cmd, :verbose => verbose?)
195
174
  end
196
175
 
197
- def time_to_deploy
198
- @now ||= Time.now.strftime("%Y-%m-%d-%H%M%S-s#{rand(9999)}")
176
+ def stages_for(app)
177
+ environments = %w(development staging production)
178
+
179
+ Dir["#{config_path}/apps/#{app}/*"].map do |env|
180
+ env_name = File.basename(env, '.rb')
181
+
182
+ env_name if environments.include?(env_name)
183
+ end.compact
199
184
  end
200
185
 
201
- def touch(file, opts)
202
- puts "Touch: #{file.white}".yellow if opts[:verbose]
203
- FileUtils.touch(file)
186
+ def time_to_deploy
187
+ @now ||= Time.now.strftime("%Y-%m-%d-%H%M%S-s#{rand(9999)}")
204
188
  end
205
189
  end
206
190
  end
@@ -0,0 +1,31 @@
1
+ require "fileutils"
2
+
3
+ module Pulsar
4
+ module Helpers
5
+ module Shell
6
+ include FileUtils
7
+
8
+ def cd(path, opts, &block)
9
+ puts "Directory: #{path.white}".yellow if opts[:verbose]
10
+ FileUtils.cd(path) { yield }
11
+ end
12
+
13
+ def rm_rf(path, opts)
14
+ puts "Remove: #{path.white}".yellow if opts[:verbose]
15
+ FileUtils.rm_rf(path)
16
+ end
17
+
18
+ def run_cmd(cmd, opts)
19
+ puts "Command: #{cmd.white}".yellow if opts[:verbose]
20
+ system(cmd)
21
+
22
+ raise "Command #{cmd} Failed" if $? != 0
23
+ end
24
+
25
+ def touch(file, opts)
26
+ puts "Touch: #{file.white}".yellow if opts[:verbose]
27
+ FileUtils.touch(file)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module Pulsar
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
@@ -19,6 +19,20 @@ describe Pulsar::ListCommand do
19
19
  Dir.glob("#{tmp_path}/conf-repo*").should be_empty
20
20
  end
21
21
 
22
+ it "lists configured apps and stages" do
23
+ app_one = Regexp.escape("dummy_app".cyan)
24
+ app_two = Regexp.escape("other_dummy_app".cyan)
25
+
26
+ stages = [ "production".magenta, "staging".magenta ]
27
+ escaped_stages = Regexp.escape(stages.join(', '))
28
+ reversed_stages = Regexp.escape(stages.reverse.join(', '))
29
+
30
+ pulsar.run(full_list_args)
31
+
32
+ stdout.should match(/#{app_one}: (#{escaped_stages})|(#{reversed_stages})/)
33
+ stdout.should match(/#{app_two}: (#{escaped_stages})|(#{reversed_stages})/)
34
+ end
35
+
22
36
  it "reads configuration variables from .pulsar file in home" do
23
37
  env_vars = [ "PULSAR_CONF_REPO=\"#{dummy_conf_path}\"\n"]
24
38
 
@@ -47,6 +61,34 @@ describe Pulsar::ListCommand do
47
61
  ENV['PULSAR_CONF_REPO'].should == dummy_conf_path
48
62
  end
49
63
 
64
+ it "skips lines which cannot parse when reading .pulsar file" do
65
+ env_vars = [ "wrong_line", "# comment"]
66
+
67
+ File.stub(:file?).and_return(true)
68
+ File.stub(:readlines).with("#{File.expand_path(dummy_rack_app_path)}/.pulsar").and_return(env_vars)
69
+
70
+ FileUtils.cd(dummy_rack_app_path) do
71
+ reload_main_command
72
+
73
+ expect { pulsar.run(full_list_args) }.not_to raise_error
74
+ end
75
+ end
76
+
77
+ it "falls back to .pulsar file in home directory if it's not in the rack app directory" do
78
+ env_vars = [ "PULSAR_APP_NAME=\"dummy_app\"\n", "PULSAR_CONF_REPO=\"#{dummy_conf_path}\"\n"]
79
+
80
+ File.stub(:file?).and_return(true)
81
+ File.stub(:file?).with("#{File.expand_path(dummy_rack_app_path)}/.pulsar").and_return(false)
82
+
83
+ File.should_receive(:readlines).with("#{Dir.home}/.pulsar").and_return(env_vars)
84
+
85
+ FileUtils.cd(dummy_rack_app_path) do
86
+ reload_main_command
87
+
88
+ pulsar.run(full_list_args)
89
+ end
90
+ end
91
+
50
92
  context "--conf-repo option" do
51
93
  it "is required" do
52
94
  expect { pulsar.parse([]) }.to raise_error(Clamp::UsageError)
@@ -86,6 +86,21 @@ describe Pulsar::MainCommand do
86
86
  end
87
87
  end
88
88
 
89
+ it "falls back to .pulsar file in home directory if it's not in the rack app directory" do
90
+ env_vars = [ "PULSAR_APP_NAME=\"dummy_app\"\n", "PULSAR_CONF_REPO=\"#{dummy_conf_path}\"\n"]
91
+
92
+ File.stub(:file?).and_return(true)
93
+ File.stub(:file?).with("#{File.expand_path(dummy_rack_app_path)}/.pulsar").and_return(false)
94
+
95
+ File.should_receive(:readlines).with("#{Dir.home}/.pulsar").and_return(env_vars)
96
+
97
+ FileUtils.cd(dummy_rack_app_path) do
98
+ reload_main_command
99
+
100
+ pulsar.run(full_cap_args + %w(production))
101
+ end
102
+ end
103
+
89
104
  context "Capfile" do
90
105
  it "uses base.rb in staging stage" do
91
106
  pulsar.run(full_cap_args + dummy_app(:staging))
@@ -26,6 +26,26 @@ describe Pulsar::Helpers::Capistrano do
26
26
 
27
27
  expect { load_recipes { generic :missing_recipe } }.to raise_error(RuntimeError, /no missing_recipe recipe/)
28
28
  end
29
+
30
+ it "does not call load if :app_only is true and pulsar is not called from inside application" do
31
+ ENV.delete('APP_PATH')
32
+ File.stub(:directory?).and_return(true)
33
+ File.stub(:exists?).and_return(true)
34
+
35
+ self.should_not_receive(:load)
36
+
37
+ load_recipes(:app_only => true) { generic :recipe }
38
+ end
39
+
40
+ it "calls load if :app_only is true and pulsar is called from inside application" do
41
+ ENV['APP_PATH'] = "/app/path"
42
+ File.stub(:directory?).and_return(true)
43
+ File.stub(:exists?).and_return(true)
44
+
45
+ self.should_receive(:load)
46
+
47
+ load_recipes(:app_only => true) { generic :recipe }
48
+ end
29
49
  end
30
50
 
31
51
  context "from_application_path?" do
@@ -50,12 +50,6 @@ describe Pulsar::Helpers::Clamp do
50
50
  end
51
51
  end
52
52
 
53
- context "run_cmd" do
54
- it "raises exception if command fails" do
55
- expect { run_cmd("return 1", {}) }.to raise_error
56
- end
57
- end
58
-
59
53
  context "reset_for_other_app!" do
60
54
  it "resets instance variables written by the module" do
61
55
  cap, conf, time = capfile_path, config_path, time_to_deploy
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Pulsar::Helpers::Shell do
4
+ include Pulsar::Helpers::Shell
5
+
6
+ context "run_cmd" do
7
+ it "raises exception if command fails" do
8
+ expect { run_cmd("return 1", {}) }.to raise_error
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pulsar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-05-02 00:00:00.000000000 Z
13
+ date: 2013-05-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: clamp
@@ -165,6 +165,7 @@ files:
165
165
  - lib/pulsar/helpers/all.rb
166
166
  - lib/pulsar/helpers/capistrano.rb
167
167
  - lib/pulsar/helpers/clamp.rb
168
+ - lib/pulsar/helpers/shell.rb
168
169
  - lib/pulsar/options/all.rb
169
170
  - lib/pulsar/options/conf_repo.rb
170
171
  - lib/pulsar/options/shared.rb
@@ -176,6 +177,7 @@ files:
176
177
  - spec/pulsar/commands/utils_spec.rb
177
178
  - spec/pulsar/helpers/capistrano_spec.rb
178
179
  - spec/pulsar/helpers/clamp_spec.rb
180
+ - spec/pulsar/helpers/shell_spec.rb
179
181
  - spec/spec_helper.rb
180
182
  - spec/support/dummies/dummy_app/config.ru
181
183
  - spec/support/dummies/dummy_conf/Gemfile
@@ -207,7 +209,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
207
209
  version: '0'
208
210
  segments:
209
211
  - 0
210
- hash: 2516214880330231667
212
+ hash: 882451203726848959
211
213
  required_rubygems_version: !ruby/object:Gem::Requirement
212
214
  none: false
213
215
  requirements:
@@ -216,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
218
  version: '0'
217
219
  segments:
218
220
  - 0
219
- hash: 2516214880330231667
221
+ hash: 882451203726848959
220
222
  requirements: []
221
223
  rubyforge_project:
222
224
  rubygems_version: 1.8.25
@@ -232,6 +234,7 @@ test_files:
232
234
  - spec/pulsar/commands/utils_spec.rb
233
235
  - spec/pulsar/helpers/capistrano_spec.rb
234
236
  - spec/pulsar/helpers/clamp_spec.rb
237
+ - spec/pulsar/helpers/shell_spec.rb
235
238
  - spec/spec_helper.rb
236
239
  - spec/support/dummies/dummy_app/config.ru
237
240
  - spec/support/dummies/dummy_conf/Gemfile