Use FreeBSD Forgejo CI runner

user <user@users-MacBook-Pro.local>

da8f4cde25f78c227a3df9c7a74136c3dce74df9

diff --git a/.forgejo/ci-required.sh b/.forgejo/ci-required.sh
index 8496b8d..7be8575 100755
--- a/.forgejo/ci-required.sh
+++ b/.forgejo/ci-required.sh
@@ -1,18 +1,26 @@
 #!/bin/sh
 set -eu
 
+if [ -n "${MAKE:-}" ]; then
+  MAKE_CMD=$MAKE
+elif command -v gmake >/dev/null 2>&1; then
+  MAKE_CMD=gmake
+else
+  MAKE_CMD=make
+fi
+
 has_target() {
   target=$1
   [ -f Makefile ] && grep -Eq "^${target}[[:space:]]*:" Makefile
 }
 
 if has_target verify; then
-  make verify
+  "$MAKE_CMD" verify
 else
   ran=0
   for target in security test check build; do
     if has_target "$target"; then
-      make "$target"
+      "$MAKE_CMD" "$target"
       ran=1
     fi
   done
@@ -27,38 +35,38 @@ if ! has_target binary; then
   exit 0
 fi
 
-make binary
+"$MAKE_CMD" binary
 
 if has_target binary-smoke; then
-  make binary-smoke
+  "$MAKE_CMD" binary-smoke
   exit 0
 fi
 if has_target smoke; then
-  make smoke
+  "$MAKE_CMD" smoke
   exit 0
 fi
 
 binary_list=$(mktemp)
 trap 'rm -f "$binary_list"' EXIT HUP INT TERM
-find . -maxdepth 2 -type f -perm -111 \
-  ! -path './.git/*' ! -path './.jerboa/*' ! -path './vendor/*' \
-  ! -path './test/*' ! -path './tests/*' \
-  -exec file {} \; |
+find . -maxdepth 3 -type f -perm -111   ! -path './.git/*' ! -path './.jerboa/*' ! -path './vendor/*'   ! -path './test/*' ! -path './tests/*'   -exec file {} \; |
   awk -F: '/(ELF .*executable|Mach-O .*executable)/ { print $1 }' > "$binary_list"
 
 [ -s "$binary_list" ] || {
-  echo "ERROR: make binary succeeded but produced no runnable ELF executable" >&2
+  echo "ERROR: make binary succeeded but produced no runnable executable" >&2
   exit 1
 }
 
 while IFS= read -r binary; do
   echo "Smoke-checking $binary"
-  if timeout 30 env QT_QPA_PLATFORM=offscreen \
-      QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --version >/dev/null 2>&1; then
+  if command -v timeout >/dev/null 2>&1; then
+    run_with_timeout() { timeout 30 "$@"; }
+  else
+    run_with_timeout() { "$@"; }
+  fi
+  if run_with_timeout env QT_QPA_PLATFORM=offscreen       QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --version >/dev/null 2>&1; then
     continue
   fi
-  if timeout 30 env QT_QPA_PLATFORM=offscreen \
-      QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --help >/dev/null 2>&1; then
+  if run_with_timeout env QT_QPA_PLATFORM=offscreen       QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --help >/dev/null 2>&1; then
     continue
   fi
   echo "ERROR: $binary failed both --version and --help runtime smoke checks" >&2
diff --git a/.forgejo/require-version-bump.sh b/.forgejo/require-version-bump.sh
index ec38872..85ebd63 100755
--- a/.forgejo/require-version-bump.sh
+++ b/.forgejo/require-version-bump.sh
@@ -1,50 +1,38 @@
 #!/bin/sh
 set -eu
+base_ref=${1:-origin/${GITHUB_BASE_REF:-${FORGEJO_BASE_REF:-master}}}
+head_ref=${2:-HEAD}
 
