Use FreeBSD Forgejo CI runner

ober

ea202f82489f1ccf2e83f94cb0802cb404bc7f0d

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 b3c5cf6..a8892cb 100644
--- a/.forgejo/workflows/ci.yaml
+++ b/.forgejo/workflows/ci.yaml
@@ -10,47 +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
+          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: Build, test, and smoke-check
-        run: |
-          . "$HOME/.cargo/env"
-          CARGO_BUILD_JOBS=1 CHEZ_CONFIGURE_EXTRA=--disable-x11 \
-            JERBOA_NATIVE_FEATURES=tls JERBOA_CC_OPT=-O0 \
-            sh .forgejo/ci-required.sh
-      - name: Verify and pack package
-        run: |
-          version=$(cat VERSION)
-          J="$PWD/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-$version.jpkg"
-          "$J" pkg verify "$PWD/jerboa-$version.jpkg"
-          "$J" pkg verify --reproduce
-      - name: Upload package
-        uses: https://code.forgejo.org/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3
-        with:
-          name: jerboa-package
-          path: jerboa-*.jpkg
-          if-no-files-found: error
+        run: sh .forgejo/ci-required.sh
diff --git a/.forgejo/workflows/release.yaml b/.forgejo/workflows/release.yaml
index f3bdd06..01976a9 100644
--- a/.forgejo/workflows/release.yaml
+++ b/.forgejo/workflows/release.yaml
@@ -6,139 +6,42 @@ on:
   workflow_dispatch:
 
 jobs:
-  linux-amd64:
-    runs-on: docker
-    container:
-      image: debian:stable
-    steps:
-      - run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends bash build-essential ca-certificates curl file git tar
-      - uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
-        with:
-          fetch-depth: 0
-          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: Build, package, and run
-        run: |
-          . "$HOME/.cargo/env"
-          version="v$(cat VERSION)"
-          test "${FORGEJO_REF#refs/tags/}" = "$version"
-          make jerboa
-          make release-artifact RELEASE_VERSION="$version" RELEASE_TARGET=linux-amd64
-          archive=$(find dist/release -name '*linux-amd64.tar.gz' -type f)
-          tmp=$(mktemp -d)
-          tar -xzf "$archive" -C "$tmp"
-          binary=$(find "$tmp" -path '*/bin/jerboa' -type f)
-          "$binary" version
-      - uses: https://code.forgejo.org/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3
-        with:
-          name: release-linux-amd64
-          path: |
-            dist/release/*.tar.gz
-            dist/release/*.tar.gz.sha256
-          if-no-files-found: error
-
-  linux-arm64:
-    runs-on: docker-arm64
-    container:
-      image: debian:stable
-    steps:
-      - run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends bash build-essential ca-certificates curl file git tar
-      - uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
-        with:
-          fetch-depth: 0
-          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: Build, package, and run
-        run: |
-          . "$HOME/.cargo/env"
-          version="v$(cat VERSION)"
-          test "${FORGEJO_REF#refs/tags/}" = "$version"
-          make jerboa
-          make release-artifact RELEASE_VERSION="$version" RELEASE_TARGET=linux-arm64
-          archive=$(find dist/release -name '*linux-arm64.tar.gz' -type f)
-          tmp=$(mktemp -d)
-          tar -xzf "$archive" -C "$tmp"
-          binary=$(find "$tmp" -path '*/bin/jerboa' -type f)
-          "$binary" version
-      - uses: https://code.forgejo.org/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3
-        with:
-          name: release-linux-arm64
-          path: |
-            dist/release/*.tar.gz
-            dist/release/*.tar.gz.sha256
-          if-no-files-found: error
-
   freebsd-amd64:
     runs-on: freebsd-amd64
     steps:
-      - run: pkg install -y bash ca_root_nss curl git gmake
+      - name: Check runner tools
+        run: |
+          for tool in sh git gmake tar ssh-keygen base64; do
+            command -v "$tool"
+          done
       - uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
         with:
           fetch-depth: 0
           persist-credentials: false
-      - name: Build, package, and run
+      - name: Build, package, run, sign, and publish Forgejo release
+        env:
+          FORGEJO_TOKEN: ${{ forgejo.token }}
+          SIGNING_KEY_B64: ${{ secrets.JERBOA_RELEASE_SSH_SIGNING_KEY_B64 }}
         run: |
           version="v$(cat VERSION)"
           test "${FORGEJO_REF#refs/tags/}" = "$version"
+
           gmake jerboa
           gmake release-artifact RELEASE_VERSION="$version" RELEASE_TARGET=freebsd-amd64
+
           archive=$(find dist/release -name '*freebsd-amd64.tar.gz' -type f)
           tmp=$(mktemp -d)
           tar -xzf "$archive" -C "$tmp"
           binary=$(find "$tmp" -path '*/bin/jerboa' -type f)
           "$binary" version
