git-lighttp 0.3.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/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
|
+
}
|