public
Description: Serve and install gems over Bonjour
Homepage:
Clone URL: git://github.com/evanphx/gemjour.git
gemjour / lib / gemjour.rb
100644 143 lines (108 sloc) 3.238 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
$:.unshift(File.dirname(__FILE__)) unless
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
 
require "dnssd"
require "set"
require "gemjour/version"
 
Thread.abort_on_exception = true
 
module Gemjour
  Server = Struct.new(:name, :host, :port)
  PORT = 8808
  SERVICE = "_rubygems._tcp"
 
  def self.usage
    puts <<-HELP
Usage:
 
serve [<name>] [<port>]
Start up a gem server as <name> on <port>. <name> is your username
by default, and <port> is 8088. If you want to use the default <name>,
pass it as "".
 
install <server> <gem>
Installs <gem> from <server>. <server> is the name the server announces
itself as.
 
list
List all available gem servers.
 
show <server>
List all gems available from <server>
 
HELP
  end
 
  def self.find(name)
    host = nil
 
    waiting = Thread.current
 
    service = DNSSD.browse(SERVICE) do |reply|
      if name === reply.name
        DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
          host = Server.new(reply.name, rr.target, rr.port)
          waiting.run
        end
      end
    end
 
    sleep 5
    service.stop
 
    host
  end
 
  def self.list(name = nil)
    return show(name) if name
    hosts = []
 
    waiting = Thread.current
 
    service = DNSSD.browse(SERVICE) do |reply|
      DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
        host = Server.new(reply.name, rr.target, rr.port)
        unless hosts.include? host
          puts "#{host.name} (#{host.host}:#{host.port})"
          hosts << host
        end
      end
    end
 
    sleep 5
    service.stop
  end
 
  def self.show(name)
    host = find(name)
 
    unless host
      puts "ERROR: Unable to find server '#{name}'"
      return
    end
 
    system "gem list -r --source=http://#{host.host}:#{host.port}"
  end
  
  def self._diff(name)
    host = find(name)
 
    unless host
      puts "ERROR: Unable to find server named '#{name}'"
      return
    end
    
    require "tempfile"
    local_gems, remote_gems = Tempfile.new("local_gems"), Tempfile.new("remote_gems")
    local_gems.print(`gem list --no-versions --no-verbose`)
    local_gems.close
    remote_gems.print(`gem list --no-versions --no-verbose -r --source=http://#{host.host}:#{host.port}`)
    remote_gems.close
    `diff -u #{local_gems.path} #{remote_gems.path}`
  end
 
  def self.diff(name)
    return unless diff = _diff(name)
    puts diff
  end
  
  def self.install_diff(name)
    return unless gem_diff = _diff(name)
    gem_diff.scan(/^\+([\w_\-]+)/) do |match|
      install(name, match)
    end
  end
 
  def self.install(name, gem)
    host = find(name)
 
    unless host
      puts "ERROR: Unable to find server named '#{name}'"
      return
    end
 
    puts "Installing gem '#{gem}' from #{host.host}:#{host.port}"
    system "gem install #{gem} -s http://#{host.host}:#{host.port}"
  end
 
  def self.serve(name="", port=PORT)
    name = ENV['USER'] if name.empty?
 
    tr = DNSSD::TextRecord.new
    tr['description'] = "#{name}'s gem server"
    
    DNSSD.register(name, SERVICE, "local", port, tr.encode) do |reply|
      puts "Serving gems under name '#{name}'..."
    end
 
    system "gem server --port=#{port} -q > /dev/null 2>&1"
  end
 
end