git-lighttp 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +11 -0
- data/Makefile +41 -0
- data/README.pt-BR.rdoc +81 -0
- data/README.rdoc +75 -0
- data/Rakefile +50 -0
- data/doc/releases/v0.1.0.rdoc +11 -0
- data/doc/releases/v0.2.0.rdoc +24 -0
- data/doc/releases/v0.3.0.rdoc +13 -0
- data/git-lighttp.gemspec +25 -0
- data/lib/git/lighttp.rb +365 -0
- data/lib/git/lighttp/extensions.rb +73 -0
- data/lib/git/lighttp/http_backend.rb +178 -0
- data/lib/git/lighttp/treeish.rb +28 -0
- data/lib/git/lighttp/version.rb +26 -0
- data/test/all.rb +12 -0
- data/test/config_test.rb +43 -0
- data/test/fixtures/config.yml +13 -0
- data/test/fixtures/htgroup +3 -0
- data/test/fixtures/htpasswd +4 -0
- data/test/fixtures/mycode.git/HEAD +1 -0
- data/test/fixtures/mycode.git/config +4 -0
- data/test/fixtures/mycode.git/description +1 -0
- data/test/fixtures/mycode.git/hooks/applypatch-msg.sample +15 -0
- data/test/fixtures/mycode.git/hooks/commit-msg.sample +24 -0
- data/test/fixtures/mycode.git/hooks/post-commit.sample +8 -0
- data/test/fixtures/mycode.git/hooks/post-receive.sample +15 -0
- data/test/fixtures/mycode.git/hooks/post-update.sample +8 -0
- data/test/fixtures/mycode.git/hooks/pre-applypatch.sample +14 -0
- data/test/fixtures/mycode.git/hooks/pre-commit.sample +46 -0
- data/test/fixtures/mycode.git/hooks/pre-rebase.sample +169 -0
- data/test/fixtures/mycode.git/hooks/prepare-commit-msg.sample +36 -0
- data/test/fixtures/mycode.git/hooks/update.sample +128 -0
- data/test/fixtures/mycode.git/info/exclude +6 -0
- data/test/fixtures/mycode.git/info/refs +3 -0
- data/test/fixtures/mycode.git/objects/02/83eb96425444e17b97182e1ba9f216cc67c132 +0 -0
- data/test/fixtures/mycode.git/objects/03/9927042df267a1bc606fc4485b7a79b6a9e3cd +1 -0
- data/test/fixtures/mycode.git/objects/0d/eed0a56fa8f5f2a788d58b3ea235afd547b828 +2 -0
- data/test/fixtures/mycode.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
- data/test/fixtures/mycode.git/objects/5e/54a0767e0c380f3baab17938d68c7f464cf171 +1 -0
- data/test/fixtures/mycode.git/objects/63/9b96262e7e19ca2169575e797b234098b8a72e +0 -0
- data/test/fixtures/mycode.git/objects/71/6e9568eed27d5ee4378b3ecf6dd095a547bde9 +1 -0
- data/test/fixtures/mycode.git/objects/b6/f3f0fabeaaaaf2db22b8ef98f59115baec7ef9 +0 -0
- data/test/fixtures/mycode.git/objects/be/118435b9d908fd4a689cd8b0cc98059911a31a +0 -0
- data/test/fixtures/mycode.git/objects/db/aefcb5bde664671c73b99515c386dcbc7f22b6 +0 -0
- data/test/fixtures/mycode.git/objects/eb/669b878d2013ac70aa5dee75e6357ea81d16ea +0 -0
- data/test/fixtures/mycode.git/objects/ed/10cfcf72862e140c97fe899cba2a55f4cb4c20 +0 -0
- data/test/fixtures/mycode.git/objects/ed/1c3a255ab3fce056dc31cd82df9f61a4d9fa22 +0 -0
- data/test/fixtures/mycode.git/objects/info/alternates +0 -0
- data/test/fixtures/mycode.git/objects/info/http-alternates +0 -0
- data/test/fixtures/mycode.git/objects/info/packs +2 -0
- data/test/fixtures/mycode.git/objects/pack/pack-40a8636b62258fffd78ec1e8d254116e72d385a9.idx +0 -0
- data/test/fixtures/mycode.git/objects/pack/pack-40a8636b62258fffd78ec1e8d254116e72d385a9.pack +0 -0
- data/test/fixtures/mycode.git/packed-refs +4 -0
- data/test/fixtures/mycode.git/refs/heads/master +1 -0
- data/test/fixtures/mycode.git/refs/tags/v0.1.0 +1 -0
- data/test/helpers.rb +53 -0
- data/test/htgroup_test.rb +29 -0
- data/test/htpasswd_test.rb +63 -0
- data/test/http_backend_authentication_test.rb +61 -0
- data/test/http_backend_test.rb +123 -0
- data/test/project_handler_test.rb +45 -0
- data/test/treeish_test.rb +33 -0
- metadata +121 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
class Object
|
2
|
+
# Set instance variables by key and value only if object respond
|
3
|
+
# to access method for variable.
|
4
|
+
def instance_variables_set_from(hash)
|
5
|
+
hash.collect do |variable, value|
|
6
|
+
self.instance_variable_set("@#{variable}", value) if self.respond_to? variable
|
7
|
+
end
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class Symbol
|
14
|
+
|
15
|
+
# Method for comparison between symbols.
|
16
|
+
def <=>(other)
|
17
|
+
self.to_s <=> other.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# Parse the symbol name to constant name. Example:
|
21
|
+
#
|
22
|
+
# $ :http_backend.to_const_name
|
23
|
+
# => "HttpBackend"
|
24
|
+
def to_const_name
|
25
|
+
n = self.to_s.split(/_/).map(&:capitalize).join
|
26
|
+
RUBY_VERSION =~ /1\.8/ ? n : n.to_sym
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class Hash
|
32
|
+
|
33
|
+
# Only symbolize all keys, including all key in sub-hashes.
|
34
|
+
def symbolize_keys
|
35
|
+
return self.clone if self.empty?
|
36
|
+
self.inject({}) do |h, (k, v)|
|
37
|
+
h[k.to_sym] = (v.kind_of? Hash) ? v.symbolize_keys : v
|
38
|
+
h
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Convert to Struct including all values that are Hash class.
|
43
|
+
def to_struct
|
44
|
+
keys = self.keys.sort
|
45
|
+
members = keys.map(&:to_sym)
|
46
|
+
Struct.new(*members).new(*keys.map do |key|
|
47
|
+
(self[key].kind_of? Hash) ? self[key].to_struct : self[key]
|
48
|
+
end) unless self.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class String
|
54
|
+
|
55
|
+
def to_semver_h
|
56
|
+
tags = [:major, :minor, :patch, :status]
|
57
|
+
values = self.split(".").map do |key|
|
58
|
+
# Check pre-release status
|
59
|
+
if key.match(/^(\d{1,})([a-z]+[\d\w]{1,}.*)$/i)
|
60
|
+
[ $1.to_i, $2 ]
|
61
|
+
else
|
62
|
+
key.to_i
|
63
|
+
end
|
64
|
+
end.flatten
|
65
|
+
Hash[tags.zip(values)]
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_attr_name
|
69
|
+
self.split("::").last.gsub(/(.)([A-Z])/){"#{$1}_#{$2.downcase}"}.downcase
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Git::Lighttp
|
2
|
+
|
3
|
+
module HttpBackendHelpers #:nodoc:
|
4
|
+
|
5
|
+
include GitHelpers
|
6
|
+
|
7
|
+
def service_request?
|
8
|
+
not params[:service].nil?
|
9
|
+
end
|
10
|
+
|
11
|
+
# select_service feature
|
12
|
+
def service
|
13
|
+
@service = params[:service]
|
14
|
+
return false if @service.nil?
|
15
|
+
return false if @service[0, 4] != "git-"
|
16
|
+
@service = @service.gsub("git-", "")
|
17
|
+
end
|
18
|
+
|
19
|
+
# pkt_write feature
|
20
|
+
def packet_write(line)
|
21
|
+
(line.size + 4).to_s(base=16).rjust(4, "0") + line
|
22
|
+
end
|
23
|
+
|
24
|
+
# pkt_flush feature
|
25
|
+
def packet_flush
|
26
|
+
"0000"
|
27
|
+
end
|
28
|
+
|
29
|
+
# hdr_nocache feature
|
30
|
+
def header_nocache
|
31
|
+
headers "Expires" => "Fri, 01 Jan 1980 00:00:00 GMT",
|
32
|
+
"Pragma" => "no-cache",
|
33
|
+
"Cache-Control" => "no-cache, max-age=0, must-revalidate"
|
34
|
+
end
|
35
|
+
|
36
|
+
# hdr_cache_forever feature
|
37
|
+
def header_cache_forever
|
38
|
+
now = Time.now
|
39
|
+
headers "Date" => now.to_s,
|
40
|
+
"Expires" => (now + 31536000).to_s,
|
41
|
+
"Cache-Control" => "public, max-age=31536000"
|
42
|
+
end
|
43
|
+
|
44
|
+
# select_getanyfile feature
|
45
|
+
def read_any_file
|
46
|
+
unless settings.get_any_file
|
47
|
+
halt 403, "Unsupported service: getanyfile"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# get_text_file feature
|
52
|
+
def read_text_file(*file)
|
53
|
+
read_any_file
|
54
|
+
header_nocache
|
55
|
+
content_type "text/plain"
|
56
|
+
repository.read_file(*file)
|
57
|
+
end
|
58
|
+
|
59
|
+
# get_loose_object feature
|
60
|
+
def send_loose_object(prefix, suffix)
|
61
|
+
read_any_file
|
62
|
+
header_cache_forever
|
63
|
+
content_type_for_git :loose, :object
|
64
|
+
send_file(repository.loose_object_path(prefix, suffix))
|
65
|
+
end
|
66
|
+
|
67
|
+
# get_pack_file and get_idx_file
|
68
|
+
def send_pack_idx_file(pack, idx = false)
|
69
|
+
read_any_file
|
70
|
+
header_cache_forever
|
71
|
+
content_type_for_git :packed, :objects, (idx ? :toc : nil)
|
72
|
+
send_file(repository.pack_idx_path(pack))
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_info_packs
|
76
|
+
read_any_file
|
77
|
+
header_nocache
|
78
|
+
content_type "text/plain; charset=utf-8"
|
79
|
+
send_file(repository.info_packs_path)
|
80
|
+
end
|
81
|
+
|
82
|
+
# run_service feature
|
83
|
+
def run_advertisement(service)
|
84
|
+
header_nocache
|
85
|
+
content_type_for_git service, :advertisement
|
86
|
+
response.body.clear
|
87
|
+
response.body << packet_write("# service=git-#{service}\n")
|
88
|
+
response.body << packet_flush
|
89
|
+
response.body << repository.run(service, "--stateless-rpc --advertise-refs .")
|
90
|
+
response.finish
|
91
|
+
end
|
92
|
+
|
93
|
+
def run_process(service)
|
94
|
+
content_type_for_git service, :result
|
95
|
+
input = request.body.read
|
96
|
+
command = repository.cli(service, "--stateless-rpc #{git.repository}")
|
97
|
+
# This source has extracted from Grack written by Scott Chacon.
|
98
|
+
IO.popen(command, File::RDWR) do |pipe|
|
99
|
+
pipe.write(input)
|
100
|
+
while !pipe.eof?
|
101
|
+
block = pipe.read(8192) # 8M at a time
|
102
|
+
response.write block # steam it to the client
|
103
|
+
end
|
104
|
+
end # IO
|
105
|
+
response.finish
|
106
|
+
end
|
107
|
+
|
108
|
+
end # HttpBackendHelpers
|
109
|
+
|
110
|
+
# The Smart HTTP handler server. This is the main Web application which respond to following requests:
|
111
|
+
#
|
112
|
+
# <repo.git>/HEAD :: HEAD contents
|
113
|
+
# <repo.git>/info/refs :: Text file that contains references.
|
114
|
+
# <repo.git>/objects/info/* :: Text file that contains all list of packets, alternates or http-alternates.
|
115
|
+
# <repo.git>/objects/*/* :: Git objects, packets or indexes.
|
116
|
+
# <repo.git>/upload-pack :: Post an upload packets.
|
117
|
+
# <repo.git>/receive-pack :: Post a receive packets.
|
118
|
+
#
|
119
|
+
# See ::configure for more details.
|
120
|
+
class HttpBackend < Application
|
121
|
+
|
122
|
+
set :authenticate, true
|
123
|
+
set :get_any_file, true
|
124
|
+
set :upload_pack, true
|
125
|
+
set :receive_pack, false
|
126
|
+
|
127
|
+
helpers HttpBackendHelpers
|
128
|
+
|
129
|
+
before do
|
130
|
+
authenticate! if settings.authenticate
|
131
|
+
end
|
132
|
+
|
133
|
+
# implements the get_text_file function
|
134
|
+
get "/:repository/HEAD" do
|
135
|
+
read_text_file("HEAD")
|
136
|
+
end
|
137
|
+
|
138
|
+
# implements the get_info_refs function
|
139
|
+
get "/:repository/info/refs" do
|
140
|
+
if service_request? # by URL query parameters
|
141
|
+
run_advertisement service
|
142
|
+
else
|
143
|
+
read_text_file(:info, :refs)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# implements the get_text_file and get_info_packs functions
|
148
|
+
get %r{/(.*?)/objects/info/(packs|alternates|http-alternates)$} do |repository, file|
|
149
|
+
if file == "packs"
|
150
|
+
send_info_packs
|
151
|
+
else
|
152
|
+
read_text_file(:objects, :info, file)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# implements the get_loose_object function
|
157
|
+
get %r{/(.*?)/objects/([0-9a-f]{2})/([0-9a-f]{38})$} do |repository, prefix, suffix|
|
158
|
+
send_loose_object(prefix, suffix)
|
159
|
+
end
|
160
|
+
|
161
|
+
# implements the get_pack_file and get_idx_file functions
|
162
|
+
get %r{/(.*?)/objects/pack/(pack-[0-9a-f]{40}.(pack|idx))$} do |repository, pack, ext|
|
163
|
+
send_pack_idx_file(pack, ext == "idx")
|
164
|
+
end
|
165
|
+
|
166
|
+
# implements the service_rpc function
|
167
|
+
post "/:repository/:service" do
|
168
|
+
run_process service
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
helpers AuthenticationHelpers
|
174
|
+
|
175
|
+
end # HttpBackend
|
176
|
+
|
177
|
+
end # Git::Lighttp
|
178
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Git::Lighttp
|
2
|
+
|
3
|
+
class Treeish < Application
|
4
|
+
|
5
|
+
set :authenticate, false
|
6
|
+
|
7
|
+
helpers GitHelpers
|
8
|
+
|
9
|
+
before do
|
10
|
+
authenticate! if settings.authenticate
|
11
|
+
end
|
12
|
+
|
13
|
+
get %r{/(.*?)/(.*?/{0,1}.*)$} do |name, path|
|
14
|
+
content_type :json
|
15
|
+
path = path.split("/")
|
16
|
+
ref = path.shift
|
17
|
+
tree = repository.tree(ref, path.join("/"))
|
18
|
+
tree.to_json(:max_nesting => tree.size*6)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
helpers AuthenticationHelpers
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Git
|
2
|
+
|
3
|
+
# The objective of this class is to implement various ideas proposed by the
|
4
|
+
# Semantic Versioning Specification (see reference[http://semver.org/]).
|
5
|
+
module Lighttp #:nodoc:
|
6
|
+
|
7
|
+
VERSION = "0.3.0"
|
8
|
+
RELEASE = "2016-01-27"
|
9
|
+
TIMESTAMP = "2011-07-05 12:32:36 -04:00"
|
10
|
+
|
11
|
+
def self.info
|
12
|
+
"#{name} v#{VERSION} (#{RELEASE})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.to_h
|
16
|
+
{ :name => name,
|
17
|
+
:version => VERSION,
|
18
|
+
:semver => VERSION.to_semver_h,
|
19
|
+
:release => RELEASE,
|
20
|
+
:timestamp => TIMESTAMP }
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
data/test/all.rb
ADDED
data/test/config_test.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
describe 'Configuration' do
|
2
|
+
before do
|
3
|
+
@config = {
|
4
|
+
:default => {
|
5
|
+
:project_root => '/var/git/repos',
|
6
|
+
:git_path => '/usr/local/bin/git'
|
7
|
+
},
|
8
|
+
:treeish => {
|
9
|
+
:authenticate => true
|
10
|
+
},
|
11
|
+
:http_backend => {
|
12
|
+
:authenticate => true,
|
13
|
+
:get_any_file => true,
|
14
|
+
:upload_pack => true,
|
15
|
+
:receive_pack => false,
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'configure by application' do
|
21
|
+
Git::Lighttp.configure do |app|
|
22
|
+
app.default.project_root = '/var/git/repos'
|
23
|
+
app.default.git_path = '/usr/local/bin/git'
|
24
|
+
app.treeish.authenticate = true
|
25
|
+
end
|
26
|
+
|
27
|
+
@config.keys.each do |app|
|
28
|
+
@config[app].each do |option, value|
|
29
|
+
assert_equal value, Git::Lighttp.config[app][option]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'load from YAML file' do
|
35
|
+
yaml = YAML.load_file(fixtures('config.yml')).symbolize_keys
|
36
|
+
config = Git::Lighttp.load_config_file(fixtures('config.yml'))
|
37
|
+
yaml.keys.each do |app|
|
38
|
+
yaml[app].each do |option, value|
|
39
|
+
assert_equal value, config[app][option]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ref: refs/heads/master
|
@@ -0,0 +1 @@
|
|
1
|
+
Unnamed repository; edit this file 'description' to name the repository.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# An example hook script to check the commit log message taken by
|
4
|
+
# applypatch from an e-mail message.
|
5
|
+
#
|
6
|
+
# The hook should exit with non-zero status after issuing an
|
7
|
+
# appropriate message if it wants to stop the commit. The hook is
|
8
|
+
# allowed to edit the commit message file.
|
9
|
+
#
|
10
|
+
# To enable this hook, rename this file to "applypatch-msg".
|
11
|
+
|
12
|
+
. git-sh-setup
|
13
|
+
test -x "$GIT_DIR/hooks/commit-msg" &&
|
14
|
+
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
15
|
+
:
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#
|
3
|
+
# An example hook script to check the commit log message.
|
4
|
+
# Called by "git commit" with one argument, the name of the file
|
5
|
+
# that has the commit message. The hook should exit with non-zero
|
6
|
+
# status after issuing an appropriate message if it wants to stop the
|
7
|
+
# commit. The hook is allowed to edit the commit message file.
|
8
|
+
#
|
9
|
+
# To enable this hook, rename this file to "commit-msg".
|
10
|
+
|
11
|
+
# Uncomment the below to add a Signed-off-by line to the message.
|
12
|
+
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
13
|
+
# hook is more suited to it.
|
14
|
+
#
|
15
|
+
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
16
|
+
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
17
|
+
|
18
|
+
# This example catches duplicate Signed-off-by lines.
|
19
|
+
|
20
|
+
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
21
|
+
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
22
|
+
echo >&2 Duplicate Signed-off-by lines.
|
23
|
+
exit 1
|
24
|
+
}
|