nullobject-gemjour 1.1.0
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.rdoc +50 -0
- data/Rakefile +47 -0
- data/bin/gemjour +31 -0
- data/lib/gemjour/version.rb +3 -0
- data/lib/gemjour.rb +217 -0
- metadata +68 -0
data/README.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
= Gemjour
|
2
|
+
|
3
|
+
Advertise, list, and install gems over Bonjour.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
sudo gem install dnssd
|
8
|
+
sudo gem install evanphx-gemjour --source=http://gems.github.com
|
9
|
+
|
10
|
+
== Usage
|
11
|
+
|
12
|
+
alice$ gemjour serve # advertises gems over Bonjour
|
13
|
+
bob$ gemjour list # finds alice
|
14
|
+
bob$ gemjour list alice # shows alice's gems
|
15
|
+
bob$ gemjour diff alice # shows differences between bob and alice gems
|
16
|
+
bob$ gemjour install_diff alice # installs any gem bob doesn't have installed from alice
|
17
|
+
bob$ gemjour search foo # searches for a gem "foo"
|
18
|
+
|
19
|
+
alice$ gem install foo.gem; gitjour serve
|
20
|
+
bob$ gemjour install alice foo
|
21
|
+
bob$ gemjour install alice foo -v 1.1.0
|
22
|
+
|
23
|
+
Prefix the commands with "sudo" as necessary.
|
24
|
+
|
25
|
+
== Awesome!
|
26
|
+
|
27
|
+
You know it.
|
28
|
+
|
29
|
+
== License
|
30
|
+
|
31
|
+
Copyright (c) 2008 Evan Phoenix
|
32
|
+
|
33
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
34
|
+
a copy of this software and associated documentation files (the
|
35
|
+
"Software"), to deal in the Software without restriction, including
|
36
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
37
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
38
|
+
permit persons to whom the Software is furnished to do so, subject to
|
39
|
+
the following conditions:
|
40
|
+
|
41
|
+
The above copyright notice and this permission notice shall be
|
42
|
+
included in all copies or substantial portions of the Software.
|
43
|
+
|
44
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
45
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
46
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
47
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
48
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
49
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
50
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "date"
|
2
|
+
require "fileutils"
|
3
|
+
require "rubygems"
|
4
|
+
require "rake/gempackagetask"
|
5
|
+
|
6
|
+
require "./lib/gemjour/version.rb"
|
7
|
+
|
8
|
+
gemjour_gemspec = Gem::Specification.new do |s|
|
9
|
+
s.name = "gemjour"
|
10
|
+
s.version = Gemjour::VERSION
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = ["README.rdoc"]
|
14
|
+
s.summary = "Advertise, list, and install gems over Bonjour."
|
15
|
+
s.description = s.summary
|
16
|
+
s.authors = ["Evan Phoenix", "Josh Bassett"]
|
17
|
+
s.email = "evan@fallingsnow.net"
|
18
|
+
s.homepage = "http://github.com/evanphx/gemjour"
|
19
|
+
s.require_path = "lib"
|
20
|
+
s.autorequire = "gemjour"
|
21
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{bin,lib}/**/*")
|
22
|
+
s.executables = %w(gemjour)
|
23
|
+
|
24
|
+
s.add_dependency "net-mdns", ">= 0.4.0"
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(gemjour_gemspec) do |pkg|
|
28
|
+
pkg.gem_spec = gemjour_gemspec
|
29
|
+
end
|
30
|
+
|
31
|
+
namespace :gem do
|
32
|
+
namespace :spec do
|
33
|
+
desc "Update gemjour.gemspec"
|
34
|
+
task :generate do
|
35
|
+
File.open("gemjour.gemspec", "w") do |f|
|
36
|
+
f.puts(gemjour_gemspec.to_ruby)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :install => :package do
|
43
|
+
sh %{sudo gem install --local pkg/gemjour-#{Gemjour::VERSION}}
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Remove all generated artifacts"
|
47
|
+
task :clean => :clobber_package
|
data/bin/gemjour
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/gemjour'
|
5
|
+
|
6
|
+
begin
|
7
|
+
cmd = ARGV.shift
|
8
|
+
|
9
|
+
case cmd
|
10
|
+
when "serve"
|
11
|
+
Gemjour.serve(*ARGV)
|
12
|
+
when "list"
|
13
|
+
Gemjour.list(*ARGV)
|
14
|
+
when "show"
|
15
|
+
Gemjour.show(*ARGV)
|
16
|
+
when "search"
|
17
|
+
Gemjour.search(*ARGV)
|
18
|
+
when "diff"
|
19
|
+
Gemjour.diff(*ARGV)
|
20
|
+
when "install_diff"
|
21
|
+
Gemjour.install_diff(*ARGV)
|
22
|
+
when "install"
|
23
|
+
Gemjour.install(*ARGV)
|
24
|
+
else
|
25
|
+
Gemjour.usage
|
26
|
+
end
|
27
|
+
rescue => e
|
28
|
+
puts "ERROR: running '#{cmd}': #{e.message} (#{e.class})"
|
29
|
+
puts
|
30
|
+
Gemjour.usage
|
31
|
+
end
|
data/lib/gemjour.rb
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'net/dns/mdns-sd'
|
5
|
+
require 'set'
|
6
|
+
require 'gemjour/version'
|
7
|
+
|
8
|
+
DNSSD = Net::DNS::MDNSSD
|
9
|
+
|
10
|
+
Thread.abort_on_exception = true
|
11
|
+
|
12
|
+
module Gemjour
|
13
|
+
Server = Struct.new(:name, :host, :port)
|
14
|
+
PORT = 8808
|
15
|
+
SERVICE = "_rubygems._tcp"
|
16
|
+
|
17
|
+
GEM_PREAMBLE_RE = /$\s*\*\*\* REMOTE GEMS \*\*\*\s*/
|
18
|
+
|
19
|
+
def self.usage
|
20
|
+
puts <<-HELP
|
21
|
+
Usage:
|
22
|
+
|
23
|
+
serve [<name>] [<port>]
|
24
|
+
Start up a gem server as <name> on <port>. <name> is your username
|
25
|
+
by default, and <port> is 8088. If you want to use the default <name>,
|
26
|
+
pass it as "".
|
27
|
+
|
28
|
+
install <server> <gem> [<version>]
|
29
|
+
Installs <gem> from <server>. <server> is the name the server announces
|
30
|
+
itself as.
|
31
|
+
|
32
|
+
list
|
33
|
+
List all available gem servers.
|
34
|
+
|
35
|
+
show <server>
|
36
|
+
List all gems available from <server>.
|
37
|
+
|
38
|
+
search <string>
|
39
|
+
List all gems which contain <string>.
|
40
|
+
|
41
|
+
HELP
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finds the host with the specified name.
|
45
|
+
def self.find(name)
|
46
|
+
host = nil
|
47
|
+
|
48
|
+
waiting = Thread.current
|
49
|
+
|
50
|
+
service = DNSSD.browse(SERVICE) do |reply|
|
51
|
+
if name === reply.name
|
52
|
+
DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
|
53
|
+
host = Server.new(reply.name, rr.target, rr.port)
|
54
|
+
waiting.run
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
sleep 5
|
60
|
+
service.stop
|
61
|
+
|
62
|
+
host
|
63
|
+
end
|
64
|
+
|
65
|
+
# Finds all hosts.
|
66
|
+
def self.find_all
|
67
|
+
hosts = []
|
68
|
+
|
69
|
+
puts "Gathering for up to 5 seconds..."
|
70
|
+
|
71
|
+
waiting = Thread.current
|
72
|
+
|
73
|
+
service = DNSSD.browse(SERVICE) do |reply|
|
74
|
+
DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
|
75
|
+
host = Server.new(reply.name, rr.target, rr.port)
|
76
|
+
unless hosts.include? host
|
77
|
+
hosts << host
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
sleep 5
|
83
|
+
service.stop
|
84
|
+
|
85
|
+
hosts
|
86
|
+
end
|
87
|
+
|
88
|
+
# Lists the hosts. Aliased to the show command if a name is specified.
|
89
|
+
def self.list(name = nil)
|
90
|
+
if name
|
91
|
+
show(name)
|
92
|
+
else
|
93
|
+
hosts = find_all
|
94
|
+
hosts.each { |host| print_host(host) }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.show(name)
|
99
|
+
results = {}
|
100
|
+
host = find(name)
|
101
|
+
|
102
|
+
unless host
|
103
|
+
puts "ERROR: Unable to find server '#{name}'"
|
104
|
+
return
|
105
|
+
end
|
106
|
+
|
107
|
+
# List gems on host.
|
108
|
+
output =`gem list -a -r --source=http://#{host.host}:#{host.port}`
|
109
|
+
|
110
|
+
gems = parse_gems(output)
|
111
|
+
results[host] = gems unless gems.empty?
|
112
|
+
|
113
|
+
print_gems(results)
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.search(q)
|
117
|
+
results = {}
|
118
|
+
hosts = find_all
|
119
|
+
|
120
|
+
unless hosts.empty?
|
121
|
+
puts "Searching #{hosts.length} hosts..."
|
122
|
+
|
123
|
+
hosts.each do |host|
|
124
|
+
# Search for gems on host.
|
125
|
+
output = `gem search #{q} -a -r --source=http://#{host.host}:#{host.port}`
|
126
|
+
|
127
|
+
gems = parse_gems(output)
|
128
|
+
results[host] = gems unless gems.empty?
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if results.empty?
|
133
|
+
puts "Search returned no results"
|
134
|
+
else
|
135
|
+
print_gems(results)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Parses the gems from the raw gem output.
|
140
|
+
def self.parse_gems(s)
|
141
|
+
# Strip the preamble off the gem output.
|
142
|
+
s.gsub!(GEM_PREAMBLE_RE, '')
|
143
|
+
|
144
|
+
s.split(/\n/)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Prints the gems for each host.
|
148
|
+
def self.print_gems(results)
|
149
|
+
results.each do |host, gems|
|
150
|
+
print_host(host)
|
151
|
+
gems.each do |gem|
|
152
|
+
puts "\t#{gem}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.print_host(host)
|
158
|
+
puts "=== #{host.name} (#{host.host}:#{host.port})"
|
159
|
+
end
|
160
|
+
|
161
|
+
def self._diff(name)
|
162
|
+
host = find(name)
|
163
|
+
|
164
|
+
unless host
|
165
|
+
puts "ERROR: Unable to find server named '#{name}'"
|
166
|
+
return
|
167
|
+
end
|
168
|
+
|
169
|
+
require "tempfile"
|
170
|
+
local_gems, remote_gems = Tempfile.new("local_gems"), Tempfile.new("remote_gems")
|
171
|
+
local_gems.print(`gem list --no-versions --no-verbose`)
|
172
|
+
local_gems.close
|
173
|
+
remote_gems.print(`gem list --no-versions --no-verbose -r --source=http://#{host.host}:#{host.port}`)
|
174
|
+
remote_gems.close
|
175
|
+
`diff -u #{local_gems.path} #{remote_gems.path}`
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.diff(name)
|
179
|
+
return unless diff = _diff(name)
|
180
|
+
puts diff
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.install_diff(name)
|
184
|
+
return unless gem_diff = _diff(name)
|
185
|
+
gem_diff.scan(/^\+([\w_\-]+)/) do |match|
|
186
|
+
install(name, match)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.install(name, gem, version = nil)
|
191
|
+
host = find(name)
|
192
|
+
|
193
|
+
unless host
|
194
|
+
puts "ERROR: Unable to find server named '#{name}'"
|
195
|
+
return
|
196
|
+
end
|
197
|
+
|
198
|
+
puts "Installing gem '#{gem}' from #{host.host}:#{host.port}"
|
199
|
+
|
200
|
+
command = "gem install #{gem} -s http://#{host.host}:#{host.port}"
|
201
|
+
command << " #{version}" if version
|
202
|
+
system command
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.serve(name = "", port = PORT)
|
206
|
+
name = ENV['USER'] if name.empty?
|
207
|
+
|
208
|
+
tr = DNSSD::TextRecord.new
|
209
|
+
tr['description'] = "#{name}'s gem server"
|
210
|
+
|
211
|
+
DNSSD.register(name, SERVICE, "local", port, tr.encode) do |reply|
|
212
|
+
puts "Serving gems under name '#{name}'..."
|
213
|
+
end
|
214
|
+
|
215
|
+
system "gem server --port=#{port} -q > /dev/null 2>&1"
|
216
|
+
end
|
217
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nullobject-gemjour
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Evan Phoenix
|
8
|
+
- Josh Bassett
|
9
|
+
autorequire: gemjour
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-03-26 00:00:00 -07:00
|
14
|
+
default_executable: gemjour
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: net-mdns
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.4.0
|
25
|
+
version:
|
26
|
+
description: Advertise, list, and install gems over Bonjour.
|
27
|
+
email: evan@fallingsnow.net
|
28
|
+
executables:
|
29
|
+
- gemjour
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- bin/gemjour
|
38
|
+
- lib/gemjour
|
39
|
+
- lib/gemjour/version.rb
|
40
|
+
- lib/gemjour.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/evanphx/gemjour
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.2.0
|
64
|
+
signing_key:
|
65
|
+
specification_version: 2
|
66
|
+
summary: Advertise, list, and install gems over Bonjour.
|
67
|
+
test_files: []
|
68
|
+
|