stagit

git site generator
Contents

README.md

7.7 kB
  1# stagit
  2
  3static git page generator.
  4
  5It generates static HTML pages for a git repository.
  6
  7## Changes
  8This is my personal fork of stagit.    
  9It has been modified to fit my needs and some refs points
 10to cli apps on my web-server.
 11
 12* Title of files while viewing is now a link to download the file
 13* The index page now links to the repository directory
 14* md4c has been added to parse markdown files
 15* Added classes and ids to some html tags to easier style them
 16* Changed style.css to a modern looking nord theme as well as markdown styling
 17
 18The link to download the file points to `/raw/?repository=&file=` 
 19where file is the full path to the file relative to the repository. 
 20It is also possible to specify which branch to fetch the file from.
 21
 22Make necessary changes to the web-server to serve the file    
 23
 24To view a file in a bare repository:
 25
 26    $cd path/to/repo
 27    $git show HEAD:path/to/file
 28
 29Simple python cgi to serve the file:
 30
 31```
 32#!/bin/python3
 33
 34import cgi
 35import subprocess
 36import mimetypes
 37import sys
 38
 39def getFile(repo, filepath, branch):
 40    gitcmd = subprocess.run(['git', 'show', f'{branch}:{filepath}'],
 41            cwd=f'/home/pi/repositories/{repo}',
 42            capture_output=True)
 43    return gitcmd.stdout
 44
 45
 46def guessType(filepath, extensions_map):
 47    ext = '.' + filepath.split('.')[-1]
 48    if ext in extensions_map:
 49        return extensions_map[ext]
 50    ext = ext.lower()
 51    if ext in extensions_map:
 52        return extensions_map[ext]
 53    return extensions_map['']
 54
 55
 56if not mimetypes.inited:
 57    mimetypes.init()
 58extensions_map = mimetypes.types_map.copy()
 59extensions_map[''] = 'application/octet-stream'
 60
 61form = cgi.FieldStorage()
 62repo = form.getvalue('repository')
 63filepath = form.getvalue('file')
 64if (form.getvalue('branch')):
 65    branch = form.getvalue('branch')
 66else:
 67    branch = 'HEAD'
 68
 69ctype = guessType(filepath, extensions_map)
 70response = getFile(repo, filepath, branch)
 71
 72if response:
 73    print(f"Content-Type: {ctype}")
 74    print(f"Conent-Length: {str(len(response))}")
 75    print()
 76    sys.stdout.flush()
 77    sys.stdout.buffer.write(response)
 78else:
 79    print("Content-Type: text/plain")
 80    print()
 81    print("404: File not found")
 82
 83``` 
 84
 85The index page linking to the directory will not change the default 
 86behavior of stagit if the html files have been created with the [provided 
 87shell script](example_create.sh.html), since log.html is symlinked
 88to index.html.    
 89
 90This change was made so that the web-server could check if the
 91request is for a directory. If it is check if there is a file called 
 92README.md.html in the sub-directory file, if it exists serve that 
 93file instead of log.html.
 94
 95Lighttpd conf to implement this:
 96
 97    url.redirect = ( "^\/stagit\/([a-zA-Z0-9-_.,]\/$" => "/stagit/$1/file/README.md" )
 98    url.rewrite-if-not-file = ( "^\/stagit/([a-zA-Z0-9-_,.]*)\/file\/README.md.html$" => /redirect/$1" )
 99    url.redirect += ( "^\/redirect/\([a-zA-Z0-9-_,.]*)" => "/stagit/$1/log.html" )
100
101### md4c
102md4c is used to parse and convert markdown documents into html. If a file 
103ends with `.md` it will be parsed by md4c rather than the regular way 
104stagit parses plain text files.
105
106https://github.com/mity/md4c
107
108## Usage
109
110Make files per repository:
111
112    $ mkdir -p htmldir && cd htmldir
113    $ stagit path-to-repo
114
115Make index file for repositories:
116
117    $ stagit-index repodir1 repodir2 repodir3 > index.html
118
119
120## Build and install
121
122    $ make
123    # make install
124
125
126## Dependencies
127
128- C compiler (C99).
129- libc (tested with OpenBSD, FreeBSD, NetBSD, Linux: glibc and musl).
130- libgit2 (v0.22+).
131- POSIX make (optional).
132- md4c (markdown parsing to html)
133
134## Documentation
135
136See man pages: stagit(1) and stagit-index(1).
137
138
139### Building a static binary
140
141It may be useful to build static binaries, for example to run in a chroot.
142
143It can be done like this at the time of writing (v0.24):
144
145    cd libgit2-src
146
147    # change the options in the CMake file: CMakeLists.txt
148    BUILD_SHARED_LIBS to OFF (static)
149    CURL to OFF              (not needed)
150    USE_SSH OFF              (not needed)
151    THREADSAFE OFF           (not needed)
152    USE_OPENSSL OFF          (not needed, use builtin)
153    
154    mkdir -p build && cd build
155    cmake ../
156    make
157    make install
158
159
160### Extract owner field from git config
161
162A way to extract the gitweb owner for example in the format:
163
164	[gitweb]
165		owner = Name here
166
167Script:
168
169    #!/bin/sh
170    awk '/^[ 	]*owner[ 	]=/ {
171        sub(/^[^=]*=[ 	]*/, "");
172        print $0;
173    }'
174
175
176### Set clone url for a directory of repos
177
178    #!/bin/sh
179    cd "$dir"
180    for i in *; do
181        test -d "$i" && echo "git://git.codemadness.org/$i" > "$i/url"
182    done
183
184
185### Update files on git push
186
187Using a post-receive hook the static files can be automatically updated.
188Keep in mind git push -f can change the history and the commits may need
189to be recreated. This is because stagit checks if a commit file already
190exists. It also has a cache (-c) option which can conflict with the new
191history. See stagit(1).
192
193git post-receive hook (repo/.git/hooks/post-receive):
194
195    #!/bin/sh
196    # detect git push -f
197    force=0
198    while read -r old new ref; do
199        hasrevs=$(git rev-list "$old" "^$new" | sed 1q)
200        if test -n "$hasrevs"; then
201	        force=1
202            break
203        fi
204    done
205
206    # remove commits and .cache on git push -f
207    #if test "$force" = "1"; then
208    # ...
209    #fi
210
211    # see example_create.sh for normal creation of the files.
212
213
214### Create .tar.gz archives by tag
215
216    #!/bin/sh
217    name="stagit"
218    mkdir -p archives
219    git tag -l | while read -r t; do
220    	f="archives/${name}-$(echo "${t}" | tr '/' '_').tar.gz"
221    	test -f "${f}" && continue
222    	git archive \
223    		--format tar.gz \
224    		--prefix "${t}/" \
225    		-o "${f}" \
226    		-- \
227    		"${t}"
228    done
229
230
231## Features
232
233- Log of all commits from HEAD.
234- Log and diffstat per commit.
235- Show file tree with linkable line numbers.
236- Show references: local branches and tags.
237- Detect README and LICENSE file from HEAD and link it as a webpage.
238- Detect submodules (.gitmodules file) from HEAD and link it as a webpage.
239- Atom feed log (atom.xml).
240- Make index page for multiple repositories with stagit-index.
241- After generating the pages (relatively slow) serving the files is very fast,
242  simple and requires little resources (because the content is static), only
243  a HTTP file server is required.
244- Usable with text-browsers such as dillo, links, lynx and w3m.
245
246
247## Cons
248
249- Not suitable for large repositories (2000+ commits), because diffstats are
250an expensive operation, the cache (-c flag) is a workaround for this in 
251some cases.
252- Not suitable for large repositories with many files, because all files 
253are written for each execution of stagit. This is because stagit shows the 
254lines of textfiles and there is no "cache" for file metadata 
255(this would add more complexity to the code).
256- Not suitable for repositories with many branches, a quite linear 
257history is assumed (from HEAD).
258
259  In these cases it is better to just use cgit or possibly change stagit to
260  run as a CGI program.
261
262- Relatively slow to run the first time (about 3 seconds for sbase, 
2631500+ commits), incremental updates are faster.
264- Does not support some of the dynamic features cgit has, like:
265  - Snapshot tarballs per commit.
266  - File tree per commit.
267  - History log of branches diverged from HEAD.
268  - Stats (git shortlog -s).
269
270  This is by design, just use git locally.
271- After adding md4c for markdown rendering the stagit-index links are pointed
272  to /file/README.md.html instead of log.html this in turn requires every repo
273  to contain a README file in md. (Not really an issue for my personal use case).
274