harrison 0.0.1 → 0.1.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.
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-head
4
+ - 2.1.1
5
+ - 2.0.0
6
+ - 1.9.3
data/CHANGELOG ADDED
@@ -0,0 +1,10 @@
1
+ 0.1.0
2
+ -------------------
3
+ - Renamed --pkg_dir option of 'package' task to --destination
4
+ - Implemented remote destinations for 'package' task.
5
+ - Implemented remote artifact sources for 'deploy' task.
6
+ - Added a default timeout of 10 seconds when establishing an SSH connection.
7
+
8
+ 0.0.1
9
+ -------------------
10
+ - Initial public release.
data/IDEAS ADDED
@@ -0,0 +1,12 @@
1
+ Ideas:
2
+ ------
3
+ [ ] Allow more elaborate hosts config, e.g.: h.hosts = [ { host: '10.16.18.207', tags: %w(util migrate) } ]
4
+ [ ] Some kind of --dry-run option.
5
+ [ ] Allow deploy_via to include alternate user/connection options.
6
+ [ ] Rename "releases" => "builds" (and "deploys" => "releases"?)
7
+ [ ] Upload artifact to all hosts before the rest of the deployment process begins.
8
+ [ ] --force option for deploy task to overwrite an existing release
9
+ [ ] Something like a "status" command that shows what commit is live for env (on each host?)
10
+ [ ] Move artifacts out of pkg/ (and into like pkg/deployed) once they have been deployed? (Some sort of flag for the significant env?)
11
+ [ ] Include branch name in artifact file name.
12
+ [ ] Make --purge the default behavior for the package step.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Simple artifact-based deployment for web applications.
4
4
 