-version_file=${VERSION_FILE:-VERSION}
-test -f "$version_file" || {
-  echo "ERROR: $version_file is required" >&2
-  exit 1
+current_version() {
+  if [ -f VERSION ]; then
+    tr -d '[:space:]' < VERSION
+  elif [ -f Cargo.toml ]; then
+    sed -n '0,/^version[[:space:]]*=/s/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*//p' Cargo.toml | head -1
+  elif [ -f package.json ]; then
+    sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*//p' package.json | head -1
+  else
+    echo "0.0.0"
+  fi
 }
 
-new_version=$(tr -d '[:space:]' < "$version_file")
-printf '%s\n' "$new_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || {
-  echo "ERROR: VERSION must be semantic MAJOR.MINOR.PATCH, got: $new_version" >&2
+old=$(git show "$base_ref:VERSION" 2>/dev/null | tr -d '[:space:]' || echo 0.0.0)
+new=$(current_version)
+case "$new" in
+  [0-9]*.[0-9]*.[0-9]*) ;;
+  *) echo "ERROR: VERSION must be semantic version MAJOR.MINOR.PATCH, got '$new'" >&2; exit 1 ;;
+esac
+if [ "$old" = "$new" ]; then
+  echo "ERROR: version did not advance: $old" >&2
   exit 1
-}
-
-if [ -f jpkg.sexp ]; then
-  manifest_version=$(awk -F'"' '/\(version "/ { print $2; exit }' jpkg.sexp)
-  [ "$manifest_version" = "$new_version" ] || {
-    echo "ERROR: jpkg.sexp version $manifest_version must match VERSION $new_version" >&2
-    exit 1
-  }
 fi
-
-if [ "${FORGEJO_EVENT_NAME:-}" != pull_request ]; then
-  echo "VERSION $new_version is valid"
-  exit 0
-fi
-
-base_ref=${FORGEJO_BASE_REF:?FORGEJO_BASE_REF is required for pull requests}
-old_version=$(git show "origin/$base_ref:$version_file" 2>/dev/null | tr -d '[:space:]' || true)
-old_version=${old_version:-0.0.0}
-printf '%s\n' "$old_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || {
-  echo "ERROR: target branch VERSION is malformed: $old_version" >&2
-  exit 1
-}
-
-if ! awk -F. -v old="$old_version" -v new="$new_version" 'BEGIN {
-  split(old, o, ".")
-  split(new, n, ".")
-  for (i = 1; i <= 3; i++) {
-    if ((n[i] + 0) > (o[i] + 0)) exit 0
-    if ((n[i] + 0) < (o[i] + 0)) exit 1
-  }
-  exit 1
-}'; then
-  echo "ERROR: VERSION must advance beyond $old_version; got $new_version" >&2
-  exit 1
-fi
-
-echo "VERSION advances: $old_version -> $new_version"
+python3 - "$old" "$new" <<'PY2'
+import sys
+old=sys.argv[1]; new=sys.argv[2]
+def parse(v):
+    try: return tuple(int(x) for x in v.split('.')[:3])
+    except Exception: return (0,0,0)
+if parse(new) <= parse(old):
+    print(f"ERROR: version must increase: {old} -> {new}", file=sys.stderr)
+    sys.exit(1)
+print(f"VERSION {new} is valid")
+PY2
diff --git a/.forgejo/workflows/ci.yaml b/.forgejo/workflows/ci.yaml
index 3eb08bd..a8892cb 100644
--- a/.forgejo/workflows/ci.yaml
+++ b/.forgejo/workflows/ci.yaml
@@ -10,59 +10,16 @@ on:
 
 jobs:
   required:
-    runs-on: docker
-    container:
-      image: debian:stable
+    runs-on: freebsd-amd64
     steps:
-      - name: Install system dependencies
+      - name: Check runner tools
         run: |
