remote 0.0.1

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.
@@ -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
+