bananajour 2.1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Screenshot of local view of Bananajour 2.1.3](http://cloud.github.com/downloads/toolmantim/bananajour/screenshot.png)
|
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
|