2 # gitweb snapshot.cgi - generate snapshots of git repositories
3 # Copyright (C) 2005-2006 Anders Gustafsson, Sham Chukoury
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
21 # where the git tools are located
22 git_bin_dir
= "/usr/bin"
24 # where the git repositories are located
25 git_base_dir
= "/xmms2"
27 # where to put snapshots
28 snapshot_dir
= "/tmp/snapshots"
30 # where to send users if an invalid snapshot is requested
31 snapshots_url
= "http://git.xmms.se/"
33 # Note: in the context of this script, a 'tree' is a Git repository,
37 print "Location: %s\n" % url
40 print "Content-Type: text/plain\n"
44 def __init__(self
, git_dir
, treename
, commitID
):
45 self
.git_dir
= git_dir
46 self
.treename
= treename
47 self
.dir = os
.path
.join(snapshot_dir
, treename
)
49 if (commitID
== "HEAD"):
50 self
.commitID
= file("%s/HEAD" % git_dir
).read().rstrip()
51 redirect("%s/%s-snapshot-%s.tar.bz2" % (
52 os
.environ
["SCRIPT_NAME"], treename
, self
.commitID
))
55 self
.commitID
= commitID
58 os
.putenv("GIT_DIR", git_dir
)
59 f
= os
.popen("%s commit %s" % (
60 os
.path
.join(git_bin_dir
, "git-cat-file"), self
.commitID
))
61 self
.tree
= f
.readline().rstrip().split(" ")[-1]
64 commit
= f
.readline().rstrip()
67 # commit is empty, must be invalid hash
68 send_text("Invalid hash %s for tree %s" % (self
.commitID
, self
.treename
))
70 # parse committer field, generate snapshot name
71 #committime = int(commit.split(" ")[-2])
72 self
.name
= "%s-snapshot-%s" % (treename
, self
.commitID
)
73 self
.filepath
= "%s/%s.tar.bz2" % (self
.dir, self
.name
)
74 self
.tarpath
= self
.filepath
[:-4]
76 # check whether snapshot dir exists, or create it
78 if not os
.access("%s/" % self
.dir, os
.F_OK
):
79 os
.mkdir("%s/" % self
.dir)
81 def make_commithash(self
):
82 # check whether commithash file exists
83 ch
= ("%s/commithash-%s" % (self
.dir, self
.commitID
))
84 if not os
.access(ch
, os
.F_OK
):
85 # make commithash file
86 chfile
= open(ch
, "w+")
87 chfile
.write("%s\n\n" % self
.commitID
)
88 lstree
= os
.popen("git-ls-tree -r %s" % self
.commitID
)
95 # check whether snapshot file exists, or build it
98 if not os
.access(self
.filepath
, os
.F_OK
):
100 # todo: trap possible errors here
102 os
.system("%s %s %s > %s" % (
103 os
.path
.join(git_bin_dir
, "git-tar-tree"),
104 self
.tree
, self
.name
, self
.tarpath
))
106 # add commithash file to tarball
107 chFilename
= self
.make_commithash()
108 tfile
= tarfile
.TarFile(self
.tarpath
, "a")
109 tfile
.add(chFilename
, "%s/commithash" % self
.name
)
112 os
.system("bzip2 %s" % self
.tarpath
)
115 file("%s/.htaccess" % self
.dir,"a").write('AddDescription "%s git snapshot (%s)" %s.tar.bz2\n' % (self
.treename
, self
.commitID
, self
.name
))
120 retFile
= file(self
.filepath
, "r")
125 def send_bheaders(self
, filename
= None, size
= None, lastmod
= None):
127 filename
= self
.name
+ ".tar.bz2"
130 sizestr
= "; size=%i" % size
131 print "Content-Type: application/x-bzip2"
132 print "Content-Encoding: x-bzip2"
133 print "Content-Disposition: inline; filename=%s%s" % (
135 print "Accept-Ranges: none"
137 print "Content-Length: %i" % size
138 if lastmod
is not None:
139 print "Last-Modified: %s" % (
140 time
.strftime("%a, %d %b %Y %H:%M:%S GMT", time
.gmtime(lastmod
)))
143 # send pre-made tarball (self.build, or otherwise)
144 def send_binary(self
):
145 bfile
= self
.get_file()
147 send_text("Sorry, could not provide snapshot for tree %s, commit %s" % (self
.treename
, self
.commitID
))
149 self
.send_bheaders(size
=os
.stat(self
.filepath
)[6],
150 lastmod
= os
.stat(self
.filepath
)[8])
152 sys
.stdout
.write(line
)
155 # make snapshot tarball and send, on the fly
156 def on_the_fly(self
):
157 def cache_chunk_send(chunk
, cfile
):
159 sys
.stdout
.write(chunk
)
161 # try to get file from disk if it exists, first
162 if os
.access(self
.filepath
, os
.F_OK
):
166 cachefile
= file(self
.filepath
, "w")
167 tar
= os
.popen("%s %s %s" % (
168 os
.path
.join(git_bin_dir
, "git-tar-tree"),
169 self
.tree
, self
.name
))
171 kompressor
= bz2
.BZ2Compressor()
174 cache_chunk_send(kompressor
.compress(line
),
176 cache_chunk_send(kompressor
.flush(), cachefile
)
180 if not os
.access(snapshot_dir
, os
.F_OK
):
182 os
.mkdir(snapshot_dir
)
184 send_text("Could not create snapshot dir '%s'" % snapshot_dir
)
187 def valid_hash(ahash
):
194 if char
not in "0123456789abcdef":
198 def valid_pathinfo():
202 path
= os
.environ
["PATH_INFO"]
207 # path must be '/treename-snapshot-hash.tar.bz2'
208 match
= re
.compile(r
"^/[\w\d\.-]+-snapshot").search(path
)
209 if match
is not None:
210 tree
= match
.group()[1:-len("-snapshot")]
211 match
= re
.compile(r
"snapshot-(([\da-f]{40})|(HEAD))\.tar\.bz2$").search(path
)
212 if match
is not None:
213 commit
= match
.group()[len("snapshot-"):-len(".tar.bz2")]
215 retVal
= (tree
, commit
)
218 def send_snapshot(gitdir
, tree
, commit
):
220 if not os
.access(gitdir
, os
.F_OK
):
221 send_text("No such tree: %s" % tree
)
224 snap
= Snapshot(gitdir
, tree
, commit
)
228 fs
= cgi
.FieldStorage()
229 pathArgs
= valid_pathinfo()
230 if (fs
.has_key("tree") and fs
.has_key("commit")):
231 tree
= fs
["tree"].value
232 commit
= fs
["commit"].value
234 # validate commit hash
235 if not valid_hash(commit
):
236 send_text("Invalid hash: %s" % commit
)
239 send_snapshot(os
.path
.join(git_base_dir
, tree
), tree
, commit
)
241 #elif fs.has_key("tree"):
242 # redirect("%s%s" % (snapshots_url, fs["tree"].value))
243 # #if os.access(os.path.join(snapshot_dir, fs["tree"].value), os.F_OK):
244 # # redirect("%s%s" % (snapshots_url, fs["tree"].value))
246 # # send_text("No such tree: %s\n" % fs["tree"].value)
252 send_snapshot(os
.path
.join(git_base_dir
, tree
), tree
, commit
)
254 # user requested url directly, without a commit hash
255 # redirect to snapshots dir
256 redirect(snapshots_url
)