bananajour 2.1.9
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/Rakefile +66 -0
- data/Readme.md +86 -0
- data/bin/bananajour +58 -0
- data/lib/bananajour.rb +99 -0
- data/lib/bananajour/bonjour.rb +9 -0
- data/lib/bananajour/bonjour/advertiser.rb +57 -0
- data/lib/bananajour/bonjour/bananajour_browser.rb +24 -0
- data/lib/bananajour/bonjour/browser.rb +52 -0
- data/lib/bananajour/bonjour/person.rb +21 -0
- data/lib/bananajour/bonjour/repository.rb +39 -0
- data/lib/bananajour/bonjour/repository_browser.rb +36 -0
- data/lib/bananajour/commands.rb +80 -0
- data/lib/bananajour/gem_dependencies.rb +34 -0
- data/lib/bananajour/grit_extensions.rb +11 -0
- data/lib/bananajour/helpers.rb +100 -0
- data/lib/bananajour/repository.rb +85 -0
- data/lib/bananajour/version.rb +3 -0
- data/sinatra/app.rb +87 -0
- data/sinatra/lib/browsers.rb +2 -0
- data/sinatra/lib/diff_helpers.rb +92 -0
- data/sinatra/lib/mock_browsers.rb +55 -0
- data/sinatra/public/jquery-1.3.2.min.js +19 -0
- data/sinatra/public/loader.gif +0 -0
- data/sinatra/public/logo.png +0 -0
- data/sinatra/public/pbjt.swf +0 -0
- data/sinatra/public/peanut.png +0 -0
- data/sinatra/views/commit.haml +112 -0
- data/sinatra/views/home.haml +305 -0
- data/sinatra/views/layout.haml +119 -0
- data/sinatra/views/readme.haml +52 -0
- metadata +183 -0
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
$:.unshift "#{File.dirname(__FILE__)}/lib"
|
4
|
+
|
5
|
+
require "bananajour/version"
|
6
|
+
require "bananajour/gem_dependencies"
|
7
|
+
|
8
|
+
gem = Gem::Specification.new do |gem|
|
9
|
+
gem.name = "bananajour"
|
10
|
+
gem.version = Bananajour::VERSION
|
11
|
+
gem.platform = Gem::Platform::RUBY
|
12
|
+
gem.extra_rdoc_files = ["Readme.md"]
|
13
|
+
gem.summary = "Local git repository hosting with a sexy web interface and bonjour discovery. It's like your own little adhoc, network-aware github!"
|
14
|
+
gem.description = gem.summary
|
15
|
+
gem.authors = ["Tim Lucas"]
|
16
|
+
gem.email = "t.lucas@toolmantim.com"
|
17
|
+
gem.homepage = "http://github.com/toolmantim/bananajour"
|
18
|
+
gem.require_path = "lib"
|
19
|
+
gem.files = %w(Readme.md Rakefile) + Dir.glob("{bin,lib,sinatra}/**/*")
|
20
|
+
gem.has_rdoc = false
|
21
|
+
gem.bindir = 'bin'
|
22
|
+
gem.executables = [ 'bananajour' ]
|
23
|
+
Bananajour::GemDependencies.all.each {|dep| gem.add_runtime_dependency( dep.name, dep.version ) }
|
24
|
+
end
|
25
|
+
|
26
|
+
task :clean do
|
27
|
+
FileUtils.rm_rf Dir['*.gem', '*.gemspec']
|
28
|
+
end
|
29
|
+
|
30
|
+
namespace :gem do
|
31
|
+
|
32
|
+
desc "Rebuild and install bananajour as a gem"
|
33
|
+
task :install => :package do
|
34
|
+
require 'rubygems/installer'
|
35
|
+
Dir['*.gem'].each do |gem|
|
36
|
+
Gem::Installer.new(gem).install
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Create the gem"
|
41
|
+
task :package => [:clean, "spec:generate"] do
|
42
|
+
require 'rubygems/builder'
|
43
|
+
Gem::Builder.new( gem ).build
|
44
|
+
end
|
45
|
+
|
46
|
+
namespace :spec do
|
47
|
+
|
48
|
+
desc "Update #{gem.name}.gemspec"
|
49
|
+
task :generate do
|
50
|
+
File.open("#{gem.name}.gemspec", "w") do |f|
|
51
|
+
f.puts(gem.to_ruby)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Test spec in github cleanroom"
|
56
|
+
task :test => :generate do
|
57
|
+
require 'rubygems/specification'
|
58
|
+
data = File.read("#{gem.name}.gemspec")
|
59
|
+
spec = nil
|
60
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
61
|
+
puts "#{spec} - Good to go!"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
Bananajour - Local git publication and collaboration
|
2
|
+
====================================================
|
3
|
+
|
4
|
+
Local git repository hosting with a sexy web interface and Bonjour discovery. It's like a bunch of adhoc, local, network-aware githubs!
|
5
|
+
|
6
|
+
Unlike Gitjour, the repositories you're serving are not your working git repositories, they're served from `~/.bananajour/repositories`. You can push to your bananajour repositories from your working copies just like you do with github.
|
7
|
+
|
8
|
+
Bananajour is the baby of [Tim Lucas](http://toolmantim.com/) with much railscamp hackage by [Nathan de Vries](http://github.com/atnan), [Lachlan Hardy](http://github.com/lachlanhardy), [Josh Bassett](http://github.com/nullobject), [Myles Byrne](http://github.com/quackingduck), [Ben Hoskings](http://github.com/benhoskings), [Brett Goulder](http://github.com/brettgo1), [Tony Issakov](https://github.com/tissak), and [Mark Bennett](http://github.com/MarkBennett). The rad logo was by [Carla Hackett](http://carlahackettdesign.com/). Other various fixes and contributions by [Travis Swicegood](http://github.com/tswicegood) and [Nate Haas](http://github.com/natehaas).
|
9
|
+
|
10
|
+

|
11
|
+
|
12
|
+
Installation and usage
|
13
|
+
----------------------
|
14
|
+
|
15
|
+
You'll need at least [git version 1.6](http://git-scm.com/). Run `git --version` if you're unsure.
|
16
|
+
|
17
|
+
Install it from github via gems:
|
18
|
+
|
19
|
+
gem install toolmantim-bananajour
|
20
|
+
|
21
|
+
(you might need to do a `gem sources -a http://gems.github.com` beforehand!)
|
22
|
+
|
23
|
+
Start it up:
|
24
|
+
|
25
|
+
bananajour
|
26
|
+
|
27
|
+
Go into an existing project and add it to bananajour:
|
28
|
+
|
29
|
+
cd ~/code/myproj
|
30
|
+
bananajour add
|
31
|
+
|
32
|
+
Publish your codez:
|
33
|
+
|
34
|
+
git push banana master
|
35
|
+
|
36
|
+
Fire up [http://localhost:9331/](http://localhost:9331/) to check it out.
|
37
|
+
|
38
|
+
If somebody starts sharing a Bananajour repository with the same name on the
|
39
|
+
network it'll automatically show up in the network thanks to the wonder that is Bonjour.
|
40
|
+
|
41
|
+
For a list of all the commands:
|
42
|
+
|
43
|
+
bananajour help
|
44
|
+
|
45
|
+
Linux support
|
46
|
+
-------------
|
47
|
+
|
48
|
+
To install the dnssd gem on linux you'll need [avahi](http://avahi.org/). For Ubunutu peeps this means:
|
49
|
+
|
50
|
+
sudo apt-get install libavahi-compat-libdnssd-dev
|
51
|
+
|
52
|
+
Using with Ginatra
|
53
|
+
------------------
|
54
|
+
|
55
|
+
Rumour has it [ginatra](http://github.com/lenary/ginatra) can be used to provide richer gitweb-like browsing of your bananajour repositories. Symlink ginatra's `repos` directory to `~/.bananajour/repositories` to serve up your bananajour repositories.
|
56
|
+
|
57
|
+
Official repository and support
|
58
|
+
-------------------------------
|
59
|
+
|
60
|
+
The official repo and support issues/tickets live at [github.com/toolmantim/bananajour](http://github.com/toolmantim/bananajour).
|
61
|
+
|
62
|
+
Feature and support discussions live at [groups.google.com/group/bananajour](http://groups.google.com/group/bananajour).
|
63
|
+
|
64
|
+
Developing
|
65
|
+
----------
|
66
|
+
|
67
|
+
If you want to hack on the sinatra app then do so from a local clone but run your actual bananjour from the gem version. Running the sinatra app directly won't broadcast it onto the network and it'll run on a different port:
|
68
|
+
|
69
|
+
ruby sinatra/app.rb -s thin
|
70
|
+
|
71
|
+
If you want code reloading use [shotgun](http://github.com/rtomayko/shotgun) instead:
|
72
|
+
|
73
|
+
shotgun sinatra/app.rb -s thin
|
74
|
+
|
75
|
+
If you then want to run your working copy as your public bananajour rebuild and install it as a gem:
|
76
|
+
|
77
|
+
sudo rake gem:install
|
78
|
+
|
79
|
+
License
|
80
|
+
-------
|
81
|
+
|
82
|
+
All directories and files are MIT Licensed.
|
83
|
+
|
84
|
+
Warning to all those who still believe secrecy will save their revenue stream
|
85
|
+
-----------------------------------------------------------------------------
|
86
|
+
Bananas were meant to be shared. There are no secret bananas.
|
data/bin/bananajour
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require (File.symlink?(__FILE__) ? "bananajour" : File.join(File.dirname(__FILE__), "/../lib/bananajour"))
|
5
|
+
|
6
|
+
Thread.abort_on_exception = true
|
7
|
+
|
8
|
+
Bananajour.setup! if !Bananajour.setup?
|
9
|
+
Bananajour.check_git!
|
10
|
+
Bananajour.check_git_config!
|
11
|
+
|
12
|
+
case ARGV.first
|
13
|
+
|
14
|
+
when nil
|
15
|
+
Bananajour.serve_web!
|
16
|
+
Bananajour.serve_git!
|
17
|
+
Bananajour.advertise!
|
18
|
+
Process.wait
|
19
|
+
|
20
|
+
when "add", "init"
|
21
|
+
repo = Bananajour.add!(ARGV[1] || File.expand_path("."))
|
22
|
+
|
23
|
+
when "remove", "rm"
|
24
|
+
name = ARGV[1]
|
25
|
+
|
26
|
+
if !name || name.empty?
|
27
|
+
abort "You need to specify the name of the repository you'd like to remove:\n#{File.basename($0)} remove <path>"
|
28
|
+
elsif !(repo = Bananajour::Repository.for_name(name))
|
29
|
+
abort "#{name.inspect} is not a valid repository name"
|
30
|
+
end
|
31
|
+
|
32
|
+
repo.remove!
|
33
|
+
|
34
|
+
when "clone"
|
35
|
+
if ARGV[1].nil? || ARGV[1].empty?
|
36
|
+
abort "You need to specify the path to the repository you'd like to clone:\n$ bananajour clone <path>"
|
37
|
+
end
|
38
|
+
repo = Bananajour.clone!(ARGV[1], ARGV[2])
|
39
|
+
|
40
|
+
when "help", "--help", "-h"
|
41
|
+
puts <<-HELP
|
42
|
+
Usage: #{File.basename($0)} [<command>]
|
43
|
+
|
44
|
+
Commands:
|
45
|
+
none - Start the web, git and bonjour serving
|
46
|
+
add [path] - Add an existing git repo to bananajour
|
47
|
+
remove <name> - Remove a repo
|
48
|
+
clone <url> [path] - Clone a remote repo and add it to bananajour
|
49
|
+
help
|
50
|
+
version
|
51
|
+
HELP
|
52
|
+
|
53
|
+
when "version", "--version", "-v"
|
54
|
+
puts "bananajour version #{Bananajour::VERSION}"
|
55
|
+
|
56
|
+
else
|
57
|
+
abort "Say what? Try: #{File.basename($0)} help"
|
58
|
+
end
|
data/lib/bananajour.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'socket'
|
7
|
+
require 'md5'
|
8
|
+
|
9
|
+
require 'bananajour/gem_dependencies'
|
10
|
+
|
11
|
+
Bananajour.require_gem 'rainbow'
|
12
|
+
Bananajour.require_gem 'dnssd'
|
13
|
+
Bananajour.require_gem 'chrislloyd-fancypath', 'fancypath'
|
14
|
+
|
15
|
+
require 'bananajour/repository'
|
16
|
+
require 'bananajour/grit_extensions'
|
17
|
+
require 'bananajour/version'
|
18
|
+
require 'bananajour/bonjour'
|
19
|
+
require 'bananajour/helpers'
|
20
|
+
require 'bananajour/commands'
|
21
|
+
|
22
|
+
module Bananajour
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
include DateHelpers
|
27
|
+
include GravatarHelpers
|
28
|
+
include Commands
|
29
|
+
|
30
|
+
def setup?
|
31
|
+
repositories_path.exists?
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup!
|
35
|
+
repositories_path.create_dir
|
36
|
+
end
|
37
|
+
|
38
|
+
def path
|
39
|
+
Fancypath("~/.bananajour").expand_path
|
40
|
+
end
|
41
|
+
|
42
|
+
def repositories_path
|
43
|
+
path/"repositories"
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_git_global_config(key)
|
47
|
+
`git config --global #{key}`.strip
|
48
|
+
end
|
49
|
+
|
50
|
+
def config
|
51
|
+
@config ||= begin
|
52
|
+
OpenStruct.new({
|
53
|
+
:name => get_git_global_config("user.name"),
|
54
|
+
:email => get_git_global_config("user.email")
|
55
|
+
})
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def web_port
|
60
|
+
9331
|
61
|
+
end
|
62
|
+
|
63
|
+
def web_uri
|
64
|
+
"http://#{host_name}:#{web_port}/"
|
65
|
+
end
|
66
|
+
|
67
|
+
def host_name
|
68
|
+
Socket.gethostname
|
69
|
+
end
|
70
|
+
|
71
|
+
def git_uri
|
72
|
+
"git://#{host_name}/"
|
73
|
+
end
|
74
|
+
|
75
|
+
def repositories
|
76
|
+
repositories_path.children.map {|r| Repository.new(r)}.sort_by {|r| r.name}
|
77
|
+
end
|
78
|
+
|
79
|
+
def repository(name)
|
80
|
+
repositories.find {|r| r.name == name}
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_hash
|
84
|
+
{
|
85
|
+
"name" => config.name,
|
86
|
+
"email" => config.email,
|
87
|
+
"uri" => web_uri,
|
88
|
+
"git-uri" => git_uri,
|
89
|
+
"gravatar" => Bananajour.gravatar,
|
90
|
+
"version" => Bananajour::VERSION,
|
91
|
+
"repositories" => repositories.collect do |r|
|
92
|
+
{"name" => r.name, "uri" => r.uri}
|
93
|
+
end
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Bananajour::Bonjour
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'bananajour/bonjour/person'
|
5
|
+
require 'bananajour/bonjour/repository'
|
6
|
+
require 'bananajour/bonjour/browser'
|
7
|
+
require 'bananajour/bonjour/repository_browser'
|
8
|
+
require 'bananajour/bonjour/bananajour_browser'
|
9
|
+
require 'bananajour/bonjour/advertiser'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Bananajour::Bonjour::Advertiser
|
2
|
+
def initialize
|
3
|
+
@services = []
|
4
|
+
end
|
5
|
+
def go!
|
6
|
+
register_app
|
7
|
+
register_repos
|
8
|
+
end
|
9
|
+
private
|
10
|
+
def register_app
|
11
|
+
STDOUT.puts "Registering #{Bananajour.web_uri}"
|
12
|
+
tr = DNSSD::TextRecord.new
|
13
|
+
tr["name"] = Bananajour.config.name
|
14
|
+
tr["email"] = Bananajour.config.email
|
15
|
+
tr["uri"] = Bananajour.web_uri
|
16
|
+
tr["gravatar"] = Bananajour.gravatar
|
17
|
+
tr["version"] = Bananajour::VERSION
|
18
|
+
DNSSD.register("#{Bananajour.config.name}'s bananajour", "_http._tcp,_bananajour", nil, Bananajour.web_port, tr) {}
|
19
|
+
end
|
20
|
+
def register_repos
|
21
|
+
loop do
|
22
|
+
stop_old_services
|
23
|
+
register_new_repositories
|
24
|
+
sleep(1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def stop_old_services
|
28
|
+
old_services.each do |old_service|
|
29
|
+
STDOUT.puts "Unregistering #{old_service.repository.uri}"
|
30
|
+
old_service.stop
|
31
|
+
@services.delete(old_service)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
def old_services
|
35
|
+
@services.reject {|s| Bananajour.repositories.include?(s.repository)}
|
36
|
+
end
|
37
|
+
def register_new_repositories
|
38
|
+
new_repositories.each do |new_repo|
|
39
|
+
STDOUT.puts "Registering #{new_repo.uri}"
|
40
|
+
tr = DNSSD::TextRecord.new
|
41
|
+
tr["name"] = new_repo.name
|
42
|
+
tr["uri"] = new_repo.uri
|
43
|
+
tr["bjour-name"] = Bananajour.config.name
|
44
|
+
tr["bjour-email"] = Bananajour.config.email
|
45
|
+
tr["bjour-uri"] = Bananajour.web_uri
|
46
|
+
tr["bjour-gravatar"] = Bananajour.gravatar
|
47
|
+
tr["bjour-version"] = Bananajour::VERSION
|
48
|
+
service = DNSSD.register(new_repo.name, "_git._tcp,_bananajour", nil, 9418, tr) {}
|
49
|
+
service.class.instance_eval { attr_accessor(:repository) }
|
50
|
+
service.repository = new_repo
|
51
|
+
@services << service
|
52
|
+
end
|
53
|
+
end
|
54
|
+
def new_repositories
|
55
|
+
Bananajour.repositories.select {|repo| !@services.any? {|s| s.repository == repo } }
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Bananajour::Bonjour
|
2
|
+
class BananajourBrowser
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@browser = Browser.new('_bananajour._http._tcp')
|
6
|
+
end
|
7
|
+
|
8
|
+
def bananajours
|
9
|
+
@browser.replies.map do |reply|
|
10
|
+
Person.new(
|
11
|
+
reply.text_record["name"],
|
12
|
+
reply.text_record["email"],
|
13
|
+
reply.text_record["uri"],
|
14
|
+
reply.text_record["gravatar"]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def other_bananajours
|
20
|
+
bananajours.reject {|b| b.uri == Bananajour.web_uri}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Bananajour.require_gem 'dnssd'
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
Thread.abort_on_exception = true
|
7
|
+
|
8
|
+
# Generic bonjour browser
|
9
|
+
#
|
10
|
+
# Example use:
|
11
|
+
#
|
12
|
+
# browser = BonjourBrowser.new("_bananajour._git._tcp")
|
13
|
+
# loop do
|
14
|
+
# sleep(1)
|
15
|
+
# pp browser.replies.map {|r| r.name}
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Probably gem-worthy
|
19
|
+
class Bananajour::Bonjour::Browser
|
20
|
+
attr_reader :replies
|
21
|
+
def initialize(service)
|
22
|
+
@service = service
|
23
|
+
@mutex = Mutex.new
|
24
|
+
@replies = []
|
25
|
+
watch!
|
26
|
+
end
|
27
|
+
private
|
28
|
+
def watch!
|
29
|
+
DNSSD.browse(@service) do |br|
|
30
|
+
begin
|
31
|
+
Timeout.timeout(5) do
|
32
|
+
DNSSD.resolve(br.name, br.type, br.domain) do |rr|
|
33
|
+
begin
|
34
|
+
@mutex.synchronize do
|
35
|
+
rr_exists = Proc.new {|existing_rr| existing_rr.target == rr.target && existing_rr.fullname == rr.fullname}
|
36
|
+
if (DNSSD::Flags::Add & br.flags.to_i) != 0
|
37
|
+
@replies << rr unless @replies.any?(&rr_exists)
|
38
|
+
else
|
39
|
+
@replies.delete_if(&rr_exists)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
rr.service.stop
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue Timeout::Error
|
48
|
+
# Do nothing
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|