remote 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ .rvmrc
2
+ *~
3
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Rico Sta. Cruz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ Remote
2
+ ======
3
+
4
+ SSH helpers.
5
+
6
+ Getting started
7
+ ---------------
8
+
9
+ First, install it.
10
+
11
+ gem install remote
12
+
13
+ Then, create a sample config file.
14
+
15
+ remote --sample
16
+
17
+ This generates a file called `remotes.yml`. Edit this sample file. (Tip: you can also put this in `config/remotes.yml` -- useful for Rails/Sinatra projects.)
18
+
19
+ # remotes.yml
20
+ live:
21
+ host: foo.mysite.com
22
+ user: app
23
+ key: ~/.ssh/id_rsa
24
+ path: /home/app/myapp
25
+
26
+ Now, you may easily run commands on your given servers.
27
+
28
+ remote live rake db:migrate
29
+ # Equivalent of running `ssh -i ~/.ssh/id_rsa app@foo.mysite.com -- rake db:migrate`
30
+
31
+ Going further
32
+ -------------
33
+
34
+ You may even define aliases for complicated commands.
35
+
36
+ # remotes.yml
37
+ live:
38
+ host: foo.mysite.com
39
+ commands:
40
+ deploy: |
41
+ git pull
42
+ thin -C config/thin.yml restart
43
+ rake cdn:propogate
44
+ echo Deployed new version `rake app:version`
45
+
46
+ You may then run it easily with one command:
47
+
48
+ remote live deploy
49
+ # Runs the deploy script you've defined in the config file
50
+
51
+ You may also define aliases than take arguments.
52
+
53
+ # remotes.yml
54
+ live:
55
+ host: foo.mysite.com
56
+ commands:
57
+ thor: env RACK_ENV=production rake %s
58
+
59
+ So then you may:
60
+
61
+ remote live rake app:version
62
+ # Executes 'env RACK_ENV=production rake app:version' on the remote server
63
+
64
+ Using in Ruby
65
+ -------------
66
+
67
+ require 'remote'
68
+
69
+ r = Remote::App.new
70
+ r = Remote::App.new(:config => 'servers.yml') # Optional
71
+
72
+ r.run 'live', 'ls' # Same as 'remote live ls'
73
+ r.servers #=> [<Server 'development'>, <Server 'production'>, ...]
74
+ r.write_sample # same as 'remote --sample'
75
+
76
+ r = Remote::App.new(:console => true)
77
+ r.list # Same as 'remote --list'
78
+ r.help # Same as 'remote --help'
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "remote"
8
+ gem.summary = "Helpers to execute complex commands in multiple servers via SSH"
9
+ gem.description = "Remote lets you define a YAML config file with your servers and command aliases, and let you run them easily from the command line."
10
+ gem.email = "rico@sinefunc.com"
11
+ gem.homepage = "http://github.com/sinefunc/remote"
12
+ gem.authors = ["Rico Sta. Cruz"]
13
+ # gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ prefix = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ require "#{prefix}/remote"
5
+
6
+ app = Remote::App.new :console => true
7
+
8
+ if __FILE__ == $0
9
+ if ARGV.first == '--sample'
10
+ app.write_sample
11
+
12
+ elsif ARGV.first == "--list"
13
+ app.list
14
+
15
+ elsif ARGV.empty? or ARGV == ['-h'] or ARGV == ['--help'] or ARGV == ['-?']
16
+ cmd = File.basename($0)
17
+ app.help(cmd)
18
+
19
+ else
20
+ servers = ARGV.shift.to_s.split(',')
21
+ command = ARGV.join(' ')
22
+
23
+ app.run servers, *command
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ module Remote
2
+ PREFIX = File.expand_path(File.join(File.dirname(__FILE__), 'remote'))
3
+
4
+ autoload :Server, "#{PREFIX}/server"
5
+ autoload :App, "#{PREFIX}/app"
6
+ autoload :Printer, "#{PREFIX}/printer"
7
+ end
@@ -0,0 +1,154 @@
1
+ require 'yaml'
2
+
3
+ module Remote
4
+ class App
5
+ def initialize(options={})
6
+ if options[:config]
7
+ @config_file = [options[:config]].flatten
8
+ end
9
+
10
+ self.extend Printer if options[:console]
11
+ end
12
+
13
+ # Returns the config file location.
14
+ def config_file
15
+ config_file_locations.detect { |f| File.exists? (f) }
16
+ end
17
+
18
+ # Returns a list of where configuration files are expected to be present.
19
+ def config_file_locations
20
+ @config_file || ['config/remotes.yml', 'remotes.yml']
21
+ end
22
+
23
+ # Returns the configuration hash.
24
+ def config
25
+ verify_config
26
+ @config ||= YAML::load_file(config_file)
27
+ end
28
+
29
+ def servers
30
+ return @servers unless @servers.nil?
31
+ @servers = Hash.new
32
+ config.each { |name, data| @servers[name] = Server.new(name, data) }
33
+ @servers
34
+ end
35
+
36
+ def list
37
+ servers.keys.each { |name| log " #{name}" }
38
+ end
39
+
40
+ def run(to, *cmd)
41
+ verify_config
42
+ [to].flatten.each do |server_name|
43
+ svr = servers[server_name]
44
+ if svr.nil?
45
+ log "Unknown server '#{server_name}'. Available servers are:"
46
+ list
47
+ log "See #{config_file} for more information."
48
+ exit
49
+ end
50
+
51
+ command = svr.to_cmd(*cmd)
52
+ what = cmd.any? ? cmd.join(' ') : 'console'
53
+ status svr.to_s, what
54
+ system command
55
+ end
56
+ end
57
+
58
+ def write_sample
59
+ unless config_file.nil?
60
+ log "A configuration file already exists in #{config_file}."
61
+ return
62
+ end
63
+
64
+ fname = config_file_locations.last
65
+ begin
66
+ File.open(fname, 'w') { |f| f.write(sample_data) }
67
+ log "Created the file #{fname}."
68
+ log "Edit this file with your list of servers, then type 'remote <yourserver>' to try it out."
69
+ rescue => e
70
+ log "Error: unable to save to #{fname}."
71
+ end
72
+ end
73
+
74
+ def help(cmd='remote')
75
+ log "Usage: #{cmd} <server>"
76
+ log " Opens a console session in the given server."
77
+ log ""
78
+ log "Usage: #{cmd} <server> <command>"
79
+ log " Executes the given command in the given server."
80
+ log ""
81
+ log "Usage: remote <svr1>,<svr2> <command>"
82
+ log " Executes the given command in multiple servers."
83
+ log ""
84
+ log "Usage: remote --list"
85
+ log " Lists available servers."
86
+ log ""
87
+ log "Configuration"
88
+ log "-------------"
89
+ log ""
90
+ log "Servers are defined in a config file. Use `#{cmd} --sample` to"
91
+ log "create a sample config file."
92
+ log ""
93
+
94
+ if config_file.nil?
95
+ log "Config files are searched for in:"
96
+ log " " + config_file_locations.join(", ")
97
+ else
98
+ log "Your configuration file is in #{config_file}."
99
+ end
100
+
101
+ log ""
102
+ log "Examples"
103
+ log "--------"
104
+ log ""
105
+ log "1) Executes 'irb -r./init' in the server called 'live'."
106
+ log " #{cmd} live irb -r./init"
107
+ log ""
108
+ log "2) Starts a console for the 'live' server."
109
+ log " #{cmd} live"
110
+ log ""
111
+ end
112
+
113
+ protected
114
+ def status(where, what)
115
+ end
116
+
117
+ def log(what)
118
+ end
119
+
120
+ def verify_config
121
+ if config_file.nil?
122
+ log "Error: no config file is present."
123
+ exit
124
+ end
125
+ end
126
+
127
+ def sample_data
128
+ <<-adios.gsub(/^ {6}/, '')
129
+ staging: &defaults
130
+ host: staging.myserver.com
131
+ # Everything below are optional.
132
+ user: rsc
133
+ path: /home/rsc/app/current
134
+ key: ~/.ssh/id_rsa.pub
135
+ commands:
136
+ # These are optional aliases.
137
+ # Typing 'remoter staging rake x' will execute this, with %s replaced by 'x'.
138
+ rake: 'bin/rake %s'
139
+
140
+ # Typing 'remoter staging deploy' will execute this script.
141
+ deploy: |
142
+ echo Deploying...
143
+ git pull
144
+ echo Done!
145
+
146
+ # This is an example of how to define another server via inheritance.
147
+ live:
148
+ <<: *defaults
149
+ path: /home/rsc/app
150
+ host: www.myserver.com
151
+ adios
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,14 @@
1
+ module Remote
2
+ module Printer
3
+ def log(str)
4
+ $stderr << "#{str}\n"
5
+ end
6
+
7
+ def status(where, what)
8
+ c1 = "\033[0;30m"
9
+ c2 = "\033[0;33m"
10
+ c0 = "\033[0m"
11
+ log "#{c1}[#{where}]$#{c2} #{what}#{c0}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ require 'ostruct'
2
+ require 'shellwords'
3
+
4
+ module Remote
5
+ class Server < OpenStruct
6
+ def initialize(name, data)
7
+ super data
8
+ self.host ||= name
9
+ self.commands ||= Hash.new
10
+ end
11
+
12
+ def to_s
13
+ host.to_s
14
+ end
15
+
16
+ # Construct an SSH command line for the given command.
17
+ def to_cmd(*what)
18
+ hostname = self.user.nil? ? self.host.to_s : "#{self.user}@#{self.host}"
19
+
20
+ cmd = [ "ssh", hostname ]
21
+ cmd << "-i #{self.key}" unless self.key.nil?
22
+
23
+ [ cmd.join(' '), translate(*what) ].compact.join(' -- ')
24
+ end
25
+
26
+ # Translates a given set of commands, taking into account aliases.
27
+ #
28
+ # translate('git pull', 'thor app:restart') #=> "cd ~/x; git pull; env RACK_ENV=production thor app:restart"
29
+ def translate(*cmds)
30
+ return nil if cmds.empty?
31
+
32
+ ret = []
33
+ ret << "cd #{self.path}" unless self.path.nil?
34
+ cmds.each { |full_cmd| ret << translate_single(full_cmd) }
35
+ ret.flatten.compact.join(';').shellescape
36
+ end
37
+
38
+ # Translates a single command by resolving aliases.
39
+ def translate_single(full_cmd)
40
+ key, *args = full_cmd.split(' ')
41
+ command_alias = self.commands[key]
42
+ unless command_alias.nil?
43
+ command_alias.gsub("\n",';').gsub('%s', args.join(' '))
44
+ else
45
+ full_cmd
46
+ end
47
+ end
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remote
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Rico Sta. Cruz
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-26 00:00:00 +08:00
18
+ default_executable: remote
19
+ dependencies: []
20
+
21
+ description: Remote lets you define a YAML config file with your servers and command aliases, and let you run them easily from the command line.
22
+ email: rico@sinefunc.com
23
+ executables:
24
+ - remote
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - LICENSE
29
+ - README.md
30
+ files:
31
+ - .gitignore
32
+ - LICENSE
33
+ - README.md
34
+ - Rakefile
35
+ - bin/remote
36
+ - lib/remote.rb
37
+ - lib/remote/app.rb
38
+ - lib/remote/printer.rb
39
+ - lib/remote/server.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/sinefunc/remote
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.7
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Helpers to execute complex commands in multiple servers via SSH
72
+ test_files: []
73
+