git-lighttp 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +11 -0
  3. data/Makefile +41 -0
  4. data/README.pt-BR.rdoc +81 -0
  5. data/README.rdoc +75 -0
  6. data/Rakefile +50 -0
  7. data/doc/releases/v0.1.0.rdoc +11 -0
  8. data/doc/releases/v0.2.0.rdoc +24 -0
  9. data/doc/releases/v0.3.0.rdoc +13 -0
  10. data/git-lighttp.gemspec +25 -0
  11. data/lib/git/lighttp.rb +365 -0
  12. data/lib/git/lighttp/extensions.rb +73 -0
  13. data/lib/git/lighttp/http_backend.rb +178 -0
  14. data/lib/git/lighttp/treeish.rb +28 -0
  15. data/lib/git/lighttp/version.rb +26 -0
  16. data/test/all.rb +12 -0
  17. data/test/config_test.rb +43 -0
  18. data/test/fixtures/config.yml +13 -0
  19. data/test/fixtures/htgroup +3 -0
  20. data/test/fixtures/htpasswd +4 -0
  21. data/test/fixtures/mycode.git/HEAD +1 -0
  22. data/test/fixtures/mycode.git/config +4 -0
  23. data/test/fixtures/mycode.git/description +1 -0
  24. data/test/fixtures/mycode.git/hooks/applypatch-msg.sample +15 -0
  25. data/test/fixtures/mycode.git/hooks/commit-msg.sample +24 -0
  26. data/test/fixtures/mycode.git/hooks/post-commit.sample +8 -0
  27. data/test/fixtures/mycode.git/hooks/post-receive.sample +15 -0
  28. data/test/fixtures/mycode.git/hooks/post-update.sample +8 -0
  29. data/test/fixtures/mycode.git/hooks/pre-applypatch.sample +14 -0
  30. data/test/fixtures/mycode.git/hooks/pre-commit.sample +46 -0
  31. data/test/fixtures/mycode.git/hooks/pre-rebase.sample +169 -0
  32. data/test/fixtures/mycode.git/hooks/prepare-commit-msg.sample +36 -0
  33. data/test/fixtures/mycode.git/hooks/update.sample +128 -0
  34. data/test/fixtures/mycode.git/info/exclude +6 -0
  35. data/test/fixtures/mycode.git/info/refs +3 -0
  36. data/test/fixtures/mycode.git/objects/02/83eb96425444e17b97182e1ba9f216cc67c132 +0 -0
  37. data/test/fixtures/mycode.git/objects/03/9927042df267a1bc606fc4485b7a79b6a9e3cd +1 -0
  38. data/test/fixtures/mycode.git/objects/0d/eed0a56fa8f5f2a788d58b3ea235afd547b828 +2 -0
  39. data/test/fixtures/mycode.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
  40. data/test/fixtures/mycode.git/objects/5e/54a0767e0c380f3baab17938d68c7f464cf171 +1 -0
  41. data/test/fixtures/mycode.git/objects/63/9b96262e7e19ca2169575e797b234098b8a72e +0 -0
  42. data/test/fixtures/mycode.git/objects/71/6e9568eed27d5ee4378b3ecf6dd095a547bde9 +1 -0
  43. data/test/fixtures/mycode.git/objects/b6/f3f0fabeaaaaf2db22b8ef98f59115baec7ef9 +0 -0
  44. data/test/fixtures/mycode.git/objects/be/118435b9d908fd4a689cd8b0cc98059911a31a +0 -0
  45. data/test/fixtures/mycode.git/objects/db/aefcb5bde664671c73b99515c386dcbc7f22b6 +0 -0
  46. data/test/fixtures/mycode.git/objects/eb/669b878d2013ac70aa5dee75e6357ea81d16ea +0 -0
  47. data/test/fixtures/mycode.git/objects/ed/10cfcf72862e140c97fe899cba2a55f4cb4c20 +0 -0
  48. data/test/fixtures/mycode.git/objects/ed/1c3a255ab3fce056dc31cd82df9f61a4d9fa22 +0 -0
  49. data/test/fixtures/mycode.git/objects/info/alternates +0 -0
  50. data/test/fixtures/mycode.git/objects/info/http-alternates +0 -0
  51. data/test/fixtures/mycode.git/objects/info/packs +2 -0
  52. data/test/fixtures/mycode.git/objects/pack/pack-40a8636b62258fffd78ec1e8d254116e72d385a9.idx +0 -0
  53. data/test/fixtures/mycode.git/objects/pack/pack-40a8636b62258fffd78ec1e8d254116e72d385a9.pack +0 -0
  54. data/test/fixtures/mycode.git/packed-refs +4 -0
  55. data/test/fixtures/mycode.git/refs/heads/master +1 -0
  56. data/test/fixtures/mycode.git/refs/tags/v0.1.0 +1 -0
  57. data/test/helpers.rb +53 -0
  58. data/test/htgroup_test.rb +29 -0
  59. data/test/htpasswd_test.rb +63 -0
  60. data/test/http_backend_authentication_test.rb +61 -0
  61. data/test/http_backend_test.rb +123 -0
  62. data/test/project_handler_test.rb +45 -0
  63. data/test/treeish_test.rb +33 -0
  64. 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
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift 'lib'
2
+ $LOAD_PATH.unshift '.'
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/rg'
6
+ require 'test/helpers'
7
+ require 'rack/test'
8
+ require 'git/lighttp'
9
+
10
+ Dir['test/*_test.rb'].each do |test|
11
+ load test
12
+ end
@@ -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,13 @@
1
+ default:
2
+ project_root: /var/git/repos
3
+ git_path : /usr/bin/git
4
+
5
+ treeish:
6
+ authenticate: true
7
+
8
+ http_backend:
9
+ authenticate: true
10
+ get_any_file: true
11
+ upload_pack : true
12
+ receive_pack: false
13
+
@@ -0,0 +1,3 @@
1
+ prophets: malachi jeremiah
2
+ writers: matthew mark luke john
3
+ disciples: john peter
@@ -0,0 +1,4 @@
1
+ john:BInD5.JEyr5Ng
2
+ luke:1y687odVzuFJs
3
+ mark:V5.e7XhcXHmQc
4
+ matthew:zKOzsdCzE.mEE
@@ -0,0 +1 @@
1
+ ref: refs/heads/master
@@ -0,0 +1,4 @@
1
+ [core]
2
+ repositoryformatversion = 0
3
+ filemode = true
4
+ bare = true
@@ -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
+ }