stagit

README.md

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