Parallel agent work: bandwidth fix, CPU platforms, packaging
All checks were successful
CI / test (push) Successful in 2m8s

5 agents ran in parallel:

1. Fix bandwidth limit (-b): new advance_next_send() prevents drift
   bursts by resetting when >2x interval behind (bandwidth.rs, client.rs, server.rs)

2. Windows + FreeBSD CPU support (cpu.rs):
   - Windows: GetSystemTimes via raw FFI
   - FreeBSD: sysctl kern.cp_time parsing

3. Ubuntu .deb packaging (deploy/deb/):
   - build-deb.sh: creates .deb from pre-built binary
   - test-deb.sh: tests in Ubuntu Docker container

4. Fedora/RHEL RPM packaging (deploy/rpm/):
   - btest-rs.spec: full RPM spec with systemd unit
   - build-rpm.sh + test-rpm.sh

5. Alpine Linux apk packaging (deploy/alpine/):
   - APKBUILD with OpenRC init script
   - test-alpine.sh

58 tests pass, zero warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-01 14:04:00 +04:00
parent fe28c04c19
commit 8c853c3605
12 changed files with 816 additions and 15 deletions

52
deploy/alpine/APKBUILD Normal file
View File

@@ -0,0 +1,52 @@
# Maintainer: Siavash Sameni <manwe at manko dot yoga>
pkgname=btest-rs
pkgver=0.6.0
pkgrel=0
pkgdesc="MikroTik Bandwidth Test server and client with EC-SRP5 auth"
url="https://github.com/manawenuz/btest-rs"
license="MIT AND Apache-2.0"
arch="x86_64 aarch64 armv7"
makedepends="cargo rust"
install="$pkgname.pre-install"
source="$pkgname-$pkgver.tar.gz::https://github.com/manawenuz/btest-rs/archive/refs/tags/v$pkgver.tar.gz
btest.initd
"
sha256sums="SKIP
SKIP
"
prepare() {
default_prepare
cd "$builddir"
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
}
build() {
cd "$builddir"
export CARGO_TARGET_DIR=target
cargo build --frozen --release
}
check() {
cd "$builddir"
cargo test --frozen --release
}
package() {
cd "$builddir"
# binary
install -Dm755 "target/release/btest" "$pkgdir/usr/bin/btest"
# man page
install -Dm644 "docs/man/btest.1" "$pkgdir/usr/share/man/man1/btest.1"
# license
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
# documentation
install -Dm644 "README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
# OpenRC init script
install -Dm755 "$srcdir/btest.initd" "$pkgdir/etc/init.d/btest"
}

37
deploy/alpine/btest.initd Executable file
View File

@@ -0,0 +1,37 @@
#!/sbin/openrc-run
# OpenRC init script for btest-rs
# MikroTik Bandwidth Test server
name="btest"
description="MikroTik Bandwidth Test Server (btest-rs)"
command="/usr/bin/btest"
command_args="-s"
command_background=true
pidfile="/run/$name.pid"
# Run as dedicated user if it exists, otherwise root
command_user="btest:btest"
# Logging
output_log="/var/log/$name/$name.log"
error_log="/var/log/$name/$name.err"
depend() {
need net
after firewall
use dns logger
}
start_pre() {
# Create log directory
checkpath -d -m 0755 -o "$command_user" /var/log/$name
# Create runtime directory
checkpath -d -m 0755 -o "$command_user" /run
}
stop() {
ebegin "Stopping $name"
start-stop-daemon --stop --pidfile "$pidfile" --retry TERM/5/KILL/3
eend $?
}

118
deploy/alpine/test-alpine.sh Executable file
View File

@@ -0,0 +1,118 @@
#!/bin/sh
# Test Alpine Linux packaging for btest-rs
# Runs inside an Alpine Docker container to build and verify the APK.
#
# Usage (from repository root):
# docker run --rm -v "$PWD":/src alpine:latest /src/deploy/alpine/test-alpine.sh
#
set -eu
ALPINE_DIR="/src/deploy/alpine"
echo "=== Alpine APK packaging test ==="
echo "Alpine version: $(cat /etc/alpine-release)"
# ── Install build dependencies ──────────────────────────────────────
echo "--- Installing build dependencies ---"
apk update
apk add --no-cache \
alpine-sdk \
rust \
cargo \
sudo
# ── Create a non-root build user (abuild refuses to run as root) ──
echo "--- Setting up build user ---"
adduser -D builder
addgroup builder abuild
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# ── Prepare build tree ──────────────────────────────────────────────
echo "--- Preparing build tree ---"
BUILD_DIR="/home/builder/btest-rs"
mkdir -p "$BUILD_DIR"
cp "$ALPINE_DIR/APKBUILD" "$BUILD_DIR/"
cp "$ALPINE_DIR/btest.initd" "$BUILD_DIR/"
# Generate signing key (required by abuild)
su builder -c "abuild-keygen -a -n -q"
sudo cp /home/builder/.abuild/*.rsa.pub /etc/apk/keys/
# ── Build the package ──────────────────────────────────────────────
echo "--- Building APK ---"
cd "$BUILD_DIR"
chown -R builder:builder "$BUILD_DIR"
su builder -c "abuild -r"
echo "--- Build succeeded ---"
# ── Locate and install the package ──────────────────────────────────
echo "--- Installing built APK ---"
APK_FILE=$(find /home/builder/packages -name "btest-rs-*.apk" -not -name "*doc*" | head -1)
if [ -z "$APK_FILE" ]; then
echo "FAIL: APK file not found"
exit 1
fi
echo "Found APK: $APK_FILE"
apk add --allow-untrusted "$APK_FILE"
# ── Verify installation ────────────────────────────────────────────
echo "--- Verifying installation ---"
FAIL=0
# Binary exists and is executable
if command -v btest >/dev/null 2>&1; then
echo "PASS: btest binary installed"
else
echo "FAIL: btest binary not found in PATH"
FAIL=1
fi
# Binary runs (show version / help)
if btest --help >/dev/null 2>&1; then
echo "PASS: btest --help exits successfully"
else
echo "FAIL: btest --help failed"
FAIL=1
fi
# Man page installed
if [ -f /usr/share/man/man1/btest.1 ]; then
echo "PASS: man page installed"
else
echo "FAIL: man page not found"
FAIL=1
fi
# License installed
if [ -f /usr/share/licenses/btest-rs/LICENSE ]; then
echo "PASS: LICENSE installed"
else
echo "FAIL: LICENSE not found"
FAIL=1
fi
# OpenRC init script installed
if [ -f /etc/init.d/btest ]; then
echo "PASS: OpenRC init script installed"
else
echo "FAIL: OpenRC init script not found"
FAIL=1
fi
# Init script is executable
if [ -x /etc/init.d/btest ]; then
echo "PASS: init script is executable"
else
echo "FAIL: init script is not executable"
FAIL=1
fi
# ── Summary ─────────────────────────────────────────────────────────
echo ""
if [ "$FAIL" -eq 0 ]; then
echo "=== All Alpine packaging tests PASSED ==="
else
echo "=== Some Alpine packaging tests FAILED ==="
exit 1
fi