gitoe 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.gitmodules +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +91 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +30 -0
- data/Rakefile +35 -0
- data/Rules +47 -0
- data/bin/gitoe +36 -0
- data/content/gitoe-draw.coffee +342 -0
- data/content/gitoe-repo.coffee +546 -0
- data/content/gitoe.coffee +182 -0
- data/content/index.haml +71 -0
- data/content/jquery/jquery-1.9.1.min.js +5 -0
- data/content/jquery/jquery.scrollTo.min.js +7 -0
- data/content/raphael-min.js +10 -0
- data/content/reset.sass +46 -0
- data/content/style.sass +109 -0
- data/gitoe.gemspec +34 -0
- data/lib/gitoe.rb +16 -0
- data/lib/gitoe/httpserver/public/gitoe-draw.js +399 -0
- data/lib/gitoe/httpserver/public/gitoe-repo.js +618 -0
- data/lib/gitoe/httpserver/public/gitoe.js +249 -0
- data/lib/gitoe/httpserver/public/index.html +49 -0
- data/lib/gitoe/httpserver/public/jquery/jquery-1.9.1.min.js +5 -0
- data/lib/gitoe/httpserver/public/jquery/jquery.scrollTo.min.js +7 -0
- data/lib/gitoe/httpserver/public/raphael-min.js +10 -0
- data/lib/gitoe/httpserver/public/reset.css +45 -0
- data/lib/gitoe/httpserver/public/style.css +106 -0
- data/lib/gitoe/httpserver/repos.rb +71 -0
- data/lib/gitoe/httpserver/static.rb +17 -0
- data/lib/gitoe/repo/repo.rb +174 -0
- data/lib/gitoe/repo/rugged.rb +121 -0
- data/lib/gitoe/version.rb +3 -0
- data/nanoc.yaml +77 -0
- data/test/test.rb +9 -0
- data/todo.markdown +16 -0
- data/vendor/jquery-1.9.1.min.js +5 -0
- data/vendor/raphael-min.js +10 -0
- metadata +239 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
# rack app for repos
|
2
|
+
require "gitoe"
|
3
|
+
require "gitoe/repo/rugged"
|
4
|
+
require "sinatra"
|
5
|
+
require "active_support"
|
6
|
+
|
7
|
+
module Gitoe::HTTPServer
|
8
|
+
class Repos < ::Sinatra::Base
|
9
|
+
|
10
|
+
Repo = ::Gitoe::Repo::RestfulRugged
|
11
|
+
|
12
|
+
set :environment, :production
|
13
|
+
|
14
|
+
def json reply
|
15
|
+
content_type 'application/json'
|
16
|
+
::ActiveSupport::JSON.encode(reply)
|
17
|
+
end
|
18
|
+
|
19
|
+
error do
|
20
|
+
json \
|
21
|
+
:error_message => env['sinatra.error'],
|
22
|
+
:backtrace => env['sinatra.error'].backtrace
|
23
|
+
end
|
24
|
+
|
25
|
+
# index
|
26
|
+
get "/" do
|
27
|
+
json Repo.instances[:by_arg]
|
28
|
+
end
|
29
|
+
|
30
|
+
# create
|
31
|
+
post "/new" do
|
32
|
+
# create instance if not existing
|
33
|
+
# and return id
|
34
|
+
path = params["path"] or raise "path not specified"
|
35
|
+
repo_id = Repo.id_for(path)
|
36
|
+
repo = Repo.find repo_id
|
37
|
+
json \
|
38
|
+
:id => repo_id ,
|
39
|
+
:path => repo.path
|
40
|
+
end
|
41
|
+
|
42
|
+
# show
|
43
|
+
get "/:repo_id/?" do
|
44
|
+
repo = Repo.find params["repo_id"]
|
45
|
+
json repo.status
|
46
|
+
end
|
47
|
+
|
48
|
+
# namespace under /:repo_id/:resource
|
49
|
+
Resources = Set[ 'commits', 'commit' ].freeze
|
50
|
+
get "/:repo_id/**" do
|
51
|
+
|
52
|
+
repo = Repo.find( params["repo_id"] )
|
53
|
+
|
54
|
+
sub_str = params[:splat].last # "**" part
|
55
|
+
|
56
|
+
resource, url = sub_str.split('/',2)
|
57
|
+
raise "invalid resource '#{resource}'" unless Resources.include? resource
|
58
|
+
query_hash = env['rack.request.query_hash']
|
59
|
+
# /1/commits/aaaaaaa/b/c/d ? a=1 & b=1
|
60
|
+
# => {
|
61
|
+
# repo_id: "1",
|
62
|
+
# resource: "commits"
|
63
|
+
# arg: "aaaaaaa/b/c/d"
|
64
|
+
# query_hash:
|
65
|
+
# }
|
66
|
+
# json resource => repo.send(resource.to_sym, arg, env[])
|
67
|
+
json repo.send(resource, url, query_hash )
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# rack app for static files
|
2
|
+
require "gitoe"
|
3
|
+
require "sinatra"
|
4
|
+
|
5
|
+
module Gitoe::HTTPServer
|
6
|
+
class Static < ::Sinatra::Base
|
7
|
+
|
8
|
+
set :app_file, __FILE__
|
9
|
+
set :environment, :production
|
10
|
+
set :static_cache_control, [:public, max_age: 3600]
|
11
|
+
|
12
|
+
get "/" do
|
13
|
+
send_file File.join( settings.public_folder, "index.html" )
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require "gitoe"
|
2
|
+
|
3
|
+
module Gitoe::Repo
|
4
|
+
|
5
|
+
# instance methods, enable querying of instances
|
6
|
+
module Query
|
7
|
+
|
8
|
+
def instances
|
9
|
+
@instances ||= {
|
10
|
+
:by_arg => {}, # arg => id
|
11
|
+
:by_id => {}, # id => instance
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def create arg
|
16
|
+
id = instances[:by_id].size
|
17
|
+
instances[:by_id][id] = self.new arg
|
18
|
+
instances[:by_arg][arg] = id
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy id
|
22
|
+
raise NotImplementedError # TODO
|
23
|
+
end
|
24
|
+
|
25
|
+
def find id
|
26
|
+
id = Integer(id) unless id.is_a? Integer
|
27
|
+
instances[:by_id][id] or raise "repo not found"
|
28
|
+
end
|
29
|
+
|
30
|
+
def id_for arg
|
31
|
+
existing = instances[:by_arg][arg]
|
32
|
+
if existing
|
33
|
+
existing
|
34
|
+
else
|
35
|
+
create arg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module RestfulRepo
|
41
|
+
# public methods( 'resources' ):
|
42
|
+
# #status
|
43
|
+
# #commits
|
44
|
+
|
45
|
+
def status
|
46
|
+
{
|
47
|
+
:refs => refs ,
|
48
|
+
:path => path ,
|
49
|
+
:cached_commits => size_of_cache,
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def commits start, options={}
|
54
|
+
# start :
|
55
|
+
# commits to start at,
|
56
|
+
# possible multiple, ','
|
57
|
+
# options :
|
58
|
+
# limit
|
59
|
+
limit = (options['limit'] || 1000).to_i
|
60
|
+
|
61
|
+
to_query = start.split ','
|
62
|
+
queried = {}
|
63
|
+
|
64
|
+
while to_query.size > 0 and queried.size < limit
|
65
|
+
|
66
|
+
another = to_query.shift
|
67
|
+
next if queried.has_key?(another)
|
68
|
+
|
69
|
+
parent = commit another
|
70
|
+
queried[ parent[:sha1] ] ||= parent
|
71
|
+
parent[:parents].each do |p|
|
72
|
+
next if queried.has_key? p
|
73
|
+
to_query.push p
|
74
|
+
end
|
75
|
+
end
|
76
|
+
$gitoe_log[ "gathered #{queried.size} from #{start}" ]
|
77
|
+
|
78
|
+
queried
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
module Cache
|
84
|
+
def initialize *args
|
85
|
+
init_cache
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def ref name
|
92
|
+
follow_ref super
|
93
|
+
end
|
94
|
+
|
95
|
+
def follow_ref ref_hash
|
96
|
+
ref_hash
|
97
|
+
end
|
98
|
+
|
99
|
+
def commit sha1, options={}
|
100
|
+
# if in_cache?
|
101
|
+
# just take from cache
|
102
|
+
# else
|
103
|
+
# add sha1 and all its ancestors to cache, in topological order
|
104
|
+
if in_cache? sha1
|
105
|
+
return from_cache sha1
|
106
|
+
end
|
107
|
+
this = super(sha1).freeze
|
108
|
+
if not in_cache? this[:sha1]
|
109
|
+
add_to_cache this[:sha1], this
|
110
|
+
end
|
111
|
+
this
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def add_to_cache sha1, content
|
117
|
+
if $gitoe_debug and in_cache?(sha1)
|
118
|
+
raise "#{sha1} already in cache"
|
119
|
+
end
|
120
|
+
$gitoe_log[ "add #{sha1} to cache" ]
|
121
|
+
@cached_commits[sha1] = content
|
122
|
+
end
|
123
|
+
|
124
|
+
def init_cache
|
125
|
+
@cached_commits = {} # { sha1:content, order:content }
|
126
|
+
end
|
127
|
+
|
128
|
+
def size_of_cache
|
129
|
+
@cached_commits.size
|
130
|
+
end
|
131
|
+
|
132
|
+
def from_cache sha1
|
133
|
+
@cached_commits[sha1] or raise "not in cache"
|
134
|
+
end
|
135
|
+
|
136
|
+
def in_cache? key
|
137
|
+
@cached_commits.has_key? key
|
138
|
+
end
|
139
|
+
|
140
|
+
def sort_topo queried
|
141
|
+
|
142
|
+
sorted = []
|
143
|
+
|
144
|
+
in_degree = Hash.new{|h,k| h[k] = 0 }
|
145
|
+
children = Hash.new{|h,k| h[k] = [] }
|
146
|
+
|
147
|
+
queried.each do |sha1,content|
|
148
|
+
in_degree[sha1]
|
149
|
+
content[:parents].each do |p|
|
150
|
+
in_degree[p]
|
151
|
+
in_degree[sha1] += 1
|
152
|
+
children[p] << sha1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
to_remove = in_degree.keys.select{|k| in_degree[k] == 0 }
|
157
|
+
while to_remove.size > 0
|
158
|
+
# $gitoe_log[ "topo-sorting : remaining #{in_degree.size}" ]
|
159
|
+
|
160
|
+
n = to_remove.shift
|
161
|
+
sorted.push n
|
162
|
+
in_degree.delete n
|
163
|
+
|
164
|
+
children[n].each do |c|
|
165
|
+
new_in_degree = in_degree[c] -= 1
|
166
|
+
to_remove.push c if new_in_degree == 0
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
$gitoe_log[ "topo-sorting DONE" ]
|
171
|
+
return sorted.select{|s| queried.has_key? s }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# repo using rugged ( https://github.com/libgit2/rugged )
|
2
|
+
require "gitoe/repo/repo"
|
3
|
+
require "rugged"
|
4
|
+
|
5
|
+
module Gitoe::Repo
|
6
|
+
class Rugged_backend
|
7
|
+
|
8
|
+
# implements
|
9
|
+
# path :: -> String
|
10
|
+
# commit :: sha1::String -> { prop:val }
|
11
|
+
# refs :: -> { ref_name : reflogs }
|
12
|
+
# ref :: String -> reflogs
|
13
|
+
|
14
|
+
# private
|
15
|
+
# ref_names :: -> [ String ]
|
16
|
+
# reflog :: String -> [ {} ]
|
17
|
+
|
18
|
+
include ::Rugged
|
19
|
+
|
20
|
+
def initialize path
|
21
|
+
@rugged = Repository.new path
|
22
|
+
end
|
23
|
+
|
24
|
+
def path
|
25
|
+
@rugged.path
|
26
|
+
end
|
27
|
+
|
28
|
+
def commit sha1
|
29
|
+
# $gitoe_log[ "query #{sha1}" ]
|
30
|
+
obj = @rugged.lookup(sha1)
|
31
|
+
case obj
|
32
|
+
when Commit
|
33
|
+
commit_to_hash obj
|
34
|
+
else
|
35
|
+
raise "#{obj} is not commit"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def refs
|
40
|
+
Hash[
|
41
|
+
ref_names.map do |ref_name|
|
42
|
+
[ ref_name, ref(ref_name) ]
|
43
|
+
end
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
def ref(name)
|
48
|
+
resolved = Reference.lookup(@rugged, name)
|
49
|
+
basic = {
|
50
|
+
:name => resolved.name ,
|
51
|
+
:target => resolved.target ,
|
52
|
+
:type => resolved.type ,
|
53
|
+
:log => reflog( resolved ),
|
54
|
+
}
|
55
|
+
case name
|
56
|
+
when %r{^refs/remotes/}, %r{^refs/heads/}, 'HEAD'
|
57
|
+
extra = {}
|
58
|
+
when %r{^refs/tags/}
|
59
|
+
extra = deref_tag(resolved)
|
60
|
+
else
|
61
|
+
raise "error parsing ref <ref>"
|
62
|
+
end
|
63
|
+
basic.merge(extra).freeze
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def ref_names
|
69
|
+
@rugged.refs.to_a.each do |ref_name|
|
70
|
+
ref_name.sub! %r{^/} , ""
|
71
|
+
end << "HEAD"
|
72
|
+
end
|
73
|
+
|
74
|
+
def deref_tag tag
|
75
|
+
target = @rugged.lookup(tag.target)
|
76
|
+
case target
|
77
|
+
when Tag
|
78
|
+
{
|
79
|
+
tag_type: 'annotated',
|
80
|
+
real_target: target.target.oid
|
81
|
+
}
|
82
|
+
when Commit
|
83
|
+
{
|
84
|
+
tag_type: 'lightweight'
|
85
|
+
}
|
86
|
+
else
|
87
|
+
raise "don't know #{tag}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def reflog ref
|
92
|
+
raise "demand Reference" unless ref.is_a? Reference
|
93
|
+
log =
|
94
|
+
begin
|
95
|
+
ref.log
|
96
|
+
rescue
|
97
|
+
[]
|
98
|
+
end
|
99
|
+
log.each do |change|
|
100
|
+
change[:committer][:time] = change[:committer][:time].to_i # seconds from epoch
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def commit_to_hash commit_obj
|
105
|
+
{
|
106
|
+
:sha1 => commit_obj.oid,
|
107
|
+
:parents => commit_obj.parents.map(&:oid),
|
108
|
+
# :type => :commit,
|
109
|
+
:author => commit_obj.author,
|
110
|
+
:committer => commit_obj.committer
|
111
|
+
}.freeze
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class RestfulRugged < Rugged_backend
|
116
|
+
extend Query
|
117
|
+
include Cache
|
118
|
+
include RestfulRepo
|
119
|
+
# ancestors: [RestfulRugged, RestfulRepo, Cache, Rugged_backend]
|
120
|
+
end
|
121
|
+
end
|
data/nanoc.yaml
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# A list of file extensions that nanoc will consider to be textual rather than
|
2
|
+
# binary. If an item with an extension not in this list is found, the file
|
3
|
+
# will be considered as binary.
|
4
|
+
text_extensions: [ 'coffee', 'haml', 'sass' ]
|
5
|
+
|
6
|
+
# The path to the directory where all generated files will be written to. This
|
7
|
+
# can be an absolute path starting with a slash, but it can also be path
|
8
|
+
# relative to the site directory.
|
9
|
+
output_dir: lib/gitoe/httpserver/public/
|
10
|
+
|
11
|
+
# A list of index filenames, i.e. names of files that will be served by a web
|
12
|
+
# server when a directory is requested. Usually, index files are named
|
13
|
+
# “index.html”, but depending on the web server, this may be something else,
|
14
|
+
# such as “default.htm”. This list is used by nanoc to generate pretty URLs.
|
15
|
+
index_filenames: [ 'index.html' ]
|
16
|
+
|
17
|
+
# Whether or not to generate a diff of the compiled content when compiling a
|
18
|
+
# site. The diff will contain the differences between the compiled content
|
19
|
+
# before and after the last site compilation.
|
20
|
+
enable_output_diff: false
|
21
|
+
|
22
|
+
prune:
|
23
|
+
# Whether to automatically remove files not managed by nanoc from the output
|
24
|
+
# directory. For safety reasons, this is turned off by default.
|
25
|
+
auto_prune: true
|
26
|
+
|
27
|
+
# Which files and directories you want to exclude from pruning. If you version
|
28
|
+
# your output directory, you should probably exclude VCS directories such as
|
29
|
+
# .git, .svn etc.
|
30
|
+
exclude: [ '.git', '.hg', '.svn', 'CVS' ]
|
31
|
+
|
32
|
+
# The data sources where nanoc loads its data from. This is an array of
|
33
|
+
# hashes; each array element represents a single data source. By default,
|
34
|
+
# there is only a single data source that reads data from the “content/” and
|
35
|
+
# “layout/” directories in the site directory.
|
36
|
+
data_sources:
|
37
|
+
-
|
38
|
+
# The type is the identifier of the data source. By default, this will be
|
39
|
+
# `filesystem_unified`.
|
40
|
+
type: filesystem_unified
|
41
|
+
|
42
|
+
# The path where items should be mounted (comparable to mount points in
|
43
|
+
# Unix-like systems). This is “/” by default, meaning that items will have
|
44
|
+
# “/” prefixed to their identifiers. If the items root were “/en/”
|
45
|
+
# instead, an item at content/about.html would have an identifier of
|
46
|
+
# “/en/about/” instead of just “/about/”.
|
47
|
+
items_root: /
|
48
|
+
|
49
|
+
# The path where layouts should be mounted. The layouts root behaves the
|
50
|
+
# same as the items root, but applies to layouts rather than items.
|
51
|
+
layouts_root: /
|
52
|
+
|
53
|
+
# Whether to allow periods in identifiers. When turned off, everything
|
54
|
+
# past the first period is considered to be the extension, and when
|
55
|
+
# turned on, only the characters past the last period are considered to
|
56
|
+
# be the extension. For example, a file named “content/about.html.erb”
|
57
|
+
# will have the identifier “/about/” when turned off, but when turned on
|
58
|
+
# it will become “/about.html/” instead.
|
59
|
+
allow_periods_in_identifiers: true
|
60
|
+
|
61
|
+
# Configuration for the “watch” command, which watches a site for changes and
|
62
|
+
# recompiles if necessary.
|
63
|
+
watcher:
|
64
|
+
# A list of directories to watch for changes. When editing this, make sure
|
65
|
+
# that the “output/” and “tmp/” directories are _not_ included in this list,
|
66
|
+
# because recompiling the site will cause these directories to change, which
|
67
|
+
# will cause the site to be recompiled, which will cause these directories
|
68
|
+
# to change, which will cause the site to be recompiled again, and so on.
|
69
|
+
dirs_to_watch: [ 'content' ]
|
70
|
+
|
71
|
+
# A list of single files to watch for changes. As mentioned above, don’t put
|
72
|
+
# any files from the “output/” or “tmp/” directories in here.
|
73
|
+
files_to_watch: [ 'nanoc.yaml', 'Rules' ]
|
74
|
+
|
75
|
+
# When to send notifications (using Growl or notify-send).
|
76
|
+
notify_on_compilation_success: true
|
77
|
+
notify_on_compilation_failure: true
|