18 Commits
Author SHA1 Message Date
Klagarge aefd2e38e1 fix: depth of side toc
Build site / build (push) Successful in 2m51s
Build site / deploy (push) Skipped
2026-10-20 11:01:54 +02:00
Klagarge 7b34f5a589 feat(cs): add initial lazygit doc
Build site / build (push) Successful in 2m50s
Build site / deploy (push) Skipped
2026-10-12 14:06:04 +02:00
Klagarge 6436f52889 feat(cs): add initial git doc 2026-10-12 14:05:52 +02:00
Klagarge 49e6820eae feat(cs): add tools section 2026-10-12 14:05:28 +02:00
Klagarge f8350ba235 ops: adapt script to work on windows 2026-10-12 13:32:43 +02:00
Klagarge 09f0dc42e4 chore(home): add metadatas
Build site / build (push) Successful in 2m44s
Build site / deploy (push) Successful in 7s
2026-10-11 23:46:05 +02:00
Klagarge 8e2303fcdf refactor: change main categories
Build site / build (push) Successful in 2m48s
Build site / deploy (push) Successful in 6s
2026-10-11 23:32:36 +02:00
Klagarge 4917abe9e6 ops: add toc on side 2026-10-11 23:26:28 +02:00
Klagarge b52629d1f6 feat(home): add gardening with yuca and dracaena 2026-10-11 23:23:48 +02:00
Klagarge b9159581f3 ops: add strip-exif function in justfile 2026-10-11 23:23:14 +02:00
Klagarge 9e9163f01b chore: add IDE in gitignore 2026-10-11 23:22:57 +02:00
Klagarge 54a69b51ae ops: add lib with tag function 2026-10-11 23:22:42 +02:00
Klagarge 975ba4f312 fix: remove act check
Build site / build (push) Successful in 50s
Build site / deploy (push) Successful in 3s
2026-10-11 20:09:47 +02:00
Klagarge 6cd5ed61c4 Merge branch 'refactor: migrate to calepin'
Build site / build (push) Successful in 51s
Build site / deploy (push) Successful in 2s
Reviewed-on: #1
2026-10-11 18:06:17 +00:00
Klagarge 1adca36802 ci: publish to the pages branch on main pushes
Build site / build (push) Successful in 52s
Build site / deploy (push) Skipped
Assisted-by: Delta:zai-glm-5.3
2026-10-11 20:00:54 +02:00
Klagarge 3b1ef34bce feat: enable PDF output for pages
Assisted-by: Delta:zai-glm-5.3
2026-10-11 18:56:04 +02:00
Klagarge 6b33be6956 chore: add a recipe for local act runs
Assisted-by: Delta:zai-glm-5.3
2026-10-11 00:22:23 +02:00
Klagarge f94a5cbe17 ci: use upload-artifact v3 for gitea
Assisted-by: Delta:zai-glm-5.3
2026-10-11 00:22:23 +02:00
23 changed files with 417 additions and 79 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
# Shell scripts and the Justfile must stay LF so they run in Git Bash on Windows.
*.sh text eol=lf
Justfile text eol=lf
+49 -2
View File
@@ -1,6 +1,8 @@
name: Build site
on: push
on:
- push
- workflow_dispatch
jobs:
build:
@@ -16,8 +18,53 @@ jobs:
- name: Build site
run: ./scripts/calepin.sh compile site public
# The official upload-artifact@v4 refuses to run on Gitea (it detects a
# non-GitHub server and aborts with GHESNotSupportedError). v3 uses the
# artifact API that Gitea implements. On Gitea >= 1.22 with a current
# act_runner, christopherhx/gitea-upload-artifact@v4 is a drop-in
# replacement if you want the v4 API.
- name: Upload site artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: public
path: public/
deploy:
needs: build
# Publish only from main; move the gate to publish from another branch.
if: github.ref == 'refs/heads/main'
# Write access for the automatic Actions token to push the pages branch.
permissions:
contents: write
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
runs-on: ubuntu-latest
steps:
- name: Download site artifact
uses: actions/download-artifact@v3
with:
name: public
path: public
- name: Check artifact layout
run: test -f public/index.html
# Publishing interfaces with prod; it never runs under local act tests
# (act sets env.ACT, the Gitea runner does not). The force-push to the
# `pages` branch is what triggers the git-pages webhook.
- name: Publish to pages branch
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
if [ -z "$GITEA_TOKEN" ]; then
echo "::error::Missing GITEA_TOKEN (the automatic Actions token)."
exit 1
fi
cd public
git init -q -b pages .
git config user.name "Yggdrasil CI"
git config user.email "ci@noreply.kb28.ch"
git add -A
git commit -q -m "Publish site from ${GITHUB_SHA}"
repo_host="${GITHUB_SERVER_URL#*://}"
repo_host="${repo_host%/}"
git push -q --force "https://${GITHUB_REPOSITORY_OWNER}:${GITEA_TOKEN}@${repo_host}/${GITHUB_REPOSITORY}.git" pages
+3 -1
View File
@@ -1,6 +1,6 @@
# Generated site and pinned toolchain
public/
tools/
/tools/
# Calepin build artifacts
.calepin/
@@ -8,3 +8,5 @@ tools/
# IDE
.idea/
.zed/
.vscode/
+8
View File
@@ -21,3 +21,11 @@ watch: tools
# Remove generated output and build caches
clean:
./scripts/clean.sh
# Remove all EXIF metadata from a file
strip-exif file:
exiftool -all= -overwrite_original {{ quote(file) }}
# Run the CI workflow locally with act (requires act + docker)
ci:
act push -W .gitea/workflows/build.yml -j build --artifact-server-path /tmp/act-artifacts
+17 -4
View File
@@ -24,6 +24,7 @@ checksum-verified into `tools/` on first use (no global installs, no cargo):
just preview # build, then serve the site locally
just watch # rebuild on change and serve
just clean # remove public/ and build caches
just ci # run the CI workflow locally with act (needs act + docker)
## Adding a page
@@ -49,6 +50,10 @@ target = "<category>/<page>.typ"
Sidebar labels come from the page's `#metadata(...)` line, not from the config.
Every page also renders a printable PDF twin (`pdf = true` in
`calepin.toml`, selectable in the page's view picker). Add `pdf: false` to a
page's `#metadata(...)` to skip its PDF, as `404.typ` does.
## Adding a category
1. Create `site/<category>/` with an `index.typ`, using
@@ -68,8 +73,16 @@ sidebar.
## Deployment
`just build` produces a fully static site in `public/`: HTML pages plus
`sitemap.xml`, `robots.txt`, and `llms.txt`. The Gitea Actions workflow in
`just build` produces a fully static site in `public/`: HTML pages with
printable PDF twins, plus `sitemap.xml`, `robots.txt`, and `llms.txt`. The
Gitea Actions workflow in
`.gitea/workflows/build.yml` builds the site on every push with the same
pinned toolchain and uploads `public/` as an artifact for deployment to any
static host.
pinned toolchain and uploads `public/` as an artifact.
Pushes to `main` are also published: the `deploy` job force-pushes `public/`
to the repo's `pages` branch using the automatic Actions token (no secrets to
configure). A Gitea webhook on the repo then notifies the self-hosted
[git-pages](https://codeberg.org/git-pages/git-pages) server, which clones
the `pages` branch and serves the site. The push step is gated with
`if: ${{ !env.ACT }}` so local `act` runs never publish, and
`workflow_dispatch` enables manual publishes from the Gitea UI.
+78 -19
View File
@@ -1,14 +1,16 @@
#!/usr/bin/env bash
# Bootstrap the pinned toolchain (Calepin + Typst) into tools/.
# Idempotent: skips downloads when the binaries are already in place.
# Supports x86_64 Linux and Windows (run through Git Bash / MSYS2).
set -euo pipefail
# Pinned toolchain -- bump both binaries together and re-verify checksums.
calepin_version="0.0.61"
typst_version="0.15.1"
# Typst publishes no .sha256 assets; this digest comes from the official
# release metadata (GitHub asset digest of typst-x86_64-unknown-linux-musl.tar.xz).
typst_sha256="a6d077d0a95eed5a2eba715b2dae06be954f624ccbf85758a03f389ded33118c"
# Typst publishes no .sha256 assets; these digests come from the official
# release metadata (GitHub asset digests of the downloads below).
typst_sha256_linux="a6d077d0a95eed5a2eba715b2dae06be954f624ccbf85758a03f389ded33118c"
typst_sha256_windows="19ce3551153c2fe7ee9fa2f95208310c8f4d3209fedb699e0333faf8913f6736"
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
tools="$root/tools"
@@ -18,26 +20,83 @@ if [ "$(uname -m)" != "x86_64" ]; then
exit 1
fi
case "$(uname -s)" in
Linux*)
exe=""
calepin_name="calepin-x86_64-unknown-linux-gnu.tar.xz"
typst_name="typst-x86_64-unknown-linux-musl.tar.xz"
typst_sha256="$typst_sha256_linux"
;;
MINGW*|MSYS*|CYGWIN*)
exe=".exe"
calepin_name="calepin-x86_64-pc-windows-msvc.zip"
typst_name="typst-x86_64-pc-windows-msvc.zip"
typst_sha256="$typst_sha256_windows"
;;
*)
echo "error: unsupported operating system $(uname -s)" >&2
exit 1
;;
esac
# Extract a single binary from a zip into tools/. The zip may or may not nest
# it in a top-level directory. Git Bash ships no unzip, so fall back to the
# bsdtar.exe bundled with Windows 10+.
install_zip_binary() {
local archive="$1" bin="$2"
local tmp="$tools/.extract"
rm -rf "$tmp"
mkdir -p "$tmp"
if command -v unzip >/dev/null 2>&1; then
unzip -q -o "$archive" -d "$tmp"
else
local wintar=""
if command -v cygpath >/dev/null 2>&1; then
wintar="$(cygpath -u "${SYSTEMROOT:-C:/Windows}")/System32/tar.exe"
fi
if [ -z "$wintar" ] || [ ! -x "$wintar" ]; then
echo "error: extracting $archive needs unzip or Windows tar.exe" >&2
return 1
fi
"$wintar" -xf "$archive" -C "$tmp"
fi
local found
found="$(find "$tmp" -type f -name "$bin" -print -quit)"
if [ -z "$found" ]; then
echo "error: $bin not found in $archive" >&2
return 1
fi
mv "$found" "$tools/$bin"
rm -rf "$tmp"
}
mkdir -p "$tools"
if [ ! -x "$tools/calepin" ]; then
name="calepin-x86_64-unknown-linux-gnu.tar.xz"
if [ ! -x "$tools/calepin$exe" ]; then
# Calepin publishes .sha256 assets next to its downloads.
base="https://github.com/vincentarelbundock/calepin/releases/download/v$calepin_version"
curl -fsSLo "$tools/$name" "$base/$name"
curl -fsSLo "$tools/$name.sha256" "$base/$name.sha256"
(cd "$tools" && sha256sum -c "$name.sha256")
tar -xJf "$tools/$name" -C "$tools" --strip-components=1 "calepin-x86_64-unknown-linux-gnu/calepin"
rm "$tools/$name" "$tools/$name.sha256"
curl -fsSLo "$tools/$calepin_name" "$base/$calepin_name"
curl -fsSLo "$tools/$calepin_name.sha256" "$base/$calepin_name.sha256"
(cd "$tools" && sha256sum -c "$calepin_name.sha256")
if [ "$exe" ]; then
install_zip_binary "$tools/$calepin_name" "calepin.exe"
else
tar -xJf "$tools/$calepin_name" -C "$tools" --strip-components=1 "${calepin_name%.tar.xz}/calepin"
fi
rm "$tools/$calepin_name" "$tools/$calepin_name.sha256"
fi
if [ ! -x "$tools/typst" ]; then
name="typst-x86_64-unknown-linux-musl.tar.xz"
curl -fsSLo "$tools/$name" "https://github.com/typst/typst/releases/download/v$typst_version/$name"
echo "$typst_sha256 $name" > "$tools/$name.sha256"
(cd "$tools" && sha256sum -c "$name.sha256")
tar -xJf "$tools/$name" -C "$tools" --strip-components=1 "typst-x86_64-unknown-linux-musl/typst"
rm "$tools/$name" "$tools/$name.sha256"
if [ ! -x "$tools/typst$exe" ]; then
curl -fsSLo "$tools/$typst_name" "https://github.com/typst/typst/releases/download/v$typst_version/$typst_name"
echo "$typst_sha256 $typst_name" > "$tools/$typst_name.sha256"
(cd "$tools" && sha256sum -c "$typst_name.sha256")
if [ "$exe" ]; then
install_zip_binary "$tools/$typst_name" "typst.exe"
else
tar -xJf "$tools/$typst_name" -C "$tools" --strip-components=1 "${typst_name%.tar.xz}/typst"
fi
rm "$tools/$typst_name" "$tools/$typst_name.sha256"
fi
"$tools/calepin" --version
"$tools/typst" --version
"$tools/calepin$exe" --version
"$tools/typst$exe" --version
+7 -2
View File
@@ -4,9 +4,14 @@ set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [ ! -x "$root/tools/calepin" ]; then
exe=""
case "$(uname -s)" in
MINGW*|MSYS*|CYGWIN*) exe=".exe" ;;
esac
if [ ! -x "$root/tools/calepin$exe" ]; then
"$root/scripts/bootstrap.sh"
fi
export PATH="$root/tools:$PATH"
exec "$root/tools/calepin" "$@"
exec "$root/tools/calepin$exe" "$@"
+1
View File
@@ -1,4 +1,5 @@
#set document(title: [Not found])
#metadata((pdf: false)) <website-metadata>
#title()
+18 -16
View File
@@ -2,7 +2,7 @@ title = "Yggdrasil"
description = "Personal knowledge base."
base-url = "https://yggdrasil.kb28.ch"
theme = "themes/yggdrasil"
pdf = false
pdf = true
minify = true
search = "pagefind"
logo = "assets/yggdrasil.svg"
@@ -22,12 +22,16 @@ include = [
]
exclude = ["drafts/**"]
[toc]
enabled = true
depth = 3
floating = true
[menus]
main = [
{ label = "Home", target = "home/index.typ" },
{ label = "IT", target = "it/index.typ" },
{ label = "OT", target = "ot/index.typ" },
{ label = "Security", target = "security/index.typ" },
{ label = "Computer Science", target = "cs/index.typ" },
{ label = "HomeLab", target = "homelab/index.typ" },
{ label = "Medias", target = "medias/index.typ" },
]
social = [
@@ -54,24 +58,22 @@ item = [
[[sidebar.section]]
title = "IT"
title = "Computer Science"
item = [
{ target = "it/index.typ" },
{ glob = "it/*" }
{ target = "cs/index.typ" },
{ glob = "cs/*" }
]
section = [
{ title = "Tools", item = [{ glob = "cs/tools/*.typ" }] }
]
[[sidebar.section]]
title = "OT"
item = [
{ target = "ot/index.typ" },
{ glob = "ot/*" }
]
[[sidebar.section]]
title = "Security"
title = "HomeLab"
item = [
{ target = "security/index.typ" },
{ glob = "security/*" }
{ target = "homelab/index.typ" },
{ glob = "homelab/*" }
]
[[sidebar.section]]
+7
View File
@@ -0,0 +1,7 @@
#set document(title: [Computer Science])
#metadata((title: "Overview", translation_key: "cs-index")) <website-metadata>
#title()
Placeholder page for the Computer Science category.
The real content comes later.
+85
View File
@@ -0,0 +1,85 @@
#import "/lib/lib.typ": *
#set document(title: [Git])
#metadata((
title: "Git",
translation_key: "git",
summary: "What's git and lot of usefull command"
)) <website-metadata>
#calepin.setup(eval: false)
= What's Git
This is git
= Bascis command
TODO:
- fetch
- pull
- push
- commit (with sign)
- checkout
- rebase
= Config
Git config live in `~/.gitconfig`
== User
In User section, we can specify default `name` and `email`:
```yaml
[user]
name = Rémi Heredero
email = remi@heredero.ch
```
== Signing
To sign commit and tag by default, you can set the flag on the configfile:
```yaml
[commit]
gpgsign = true
[tag]
gpgSign = true
```
You also probably want to specify your default gpg key in the user section:
```yaml
[user]
signingkey = 4A8C1907D8B15B47
```
Sometime (often in windows), you need to specify the path of the gpg program with:
```yaml
[gpg]
program = C:/Users/her/dev/GnuPG/bin/gpg.exe
```
== Default editor
You can define a default editor to edit commit message or solve conflict. In my case, my favourite code editor is Zed, so:
```yaml
[core]
editor = zed --wait
```
== Include If
You maybe want a different config for some repo. It's easy to do that with `includeIf`. You can define a gitconfig overlay to apply to a specific repository:
```yaml
[includeIf "gitdir:<path-to-work-repo>"]
path = ~/.gitconfig-work
```
= Advanced command
== Reset author
If you didn't use the right config for commiting somewhere, you can reset the author of commit with the following command.
=== Single commit
```sh
git commit --amend --no-edit --reset-author
```
=== Entire project history
```sh
git rebase -r --root --exec "git commit --amend --no-edit --reset-author"
```
=== From a specific commit
```sh
git rebase -r <hash> --exec "git commit --amend --no-edit --reset-author"
```
+25
View File
@@ -0,0 +1,25 @@
#import "/lib/lib.typ": *
#set document(title: [Lazygit])
#metadata((
title: "Lazygit",
translation_key: "lazygit",
summary: "Simple TUI for git commands build in GO"
)) <website-metadata>
#calepin.setup(eval: false)
= Install
TODO
= Config file
On linux:
`~/.config/lazygit/config.yml`
On Windows:
`%USERPROFILE%\AppData\Local\lazygit\config.yml `
= Disable auto-fetch
Add the following code in your config gile:
```yaml
git:
autoFetch: false
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

+52 -2
View File
@@ -1,6 +1,56 @@
#import "/lib/lib.typ": *
#set document(title: [Gardening])
#metadata((title: "Gardening", translation_key: "home-gardening")) <website-metadata>
#metadata((
title: "Gardening",
translation_key: "home-gardening",
date: "2026-10-11",
tags: ("plants", "yucca", "dracaena", "light", "water"),
author: "Rémi Herdero",
summary: "Some plants description and advices for keep them alive"
)) <website-metadata>
#title()
Placeholder page — the real content comes later.
= Yucca elephantipes <home:gardening:yucca>
#calepin.elements.columns(
columns: 2,
wrap: true,
[
Yucca (@fig:plant:yucca_elephantipes) from central America is accustomed to arid conditions.
#tag(fill: yellow.lighten(30%))[Light ++]
#tag(fill: blue.lighten(50%))[Water --]
This plant love light and should be placed on one of the most brightful parts of the room. It even tolerates direct sunlight very well.
The Yucca stores water in its thick trunk. You need to let the soil dry out almost completely between waterings. Generally speaking, watering once *every 2 to 3 weeks* is more than enough. It's better to forget about it for a while than to overwater it, otherwise its roots will decay. Usually we can put around #frackable(1, 2) liter every 2 Weeks. But it's important to check the dirt is dry before watering.
],[
#figure(
image("assets/yucca_elephantipes.jpg", width: 60%),
caption: "Yucca elephantipes"
) <fig:plant:yucca_elephantipes>
]
)
= Dracaena marginata <home:gardening:dracaena>
#calepin.elements.columns(
columns: 2,
wrap: true,
[
The Dracaena (@fig:plant:dracaena_marginata) come from Madagascar.
#tag(fill: yellow.lighten(30%))[Light +]
#tag(fill: blue.lighten(50%))[Water -]
This plant prefer lot of indirect light. Avoid to place it in direct sunlight because it can burn its leaves.
The Dracaena don't need a lot of water. Before watering, make sure the first 3 or 4 centimeters of soil are dry. In average, water the plant once with #frackable(1, 2) litre *every 10 to 14 days*. The extremity of the leaves tend to turn brown when the air is too dry or the water contain too much calcium or chlorine.
], [
#figure(
image("assets/dracaena_marginata.jpg", width: 60%),
caption: [Dracaena marginata]
) <fig:plant:dracaena_marginata>
]
)
-6
View File
@@ -1,6 +0,0 @@
#set document(title: [Washing clothes])
#metadata((title: "Washing clothes", translation_key: "home-washing-clothes")) <website-metadata>
#title()
Placeholder page — the real content comes later.
+6
View File
@@ -0,0 +1,6 @@
#set document(title: [Backup])
#metadata((title: "Backup", translation_key: "homelab-backup")) <website-metadata>
#title()
Placeholder page — the real content comes later.
+7
View File
@@ -0,0 +1,7 @@
#set document(title: [HomeLab])
#metadata((title: "Overview", translation_key: "homelab")) <website-metadata>
#title()
Placeholder page for the homelab category.
The real content comes later.
-7
View File
@@ -1,7 +0,0 @@
#set document(title: [IT])
#metadata((title: "Overview", translation_key: "it-index")) <website-metadata>
#title()
Placeholder page for the IT category.
The real content comes later.
+51
View File
@@ -0,0 +1,51 @@
// #import "@preview/calepin:0.1.0" as calepin
#import "@preview/hes-so-core:0.1.0"
#import "@preview/frackable:0.2.0": *
// #import "@preview/cetz:0.5.2"
//
// #let drawing(..args) = {
// let canvas = cetz.canvas(..args)
// context if target() == "html" {
// html.frame(canvas)
// } else {
// canvas
// }
// }
#let tag(body, fill: yellow, stroke: auto) = {
let stroke-color = if stroke == auto {
fill.darken(25%)
} else if type(stroke) == color {
stroke
} else if type(stroke) == stroke {
stroke.paint
} else {
fill.darken(25%)
}
let stroke-rule = if stroke == auto {
1pt + stroke-color
} else if type(stroke) == color {
1pt + stroke
} else {
stroke
}
context if target() == "html" {
let bg-hex = fill.to-hex()
let border-hex = stroke-color.to-hex()
html.elem("span", attrs: (
style: "display: inline-block; background-color: " + bg-hex
+ "; border: 1px solid " + border-hex
+ "; color: #1f2933; border-radius: 1em; padding: 0.2em 0.6em; font-size: 0.85em; font-weight: 500; line-height: 1.2;"
))[#body]
} else {
box(
fill: fill,
stroke: stroke-rule,
radius: 1em,
inset: (x: 0.5em, y: 0.5em)
)[#body]
}
}
-7
View File
@@ -1,7 +0,0 @@
#set document(title: [OT])
#metadata((title: "Overview", translation_key: "ot-index")) <website-metadata>
#title()
Placeholder page for the OT category.
The real content comes later.
-7
View File
@@ -1,7 +0,0 @@
#set document(title: [Security])
#metadata((title: "Overview", translation_key: "security")) <website-metadata>
#title()
Placeholder page for the security category.
The real content comes later.
-6
View File
@@ -1,6 +0,0 @@
#set document(title: [SSH])
#metadata((title: "SSH", translation_key: "security-ssh")) <website-metadata>
#title()
Placeholder page — the real content comes later.