-          apt-get update
-          DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
-            bash build-essential ca-certificates curl file git libfuse-dev \
-            libgl1-mesa-dev liblz4-dev libncurses-dev libqt5gui5 libssl-dev \
-            libx11-dev make pkg-config tar zlib1g-dev
-      - name: Check out jerboa-coreutils
+          for tool in sh git gmake file; do
+            command -v "$tool"
+          done
+      - name: Check out repository
         uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
         with:
           persist-credentials: false
-      - name: Install Rust
-        run: |
-          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs |
-            sh -s -- -y --profile minimal --default-toolchain 1.94.1
-          echo "$HOME/.cargo/bin" >> "$FORGEJO_PATH"
-      - name: Fetch and build Jerboa
-        run: |
-          mkdir ../jerboa
-          git -C ../jerboa init
-          git -C ../jerboa remote add origin https://git.jerboa.sh/ober/jerboa.git
-          git -C ../jerboa fetch --depth 1 origin 6a5230800b3599147f04bd5b1493bc996047506a
-          git -C ../jerboa checkout --detach FETCH_HEAD
-          test "$(git -C ../jerboa rev-parse 'HEAD^{tree}')" = 6eb4710558222c4e2ef9b076491f7db5af5e3748
-          . "$HOME/.cargo/env"
-          CARGO_BUILD_JOBS=1 make -C ../jerboa jerboa \
-            CHEZ_CONFIGURE_EXTRA=--disable-x11 JERBOA_NATIVE_FEATURES=tls \
-            JERBOA_CC_OPT=-O0
       - name: Build, test, and smoke-check
-        run: |
-          . "$HOME/.cargo/env"
-          PATH="$PWD/../jerboa/dist:$PATH" \
-          JERBUILD="$PWD/../jerboa/dist/jerbuild" \
-            sh .forgejo/ci-required.sh
-      - name: Verify and pack package
-        run: |
-          version=$(cat VERSION)
-          J="$PWD/../jerboa/dist/jerboa"
-          mkdir -p "$HOME/.cache"
-          chmod -R go-w "$HOME/.cache"
-          "$J" pkg verify
-          "$J" pkg policy
-          "$J" pkg build
-          "$J" pkg pack --output "$PWD/jerboa-coreutils-$version.jpkg"
-          "$J" pkg verify "$PWD/jerboa-coreutils-$version.jpkg"
-          "$J" pkg verify --reproduce
-      - name: Upload package
-        uses: https://code.forgejo.org/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3
-        with:
-          name: jerboa-coreutils-package
-          path: jerboa-coreutils-*.jpkg
-          if-no-files-found: error
+        run: sh .forgejo/ci-required.sh
diff --git a/.forgejo/workflows/version-policy.yaml b/.forgejo/workflows/version-policy.yaml
index 4d9f784..54609bf 100644
--- a/.forgejo/workflows/version-policy.yaml
+++ b/.forgejo/workflows/version-policy.yaml
@@ -6,18 +6,14 @@ on:
 
 jobs:
   required:
-    runs-on: docker
-    container:
-      image: debian:stable
+    runs-on: freebsd-amd64
     steps:
-      - name: Install Git
-        run: |
-          apt-get update
-          DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates
+      - name: Check runner tools
+        run: command -v git
       - name: Check out full history
         uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
         with:
           fetch-depth: 0
           persist-credentials: false
       - name: Require semantic version advancement
-        run: sh .forgejo/require-version-bump.sh
+        run: sh .forgejo/require-version-bump.sh origin/master HEAD
diff --git a/VERSION b/VERSION
index 17e51c3..d917d3e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.1
+0.1.2
diff --git a/jpkg.sexp b/jpkg.sexp
index f1e8d36..6ddcdcf 100644
--- a/jpkg.sexp
+++ b/jpkg.sexp
@@ -1,6 +1,6 @@
 (package
   (name "@ober/jerboa-coreutils")
-  (version "0.1.1")
+  (version "0.1.2")
   (description "Core utilities (ls, cat, grep, etc.) for Jerboa")
   (license "MIT")
   (source "https://git.jerboa.sh/ober/jerboa-coreutils")