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.
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
+ }