ginatra 3.0.1 → 4.0.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 -15
- data/.travis.yml +7 -6
- data/CONTRIBUTING.md +30 -0
- data/Gemfile +1 -9
- data/LICENSE.txt +30 -0
- data/README.md +63 -114
- data/Rakefile +10 -12
- data/bin/ginatra +79 -63
- data/config.ru +35 -3
- data/ginatra.gemspec +29 -18
- data/lib/ginatra.rb +161 -148
- data/lib/ginatra/config.rb +18 -121
- data/lib/ginatra/errors.rb +10 -0
- data/lib/ginatra/helpers.rb +154 -139
- data/lib/ginatra/repo.rb +67 -82
- data/lib/ginatra/repo_list.rb +25 -18
- data/lib/ginatra/repo_stats.rb +93 -0
- data/lib/ginatra/version.rb +4 -0
- data/lib/git/webby.rb +292 -0
- data/lib/git/webby/extensions.rb +10 -0
- data/lib/git/webby/http_backend.rb +177 -0
- data/lib/sinatra/partials.rb +1 -1
- data/public/css/application.css +6 -0
- data/public/css/custom.css +57 -0
- data/public/css/lib/bootstrap-responsive.min.css +9 -0
- data/public/css/lib/bootstrap.min.css +9 -0
- data/public/css/lib/highlight.css +209 -0
- data/public/img/glyphicons-halflings-white.png +0 -0
- data/public/img/glyphicons-halflings.png +0 -0
- data/public/img/spin.gif +0 -0
- data/public/js/application.js +5 -0
- data/public/js/custom.js +51 -0
- data/public/js/lib/bootstrap.min.js +6 -0
- data/public/js/lib/jquery.lazyload.min.js +2 -0
- data/public/js/lib/jquery.min.js +2 -0
- data/public/js/lib/jquery.pjax.js +739 -0
- data/repos/README.md +21 -8
- data/spec/ginatra/helpers_spec.rb +95 -0
- data/spec/ginatra/repo_list_spec.rb +66 -0
- data/spec/ginatra/repo_spec.rb +78 -0
- data/spec/ginatra/repo_stats_spec.rb +27 -0
- data/spec/ginatra_spec.rb +121 -0
- data/spec/spec_helper.rb +8 -17
- data/views/404.erb +18 -0
- data/views/500.erb +18 -0
- data/views/_footer.erb +7 -0
- data/views/_header.erb +12 -6
- data/views/_tree_nav.erb +53 -0
- data/views/atom.erb +32 -0
- data/views/blob.erb +27 -8
- data/views/commit.erb +95 -17
- data/views/empty_repo.erb +10 -0
- data/views/index.erb +27 -11
- data/views/layout.erb +16 -20
- data/views/log.erb +74 -54
- data/views/stats.erb +89 -0
- data/views/tree.erb +32 -20
- metadata +168 -94
- data/bin/ginatra-daemon +0 -87
- data/bin/ginatra-directory +0 -55
- data/bin/ginatra-server +0 -27
- data/bin/ginatra-setup +0 -28
- data/lib/ginatra/graph_commit.rb +0 -77
- data/public/img/add.png +0 -0
- data/public/img/diff.png +0 -0
- data/public/img/doc.png +0 -0
- data/public/img/rm.png +0 -0
- data/public/img/tree.png +0 -0
- data/public/src/branch-graph.js +0 -170
- data/public/src/colour.css +0 -86
- data/public/src/commit.css +0 -211
- data/public/src/ginatra.js +0 -7
- data/public/src/github.css +0 -129
- data/public/src/graph.css +0 -9
- data/public/src/highlight.pack.js +0 -1
- data/public/src/index.css +0 -92
- data/public/src/lists.css +0 -25
- data/public/src/raphael.js +0 -7
- data/public/src/reset.css +0 -49
- data/public/src/table.css +0 -33
- data/public/src/type.css +0 -30
- data/rackup.ru +0 -5
- data/spec/graph_commit_spec.rb +0 -54
- data/spec/repo_list_spec.rb +0 -84
- data/spec/repo_spec.rb +0 -61
- data/views/_actor_box.erb +0 -13
- data/views/_commit_info_box.erb +0 -27
- data/views/_tree_part.erb +0 -11
- data/views/atom.builder +0 -32
- data/views/graph.erb +0 -15
data/lib/ginatra/repo.rb
CHANGED
@@ -1,113 +1,103 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
module Grit
|
4
|
-
class Commit
|
5
|
-
# this lets us add a link between commits and refs directly
|
6
|
-
attr_accessor :refs
|
7
|
-
|
8
|
-
def ==(other_commit)
|
9
|
-
id == other_commit.id
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
1
|
+
require 'rugged'
|
13
2
|
|
14
3
|
module Ginatra
|
15
|
-
|
16
|
-
# A thin wrapper to the Grit::repo class so that we can add a name and a url-sanitised-name
|
17
|
-
# to a repo, and also intercept and add refs to the commit objects.
|
18
4
|
class Repo
|
19
|
-
|
20
5
|
attr_reader :name, :param, :description
|
21
6
|
|
22
7
|
# Create a new repository, and sort out clever stuff including assigning
|
23
8
|
# the param, the name and the description.
|
24
9
|
#
|
25
|
-
# @todo cleanup!
|
26
|
-
#
|
27
10
|
# @param [String] path a path to the repository you want created
|
28
11
|
# @return [Ginatra::Repo] a repository instance
|
29
12
|
def initialize(path)
|
30
|
-
@repo =
|
13
|
+
@repo = Rugged::Repository.new(path)
|
31
14
|
@param = File.split(path).last
|
32
15
|
@name = @param
|
33
|
-
@description = @repo.description
|
34
|
-
@description =
|
16
|
+
@description = File.read("#{@repo.path}description").strip
|
17
|
+
@description = '' if @description.match(/\AUnnamed repository;/)
|
35
18
|
end
|
36
19
|
|
37
|
-
|
38
|
-
|
39
|
-
|
20
|
+
# Return a commit corresponding to sha in the repo.
|
21
|
+
#
|
22
|
+
# @param [String] sha the commit id or tag name
|
23
|
+
# @return [Rugged::Commit] the commit object
|
24
|
+
def commit(sha)
|
25
|
+
@repo.lookup(sha)
|
40
26
|
end
|
41
27
|
|
42
|
-
# Return a commit corresponding to
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def commit(id)
|
52
|
-
@commit = @repo.commit(id)
|
53
|
-
raise(Ginatra::InvalidCommit.new(id)) if @commit.nil?
|
54
|
-
add_refs(@commit,{})
|
55
|
-
@commit
|
28
|
+
# Return a commit corresponding to tag in the repo.
|
29
|
+
def commit_by_tag(name)
|
30
|
+
target = @repo.ref("refs/tags/#{name}").target
|
31
|
+
|
32
|
+
if target.is_a? Rugged::Tag::Annotation
|
33
|
+
target = target.target
|
34
|
+
end
|
35
|
+
|
36
|
+
target
|
56
37
|
end
|
57
38
|
|
58
|
-
# Return a list of commits
|
39
|
+
# Return a list of commits in a certain branch, including pagination options and all the refs.
|
59
40
|
#
|
60
41
|
# @param [String] start the branch to look for commits in
|
61
42
|
# @param [Integer] max_count the maximum count of commits
|
62
43
|
# @param [Integer] skip the number of commits in the branch to skip before taking the count.
|
63
44
|
#
|
64
|
-
# @
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@repo.
|
71
|
-
|
72
|
-
|
45
|
+
# @return [Array<Rugged::Commit>] the array of commits.
|
46
|
+
def commits(branch='master', max_count=10, skip=0)
|
47
|
+
raise Ginatra::InvalidRef unless branch_exists?(branch)
|
48
|
+
|
49
|
+
walker = Rugged::Walker.new(@repo)
|
50
|
+
walker.sorting(Rugged::SORT_TOPO)
|
51
|
+
walker.push(@repo.ref("refs/heads/#{branch}").target)
|
52
|
+
|
53
|
+
commits = walker.collect {|commit| commit }
|
54
|
+
commits[skip, max_count]
|
73
55
|
end
|
74
56
|
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
add_refs(commit, ref_cache)
|
89
|
-
GraphCommit.new(commit)
|
57
|
+
# Returns list of branches sorted by name alphabetically
|
58
|
+
def branches
|
59
|
+
@repo.branches.each(:local).sort_by {|b| b.name }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns list of branches containing the commit
|
63
|
+
def branches_with(commit)
|
64
|
+
b = []
|
65
|
+
branches.each do |branch|
|
66
|
+
walker = Rugged::Walker.new(@repo)
|
67
|
+
walker.sorting(Rugged::SORT_TOPO)
|
68
|
+
walker.push(@repo.ref("refs/heads/#{branch.name}").target)
|
69
|
+
walker.collect { |c| b << branch if c.oid == commit }
|
90
70
|
end
|
71
|
+
b
|
91
72
|
end
|
92
|
-
|
93
|
-
#
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
#
|
99
|
-
def
|
100
|
-
|
101
|
-
|
73
|
+
|
74
|
+
# Checks existence of branch by name
|
75
|
+
def branch_exists?(branch_name)
|
76
|
+
@repo.branches.exists?(branch_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Find blob by oid
|
80
|
+
def find_blob(oid)
|
81
|
+
Rugged::Blob.new @repo, oid
|
82
|
+
end
|
83
|
+
|
84
|
+
# Find tree by tree oid or branch name
|
85
|
+
def find_tree(oid)
|
86
|
+
if branch_exists?(oid)
|
87
|
+
last_commit_sha = @repo.ref("refs/heads/#{oid}").target.oid
|
88
|
+
lookup(last_commit_sha).tree
|
89
|
+
else
|
90
|
+
lookup(oid)
|
102
91
|
end
|
103
|
-
|
104
|
-
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns Rugged::Repository instance
|
95
|
+
def to_rugged
|
96
|
+
@repo
|
105
97
|
end
|
106
98
|
|
107
99
|
# Catch all
|
108
100
|
#
|
109
|
-
# Warning! contains: Magic
|
110
|
-
#
|
111
101
|
# @todo update respond_to? method
|
112
102
|
def method_missing(sym, *args, &block)
|
113
103
|
if @repo.respond_to?(sym)
|
@@ -121,10 +111,5 @@ module Ginatra
|
|
121
111
|
def respond_to?(sym)
|
122
112
|
@repo.respond_to?(sym) || super
|
123
113
|
end
|
124
|
-
|
125
|
-
# not sure why we need this but whatever.
|
126
|
-
def to_s
|
127
|
-
@name.to_s
|
128
|
-
end
|
129
114
|
end
|
130
115
|
end
|
data/lib/ginatra/repo_list.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
|
3
3
|
module Ginatra
|
4
|
-
# A singleton class that lets us make and use a constantly updating
|
4
|
+
# A singleton class that lets us make and use a constantly updating
|
5
5
|
# list of repositories.
|
6
6
|
class RepoList
|
7
7
|
include Singleton
|
8
8
|
attr_accessor :list
|
9
|
-
|
10
|
-
# This creates the list, then does the first refresh to
|
9
|
+
|
10
|
+
# This creates the list, then does the first refresh to
|
11
11
|
# populate it.
|
12
12
|
#
|
13
13
|
# It returns what refresh returns.
|
@@ -15,9 +15,9 @@ module Ginatra
|
|
15
15
|
self.list = []
|
16
16
|
self.refresh
|
17
17
|
end
|
18
|
-
|
19
|
-
# The preferred way to access the list publicly.
|
20
|
-
#
|
18
|
+
|
19
|
+
# The preferred way to access the list publicly.
|
20
|
+
#
|
21
21
|
# @return [Array<Ginatra::Repo>] a list of ginatra repos.
|
22
22
|
def self.list
|
23
23
|
self.instance.refresh
|
@@ -28,9 +28,16 @@ module Ginatra
|
|
28
28
|
# and adds them if they're not already there.
|
29
29
|
def refresh
|
30
30
|
list.clear
|
31
|
-
Ginatra
|
32
|
-
|
33
|
-
|
31
|
+
Ginatra.config.git_dirs.map do |git_dir|
|
32
|
+
|
33
|
+
if Dir.exist?(git_dir.chop)
|
34
|
+
files = Dir.glob(git_dir).sort
|
35
|
+
else
|
36
|
+
dir = File.expand_path("../../../#{git_dir}", __FILE__)
|
37
|
+
files = Dir.glob(dir).sort
|
38
|
+
end
|
39
|
+
|
40
|
+
files.each { |e| add(e) unless Ginatra.config.ignored_files.include?(File.split(e).last) }
|
34
41
|
end
|
35
42
|
list
|
36
43
|
end
|
@@ -39,23 +46,23 @@ module Ginatra
|
|
39
46
|
# globs. Checks to see that it's not there first
|
40
47
|
#
|
41
48
|
# @param [String] path the path of the git repo
|
42
|
-
# @param [String] param the param of the repo if it differs,
|
49
|
+
# @param [String] param the param of the repo if it differs,
|
43
50
|
# for looking to see if it's already on the list
|
44
|
-
def add(path, param
|
51
|
+
def add(path, param=File.split(path).last)
|
45
52
|
unless self.has_repo?(param)
|
46
53
|
begin
|
47
54
|
list << Repo.new(path)
|
48
|
-
rescue
|
55
|
+
rescue Rugged::RepositoryError
|
49
56
|
# If the path is not a git repository, then this error is raised
|
50
57
|
# and causes an error page to result.
|
51
58
|
# Is it preferable to just log that the error happened and not show the error page?
|
52
|
-
Ginatra::
|
59
|
+
raise Ginatra::Error, "Invalid git repository at #{path}. Did you create the directory and forget to run 'git init' inside that directory?"
|
53
60
|
end
|
54
61
|
end
|
55
62
|
list
|
56
63
|
end
|
57
64
|
|
58
|
-
# checks to see if the list contains a repo with a param
|
65
|
+
# checks to see if the list contains a repo with a param
|
59
66
|
# matching the one passed in.
|
60
67
|
#
|
61
68
|
# @param [String] local_param param to check.
|
@@ -65,7 +72,7 @@ module Ginatra
|
|
65
72
|
!list.find { |r| r.param == local_param }.nil?
|
66
73
|
end
|
67
74
|
|
68
|
-
# quick way to look up if there is a repo with a given param in the list.
|
75
|
+
# quick way to look up if there is a repo with a given param in the list.
|
69
76
|
# If not, it refreshes the list and tries again.
|
70
77
|
#
|
71
78
|
# @param [String] local_param the param to lookup
|
@@ -76,7 +83,9 @@ module Ginatra
|
|
76
83
|
repo
|
77
84
|
else
|
78
85
|
refresh
|
79
|
-
list.find { |r| r.param == local_param }
|
86
|
+
repo = list.find { |r| r.param == local_param }
|
87
|
+
raise Ginatra::RepoNotFound if repo.nil?
|
88
|
+
repo
|
80
89
|
end
|
81
90
|
end
|
82
91
|
|
@@ -88,8 +97,6 @@ module Ginatra
|
|
88
97
|
end
|
89
98
|
|
90
99
|
# allows missing methods to cascade to the instance,
|
91
|
-
#
|
92
|
-
# Caution! contains: Magic
|
93
100
|
def self.method_missing(sym, *args, &block)
|
94
101
|
instance.send(sym, *args, &block)
|
95
102
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Ginatra
|
2
|
+
class RepoStats
|
3
|
+
# @param [Ginatra::Repo] repo Ginatra::Repo instance
|
4
|
+
# @param [String] branch_name Branch name of repository
|
5
|
+
# @return [Ginatra::RepoStats]
|
6
|
+
def initialize(repo, branch_name)
|
7
|
+
@repo = repo
|
8
|
+
@branch = branch_name
|
9
|
+
end
|
10
|
+
|
11
|
+
# Contributors to repository
|
12
|
+
#
|
13
|
+
# @return [Array] Information about contributors sorted by commits count
|
14
|
+
def contributors
|
15
|
+
contributors = {}
|
16
|
+
ref = @repo.ref("refs/heads/#{@branch}")
|
17
|
+
walker = Rugged::Walker.new(@repo.to_rugged)
|
18
|
+
walker.push(ref.target)
|
19
|
+
|
20
|
+
walker.each do |commit|
|
21
|
+
author = commit.author
|
22
|
+
email = author[:email]
|
23
|
+
|
24
|
+
if contributors[email]
|
25
|
+
contributors[email] = {
|
26
|
+
author: author[:name],
|
27
|
+
commits_count: contributors[email][:commits_count] + 1
|
28
|
+
}
|
29
|
+
else
|
30
|
+
contributors[email] = {
|
31
|
+
author: author[:name],
|
32
|
+
commits_count: 1
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
contributors.sort_by {|c| c.last[:commits_count] }.reverse
|
38
|
+
end
|
39
|
+
|
40
|
+
# Detect common OSS licenses
|
41
|
+
#
|
42
|
+
# @return [String] License name
|
43
|
+
def license
|
44
|
+
last_commit = @repo.ref("refs/heads/#{@branch}").target
|
45
|
+
license = @repo.blob_at(last_commit.oid, 'LICENSE') || @repo.blob_at(last_commit.oid, 'LICENSE.txt')
|
46
|
+
|
47
|
+
if license.nil?
|
48
|
+
'N/A'
|
49
|
+
else
|
50
|
+
license_text = license.text
|
51
|
+
|
52
|
+
case license_text
|
53
|
+
when /Apache License/
|
54
|
+
'Apache'
|
55
|
+
when /GNU GENERAL PUBLIC LICENSE/
|
56
|
+
'GPL'
|
57
|
+
when /GNU LESSER GENERAL PUBLIC LICENSE/
|
58
|
+
'LGPL'
|
59
|
+
when /Permission is hereby granted, free of charge,/
|
60
|
+
'MIT'
|
61
|
+
when /Redistribution and use in source and binary forms/
|
62
|
+
'BSD'
|
63
|
+
else
|
64
|
+
'N/A'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Repository created at time
|
70
|
+
#
|
71
|
+
# @return [Time] Date of first commit to repository
|
72
|
+
def created_at
|
73
|
+
ref = @repo.ref("refs/heads/#{@branch}")
|
74
|
+
|
75
|
+
walker = Rugged::Walker.new(@repo.to_rugged)
|
76
|
+
walker.sorting(Rugged::SORT_TOPO)
|
77
|
+
walker.push(ref.target)
|
78
|
+
commit = walker.to_a.last
|
79
|
+
Time.at(commit.time)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Commits count in defined branch
|
83
|
+
#
|
84
|
+
# @return [Integer] Commits count
|
85
|
+
def commits_count
|
86
|
+
ref = @repo.ref("refs/heads/#{@branch}")
|
87
|
+
|
88
|
+
walker = Rugged::Walker.new(@repo.to_rugged)
|
89
|
+
walker.push(ref.target)
|
90
|
+
walker.count
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/git/webby.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
# Standard requirements
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# 3rd part requirements
|
6
|
+
require 'sinatra/base'
|
7
|
+
|
8
|
+
# Internal requirements
|
9
|
+
require 'git/webby/extensions'
|
10
|
+
|
11
|
+
# See <b>Git::Webby</b> for documentation.
|
12
|
+
module Git
|
13
|
+
|
14
|
+
# The main goal of the <b>Git::Webby</b> is implement the following useful
|
15
|
+
# features.
|
16
|
+
#
|
17
|
+
# - Smart-HTTP, based on _git-http-backend_.
|
18
|
+
# - Authentication flexible based on database or configuration file like <tt>.htpasswd</tt>.
|
19
|
+
# - API to get information about repository.
|
20
|
+
#
|
21
|
+
# This class configure the needed variables used by application. See
|
22
|
+
# Config::DEFAULTS for the values will be initialized by default.
|
23
|
+
#
|
24
|
+
# Basically, the +default+ attribute set the values that will be necessary
|
25
|
+
# by all applications.
|
26
|
+
#
|
27
|
+
# The HTTP-Backend application is configured by +http_backend+ attribute
|
28
|
+
# to set the Git RCP CLI. More details about this feature, see the
|
29
|
+
# {git-http-backend official
|
30
|
+
# page}[http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html]
|
31
|
+
#
|
32
|
+
# [*default*]
|
33
|
+
# Default configuration. All attributes will be used by all modular
|
34
|
+
# applications.
|
35
|
+
#
|
36
|
+
# *project_root* ::
|
37
|
+
# Sets the root directory where repositories have been
|
38
|
+
# placed.
|
39
|
+
# *git_path* ::
|
40
|
+
# Path to the git command line.
|
41
|
+
#
|
42
|
+
# [*http_backend*]
|
43
|
+
# HTTP-Backend configuration.
|
44
|
+
#
|
45
|
+
# *authenticate* ::
|
46
|
+
# Sets if authentication is required.
|
47
|
+
#
|
48
|
+
# *get_any_file* ::
|
49
|
+
# Like +http.getanyfile+.
|
50
|
+
#
|
51
|
+
# *upload_pack* ::
|
52
|
+
# Like +http.uploadpack+.
|
53
|
+
#
|
54
|
+
# *receive_pack* ::
|
55
|
+
# Like +http.receivepack+.
|
56
|
+
module Webby
|
57
|
+
|
58
|
+
class ProjectHandler #:nodoc:
|
59
|
+
|
60
|
+
# Path to git command
|
61
|
+
attr_reader :path
|
62
|
+
|
63
|
+
attr_reader :project_root
|
64
|
+
|
65
|
+
attr_reader :repository
|
66
|
+
|
67
|
+
def initialize(project_root, path = "/usr/bin/git")
|
68
|
+
@repository = nil
|
69
|
+
@path = check_path(File.expand_path(path))
|
70
|
+
@project_root = check_path(File.expand_path(project_root))
|
71
|
+
end
|
72
|
+
|
73
|
+
def path_to(*args)
|
74
|
+
File.join(@repository || @project_root, *(args.compact.map(&:to_s)))
|
75
|
+
end
|
76
|
+
|
77
|
+
def repository=(name)
|
78
|
+
@repository = check_path(path_to(name))
|
79
|
+
end
|
80
|
+
|
81
|
+
def cli(command, *args)
|
82
|
+
%Q[#{@path} #{args.unshift(command.to_s.gsub("_","-")).compact.join(" ")}]
|
83
|
+
end
|
84
|
+
|
85
|
+
def run(command, *args)
|
86
|
+
chdir{ %x[#{cli command, *args}] }
|
87
|
+
end
|
88
|
+
|
89
|
+
def read_file(*file)
|
90
|
+
File.read(path_to(*file))
|
91
|
+
end
|
92
|
+
|
93
|
+
def loose_object_path(*hash)
|
94
|
+
path_to(:objects, *hash)
|
95
|
+
end
|
96
|
+
|
97
|
+
def pack_idx_path(pack)
|
98
|
+
path_to(:objects, :pack, pack)
|
99
|
+
end
|
100
|
+
|
101
|
+
def info_packs_path
|
102
|
+
path_to(:objects, :info, :packs)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def repository_path(name)
|
108
|
+
bare = name =~ /\w\.git/ ? name : %W[#{name} .git]
|
109
|
+
check_path(path_to(*bare))
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_path(path)
|
113
|
+
path && !path.empty? && File.ftype(path) && path
|
114
|
+
end
|
115
|
+
|
116
|
+
def chdir(&block)
|
117
|
+
Dir.chdir(@repository || @project_root, &block)
|
118
|
+
end
|
119
|
+
|
120
|
+
def ftype
|
121
|
+
{ "120" => "l", "100" => "-", "040" => "d" }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Htpasswd #:nodoc:
|
126
|
+
|
127
|
+
def initialize(file)
|
128
|
+
require 'webrick/httpauth/htpasswd'
|
129
|
+
@handler = WEBrick::HTTPAuth::Htpasswd.new(file)
|
130
|
+
yield self if block_given?
|
131
|
+
end
|
132
|
+
|
133
|
+
def find(username) #:yield: password, salt
|
134
|
+
password = @handler.get_passwd(nil, username, false)
|
135
|
+
if block_given?
|
136
|
+
yield password ? [password, password[0,2]] : [nil, nil]
|
137
|
+
else
|
138
|
+
password
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def authenticated?(username, password)
|
143
|
+
self.find username do |crypted, salt|
|
144
|
+
crypted && salt && crypted == password.crypt(salt)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def create(username, password)
|
149
|
+
@handler.set_passwd(nil, username, password)
|
150
|
+
end
|
151
|
+
alias update create
|
152
|
+
|
153
|
+
def destroy(username)
|
154
|
+
@handler.delete_passwd(nil, username)
|
155
|
+
end
|
156
|
+
|
157
|
+
def include?(username)
|
158
|
+
users.include? username
|
159
|
+
end
|
160
|
+
|
161
|
+
def size
|
162
|
+
users.size
|
163
|
+
end
|
164
|
+
|
165
|
+
def write!
|
166
|
+
@handler.flush
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def users
|
172
|
+
@handler.each{|username, password| username }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
module GitHelpers #:nodoc:
|
177
|
+
|
178
|
+
def git
|
179
|
+
@git ||= ProjectHandler.new(settings.project_root, settings.git_path)
|
180
|
+
end
|
181
|
+
|
182
|
+
def repository
|
183
|
+
git.repository ||= (params[:repository] || params[:captures].first)
|
184
|
+
git
|
185
|
+
end
|
186
|
+
|
187
|
+
def content_type_for_git(name, *suffixes)
|
188
|
+
content_type("application/x-git-#{name}-#{suffixes.compact.join("-")}")
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
module AuthenticationHelpers #:nodoc:
|
194
|
+
|
195
|
+
def htpasswd
|
196
|
+
@htpasswd ||= Htpasswd.new(git.path_to("htpasswd"))
|
197
|
+
end
|
198
|
+
|
199
|
+
def authentication
|
200
|
+
@authentication ||= Rack::Auth::Basic::Request.new request.env
|
201
|
+
end
|
202
|
+
|
203
|
+
def authenticated?
|
204
|
+
request.env["REMOTE_USER"] && request.env["git.webby.authenticated"]
|
205
|
+
end
|
206
|
+
|
207
|
+
def authenticate(username, password)
|
208
|
+
checked = [ username, password ] == authentication.credentials
|
209
|
+
validated = authentication.provided? && authentication.basic?
|
210
|
+
granted = htpasswd.authenticated? username, password
|
211
|
+
if checked and validated and granted
|
212
|
+
request.env["git.webby.authenticated"] = true
|
213
|
+
request.env["REMOTE_USER"] = authentication.username
|
214
|
+
else
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def unauthorized!(realm = Git::Webby::info)
|
220
|
+
headers "WWW-Authenticate" => %(Basic realm="#{realm}")
|
221
|
+
throw :halt, [ 401, "Authorization Required" ]
|
222
|
+
end
|
223
|
+
|
224
|
+
def bad_request!
|
225
|
+
throw :halt, [ 400, "Bad Request" ]
|
226
|
+
end
|
227
|
+
|
228
|
+
def authenticate!
|
229
|
+
return if authenticated?
|
230
|
+
unauthorized! unless authentication.provided?
|
231
|
+
bad_request! unless authentication.basic?
|
232
|
+
unauthorized! unless authenticate(*authentication.credentials)
|
233
|
+
request.env["REMOTE_USER"] = authentication.username
|
234
|
+
end
|
235
|
+
|
236
|
+
def access_granted?(username, password)
|
237
|
+
authenticated? || authenticate(username, password)
|
238
|
+
end
|
239
|
+
|
240
|
+
end # AuthenticationHelpers
|
241
|
+
|
242
|
+
# Servers
|
243
|
+
autoload :HttpBackend, "git/webby/http_backend"
|
244
|
+
|
245
|
+
class << self
|
246
|
+
|
247
|
+
def config
|
248
|
+
@config ||= {
|
249
|
+
:default => {
|
250
|
+
:project_root => "/home/git",
|
251
|
+
:git_path => "/usr/bin/git"
|
252
|
+
},
|
253
|
+
:http_backend => {
|
254
|
+
:authenticate => true,
|
255
|
+
:get_any_file => true,
|
256
|
+
:upload_pack => true,
|
257
|
+
:receive_pack => false
|
258
|
+
}
|
259
|
+
}.to_struct
|
260
|
+
end
|
261
|
+
|
262
|
+
# Configure Git::Webby modules using keys. See Config for options.
|
263
|
+
def configure(&block)
|
264
|
+
yield config
|
265
|
+
config
|
266
|
+
end
|
267
|
+
|
268
|
+
def load_config_file(file)
|
269
|
+
YAML.load_file(file).to_struct.each_pair do |app, options|
|
270
|
+
options.each_pair do |option, value|
|
271
|
+
config[app][option] = value
|
272
|
+
end
|
273
|
+
end
|
274
|
+
config
|
275
|
+
rescue IndexError
|
276
|
+
abort "configuration option not found"
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
class Application < Sinatra::Base #:nodoc:
|
282
|
+
|
283
|
+
set :project_root, lambda { Git::Webby.config.default.project_root }
|
284
|
+
set :git_path, lambda { Git::Webby.config.default.git_path }
|
285
|
+
|
286
|
+
mime_type :json, "application/json"
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|