Runner pool classes + the git host script: a bounded build-remote run takes its pool cores as class release (v5 when its label names a v5 gate or kit), writes the class into its wait line so lease-pool sweeps yield to it, and yields itself only to a higher class (slot self-test green on build-2); infra/build-server/forgejo.sh = the Forgejo stand-up on build-1 as run at 20:29 BST (docker, Caddy block, ufw 2222, app.ini with generated secrets kept on the box)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 19:56:50 +00:00
parent e48a512c3f
commit 0a10ca5e2a
2 changed files with 143 additions and 8 deletions

View file

@ -0,0 +1,126 @@
#!/usr/bin/env bash
# The git host (main, 7 October 2026 20:29 BST; GitHub suspended): Forgejo 9 under docker on build-1, data under /srv/git (config
# app.ini written once with generated secrets, kept on the box only), ssh on 2222 public, http 3000 on loopback behind Caddy at
# git.igneum.network (ACME), deSEC A record by hand (dns.sh pattern). Then, by hand through the API as the owner user igneum-labs
# (credentials in ~/.config/igneum/forgejo-admin on the Mac): org igneum-network, private repos igneum and spec, the Mac key on the
# owner; registration off, anonymous read off, push-to-create off. Nightly dump to build-2 by /usr/local/bin/forgejo-backup.sh
# (forgejo-backup.timer 03:30 UTC). Runs as root on the box: scp it over and run it. Idempotent.
set -euo pipefail
mkdir -p /srv/git/data /srv/git/config
chown -R 1000:1000 /srv/git
if [ ! -s /srv/git/config/app.ini ]; then
SECRET=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
JWT=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
LFSJWT=$(head -c 48 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
cat > /srv/git/config/app.ini <<INI
APP_NAME = Igneum
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /data/gitea
[server]
PROTOCOL = http
DOMAIN = git.igneum.network
ROOT_URL = https://git.igneum.network/
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
SSH_DOMAIN = git.igneum.network
SSH_PORT = 2222
SSH_LISTEN_PORT = 22
START_SSH_SERVER = false
DISABLE_SSH = false
LFS_START_SERVER = true
LFS_JWT_SECRET = $LFSJWT
OFFLINE_MODE = true
LANDING_PAGE = login
[database]
DB_TYPE = sqlite3
PATH = /data/gitea/forgejo.db
[repository]
ROOT = /data/git/repositories
DEFAULT_PRIVATE = private
FORCE_PRIVATE = true
ENABLE_PUSH_CREATE_USER = false
ENABLE_PUSH_CREATE_ORG = false
DEFAULT_BRANCH = master
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
ENABLE_NOTIFY_MAIL = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = false
[security]
INSTALL_LOCK = true
SECRET_KEY = $SECRET
INTERNAL_TOKEN = $JWT
PASSWORD_HASH_ALGO = argon2
MIN_PASSWORD_LENGTH = 16
[oauth2]
ENABLED = false
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false
[mailer]
ENABLED = false
[session]
PROVIDER = file
COOKIE_SECURE = true
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /data/gitea/log
[other]
SHOW_FOOTER_VERSION = false
[actions]
ENABLED = false
[packages]
ENABLED = false
[federation]
ENABLED = false
INI
chown 1000:1000 /srv/git/config/app.ini; chmod 600 /srv/git/config/app.ini
fi
if ! docker ps -a --format '{{.Names}}' | grep -qx forgejo; then
docker run -d --name forgejo --restart unless-stopped \
-e USER_UID=1000 -e USER_GID=1000 -e FORGEJO_CUSTOM=/data/gitea \
-v /srv/git/data:/data -v /srv/git/config/app.ini:/data/gitea/conf/app.ini \
-v /etc/timezone:/etc/timezone:ro -v /etc/localtime:/etc/localtime:ro \
-p 127.0.0.1:3000:3000 -p 2222:22 \
--cpus 4 --memory 8g \
codeberg.org/forgejo/forgejo:9 >/dev/null
fi
ufw allow 2222/tcp >/dev/null
if ! grep -q "^git.igneum.network" /etc/caddy/Caddyfile; then
cat >> /etc/caddy/Caddyfile <<'CADDY'
# the git host (Forgejo in docker, infra/build-server/forgejo.sh): everything proxied, nothing cached
git.igneum.network {
tls {
issuer acme
}
reverse_proxy 127.0.0.1:3000
request_body {
max_size 2GB
}
}
CADDY
caddy fmt --overwrite /etc/caddy/Caddyfile >/dev/null 2>&1 || true
systemctl reload caddy
fi
for i in $(seq 1 30); do curl -fsS -o /dev/null http://127.0.0.1:3000/ 2>/dev/null && break; sleep 2; done
echo "forgejo container: $(docker ps --format '{{.Names}} {{.Status}}' | grep forgejo)"
echo "http 3000: $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:3000/)"
echo "ssh 2222: $(timeout 5 bash -c 'exec 3<>/dev/tcp/127.0.0.1/2222; head -c 40 <&3' 2>/dev/null | tr -d '\r\n' | cut -c1-40)"

View file

@ -480,18 +480,27 @@ BR_RUN_LOG="$RUN_LOG_DIR/$BR_HOST-$BR_T0-$BR_PID.log"; export BR_RUN_LOG
# (nice 0, a gate or a release build) is outside the pool and keeps the full set.
ncpu=$(nproc); cores_str="0-$((ncpu - 1))"
pool_fds=(); pool_cores=()
# the pool's priority classes (lease.sh, main 7 Oct 2026 20:37 BST: release > v5 > measure > adv): a bounded build or suite is class
# release (rank 0) unless its label names a "v5 gate" or "v5 kit" (class v5, rank 1); the wait file carries "class <name>/<rank>:" so
# `lease pool` sweeps (class adv) yield to it, and it yields while a higher class waits (nothing outranks release)
pool_class() { case "$BR_LABEL" in *"v5 gate"*|*"v5 kit"*) echo "v5/1" ;; *) echo "release/0" ;; esac; }
pool_take() { # <want> <min>: lease up to <want> free pool cores, at least <min>, waiting up to 2 h; sets pool_cores/pool_fds
local want="$1" min="$2" t0 c fd taken
local want="$1" min="$2" t0 c fd taken cls rank higher
cls=$(pool_class); rank=${cls#*/}
t0=$(date +%s)
while :; do
taken=0; pool_fds=(); pool_cores=()
for ((c = ${BR_POOL_RESERVE:-8}; c < ncpu && taken < want; c++)); do
exec {fd}>>"$SLOTS_DIR/core-$c"
if flock -n "$fd"; then pool_fds+=("$fd"); pool_cores+=("$c"); taken=$((taken + 1)); else exec {fd}>&-; fi
done
if [ "$taken" -ge "$min" ]; then return 0; fi
for fd in "${pool_fds[@]}"; do exec {fd}>&-; done; pool_fds=(); pool_cores=()
[ -f "$waitfile" ] || { echo "build-remote: the bounded pool has $taken free core(s) (want $want, at least $min): waiting for the pool" >&2; holder_line "$(( $(date +%s) - BR_T0 ))" > "$waitfile"; }
higher=$(cat "$SLOTS_DIR"/wait-* 2>/dev/null | grep -v "^pid $BR_PID " | sed -n 's/.* class [a-z0-9]*\/\([0-9]\):.*/\1/p' | awk -v r="$rank" '$1 < r' | head -1)
if [ -z "$higher" ]; then
for ((c = ${BR_POOL_RESERVE:-8}; c < ncpu && taken < want; c++)); do
exec {fd}>>"$SLOTS_DIR/core-$c"
if flock -n "$fd"; then pool_fds+=("$fd"); pool_cores+=("$c"); taken=$((taken + 1)); else exec {fd}>&-; fi
done
if [ "$taken" -ge "$min" ]; then return 0; fi
for fd in "${pool_fds[@]}"; do exec {fd}>&-; done; pool_fds=(); pool_cores=()
fi
[ -f "$waitfile" ] || { echo "build-remote: the bounded pool has $taken free core(s) (want $want, at least $min, class ${cls%/*}): waiting for the pool" >&2; }
printf '%s class %s: pool\n' "$(holder_line "$(( $(date +%s) - BR_T0 ))")" "$cls" > "$waitfile"
[ $(( $(date +%s) - t0 )) -lt 7200 ] || give_up "the bounded pool"
sleep 10
done