mobilize-ssh 1.297 → 1.298

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/README.md CHANGED
@@ -13,6 +13,7 @@ Table Of Contents
13
13
  * [Install Dirs and Files](#section_Install_Dirs_and_Files)
14
14
  * [Configure](#section_Configure)
15
15
  * [Ssh](#section_Configure_Ssh)
16
+ * [Git](#section_Configure_Git)
16
17
  * [Start](#section_Start)
17
18
  * [Create Job](#section_Start_Create_Job)
18
19
  * [Run Test](#section_Start_Run_Test)
@@ -79,9 +80,6 @@ Configure
79
80
  ### Configure Ssh
80
81
 
81
82
  The Ssh configuration consists of:
82
- * tmp_file_dir, which is where files will be stored before being scp'd
83
- over to the nodes. They will be deleted afterwards, unless the job
84
- fails in mid-copy. By default this is tmp/file/.
85
83
  * nodes, identified by aliases, such as `test_node`. This alias is what you should
86
84
  pass into the "node" param over in the ssh.run task.
87
85
  * if no node is specified, commands will default to the first node listed.
@@ -109,7 +107,6 @@ Sample ssh.yml:
109
107
  ``` yml
110
108
  ---
111
109
  development:
112
- tmp_file_dir: tmp/file/
113
110
  nodes:
114
111
  dev_node:
115
112
  sudoers:
@@ -126,7 +123,6 @@ development:
126
123
  port: 22
127
124
  user: gateway_user
128
125
  test:
129
- tmp_file_dir: tmp/file/
130
126
  nodes:
131
127
  test_node:
132
128
  sudoers:
@@ -143,7 +139,6 @@ test:
143
139
  port: 22
144
140
  user: gateway_user
145
141
  production:
146
- tmp_file_dir: tmp/file/
147
142
  nodes:
148
143
  prod_node:
149
144
  sudoers:
@@ -161,6 +156,57 @@ production:
161
156
  user: gateway_user
162
157
  ```
163
158
 
159
+ <a name='section_Configure_Git'></a>
160
+ ### Configure Git
161
+
162
+ Git configuration is not required but recommended, as it allows you to
163
+ pull files directly from public or private Git repositories.
164
+
165
+ The Git configuration consists of:
166
+ * domains, identified by aliases, such as `private` and `public`.
167
+ * domains are passed into the source parameters in the git.run task.
168
+ * if no domain is specified, commands will default to the first domain listed.
169
+
170
+ Each domain has:
171
+ * a host;
172
+ * a key (optional); If you don't need an ssh key to access the repo, remove that row from the configuration file.
173
+ * this is the relative path of the ssh key required to access the repository.
174
+ * a user, which is the user used for the git clone command.
175
+
176
+ Sample git.yml:
177
+
178
+ ``` yml
179
+ ---
180
+ development:
181
+ domains:
182
+ private:
183
+ host: github.<domain>.com
184
+ key: config/mobilize/ssh_private.key
185
+ user: git
186
+ public:
187
+ host: github.com
188
+ user: git
189
+ test:
190
+ domains:
191
+ private:
192
+ host: github.<domain>.com
193
+ key: config/mobilize/ssh_private.key
194
+ user: git
195
+ public:
196
+ host: github.com
197
+ user: git
198
+ production:
199
+ domains:
200
+ private:
201
+ host: github.<domain>.com
202
+ key: config/mobilize/ssh_private.key
203
+ user: git
204
+ public:
205
+ host: github.com
206
+ user: git
207
+ ```
208
+
209
+
164
210
  <a name='section_Start'></a>
165
211
  Start
166
212
  -----
@@ -173,6 +219,10 @@ Start
173
219
  * user, sources, and node are optional; cmd is required.
174
220
  * specifying user will cause the command to be prefixed with sudo su <user> -c.
175
221
  * non-google sources will also be read as the specified user.
222
+ * git sources can be specified with syntax `git://<domain>/<repo_owner>/<repo_name>/<file_path>`.
223
+ * Accessing private repos requires that you add the Mobilize public key to the repository as a deploy key.
224
+ * there is no user-level access control for git repositories at this time.
225
+ * domain defaults to the first one listed, if not included.
176
226
  * not specifying node will cause the command to be run on the default node.
177
227
  * ssh sources can be specified with syntax
178
228
  `ssh://<node><file_full_path>`. If node is omitted, default node will be used.
@@ -0,0 +1,111 @@
1
+ module Mobilize
2
+ module Git
3
+ def Git.config
4
+ Base.config('git')
5
+ end
6
+
7
+ def Git.host(domain)
8
+ Git.config['domains'][domain]['host']
9
+ end
10
+
11
+ def Git.domains
12
+ Git.config['domains'].keys
13
+ end
14
+
15
+ def Git.default_domain
16
+ Git.domains.first
17
+ end
18
+
19
+ # converts a source path or target path to a dst in the context of handler and stage
20
+ def Git.path_to_dst(path,stage_path,gdrive_slot)
21
+ red_path = path.split("://").last
22
+ git_url = Git.url_by_path(red_path)
23
+ return Dataset.find_or_create_by_url(git_url)
24
+ end
25
+
26
+ def Git.url_by_path(path)
27
+ path_nodes = path.split("/")
28
+ domain = path_nodes.first.to_s
29
+ revision = "HEAD"
30
+ if Git.domains.include?(domain)
31
+ repo = path_nodes[1..2].join("/")
32
+ file_path = path_nodes[3..-1].join("/")
33
+ else
34
+ domain = Git.default_domain
35
+ repo = path_nodes[0..1].join("/")
36
+ file_path = path_nodes[2..-1].join("/")
37
+ end
38
+ url = "git://#{domain}/#{repo}/#{revision}/#{file_path}"
39
+ return url
40
+ end
41
+
42
+ #return path to tar.gz of git repo
43
+ def Git.pack(domain,repo,revision="HEAD")
44
+ repo_dir = Git.pull(domain,repo,revision)
45
+ repo_name = repo.split("/").last
46
+ tar_gz_path = "#{repo_dir}/../#{repo_name}.tar.gz"
47
+ pack_cmd = "cd #{repo_dir} && git archive #{revision} --format=tar.gz > #{tar_gz_path}"
48
+ pack_cmd.bash(true)
49
+ FileUtils.rm_r(repo_dir,:force=>true)
50
+ return tar_gz_path
51
+ end
52
+
53
+ #confirm that git file exists
54
+ def Git.exists?(url)
55
+ domain,repo,revision,file_path=[]
56
+ url.split("/").ie do |url_nodes|
57
+ domain = url_nodes[2]
58
+ repo = url_nodes[3..4].join("/")
59
+ revision = url_nodes[5]
60
+ file_path = url_nodes[6..-1].join("/")
61
+ end
62
+ repo_dir = Git.pull(domain,repo,revision)
63
+ full_path = "#{repo_dir}/#{file_path}"
64
+ exists = File.exists?(full_path)
65
+ if exists
66
+ FileUtils.rm_r(repo_dir,:force=>true)
67
+ return exists
68
+ else
69
+ raise "Unable to find #{full_path}"
70
+ end
71
+ end
72
+
73
+ #pulls a git repo and sets it to the specified revision in the
74
+ #specified folder
75
+ def Git.pull(domain,repo,revision,run_dir=Dir.mktmpdir)
76
+ domain_properties = Git.config['domains'][domain]
77
+ user,host,key = ['user','host','key'].map{|k| domain_properties[k]}
78
+ #create folder for repo and command
79
+ run_file_path = run_dir + "/cmd.sh"
80
+ #put together command
81
+ git_prefix = key ? "ssh-add #{Base.root}/#{key};" : ""
82
+ git_suffix = (revision=="HEAD" ? " --depth=1" : "; git checkout -q #{revision}")
83
+ #add keys, clone repo, go to specific revision, execute command
84
+ full_cmd = "cd #{run_dir};#{git_prefix}git clone -q #{user}@#{host}:#{repo}.git#{git_suffix}"
85
+ #put command in file, run ssh-agent bash on it
86
+ File.open(run_file_path,"w") {|f| f.print(full_cmd)}
87
+ run_cmd = "ssh-agent bash #{run_file_path}"
88
+ #run the command, it will return an exception if there are issues
89
+ run_cmd.bash(true)
90
+ repo_name = repo.split("/").last
91
+ repo_dir = "#{run_dir}/#{repo_name}"
92
+ return repo_dir
93
+ end
94
+
95
+ def Git.read_by_dataset_path(dst_path,user_name,*args)
96
+ domain,repo,revision,file_path = []
97
+ dst_path.split("/").ie do |path_nodes|
98
+ domain = path_nodes[0]
99
+ repo = path_nodes[1..2].join("/")
100
+ revision = path_nodes[3]
101
+ file_path = path_nodes[4..-1].join("/")
102
+ end
103
+ #slash in front of path
104
+ repo_dir = Git.pull(domain,repo,revision)
105
+ full_path = "#{repo_dir}/#{file_path}"
106
+ result = "cat #{full_path}".bash(true)
107
+ FileUtils.rm_r(repo_dir,:force=>true)
108
+ return result
109
+ end
110
+ end
111
+ end
@@ -1,45 +1,8 @@
1
1
  module Mobilize
2
2
  module Ssh
3
- def Ssh.config
4
- Base.config('ssh')
5
- end
6
-
7
- def Ssh.tmp_file_dir
8
- Ssh.config['tmp_file_dir']
9
- end
10
-
11
- def Ssh.host(node)
12
- Ssh.config['nodes'][node]['host']
13
- end
14
-
15
- def Ssh.gateway(node)
16
- Ssh.config['nodes'][node]['gateway']
17
- end
18
-
19
- def Ssh.sudoers(node)
20
- Ssh.config['nodes'][node]['sudoers']
21
- end
22
-
23
- def Ssh.su_all_users(node)
24
- Ssh.config['nodes'][node]['su_all_users']
25
- end
26
-
27
- def Ssh.nodes
28
- Ssh.config['nodes'].keys
29
- end
30
-
31
- def Ssh.default_node
32
- Ssh.nodes.first
33
- end
34
-
35
- #determine if current machine is on host domain, needs gateway if one is provided and it is not
36
- def Ssh.needs_gateway?(node)
37
- host_domain_name = Ssh.host(node)['name'].split(".")[-2..-1].join(".")
38
- return true if Ssh.gateway(node) and Socket.domain_name != host_domain_name
39
- end
40
-
3
+ #adds convenience methods
4
+ require "#{File.dirname(__FILE__)}/../helpers/ssh_helper"
41
5
  def Ssh.pop_comm_dir(comm_dir,file_hash)
42
- FileUtils.rm_r comm_dir, :force=>true
43
6
  file_hash.each do |fname,fdata|
44
7
  fpath = "#{comm_dir}/#{fname}"
45
8
  #for now, only gz is binary
@@ -50,18 +13,6 @@ module Mobilize
50
13
  return true if file_hash.keys.length>0
51
14
  end
52
15
 
53
- def Ssh.set_key_permissions(key_path)
54
- #makes sure permissions are set as appropriate for ssh key
55
- raise "could not find ssh key at #{key_path}" unless File.exists?(key_path)
56
- #keys named with .pem are
57
- if key_path.ends_with?(".pem")
58
- File.chmod(0400,key_path) unless File.stat(key_path).mode.to_s(8)[3..5] == "400"
59
- else
60
- File.chmod(0600,key_path) unless File.stat(key_path).mode.to_s(8)[3..5] == "600"
61
- end
62
- return true
63
- end
64
-
65
16
  # converts a source path or target path to a dst in the context of handler and stage
66
17
  def Ssh.path_to_dst(path,stage_path,gdrive_slot)
67
18
  has_handler = true if path.index("://")
@@ -102,7 +53,6 @@ module Mobilize
102
53
  def Ssh.scp(node,from_path,to_path)
103
54
  name,key,port,user = Ssh.host(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
104
55
  key_path = "#{Base.root}/#{key}"
105
- Ssh.set_key_permissions(key_path)
106
56
  opts = {:port=>(port || 22),:keys=>key_path}
107
57
  if Ssh.needs_gateway?(node)
108
58
  gname,gkey,gport,guser = Ssh.gateway(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
@@ -117,25 +67,31 @@ module Mobilize
117
67
  return true
118
68
  end
119
69
 
120
- def Ssh.run(node,command,user,file_hash={})
121
- key,default_user = Ssh.host(node).ie{|h| ['key','user'].map{|k| h[k]}}
122
- key_path = "#{Base.root}/#{key}"
123
- Ssh.set_key_permissions(key_path)
70
+ def Ssh.run(node,command,user,file_hash={},params={})
71
+ default_user = Ssh.host(node)['user']
124
72
  file_hash ||= {}
125
73
  #make sure the dir for this command is clear
126
74
  comm_md5 = [user,node,command,file_hash.keys.to_s,Time.now.to_f.to_s].join.to_md5
127
- comm_dir = "#{Ssh.tmp_file_dir}#{comm_md5}"
75
+ comm_dir = Dir.mktmpdir
76
+ #add in default methods to the params
77
+ params.merge(Ssh.default_params)
78
+ #replace any params in the file_hash and command
79
+ params.each do |k,v|
80
+ command.gsub!(k,v)
81
+ file_hash.each do |name,data|
82
+ data.gsub!(k,v)
83
+ end
84
+ end
128
85
  #populate comm dir with any files
129
86
  Ssh.pop_comm_dir(comm_dir,file_hash)
130
- #move any files up to the node
131
- rem_dir = nil
132
87
  #make sure user starts in rem_dir
133
88
  rem_dir = "#{comm_md5}/"
134
89
  #make sure the rem_dir is gone
135
90
  Ssh.fire!(node,"sudo rm -rf #{rem_dir}")
136
91
  if File.exists?(comm_dir)
137
92
  Ssh.scp(node,comm_dir,rem_dir)
138
- FileUtils.rm_r comm_dir, :force=>true
93
+ #make sure comm_dir is removed
94
+ FileUtils.rm_r(comm_dir,:force=>true)
139
95
  else
140
96
  #create folder
141
97
  mkdir_command = "mkdir #{rem_dir}"
@@ -163,7 +119,6 @@ module Mobilize
163
119
  def Ssh.fire!(node,cmd)
164
120
  name,key,port,user = Ssh.host(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
165
121
  key_path = "#{Base.root}/#{key}"
166
- Ssh.set_key_permissions(key_path)
167
122
  opts = {:port=>(port || 22),:keys=>key_path}
168
123
  response = if Ssh.needs_gateway?(node)
169
124
  gname,gkey,gport,guser = Ssh.gateway(node).ie{|h| ['name','key','port','user'].map{|k| h[k]}}
@@ -193,17 +148,15 @@ module Mobilize
193
148
  def Ssh.write(node,fdata,to_path,binary=false)
194
149
  from_path = Ssh.tmp_file(fdata,binary)
195
150
  Ssh.scp(node,from_path,to_path)
196
- FileUtils.rm from_path
151
+ #make sure local is removed
152
+ FileUtils.rm_r(from_path,:force=>true)
197
153
  return true
198
154
  end
199
155
 
200
156
  def Ssh.tmp_file(fdata,binary=false,fpath=nil)
201
157
  #creates a file under tmp/files with an md5 from the data
202
- tmp_file_path = fpath || "#{Ssh.tmp_file_dir}#{(fdata + Time.now.utc.to_f.to_s).to_md5}"
158
+ tmp_file_path = fpath || "#{Dir.mktmpdir}/#{(fdata + Time.now.utc.to_f.to_s).to_md5}"
203
159
  write_mode = binary ? "wb" : "w"
204
- #make sure folder is created
205
- tmp_file_dir = tmp_file_path.split("/")[0..-2].join("/")
206
- FileUtils.mkdir_p(tmp_file_dir)
207
160
  #write data to path
208
161
  File.open(tmp_file_path,write_mode) {|f| f.print(fdata)}
209
162
  return tmp_file_path
@@ -268,6 +221,8 @@ module Mobilize
268
221
  response = {}
269
222
  response['out_url'] = Dataset.write_by_url("gridfs://#{s.path}/out",result['stdout'].to_s,Gdrive.owner_name)
270
223
  response['err_url'] = Dataset.write_by_url("gridfs://#{s.path}/err",result['stderr'].to_s,Gdrive.owner_name) if result['stderr'].to_s.length>0
224
+ #is an error if there is no out and there is an err, regardless of signal
225
+ result['exit_code'] = 500 if result['stdout'].to_s.strip.length==0 and result['stderr'].to_s.strip.length>0
271
226
  response['signal'] = result['exit_code']
272
227
  response
273
228
  end
@@ -0,0 +1,45 @@
1
+ module Mobilize
2
+ module Ssh
3
+ def self.config
4
+ Base.config('ssh')
5
+ end
6
+
7
+ def self.host(node)
8
+ self.config['nodes'][node]['host']
9
+ end
10
+
11
+ def self.gateway(node)
12
+ self.config['nodes'][node]['gateway']
13
+ end
14
+
15
+ def self.sudoers(node)
16
+ self.config['nodes'][node]['sudoers']
17
+ end
18
+
19
+ def self.su_all_users(node)
20
+ self.config['nodes'][node]['su_all_users']
21
+ end
22
+
23
+ def self.nodes
24
+ self.config['nodes'].keys
25
+ end
26
+
27
+ def self.default_node
28
+ self.nodes.first
29
+ end
30
+
31
+ #determine if current machine is on host domain, needs gateway if one is provided and it is not
32
+ def self.needs_gateway?(node)
33
+ host_domain_name = self.host(node)['name'].split(".")[-2..-1].join(".")
34
+ return true if self.gateway(node) and Socket.domain_name != host_domain_name
35
+ end
36
+
37
+ def self.default_params
38
+ time = Time.now.utc
39
+ {
40
+ '$utc_date'=>time.strftime("%Y-%m-%d"),
41
+ '$utc_time'=>time.strftime("%H:%M"),
42
+ }
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  module Mobilize
2
2
  module Ssh
3
- VERSION = "1.297"
3
+ VERSION = "1.298"
4
4
  end
5
5
  end
data/lib/mobilize-ssh.rb CHANGED
@@ -12,3 +12,4 @@ module Mobilize
12
12
  end
13
13
  end
14
14
  require "mobilize-ssh/handlers/ssh"
15
+ require "mobilize-ssh/handlers/git"
data/mobilize-ssh.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
- gem.add_runtime_dependency "mobilize-base","1.297"
19
+ gem.add_runtime_dependency "mobilize-base","1.298"
20
20
  gem.add_runtime_dependency "net-ssh"
21
21
  gem.add_runtime_dependency "net-scp"
22
22
  gem.add_runtime_dependency "net-ssh-gateway"
@@ -14,8 +14,8 @@ describe "Mobilize" do
14
14
 
15
15
  gdrive_slot = Mobilize::Gdrive.owner_email
16
16
  puts "create user 'mobilize'"
17
- user = gdrive_slot.split("@").first
18
- u = Mobilize::User.where(:name=>user).first
17
+ user_name = gdrive_slot.split("@").first
18
+ u = Mobilize::User.where(:name=>user_name).first
19
19
  r = u.runner
20
20
 
21
21
  rb_code_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/code.rb",gdrive_slot)
@@ -42,6 +42,7 @@ describe "Mobilize" do
42
42
  ssh_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_1.out",gdrive_slot)
43
43
  ssh_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_2.out",gdrive_slot)
44
44
  ssh_target_sheet_3 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_3.out",gdrive_slot)
45
+ ssh_target_sheet_4 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_4.out",gdrive_slot)
45
46
  [ssh_target_sheet_1,ssh_target_sheet_2,ssh_target_sheet_3].each {|s| s.delete if s}
46
47
 
47
48
  ssh_job_rows = ::YAML.load_file("#{Mobilize::Base.root}/test/ssh_job_rows.yml")
@@ -59,10 +60,12 @@ describe "Mobilize" do
59
60
  ssh_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_1.out",gdrive_slot)
60
61
  ssh_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_2.out",gdrive_slot)
61
62
  ssh_target_sheet_3 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_3.out",gdrive_slot)
63
+ ssh_target_sheet_4 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/test_ssh_4.out",gdrive_slot)
62
64
 
63
65
  assert ssh_target_sheet_1.to_tsv.length > 100
64
66
  assert ssh_target_sheet_2.to_tsv.length > 100
65
67
  assert ssh_target_sheet_3.to_tsv.length > 3
68
+ assert ssh_target_sheet_4.to_tsv.length > 100
66
69
 
67
70
  end
68
71
 
@@ -1,3 +1,4 @@
1
+ ---
1
2
  - name: test_ssh_1
2
3
  active: true
3
4
  trigger: once
@@ -16,3 +17,9 @@
16
17
  status: ""
17
18
  stage1: 'ssh.run cmd:"whoami"'
18
19
  stage2: 'gsheet.write source:"stage1", target:"test_ssh_3.out"'
20
+ - name: test_ssh_4
21
+ active: true
22
+ trigger: "after test_ssh_3"
23
+ status: ""
24
+ stage1: 'ssh.run node:"test_node", user:root, sources:["git://DeNA/mobilize-ssh/test/code.rb","git://DeNA/mobilize-ssh/test/code.sh"], cmd:"ruby code.rb"'
25
+ stage2: 'gsheet.write source:stage1, target:"test_ssh_4.out"'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mobilize-ssh
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.297'
4
+ version: '1.298'
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: 2013-04-06 00:00:00.000000000 Z
12
+ date: 2013-04-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mobilize-base
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: '1.297'
21
+ version: '1.298'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: '1.297'
29
+ version: '1.298'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: net-ssh
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -91,7 +91,9 @@ files:
91
91
  - lib/mobilize-ssh/extensions/net-ssh-connection-session.rb
92
92
  - lib/mobilize-ssh/extensions/net-ssh-gateway.rb
93
93
  - lib/mobilize-ssh/extensions/socket.rb
94
+ - lib/mobilize-ssh/handlers/git.rb
94
95
  - lib/mobilize-ssh/handlers/ssh.rb
96
+ - lib/mobilize-ssh/helpers/ssh_helper.rb
95
97
  - lib/mobilize-ssh/tasks.rb
96
98
  - lib/mobilize-ssh/version.rb
97
99
  - lib/samples/ssh.yml
@@ -117,7 +119,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
119
  version: '0'
118
120
  segments:
119
121
  - 0
120
- hash: 1145721245595334784
122
+ hash: 2943604812785338750
121
123
  required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  none: false
123
125
  requirements:
@@ -126,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
128
  version: '0'
127
129
  segments:
128
130
  - 0
129
- hash: 1145721245595334784
131
+ hash: 2943604812785338750
130
132
  requirements: []
131
133
  rubyforge_project:
132
134
  rubygems_version: 1.8.25