- provision.sh step_runner: actions/runner 2.338.0 (sha256 checked) at /opt/actions-runner under a dedicated user `runner`
(no sudo, not in build's group), rustup 1.99.0 pinned with both targets, sccache against /srv/sccache in READ_ONLY mode
on its own server port, Node 22 and mingw from the system, GitHub's svc.sh unit with a Nice 10 drop-in; registered on
igneum-network/igneum as igneum-build-1 (labels self-hosted, linux, x64, igneum-build-1) through
infra/build-server/runner/register.sh (gh as igneum-labs, the token on ssh stdin, never logged). Idempotent after
the env files moved behind svc.sh install (its env.sh rewrites them). libicu74 and python3-numpy added to APT.
- main's slots ruling: SLOTS default 2; remote-run.sh sets CARGO_BUILD_JOBS 90 when it holds the only taken slot and 45
when both are held, BR_MEASURE=1 takes the `measure` file exclusively and excludes builds (builds hold it shared),
lock files open in append mode (the old `exec {fd}>` truncated a busy slot's holder line on every probe), env
IGNEUM_BUILD_SLOTS_DIR and IGNEUM_BUILD_LOG_DIR win over the profile, `--self-test-slots` with five cases (the old
script fails it with JOBS=none); build-remote.sh and cross-remote.sh pass -j only when --jobs is given.
- infra/build-server/prover/cpu-trial.sh: the SP1 CPU prover on one fixture shard under the measure hold with a VmHWM
poller; 6 Oct 2026 run: core 34.2 s, compressed 85.9 s, peak RSS 28.2 GB on 96 threads, so no standing CPU prover.
- docs/plans/ci-self-hosted.md: the proposed runs-on change for ci.yml behind the repository variable IGNEUM_CI_RUNNER
(GitHub-hosted is the fallback), and why windows.yml cannot move to a Linux box. Workflows untouched.
- docs/plans/build-server.md section 7.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
119 lines
7.9 KiB
Bash
Executable file
119 lines
7.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Build on igneum-build-1 instead of this Mac. Run from any crate directory of any worktree on the Mac (a fork worktree under
|
|
# vendor/, igneum-pow, app/igneum-app, proving/igneum-prove): the sources go to /srv/builds/<worktree>/<same relative path> on
|
|
# the box (HEAD through the bare mirror, uncommitted changes by rsync, changed files re-stamped with touch in lib.sh's
|
|
# bs_overlay_dir: the copied-sources rule), the cargo command runs there
|
|
# with sccache and -j 90 under one of the box's build slots, and the artefacts come back into target-remote/ here.
|
|
#
|
|
# tools/build-remote.sh the default command for this crate (below)
|
|
# tools/build-remote.sh -- build --release -p kaspad --features kaspad/igneum-pow
|
|
# tools/build-remote.sh -- test --release -p kaspa-consensus-core --lib
|
|
# tools/build-remote.sh --artefacts "target/release/igneumd" --out /tmp/x -- build --release -p kaspad --features kaspad/igneum-pow
|
|
# tools/build-remote.sh --jobs 48 -- check
|
|
# tools/build-remote.sh --target-dir target-exp -- build --release another persistent target dir on the box
|
|
# tools/build-remote.sh --no-fetch -- clippy --all-targets nothing comes back (tests, check, clippy)
|
|
#
|
|
# Defaults by crate: a fork worktree builds `-p kaspad -p igneum-miner --features kaspad/igneum-pow` in release and fetches
|
|
# target/release/{igneumd,igneum-miner} (what packaging/README-ship.md and infra/cross expect); app/igneum-app builds release
|
|
# and fetches igneum-app, igneum-ota-sign, igneum-prove-verify; proving/igneum-prove fetches igneum-prove-host and
|
|
# igneum-prove-export; any other crate builds release and fetches nothing unless --artefacts names files.
|
|
#
|
|
# Artefacts land in <crate>/target-remote/<path without the leading target/> (target-remote/release/igneumd), NEVER in
|
|
# target/: the box builds x86_64 Linux ELF binaries (glibc 2.39, Ubuntu 24.04), which do not run on this Mac. Each one is
|
|
# reported with size and sha256. For Windows exes use tools/cross-remote.sh.
|
|
#
|
|
# Slots: the box has its own slot files (/srv/builds/_locks/build-<k>, count in /srv/builds/_locks/slots, default 1); this
|
|
# script takes one of THOSE, never the Mac's ~/.config/igneum/build-slots or tools/lock/with-lock.sh, so a remote build does
|
|
# not hold a Mac slot. A build waits up to 2 h for a remote slot, as with-lock.sh does.
|
|
#
|
|
# Needs: ~/.config/igneum/build-server (build@<ip>, written by infra/build-server/run-from-mac.sh), ~/.ssh/igneum_ed25519,
|
|
# the same rustc version on both sides (refused otherwise; IGNEUM_TOOLCHAIN_MISMATCH=ok overrides). IGNEUM_AGENT names the
|
|
# agent in the slot-file label and the box's JSONL log (/srv/builds/_log/builds.jsonl); default the worktree name.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck disable=SC2034 # shared with the scripts that source lib.sh
|
|
BS_TOOL=build-remote
|
|
# shellcheck source=../infra/build-server/lib.sh
|
|
. "$HERE/../infra/build-server/lib.sh"
|
|
|
|
JOBS="${JOBS:-}"; # empty = the box decides: 90 alone, 45 beside another slot holder (remote-run.sh, main's ruling 6 Oct 2026) OUT=""; ARTEFACTS=""; TARGET_DIR="target"; FETCH=1; CARGO_ARGS=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--jobs) JOBS="$2"; shift 2 ;;
|
|
--out) OUT="$2"; shift 2 ;;
|
|
--artefacts) ARTEFACTS="$2"; ARTEFACTS_SET=1; shift 2 ;;
|
|
--target-dir) TARGET_DIR="$2"; shift 2 ;;
|
|
--no-fetch) FETCH=0; shift ;;
|
|
--) shift; CARGO_ARGS=("$@"); break ;;
|
|
-h|--help) sed -n '2,32p' "$0"; exit 0 ;;
|
|
*) CARGO_ARGS=("$@"); break ;;
|
|
esac
|
|
done
|
|
[ "${CARGO_ARGS[0]:-}" = cargo ] && CARGO_ARGS=("${CARGO_ARGS[@]:1}")
|
|
CARGO_ARGS_GIVEN=""; [ "${#CARGO_ARGS[@]}" -gt 0 ] && CARGO_ARGS_GIVEN=1
|
|
|
|
bs_host
|
|
bs_context
|
|
|
|
# defaults per crate
|
|
case "$BS_KIND:$BS_CRATE_REL" in
|
|
node:*)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release -p kaspad -p igneum-miner --features kaspad/igneum-pow)
|
|
[ -n "$ARTEFACTS" ] || ARTEFACTS="$TARGET_DIR/release/igneumd $TARGET_DIR/release/igneum-miner" ;;
|
|
repo:app/igneum-app)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release)
|
|
[ -n "$ARTEFACTS" ] || ARTEFACTS="$TARGET_DIR/release/igneum-app $TARGET_DIR/release/igneum-ota-sign $TARGET_DIR/release/igneum-prove-verify" ;;
|
|
repo:proving/igneum-prove)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release)
|
|
[ -n "$ARTEFACTS" ] || ARTEFACTS="$TARGET_DIR/release/igneum-prove-host $TARGET_DIR/release/igneum-prove-export" ;;
|
|
*)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release) ;;
|
|
esac
|
|
case "${CARGO_ARGS[0]}" in build) ;; *) [ -n "${ARTEFACTS_SET:-}" ] || { FETCH=0; ARTEFACTS=""; } ;; esac # test, check, clippy: nothing to fetch
|
|
[ -n "$OUT" ] || OUT="$BS_CRATE/target-remote"
|
|
|
|
bs_log "$BS_KIND crate $BS_WT/$BS_CRATE_REL at $BS_SHA ($BS_BRANCH) -> $BS_HOST:$BS_REMOTE_CRATE; cargo ${CARGO_ARGS[*]} -j ${JOBS:-auto}; target dir $TARGET_DIR"
|
|
bs_toolchain_check
|
|
t_sync0=$(date +%s)
|
|
bs_sync_sources
|
|
bs_log "sources in place after $(( $(date +%s) - t_sync0 )) s"
|
|
|
|
# the fork's kaspa-build-info embeds the commit through a build script that, having once found no branch, emits no
|
|
# rerun-if-changed and is never run again by cargo (release-0.3.11 plan: `cargo clean -p kaspa-build-info` first); so when
|
|
# the commit the box builds differs from the last one built in this target dir, that one crate is cleaned (a relink, seconds)
|
|
pre=""
|
|
if [ "$BS_KIND" = node ] && [ "${CARGO_ARGS[0]}" = build ]; then
|
|
pre="[ \"\$(cat '.build-remote-sha-$TARGET_DIR' 2>/dev/null)\" = '$BS_SHA' ] || CARGO_TARGET_DIR='$TARGET_DIR' cargo clean -q --release -p kaspa-build-info 2>/dev/null; "
|
|
fi
|
|
cmd="${pre}CARGO_TARGET_DIR='$TARGET_DIR' cargo $(printf '%q ' "${CARGO_ARGS[@]}")${JOBS:+-j $JOBS} 2>&1 | tee -a '$BS_REMOTE_WT/.build-remote.log'; rc=\${PIPESTATUS[0]}; [ \$rc = 0 ] && echo '$BS_SHA' > '.build-remote-sha-$TARGET_DIR'; ( exit \$rc )" # a subshell exit: the runner reads \$? and still prints its RESULT line
|
|
label="$BS_WT/$BS_CRATE_REL cargo ${CARGO_ARGS[*]}"
|
|
BR_KIND=$(bs_kind build-remote "${CARGO_ARGS[0]}"); BR_COMMAND="cargo ${CARGO_ARGS[*]}"; BR_TARGET=x86_64-unknown-linux-gnu
|
|
for ((i = 0; i < ${#CARGO_ARGS[@]}; i++)); do [ "${CARGO_ARGS[$i]}" = --target ] && BR_TARGET="${CARGO_ARGS[$((i + 1))]:-}"; done
|
|
BR_ARTEFACTS=""; [ "$FETCH" = 1 ] && BR_ARTEFACTS="$ARTEFACTS"
|
|
export BR_KIND BR_COMMAND BR_TARGET BR_ARTEFACTS
|
|
t0=$(date +%s)
|
|
set +e
|
|
bs_remote_run "$BS_REMOTE_CRATE" "$label" "$cmd" 2>&1 | tee "/tmp/build-remote-$$.log"
|
|
rc=${PIPESTATUS[0]}
|
|
set -e
|
|
secs=$(( $(date +%s) - t0 ))
|
|
result=$(grep -m1 '^build-remote: RESULT' "/tmp/build-remote-$$.log" || true); rm -f "/tmp/build-remote-$$.log"
|
|
if [ "$rc" != 0 ]; then bs_die "remote cargo failed (rc $rc) after $(bs_fmt_secs "$secs"); $result"; fi
|
|
bs_log "remote cargo ${CARGO_ARGS[0]} done in $(bs_fmt_secs "$secs") wall from the Mac; ${result#build-remote: RESULT }"
|
|
|
|
if [ "$FETCH" = 1 ] && [ -n "$ARTEFACTS" ]; then
|
|
mkdir -p "$OUT"
|
|
for a in $ARTEFACTS; do
|
|
rel="${a#"$TARGET_DIR"/}"; dest="$OUT/$rel"; mkdir -p "$(dirname "$dest")"
|
|
if ! bs_rsync -p "$BS_HOST:$BS_REMOTE_CRATE/$a" "$dest" 2>/dev/null; then
|
|
# a default artefact the caller's own `-p` selection did not build is noted, not fatal (6 Oct 2026: `-p igneum-prove-host`
|
|
# alone left no igneum-prove-export); a missing artefact the caller NAMED with --artefacts is fatal
|
|
if [ -n "${ARTEFACTS_SET:-}" ] || [ -z "${CARGO_ARGS_GIVEN:-}" ]; then bs_die "no $a on the box after the build"; fi
|
|
bs_log "no $a on the box (not built by cargo ${CARGO_ARGS[*]}); skipped"; continue
|
|
fi
|
|
bs_log "artefact $dest: $(bs_size "$dest") bytes, sha256 $(bs_sha256 "$dest"), $(file -b "$dest" | cut -c1-60)"
|
|
# the commit-string gate (rule of 6 October 2026): a node binary without its commit in its strings fails the run
|
|
case "$BS_KIND:$(basename "$dest")" in node:igneumd) "$HERE/ci/commit-string-check.sh" "$dest" "$BS_SHA" || bs_die "commit-string gate failed for $a" ;; esac # only kaspad depends on kaspa-build-info
|
|
done
|
|
fi
|
|
bs_wt_unlock
|