scatter 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,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'scatter'
4
+ require 'optparse'
5
+
6
+ Scatter::CLI.new(STDOUT).run(ARGV)
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/scatter/cli'
2
+ require File.dirname(__FILE__) + '/scatter/command'
3
+ require File.dirname(__FILE__) + '/scatter/super_command'
4
+ require File.dirname(__FILE__) + '/scatter/sub_command'
5
+ require File.dirname(__FILE__) + '/scatter/commands/list'
6
+ require File.dirname(__FILE__) + '/scatter/commands/push'
7
+ require File.dirname(__FILE__) + '/scatter/commands/receive'
8
+ require File.dirname(__FILE__) + '/scatter/commands/remote'
9
+ require File.dirname(__FILE__) + '/scatter/commands/remote/add'
10
+ require File.dirname(__FILE__) + '/scatter/commands/remote/list'
11
+ require File.dirname(__FILE__) + '/scatter/commands/remote/rm'
12
+ require File.dirname(__FILE__) + '/scatter/commands/node'
13
+ require File.dirname(__FILE__) + '/scatter/commands/node/add'
14
+ require File.dirname(__FILE__) + '/scatter/commands/node/list'
15
+ require File.dirname(__FILE__) + '/scatter/commands/node/rm'
16
+ require File.dirname(__FILE__) + '/scatter/commands/init'
17
+ require File.dirname(__FILE__) + '/scatter/config'
18
+ require File.dirname(__FILE__) + '/scatter/logger'
19
+ require File.dirname(__FILE__) + '/scatter/node'
20
+ require File.dirname(__FILE__) + '/scatter/remote'
21
+ require File.dirname(__FILE__) + '/scatter/gemlist'
22
+ require File.dirname(__FILE__) + '/scatter/shell'
23
+ require File.dirname(__FILE__) + '/scatter/version'
24
+
25
+ module Scatter
26
+ class CommandLineError< StandardError; end
27
+ end
@@ -0,0 +1,64 @@
1
+ module Scatter
2
+ class CLI
3
+ def initialize(out)
4
+ @out = out
5
+ end
6
+
7
+ def run!(args)
8
+ raise CommandLineError, 'No command given' if args.empty?
9
+
10
+ case command_name = args.shift
11
+ when /^-?-?h(elp)?$/
12
+ if args.empty?
13
+ usage
14
+ else
15
+ if args.first == 'commands'
16
+ commands_help
17
+ else
18
+ @out.puts command_class(args.first).help
19
+ end
20
+ end
21
+ when /^-?-?v(ersion)?$/
22
+ @out.puts "scatter version #{Scatter.version}"
23
+ else
24
+ klass = command_class(command_name)
25
+ command = klass.new(@out, *args)
26
+ command.execute!
27
+ end
28
+ end
29
+
30
+ def run(args)
31
+ run!(args)
32
+ rescue CommandLineError => e
33
+ @out.puts "Error: #{e.message}"
34
+ usage
35
+ end
36
+
37
+ protected
38
+ def usage
39
+ @out.puts "Usage: scatter command [options ...]"
40
+ @out.puts "For information about the commands use: scatter help commands"
41
+ end
42
+
43
+ def commands_help
44
+ @out.puts "To get help for a command, use: scatter help <command_name>"
45
+ @out.puts
46
+ hash = Scatter::Command.command_classes.inject({}) do |hash, cmd|
47
+ next hash if cmd.abstract
48
+ command_name = cmd.name.split('::').last.downcase
49
+ hash[command_name] = cmd.short_help
50
+ hash
51
+ end
52
+
53
+ hash.sort.each do |command, help|
54
+ @out.puts "#{command} - #{help}"
55
+ end
56
+ end
57
+
58
+ def command_class(command_name)
59
+ eval("Scatter::Commands::#{command_name.capitalize}")
60
+ rescue
61
+ raise CommandLineError, "Unknown command #{command_name}"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ module Scatter
2
+ class Command
3
+ def initialize(out)
4
+ @out = out
5
+ end
6
+
7
+ class << self
8
+ attr_reader :short_help
9
+ attr_reader :command_classes
10
+ attr_accessor :abstract
11
+
12
+ def usage(short_help, long_help = nil)
13
+ @short_help, @help = short_help, long_help || short_help
14
+ end
15
+
16
+ def help
17
+ lines = @help.split("\n")
18
+ indent = lines.collect { |l| l[/^(\s*)/, 1].size }.min
19
+ lines.collect { |l| l[indent..-1] }.join("\n")
20
+ end
21
+
22
+ def inherited(sub)
23
+ (@command_classes ||= []) << sub
24
+ end
25
+
26
+ def command_name
27
+ name.split('::').last.downcase
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+
3
+ module Scatter
4
+ module Commands
5
+ class Init < Scatter::Command
6
+ usage "Initialize scatter configuration", <<-end
7
+ Initializes your machine to use scatter. You need to run this command only once.
8
+ However, it should not break anything if you run it again.
9
+ end
10
+
11
+ def execute!
12
+ FileUtils.mkdir_p File.dirname(Scatter::Config.file_name)
13
+ Scatter::Config.save!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module Scatter
2
+ module Commands
3
+ class List < Scatter::Command
4
+ usage "List all gems installed on node", <<-end
5
+ List all gems on a node. The node can be specified by name or by fully qualified
6
+ node name. Examples:
7
+
8
+ scatter list nodename
9
+ scatter list remote/nodename
10
+ end
11
+
12
+ def initialize(out, node_name)
13
+ super(out)
14
+ @node_name = node_name
15
+ end
16
+
17
+ def execute!
18
+ gemlist = node.list
19
+ @out.puts gemlist.gem_output
20
+ end
21
+
22
+ def node
23
+ @node ||= Scatter::Config.find_node(@node_name)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module Scatter
2
+ module Commands
3
+ class Node < Scatter::SuperCommand
4
+ usage "List, add or remove nodes", <<-end
5
+ List, add or remove nodes from remote.
6
+ To list nodes use the following syntax:
7
+
8
+ scatter node list <remote_name>
9
+
10
+ To add a node to a remote, use
11
+
12
+ scatter node add <remote_name> <node_name> <username>@<hostname>
13
+
14
+ To remove a node, use either of
15
+
16
+ scatter node rm <node_name>
17
+ scatter node rm <remote_name>/<node_name>
18
+ scatter node rm <remote_name> <node_name>
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Scatter
2
+ module Commands
3
+ class Node
4
+ class Add < Scatter::SubCommand
5
+ def initialize(out, remote_name, node, credentials)
6
+ super(out)
7
+ @remote_name, @node, @credentials = remote_name, node, credentials
8
+ end
9
+
10
+ def execute!
11
+ remote = Scatter::Config.find_remote(@remote_name)
12
+ raise CommandLineError, "Unknown remote #{@remote_name}" unless remote
13
+ raise CommandLineError, "Remote #{@remote_name} already has node #{@node}" if remote.find_node(@node)
14
+ remote.nodes << Scatter::Node.new(remote, @node, *(@credentials.split('@')))
15
+ Scatter::Config.save!
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ module Scatter
2
+ module Commands
3
+ class Node
4
+ class List < Scatter::SubCommand
5
+ def initialize(out, name = nil)
6
+ super(out)
7
+ @name = name
8
+ end
9
+
10
+ def execute!
11
+ nodes = if @name
12
+ remote = Scatter::Config.find_remote(@name)
13
+ raise CommandLineError, "Unknown remote #{@name}" unless remote
14
+ remote.nodes
15
+ else
16
+ Scatter::Config.nodes
17
+ end
18
+
19
+ nodes.each do |node|
20
+ @out.puts "#{node.remote.name}/#{node.name}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ module Scatter
2
+ module Commands
3
+ class Node
4
+ class Rm < Scatter::SubCommand
5
+ def initialize(out, remote, node = nil)
6
+ super(out)
7
+ @remote, @node = remote, node
8
+ end
9
+
10
+ def execute!
11
+ node = if @node
12
+ remote = Scatter::Config.find_remote(@remote)
13
+ raise CommandLineError, "Unknown remote #{@remote}" unless remote
14
+ remote.find_node(@node)
15
+ else
16
+ Scatter::Config.find_destination(@remote)
17
+ end
18
+
19
+ unless node
20
+ dest = [@remote, @node].compact.join('/')
21
+ raise CommandLineError, "Unknown node #{dest}"
22
+ end
23
+
24
+ node.remote.nodes.delete(node)
25
+ Scatter::Config.save!
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Scatter
2
+ module Commands
3
+ class Push < Scatter::Command
4
+ usage "Push gem to remote or node", <<-end
5
+ Pushes a gem to a remote or node. Use the remote name, node name or fully
6
+ qualified node name to specify the target. Examples:
7
+
8
+ # to push to node my_node
9
+ scatter push my_gem.gem my_node
10
+ scatter push my_gem.gem my_remote/my_node
11
+
12
+ # to push to remote my_remote
13
+ scatter push my_gem.gem my_remote
14
+ end
15
+
16
+ def initialize(out, gemfile, destination)
17
+ super(out)
18
+ @gemfile, @destination = gemfile, destination
19
+ end
20
+
21
+ def execute!
22
+ dest.push(@gemfile)
23
+ end
24
+
25
+ def dest
26
+ @dest ||= Scatter::Config.find_destination(@destination)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,19 @@
1
+ module Scatter
2
+ module Commands
3
+ class Receive < Scatter::Command
4
+ usage "Inform scatter that gem was received; calls post receive hook"
5
+
6
+ def initialize(out, gemfile)
7
+ super(out)
8
+ @gemfile = gemfile
9
+ end
10
+
11
+ def execute!
12
+ if File.executable?("#{ENV['HOME']}/.scatter/post-receive")
13
+ Scatter::Logger.log("Executing post receive hook")
14
+ Scatter::Shell.execute "#{ENV['HOME']}/.scatter/post-receive #{@gemfile}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Scatter
2
+ module Commands
3
+ class Remote < Scatter::SuperCommand
4
+ usage "List, add or remove remotes", <<-end
5
+ List, add or remove remotes.
6
+ To list remotes use the following syntax:
7
+
8
+ scatter remote list
9
+
10
+ To add a remote, use
11
+
12
+ scatter remote add <remote_name>
13
+
14
+ To remove a remote, use
15
+
16
+ scatter remote rm <remote_name>
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Scatter
2
+ module Commands
3
+ class Remote
4
+ class Add < Scatter::SubCommand
5
+ def initialize(out, name)
6
+ super(out)
7
+ @name = name
8
+ end
9
+
10
+ def execute!
11
+ raise CommandLineError, "Remote #{@name} already exists" if Scatter::Config.find_remote(@name)
12
+ Scatter::Config.remotes << Scatter::Remote.new(@name)
13
+ Scatter::Config.save!
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module Scatter
2
+ module Commands
3
+ class Remote
4
+ class List < Scatter::SubCommand
5
+ def execute!
6
+ Scatter::Config.remotes.each do |remote|
7
+ @out.puts remote.name
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Scatter
2
+ module Commands
3
+ class Remote
4
+ class Rm < Scatter::SubCommand
5
+ def initialize(out, name)
6
+ super(out)
7
+ @name = name
8
+ end
9
+
10
+ def execute!
11
+ remote = Scatter::Config.find_remote(@name)
12
+ raise CommandLineError, "Remote #{@name} does not exist" unless remote
13
+ Scatter::Config.remotes.delete(remote)
14
+ Scatter::Config.save!
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ module Scatter
2
+ class Config
3
+ class << self
4
+ def instance
5
+ @instance ||= new
6
+ end
7
+
8
+ def clear
9
+ @instance = nil
10
+ end
11
+
12
+ def reload
13
+ @instance = new(@instance.file_name)
14
+ end
15
+
16
+ def method_missing(method, *args)
17
+ if instance.respond_to?(method)
18
+ instance.send(method, *args)
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+
25
+ attr_accessor :file_name
26
+
27
+ def initialize(file_name = nil)
28
+ self.file_name = file_name || "#{ENV['HOME']}/.scatter/config"
29
+ end
30
+
31
+ def remotes
32
+ load!
33
+ @remotes ||= (@config['remotes'] || {}).collect { |name, config| Scatter::Remote.decode_from_config(name, config) }
34
+ end
35
+
36
+ def nodes
37
+ @nodes ||= remotes.collect { |remote| remote.nodes }.flatten
38
+ end
39
+
40
+ def load!
41
+ @loaded ||= begin
42
+ @config = YAML.load(File.read(file_name)) rescue {}
43
+ true
44
+ end
45
+ end
46
+
47
+ def save!
48
+ config = {
49
+ 'remotes' => self.remotes.inject({}) { |hash, remote| hash[remote.name] = remote.encode_for_config; hash }
50
+ }
51
+ File.open(file_name, 'w') { |file| file.puts config.to_yaml }
52
+ end
53
+
54
+ def find_remote(name)
55
+ remotes.find { |remote| remote.name == name }
56
+ end
57
+
58
+ def find_node(name)
59
+ nodes.find { |node| node.name == name || node.full_name == name }
60
+ end
61
+
62
+ def find_destination(name)
63
+ if name =~ /\//
64
+ remote, node = *(name.split('/'))
65
+ remote_object = find_remote(remote)
66
+ remote_object.find_node(node) if remote_object
67
+ else
68
+ (remotes + nodes).find { |dest| dest.name == name }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ module Scatter
2
+ class Gemlist
3
+ attr_accessor :gem_output
4
+
5
+ def initialize(gem_output)
6
+ @gem_output = gem_output
7
+ end
8
+
9
+ def self.parse(gem_output)
10
+ new(gem_output)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module Scatter
2
+ class Logger
3
+ def self.log(message)
4
+ puts message
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,34 @@
1
+ module Scatter
2
+ class Node
3
+ attr_reader :remote, :name, :username, :hostname
4
+
5
+ def initialize(remote, name, username, hostname)
6
+ @remote, @name, @username, @hostname = remote, name, username, hostname
7
+ end
8
+
9
+ def full_name
10
+ [remote.name, name].join('/')
11
+ end
12
+
13
+ def list
14
+ Scatter::Gemlist.parse(Scatter::Shell.capture("ssh #{@username}@#{@hostname} gem list"))
15
+ end
16
+
17
+ def push(gemfile)
18
+ Scatter::Shell.execute "scp #{gemfile} #{@username}@#{@hostname}:/tmp"
19
+ Scatter::Shell.execute "ssh #{@username}@#{@hostname} sudo gem install /tmp/#{File.basename(gemfile)}"
20
+ Scatter::Shell.execute "ssh #{@username}@#{@hostname} scatter receive /tmp/#{File.basename(gemfile)}"
21
+ end
22
+
23
+ def self.decode_from_config(remote, name, config)
24
+ new(remote, name, config['username'], config['hostname'])
25
+ end
26
+
27
+ def encode_for_config
28
+ {
29
+ 'username' => @username,
30
+ 'hostname' => @hostname
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Scatter
2
+ class Remote
3
+ attr_reader :name, :nodes
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ @nodes = []
8
+ end
9
+
10
+ def push(gemfile)
11
+ @nodes.each do |node|
12
+ node.push(gemfile)
13
+ end
14
+ end
15
+
16
+ def find_node(name)
17
+ nodes.find { |node| node.name == name }
18
+ end
19
+
20
+ def self.decode_from_config(name, config)
21
+ remote = new(name)
22
+ nodes = config['nodes'].collect { |nodename, config| Node.decode_from_config(remote, nodename, config) } rescue []
23
+ remote.nodes.concat(nodes)
24
+ remote
25
+ end
26
+
27
+ def encode_for_config
28
+ {
29
+ 'nodes' => @nodes.inject({}) { |hash, node| hash[node.name] = node.encode_for_config; hash }
30
+ }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ module Scatter
2
+ module Shell
3
+ extend self
4
+
5
+ attr_accessor :dry_run
6
+ attr_accessor :command_log
7
+
8
+ def execute(command)
9
+ log_command command
10
+ system command unless dry_run
11
+ end
12
+
13
+ def capture(command)
14
+ log_command command
15
+ %x{#{command}} unless dry_run
16
+ end
17
+
18
+ def clear_log
19
+ self.command_log = []
20
+ end
21
+
22
+ protected
23
+ def log_command(command)
24
+ self.command_log ||= []
25
+ self.command_log << command
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Scatter
2
+ class SubCommand
3
+ def initialize(out)
4
+ @out = out
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ module Scatter
2
+ class SuperCommand < Command
3
+ self.abstract = true
4
+
5
+ # needed?
6
+ class << self
7
+ def inherited(sub)
8
+ Command.inherited(sub)
9
+ end
10
+ end
11
+
12
+ def initialize(out, *args)
13
+ super(out)
14
+ @subcmd = args.shift
15
+ @arguments = args
16
+ end
17
+
18
+ def execute!
19
+ subcmd_class = eval("#{self.class.name}::#{@subcmd.capitalize}")
20
+ cmd = subcmd_class.new(@out, *@arguments)
21
+ cmd.execute!
22
+ rescue ArgumentError
23
+ raise CommandLineError, "Wrong number of mandatory arguments for command #{self.class.command_name} #{@subcmd}"
24
+ rescue NameError
25
+ raise CommandLineError, "Unknown subcommand #{@subcmd} for command #{self.class.command_name}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Scatter
2
+ def self.version
3
+ "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scatter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - imedo GmbH
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-17 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: entwicker@imedo.de
18
+ executables:
19
+ - scatter
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - bin/scatter
26
+ - lib/scatter
27
+ - lib/scatter/cli.rb
28
+ - lib/scatter/command.rb
29
+ - lib/scatter/commands
30
+ - lib/scatter/commands/init.rb
31
+ - lib/scatter/commands/list.rb
32
+ - lib/scatter/commands/node
33
+ - lib/scatter/commands/node/add.rb
34
+ - lib/scatter/commands/node/list.rb
35
+ - lib/scatter/commands/node/rm.rb
36
+ - lib/scatter/commands/node.rb
37
+ - lib/scatter/commands/push.rb
38
+ - lib/scatter/commands/receive.rb
39
+ - lib/scatter/commands/remote
40
+ - lib/scatter/commands/remote/add.rb
41
+ - lib/scatter/commands/remote/list.rb
42
+ - lib/scatter/commands/remote/rm.rb
43
+ - lib/scatter/commands/remote.rb
44
+ - lib/scatter/config.rb
45
+ - lib/scatter/gemlist.rb
46
+ - lib/scatter/logger.rb
47
+ - lib/scatter/node.rb
48
+ - lib/scatter/remote.rb
49
+ - lib/scatter/shell.rb
50
+ - lib/scatter/sub_command.rb
51
+ - lib/scatter/super_command.rb
52
+ - lib/scatter/version.rb
53
+ - lib/scatter.rb
54
+ has_rdoc: false
55
+ homepage: http://www.imedo.de/
56
+ post_install_message:
57
+ rdoc_options: []
58
+
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.1
77
+ signing_key:
78
+ specification_version: 2
79
+ summary: Gem deployment tool
80
+ test_files: []
81
+