commit 2d9a6bc63a19ca7710b85f65fb2d3f5e9a46c841
parent 2456bf9230380b50a966e65571114fa66ce64829
Author: Chris Bracken <chris@bracken.jp>
Date: Sun, 16 Aug 2026 18:26:57 +0900
Add gemini, www publish hook
Diffstat:
2 files changed, 123 insertions(+), 0 deletions(-)
diff --git a/post-receive.d/publish_gemini b/post-receive.d/publish_gemini
@@ -0,0 +1,62 @@
+#!/bin/sh -e
+#
+# post-receive.d hook: publish the Gemini capsule to agate's content root.
+#
+# Replaces yakumo's hourly cronjob:
+# git -C ~/gemini/chris.bracken.jp fetch origin \
+# && git -C ~/gemini/chris.bracken.jp reset --hard origin/master
+#
+# agate serves one subdirectory per configured hostname, so the target is
+# content/<hostname>/ and NOT content/ itself.
+#
+# Install:
+# /home/git/git_infra/post-receive.d/publish_gemini (this file, chmod +x)
+# /home/git/<capsule-repo>.git/hooks/post-receive.d/publish_gemini -> symlink
+#
+# Requires zroot/data/gemini mounted READ-WRITE into the git jail at
+# /var/gemini, and the content tree owned by the git user (1001). agate runs
+# as gemini (1965) in its own jail and only needs to read, which the world
+# bits allow. .certificates stays owned by 1965 - agate may rewrite it.
+
+BRANCH="master"
+TARGET="/var/gemini/content/chris.bracken.jp"
+
+# agate reads these as a different user in a different jail.
+umask 022
+
+repo_dir=$(pwd)
+
+if [ ! -d "$TARGET" ]; then
+ echo "[gemini] ERROR: $TARGET missing - is the nullfs mount attached?" >&2
+ exit 1
+fi
+if [ ! -w "$TARGET" ]; then
+ echo "[gemini] ERROR: $TARGET not writable - check ownership (should be git)" >&2
+ exit 1
+fi
+
+# stdin gives one line per updated ref: <old> <new> <refname>
+pushed=0
+while read -r _old new ref; do
+ [ "$ref" = "refs/heads/${BRANCH}" ] || continue
+ # Branch deletion - do not wipe the live capsule.
+ [ "$new" = "0000000000000000000000000000000000000000" ] && continue
+ pushed=1
+done
+
+if [ "$pushed" = "0" ]; then
+ echo "[gemini] ${BRANCH} not updated; nothing to publish"
+ exit 0
+fi
+
+echo "[gemini] Publishing ${BRANCH} to ${TARGET}"
+
+git --git-dir="$repo_dir" --work-tree="$TARGET" checkout -f "$BRANCH"
+
+# checkout -f does not remove files deleted from the branch. This is what
+# `reset --hard` handled before.
+#
+# NOTE: deletes ANY untracked file under $TARGET.
+git --git-dir="$repo_dir" --work-tree="$TARGET" clean -fd
+
+echo "[gemini] Done"
diff --git a/post-receive.d/publish_www b/post-receive.d/publish_www
@@ -0,0 +1,61 @@
+#!/bin/sh -e
+#
+# post-receive.d hook: publish the built site to the web root.
+#
+# Install:
+# /home/git/git_infra/post-receive.d/publish_www (this file, chmod +x)
+# /home/git/bracken_jp.git/hooks/post-receive.d/publish_www -> symlink to it
+#
+# Note this is symlinked ONLY into bracken_jp.git, unlike gout_html which is
+# linked into every repo.
+
+BRANCH="master"
+WWW="/var/www/chris.bracken.jp"
+
+# Files must be readable by the www user in the www jail, which reads this
+# same dataset over a read-only nullfs mount.
+umask 022
+
+# Hooks are called from the repo directory.
+repo_dir=$(pwd)
+
+# Refuse to run if the web root is missing or not writable. Without this a
+# missing nullfs mount would publish into the jail's own filesystem, which
+# would look like it worked and serve nothing.
+if [ ! -d "$WWW" ]; then
+ echo "[publish] ERROR: $WWW does not exist - is the nullfs mount attached?" >&2
+ exit 1
+fi
+if [ ! -w "$WWW" ]; then
+ echo "[publish] ERROR: $WWW is not writable" >&2
+ exit 1
+fi
+
+# stdin gives one line per updated ref: <old> <new> <refname>
+pushed=0
+while read -r _old new ref; do
+ [ "$ref" = "refs/heads/${BRANCH}" ] || continue
+ # Branch deletion - do not wipe the live site.
+ [ "$new" = "0000000000000000000000000000000000000000" ] && continue
+ pushed=1
+done
+
+if [ "$pushed" = "0" ]; then
+ echo "[publish] ${BRANCH} not updated; nothing to publish"
+ exit 0
+fi
+
+echo "[publish] Publishing ${BRANCH} to ${WWW}"
+
+# checkout -f writes/overwrites every tracked file into the work tree.
+git --git-dir="$repo_dir" --work-tree="$WWW" checkout -f "$BRANCH"
+
+# checkout -f does not remove files deleted from the branch, so clean up
+# anything untracked. This is what made `reset --hard` sufficient before.
+#
+# NOTE: this deletes ANY untracked file under $WWW. That is correct while the
+# whole tree is repo-managed. If you ever need to keep something unmanaged
+# there, add it to .gitignore AND drop -x, or this will remove it.
+git --git-dir="$repo_dir" --work-tree="$WWW" clean -fd
+
+echo "[publish] Done"