aboutsummaryrefslogtreecommitdiff
path: root/src/blog/2022-11-23-git-host
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2022-11-24 00:01:47 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2022-11-24 00:01:47 +0100
commit1b69fbdce80f4ee2d46be63be34b4cfb4e4e11d7 (patch)
treed587a54bfd58054a441c3e6df980ca71290aea32 /src/blog/2022-11-23-git-host
parentb473f7ce3895eccaf7691f6fc9296cb3c695a189 (diff)
downloadsebastiano.tronto.net-1b69fbdce80f4ee2d46be63be34b4cfb4e4e11d7.tar.gz
sebastiano.tronto.net-1b69fbdce80f4ee2d46be63be34b4cfb4e4e11d7.zip
Added blog post(s)
Diffstat (limited to 'src/blog/2022-11-23-git-host')
-rw-r--r--src/blog/2022-11-23-git-host/git-host.md299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/blog/2022-11-23-git-host/git-host.md b/src/blog/2022-11-23-git-host/git-host.md
new file mode 100644
index 0000000..cc9fc27
--- /dev/null
+++ b/src/blog/2022-11-23-git-host/git-host.md
@@ -0,0 +1,299 @@
1# Self-hosted git pages with stagit (featuring ed, the standard editor)
2
3This is a follow-up to my earlier blog entry
4[How I update my website](../2022-08-14-website).
5
6If you work on one or more personal software projects as a hobby,
7chances are you are using `git`. To publish your project online
8you may be using a website like GitHub, or perhaps a more
9open source friendly alternative such as
10[GitLab](https://about.gitlab.com/) or [sourcehut](https://sourcehut.org/).
11
12But have you considered hosting your repositories, and serving them
13via web pages, on your personal server? In this post I am going to show
14you how I do it, in the usual minimalist and
15not-using-what-I-do-not-understand style.
16
17You can see the final result on my [git pages](https://git.tronto.net).
18The scripts and other files I use to set this up are accessible
19[here](https://git.tronto.net/git-hooks).
20
21## Hosting git repositories on your own server
22
23This step is quite simple, and you can just follow
24[Roman Zolotarev's tutorial](https://rgz.ee/git.html) like I did - adapting
25the first few steps to your OS if you are not running OpenBSD.
26
27To sum it up, you need to:
28
291. Create a dedicate `git` account on your server (optional, if you know
30 what you are doing).
31
322. Add your public SSH key to the `~/.ssh/authorized_keys` file of your newly
33 created remote account.
34
353. Initialize a git repository on your server with `git init REPOSITORY`.
36
374. Clone it via SSH with `git clone git@SERVER:REPOSITORY`.
38
39And you are done! If you want to use multiple remotes, for example your private
40server and GitHub, you can do so by adding a push-only URL with
41`git remote set-url --add --push origin URL`. But don't trust me on this
42exact command, I always have to look it up
43- you should do the same before running it.
44
45Now your repositories are online, but how can you make them browsable via web?
46
47## stagit
48
49The tool I use to serve my git repositories as static web pages is
50[stagit](https://codemadness.org/stagit.html). It is very easy to describe what
51stagit does: running it on a git reporitory produces some directories with
52a bunch of html files that you can simply move to your www hosting directory.
53
54After generating the pages you can personalize them by copying your logo,
55[favicon](https://en.wikipedia.org/wiki/Favicon) or CSS style sheet. You can
56use `stagit-index` to generate
57[an index page for your repositories](https://git.tronto.net/). Since everything
58consists of html files, you can simply edit them to personalize your git pages
59even further - and below you'll see some examples.
60
61But you definitely do not want to do this by hand every time you push a commit.
62Since the pages stagit generates are *static*, they do not update
63automatically: you'll have to run stagit again every time. You can automate
64this for example by running stagit periodically with cron, but there is an
65easier way:
66[git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).
67
68## Git hooks
69
70By saving suitably named executable files inside your project's
71`.git/hooks` directory you can automate any process you want during
72certain stages of your git workflow. For example you can use a `pre-commit`
73script to run commands before you commit changes, or a `pre-push` script
74to do something before pushing a commit.
75
76The hooks are divided in client-side and server-side. We are interested in
77the server-side `post-receive` hook, which is executed on the remote every
78time a commit is received. This is the last ingredient we need for our setup:
79a simple `post-receive` hook that runs stagit and copies the files it
80generates to the appropriate folder will do the trick.
81
82## My setup
83
84Throughout the rest of the post I will use the following variables, to be
85set at the beginning of the script:
86
87```
88yourname="Sebastiano Tronto" # Author's name
89repo="$PWD" # The full path of the repository
90name="$(basename $repo .git)" # The name of the repository
91baseurl="https://git.tronto.net" # The base URL for the repository
92basedir="/var/www/htdocs/git.tronto.net" # The base directory for www files
93htdir="$basedir/$name" # The www directory for the repository
94```
95
96### Basic stagit usage
97
98The first thing we want to do is to set up some basic information in the
99`owner` and `url` files that stagit is going to use. The hook is run in the
100repository's directory, so it is not necessary to specify a full path:
101
102```
103echo "$yourname" > owner
104echo "$baseurl/$name" > url
105```
106
107Next we prepare the target directory by removing old files and creating
108it if necessary:
109
110```
111rm -rf "$htdir"
112mkdir -p "$basedir"
113```
114
115To make the repository clonable by anyone from the same URL used to
116view it, we need to copy the whole directory to the
117www directory we have just created:
118
119```
120cp -r "$repo" "$htdir"
121```
122
123And finally we can run stagit:
124
125```
126cd "$htdir"
127stagit -l 100 "$repo"
128```
129
130The `-l` option is used to specify how many commits should be visibile
131in the log page.
132
133For some basic personalization we can choose a different default page (index
134file). I like to have the file list:
135
136```
137cp "$htdir/files.html" "$htdir/index.html"
138```
139
140And we can use our css style sheet, logo and icon:
141
142```
143cp $filesdir/favicon.png ./
144cp $filesdir/logo.png ./
145cp $filesdir/style.css ./
146```
147
148### Bells and whistles
149
150I like stagit's simplicity, but there are a couple of things that I want to
151add or change:
152
153* I would like every page to show a simple footer at the bottom of
154 each page.
155* I would like to have a download button so that people who don't use git
156 can still download my files. This makes sense especially for those
157 repos that are mostly documents, such as my
158 [lecture notes](https://git.tronto.net/mathsoftware) or my
159 [FMC tutorial](https://git.tronto.net/fmctutorial).
160* I would like to convert README.md files to html.
161
162If I were calling stagit by hand after each `git push`, I
163could simply make these changes with a text editor. But I want to automate
164this! How can we edit files in a shell script?
165
166Enter `ed`, [the standard editor](https://www.gnu.org/fun/jokes/ed-msg.html).
167`ed` is a [line text editor](https://en.wikipedia.org/wiki/Line_editor)
168initially released with UNIX Version 1. I am going to talk about it more
169extensively in the next episode of my
170[man page reading club](../2022-05-29-man/)
171series. Without going into detail, `ed` does not show you the text you
172are editing in a 2-dimensional windows: instead, it offers you a command
173line prompt that you can use to run editing commands, such as `a` to add
174text or `p` to print one or more lines of the file.
175
176This might seems like a totally cumbersome way of editing a file, but
177there is one nice side-effect: `ed` is completely scriptable. This means
178that if you know exactly what the file you want to edit looks like,
179you can write the commands you want to run in advance and feed them to
180the editor via standard input, instead of typing them interactively.
181This is exactly what we want to do!
182
183Going back to my stagit setup, say we have a file `bottom.html` that
184looks like this:
185
186```
187<hr class="line">
188<footer> <table>
189<tr> <td class="contact">
190 Back to <a href="https://sebastiano.tronto.net"> sebastiano.tronto.net </a>
191</td>
192<td class="hosted">
193 Generated with <a href="https://codemadness.org/stagit.html">stagit</a>
194</td> </tr>
195</table> </footer>
196```
197
198and we want to insert its content in the file `file.html`, before
199the line that contains the closing tag `</body>`. We can use the
200following one-liner:
201
202```
203printf '%s\n' "/<\/body>" i "$(cat bottom.html)" . w | ed -s file.html
204```
205
206Here the `printf` command is used to feed the tokens `/<\/body>`, `i`,
207`$(cat bottom.html)`, `.` and `w` to `ed`. These are going to
208be interpreted as: "search for the closing tag `</body>`;
209insert the following text until you encounter a single dot on a line:
210[contents of the file `bottom.html`] single dot; save."
211If this seems obscure, I suggest you read
212[`ed`'s manual page](https://man.openbsd.org/OpenBSD-7.2/ed), or wait
213for my next blog post!
214
215The command for adding the download button is similar, after we
216generate a zip archive of the repository using `git archive`:
217
218```
219git archive HEAD -o "$basedir/$name.zip"
220printf '%s\n' \
221 "/log\.html\">Log<\/a>" i \
222 "<a href=\"$baseurl/$name.zip\">Download</a> |" . w \
223| ed -s file.html
224```
225
226Here I am using backlashes to ignore the newline character, so that
227I can use more lines for readability.
228
229The two code snippets above have to be run for every html file generated
230by stagit. To loop over all these files, you can use `find`:
231
232```
233for f in $(find "$htdir" -name "*.html"); do
234 [stuff...]
235done
236```
237
238The command to turn README.md files into a formatted html page is a bit
239more complicated, but I will try to keep the explanation short, since
240this post is already quite long. Feel free to send me an email if you
241have questions!
242
243To have an idea of what the README.md.html file generated by
244stagit looks like, you can check out the html of
245[this page](https://codemadness.org/git/stagit/file/README.html),
246for example (right click and "View page source" or something similar in
247most browsers, or `curl [URL]` if you are cool).
248
249First, since I am using bare git repositories, I need to actually "create"
250the original README.md file - instead of using its rendered-as-plain-text
251html version generated by stagit - using `git show`. Then we need to
252remove from README.md.html all the lines that
253are part of the code listing, i.e. all those that contain a `class="line"`
254string. The `ed` command to do this is `g/class=\"line\"/d`. Then we
255need to remove a couple more lines and finally we can insert the result
256of the command `lowdown file/README.md`, which converts the markdown
257file to html, into the correct place. The final result is:
258
259```
260git show master:README.md > file/README.md
261printf '%s\n' \
262 g/class=\"line\"/d \
263 "/<pre id=\"blob\">" d d i "$(lowdown file/README.md)" . w \
264| ed -s file/README.md.html > /dev/null
265```
266
267### stagit-index
268
269Just a quick mention to how I use stagit-index, the command used to
270generate the index page.
271The only change I make from the default configuration is to change
272the links to each repository to point to the file list instead
273of the log page. stagit-index writes its result to standard output, so
274I can simply use `sed`:
275
276```
277stagit-index /home/git/*.git | sed 's|/log\.html||g' > "$basedir/index.html"
278```
279
280And that's it. Well, I also copy the style files and add a bottom bar,
281and change the title from a `<span class="desc">` to an `<h1>` element,
282again using `ed`. If you want to see the details you can check them out
283[here](https://git.tronto.net/git-hooks/file/post-receive-stagit.html).
284
285## Conclusions
286
287stagit is the perfect minimalist tool to publish your git repository
288with a simple, static web interface. It requires nothing more than
289an http server capable of serving html files. Static files are also
290very simple to customize and tune to your needs.
291
292I have wanted to make this post for quite some time now, mainly
293as an excuse to clean up and document my scripts. I finally had some
294time to work on this - even if scattered around multiple days.
295
296As always, I have tried but failed to keep my post short - I am too
297eager to explain everything I know as clearly as possbile!
298I hope you enjoyed or found it useful. If you have questions or comments,
299feel free to send me an [email](mailto:sebastiano@tronto.net).

Generated with cgit - Back to sebastiano.tronto.net