znz-gitpub 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +5 -0
  2. data/gitpub.rb +219 -0
  3. data/gitpub_spec.rb +208 -0
  4. data/linux-2.6.ru +3 -0
  5. metadata +63 -0
data/README ADDED
@@ -0,0 +1,5 @@
1
+ GitPub
2
+
3
+ Web application to publish static files based on git and rack.
4
+
5
+ Pub means 'publish' and supporting only static files like publicfile of DJB.
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/ruby
2
+ # -*- coding: utf-8 -*-
3
+ # = gitpub.rb
4
+ #
5
+ # Web application to publish static files based on git and rack.
6
+ #
7
+ # Pub means 'publish' and supporting only static files like publicfile of DJB.
8
+ #
9
+ # == Example Usage
10
+ #
11
+ # execute some commands:
12
+ # gem install rack
13
+ # git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
14
+ # rackup -d linux-2.6.ru
15
+ # and open http://localhost:9292/ with your browser.
16
+ #
17
+ # linux-2.6.ru is:
18
+ # require 'gitpub'
19
+ # run GitPub.new('linux-2.6/.git', /\Av2\.6\.\d+\z/)
20
+ #
21
+ #
22
+ # == License (The MIT License)
23
+ #
24
+ # Copyright (c) 2009 Kazuhiro NISHIYAMA
25
+ #
26
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
27
+ # of this software and associated documentation files (the "Software"), to deal
28
+ # in the Software without restriction, including without limitation the rights
29
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30
+ # copies of the Software, and to permit persons to whom the Software is
31
+ # furnished to do so, subject to the following conditions:
32
+ #
33
+ # The above copyright notice and this permission notice shall be included in
34
+ # all copies or substantial portions of the Software.
35
+ #
36
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
42
+ # THE SOFTWARE.
43
+ #
44
+
45
+
46
+ require 'erb'
47
+ require 'rack/mime'
48
+
49
+ class GitPub
50
+ include ERB::Util
51
+
52
+ # path to git command
53
+ GIT_BIN = "git"
54
+
55
+ # Create GitPub instance.
56
+ #
57
+ # git_dir :: path to .git or git bare repository.
58
+ # pub_tag_regexp :: Regexp of publish tag.
59
+ # Recommends use \A and \z instead of ^ and $.
60
+ # title :: HTML title prefix. Default is repository's directory name.
61
+ def initialize(git_dir, pub_tag_regexp=/\A([^\/:]+)\z/, title=nil)
62
+ @git_dir = git_dir
63
+ @pub_tag_regexp = pub_tag_regexp
64
+ unless title
65
+ title = File.basename(git_dir)
66
+ if title == ".git"
67
+ title = File.basename(File.dirname(git_dir))
68
+ end
69
+ end
70
+ @title = title
71
+ end
72
+
73
+ # Wrapper of git command.
74
+ def git(*args)
75
+ IO.popen("-", "r") do |io|
76
+ if io
77
+ yield io
78
+ else
79
+ #STDERR.puts [GIT_BIN, "--git-dir", @git_dir, *args].inspect
80
+ exec(GIT_BIN, "--git-dir", @git_dir, *args)
81
+ end
82
+ end
83
+ end
84
+ private :git
85
+
86
+ # List public tags.
87
+ def list_pub_tag(env, res)
88
+ tags = []
89
+ git("tag") do |io|
90
+ io.each_line do |line|
91
+ line.chomp!
92
+ if @pub_tag_regexp =~ line
93
+ tags << $&
94
+ end
95
+ end
96
+ end
97
+ tags.sort!
98
+ E_list_pub_tag.result(binding)
99
+ end
100
+
101
+ # ERB template of list_pub_tag.
102
+ E_list_pub_tag = ERB.new(<<-ERUBY, 1, '-')
103
+ <html>
104
+ <head>
105
+ <title><%=h @title %></title>
106
+ </head>
107
+ <body>
108
+ <h1><%=h @title %></h1>
109
+ <ul>
110
+ <%- tags.each do |tag| -%>
111
+ <li><a href="<%=h tag %>/"><%=h tag %></a></li>
112
+ <%- end -%>
113
+ </ul>
114
+ </body>
115
+ </html>
116
+ ERUBY
117
+
118
+ # List files in the blob (tag or directory)
119
+ def list_files(env, res, tag, path)
120
+ files = []
121
+ git("ls-tree", "-z", "#{tag}:#{path}") do |io|
122
+ io.each_line("\0") do |line|
123
+ line.chomp!("\0")
124
+ #STDERR.puts line
125
+ info, file = line.split(/\t/, 2)
126
+ mode, type, object = info.split(/ /)
127
+ files << {
128
+ :mode => mode,
129
+ :type => type,
130
+ :object => object,
131
+ :file => file,
132
+ }
133
+ end
134
+ end
135
+ files = files.sort_by{|h| h[:file] }
136
+ E_list_files.result(binding)
137
+ end
138
+
139
+ # ERB template of list_files.
140
+ E_list_files = ERB.new(<<-ERUBY, 1, '-')
141
+ <html>
142
+ <head>
143
+ <title><%=h @title %> <%=h tag %>:<%=h path %></title>
144
+ </head>
145
+ <body>
146
+ <h1><%=h @title %> <%=h tag %>:<%=h path %></h1>
147
+ <ol>
148
+ <li><a href="..">..</a></li>
149
+ <%- files.each do |h| -%>
150
+ <%- case h[:type] -%>
151
+ <%- when "tree" -%>
152
+ <li><a href="<%=h h[:file] %>/"><%=h h[:file] %>/</a></li>
153
+ <%- when "blob" -%>
154
+ <li><a href="<%=h h[:file] %>"><%=h h[:file] %></a></li>
155
+ <%- else -%>
156
+ <li><%=h h[:file] %></li>
157
+ <%- end -%>
158
+ <%- end -%>
159
+ </ol>
160
+ </body>
161
+ </html>
162
+ ERUBY
163
+
164
+ # Show file content of object.
165
+ # Content-Type choose by Rack::Mime.mime_type of file extension.
166
+ # Default is text/plain.
167
+ def show_file(env, res, tag, path)
168
+ body = "(empty)"
169
+ git("show", "#{tag}:#{path}") do |io|
170
+ body = io.read
171
+ end
172
+ mime_type = Rack::Mime.mime_type(File.extname(path), "text/plain")
173
+ if mime_type != "text/plain"
174
+ res.write body
175
+ res['Content-Type'] = mime_type
176
+ res['Content-Length'] = (body.bytesize rescue body.size).to_s
177
+ return
178
+ end
179
+ E_show_file.result(binding)
180
+ end
181
+
182
+ # ERB template of show_file.
183
+ E_show_file = ERB.new(<<-ERUBY, 1, '-')
184
+ <html>
185
+ <head>
186
+ <title><%=h @title %> <%=h tag %>:<%=h path %></title>
187
+ </head>
188
+ <body>
189
+ <h1><%=h @title %> <%=h tag %>:<%=h path %></h1>
190
+ <p><a href=".">back</a><p>
191
+ <pre><%=h body %></pre>
192
+ </body>
193
+ </html>
194
+ ERUBY
195
+
196
+ # Rack interface.
197
+ def call(env)
198
+ res = Rack::Response.new
199
+ body = nil
200
+ if /\A\/([^\/:]+)\// =~ env['PATH_INFO']
201
+ tag, rest = $1, $'
202
+ end
203
+ if tag && @pub_tag_regexp =~ tag
204
+ if /\/\z/ =~ env['PATH_INFO']
205
+ body = list_files(env, res, tag, rest)
206
+ else
207
+ body = show_file(env, res, tag, rest)
208
+ end
209
+ else
210
+ body = list_pub_tag(env, res)
211
+ end
212
+ if body
213
+ res.write body
214
+ res['Content-Type'] = 'text/html; charset=utf-8'
215
+ res['Content-Length'] = (body.bytesize rescue body.size).to_s
216
+ end
217
+ res.finish
218
+ end
219
+ end
@@ -0,0 +1,208 @@
1
+ # -*- coding: utf-8 -*-
2
+ #!/usr/bin/env spec
3
+ require "gitpub"
4
+ require "rack"
5
+ require "tempfile"
6
+
7
+ describe GitPub, "with linux-2.6" do
8
+ before do
9
+ @git_dir = File.expand_path("linux-2.6/.git")
10
+ end
11
+
12
+ it "list all tags without PATH_INFO" do
13
+ gitpub = GitPub.new(@git_dir)
14
+ code, headers, res = gitpub.call({'PATH_INFO' => nil})
15
+ code.should == 200
16
+ headers["Content-Type"].should == "text/html; charset=utf-8"
17
+ headers["Content-Length"].should be_instance_of String
18
+ headers["Content-Length"].to_i.should > 0
19
+ body = res.body.to_s
20
+ /\.\./.should_not =~ body
21
+ /<pre>/.should_not =~ body
22
+ end
23
+
24
+ it "list all tags with PATH_INFO=/" do
25
+ gitpub = GitPub.new(@git_dir)
26
+ code, headers, res0 = gitpub.call({'PATH_INFO' => nil})
27
+ code, headers, res1 = gitpub.call({'PATH_INFO' => "/"})
28
+ code.should == 200
29
+ headers["Content-Type"].should == "text/html; charset=utf-8"
30
+ headers["Content-Length"].should be_instance_of String
31
+ headers["Content-Length"].to_i.should > 0
32
+ res0.body.should == res1.body
33
+ body = res1.body.to_s
34
+ /\.\./.should_not =~ body
35
+ /<pre>/.should_not =~ body
36
+ end
37
+
38
+ it "list all tags with PATH_INFO=/invalid-tag" do
39
+ gitpub = GitPub.new(@git_dir)
40
+ code, headers, res0 = gitpub.call({'PATH_INFO' => nil})
41
+ code, headers, res1 = gitpub.call({'PATH_INFO' => "/invalid-tag"})
42
+ code.should == 200
43
+ headers["Content-Type"].should == "text/html; charset=utf-8"
44
+ headers["Content-Length"].should be_instance_of String
45
+ headers["Content-Length"].to_i.should > 0
46
+ res0.body.should == res1.body
47
+ body = res1.body.to_s
48
+ /\.\./.should_not =~ body
49
+ /<pre>/.should_not =~ body
50
+ end
51
+
52
+ it "list no files with PATH_INFO=/invalid-tag/" do
53
+ gitpub = GitPub.new(@git_dir)
54
+ code, headers, res0 = gitpub.call({'PATH_INFO' => nil})
55
+
56
+ temp_stderr = Tempfile.new("stderr")
57
+ orig_stderr = STDERR.dup
58
+ STDERR.reopen(temp_stderr)
59
+ code, headers, res1 = gitpub.call({'PATH_INFO' => "/invalid-tag/"})
60
+ STDERR.reopen(orig_stderr)
61
+ temp_stderr.rewind
62
+ error = temp_stderr.read
63
+ temp_stderr.close
64
+ error.should == "fatal: Not a valid object name invalid-tag:\n"
65
+
66
+ code.should == 200
67
+ headers["Content-Type"].should == "text/html; charset=utf-8"
68
+ headers["Content-Length"].should be_instance_of String
69
+ headers["Content-Length"].to_i.should > 0
70
+ res0.body.should_not == res1.body
71
+ body = res1.body.to_s
72
+ /\.\./.should =~ body
73
+ /<pre>/.should_not =~ body
74
+ end
75
+
76
+ it "list pub tags only without PATH_INFO" do
77
+ gitpub = GitPub.new(@git_dir)
78
+ code, headers, res = gitpub.call({'PATH_INFO' => nil})
79
+ all_tags_length = headers["Content-Length"].to_i
80
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
81
+ code, headers, res = gitpub.call({'PATH_INFO' => nil})
82
+ code.should == 200
83
+ headers["Content-Type"].should == "text/html; charset=utf-8"
84
+ headers["Content-Length"].should be_instance_of String
85
+ headers["Content-Length"].to_i.should > 0
86
+ headers["Content-Length"].to_i.should < all_tags_length
87
+ body = res.body.to_s
88
+ /\.\./.should_not =~ body
89
+ /<pre>/.should_not =~ body
90
+ end
91
+
92
+ it "list pub tags only with PATH_INFO=/" do
93
+ gitpub = GitPub.new(@git_dir)
94
+ code, headers, res = gitpub.call({'PATH_INFO' => "/"})
95
+ all_tags_length = headers["Content-Length"].to_i
96
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
97
+ code, headers, res = gitpub.call({'PATH_INFO' => "/"})
98
+ code.should == 200
99
+ headers["Content-Type"].should == "text/html; charset=utf-8"
100
+ headers["Content-Length"].should be_instance_of String
101
+ headers["Content-Length"].to_i.should > 0
102
+ headers["Content-Length"].to_i.should < all_tags_length
103
+ body = res.body.to_s
104
+ /\.\./.should_not =~ body
105
+ /<pre>/.should_not =~ body
106
+ end
107
+
108
+ it "list pub tags with PATH_INFO=/invalid-tag/" do
109
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
110
+ code, headers, res0 = gitpub.call({'PATH_INFO' => nil})
111
+ code, headers, res1 = gitpub.call({'PATH_INFO' => "/invalid-tag/"})
112
+ code.should == 200
113
+ headers["Content-Type"].should == "text/html; charset=utf-8"
114
+ headers["Content-Length"].should be_instance_of String
115
+ headers["Content-Length"].to_i.should > 0
116
+ res0.body.should == res1.body
117
+ body = res1.body.to_s
118
+ /\.\./.should_not =~ body
119
+ /<pre>/.should_not =~ body
120
+ end
121
+
122
+ it "list no files with PATH_INFO=/v2.6.10/" do
123
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
124
+
125
+ temp_stderr = Tempfile.new("stderr")
126
+ orig_stderr = STDERR.dup
127
+ STDERR.reopen(temp_stderr)
128
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.10/"})
129
+ STDERR.reopen(orig_stderr)
130
+ temp_stderr.rewind
131
+ error = temp_stderr.read
132
+ temp_stderr.close
133
+ error.should == "fatal: Not a valid object name v2.6.10:\n"
134
+
135
+ code.should == 200
136
+ headers["Content-Type"].should == "text/html; charset=utf-8"
137
+ headers["Content-Length"].should be_instance_of String
138
+ headers["Content-Length"].to_i.should > 0
139
+ res.body.to_s.scan(/<li>/).size.should == 1
140
+ body = res.body.to_s
141
+ /\.\./.should =~ body
142
+ /<pre>/.should_not =~ body
143
+ end
144
+
145
+ it "list files with PATH_INFO=/v2.6.11/" do
146
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
147
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.11/"})
148
+ code.should == 200
149
+ headers["Content-Type"].should == "text/html; charset=utf-8"
150
+ headers["Content-Length"].should be_instance_of String
151
+ headers["Content-Length"].to_i.should > 0
152
+ body = res.body.to_s
153
+ body.scan(/<li>/).size.should > 1
154
+ /\.\./.should =~ body
155
+ /<pre>/.should_not =~ body
156
+ end
157
+
158
+ it "list files with PATH_INFO=/v2.6.11/" do
159
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
160
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.11/"})
161
+ code.should == 200
162
+ headers["Content-Type"].should == "text/html; charset=utf-8"
163
+ headers["Content-Length"].should be_instance_of String
164
+ headers["Content-Length"].to_i.should > 0
165
+ body = res.body.to_s
166
+ body.scan(/<li>/).size.should > 1
167
+ /\.\./.should =~ body
168
+ /<pre>/.should_not =~ body
169
+ end
170
+
171
+ it "show file with PATH_INFO=/v2.6.11/COPYING" do
172
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
173
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.11/COPYING"})
174
+ code.should == 200
175
+ headers["Content-Type"].should == "text/html; charset=utf-8"
176
+ headers["Content-Length"].should be_instance_of String
177
+ headers["Content-Length"].to_i.should > 0
178
+ body = res.body.to_s
179
+ /<pre>/.should =~ body
180
+ /GNU GENERAL PUBLIC LICENSE/.should =~ body
181
+ end
182
+
183
+ it "list files with PATH_INFO=/v2.6.11/Documentation/" do
184
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
185
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.11/Documentation/"})
186
+ code.should == 200
187
+ headers["Content-Type"].should == "text/html; charset=utf-8"
188
+ headers["Content-Length"].should be_instance_of String
189
+ headers["Content-Length"].to_i.should > 0
190
+ body = res.body.to_s
191
+ body.scan(/<li>/).size.should > 1
192
+ /\.\./.should =~ body
193
+ /<pre>/.should_not =~ body
194
+ end
195
+
196
+ it "show file with PATH_INFO=/v2.6.11/Documentation/CodingStyle" do
197
+ gitpub = GitPub.new(@git_dir, /\Av2\.6\.\d+\z/)
198
+ code, headers, res = gitpub.call({'PATH_INFO' => "/v2.6.11/Documentation/CodingStyle"})
199
+ code.should == 200
200
+ headers["Content-Type"].should == "text/html; charset=utf-8"
201
+ headers["Content-Length"].should be_instance_of String
202
+ headers["Content-Length"].to_i.should > 0
203
+ body = res.body.to_s
204
+ /<pre>/.should =~ body
205
+ /Linux kernel coding style/.should =~ body
206
+ end
207
+
208
+ end
@@ -0,0 +1,3 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+ require 'gitpub'
3
+ run GitPub.new('linux-2.6/.git', /\Av2\.6\.\d+\z/)
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: znz-gitpub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kazuhiro NISHIYAMA
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-30 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">"
21
+ - !ruby/object:Gem::Version
22
+ version: 0.9.0
23
+ version:
24
+ description:
25
+ email: zn@mbf.nifty.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README
34
+ - gitpub.rb
35
+ - gitpub_spec.rb
36
+ - linux-2.6.ru
37
+ has_rdoc: true
38
+ homepage: http://github.com/znz/gitpub
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths: .
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: web application to publish static files based on git and rack
62
+ test_files: []
63
+