znz-gitpub 0.1.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 (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
+