Files
AWG2_harness/tools/build.sh
T
founder b83f2ee3a9 Пакет: убрать docker.io из metadata и улучшить help/docs layout
Удалён Recommends: docker.io из control. Ужесточено предупреждение, что --install не является production-path. В .deb docs переведены на repo-like структуру (/usr/share/doc/awg2-harness/repo/...), чтобы не ломались относительные ссылки.
2026-05-10 17:36:31 +05:00

403 lines
10 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CRATE_MANIFEST="$ROOT_DIR/traffic_core/Cargo.toml"
TARGET_BIN="$ROOT_DIR/traffic_core/target/release/awg2-harness"
RUST_TOOLCHAIN_FILE="$ROOT_DIR/rust-toolchain.toml"
RUST_TOOLCHAIN_VERSION="1.95.0"
PREPARE_BUILD_ENV=false
PREPARE_RUNTIME_ENV=false
BOOTSTRAP_RUST=false
DO_BUILD=false
PACKAGE=false
INSTALL=false
ENABLE=false
YES=false
SKIP_FMT=false
HAS_ACTION=false
DIST_DIR="$ROOT_DIR/dist"
PACKAGE_VERSION=""
RUN_AS_ROOT=()
BUILD_PACKAGES=(
ca-certificates
curl
build-essential
pkg-config
file
shellcheck
dpkg-dev
)
RUNTIME_PACKAGES=(
iproute2
wireguard-tools
nftables
kmod
util-linux
procps
systemd
)
usage() {
cat <<'EOF'
Usage:
./tools/build.sh
./tools/build.sh --prepare-build-env --yes
./tools/build.sh --prepare-runtime-env --yes
./tools/build.sh --bootstrap-rust --yes
./tools/build.sh --build
./tools/build.sh --package
./tools/build.sh --build --package
./tools/build.sh --install
./tools/build.sh --enable
Flags:
--prepare-build-env Install missing build dependencies via apt
--prepare-runtime-env Install missing runtime dependencies via apt
--bootstrap-rust Install rustup/toolchain pinned by this repo
--build Run build gate (default when no action flags)
--package Build Debian package into dist/ (runs build gate by default)
--dist-dir <path> Output directory for package artifacts (default: ./dist)
--version <version> Override package version (default: read from Cargo.toml)
--install Install binary and systemd unit (no enable/start)
--enable Enable and start awg2-harness.service
--yes Confirm system mutations (apt/rustup)
--skip-fmt Skip cargo fmt --check
--help Show this help
Notes:
--install is not the production deployment path; it is for manual source-tree installs (staging/debug).
Use generated .deb packages on production hosts: apt install ./awg2-harness_<version>_amd64.deb
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--prepare-build-env) PREPARE_BUILD_ENV=true; HAS_ACTION=true ;;
--prepare-runtime-env) PREPARE_RUNTIME_ENV=true; HAS_ACTION=true ;;
--bootstrap-rust) BOOTSTRAP_RUST=true; HAS_ACTION=true ;;
--build) DO_BUILD=true; HAS_ACTION=true ;;
--package) PACKAGE=true; HAS_ACTION=true ;;
--dist-dir)
[[ $# -ge 2 ]] || { echo "--dist-dir requires value"; exit 1; }
DIST_DIR="$2"
shift
;;
--version)
[[ $# -ge 2 ]] || { echo "--version requires value"; exit 1; }
PACKAGE_VERSION="$2"
shift
;;
--install) INSTALL=true; HAS_ACTION=true ;;
--enable) ENABLE=true; HAS_ACTION=true ;;
--yes) YES=true ;;
--skip-fmt) SKIP_FMT=true ;;
--help|-h) usage; exit 0 ;;
*) echo "Unknown arg: $1"; usage; exit 1 ;;
esac
shift
done
if [[ "$HAS_ACTION" == false ]]; then
DO_BUILD=true
fi
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
echo "Missing command: $1"
exit 1
}
}
setup_root_runner() {
if [[ "$(id -u)" -eq 0 ]]; then
RUN_AS_ROOT=()
else
require_cmd sudo
RUN_AS_ROOT=(sudo)
fi
}
ensure_supported_host() {
if [[ -f /etc/os-release ]]; then
# shellcheck disable=SC1091
source /etc/os-release
case "${ID:-}" in
debian|ubuntu) ;;
*) echo "Unsupported OS: ${ID:-unknown}. Only Debian/Ubuntu are supported."; exit 1 ;;
esac
fi
local arch
arch="$(uname -m)"
if [[ "$arch" != "x86_64" && "$arch" != "amd64" ]]; then
echo "Unsupported arch: $arch. Only amd64/x86_64 is supported."
exit 1
fi
}
apt_install_missing() {
local packages=("$@")
local missing=()
require_cmd dpkg-query
require_cmd apt-get
for pkg in "${packages[@]}"; do
if ! dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
missing+=("$pkg")
fi
done
if [[ "${#missing[@]}" -eq 0 ]]; then
echo "All required packages already installed"
return 0
fi
echo "Missing packages: ${missing[*]}"
if [[ "$YES" != true ]]; then
echo "Re-run with --yes to install them"
exit 1
fi
setup_root_runner
"${RUN_AS_ROOT[@]}" apt-get update
"${RUN_AS_ROOT[@]}" apt-get install -y --no-install-recommends "${missing[@]}"
}
bootstrap_rust() {
require_cmd curl
if [[ "$YES" != true ]]; then
echo "This will install rustup and Rust toolchain $RUST_TOOLCHAIN_VERSION"
echo "Re-run with --yes to proceed"
exit 1
fi
if ! command -v rustup >/dev/null 2>&1; then
local rustup_script
rustup_script="$(mktemp)"
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o "$rustup_script"
sh "$rustup_script" -y --profile minimal --default-toolchain "$RUST_TOOLCHAIN_VERSION"
rm -f "$rustup_script"
fi
export PATH="$HOME/.cargo/bin:$PATH"
require_cmd rustup
rustup toolchain install "$RUST_TOOLCHAIN_VERSION" --profile minimal --component rustfmt --component clippy
rustup component add rustfmt clippy --toolchain "$RUST_TOOLCHAIN_VERSION"
rustc --version
}
prepare_build_gate() {
require_cmd cargo
require_cmd rustc
require_cmd gcc
require_cmd pkg-config
require_cmd file
require_cmd ldd
if [[ ! -f "$RUST_TOOLCHAIN_FILE" ]]; then
echo "Missing rust toolchain pin file: $RUST_TOOLCHAIN_FILE"
exit 1
fi
if [[ ! -f "$ROOT_DIR/traffic_core/Cargo.lock" ]]; then
echo "Missing lockfile: $ROOT_DIR/traffic_core/Cargo.lock"
exit 1
fi
if [[ "$SKIP_FMT" != true ]]; then
cargo fmt --manifest-path "$CRATE_MANIFEST" --all --check
fi
cargo clippy --locked --manifest-path "$CRATE_MANIFEST" --all-targets -- -D warnings
cargo test --locked --manifest-path "$CRATE_MANIFEST" --release
cargo build --locked --manifest-path "$CRATE_MANIFEST" --release
file "$TARGET_BIN"
ldd "$TARGET_BIN" || true
"$TARGET_BIN" --help >/dev/null
sha256sum "$TARGET_BIN"
ls -lh "$TARGET_BIN"
}
install_artifacts() {
require_cmd install
require_cmd systemctl
if [[ ! -f "$TARGET_BIN" ]]; then
echo "Binary not found: $TARGET_BIN"
echo "Run build first: ./tools/build.sh"
exit 1
fi
if [[ ! -f /etc/awg2-harness/config.toml ]]; then
echo "Missing /etc/awg2-harness/config.toml"
echo "Refusing to install example config as production config"
echo "Create config manually from $ROOT_DIR/config.example.toml and re-run with --install"
exit 1
fi
setup_root_runner
"${RUN_AS_ROOT[@]}" install -d -m 0755 /usr/sbin
"${RUN_AS_ROOT[@]}" install -m 0755 "$TARGET_BIN" /usr/sbin/awg2-harness
"${RUN_AS_ROOT[@]}" install -d -m 0755 /lib/systemd/system
"${RUN_AS_ROOT[@]}" install -m 0644 "$ROOT_DIR/awg2-harness.service" /lib/systemd/system/awg2-harness.service
if command -v systemd-analyze >/dev/null 2>&1; then
"${RUN_AS_ROOT[@]}" systemd-analyze verify /lib/systemd/system/awg2-harness.service
fi
"${RUN_AS_ROOT[@]}" systemctl daemon-reload
}
read_package_version_from_manifest() {
awk -F '"' '/^version\s*=\s*"/ { print $2; exit }' "$CRATE_MANIFEST"
}
package_deb() {
require_cmd dpkg-deb
require_cmd install
require_cmd sha256sum
require_cmd file
require_cmd ldd
require_cmd mktemp
if [[ "$DO_BUILD" != true ]]; then
prepare_build_gate
fi
if [[ ! -f "$TARGET_BIN" ]]; then
echo "Binary not found after build: $TARGET_BIN"
exit 1
fi
local version
if [[ -n "$PACKAGE_VERSION" ]]; then
version="$PACKAGE_VERSION"
else
version="$(read_package_version_from_manifest)"
fi
if [[ -z "$version" ]]; then
echo "Unable to determine package version"
exit 1
fi
local staging
staging="$(mktemp -d)"
trap 'rm -rf "$staging"' RETURN
install -d -m 0755 "$staging/DEBIAN"
install -d -m 0755 "$staging/usr/sbin"
install -d -m 0755 "$staging/lib/systemd/system"
install -d -m 0755 "$staging/usr/share/doc/awg2-harness/repo"
install -d -m 0755 "$staging/usr/share/doc/awg2-harness/repo/docs"
install -d -m 0755 "$staging/usr/share/doc/awg2-harness/repo/tools"
install -m 0755 "$TARGET_BIN" "$staging/usr/sbin/awg2-harness"
install -m 0644 "$ROOT_DIR/awg2-harness.service" "$staging/lib/systemd/system/awg2-harness.service"
install -m 0644 "$ROOT_DIR/config.example.toml" "$staging/usr/share/doc/awg2-harness/repo/config.example.toml"
install -m 0644 "$ROOT_DIR/README.md" "$staging/usr/share/doc/awg2-harness/repo/README.md"
install -m 0644 "$ROOT_DIR/docs/INSTALLATION.md" "$staging/usr/share/doc/awg2-harness/repo/docs/INSTALLATION.md"
install -m 0644 "$ROOT_DIR/tools/README.md" "$staging/usr/share/doc/awg2-harness/repo/tools/README.md"
cat >"$staging/DEBIAN/control" <<EOF
Package: awg2-harness
Version: $version
Section: net
Priority: optional
Architecture: amd64
Maintainer: AWG2 Harness
Depends: libc6, iproute2, wireguard-tools, nftables, kmod, util-linux, procps, systemd
Description: Host-side orchestrator for AmneziaWG traffic shaping and lifecycle recovery
AWG2 Harness manages AmneziaWG traffic shaping, IFB ingress shaping,
nftables anti-torrent guard, reboot scheduling, swap setup and daemonized
drift recovery on dedicated Debian hosts.
EOF
cat >"$staging/DEBIAN/postinst" <<'EOF'
#!/bin/sh
set -e
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload || true
fi
exit 0
EOF
cat >"$staging/DEBIAN/postrm" <<'EOF'
#!/bin/sh
set -e
if command -v systemctl >/dev/null 2>&1; then
systemctl daemon-reload || true
fi
exit 0
EOF
chmod 0755 "$staging/DEBIAN/postinst" "$staging/DEBIAN/postrm"
mkdir -p "$DIST_DIR"
local pkg_name
pkg_name="awg2-harness_${version}_amd64.deb"
local pkg_path
pkg_path="$DIST_DIR/$pkg_name"
dpkg-deb --build "$staging" "$pkg_path"
(
cd "$DIST_DIR"
sha256sum "$pkg_name" > SHA256SUMS
)
echo "Package created: $pkg_path"
echo "Checksums: $DIST_DIR/SHA256SUMS"
}
enable_service() {
require_cmd systemctl
setup_root_runner
"${RUN_AS_ROOT[@]}" systemctl enable --now awg2-harness.service
}
ensure_supported_host
if [[ "$PREPARE_BUILD_ENV" == true ]]; then
apt_install_missing "${BUILD_PACKAGES[@]}"
fi
if [[ "$PREPARE_RUNTIME_ENV" == true ]]; then
apt_install_missing "${RUNTIME_PACKAGES[@]}"
fi
if [[ "$BOOTSTRAP_RUST" == true ]]; then
bootstrap_rust
fi
if [[ "$DO_BUILD" == true ]]; then
prepare_build_gate
fi
if [[ "$PACKAGE" == true ]]; then
package_deb
fi
if [[ "$INSTALL" == true ]]; then
install_artifacts
fi
if [[ "$ENABLE" == true ]]; then
enable_service
fi
if [[ "$DO_BUILD" == true ]]; then
echo "Build completed: $TARGET_BIN"
fi