5
+ [![Build Status](https://travis-ci.org/scotje/harrison.svg?branch=master)](https://travis-ci.org/scotje/harrison)
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
@@ -35,6 +37,10 @@ Harrison.package do |h|
35
37
  # Things we don't want to package.
36
38
  h.exclude = %w(.git ./config ./coverage ./examples ./log ./pkg ./tmp ./spec)
37
39
 
40
+ # Where to save the artifact by default.
41
+ h.destination = 'pkg' # Local folder
42
+ # h.destination = 'jesse@artifact-host.example.com:/tmp/artifacts' # Remote folder
43
+
38
44
  # Define the build process here.
39
45
  h.run do |h|
40
46
  # Bundle Install
@@ -82,7 +88,7 @@ The `--commit` option understands anything that `git rev-parse` understands. *NO
82
88
  reference must be pushed to the repository referenced as `git_src` in the Harrisonfile before
83
89
  you can build it.*
84
90
 
85
- The packaged release artifact will, by default, be saved into a 'pkg' subfolder:
91
+ The packaged release artifact will, by default, be saved into a local 'pkg' subfolder:
86
92
 
87
93
  ```
88
94
  $ harrison package
@@ -90,6 +96,23 @@ Packaging 5a547d8 for "harrison" on build-server.example.com...
90
96
  Sucessfully packaged 5a547d8 to pkg/20140711170226-5a547d8.tar.gz
91
97
  ```
92
98
 
99
+ You can set the destination on the command line with the `--destination` option, or
100
+ specify a new default in your Harrisonfile:
101
+
102
+ ```
103
+ h.destination = '/tmp'
104
+ ```
105
+
106
+ You can also specify a remote destination:
107
+
108
+ ```
109
+ h.destination = 'jesse@artifact-host.example.com:/tmp/artifacts'
110
+ ```
111
+
112
+ The username is optional and, if omitted, the build user will be used. *NOTE: Your build server
113
+ must have already accepted the SSH host key of the destination server in order to transfer the
114
+ artifact.*
115
+
93
116
  There are some additional options available, run `harrison package --help` to see everything available.
94
117
 
95
118
 
@@ -101,7 +124,16 @@ Use the `harrison deploy` command passing the artifact to be deployed as an argu
101
124
  $ harrison deploy pkg/20140711170226-5a547d8.tar.gz
102
125
  ```
103
126
 
104
- By default, this will deploy to the list of hosts defined in your Harrisonfile.
127
+ You can also deploy from a remote artifact source:
128
+
129
+ ```
130
+ $ harrison deploy jesse@artifact-host.example.com:/tmp/artifacts/20140711170226-5a547d8.tar.gz
131
+ ```
132
+
133
+ *NOTE: Each target server must have already accepted the SSH host key of the source server in order to
134
+ transfer the artifact.*
135
+
136
+ By default, the artifact will be deployed to the list of hosts defined in your Harrisonfile.
105
137
 
106
138
  You can override the target hosts by passing a `--hosts` option:
107
139
 
data/harrison.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.6"
27
27
  spec.add_development_dependency "rake"
28
- spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
29
  spec.add_development_dependency "debugger" if RUBY_VERSION < "2.0.0"
30
30
  spec.add_development_dependency "byebug" if RUBY_VERSION >= "2.0.0"
31
31
  spec.add_development_dependency "sourcify"
data/lib/harrison/base.rb CHANGED
@@ -87,15 +87,22 @@ module Harrison
87
87
  @ssh ||= Harrison::SSH.new(host: @options[:host], user: @options[:user])
88
88
  end
89
89
 
90
+ def remote_regex
91
+ /^(?:(\S+)@)?(\S+):(\S+)$/
92
+ end
93
+
90
94
  def ensure_local_dir(dir)
91
95
  @_ensured_local ||= {}
92
96
  @_ensured_local[dir] || (system("if [ ! -d #{dir} ] ; then mkdir -p #{dir} ; fi") && @_ensured_local[dir] = true) || abort("Error: Unable to create local directory \"#{dir}\".")
93
97
  end
94
98
 
95
- def ensure_remote_dir(host, dir)
99
+ def ensure_remote_dir(dir, with_ssh = nil)
100
+ with_ssh = ssh if with_ssh.nil?
101
+ host = with_ssh.host
102
+
96
103
  @_ensured_remote ||= {}
97
104
  @_ensured_remote[host] ||= {}
98
- @_ensured_remote[host][dir] || (ssh.exec("if [ ! -d #{dir} ] ; then mkdir -p #{dir} ; fi") && @_ensured_remote[host][dir] = true) || abort("Error: Unable to create remote directory \"#{dir}\" on \"#{host}\".")
105
+ @_ensured_remote[host][dir] || (with_ssh.exec("if [ ! -d #{dir} ] ; then mkdir -p #{dir} ; fi") && @_ensured_remote[host][dir] = true) || abort("Error: Unable to create remote directory \"#{dir}\" on \"#{host}\".")
99
106
  end
100
107
  end
101
108
  end
@@ -56,14 +56,22 @@ module Harrison
56
56
  hosts.each do |h|
57
57
  self.host = h
58
58
 
59
- ensure_remote_dir(self.host, "#{remote_project_dir}/deploys")
60
- ensure_remote_dir(self.host, "#{remote_project_dir}/releases")
59
+ ensure_remote_dir("#{remote_project_dir}/deploys", self.ssh)
60
+ ensure_remote_dir("#{remote_project_dir}/releases", self.ssh)
61
61
 
62
62
  # Make folder for release or bail if it already exists.
63
63
  remote_exec("mkdir #{release_dir}")
64
64
 
65
- # Upload artifact to host.
66
- upload(artifact, "#{remote_project_dir}/releases/")
65
+ if match = remote_regex.match(artifact)
66
+ # Copy artifact to host from remote source.
67
+ src_user, src_host, src_path = match.captures
68
+ src_user ||= self.user
69
+
70
+ remote_exec("scp #{src_user}@#{src_host}:#{src_path} #{remote_project_dir}/releases/")
71
+ else
72
+ # Upload artifact to host.
73
+ upload(artifact, "#{remote_project_dir}/releases/")
74
+ end
67
75
 
68
76
  # Unpack.
69
77
  remote_exec("cd #{release_dir} && tar -xzf ../#{File.basename(artifact)}")
@@ -5,7 +5,7 @@ module Harrison
5
5
  self.class.option_helper(:host)
6
6
  self.class.option_helper(:commit)
7
7
  self.class.option_helper(:purge)
8
- self.class.option_helper(:pkg_dir)
8
+ self.class.option_helper(:destination)
9
9
  self.class.option_helper(:remote_dir)
10
10
  self.class.option_helper(:exclude)
11
11
 
@@ -13,7 +13,7 @@ module Harrison
13
13
  arg_opts = [
14
14
  [ :commit, "Specific commit to be packaged. Accepts anything that `git rev-parse` understands.", :type => :string, :default => "HEAD" ],
15
15
  [ :purge, "Remove all previously packaged commits and working copies from the build host when finished.", :type => :boolean, :default => false ],
16
- [ :pkg_dir, "Local folder to save package to.", :type => :string, :default => "pkg" ],
16
+ [ :destination, "Local or remote folder to save package to. Remote syntax is: (user@)host:/path", :type => :string, :default => "pkg" ],
17
17
  [ :remote_dir, "Remote working folder.", :type => :string, :default => "~/.harrison" ],
18
18
  ]
19
19
 
@@ -21,7 +21,7 @@ module Harrison
21
21
  end
22
22
 
23
23
  def remote_exec(cmd)
24
- ensure_remote_dir(self.host, "#{remote_project_dir}/package")
24
+ ensure_remote_dir("#{remote_project_dir}/package")
25
25
 
26
26
  super("cd #{remote_project_dir}/package && #{cmd}")
27
27
  end
@@ -35,7 +35,7 @@ module Harrison
35
35
  puts "Packaging #{commit} for \"#{project}\" on #{host}..."
36
36
 
37
37
  # Make sure the folder to save the artifact to locally exists.
38
- ensure_local_dir(pkg_dir)
38
+ ensure_destination(destination)
39
39
 
40
40
  # Fetch/clone git repo on remote host.
41
41
  remote_exec("if [ -d cached ] ; then cd cached && git fetch origin -p ; else git clone #{git_src} cached ; fi")
@@ -53,14 +53,22 @@ module Harrison
53
53
  # Package build folder into tgz.
54
54
  remote_exec("rm -f #{artifact_name(commit)}.tar.gz && cd #{commit} && tar #{excludes_for_tar} -czf ../#{artifact_name(commit)}.tar.gz .")
55
55
 
56
- # Download (Expand remote path since Net::SCP doesn't expand ~)
57
- download(remote_exec("readlink -m #{artifact_name(commit)}.tar.gz"), "#{pkg_dir}/#{artifact_name(commit)}.tar.gz")
56
+ if match = remote_regex.match(destination)
57
+ # Copy artifact to remote destination.
58
+ dest_user, dest_host, dest_path = match.captures
59
+ dest_user ||= self.user
60
+
61
+ remote_exec("scp #{artifact_name(commit)}.tar.gz #{dest_user}@#{dest_host}:#{dest_path}")
62
+ else
63
+ # Download (Expand remote path since Net::SCP doesn't expand ~)
64
+ download(remote_exec("readlink -m #{artifact_name(commit)}.tar.gz"), "#{destination}/#{artifact_name(commit)}.tar.gz")
65
+ end
58
66
 
59
67
  if purge
60
68
  remote_exec("cd .. && rm -rf package")
61
69
  end
62
70
 
63
- puts "Sucessfully packaged #{commit} to #{pkg_dir}/#{artifact_name(commit)}.tar.gz"
71
+ puts "Sucessfully packaged #{commit} to #{destination}/#{artifact_name(commit)}.tar.gz"
64
72
  end
65
73
 
66
74
  protected
@@ -83,5 +91,16 @@ module Harrison
83
91
  @_timestamp ||= Time.new.utc.strftime('%Y%m%d%H%M%S')
84
92
  "#{@_timestamp}-#{commit}"
85
93
  end
94
+
95
+ def ensure_destination(destination)
96
+ if match = remote_regex.match(destination)
97
+ dest_user, dest_host, dest_path = match.captures
98
+ dest_user ||= self.user
99
+
100
+ ensure_remote_dir(dest_path, Harrison::SSH.new(host: dest_host, user: dest_user))
101
+ else
102
+ ensure_local_dir(destination)
103
+ end
104
+ end
86
105
  end
87
106
  end
data/lib/harrison/ssh.rb CHANGED
@@ -7,46 +7,23 @@ module Harrison
7
7
  def initialize(opts={})
8
8
  if opts[:proxy]
9
9
  @proxy = Net::SSH::Proxy::Command.new("ssh #{opts[:proxy]} \"nc %h %p\" 2>/dev/null")
10
- @conn = Net::SSH.start(opts[:host], opts[:user], forward_agent: true, proxy: @proxy)
10
+ @conn = Net::SSH.start(opts[:host], opts[:user], forward_agent: true, proxy: @proxy, timeout: 10)
11
11
  else
12
- @conn = Net::SSH.start(opts[:host], opts[:user], forward_agent: true)
12
+ @conn = Net::SSH.start(opts[:host], opts[:user], forward_agent: true, timeout: 10)
13
13
  end
14
14
  end
15
15
 
16
- # Helper to catch non-zero exit status and report errors.
17
16
  def exec(command)
18
17
  puts "INFO (ssh-exec #{desc}): #{command}" if Harrison::DEBUG
19
18
 
20
- stdout_data = ""
21
- stderr_data = ""
22
- exit_code = nil
23
-
24
- @conn.open_channel do |channel|
25
- channel.exec(command) do |ch, success|
26
- warn "Couldn't execute command (ssh.channel.exec) on remote host: #{command}" unless success
27
-
28
- channel.on_data do |ch,data|
29
- stdout_data += data
30
- end
31
-
32
- channel.on_extended_data do |ch,type,data|
33
- stderr_data += data
34
- end
19
+ result = invoke(@conn, command)
35
20
 
36
- channel.on_request("exit-status") do |ch,data|
37
- exit_code = data.read_long
38
- end
39
- end
21
+ if Harrison::DEBUG || result[:status] != 0
22
+ warn "STDERR (ssh-exec #{desc}): #{result[:stderr]}" unless result[:stderr].empty?
23
+ warn "STDOUT (ssh-exec #{desc}): #{result[:stdout]}" unless result[:stdout].empty?
40
24
  end
41
25
 
42
- @conn.loop
43
-
44
- if Harrison::DEBUG || exit_code != 0
45
- warn "STDERR (ssh-exec #{desc}): #{stderr_data.strip}" unless stderr_data.empty?
46
- warn "STDOUT (ssh-exec #{desc}): #{stdout_data.strip}" unless stdout_data.empty?
47
- end
48
-
49
- (exit_code == 0) ? stdout_data : nil
26
+ (result[:status] == 0) ? result[:stdout] : nil
50
27
  end
51
28
 
52
29
  def download(remote_path, local_path)
@@ -76,5 +53,40 @@ module Harrison
76
53
  @conn.host
77
54
  end
78
55
  end
56
+
57
+ def host
58
+ @conn.host
59
+ end
60
+
61
+ protected
62
+ # ----------------------------------------
63
+
64
+ def invoke(conn, cmd)
65
+ stdout_data = ""
66
+ stderr_data = ""
67
+ exit_code = nil
68
+
69
+ conn.open_channel do |channel|
70
+ channel.exec(cmd) do |ch, success|
71
+ warn "Couldn't execute command (ssh.channel.exec) on remote host: #{cmd}" unless success
72
+
73
+ channel.on_data do |ch,data|
74
+ stdout_data += data
75
+ end
76
+
77
+ channel.on_extended_data do |ch,type,data|
78
+ stderr_data += data
79
+ end
80
+
81
+ channel.on_request("exit-status") do |ch,data|
82
+ exit_code = data.read_long
83
+ end
84
+ end
85
+ end
86
+
87
+ conn.loop
88
+
89
+ { status: exit_code, stdout: stdout_data.strip, stderr: stderr_data.strip }
90
+ end
79
91
  end
80
92
  end
@@ -1,3 +1,3 @@
1
1
  module Harrison
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/spec/spec_helper.rb CHANGED
@@ -5,7 +5,6 @@ require 'harrison'
5
5
 
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
  RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
8
  config.run_all_when_everything_filtered = true
10
9
  config.filter_run :focus
11
10
 
@@ -28,12 +27,12 @@ RSpec::Matchers.define :exit_with_code do |exp_code|
28
27
  actual and actual == exp_code
29
28
  end
30
29
 
31
- failure_message_for_should do |block|
30
+ failure_message do |block|
32
31
  "expected block to call exit(#{exp_code}) but exit" +
33
32
  (actual.nil? ? " not called" : "(#{actual}) was called")
34
33
  end
35
34
 
36
- failure_message_for_should_not do |block|
35
+ failure_message_when_negated do |block|
37
36
  "expected block not to call exit(#{exp_code})"
38
37
  end
39
38
 
@@ -7,17 +7,17 @@ describe Harrison::Base do
7
7
  it 'should persist arg_opts' do
8
8
  instance = Harrison::Base.new(['foo'])
9
9
 
10
- instance.instance_variable_get('@arg_opts').should include('foo')
10
+ expect(instance.instance_variable_get('@arg_opts')).to include('foo')
11
11
  end
12
12
 
13
13
  it 'should add debug to arg_opts' do
14
- instance.instance_variable_get('@arg_opts').to_s.should include(':debug')
14
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':debug')
15
15
  end
16
16
 
17
17
  it 'should persist options' do
18
18
  instance = Harrison::Base.new([], testopt: 'foo')
19
19
 
20
- instance.instance_variable_get('@options').should include(testopt: 'foo')
20
+ expect(instance.instance_variable_get('@options')).to include(testopt: 'foo')
21
21
  end
22
22
  end
23
23
 
@@ -26,13 +26,13 @@ describe Harrison::Base do
26
26
  it 'should define a getter instance method for the option' do
27
27
  Harrison::Base.option_helper('foo')
28
28
 
29
- instance.methods.should include(:foo)
29
+ expect(instance.methods).to include(:foo)
30
30
  end
31
31
 
32
32
  it 'should define a setter instance method for the option' do
33
33
  Harrison::Base.option_helper('foo')
34
34
 
35
- instance.methods.should include(:foo=)
35
+ expect(instance.methods).to include(:foo=)
36
36
  end
37
37
  end
38
38
  end
@@ -40,15 +40,15 @@ describe Harrison::Base do
40
40
  describe 'instance methods' do
41
41
  describe '#exec' do
42
42
  it 'should execute a command locally and return the output' do
43
- instance.exec('echo "foo"').should == 'foo'
43
+ expect(instance.exec('echo "foo"')).to eq('foo')
44
44
  end
45
45
 
46
46
  it 'should complain if command returns non-zero' do
47
47
  output = capture(:stderr) do
48
- lambda { instance.exec('cat noexist 2>/dev/null') }.should exit_with_code(1)
48
+ expect(lambda { instance.exec('cat noexist 2>/dev/null') }).to exit_with_code(1)
49
49
  end
50
50
 
51
- output.should include('unable', 'execute', 'local', 'command')
51
+ expect(output).to include('unable', 'execute', 'local', 'command')
52
52
  end
53
53
  end
54
54
 
@@ -61,21 +61,29 @@ describe Harrison::Base do
61
61
  it 'should delegate command to ssh instance' do
62
62
  expect(@mock_ssh).to receive(:exec).and_return('remote_exec_return')
63
63
 
64
- instance.remote_exec('remote exec').should == 'remote_exec_return'
64
+ expect(instance.remote_exec('remote exec')).to eq('remote_exec_return')
65
65
  end
66
66
 
67
67
  it 'should complain if command returns nil' do
68
68
  expect(@mock_ssh).to receive(:exec).and_return(nil)
69
69
 
70
70
  output = capture(:stderr) do
71
- lambda { instance.remote_exec('remote exec fail') }.should exit_with_code(1)
71
+ expect(lambda { instance.remote_exec('remote exec fail') }).to exit_with_code(1)
72
72
  end
73
73
 
74
- output.should include('unable', 'execute', 'remote', 'command')
74
+ expect(output).to include('unable', 'execute', 'remote', 'command')
75
75
  end
76
76
  end
77
77
 
78
78
  describe '#parse' do
79
+ before(:each) do
80
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
81
+ end
82
+
83
+ after(:each) do
84
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
85
+ end
86
+
79
87
  it 'should recognize options from the command line' do
80
88
  instance = Harrison::Base.new([
81
89
  [ :testopt, "Test option.", :type => :string ]
@@ -83,13 +91,13 @@ describe Harrison::Base do
83
91
 
84
92
  instance.parse(%w(test --testopt foozle))
85
93
 
86
- instance.options.should include({testopt: 'foozle'})
94
+ expect(instance.options).to include({testopt: 'foozle'})
87
95
  end
88
96
 
89
97
  it 'should set the debug flag on the module when passed --debug' do
90
98
  instance.parse(%w(test --debug))
91
99
 
92
- Harrison::DEBUG.should be_true
100
+ expect(Harrison::DEBUG).to be true
93
101
  end
94
102
  end
95
103
 
@@ -99,20 +107,20 @@ describe Harrison::Base do
99
107
  test_block = Proc.new { |test| "block_output" }
100
108
  instance.run(&test_block)
101
109
 
102
- instance.instance_variable_get("@run_block").should == test_block
110
+ expect(instance.instance_variable_get("@run_block")).to eq(test_block)
103
111
  end
104
112
  end
105
113
 
106
114
  context 'when not given a block' do
107
115
  it 'should return nil if no block stored' do
108
- instance.run.should == nil
116
+ expect(instance.run).to be_nil
109
117
  end
110
118
 
111
119
  it 'should invoke the previously stored block if it exists' do
112
120
  test_block = Proc.new { |test| "block_output" }
113
121
  instance.run(&test_block)
114
122
 
115
- instance.run.should == "block_output"
123
+ expect(instance.run).to eq("block_output")
116
124
  end
117
125
  end
118
126
  end
@@ -124,9 +132,9 @@ describe Harrison::Base do
124
132
  end
125
133
 
126
134
  it 'should delegate downloads to the SSH class' do
127
- expect(@mock_ssh).to receive(:download).with('remote', 'local').and_return(true)
135
+ expect(@mock_ssh).to receive(:download).with('remote', 'local')
128
136
 
129
- instance.download('remote', 'local').should == true
137
+ instance.download('remote', 'local')
130
138
  end
131
139
  end
132
140
 
@@ -137,9 +145,9 @@ describe Harrison::Base do
137
145
  end
138
146
 
139
147
  it 'should delegate uploads to the SSH class' do
140
- expect(@mock_ssh).to receive(:upload).with('local', 'remote').and_return(true)
148
+ expect(@mock_ssh).to receive(:upload).with('local', 'remote')
141
149
 
142
- instance.upload('local', 'remote').should == true
150
+ instance.upload('local', 'remote')
143
151
  end
144
152
  end
145
153
 
@@ -151,9 +159,9 @@ describe Harrison::Base do
151
159
  end
152
160
 
153
161
  it 'should invoke close on ssh instance' do
154
- expect(@mock_ssh).to receive(:close).and_return(true)
162
+ expect(@mock_ssh).to receive(:close)
155
163
 
156
- instance.close.should == true
164
+ instance.close
157
165
  end
158
166
  end
159
167
  end
@@ -164,7 +172,7 @@ describe Harrison::Base do
164
172
  mock_ssh = double(:ssh)
165
173
  expect(Harrison::SSH).to receive(:new).and_return(mock_ssh)
166
174
 
167
- instance.send(:ssh).should == mock_ssh
175
+ expect(instance.send(:ssh)).to be mock_ssh
168
176
  end
169
177
 
170
178
  it 'should return previously instantiated ssh instance' do
@@ -172,7 +180,21 @@ describe Harrison::Base do
172
180
  instance.instance_variable_set('@ssh', mock_ssh)
173
181
  expect(Harrison::SSH).to_not receive(:new)
174
182
 
175
- instance.send(:ssh).should == mock_ssh
183
+ expect(instance.send(:ssh)).to be mock_ssh
184
+ end
185
+ end
186
+
187
+ describe '#remote_regex' do
188
+ it 'should match a standard remote SCP target without a username' do
189
+ expect(instance.send(:remote_regex)).to match("test_host1:/tmp/target")
190
+ end
191
+
192
+ it 'should match a standard remote SCP target with a username' do
193
+ expect(instance.send(:remote_regex)).to match("testuser@test_host:/tmp/target")
194
+ end
195
+
196
+ it 'should not match a local file path' do
197
+ expect(instance.send(:remote_regex)).not_to match("tmp/target")
176
198
  end
177
199
  end
178
200
 
@@ -180,41 +202,43 @@ describe Harrison::Base do
180
202
  it 'should try to create a directory locally' do
181
203
  expect(instance).to receive(:system).with(/local_dir/).and_return(true)
182
204
 
183
- instance.send(:ensure_local_dir, 'local_dir').should == true
205
+ expect(instance.send(:ensure_local_dir, 'local_dir')).to be true
184
206
  end
185
207
 
186
208
  it 'should only try to create a directory once' do
187
209
  expect(instance).to receive(:system).with(/local_dir/).once.and_return(true)
188
210
 
189
- instance.send(:ensure_local_dir, 'local_dir').should == true
190
- instance.send(:ensure_local_dir, 'local_dir').should == true
211
+ expect(instance.send(:ensure_local_dir, 'local_dir')).to be true
212
+ expect(instance.send(:ensure_local_dir, 'local_dir')).to be true
191
213
  end
192
214
  end
193
215
 
194
216
  describe '#ensure_remote_dir' do
195
217
  before(:each) do
196
- @mock_ssh = double(:ssh)
218
+ @mock_ssh = double(:ssh, host: 'test_host1')
219
+ @mock_ssh2 = double(:ssh, host: 'test_host2')
220
+
197
221
  allow(instance).to receive(:ssh).and_return(@mock_ssh)
198
222
  end
199
223
 
200
224
  it 'should try to create a directory remotely' do
201
225
  expect(@mock_ssh).to receive(:exec).with(/remote_dir/).and_return(true)
202
-
203
- instance.send(:ensure_remote_dir, 'testhost', 'remote_dir').should == true
226
+ expect(instance.send(:ensure_remote_dir, 'remote_dir')).to be true
204
227
  end
205
228
 
206
- it 'should try to create a directory once for each distinct host' do
207
- expect(@mock_ssh).to receive(:exec).with(/remote_dir/).twice.and_return(true)
229
+ it 'should try to create a directory once for each distinct ssh connection' do
230
+ expect(@mock_ssh).to receive(:exec).with(/remote_dir/).once.and_return(true)
231
+ expect(@mock_ssh2).to receive(:exec).with(/remote_dir/).once.and_return(true)
208
232
 
209
- instance.send(:ensure_remote_dir, 'test-host', 'remote_dir').should == true
210
- instance.send(:ensure_remote_dir, 'another-host', 'remote_dir').should == true
233
+ expect(instance.send(:ensure_remote_dir, 'remote_dir')).to be true
234
+ expect(instance.send(:ensure_remote_dir, 'remote_dir', @mock_ssh2)).to be true
211
235
  end
212
236
 
213
- it 'should only try to create a directory once for the same host' do
237
+ it 'should only try to create a directory once for the same ssh connection' do
214
238
  expect(@mock_ssh).to receive(:exec).with(/remote_dir/).once.and_return(true)
215
239
 
216
- instance.send(:ensure_remote_dir, 'test-host', 'remote_dir').should == true
217
- instance.send(:ensure_remote_dir, 'test-host', 'remote_dir').should == true
240
+ expect(instance.send(:ensure_remote_dir, 'remote_dir')).to be true
241
+ expect(instance.send(:ensure_remote_dir, 'remote_dir')).to be true
218
242
  end
219
243
  end
220
244
  end
@@ -10,17 +10,17 @@ describe Harrison::Deploy do
10
10
 
11
11
  describe '.initialize' do
12
12
  it 'should add --hosts to arg_opts' do
13
- instance.instance_variable_get('@arg_opts').to_s.should include(':hosts')
13
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':hosts')
14
14
  end
15
15
 
16
16
  it 'should add --env to arg_opts' do
17
- instance.instance_variable_get('@arg_opts').to_s.should include(':env')
17
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':env')
18
18
  end
19
19
 
20
20
  it 'should persist options' do
21
21
  instance = Harrison::Deploy.new(testopt: 'foo')
22
22
 
23
- instance.instance_variable_get('@options').should include(testopt: 'foo')
23
+ expect(instance.instance_variable_get('@options')).to include(testopt: 'foo')
24
24
  end
25
25
  end
26
26
 
@@ -28,16 +28,16 @@ describe Harrison::Deploy do
28
28
  describe '#parse' do
29
29
  it 'should require an artifact to be passed in ARGV' do
30
30
  output = capture(:stderr) do
31
- lambda { instance.parse(%w(deploy)) }.should exit_with_code(1)
31
+ expect(lambda { instance.parse(%w(deploy)) }).to exit_with_code(1)
32
32
  end
33
33
 
34
- output.should include('must', 'specify', 'artifact')
34
+ expect(output).to include('must', 'specify', 'artifact')
35
35
  end
36
36
 
37
37
  it 'should use "base_dir" from Harrisonfile if present' do
38
38
  instance.parse(%w(deploy test_artifact.tar.gz))
39
39
 
40
- instance.options.should include({ base_dir: '/hf_basedir' })
40
+ expect(instance.options).to include({ base_dir: '/hf_basedir' })
41
41
  end
42
42
  end
43
43
 
@@ -68,13 +68,13 @@ describe Harrison::Deploy do
68
68
  test_block = Proc.new { |test| "block_output" }
69
69
  instance.run(&test_block)
70
70
 
71
- instance.instance_variable_get("@run_block").should == test_block
71
+ expect(instance.instance_variable_get("@run_block")).to be test_block
72
72
  end
73
73
  end
74
74
 
75
75
  context 'when not passed a block' do
76
76
  before(:each) do
77
- @mock_ssh = double(:ssh, exec: '', upload: true, download: true)
77
+ @mock_ssh = double(:ssh, host: 'test_host1', exec: '', upload: true, download: true)
78
78
  allow(instance).to receive(:ssh).and_return(@mock_ssh)
79
79
 
80
80
  instance.instance_variable_set(:@run_block, Proc.new { |h| "block for #{h.host}" })
@@ -87,9 +87,9 @@ describe Harrison::Deploy do
87
87
  instance.run
88
88
  end
89
89
 
90
- instance.hosts.should == [ 'argv_host1', 'argv_host2' ]
91
- output.should include('argv_host1', 'argv_host2')
92
- output.should_not include('hf_host')
90
+ expect(instance.hosts).to eq([ 'argv_host1', 'argv_host2' ])
91
+ expect(output).to include('argv_host1', 'argv_host2')
92
+ expect(output).to_not include('hf_host')
93
93
  end
94
94
 
95
95
  it 'should use hosts from Harrisonfile if --hosts not passed' do
@@ -97,18 +97,18 @@ describe Harrison::Deploy do
97
97
  instance.run
98
98
  end
99
99
 
100
- instance.hosts.should == [ 'hf_host' ]
101
- output.should include('hf_host')
100
+ expect(instance.hosts).to eq([ 'hf_host' ])
101
+ expect(output).to include('hf_host')
102
102
  end
103
103
 
104
104
  it 'should require hosts to be set somehow' do
105
105
  instance.hosts = nil
106
106
 
107
107
  output = capture(:stderr) do
108
- lambda { instance.run }.should exit_with_code(1)
108
+ expect(lambda { instance.run }).to exit_with_code(1)
109
109
  end
110
110
 
111
- output.should include('must', 'specify', 'hosts')
111
+ expect(output).to include('must', 'specify', 'hosts')
112
112
  end
113
113
 
114
114
  it 'should invoke the previously stored block once for each host' do
@@ -118,7 +118,34 @@ describe Harrison::Deploy do
118
118
  expect { |b| instance.run(&b); instance.run }.to yield_control.exactly(3).times
119
119
  end
120
120
 
121
- output.should include('host1', 'host2', 'host3')
121
+ expect(output).to include('host1', 'host2', 'host3')
122
+ end
123
+
124
+ context 'when deploying from a remote artifact source' do
125
+ before(:each) do
126
+ instance.artifact = 'test_user@test_host1:/tmp/test_artifact.tar.gz'
127
+ end
128
+
129
+ it 'should invoke scp on the remote host' do
130
+ allow(instance).to receive(:remote_exec).and_return('')
131
+ expect(instance).to receive(:remote_exec).with(/scp test_user@test_host1:\/tmp\/test_artifact.tar.gz/).and_return('')
132
+
133
+ output = capture(:stdout) do
134
+ instance.run
135
+ end
136
+
137
+ expect(output).to include('deployed', 'test_user', 'test_host1', '/tmp/test_artifact.tar.gz')
138
+ end
139
+
140
+ it 'should not invoke Harrison::SSH.upload' do
141
+ expect(@mock_ssh).not_to receive(:upload)
142
+
143
+ output = capture(:stdout) do
144
+ instance.run
145
+ end
146
+
147
+ expect(output).to include('deployed', 'test_user', 'test_host1', '/tmp/test_artifact.tar.gz')
148
+ end
122
149
  end
123
150
  end
124
151
  end
@@ -156,7 +183,7 @@ describe Harrison::Deploy do
156
183
 
157
184
  instance.host = 'test_host'
158
185
 
159
- instance.send(:ssh).should == mock_ssh
186
+ expect(instance.send(:ssh)).to be mock_ssh
160
187
  end
161
188
 
162
189
  it 'should reuse an existing connection to self.host' do
@@ -165,7 +192,7 @@ describe Harrison::Deploy do
165
192
 
166
193
  instance.host = :test_host2
167
194
 
168
- instance.send(:ssh).should == mock_ssh
195
+ expect(instance.send(:ssh)).to be mock_ssh
169
196
  end
170
197
  end
171
198
 
@@ -174,7 +201,7 @@ describe Harrison::Deploy do
174
201
  instance.base_dir = '/test_base_dir'
175
202
  instance.project = 'test_project'
176
203
 
177
- instance.send(:remote_project_dir).should include('/test_base_dir', 'test_project')
204
+ expect(instance.send(:remote_project_dir)).to include('/test_base_dir', 'test_project')
178
205
  end
179
206
  end
180
207
  end
@@ -11,25 +11,25 @@ describe Harrison::Package do
11
11
 
12
12
  describe '.initialize' do
13
13
  it 'should add --commit to arg_opts' do
14
- instance.instance_variable_get('@arg_opts').to_s.should include(':commit')
14
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':commit')
15
15
  end
16
16
 
17
17
  it 'should add --purge to arg_opts' do
18
- instance.instance_variable_get('@arg_opts').to_s.should include(':purge')
18
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':purge')
19
19
  end
20
20
 
21
- it 'should add --pkg-dir to arg_opts' do
22
- instance.instance_variable_get('@arg_opts').to_s.should include(':pkg_dir')
21
+ it 'should add --destination to arg_opts' do
22
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':destination')
23
23
  end
24
24
 
25
25
  it 'should add --remote-dir to arg_opts' do
26
- instance.instance_variable_get('@arg_opts').to_s.should include(':remote_dir')
26
+ expect(instance.instance_variable_get('@arg_opts').to_s).to include(':remote_dir')
27
27
  end
28
28
 
29
29
  it 'should persist options' do
30
30
  instance = Harrison::Package.new(testopt: 'foo')
31
31
 
32
- instance.instance_variable_get('@options').should include(testopt: 'foo')
32
+ expect(instance.instance_variable_get('@options')).to include(testopt: 'foo')
33
33
  end
34
34
  end
35
35
 
@@ -57,16 +57,16 @@ describe Harrison::Package do
57
57
  test_block = Proc.new { |test| "block_output" }
58
58
  instance.run(&test_block)
59
59
 
60
- instance.instance_variable_get("@run_block").should == test_block
60
+ expect(instance.instance_variable_get("@run_block")).to be test_block
61
61
  end
62
62
  end
63
63
 
64
64
  context 'when not passed a block' do
65
65
  before(:each) do
66
- @mock_ssh = double(:ssh, exec: '', upload: true, download: true)
66
+ @mock_ssh = double(:ssh, host: 'test_host1', exec: '', upload: true, download: true)
67
67
  allow(instance).to receive(:ssh).at_least(:once).and_return(@mock_ssh)
68
68
 
69
- allow(instance).to receive(:ensure_local_dir).and_return(true)
69
+ allow(instance).to receive(:ensure_destination).and_return(true)
70
70
  allow(instance).to receive(:resolve_commit!).and_return('test')
71
71
  allow(instance).to receive(:excludes_for_tar).and_return('')
72
72
  end
@@ -79,7 +79,7 @@ describe Harrison::Package do
79
79
  instance.run
80
80
  end
81
81
 
82
- output.should include('block for hf_host')
82
+ expect(output).to include('block for hf_host')
83
83
  end
84
84
  end
85
85
  end
@@ -91,7 +91,7 @@ describe Harrison::Package do
91
91
  instance.remote_dir = '~/.harrison'
92
92
  instance.project = 'test_project'
93
93
 
94
- instance.send(:remote_project_dir).should include('~/.harrison', 'test_project')
94
+ expect(instance.send(:remote_project_dir)).to include('~/.harrison', 'test_project')
95
95
  end
96
96
  end
97
97
 
@@ -100,7 +100,7 @@ describe Harrison::Package do
100
100
  instance.commit = 'giant'
101
101
  expect(instance).to receive(:exec).with(/git rev-parse.*giant/).and_return('fef1f0')
102
102
 
103
- instance.send(:resolve_commit!).should == 'fef1f0'
103
+ expect(instance.send(:resolve_commit!)).to eq('fef1f0')
104
104
  end
105
105
  end
106
106
 
@@ -108,19 +108,35 @@ describe Harrison::Package do
108
108
  it 'should return an empty string if exclude is nil' do
109
109
  instance.exclude = nil
110
110
 
111
- instance.send(:excludes_for_tar).should == ''
111
+ expect(instance.send(:excludes_for_tar)).to be_empty
112
112
  end
113
113
 
114
114
  it 'should return an empty string if exclude is empty' do
115
115
  instance.exclude = []
116
116
 
117
- instance.send(:excludes_for_tar).should == ''
117
+ expect(instance.send(:excludes_for_tar)).to be_empty
118
118
  end
119
119
 
120
120
  it 'should return an --exclude option for each member of exclude' do
121
121
  instance.exclude = [ 'fee', 'fi', 'fo', 'fum' ]
122
122
 
123
- instance.send(:excludes_for_tar).scan(/--exclude/).size.should == instance.exclude.size
123
+ expect(instance.send(:excludes_for_tar).scan(/--exclude/).size).to eq(instance.exclude.size)
124
+ end
125
+ end
126
+
127
+ describe '#ensure_destination' do
128
+ it 'should ensure a local destination' do
129
+ expect(instance).to receive(:ensure_local_dir).with('/tmp/test_path').and_return(true)
130
+
131
+ instance.send(:ensure_destination, '/tmp/test_path')
132
+ end
133
+
134
+ it 'should ensure a remote destination' do
135
+ mock_ssh = double(:ssh, host: 'test_host1')
136
+ expect(mock_ssh).to receive(:exec).with(/\/tmp\/test_path/).and_return(true)
137
+ expect(Harrison::SSH).to receive(:new).with({ host: 'test_host1', user: 'test_user' }).and_return(mock_ssh)
138
+
139
+ instance.send(:ensure_destination, 'test_user@test_host1:/tmp/test_path')
124
140
  end
125
141
  end
126
142
  end
@@ -1,5 +1,245 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Harrison::SSH do
4
- pending
4
+ before(:all) do
5
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
6
+ Harrison.const_set("DEBUG", false)
7
+ end
8
+
9
+ after(:all) do
10
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
11
+ end
12
+
13
+ before(:each) do
14
+ @mock_ssh_chan = double(:ssh_channel)
15
+
16
+ @mock_ssh_conn = double(:ssh_conn, host: 'test.example.com')
17
+ allow(@mock_ssh_conn).to receive(:loop)
18
+ allow(@mock_ssh_conn).to receive(:open_channel).and_yield(@mock_ssh_chan)
19
+
20
+ allow(Net::SSH).to receive(:start).and_return(@mock_ssh_conn)
21
+ end
22
+
23
+ let(:instance) { Harrison::SSH.new(host: 'test.example.com', user: 'test_user') }
24
+
25
+ describe 'initialize' do
26
+ it 'should open an SSH connection' do
27
+ expect(Net::SSH).to receive(:start).with('test.example.com', 'test_user', anything).and_return(double(:ssh_conn))
28
+
29
+ Harrison::SSH.new(host: 'test.example.com', user: 'test_user')
30
+ end
31
+
32
+ context 'when passed a :proxy option' do
33
+ it 'should open an SSH connection with a proxy command' do
34
+ proxy = double(:ssh_proxy)
35
+
36
+ expect(Net::SSH::Proxy::Command).to receive(:new).and_return(proxy)
37
+ expect(Net::SSH).to receive(:start).with('test.example.com', 'test_user', { forward_agent: true, proxy: proxy, timeout: 10 }).and_return(double(:ssh_conn))
38
+
39
+ Harrison::SSH.new(host: 'test.example.com', user: 'test_user', proxy: 'test-proxy.example.com')
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#exec' do
45
+ it 'should exec the passed command on an SSH connection channel' do
46
+ command = "pwd"
47
+ expect(@mock_ssh_chan).to receive(:exec).with(command)
48
+
49
+ instance.exec(command)
50
+ end
51
+
52
+ context 'when the command exits non-zero' do
53
+ before(:each) do
54
+ allow(instance).to receive(:invoke).with(@mock_ssh_conn, anything).and_return({ status: 1, stdout: 'standard output', stderr: 'standard error' })
55
+ end
56
+
57
+ it 'should warn whatever the command emitted to stdout' do
58
+ output = capture(:stderr) do
59
+ instance.exec('cat noexist 2>/dev/null')
60
+ end
61
+
62
+ expect(output).to include('stdout', 'standard output')
63
+ end
64
+
65
+ it 'should warn whatever the command emitted to stderr' do
66
+ output = capture(:stderr) do
67
+ instance.exec('cat noexist 2>/dev/null')
68
+ end
69
+
70
+ expect(output).to include('stderr', 'standard error')
71
+ end
72
+
73
+ it 'should return nil' do
74
+ capture(:stderr) do
75
+ expect(instance.exec('cat noexist 2>/dev/null')).to be_nil
76
+ end
77
+ end
78
+ end
79
+
80
+ context 'when --debug is set' do
81
+ before(:each) do
82
+ allow(instance).to receive(:invoke).with(@mock_ssh_conn, anything).and_return({ status: 0, stdout: 'standard output', stderr: 'standard error' })
83
+ end
84
+
85
+ before(:each) do
86
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
87
+ Harrison.const_set("DEBUG", true)
88
+ end
89
+
90
+ after(:each) do
91
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
92
+ Harrison.const_set("DEBUG", false)
93
+ end
94
+
95
+ it 'should output the command being run' do
96
+ output = capture(:stdout) do
97
+ capture(:stderr) do
98
+ instance.exec('touch testfile')
99
+ end
100
+ end
101
+
102
+ expect(output).to include('info', 'touch testfile')
103
+ end
104
+
105
+ it 'should warn whatever the command emitted to stdout' do
106
+ output = capture(:stderr) do
107
+ capture(:stdout) do
108
+ instance.exec('touch testfile')
109
+ end
110
+ end
111
+
112
+ expect(output).to include('stdout', 'standard output')
113
+ end
114
+
115
+ it 'should warn whatever the command emitted to stderr' do
116
+ output = capture(:stderr) do
117
+ capture(:stdout) do
118
+ instance.exec('touch testfile')
119
+ end
120
+ end
121
+
122
+ expect(output).to include('stderr', 'standard error')
123
+ end
124
+ end
125
+ end
126
+
127
+ describe '#download' do
128
+ before(:each) do
129
+ @mock_scp = double(:net_scp)
130
+ allow(@mock_ssh_conn).to receive(:scp).and_return(@mock_scp)
131
+ end
132
+
133
+ it 'should delegate to net-scp' do
134
+ expect(@mock_scp).to receive(:download!).with('remote', 'local')
135
+
136
+ instance.download('remote', 'local')
137
+ end
138
+
139
+ context 'when --debug is set' do
140
+ before(:each) do
141
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
142
+ Harrison.const_set("DEBUG", true)
143
+ end
144
+
145
+ after(:each) do
146
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
147
+ Harrison.const_set("DEBUG", false)
148
+ end
149
+
150
+ it 'should output what is being downloaded and to where' do
151
+ expect(@mock_scp).to receive(:download!).with('remote', 'local')
152
+
153
+ output = capture(:stdout) do
154
+ instance.download('remote', 'local')
155
+ end
156
+
157
+ expect(output).to include('scp-down', 'local', 'remote')
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#upload' do
163
+ before(:each) do
164
+ @mock_scp = double(:net_scp)
165
+ allow(@mock_ssh_conn).to receive(:scp).and_return(@mock_scp)
166
+ end
167
+
168
+ it 'should delegate to net-scp' do
169
+ expect(@mock_scp).to receive(:upload!).with('local', 'remote')
170
+
171
+ instance.upload('local', 'remote')
172
+ end
173
+
174
+ context 'when --debug is set' do
175
+ before(:each) do
176
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
177
+ Harrison.const_set("DEBUG", true)
178
+ end
179
+
180
+ after(:each) do
181
+ Harrison.send(:remove_const, "DEBUG") if Harrison.const_defined?("DEBUG")
182
+ Harrison.const_set("DEBUG", false)
183
+ end
184
+
185
+ it 'should output what is being uploaded and to where' do
186
+ expect(@mock_scp).to receive(:upload!).with('local', 'remote')
187
+
188
+ output = capture(:stdout) do
189
+ instance.upload('local', 'remote')
190
+ end
191
+
192
+ expect(output).to include('scp-up', 'local', 'remote')
193
+ end
194
+ end
195
+ end
196
+
197
+ describe '#close' do
198
+ it 'should delegate to net-ssh' do
199
+ expect(@mock_ssh_conn).to receive(:close)
200
+
201
+ instance.close
202
+ end
203
+
204
+ context 'when using a proxy command' do
205
+ before(:each) do
206
+ @proxy = double(:ssh_proxy)
207
+ instance.instance_variable_set(:@proxy, @proxy)
208
+ end
209
+
210
+ it 'should send TERM to the proxy command' do
211
+ allow(@mock_ssh_conn).to receive(:transport).and_return(double(:transport, socket: double(:socket, pid: 100000)))
212
+
213
+ expect(Process).to receive(:kill).with("TERM", 100000)
214
+ expect(@mock_ssh_conn).to receive(:close)
215
+
216
+ instance.close
217
+ end
218
+ end
219
+ end
220
+
221
+ describe '#closed?' do
222
+ it 'should delegate to net-ssh' do
223
+ expect(@mock_ssh_conn).to receive(:closed?)
224
+
225
+ instance.closed?
226
+ end
227
+ end
228
+
229
+ describe '#desc' do
230
+ it 'should include the host connected to' do
231
+ expect(instance.desc).to include('test.example.com')
232
+ end
233
+
234
+ context 'when using a proxy host' do
235
+ before(:each) do
236
+ @proxy = double(:ssh_proxy, command_line: "ssh proxy.example.com arg1 arg2")
237
+ instance.instance_variable_set(:@proxy, @proxy)
238
+ end
239
+
240
+ it 'should include the proxy host' do
241
+ expect(instance.desc).to include('proxy.example.com')
242
+ end
243
+ end
244
+ end
5
245
  end
@@ -16,38 +16,38 @@ describe Harrison do
16
16
  describe '.invoke' do
17
17
  it 'should exit when no args are passed' do
18
18
  output = capture(:stderr) do
19
- lambda { Harrison.invoke([]) }.should exit_with_code(1)
19
+ expect(lambda { Harrison.invoke([]) }).to exit_with_code(1)
20
20
  end
21
21
 
22
- output.should include('no', 'command', 'given')
22
+ expect(output).to include('no', 'command', 'given')
23
23
  end
24
24
 
25
25
  it 'should output base help when first arg is --help' do
26
26
  output = capture(:stdout) do
27
- lambda { Harrison.invoke(['--help']) }.should exit_with_code(0)
27
+ expect(lambda { Harrison.invoke(['--help']) }).to exit_with_code(0)
28
28
  end
29
29
 
30
- output.should include('options', 'debug', 'help')
30
+ expect(output).to include('options', 'debug', 'help')
31
31
  end
32
32
 
33
33
  it 'should look for a Harrisonfile' do
34
34
  expect(Harrison).to receive(:find_harrisonfile).and_return(harrisonfile_fixture_path)
35
35
 
36
36
  output = capture(:stderr) do
37
- lambda { Harrison.invoke(['test']) }.should exit_with_code(1)
37
+ expect(lambda { Harrison.invoke(['test']) }).to exit_with_code(1)
38
38
  end
39
39
 
40
- output.should include('unrecognized', 'command', 'test')
40
+ expect(output).to include('unrecognized', 'command', 'test')
41
41
  end
42
42
 
43
43
  it 'should complain if unable to find a Harrisonfile' do
44
44
  expect(Harrison).to receive(:find_harrisonfile).and_return(nil)
45
45
 
46
46
  output = capture(:stderr) do
47
- lambda { Harrison.invoke(['test']) }.should exit_with_code(1)
47
+ expect(lambda { Harrison.invoke(['test']) }).to exit_with_code(1)
48
48
  end
49
49
 
50
- output.should include('could', 'not', 'find', 'harrisonfile')
50
+ expect(output).to include('could', 'not', 'find', 'harrisonfile')
51
51
  end
52
52
  end
53
53
 
@@ -56,7 +56,7 @@ describe Harrison do
56
56
  mock_config = double(:config)
57
57
  expect(Harrison::Config).to receive(:new).and_return(mock_config)
58
58
 
59
- Harrison.config.should == mock_config
59
+ expect(Harrison.config).to be mock_config
60
60
  end
61
61
 
62
62
  it 'should return existing Harrison::Config if defined' do
@@ -64,7 +64,7 @@ describe Harrison do
64
64
  Harrison.class_variable_set(:@@config, mock_config)
65
65
  expect(Harrison::Config).to_not receive(:new)
66
66
 
67
- Harrison.config.should == mock_config
67
+ expect(Harrison.config).to be mock_config
68
68
  end
69
69
 
70
70
  it 'should yield config if given a block' do
@@ -124,13 +124,13 @@ describe Harrison do
124
124
  it 'should find a Harrisonfile if it exists in pwd' do
125
125
  expect(Dir).to receive(:pwd).and_return(fixture_path)
126
126
 
127
- Harrison.send(:find_harrisonfile).should == harrisonfile_fixture_path
127
+ expect(Harrison.send(:find_harrisonfile)).to eq(harrisonfile_fixture_path)
128
128
  end
129
129
 
130
130
  it 'should find a Harrisonfile if it exists in parent of pwd' do
131
131
  expect(Dir).to receive(:pwd).and_return(fixture_path + '/nested')
132
132
 
133
- Harrison.send(:find_harrisonfile).should == harrisonfile_fixture_path
133
+ expect(Harrison.send(:find_harrisonfile)).to eq(harrisonfile_fixture_path)
134
134
  end
135
135
 
136
136
  it 'should return nil if there is no Harrisonfile in tree' do
@@ -140,7 +140,7 @@ describe Harrison do
140
140
  allow(File).to receive(:expand_path).and_call_original
141
141
  allow(File).to receive(:expand_path).with("..", File.dirname(__FILE__)).and_return(File.dirname(__FILE__))
142
142
 
143
- Harrison.send(:find_harrisonfile).should be_nil
143
+ expect(Harrison.send(:find_harrisonfile)).to be_nil
144
144
  end
145
145
  end
146
146
 
@@ -150,7 +150,7 @@ describe Harrison do
150
150
  Harrison.send(:eval_script, fixture_path + '/eval_script.rb')
151
151
  end
152
152
 
153
- output.should == "this file was eval-led\n"
153
+ expect(output).to eq("this file was eval-led\n")
154
154
  end
155
155
  end
156
156
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harrison
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-21 00:00:00.000000000 Z
12
+ date: 2014-08-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -96,17 +96,17 @@ dependencies:
96
96
  requirement: !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
- - - ! '>='
99
+ - - ~>
100
100
  - !ruby/object:Gem::Version
101
- version: '0'
101
+ version: '3.0'
102
102
  type: :development
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
- - - ! '>='
107
+ - - ~>
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: '3.0'
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: debugger
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -149,11 +149,13 @@ extra_rdoc_files: []
149
149
  files:
150
150
  - .gitignore
151
151
  - .rspec
152
+ - .travis.yml
153
+ - CHANGELOG
152
154
  - Gemfile
155
+ - IDEAS
153
156
  - LICENSE.txt
154
157
  - README.md
155
158
  - Rakefile
156
- - TODO
157
159
  - bin/harrison
158
160
  - harrison.gemspec
159
161
  - lib/harrison.rb
@@ -193,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
195
  version: '0'
194
196
  segments:
195
197
  - 0
196
- hash: 1981909616735500300
198
+ hash: -1749031995180641465
197
199
  requirements: []
198
200
  rubyforge_project:
199
201
  rubygems_version: 1.8.29
data/TODO DELETED
@@ -1,26 +0,0 @@
1
- Bugs:
2
- -----
3
- [ ] Test that artifact exists before doing anything for deploy task.
4
-
5
- In Progress:
6
- ------------
7
- [ ] Unit Testing
8
-
9
- Definitely:
10
- -----------
11
- [ ] Rollback action.
12
- [ ] Error handling (with automatic rollback/undo)
13
- [ ] Further de-compose actions so that specific parts can be easily overridden.
14
- [ ] When deploying via a proxy, cache artifact on proxy before deploying to hosts.
15
- [ ] Banner text in --help output.
16
- [ ] Improve unit testing, try to minimize coupling to implementation details.
17
- [ ] Include branch name in artifact file name.
18
-
19
- Maybe:
20
- ------
21
- [ ] Allow more elaborate hosts config, e.g.: h.hosts = [ { host: '10.16.18.207', tags: %w(util migrate) } ]
22
- [ ] Some kind of --dry-run option.
23
- [ ] Allow deploy_via to include alternate user/connection options.
24
- [ ] Rename "releases" => "builds" (and "deploys" => "releases"?)
25
- [ ] Upload artifact to all hosts before the rest of the deployment process begins.
26
- [ ] --force option for deploy task to overwrite an existing release