Find a file
2026-01-16 15:51:54 +01:00
code docs! 2026-01-16 14:22:26 +01:00
Readme.md code blocks 2026-01-16 15:51:54 +01:00

Braids: Intro to Git workshop

This repo contains technical notes from the preparation for the Braids: Intro to git we gave at Het Nieuwe Instituut on Januari 15 2026 on invitation of The Hmm and NNAD as part of the workshop series "How to Archive Better: Preserving Practices".

The slides for the workshop live in their own repo at: https://git.hackersanddesigners.nl/hrk/Braid-slides and are viewable at https://braids.hackersanddesigners.nl/slides/

Weve set up a Forgejo instance (you're probably reading this there) on a Raspberry Pi, accessible over the web via Tinc. During the workshop, user account registration was temporarily open. After a short introduction to the basics of Git, participants were invited to clone the workshop repository, create a personal branch (for example braids/), make a few small edits to the starter HTML page, commit their changes, and push the branch back to Forgejo. A webhook on the same machine published each pushed branch to a shared gallery site, so everyone could immediately view each others pages in the browser and iterate with additional commits.

The Forgejo repository is configured with a push webhook that calls a small deployment script on the Raspberry Pi. When a branch is pushed, the webhook triggers the script to fetch the latest updates from Forgejo, export the contents of each participant branch, and write them to the web server directory under a per-branch path. It also regenerates the gallery index page so new or updated branches appear immediately. This provides a near real-time “publish on push” loop during the workshop. The main branch was protect to commits in the Forgejo repo settings.

This involved:

Installing Forgejo

Mostly based on https://pimylifeup.com/raspberry-pi-forgejo/ with some modification because we're storing the repos on an external SSD

I am going to pretend i did this in the right order. We first setup the SSD, so we can configure the proper paths rightaway.

Set up the SSD.

Create mount directory:

sudo mkdir /media/ssd

Test mount. sudo mount /dev/sda1 /media/ssd

If it works add it to fstab

To get uuid
lsblk -f

Edit /etc/fstab and add:
UUID=<uuid> /media/ssd ext4 defaults,no-fail 0 2

No-fail so the raspi boot even if the disk unplugged.

Then install Forgejo

Following: https://pimylifeup.com/raspberry-pi-forgejo/

Install docker with:
curl -sSL https://get.docker.com | sh

Create user (in current user group)
sudo usermod -aG docker $USER

Logout
logout
Check if the group exist:
groups

test:
docker run hello-world

Docker should now be set up.

Create a directory for the installation:

sudo mkdir -p /opt/stacks/forgejo
cd /opt/stacks/forgejo
nano compose.yaml
services:
  server:
    image: codeberg.org/forgejo/forgejo:7
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - FORGEJO__database__DB_TYPE=postgres
      - FORGEJO__database__HOST=db:5432
      - FORGEJO__database__NAME=forgejo
      - FORGEJO__database__USER=forgejo
      - FORGEJO__database__PASSWD=<db_password>
      - FORGEJO__WEBHOOK__ALLOWED_HOST_LIST=private,loopback
    restart: always
    networks:
      - forgejo
    volumes:
      - /media/ssd/forgejo:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "222:22"
    depends_on:
      - db

  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=forgejo
      - POSTGRES_PASSWORD=<db_password>
      - POSTGRES_DB=forgejo
    networks:
      - forgejo
    volumes:
      - ./postgres:/var/lib/postgresql/data

networks:
  forgejo:
    external: false

docker compose up -d

It should now be accessible at :3000

Now we have to set up tinc proxying, but in this case it was already setup for another service. So only nginx config on the tinc edge node and creating a certificate.

sudo certbot --nginx -d git.hackersanddesigners.nl

And configure nginx in /etc/nginx/sites-available/forgejo.conf:

Set the newly created domain in the Forgejo config with:
nano /media/ssd/forgejo/gitea/conf/app.ini

And change the domains:

ROOT_URL = https://git.hackersanddesigners.nl
DOMAIN   = git.hackersanddesigners.nl

And restart the service
docker compose restart

We shoudl now have a running instance of Forgejo

Setting up a webhook

Setting up the webhook took a bit of time because it is somewhat confusing what IP to use to get from the Docker container to the host. But that is probably my inexperience with Docker.

Setup web hooks like this: https://www.shawenyao.com/Voice-Controlled-Raspberry-Pi/

sudo apt install webhook

configure
sudo nano /etc/webhook.conf

[
  {
    "id": "git-pull-site",
    "execute-command": "/usr/local/bin/deploy-git-site.sh",
    "command-working-directory": "/var/www/html/",
    "pass-arguments-to-command": []
  }
]

Create the shell script sudo nano /usr/local/bin/deploy-git-site.sh

#!/usr/bin/env bash
set -euo pipefail

cd /var/www/html/testsite
git pull --ff-only

Make it executable
sudo chmod +x /usr/local/bin/deploy-braids.py

Edit the web hook:
sudo systemctl edit webhook Add:

[Service]
ExecStart=
ExecStart=/usr/bin/webhook -hooks /etc/webhook.conf -ip 0.0.0.0 -port 9010 -verbose

sudo systemctl daemon-reload sudo systemctl enable --now webhook sudo systemctl restart webhook

Check for errors: sudo systemctl status webhook --no-pager

Allow the webhook to connect to localhost, add to compose.yaml

- FORGEJO__WEBHOOK__ALLOWED_HOST_LIST=private,loopback

We have to call the webhook on the IP of the docker host. Get it with:

ip -4 addr show docker0

Gave:

5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

(if i understand correctly 172.17.0.1 is kinda standard, but i did not know that)

Now add the webhook to the repo with that IP: url: http://172.17.0.1:9010/hooks/git-pull-site

Deploying the sites

The script called by the webhook is a python script that was cobbled together from a bunch of resources:

You can find the script in this repo under /code/deploy-braids.py