gitlab-grack 2.0.0.rc2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f620ff134c43ad395371b842da7bbeb812cf0434
4
- data.tar.gz: edb62816e986e9eb510d66d077bba78514c575f4
3
+ metadata.gz: fdf3609511e6c0a489c572a4add4f89af65bae4b
4
+ data.tar.gz: 8ad35782833f609c0dba191250746d5a2dfd20bc
5
5
  SHA512:
6
- metadata.gz: 662499bdfca3cb601ed8b64c14bde3160087f108e57062452610f47b1e34e873d4cb81131cc23be990e42ac2101f879015d06ee231fc87ace57b8eb8bf700505
7
- data.tar.gz: 49a2126c89906af736b3bb4d4f3243dd8efa5656405ca0010f36f5a597ce5be4759d5bfa5166984a81d985f88e96648637e6299e325d9a68b069f8ea22bfd5a9
6
+ metadata.gz: 4f506cdcc801556af8b5ab796d8ebf46585ef4025805307bc2cef094a653c5a3a2b3efa941bf61f48593def2696b99ba31c4a026e0195309d63e1ede3d70f0e9
7
+ data.tar.gz: 0c5bb7dd97d1d5f07e57d3642a0f27b3bdc3d65b00586780d0e23a435e16f2ff5fd2f2185b563070ad77cd0ff15e695471466498dc7868af04ba4416e4ebfbd1
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  coverage/
2
+ examples/*.git
2
3
  pkg/
3
4
  *.gem
data/CHANGELOG CHANGED
@@ -1,2 +1,10 @@
1
+ 2.0.0
2
+ - Use safer shell commands and avoid Dir.chdir
3
+ - Restrict the environment for shell commands
4
+ - Make Grack::Server thread-safe (zimbatm)
5
+ - Make sure child processes get reaped after popen
6
+ - Verify requested path is actually a Git directory (Ryan Canty)
7
+
8
+
1
9
  1.1.0
2
10
  - Modifies service_rpc to use chunked transfer (https://github.com/gitlabhq/grack/pull/1)
data/Gemfile CHANGED
@@ -4,5 +4,6 @@ gemspec
4
4
 
5
5
  group :development do
6
6
  gem 'rake'
7
+ gem 'pry'
7
8
  gem 'rack-test'
8
9
  end
@@ -1,19 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab-grack (2.0.0.pre)
4
+ gitlab-grack (2.0.0.rc2)
5
5
  rack (~> 1.5.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ coderay (1.1.0)
10
11
  metaclass (0.0.1)
12
+ method_source (0.8.2)
11
13
  mocha (0.14.0)
12
14
  metaclass (~> 0.0.1)
15
+ pry (0.10.1)
16
+ coderay (~> 1.1.0)
17
+ method_source (~> 0.8.1)
18
+ slop (~> 3.4)
13
19
  rack (1.5.2)
14
20
  rack-test (0.6.2)
15
21
  rack (>= 1.0)
16
22
  rake (10.1.0)
23
+ slop (3.6.0)
17
24
 
18
25
  PLATFORMS
19
26
  ruby
@@ -21,5 +28,6 @@ PLATFORMS
21
28
  DEPENDENCIES
22
29
  gitlab-grack!
23
30
  mocha (~> 0.11)
31
+ pry
24
32
  rack-test
25
33
  rake
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ end
19
19
  namespace :grack do
20
20
  desc "Start Grack"
21
21
  task :start do
22
- system "rackup config.ru -p 8080"
22
+ system('./bin/testserver')
23
23
  end
24
24
  end
25
25
 
@@ -0,0 +1,6 @@
1
+ #! /bin/bash
2
+
3
+ set -e
4
+
5
+ cd $(dirname "$0")/..
6
+ exec /usr/bin/env bundle exec pry -Ilib -r grack
@@ -0,0 +1,14 @@
1
+ #! /usr/bin/env ruby
2
+ libdir = File.absolute_path( File.join( File.dirname(__FILE__), '../lib' ) )
3
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
+
5
+ require 'grack'
6
+ require 'rack'
7
+ root = File.absolute_path( File.join( File.dirname(__FILE__), '../examples' ) )
8
+ app = Grack::Server.new({
9
+ project_root: root,
10
+ upload_pack: true,
11
+ receive_pack:true
12
+ })
13
+
14
+ Rack::Server.start app: app, Port: 3001
@@ -0,0 +1,69 @@
1
+ module Grack
2
+ class Git
3
+ attr_reader :repo
4
+
5
+ def initialize(git_path, repo_path)
6
+ @git_path = git_path
7
+ @repo = repo_path
8
+ end
9
+
10
+ def update_server_info
11
+ execute(%W(update-server-info))
12
+ end
13
+
14
+ def command(cmd)
15
+ [@git_path || 'git'] + cmd
16
+ end
17
+
18
+ def capture(cmd)
19
+ IO.popen(popen_env, cmd, popen_options).read
20
+ end
21
+
22
+ def execute(cmd)
23
+ cmd = command(cmd)
24
+ if block_given?
25
+ IO.popen(popen_env, cmd, File::RDWR, popen_options) do |pipe|
26
+ yield(pipe)
27
+ end
28
+ else
29
+ capture(cmd).chomp
30
+ end
31
+ end
32
+
33
+ def popen_options
34
+ { chdir: repo, unsetenv_others: true }
35
+ end
36
+
37
+ def popen_env
38
+ { 'PATH' => ENV['PATH'], 'GL_ID' => ENV['GL_ID'] }
39
+ end
40
+
41
+ def config_setting(service_name)
42
+ service_name = service_name.gsub('-', '')
43
+ setting = config("http.#{service_name}")
44
+
45
+ if service_name == 'uploadpack'
46
+ setting != 'false'
47
+ else
48
+ setting == 'true'
49
+ end
50
+ end
51
+
52
+ def config(config_name)
53
+ execute(%W(config #{config_name}))
54
+ end
55
+
56
+ def valid_repo?
57
+ return false unless File.exists?(repo) && File.realpath(repo) == repo
58
+
59
+ match = execute(%W(rev-parse --git-dir)).match(/\.$|\.git$/)
60
+
61
+ if match.to_s == '.git'
62
+ # Since the parent could be a git repo, we want to make sure the actual repo contains a git dir.
63
+ return false unless Dir.entries(repo).include?('.git')
64
+ end
65
+
66
+ match
67
+ end
68
+ end
69
+ end
@@ -4,8 +4,11 @@ require 'rack/response'
4
4
  require 'rack/utils'
5
5
  require 'time'
6
6
 
7
+ require 'grack/git'
8
+
7
9
  module Grack
8
10
  class Server
11
+ attr_reader :git
9
12
 
10
13
  SERVICES = [
11
14
  ["POST", 'service_rpc', "(.*?)/git-upload-pack$", 'upload-pack'],
@@ -45,12 +48,12 @@ module Grack
45
48
  cmd, path, @reqfile, @rpc = match_routing
46
49
 
47
50
  return render_method_not_allowed if cmd == 'not_allowed'
48
- return render_not_found if !cmd
51
+ return render_not_found unless cmd
49
52
 
50
- @dir = get_git_dir(path)
51
- return render_not_found if !@dir
53
+ @git = get_git(path)
54
+ return render_not_found unless git.valid_repo?
52
55
 
53
- self.method(cmd).call()
56
+ self.method(cmd).call
54
57
  end
55
58
 
56
59
  # ---------------------------------
@@ -64,7 +67,8 @@ module Grack
64
67
  CRLF = "\r\n"
65
68
 
66
69
  def service_rpc
67
- return render_no_access if !has_access(@rpc, true)
70
+ return render_no_access unless has_access?(@rpc, true)
71
+
68
72
  input = read_body
69
73
 
70
74
  @res = Rack::Response.new
@@ -74,14 +78,14 @@ module Grack
74
78
  @res["Cache-Control"] = "no-cache"
75
79
 
76
80
  @res.finish do
77
- command = git_command(%W(#{@rpc} --stateless-rpc #{@dir}))
78
- IO.popen(popen_env, command, File::RDWR, popen_options) do |pipe|
81
+ git.execute([@rpc, '--stateless-rpc', git.repo]) do |pipe|
79
82
  pipe.write(input)
80
83
  pipe.close_write
81
- while !pipe.eof?
82
- block = pipe.read(8192) # 8KB at a time
83
- @res.write encode_chunk(block) # stream it to the client
84
+
85
+ while block = pipe.read(8192) # 8KB at a time
86
+ @res.write encode_chunk(block) # stream it to the client
84
87
  end
88
+
85
89
  @res.write terminating_chunk
86
90
  end
87
91
  end
@@ -89,35 +93,33 @@ module Grack
89
93
 
90
94
  def encode_chunk(chunk)
91
95
  size_in_hex = chunk.size.to_s(16)
92
- [ size_in_hex, CRLF, chunk, CRLF ].join
96
+ [size_in_hex, CRLF, chunk, CRLF].join
93
97
  end
94
98
 
95
99
  def terminating_chunk
96
- [ 0, CRLF, CRLF ].join
100
+ [0, CRLF, CRLF].join
97
101
  end
98
102
 
99
103
  def get_info_refs
100
104
  service_name = get_service_type
105
+ return dumb_info_refs unless has_access?(service_name)
101
106
 
102
- if has_access(service_name)
103
- cmd = git_command(%W(#{service_name} --stateless-rpc --advertise-refs #{@dir}))
104
- refs = capture(cmd)
107
+ refs = git.execute([service_name, '--stateless-rpc', '--advertise-refs', git.repo])
105
108
 
106
- @res = Rack::Response.new
107
- @res.status = 200
108
- @res["Content-Type"] = "application/x-git-%s-advertisement" % service_name
109
- hdr_nocache
110
- @res.write(pkt_write("# service=git-#{service_name}\n"))
111
- @res.write(pkt_flush)
112
- @res.write(refs)
113
- @res.finish
114
- else
115
- dumb_info_refs
116
- end
109
+ @res = Rack::Response.new
110
+ @res.status = 200
111
+ @res["Content-Type"] = "application/x-git-%s-advertisement" % service_name
112
+ hdr_nocache
113
+
114
+ @res.write(pkt_write("# service=git-#{service_name}\n"))
115
+ @res.write(pkt_flush)
116
+ @res.write(refs)
117
+
118
+ @res.finish
117
119
  end
118
120
 
119
121
  def dumb_info_refs
120
- update_server_info
122
+ git.update_server_info
121
123
  send_file(@reqfile, "text/plain; charset=utf-8") do
122
124
  hdr_nocache
123
125
  end
@@ -158,38 +160,33 @@ module Grack
158
160
  # logic helping functions
159
161
  # ------------------------
160
162
 
161
- F = ::File
162
-
163
163
  # some of this borrowed from the Rack::File implementation
164
164
  def send_file(reqfile, content_type)
165
- reqfile = File.join(@dir, reqfile)
166
- return render_not_found if !F.exists?(reqfile)
165
+ reqfile = File.join(git.repo, reqfile)
166
+ return render_not_found unless File.exists?(reqfile)
167
167
 
168
- if reqfile == File.realpath(reqfile)
169
- # reqfile looks legit: no path traversal, no leading '|'
170
- else
171
- # reqfile does not look trustworthy; abort
172
- return render_not_found
173
- end
168
+ return render_not_found unless reqfile == File.realpath(reqfile)
169
+
170
+ # reqfile looks legit: no path traversal, no leading '|'
174
171
 
175
172
  @res = Rack::Response.new
176
173
  @res.status = 200
177
174
  @res["Content-Type"] = content_type
178
- @res["Last-Modified"] = F.mtime(reqfile).httpdate
175
+ @res["Last-Modified"] = File.mtime(reqfile).httpdate
179
176
 
180
177
  yield
181
178
 
182
- if size = F.size?(reqfile)
179
+ if size = File.size?(reqfile)
183
180
  @res["Content-Length"] = size.to_s
184
181
  @res.finish do
185
- F.open(reqfile, "rb") do |file|
182
+ File.open(reqfile, "rb") do |file|
186
183
  while part = file.read(8192)
187
184
  @res.write part
188
185
  end
189
186
  end
190
187
  end
191
188
  else
192
- body = [F.read(reqfile)]
189
+ body = [File.read(reqfile)]
193
190
  size = Rack::Utils.bytesize(body.first)
194
191
  @res["Content-Length"] = size
195
192
  @res.write body
@@ -197,21 +194,15 @@ module Grack
197
194
  end
198
195
  end
199
196
 
200
- def get_git_dir(path)
197
+ def get_git(path)
201
198
  root = @config[:project_root] || Dir.pwd
202
199
  path = File.join(root, path)
203
- if !File.exists?(path)
204
- false
205
- elsif File.realpath(path) != path # looks like path traversal
206
- false
207
- else
208
- path # TODO: check is a valid git directory
209
- end
200
+ Grack::Git.new(@config[:git_path], path)
210
201
  end
211
202
 
212
203
  def get_service_type
213
204
  service_type = @req.params['service']
214
- return false if !service_type
205
+ return false unless service_type
215
206
  return false if service_type[0, 4] != 'git-'
216
207
  service_type.gsub('git-', '')
217
208
  end
@@ -219,81 +210,54 @@ module Grack
219
210
  def match_routing
220
211
  cmd = nil
221
212
  path = nil
213
+
222
214
  SERVICES.each do |method, handler, match, rpc|
223
- if m = Regexp.new(match).match(@req.path_info)
224
- return ['not_allowed'] if method != @req.request_method
225
- cmd = handler
226
- path = m[1]
227
- file = @req.path_info.sub(path + '/', '')
228
- return [cmd, path, file, rpc]
229
- end
215
+ next unless m = Regexp.new(match).match(@req.path_info)
216
+
217
+ return ['not_allowed'] unless method == @req.request_method
218
+
219
+ cmd = handler
220
+ path = m[1]
221
+ file = @req.path_info.sub(path + '/', '')
222
+
223
+ return [cmd, path, file, rpc]
230
224
  end
231
- return nil
225
+
226
+ nil
232
227
  end
233
228
 
234
- def has_access(rpc, check_content_type = false)
229
+ def has_access?(rpc, check_content_type = false)
235
230
  if check_content_type
236
- return false if @req.content_type != "application/x-git-%s-request" % rpc
231
+ conten_type = "application/x-git-%s-request" % rpc
232
+ return false unless @req.content_type == conten_type
237
233
  end
238
- return false if !['upload-pack', 'receive-pack'].include? rpc
234
+
235
+ return false unless ['upload-pack', 'receive-pack'].include?(rpc)
236
+
239
237
  if rpc == 'receive-pack'
240
- return @config[:receive_pack] if @config.include? :receive_pack
238
+ return @config[:receive_pack] if @config.include?(:receive_pack)
241
239
  end
242
- if rpc == 'upload-pack'
243
- return @config[:upload_pack] if @config.include? :upload_pack
244
- end
245
- return get_config_setting(rpc)
246
- end
247
240
 
248
- def get_config_setting(service_name)
249
- service_name = service_name.gsub('-', '')
250
- setting = get_git_config("http.#{service_name}")
251
- if service_name == 'uploadpack'
252
- return setting != 'false'
253
- else
254
- return setting == 'true'
241
+ if rpc == 'upload-pack'
242
+ return @config[:upload_pack] if @config.include?(:upload_pack)
255
243
  end
256
- end
257
244
 
258
- def get_git_config(config_name)
259
- cmd = git_command(%W(config #{config_name}))
260
- capture(cmd).chomp
245
+ git.config_setting(rpc)
261
246
  end
262
247
 
263
248
  def read_body
264
249
  if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
265
- input = Zlib::GzipReader.new(@req.body).read
250
+ Zlib::GzipReader.new(@req.body).read
266
251
  else
267
- input = @req.body.read
252
+ @req.body.read
268
253
  end
269
254
  end
270
255
 
271
- def update_server_info
272
- cmd = git_command(%W(update-server-info))
273
- capture(cmd)
274
- end
275
-
276
- def git_command(command)
277
- [@config[:git_path] || 'git'] + command
278
- end
279
-
280
- def capture(command)
281
- IO.popen(popen_env, command, popen_options) { |p| p.read }
282
- end
283
-
284
- def popen_options
285
- {chdir: @dir, unsetenv_others: true}
286
- end
287
-
288
- def popen_env
289
- {'PATH' => ENV['PATH'], 'GL_ID' => ENV['GL_ID']}
290
- end
291
-
292
256
  # --------------------------------------
293
257
  # HTTP error response handling functions
294
258
  # --------------------------------------
295
259
 
296
- PLAIN_TYPE = {"Content-Type" => "text/plain"}
260
+ PLAIN_TYPE = { "Content-Type" => "text/plain" }
297
261
 
298
262
  def render_method_not_allowed
299
263
  if @env['SERVER_PROTOCOL'] == "HTTP/1.1"
@@ -321,10 +285,9 @@ module Grack
321
285
  end
322
286
 
323
287
  def pkt_write(str)
324
- (str.size + 4).to_s(base=16).rjust(4, '0') + str
288
+ (str.size + 4).to_s(16).rjust(4, '0') + str
325
289
  end
326
290
 
327
-
328
291
  # ------------------------
329
292
  # header writing functions
330
293
  # ------------------------
@@ -341,6 +304,5 @@ module Grack
341
304
  @res["Expires"] = (now + 31536000).to_s;
342
305
  @res["Cache-Control"] = "public, max-age=31536000";
343
306
  end
344
-
345
307
  end
346
308
  end
@@ -1,3 +1,3 @@
1
1
  module Grack
2
- VERSION = "2.0.0.rc2"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -5,6 +5,7 @@ require 'mocha'
5
5
  require 'digest/sha1'
6
6
 
7
7
  require_relative '../lib/grack/server.rb'
8
+ require_relative '../lib/grack/git.rb'
8
9
  require 'pp'
9
10
 
10
11
  class GitHttpTest < Test::Unit::TestCase
@@ -52,11 +53,13 @@ class GitHttpTest < Test::Unit::TestCase
52
53
  end
53
54
 
54
55
  def test_no_access_wrong_path_rcp
56
+ Grack::Git.any_instance.stubs(:valid_repo?).returns(false)
55
57
  post "/example-wrong/git-upload-pack"
56
58
  assert_equal 404, r.status
57
59
  end
58
60
 
59
61
  def test_upload_pack_rpc
62
+ Grack::Git.any_instance.stubs(:valid_repo?).returns(true)
60
63
  IO.stubs(:popen).returns(MockProcess.new)
61
64
  post "/example/git-upload-pack", {}, {"CONTENT_TYPE" => "application/x-git-upload-pack-request"}
62
65
  assert_equal 200, r.status
@@ -74,6 +77,7 @@ class GitHttpTest < Test::Unit::TestCase
74
77
  end
75
78
 
76
79
  def test_recieve_pack_rpc
80
+ Grack::Git.any_instance.stubs(:valid_repo?).returns(true)
77
81
  IO.stubs(:popen).yields(MockProcess.new)
78
82
  post "/example/git-receive-pack", {}, {"CONTENT_TYPE" => "application/x-git-receive-pack-request"}
79
83
  assert_equal 200, r.status
@@ -151,52 +155,56 @@ class GitHttpTest < Test::Unit::TestCase
151
155
 
152
156
  def test_git_config_receive_pack
153
157
  app1 = Grack::Server.new({:project_root => example})
158
+ app1.instance_variable_set(:@git, Grack::Git.new('git', example ))
154
159
  session = Rack::Test::Session.new(app1)
155
-
156
- app1.stubs(:get_git_config).with('http.receivepack').returns('')
160
+ git = Grack::Git
161
+ git.any_instance.stubs(:config).with('http.receivepack').returns('')
157
162
  session.get "/example/info/refs?service=git-receive-pack"
158
163
  assert_equal 404, session.last_response.status
159
164
 
160
- app1.stubs(:get_git_config).with('http.receivepack').returns('true')
165
+ git.any_instance.stubs(:config).with('http.receivepack').returns('true')
161
166
  session.get "/example/info/refs?service=git-receive-pack"
162
167
  assert_equal 200, session.last_response.status
163
168
 
164
- app1.stubs(:get_git_config).with('http.receivepack').returns('false')
169
+ git.any_instance.stubs(:config).with('http.receivepack').returns('false')
165
170
  session.get "/example/info/refs?service=git-receive-pack"
166
171
  assert_equal 404, session.last_response.status
167
172
  end
168
173
 
169
174
  def test_git_config_upload_pack
170
175
  app1 = Grack::Server.new({:project_root => example})
176
+ # app1.instance_variable_set(:@git, Grack::Git.new('git', example ))
171
177
  session = Rack::Test::Session.new(app1)
172
-
173
- app1.stubs(:get_git_config).with('http.uploadpack').returns('')
178
+ git = Grack::Git
179
+ git.any_instance.stubs(:config).with('http.uploadpack').returns('')
174
180
  session.get "/example/info/refs?service=git-upload-pack"
175
181
  assert_equal 200, session.last_response.status
176
182
 
177
- app1.stubs(:get_git_config).with('http.uploadpack').returns('true')
183
+ git.any_instance.stubs(:config).with('http.uploadpack').returns('true')
178
184
  session.get "/example/info/refs?service=git-upload-pack"
179
185
  assert_equal 200, session.last_response.status
180
186
 
181
- app1.stubs(:get_git_config).with('http.uploadpack').returns('false')
187
+ git.any_instance.stubs(:config).with('http.uploadpack').returns('false')
182
188
  session.get "/example/info/refs?service=git-upload-pack"
183
189
  assert_equal 404, session.last_response.status
184
190
  end
185
191
 
186
192
  def test_send_file
187
193
  app1 = app
188
- app1.instance_variable_set(:@dir, Dir.pwd)
194
+ app1.instance_variable_set(:@git, Grack::Git.new('git', Dir.pwd))
189
195
  # Reject path traversal
190
196
  assert_equal 404, app1.send_file('tests/../tests', 'text/plain').first
191
197
  # Reject paths starting with '|', avoid File.read('|touch /tmp/pawned; ls /tmp')
192
198
  assert_equal 404, app1.send_file('|tests', 'text/plain').first
193
199
  end
194
200
 
195
- def test_get_git_dir
201
+ def test_get_git
196
202
  # Guard against non-existent directories
197
- assert_equal false, app.get_git_dir('foobar')
203
+ git1 = Grack::Git.new('git', 'foobar')
204
+ assert_equal false, git1.valid_repo?
198
205
  # Guard against path traversal
199
- assert_equal false, app.get_git_dir('/../tests')
206
+ git2 = Grack::Git.new('git', '/../tests')
207
+ assert_equal false, git2.valid_repo?
200
208
  end
201
209
 
202
210
  private
@@ -241,7 +249,8 @@ class MockProcess
241
249
  def write(data)
242
250
  end
243
251
 
244
- def read(data)
252
+ def read(data = nil)
253
+ ''
245
254
  end
246
255
 
247
256
  def eof?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-grack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.rc2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-23 00:00:00.000000000 Z
11
+ date: 2015-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -41,7 +41,9 @@ dependencies:
41
41
  description: Ruby/Rack Git Smart-HTTP Server Handler
42
42
  email:
43
43
  - schacon@gmail.com
44
- executables: []
44
+ executables:
45
+ - console
46
+ - testserver
45
47
  extensions: []
46
48
  extra_rdoc_files: []
47
49
  files:
@@ -53,12 +55,15 @@ files:
53
55
  - Gemfile.lock
54
56
  - README.md
55
57
  - Rakefile
58
+ - bin/console
59
+ - bin/testserver
56
60
  - examples/dispatch.fcgi
57
61
  - grack.gemspec
58
62
  - install.txt
59
63
  - lib/grack.rb
60
64
  - lib/grack/auth.rb
61
65
  - lib/grack/bundle.rb
66
+ - lib/grack/git.rb
62
67
  - lib/grack/server.rb
63
68
  - lib/grack/version.rb
64
69
  - tests/main_test.rb
@@ -76,9 +81,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
81
  version: '0'
77
82
  required_rubygems_version: !ruby/object:Gem::Requirement
78
83
  requirements:
79
- - - ">"
84
+ - - ">="
80
85
  - !ruby/object:Gem::Version
81
- version: 1.3.1
86
+ version: '0'
82
87
  requirements: []
83
88
  rubyforge_project:
84
89
  rubygems_version: 2.2.2
@@ -87,3 +92,4 @@ specification_version: 4
87
92
  summary: Ruby/Rack Git Smart-HTTP Server Handler
88
93
  test_files:
89
94
  - tests/main_test.rb
95
+ has_rdoc: