Codeberg-style layout and Zatto branding for git.zatto.pl (Forgejo CUSTOM_PATH: templates, CSS, logo, deploy)
  • Python 43.5%
  • Shell 21.2%
  • CSS 14.3%
  • Go Template 11.9%
  • JavaScript 9.1%
Find a file
Klaudiusz 786df0ce6e docs: test fixture = the repo's own theme file
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Yf2W9HEmzJtGN9DRtT7WH
2026-09-30 13:07:04 +00:00
brand feat: Codeberg-style layout and Zatto branding for git.zatto.pl 2026-09-30 10:01:46 +00:00
custom fix: footer tone in hash-versioned CSS, theme file back to original 2026-09-30 12:54:13 +00:00
staging feat: Codeberg-style layout and Zatto branding for git.zatto.pl 2026-09-30 10:01:46 +00:00
tools docs: test fixture = the repo's own theme file 2026-09-30 13:07:04 +00:00
.gitignore feat: Codeberg-style layout and Zatto branding for git.zatto.pl 2026-09-30 10:01:46 +00:00
deploy.sh fix: footer tone in hash-versioned CSS, theme file back to original 2026-09-30 12:54:13 +00:00
README.md docs: test fixture = the repo's own theme file 2026-09-30 13:07:04 +00:00

forgejo-custom

The customisation of git.zatto.pl: Forgejo with Codeberg's page layout (logo in the navbar, full-height page, large multi-column footer), Zatto branding and our own zatto-dark colours.

No fork and no custom build. Everything lives in Forgejo's CUSTOM_PATH (/data/gitea in the container):

Path What it does
custom/templates/base/footer_content.tmpl The footer. Overrides an upstream template, see Upgrading Forgejo
custom/templates/custom/header.tmpl Loads zatto-layout.css (official hook, after the theme CSS)
custom/templates/custom/extra_links.tmpl "About" in the navbar for visitors (official hook)
custom/public/assets/css/zatto-layout.css Layout only, colours come from theme variables, so it works with every theme
custom/public/assets/css/theme-zatto-dark.css Our default theme ([ui] DEFAULT_THEME = zatto-dark)
custom/public/assets/img/ logo.svg/.png, favicon.*, apple-touch-icon.png (app icon), zatto/glyph.svg (navbar mask)

The navbar template is deliberately not overridden: logo.svg is the app icon (dark tile), and zatto-layout.css paints the bare glyph over it as a CSS mask in the theme's text colour. Upgrades can't break the navbar.

Brand assets

brand/ holds the vector logo, reconstructed from the brand raster images by measurement rather than auto-tracing:

src=brand/source                                # the brand rasters (wordmark, icon)
tools/measure_brand.py $src/wordmark.png $src/icon.png   # raw edge measurements (JSON)
tools/build_brand.py $src/wordmark.png $src/icon.png brand/
tools/verify_brand.py brand/geometry.json $src/wordmark.png $src/icon.png /tmp/verify --built brand/
tools/export_assets.sh                          # brand/ -> custom/public/assets/img (rsvg-convert)
tools/sync_inline.py                            # brand/ -> inline SVGs in the footer template

Checked against the rasters: mean edge offset 0.22 px on a 112 px cap height (wordmark) and 0.13 px on a 424 px icon. Needs numpy, pillow, scipy.

Test, then deploy

staging/up.sh [--theme forgejo-light]   # throw-away Forgejo 15.0.0 on 127.0.0.1:3300 (CT101 sandbox)
node tools/shoot.mjs staging/shots.json # screenshots through a containerised Chrome (see header)
./deploy.sh                             # git.zatto.pl: backup, install, restart, smoke test
./deploy.sh --list                      # backups on the host
./deploy.sh --restore backup-YYYYMMDD-HHMMSS.tgz

Caching. Forgejo serves /assets with a 6 h browser cache. It links theme-*.css, logo.svg and favicon.* with its own version string (or none), which our deploys don't change. Anything that must show up right after a deploy therefore goes into zatto-layout.css, whose URL carries a content hash. That is why the zatto-dark footer tone lives there and not in the theme file.

tools/assemble.sh builds the tree that staging mounts and deploy.sh ships, so staging tests exactly what goes live. It stamps a content hash into the stylesheet URL (browsers cache /assets for 6 h) and refuses a footer template that is out of sync with brand/.

Testing deploy.sh

tools/test_deploy.sh sources deploy.sh and runs it against a throw-away container that looks like production (old theme, no templates). It breaks things on purpose: truncated upload, tampered file, a restart that never gets healthy, failing smoke tests. Each time it checks that the live tree is untouched or rolled back. It refuses the container name forgejo.

docker run -d --name forgejo-deploytest -p 127.0.0.1:3301:3000 -v deploytest-data:/data \
  -e FORGEJO__database__DB_TYPE=sqlite3 -e FORGEJO__security__INSTALL_LOCK=true \
  codeberg.org/forgejo/forgejo:15.0.0            # on the sandbox host
ssh -f -N -L 13301:127.0.0.1:3301 zatto@<sandbox>  # "public" URL for the smoke test
HOST=zatto@<sandbox> CONTAINER=forgejo-deploytest LOCAL_URL=http://127.0.0.1:3301 \
  PUBLIC_URL=http://127.0.0.1:13301 PRODLIKE_THEME=<copy of our theme css on the sandbox> tools/test_deploy.sh

deploy.sh only ships committed state. A deploy replaces /data/gitea/templates and /data/gitea/public completely, so nothing can be changed there by hand. The container restart takes about 10 s. A failed upload leaves the live site untouched. A failed swap, an unhealthy restart or a failed smoke test restores the backup automatically.

Upgrading Forgejo

Before bumping the image tag:

  1. Diff upstream templates/base/footer_content.tmpl of the new version against the previous one. Carry any change over into ours (new ctx.Locale keys, language menu markup, helpers).
  2. Run the staging with the new tag (FORGEJO_TAG=16.0.0 staging/up.sh). Then screenshot it, desktop and mobile, dark and light.
  3. Deploy the image bump. If a template breaks after the upgrade, delete /data/gitea/templates/base/footer_content.tmpl and restart. Forgejo falls back to its built-in footer.

Credits