-      - uses: https://code.forgejo.org/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3
-        with:
-          name: release-freebsd-amd64
-          path: |
-            dist/release/*.tar.gz
-            dist/release/*.tar.gz.sha256
-          if-no-files-found: error
 
-  publish:
-    needs: [linux-amd64, linux-arm64, freebsd-amd64]
-    runs-on: docker
-    container:
-      image: debian:stable
-    steps:
-      - run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl git jq openssh-client
-      - uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
-        with:
-          fetch-depth: 0
-          persist-credentials: false
-      - uses: https://code.forgejo.org/actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3
-        with:
-          name: release-linux-amd64
-          path: dist/download/linux-amd64
-      - uses: https://code.forgejo.org/actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3
-        with:
-          name: release-linux-arm64
-          path: dist/download/linux-arm64
-      - uses: https://code.forgejo.org/actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3
-        with:
-          name: release-freebsd-amd64
-          path: dist/download/freebsd-amd64
-      - name: Sign, verify, and publish Forgejo release
-        env:
-          FORGEJO_TOKEN: ${{ forgejo.token }}
-          SIGNING_KEY_B64: ${{ secrets.JERBOA_RELEASE_SSH_SIGNING_KEY_B64 }}
-        run: |
-          mkdir -p dist/release
-          find dist/download -type f -exec cp {} dist/release/ \;
           test -n "$SIGNING_KEY_B64" || {
             echo "ERROR: JERBOA_RELEASE_SSH_SIGNING_KEY_B64 is required" >&2
             exit 1
           }
           key=$(mktemp)
           allowed=$(mktemp)
+          trap 'rm -f "$key" "$allowed"' EXIT HUP INT TERM
           printf '%s' "$SIGNING_KEY_B64" | base64 -d > "$key"
           chmod 0600 "$key"
           printf 'jerboa %s\n' "$(ssh-keygen -y -f "$key")" > "$allowed"
@@ -146,5 +49,5 @@ jobs:
           export JERBOA_RELEASE_SSH_SIGNING_KEY="$key"
           export JERBOA_RELEASE_SSH_ALLOWED_SIGNERS="$allowed"
           export JERBOA_RELEASE_SSH_SIGNER_IDENTITY=jerboa
-          make sign-release-artifacts
-          make release-upload RELEASE_VERSION="v$(cat VERSION)"
+          gmake sign-release-artifacts
+          gmake release-upload RELEASE_VERSION="$version"
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 13dead7..d3b5ba4 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.2.10
+0.2.11
diff --git a/jpkg.sexp b/jpkg.sexp
index 63a5c4e..9b43737 100644
--- a/jpkg.sexp
+++ b/jpkg.sexp
@@ -1,6 +1,6 @@
 (package
   (name "@ober/jerboa")
-  (version "0.2.10")
+  (version "0.2.11")
   (description "")
   (license "UNLICENSED")
   (jerboa ">=0.2.0")