Подготовить HY2XS к production-сборке
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
log_info() {
|
||||
printf '[hy2xs-build] %s\n' "$*"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
printf '\n[hy2xs-build] ==> %s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '[hy2xs-build] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_tool() {
|
||||
local tool="$1"
|
||||
command -v "$tool" >/dev/null 2>&1 || fail "required tool not found: $tool"
|
||||
}
|
||||
|
||||
version_ge() {
|
||||
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n 1)" = "$2" ]
|
||||
}
|
||||
|
||||
copy_dir_contents() {
|
||||
local src="$1"
|
||||
local dst="$2"
|
||||
|
||||
mkdir -p "$dst"
|
||||
if [ -d "$src" ]; then
|
||||
cp -a "$src"/. "$dst"/
|
||||
else
|
||||
fail "directory not found: $src"
|
||||
fi
|
||||
}
|
||||
|
||||
download_file() {
|
||||
local url="$1"
|
||||
local dst="$2"
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL "$url" -o "$dst"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -qO "$dst" "$url"
|
||||
else
|
||||
fail "curl or wget is required to download $url"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
GO_REQUIRED="${GO_REQUIRED:-1.21.13}"
|
||||
BUN_REQUIRED="${BUN_REQUIRED:-1.1.45}"
|
||||
NODE_REQUIRED="${NODE_REQUIRED:-20.19.0}"
|
||||
PNPM_REQUIRED="${PNPM_REQUIRED:-9.15.9}"
|
||||
TOOLCHAIN_DIR="${TOOLCHAIN_DIR:-$ROOT_DIR/.toolchain}"
|
||||
|
||||
require_linux_debian12_amd64() {
|
||||
[ "$(uname -s)" = "Linux" ] || fail "production builder supports only Linux Debian 12 amd64"
|
||||
|
||||
local arch
|
||||
arch="$(uname -m)"
|
||||
[ "$arch" = "x86_64" ] || [ "$arch" = "amd64" ] || fail "production builder supports only amd64, got: $arch"
|
||||
|
||||
[ -f /etc/os-release ] || fail "missing /etc/os-release"
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
[ "${ID:-}" = "debian" ] || fail "production builder supports only Debian 12, got: ${ID:-unknown}"
|
||||
[ "${VERSION_ID:-}" = "12" ] || fail "production builder supports only Debian 12, got version: ${VERSION_ID:-unknown}"
|
||||
}
|
||||
|
||||
apt_install_missing() {
|
||||
local missing=()
|
||||
local pkg
|
||||
for pkg in "$@"; 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
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Installing missing build packages: ${missing[*]}"
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${missing[@]}"
|
||||
elif command -v sudo >/dev/null 2>&1; then
|
||||
sudo apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${missing[@]}"
|
||||
else
|
||||
fail "missing packages (${missing[*]}) and neither root nor sudo is available"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_build_dependencies() {
|
||||
apt_install_missing \
|
||||
ca-certificates curl unzip tar xz-utils git build-essential pkg-config \
|
||||
bash coreutils findutils grep sed gawk openssl
|
||||
|
||||
require_tool curl
|
||||
require_tool tar
|
||||
require_tool xz
|
||||
require_tool unzip
|
||||
require_tool sha256sum
|
||||
require_tool find
|
||||
require_tool install
|
||||
require_tool sort
|
||||
require_tool xargs
|
||||
require_tool grep
|
||||
require_tool sed
|
||||
}
|
||||
|
||||
go_version() {
|
||||
"$1" version | sed -E 's/^go version go([0-9.]+).*/\1/'
|
||||
}
|
||||
|
||||
ensure_go() {
|
||||
local managed="$TOOLCHAIN_DIR/go/bin/go"
|
||||
if [ -x "$managed" ] && [ "$(go_version "$managed")" = "$GO_REQUIRED" ]; then
|
||||
GO_BIN="$managed"
|
||||
elif command -v go >/dev/null 2>&1 && [ "$(go_version "$(command -v go)")" = "$GO_REQUIRED" ]; then
|
||||
GO_BIN="$(command -v go)"
|
||||
else
|
||||
log_info "Installing Go $GO_REQUIRED into $TOOLCHAIN_DIR/go"
|
||||
mkdir -p "$TOOLCHAIN_DIR/downloads"
|
||||
local archive="$TOOLCHAIN_DIR/downloads/go${GO_REQUIRED}.linux-amd64.tar.gz"
|
||||
download_file "https://go.dev/dl/go${GO_REQUIRED}.linux-amd64.tar.gz" "$archive"
|
||||
rm -rf "$TOOLCHAIN_DIR/go"
|
||||
tar -C "$TOOLCHAIN_DIR" -xzf "$archive"
|
||||
GO_BIN="$managed"
|
||||
fi
|
||||
|
||||
export GO_BIN
|
||||
export GOROOT="$TOOLCHAIN_DIR/go"
|
||||
export PATH="$(dirname "$GO_BIN"):$PATH"
|
||||
export GOTOOLCHAIN=local
|
||||
[ "$(go_version "$GO_BIN")" = "$GO_REQUIRED" ] || fail "Go version mismatch: required $GO_REQUIRED, got $($GO_BIN version)"
|
||||
}
|
||||
|
||||
ensure_bun() {
|
||||
local managed="$TOOLCHAIN_DIR/bun/bin/bun"
|
||||
if [ -x "$managed" ] && [ "$($managed --version)" = "$BUN_REQUIRED" ]; then
|
||||
BUN_BIN="$managed"
|
||||
elif command -v bun >/dev/null 2>&1 && [ "$(bun --version)" = "$BUN_REQUIRED" ]; then
|
||||
BUN_BIN="$(command -v bun)"
|
||||
else
|
||||
log_info "Installing Bun $BUN_REQUIRED into $TOOLCHAIN_DIR/bun"
|
||||
mkdir -p "$TOOLCHAIN_DIR/downloads" "$TOOLCHAIN_DIR/bun"
|
||||
local archive="$TOOLCHAIN_DIR/downloads/bun-linux-x64-${BUN_REQUIRED}.zip"
|
||||
download_file "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_REQUIRED}/bun-linux-x64.zip" "$archive"
|
||||
rm -rf "$TOOLCHAIN_DIR/bun-tmp" "$TOOLCHAIN_DIR/bun"
|
||||
mkdir -p "$TOOLCHAIN_DIR/bun-tmp"
|
||||
unzip -q "$archive" -d "$TOOLCHAIN_DIR/bun-tmp"
|
||||
mkdir -p "$TOOLCHAIN_DIR/bun/bin"
|
||||
install -m 0755 "$TOOLCHAIN_DIR/bun-tmp/bun-linux-x64/bun" "$managed"
|
||||
rm -rf "$TOOLCHAIN_DIR/bun-tmp"
|
||||
BUN_BIN="$managed"
|
||||
fi
|
||||
|
||||
export BUN_BIN
|
||||
export PATH="$(dirname "$BUN_BIN"):$PATH"
|
||||
[ "$($BUN_BIN --version)" = "$BUN_REQUIRED" ] || fail "Bun version mismatch: required $BUN_REQUIRED, got $($BUN_BIN --version)"
|
||||
}
|
||||
|
||||
node_version() {
|
||||
"$1" --version | sed 's/^v//'
|
||||
}
|
||||
|
||||
ensure_node() {
|
||||
local managed="$TOOLCHAIN_DIR/node/bin/node"
|
||||
if [ -x "$managed" ] && [ "$(node_version "$managed")" = "$NODE_REQUIRED" ]; then
|
||||
NODE_BIN="$managed"
|
||||
elif command -v node >/dev/null 2>&1 && [ "$(node_version "$(command -v node)")" = "$NODE_REQUIRED" ]; then
|
||||
NODE_BIN="$(command -v node)"
|
||||
else
|
||||
log_info "Installing Node.js $NODE_REQUIRED into $TOOLCHAIN_DIR/node"
|
||||
mkdir -p "$TOOLCHAIN_DIR/downloads"
|
||||
local archive="$TOOLCHAIN_DIR/downloads/node-v${NODE_REQUIRED}-linux-x64.tar.xz"
|
||||
download_file "https://nodejs.org/dist/v${NODE_REQUIRED}/node-v${NODE_REQUIRED}-linux-x64.tar.xz" "$archive"
|
||||
rm -rf "$TOOLCHAIN_DIR/node" "$TOOLCHAIN_DIR/node-v${NODE_REQUIRED}-linux-x64"
|
||||
tar -C "$TOOLCHAIN_DIR" -xJf "$archive"
|
||||
mv "$TOOLCHAIN_DIR/node-v${NODE_REQUIRED}-linux-x64" "$TOOLCHAIN_DIR/node"
|
||||
NODE_BIN="$managed"
|
||||
fi
|
||||
|
||||
export NODE_BIN
|
||||
export PATH="$(dirname "$NODE_BIN"):$PATH"
|
||||
[ "$(node_version "$NODE_BIN")" = "$NODE_REQUIRED" ] || fail "Node.js version mismatch: required $NODE_REQUIRED, got $($NODE_BIN --version)"
|
||||
}
|
||||
|
||||
ensure_pnpm() {
|
||||
local managed="$TOOLCHAIN_DIR/pnpm/bin/pnpm"
|
||||
if [ -x "$managed" ] && [ "$($managed --version)" = "$PNPM_REQUIRED" ]; then
|
||||
PNPM_BIN="$managed"
|
||||
else
|
||||
log_info "Installing pnpm $PNPM_REQUIRED into $TOOLCHAIN_DIR/pnpm"
|
||||
mkdir -p "$TOOLCHAIN_DIR/pnpm/bin"
|
||||
corepack enable --install-directory "$TOOLCHAIN_DIR/pnpm/bin"
|
||||
corepack prepare "pnpm@$PNPM_REQUIRED" --activate
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
cp "$(command -v pnpm)" "$managed"
|
||||
chmod 0755 "$managed"
|
||||
fi
|
||||
PNPM_BIN="$(command -v pnpm)"
|
||||
fi
|
||||
|
||||
export PNPM_BIN
|
||||
[ "$($PNPM_BIN --version)" = "$PNPM_REQUIRED" ] || fail "pnpm version mismatch: required $PNPM_REQUIRED, got $($PNPM_BIN --version)"
|
||||
}
|
||||
|
||||
ensure_toolchain() {
|
||||
ensure_go
|
||||
ensure_bun
|
||||
ensure_node
|
||||
ensure_pnpm
|
||||
|
||||
log_info "Go: $($GO_BIN version)"
|
||||
log_info "Bun: $($BUN_BIN --version)"
|
||||
log_info "Node.js: $($NODE_BIN --version)"
|
||||
log_info "pnpm: $($PNPM_BIN --version)"
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
STAGE_DIR="tools/build/output/hy2xs-install"
|
||||
ADMIN_BUILD_DIR="tools/build/output/hy2xs-admin-build"
|
||||
|
||||
prepare_stage() {
|
||||
local version="$1"
|
||||
local build_id="$2"
|
||||
|
||||
rm -rf "$STAGE_DIR"
|
||||
mkdir -p "$STAGE_DIR" "dist"
|
||||
copy_dir_contents "package" "$STAGE_DIR"
|
||||
|
||||
rm -rf "$STAGE_DIR/orchestrator" "$STAGE_DIR/ui" "$STAGE_DIR/metadata"
|
||||
mkdir -p "$STAGE_DIR/orchestrator" "$STAGE_DIR/ui/hy2xs-admin" "$STAGE_DIR/metadata"
|
||||
|
||||
printf '%s\n' "$version" >"$STAGE_DIR/metadata/package.version"
|
||||
printf '%s\n' "$build_id" >"$STAGE_DIR/metadata/package.build_id"
|
||||
}
|
||||
|
||||
build_orchestrator() {
|
||||
(
|
||||
cd orchestrator
|
||||
"$BUN_BIN" install --frozen-lockfile
|
||||
"$BUN_BIN" build src/cli.ts --compile --target=bun-linux-x64 --outfile ../"$STAGE_DIR"/orchestrator/hy2xs-orchestrator
|
||||
)
|
||||
chmod 0755 "$STAGE_DIR/orchestrator/hy2xs-orchestrator"
|
||||
}
|
||||
|
||||
bundle_ui() {
|
||||
local ui_src="${UI_SRC:-apps}"
|
||||
|
||||
rm -rf "$ADMIN_BUILD_DIR"
|
||||
mkdir -p "$ADMIN_BUILD_DIR"
|
||||
|
||||
(
|
||||
cd "$ui_src/frontend"
|
||||
"$PNPM_BIN" install --frozen-lockfile
|
||||
"$PNPM_BIN" run build:prod
|
||||
)
|
||||
|
||||
(
|
||||
cd "$ui_src"
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOTOOLCHAIN=local "$GO_BIN" mod download
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 GOTOOLCHAIN=local "$GO_BIN" build -trimpath -ldflags "-s -w" -o "../$ADMIN_BUILD_DIR/hy2xs-admin" .
|
||||
)
|
||||
|
||||
install -m 0755 "$ADMIN_BUILD_DIR/hy2xs-admin" "$STAGE_DIR/ui/hy2xs-admin/hy2xs-admin"
|
||||
if [ -f "$ui_src/docs/sql/h_ui_db.sql" ]; then
|
||||
mkdir -p "$STAGE_DIR/ui/hy2xs-admin/docs/sql"
|
||||
install -m 0644 "$ui_src/docs/sql/h_ui_db.sql" "$STAGE_DIR/ui/hy2xs-admin/docs/sql/h_ui_db.sql"
|
||||
fi
|
||||
}
|
||||
|
||||
write_metadata() {
|
||||
local version="$1"
|
||||
local build_id="$2"
|
||||
|
||||
{
|
||||
printf 'name=HY2XS\n'
|
||||
printf 'version=%s\n' "$version"
|
||||
printf 'build_id=%s\n' "$build_id"
|
||||
printf 'build_host_os=debian12\n'
|
||||
printf 'build_host_arch=amd64\n'
|
||||
printf 'target_os=linux\n'
|
||||
printf 'target_arch=amd64\n'
|
||||
printf 'orchestrator_stack=Bun+TypeScript\n'
|
||||
printf 'go_version=%s\n' "$($GO_BIN version)"
|
||||
printf 'bun_version=%s\n' "$($BUN_BIN --version)"
|
||||
printf 'node_version=%s\n' "$($NODE_BIN --version)"
|
||||
printf 'pnpm_version=%s\n' "$($PNPM_BIN --version)"
|
||||
printf 'hysteria_source=official-upstream\n'
|
||||
printf 'hysteria_target=linux-amd64\n'
|
||||
} >"$STAGE_DIR/metadata/package.env"
|
||||
|
||||
(
|
||||
cd "$STAGE_DIR"
|
||||
find . -type f ! -path './metadata/checksums.txt' -print0 \
|
||||
| sort -z \
|
||||
| xargs -0 sha256sum >metadata/checksums.txt
|
||||
)
|
||||
}
|
||||
|
||||
create_archive() {
|
||||
local version="$1"
|
||||
local archive="dist/hy2xs-install-${version}.tar.gz"
|
||||
|
||||
rm -f "$archive"
|
||||
tar -C "tools/build/output" -czf "$archive" "hy2xs-install"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
require_repo_layout() {
|
||||
[ -d "orchestrator/src" ] || fail "missing orchestrator/src"
|
||||
[ -f "orchestrator/package.json" ] || fail "missing orchestrator/package.json"
|
||||
[ -f "orchestrator/bun.lock" ] || fail "missing orchestrator/bun.lock"
|
||||
[ -f "package/install.sh" ] || fail "missing package/install.sh"
|
||||
[ -d "package/templates" ] || fail "missing package/templates"
|
||||
[ -f "package/templates/env/post-install.env.tpl" ] || fail "missing package/templates/env/post-install.env.tpl"
|
||||
[ -d "package/systemd" ] || fail "missing package/systemd"
|
||||
[ -d "apps" ] || fail "missing apps HY2XS admin source"
|
||||
[ -f "apps/go.mod" ] || fail "missing apps/go.mod"
|
||||
[ -f "apps/go.sum" ] || fail "missing apps/go.sum"
|
||||
[ -f "apps/frontend/package.json" ] || fail "missing apps/frontend/package.json"
|
||||
[ -f "apps/frontend/pnpm-lock.yaml" ] || fail "missing apps/frontend/pnpm-lock.yaml"
|
||||
}
|
||||
|
||||
verify_archive() {
|
||||
local version="$1"
|
||||
local archive="dist/hy2xs-install-${version}.tar.gz"
|
||||
|
||||
[ -f "$archive" ] || fail "archive was not created: $archive"
|
||||
|
||||
local listing
|
||||
listing="$(tar -tzf "$archive")"
|
||||
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/install.sh$' || fail "archive missing install.sh"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/orchestrator/hy2xs-orchestrator$' || fail "archive missing orchestrator artifact"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/ui/hy2xs-admin/hy2xs-admin$' || fail "archive missing bundled UI binary"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/systemd/hysteria-server.service$' || fail "archive missing hysteria systemd unit"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/systemd/hy2xs-admin.service$' || fail "archive missing admin systemd unit"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/templates/hysteria/config.yaml.tpl$' || fail "archive missing Hysteria config template"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/templates/env/post-install.env.tpl$' || fail "archive missing post-install env template"
|
||||
printf '%s\n' "$listing" | grep -q '^hy2xs-install/metadata/checksums.txt$' || fail "archive missing checksums"
|
||||
}
|
||||
Reference in New Issue
Block a user