bigbananajour 0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +64 -0
- data/Readme.md +118 -0
- data/bin/bigbananajour +46 -0
- data/lib/bigbananajour.rb +16 -0
- data/lib/bigbananajour/bananajour/bananajour.rb +34 -0
- data/lib/bigbananajour/bananajour/bonjour/advertiser.rb +30 -0
- data/lib/bigbananajour/bananajour/bonjour/repository.rb +9 -0
- data/lib/bigbananajour/bananajour/bonjour/repository_browser.rb +19 -0
- data/lib/bigbananajour/bananajour/commands.rb +25 -0
- data/lib/bigbananajour/bananajour/repository.rb +18 -0
- data/lib/bigbananajour/eater.rb +54 -0
- data/lib/bigbananajour/email_mangle_helper.rb +10 -0
- data/lib/bigbananajour/gem_dependencies.rb +37 -0
- data/lib/bigbananajour/version.rb +3 -0
- data/sinatra/app.rb +58 -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 +114 -0
- data/sinatra/views/home.haml +204 -0
- data/sinatra/views/layout.haml +119 -0
- data/sinatra/views/readme.haml +52 -0
- metadata +190 -0
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
$:.unshift "#{File.dirname(__FILE__)}/lib"
|
2
|
+
|
3
|
+
require "bigbananajour/gem_dependencies"
|
4
|
+
require "bigbananajour/version"
|
5
|
+
|
6
|
+
gem = Gem::Specification.new do |gem|
|
7
|
+
gem.name = "bigbananajour"
|
8
|
+
gem.version = BigBananajour::VERSION
|
9
|
+
gem.platform = Gem::Platform::RUBY
|
10
|
+
gem.extra_rdoc_files = ["Readme.md"]
|
11
|
+
gem.summary = "Bananajour mirroring FTW!"
|
12
|
+
gem.description = gem.summary
|
13
|
+
gem.authors = ["James Sadler"]
|
14
|
+
gem.email = "freshtonic@gmail.com"
|
15
|
+
gem.homepage = "http://github.com/freshtonic/bigbananajour"
|
16
|
+
gem.require_path = "lib"
|
17
|
+
gem.files = %w(Readme.md Rakefile) + Dir.glob("{bin,lib,sinatra}/**/*")
|
18
|
+
gem.has_rdoc = false
|
19
|
+
gem.bindir = 'bin'
|
20
|
+
gem.executables = [ 'bigbananajour']
|
21
|
+
BigBananajour::GemDependencies.all.each {|dep| gem.add_runtime_dependency( dep.name, dep.version ) }
|
22
|
+
end
|
23
|
+
|
24
|
+
task :clean do
|
25
|
+
FileUtils.rm_rf Dir['*.gem', '*.gemspec']
|
26
|
+
end
|
27
|
+
|
28
|
+
namespace :gem do
|
29
|
+
|
30
|
+
desc "Rebuild and install bigbananajour as a gem"
|
31
|
+
task :install => :package do
|
32
|
+
require 'rubygems/installer'
|
33
|
+
Dir['*.gem'].each do |gem|
|
34
|
+
Gem::Installer.new(gem).install
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Create the gem"
|
39
|
+
task :package => [:clean, "spec:generate"] do
|
40
|
+
require 'rubygems/builder'
|
41
|
+
Gem::Builder.new( gem ).build
|
42
|
+
end
|
43
|
+
|
44
|
+
namespace :spec do
|
45
|
+
|
46
|
+
desc "Update #{gem.name}.gemspec"
|
47
|
+
task :generate do
|
48
|
+
File.open("#{gem.name}.gemspec", "w") do |f|
|
49
|
+
f.puts(gem.to_ruby)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "Test spec in github cleanroom"
|
54
|
+
task :test => :generate do
|
55
|
+
require 'rubygems/specification'
|
56
|
+
data = File.read("#{gem.name}.gemspec")
|
57
|
+
spec = nil
|
58
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
59
|
+
puts "#{spec} - Good to go!"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
BigBananajour - Local git mirroring for Bananajour!
|
2
|
+
===================================================
|
3
|
+
|
4
|
+
![Screenshot of local view of Bigbananajour 2.1.3](http://github.com/freshtonic/bigbananajour/raw/master/screenshot.png)
|
5
|
+
|
6
|
+
Installation and usage
|
7
|
+
----------------------
|
8
|
+
|
9
|
+
You'll need at least [git version 1.6](http://git-scm.com/). Run `git --version` if you're unsure.
|
10
|
+
|
11
|
+
Install it from [gemcutter](http://gemcutter.org/) via gems:
|
12
|
+
|
13
|
+
gem install bigbananajour
|
14
|
+
|
15
|
+
(you might need to do a `gem sources -a http://gemcutter.org` beforehand!)
|
16
|
+
|
17
|
+
Start it up:
|
18
|
+
|
19
|
+
bigbananajour
|
20
|
+
|
21
|
+
Fire up [http://localhost:9332/](http://localhost:9332/) to check it out.
|
22
|
+
|
23
|
+
Whenever somebody starts a Bananajour instance on the network, Bigbananajour will slurp up all the published git repos, including all branches.
|
24
|
+
And when some updates a repo, Bigbananajour notices and gets the latest changes. You get backups happening automatically and in one place,
|
25
|
+
plus an alternative place to clone from should the other user switch off their bananajour or go AWOL.
|
26
|
+
|
27
|
+
For a list of all the commands:
|
28
|
+
|
29
|
+
bigbananajour help
|
30
|
+
|
31
|
+
Optional configuration: you can override the hostname by setting a global git config option like so:
|
32
|
+
|
33
|
+
git config --global bananajour.hostname foobar
|
34
|
+
|
35
|
+
If you set this setting, then bananajour will assume that you know precisely what you're doing, it will not append .local, it will not check this hostname is valid, or do anything to it. If you set this, then you're on your own.
|
36
|
+
|
37
|
+
If you set this setting, then bananajour will assume that you know precisely what you're doing, it will not append .local, it will not check this hostname is valid, or do anything to it. If you set this, then you're on your own.
|
38
|
+
|
39
|
+
Make sure you have these ports open on your firewall:
|
40
|
+
|
41
|
+
9417 - git - bigbananajour
|
42
|
+
9418 - git - bananajour
|
43
|
+
9331 - bananajour
|
44
|
+
9332 - bigbananajour
|
45
|
+
|
46
|
+
Linux support
|
47
|
+
-------------
|
48
|
+
|
49
|
+
To install the dnssd gem on Linux you'll need [avahi](http://avahi.org/). For Ubunutu peeps this means:
|
50
|
+
|
51
|
+
sudo apt-get install libavahi-compat-libdnssd-dev libavahi-discover
|
52
|
+
|
53
|
+
You can debug whether or not Avahi can see Bananajour and git-daemon Bonjour statuses using the command 'avahi-browse'. This command can be found in the package 'avahi-utils'.
|
54
|
+
|
55
|
+
The following command will show you all of the Bonjour services running on your local network:
|
56
|
+
|
57
|
+
avahi-browse --all
|
58
|
+
|
59
|
+
If you kill bananajour with kill -9 it doesn't get a chance to unregister the Bonjour services, and when it is restarted it will die with DNSSD::AlreadyRegisteredError. Although not ideal, you can work around this my restarting avahi-daemon first.
|
60
|
+
|
61
|
+
You will also need to uncomment the following line in your /etc/avahi/avahi-daemon.conf.
|
62
|
+
|
63
|
+
domain-name=local
|
64
|
+
|
65
|
+
And then restart the Avahi service:
|
66
|
+
|
67
|
+
sudo service avahi-daemon restart
|
68
|
+
|
69
|
+
Note: You might have to restart the avahi-daemon sometimes if you are having problems seeing other bananajours.
|
70
|
+
|
71
|
+
|
72
|
+
Official repository and support
|
73
|
+
-------------------------------
|
74
|
+
|
75
|
+
The official repo and support issues/tickets live at [github.com/freshtonic/bigbananajour](http://github.com/freshtonic/bigbananajour).
|
76
|
+
|
77
|
+
Developing
|
78
|
+
----------
|
79
|
+
|
80
|
+
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:
|
81
|
+
|
82
|
+
ruby sinatra/app.rb -s thin
|
83
|
+
|
84
|
+
If you want code reloading use [shotgun](http://github.com/rtomayko/shotgun) instead:
|
85
|
+
|
86
|
+
shotgun sinatra/app.rb -s thin
|
87
|
+
|
88
|
+
If you then want to run your working copy as your public bananajour rebuild and install it as a gem:
|
89
|
+
|
90
|
+
sudo rake gem:install
|
91
|
+
|
92
|
+
Contributors
|
93
|
+
------------
|
94
|
+
|
95
|
+
* [James Sadler](http://github.com/freshtonic)
|
96
|
+
|
97
|
+
But the real credit should go to all these awesome people for making Bananajour:
|
98
|
+
|
99
|
+
* [Tim Lucas](http://github.com/toolmantim)
|
100
|
+
* [Carla Hackett](http://carlahackettdesign.com/) (logo)
|
101
|
+
* [Nathan de Vries](http://github.com/atnan)
|
102
|
+
* [Lachlan Hardy](http://github.com/lachlanhardy)
|
103
|
+
* [Josh Bassett](http://github.com/nullobject)
|
104
|
+
* [Myles Byrne](http://github.com/quackingduck)
|
105
|
+
* [Ben Hoskings](http://github.com/benhoskings)
|
106
|
+
* [Brett Goulder](http://github.com/brettgo1)
|
107
|
+
* [Tony Issakov](https://github.com/tissak)
|
108
|
+
* [Mark Bennett](http://github.com/MarkBennett)
|
109
|
+
* [Travis Swicegood](http://github.com/tswicegood)
|
110
|
+
* [Nate Haas](http://github.com/natehaas)
|
111
|
+
* [James Sadler](http://github.com/freshtonic)
|
112
|
+
* [Jason King](http://github.com/JasonKing)
|
113
|
+
|
114
|
+
License
|
115
|
+
-------
|
116
|
+
|
117
|
+
All directories and files are MIT Licensed.
|
118
|
+
|
data/bin/bigbananajour
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require (File.symlink?(__FILE__) ? "bigbananajour" : File.join(File.dirname(__FILE__), "/../lib/bigbananajour"))
|
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
|
+
parent_process_pid = Process.pid
|
13
|
+
pids = []
|
14
|
+
at_exit do
|
15
|
+
if Process.pid == parent_process_pid
|
16
|
+
pids.each do |pid|
|
17
|
+
Process.kill("TERM", pid) rescue nil
|
18
|
+
sleep 0.2
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
case ARGV.first
|
24
|
+
|
25
|
+
when nil
|
26
|
+
pids << Bananajour.serve_web!
|
27
|
+
pids << Bananajour.serve_git!
|
28
|
+
pids << Bananajour.eat_me_some_bananas!
|
29
|
+
pids << Bananajour.advertise!
|
30
|
+
Process.waitall
|
31
|
+
|
32
|
+
when "help", "--help", "-h"
|
33
|
+
puts <<-HELP
|
34
|
+
Usage: #{File.basename($0)} [<command>]
|
35
|
+
|
36
|
+
Commands:
|
37
|
+
none - Mirror all bananajour repositories and start web, git and bonjour serving
|
38
|
+
help
|
39
|
+
version
|
40
|
+
HELP
|
41
|
+
|
42
|
+
when "version", "--version", "-v"
|
43
|
+
puts "bigbananajour version #{BigBananajour::VERSION}"
|
44
|
+
else
|
45
|
+
abort "Say what? Try: #{File.basename($0)} help"
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
+
|
4
|
+
require "bananajour"
|
5
|
+
|
6
|
+
require 'bigbananajour/email_mangle_helper'
|
7
|
+
require 'bigbananajour/eater'
|
8
|
+
require 'bigbananajour/version'
|
9
|
+
|
10
|
+
# Bananajour monkeypatches.
|
11
|
+
require 'bigbananajour/bananajour/bananajour'
|
12
|
+
require 'bigbananajour/bananajour/commands'
|
13
|
+
require 'bigbananajour/bananajour/repository'
|
14
|
+
require 'bigbananajour/bananajour/bonjour/advertiser'
|
15
|
+
require 'bigbananajour/bananajour/bonjour/repository'
|
16
|
+
require 'bigbananajour/bananajour/bonjour/repository_browser'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
# Monkeypatch bananajour to override ports
|
3
|
+
# and repository resolution behaviour.
|
4
|
+
module Bananajour
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
include BigBananajour::EmailMangleHelper
|
9
|
+
|
10
|
+
def repositories_path
|
11
|
+
path/"mirrored_repositories"
|
12
|
+
end
|
13
|
+
|
14
|
+
def web_port
|
15
|
+
9332
|
16
|
+
end
|
17
|
+
|
18
|
+
def git_port
|
19
|
+
9419
|
20
|
+
end
|
21
|
+
|
22
|
+
def git_uri
|
23
|
+
"git://#{host_name}:#{git_port}/"
|
24
|
+
end
|
25
|
+
|
26
|
+
def repositories
|
27
|
+
repositories_path.children.map {|dir| Fancypath(dir).children.map{|r| Repository.new(r)}.sort_by{|r| r.name}}.flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
def people_that_have_repository(repository_name)
|
31
|
+
repositories.select{|r| r.name == repository_name}.map{|r| demangle_email(r.path.expand_path.to_s.split("/")[-2]) }.sort
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Bananajour::Bonjour::Advertiser
|
2
|
+
private
|
3
|
+
def register_app
|
4
|
+
STDOUT.puts "Registering #{Bananajour.web_uri}"
|
5
|
+
tr = DNSSD::TextRecord.new
|
6
|
+
tr["name"] = Bananajour.config.name
|
7
|
+
tr["email"] = Bananajour.config.email
|
8
|
+
tr["uri"] = Bananajour.web_uri
|
9
|
+
tr["gravatar"] = Bananajour.gravatar
|
10
|
+
tr["version"] = Bananajour::VERSION
|
11
|
+
DNSSD.register("#{Bananajour.config.name}'s BIG bananajour", "_http._tcp,_bigbananajour", nil, Bananajour.web_port, tr) {}
|
12
|
+
end
|
13
|
+
def register_new_repositories
|
14
|
+
new_repositories.each do |new_repo|
|
15
|
+
STDOUT.puts "Registering #{new_repo.uri}"
|
16
|
+
tr = DNSSD::TextRecord.new
|
17
|
+
tr["name"] = new_repo.name
|
18
|
+
tr["uri"] = new_repo.uri
|
19
|
+
tr["bjour-name"] = Bananajour.config.name
|
20
|
+
tr["bjour-email"] = Bananajour.config.email
|
21
|
+
tr["bjour-uri"] = Bananajour.web_uri
|
22
|
+
tr["bjour-gravatar"] = Bananajour.gravatar
|
23
|
+
tr["bjour-version"] = BigBananajour::VERSION
|
24
|
+
service = DNSSD.register(new_repo.name, "_git._tcp,_bigbananajour", nil, Bananajour.git_port, tr) {}
|
25
|
+
service.class.instance_eval { attr_accessor(:repository) }
|
26
|
+
service.repository = new_repo
|
27
|
+
@services << service
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bananajour::Bonjour
|
2
|
+
class RepositoryBrowser
|
3
|
+
|
4
|
+
def repositories
|
5
|
+
@browser.replies.map do |reply|
|
6
|
+
Repository.new(
|
7
|
+
reply.text_record["name"],
|
8
|
+
reply.text_record["uri"],
|
9
|
+
Person.new(
|
10
|
+
reply.text_record["bjour-name"],
|
11
|
+
reply.text_record["bjour-email"],
|
12
|
+
reply.text_record["bjour-uri"],
|
13
|
+
reply.text_record["bjour-gravatar"]
|
14
|
+
)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bananajour::Commands
|
2
|
+
|
3
|
+
def serve_web!
|
4
|
+
pid = fork { exec "/usr/bin/env ruby #{File.dirname(__FILE__)}/../../../sinatra/app.rb -p #{web_port} -e production" }
|
5
|
+
puts "* Started " + web_uri.foreground(:yellow)
|
6
|
+
pid
|
7
|
+
end
|
8
|
+
|
9
|
+
def serve_git!
|
10
|
+
pid = fork { exec "git daemon --base-path=#{repositories_path} --export-all --port=#{git_port}" }
|
11
|
+
puts "* Started " + "#{git_uri}".foreground(:yellow)
|
12
|
+
pid
|
13
|
+
end
|
14
|
+
|
15
|
+
def advertise!
|
16
|
+
pid = fork { Bananajour::Bonjour::Advertiser.new.go! }
|
17
|
+
pid
|
18
|
+
end
|
19
|
+
|
20
|
+
def eat_me_some_bananas!
|
21
|
+
pid = fork { Bananajour::Eater.new.go! }
|
22
|
+
pid
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Bananajour.require_gem 'grit'
|
2
|
+
|
3
|
+
module Bananajour
|
4
|
+
class Repository
|
5
|
+
def uri
|
6
|
+
Bananajour.git_uri + path.expand_path.to_s.split("/")[-2..-1].join("/")
|
7
|
+
end
|
8
|
+
# TODO: put this in Bananajour.
|
9
|
+
def get_config(key)
|
10
|
+
`cd #{path} && git config --get #{key}`
|
11
|
+
end
|
12
|
+
def set_config(key,value)
|
13
|
+
$stderr.puts "setting '#{key}' to '#{value}'"
|
14
|
+
`cd #{path} && git config --unset-all #{key}`
|
15
|
+
`cd #{path} && git config --replace-all #{key} "#{value}"`
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Bananajour::Eater
|
2
|
+
def initialize
|
3
|
+
@browser = Bananajour::Bonjour::RepositoryBrowser.new
|
4
|
+
end
|
5
|
+
def go!
|
6
|
+
$stderr.puts "Starting the EATER!!!"
|
7
|
+
while true do
|
8
|
+
@browser.repositories.each do |remote_repo|
|
9
|
+
$stderr.puts "Eater has seen remote repo '#{remote_repo}'"
|
10
|
+
local_repo = Bananajour::Repository.for_name(make_local_repo_name(remote_repo))
|
11
|
+
if local_repo.exists?
|
12
|
+
$stderr.puts "Eater, fetching changes from remote repo #{remote_repo.name}"
|
13
|
+
fetch_latest(local_repo)
|
14
|
+
capture_personal_details(local_repo, remote_repo)
|
15
|
+
else
|
16
|
+
$stderr.puts "Eater, cloning remote repo #{remote_repo.name}"
|
17
|
+
clone_repo(remote_repo)
|
18
|
+
fetch_latest(local_repo)
|
19
|
+
capture_personal_details(local_repo, remote_repo)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
$stderr.puts "Eater thread SLEEPING"
|
23
|
+
sleep 30
|
24
|
+
end
|
25
|
+
$stderr.puts "Eater thread exiting."
|
26
|
+
end
|
27
|
+
private
|
28
|
+
def fetch_latest(repo)
|
29
|
+
# Grit doesn't do fetching(!) so, we'll invoke this ourselves.
|
30
|
+
# Note: the repo's were created with git --mirror
|
31
|
+
# This means that when when we do git fetch, it's actually updating the BRANCHES.
|
32
|
+
# This is so that git clone will create remote refs in the cloned repo.
|
33
|
+
`cd #{repo.path} && git fetch -f`
|
34
|
+
end
|
35
|
+
def clone_repo(remote_repo)
|
36
|
+
# FYI: git clone --bare doesn't add the remote 'origin', need to do it manually.
|
37
|
+
system(["cd #{Bananajour.repositories_path}",
|
38
|
+
"(mkdir #{sanitize_email(remote_repo.person.email)} || true)",
|
39
|
+
"git clone --mirror #{remote_repo.uri} #{make_local_repo_name(remote_repo)}.git"
|
40
|
+
].join(" && "))
|
41
|
+
end
|
42
|
+
# TODO: Re-use the helper version
|
43
|
+
def sanitize_email(email)
|
44
|
+
email.gsub(/@/, "_at_").gsub(/\./, "_dot_")
|
45
|
+
end
|
46
|
+
def make_local_repo_name(remote_repo)
|
47
|
+
sanitize_email(remote_repo.person.email) + "/" + remote_repo.name
|
48
|
+
end
|
49
|
+
def capture_personal_details(local_repo, remote_repo)
|
50
|
+
$stderr.puts "capturing personal details for #{local_repo.path}"
|
51
|
+
local_repo.set_config("bigbananajour.gravatar", remote_repo.person.gravatar)
|
52
|
+
local_repo.set_config("bigbananajour.person", remote_repo.person.name)
|
53
|
+
end
|
54
|
+
end
|