|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +set -eux |
| 4 | + |
| 5 | +# ---------------------------- |
| 6 | +# Config / Inputs (env-driven) |
| 7 | +# ---------------------------- |
| 8 | +FREEBSD_VER_TAG="${FREEBSD_VERSION:-14_3}" |
| 9 | +SNAP_LABEL="${FREEBSD_SNAPSHOT:-base-20250809}" |
| 10 | +ZPOOL="${ZPOOL:-zroot}" |
| 11 | +JAILS_DS="${JAILS_DS:-${ZPOOL}/z/jails}" |
| 12 | +JAILS_MP="${JAILS_MP:-/z/jails}" |
| 13 | + |
| 14 | +GITHUB_RUN_ID="${GITHUB_RUN_ID:-localrun}" |
| 15 | +GITHUB_RUN_ATTEMPT="${GITHUB_RUN_ATTEMPT:-1}" |
| 16 | + |
| 17 | +BUILD_MODE="${BUILD_MODE:-Release}" |
| 18 | +CMAKE_ARGS="${CMAKE_ARGS:-}" |
| 19 | + |
| 20 | +BASE_DS="${JAILS_DS}/${FREEBSD_VER_TAG}" |
| 21 | +RUN_DS="${JAILS_DS}/ci-${FREEBSD_VER_TAG}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" |
| 22 | +RUN_MP="${JAILS_MP}/ci-${FREEBSD_VER_TAG}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" |
| 23 | +JAIL="ci_${FREEBSD_VER_TAG}_${GITHUB_RUN_ID}_${GITHUB_RUN_ATTEMPT}" |
| 24 | + |
| 25 | +# ---------------------------- |
| 26 | +# Helpers |
| 27 | +# ---------------------------- |
| 28 | +log() { printf '%s %s\n' "[freebsd-jail]" "$*" >&2; } |
| 29 | + |
| 30 | +# Stop a jail if it's running; kill any lingering processes by JID if needed |
| 31 | +stop_stale_jail() { |
| 32 | + jname="$1" |
| 33 | + if jls -j "$jname" >/dev/null 2>&1; then |
| 34 | + log "Stopping stale jail: $jname" |
| 35 | + if ! sudo jail -r "$jname" 2>/dev/null; then |
| 36 | + JID="$(jls -j "$jname" jid -h 2>/dev/null || true)" |
| 37 | + if [ -n "$JID" ]; then |
| 38 | + sudo killall -j "$JID" -TERM 2>/dev/null || true |
| 39 | + sleep 1 |
| 40 | + sudo killall -j "$JID" -KILL 2>/dev/null || true |
| 41 | + fi |
| 42 | + sudo jail -r "$jname" 2>/dev/null || true |
| 43 | + fi |
| 44 | + fi |
| 45 | +} |
| 46 | + |
| 47 | +# Unmount everything under a mountpoint (deepest-first) |
| 48 | +umount_tree() { |
| 49 | + rootmp="$1" |
| 50 | + # List mounted paths under $rootmp, deepest-first |
| 51 | + # shellcheck disable=SC2016 |
| 52 | + for m in $(mount | awk -v p="$rootmp" '$3 ~ ("^"p) { print length, $3 }' | sort -rn | cut -d" " -f2-); do |
| 53 | + sudo umount -f "$m" 2>/dev/null || true |
| 54 | + done |
| 55 | +} |
| 56 | + |
| 57 | +# Destroy a cloned dataset (after umount) |
| 58 | +destroy_dataset() { |
| 59 | + ds="$1" |
| 60 | + if sudo zfs list -H "$ds" >/dev/null 2>&1; then |
| 61 | + mp="$(sudo zfs get -H -o value mountpoint "$ds" 2>/dev/null || echo "")" |
| 62 | + if [ -n "$mp" ] && [ "$mp" != "-" ]; then |
| 63 | + umount_tree "$mp" |
| 64 | + fi |
| 65 | + log "Destroying dataset: $ds" |
| 66 | + sudo zfs destroy -r "$ds" |
| 67 | + fi |
| 68 | +} |
| 69 | + |
| 70 | +# Remove stale jail + dataset that match our run IDs |
| 71 | +cleanup_stale_before_start() { |
| 72 | + stop_stale_jail "$JAIL" |
| 73 | + destroy_dataset "$RUN_DS" |
| 74 | +} |
| 75 | + |
| 76 | +# Full cleanup for traps: stop jail, unmount, destroy dataset |
| 77 | +cleanup_on_exit() { |
| 78 | + # Be idempotent; ignore errors |
| 79 | + stop_stale_jail "$JAIL" |
| 80 | + destroy_dataset "$RUN_DS" |
| 81 | +} |
| 82 | + |
| 83 | +# Generate an env file inside jail that exports safe GitHub env vars |
| 84 | +write_gha_env() { |
| 85 | + out="$1" |
| 86 | + # Allow common CI vars; deny obvious secrets |
| 87 | + # Adjust allow/deny as needed for your org. |
| 88 | + ALLOW='GITHUB_* RUNNER_* CI ACTIONS_* INPUT_* MATRIX_* BUILD_MODE CMAKE_ARGS' |
| 89 | + DENY='*TOKEN* *SECRET* *PASSWORD* *PASS* *KEY* *CERT* AWS_* AZURE_* GCP_*' |
| 90 | + |
| 91 | + # Function to test name against allow/deny (POSIX sh compatible inline) |
| 92 | + is_allowed() { |
| 93 | + name="$1" |
| 94 | + case "$name" in |
| 95 | + GITHUB_*|RUNNER_*|CI|ACTIONS_*|INPUT_*|MATRIX_*|BUILD_MODE|CMAKE_ARGS) : ;; |
| 96 | + *) return 1 ;; |
| 97 | + esac |
| 98 | + case "$name" in |
| 99 | + *TOKEN*|*SECRET*|*PASSWORD*|*PASS*|*KEY*|*CERT*|AWS_*|AZURE_*|GCP_* ) return 1 ;; |
| 100 | + esac |
| 101 | + return 0 |
| 102 | + } |
| 103 | + |
| 104 | + tmp="$(mktemp)" |
| 105 | + { |
| 106 | + echo "# Autogenerated; sourced by /root/build.sh" |
| 107 | + # Ensure our two common knobs are present even if not in env |
| 108 | + printf "export BUILD_MODE='%s'\n" "$BUILD_MODE" |
| 109 | + # Escape single quotes in CMAKE_ARGS |
| 110 | + esc_ca=$(printf "%s" "$CMAKE_ARGS" | sed "s/'/'\"'\"'/g") |
| 111 | + printf "export CMAKE_ARGS='%s'\n" "$esc_ca" |
| 112 | + |
| 113 | + env | while IFS='=' read -r name value; do |
| 114 | + if is_allowed "$name"; then |
| 115 | + esc=$(printf "%s" "$value" | sed "s/'/'\"'\"'/g") |
| 116 | + printf "export %s='%s'\n" "$name" "$esc" |
| 117 | + fi |
| 118 | + done |
| 119 | + } > "$tmp" |
| 120 | + sudo mkdir -p "$(dirname "$out")" |
| 121 | + sudo cp "$tmp" "$out" |
| 122 | + sudo chmod 0644 "$out" |
| 123 | + rm -f "$tmp" |
| 124 | +} |
| 125 | + |
| 126 | +# ---------------------------- |
| 127 | +# Trap: ensure cleanup on any exit |
| 128 | +# ---------------------------- |
| 129 | +trap cleanup_on_exit EXIT INT TERM HUP |
| 130 | + |
| 131 | +# ---------------------------- |
| 132 | +# Prep: nuke stale, then clone snapshot |
| 133 | +# ---------------------------- |
| 134 | +cleanup_stale_before_start |
| 135 | + |
| 136 | +log "Cloning ${BASE_DS}@${SNAP_LABEL} -> ${RUN_DS}" |
| 137 | +sudo zfs clone "${BASE_DS}@${SNAP_LABEL}" "${RUN_DS}" |
| 138 | + |
| 139 | +# ---------------------------- |
| 140 | +# Bootstrapping jail root: DNS + mounts |
| 141 | +# ---------------------------- |
| 142 | +# DNS for networking inside jail |
| 143 | +sudo mkdir -p "${RUN_MP}/etc" |
| 144 | +sudo cp /etc/resolv.conf "${RUN_MP}/etc/resolv.conf" |
| 145 | +[ -f /etc/hosts ] && sudo cp /etc/hosts "${RUN_MP}/etc/hosts" |
| 146 | +[ -f /etc/nsswitch.conf ] && sudo cp /etc/nsswitch.conf "${RUN_MP}/etc/nsswitch.conf" |
| 147 | + |
| 148 | +# Core mounts |
| 149 | +sudo mount -t devfs devfs "${RUN_MP}/dev" |
| 150 | +sudo mount -t fdescfs fdesc "${RUN_MP}/dev/fd" |
| 151 | +sudo mount -t procfs proc "${RUN_MP}/proc" |
| 152 | +sudo mount -t tmpfs tmpfs "${RUN_MP}/tmp" |
| 153 | + |
| 154 | +# Workspace / temp / ccache / NFS from host |
| 155 | +RUNNER_WORKSPACE="${RUNNER_WORKSPACE:-$PWD}" |
| 156 | +RUNNER_TEMP="${RUNNER_TEMP:-/tmp/runner_temp}" |
| 157 | +mkdir -p "${RUNNER_TEMP}" "${HOME}/.ccache" |
| 158 | + |
| 159 | +sudo mkdir -p "${RUN_MP}/work" "${RUN_MP}/runner_temp" "${RUN_MP}/ccache" "${RUN_MP}/mnt/opensource" |
| 160 | +sudo mount -t nullfs -o rw "${RUNNER_WORKSPACE}" "${RUN_MP}/work" |
| 161 | +sudo mount -t nullfs -o rw "${RUNNER_TEMP}" "${RUN_MP}/runner_temp" |
| 162 | +sudo mount -t nullfs -o rw "${HOME}/github-ccache" "${RUN_MP}/ccache" |
| 163 | +sudo mount -t nullfs -o rw "/mnt/opensource" "${RUN_MP}/mnt/opensource" |
| 164 | + |
| 165 | +# ---------------------------- |
| 166 | +# Start jail (FUSE enabled) |
| 167 | +# ---------------------------- |
| 168 | +log "Starting jail: ${JAIL}" |
| 169 | +sudo jail -c name="${JAIL}" host.hostname="${JAIL}" \ |
| 170 | + path="${RUN_MP}" persist \ |
| 171 | + mount.devfs devfs_ruleset=5 enforce_statfs=1 \ |
| 172 | + allow.mount allow.mount.fusefs \ |
| 173 | + allow.raw_sockets \ |
| 174 | + ip4=inherit ip6=inherit |
| 175 | + |
| 176 | +# ---------------------------- |
| 177 | +# Inject env + build script, then run it |
| 178 | +# ---------------------------- |
| 179 | +write_gha_env "${RUN_MP}/root/gha_env.sh" |
| 180 | + |
| 181 | +sudo cp ".docker/build-freebsd.sh" "${RUN_MP}/root/build.sh" |
| 182 | +sudo chmod +x "${RUN_MP}/root/build.sh" |
| 183 | + |
| 184 | +# Run inside the jail with env sourced |
| 185 | +log "Executing build inside jail…" |
| 186 | +sudo jexec "${JAIL}" /bin/sh -lc '. /root/gha_env.sh; exec /root/build.sh' |
| 187 | + |
| 188 | +log "Build finished successfully." |
| 189 | +# Cleanup happens automatically via trap on EXIT. |
0 commit comments