gitoe 0.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.
- 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
|