Files
ARTEX-chixuweihu/build.sh
T

245 lines
8.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ARTEX cross-platform release builder.
#
# The default mode builds one target and embeds the already-exported frontend.
# `./build.sh --release` builds and packages all supported desktop/server targets.
#
# Environment variables:
# ARTEX_TARGET_OS=linux One target OS in single-target mode.
# ARTEX_TARGET_ARCH=amd64 One target arch in single-target mode.
# ARTEX_TARGETS=linux/amd64,... Comma-separated targets for multi-target mode.
# ARTEX_BUILD_VERSION=0.3.3 Version embedded in the binary and archive name.
# ARTEX_OUTPUT=/path/to/artex Explicit binary path in single-target mode.
# ARTEX_OUTPUT_DIR=dist Directory for default binary paths.
# ARTEX_PACKAGE=1 Create a zip archive for each target.
# ARTEX_PACKAGE_DIR=dist Directory for release archives.
# ARTEX_COMPRESS=auto UPX mode: auto, required, or 0/off.
# ARTEX_UPX_ARGS="--best --lzma" Arguments passed to UPX.
# ARTEX_SKIP_FRONTEND=1 Reuse server/webui/dist (for CI artifact builds).
# ARTEX_SKIP_NPM_CI=1 Skip npm ci while rebuilding the frontend.
# ARTEX_GOSUMDB=sum.golang.org Go checksum database.
set -euo pipefail
cd "$(cd "$(dirname "$0")" && pwd)"
info() { printf '\033[36m[*]\033[0m %s\n' "$*"; }
ok() { printf '\033[32m[+]\033[0m %s\n' "$*"; }
warn() { printf '\033[33m[!]\033[0m %s\n' "$*" >&2; }
die() { printf '\033[31m[x]\033[0m %s\n' "$*" >&2; exit 1; }
usage() {
cat <<'EOF'
用法:
./build.sh 编译当前系统当前架构
./build.sh --target linux/amd64 编译一个指定目标
./build.sh --release 编译并打包全部支持的目标
选项:
--release 构建 Linux、macOS、Windows 的 amd64/arm64 目标并生成 zip
--target OS/ARCH 设置单个目标,例如 windows/amd64
--no-compress 不使用 UPX,仅保留 Go linker 压缩
--help 显示帮助
多目标列表可通过 ARTEX_TARGETS 覆盖,例如:
ARTEX_TARGETS=linux/amd64,windows/amd64 ./build.sh --release
EOF
}
RELEASE_TARGETS_DEFAULT="linux/amd64,linux/arm64,darwin/amd64,darwin/arm64,windows/amd64"
ARTEX_RELEASE="${ARTEX_RELEASE:-0}"
ARTEX_COMPRESS="${ARTEX_COMPRESS:-auto}"
ARTEX_PACKAGE="${ARTEX_PACKAGE:-0}"
while [ "$#" -gt 0 ]; do
case "$1" in
--release)
ARTEX_RELEASE=1
ARTEX_PACKAGE=1
shift
;;
--target)
[ "$#" -ge 2 ] || die "--target 需要 OS/ARCH 参数"
target_arg="$2"
case "$target_arg" in
*/*)
ARTEX_TARGET_OS="${target_arg%%/*}"
ARTEX_TARGET_ARCH="${target_arg##*/}"
ARTEX_TARGETS="$target_arg"
;;
*) die "目标必须是 OS/ARCH,例如 linux/amd64" ;;
esac
shift 2
;;
--no-compress)
ARTEX_COMPRESS=0
shift
;;
--help|-h)
usage
exit 0
;;
*) die "未知参数:$1(使用 --help 查看用法)" ;;
esac
done
command -v go >/dev/null 2>&1 || die "未检测到 Go(项目需要 Go 1.26 或更高版本)"
ARTEX_GOSUMDB="${ARTEX_GOSUMDB:-sum.golang.org}"
ARTEX_BUILD_VERSION="${ARTEX_BUILD_VERSION:-0.3.3}"
# Release tags are commonly passed as v0.3.3; keep the binary version consistent.
ARTEX_BUILD_VERSION="${ARTEX_BUILD_VERSION#v}"
ARTEX_OUTPUT_DIR="${ARTEX_OUTPUT_DIR:-dist}"
ARTEX_PACKAGE_DIR="${ARTEX_PACKAGE_DIR:-$ARTEX_OUTPUT_DIR}"
ARTEX_UPX_ARGS="${ARTEX_UPX_ARGS:---best --lzma}"
if [ "${ARTEX_RELEASE}" = "1" ]; then
ARTEX_TARGETS="${ARTEX_TARGETS:-$RELEASE_TARGETS_DEFAULT}"
else
ARTEX_TARGET_OS="${ARTEX_TARGET_OS:-$(GOSUMDB="$ARTEX_GOSUMDB" go env GOOS)}"
ARTEX_TARGET_ARCH="${ARTEX_TARGET_ARCH:-$(GOSUMDB="$ARTEX_GOSUMDB" go env GOARCH)}"
ARTEX_TARGETS="${ARTEX_TARGETS:-${ARTEX_TARGET_OS}/${ARTEX_TARGET_ARCH}}"
fi
if [ "${ARTEX_SKIP_FRONTEND:-0}" = "1" ]; then
[ -d server/webui/dist ] || die "ARTEX_SKIP_FRONTEND=1 但 server/webui/dist 不存在"
else
command -v npm >/dev/null 2>&1 || die "未检测到 npm(前端静态构建需要 Node.js/npm)"
command -v rsync >/dev/null 2>&1 || die "未检测到 rsync"
info "构建前端静态资源"
if [ "${ARTEX_SKIP_NPM_CI:-0}" != "1" ]; then
(cd web && npm ci)
fi
(cd web && npm run build:static)
info "同步前端资源到 server/webui/dist"
mkdir -p server/webui/dist
rsync -a --delete web/out/ server/webui/dist/
fi
compress_binary() {
binary="$1"
goos="$2"
case "$ARTEX_COMPRESS" in
0|off|false|none)
info "跳过 UPX:$binary"
return 0
;;
auto|required|true|1) ;;
*) die "ARTEX_COMPRESS 必须是 auto、required 或 0/off" ;;
esac
if ! command -v upx >/dev/null 2>&1; then
if [ "$ARTEX_COMPRESS" = "required" ]; then
die "ARTEX_COMPRESS=required 但未检测到 upx"
fi
warn "未检测到 upx,保留 linker 压缩结果:$binary"
return 0
fi
before=$(wc -c < "$binary" | tr -d ' ')
upx_args="$ARTEX_UPX_ARGS"
[ "$goos" = "darwin" ] && upx_args="$upx_args --force-macos"
# shellcheck disable=SC2086
if ! upx $upx_args -- "$binary"; then
if [ "$ARTEX_COMPRESS" = "required" ]; then
die "UPX 压缩失败:$binary"
fi
warn "UPX 不支持该目标格式,保留未压缩二进制:$binary"
return 0
fi
after=$(wc -c < "$binary" | tr -d ' ')
ok "UPX 压缩完成:$binary (${before} -> ${after} bytes)"
}
package_binary() {
binary="$1"
goos="$2"
goarch="$3"
package_name="artex-${ARTEX_BUILD_VERSION}-${goos}-${goarch}"
package_root="${ARTEX_PACKAGE_DIR}/${package_name}"
archive="${ARTEX_PACKAGE_DIR}/${package_name}.zip"
command -v zip >/dev/null 2>&1 || die "打包需要 zip"
rm -rf "$package_root" "$archive"
mkdir -p "$package_root"
cp "$binary" "$package_root/"
cp -R skills "$package_root/"
cp config.example.json "$package_root/"
if [ -f README.md ]; then cp README.md "$package_root/"; fi
(cd "$ARTEX_PACKAGE_DIR" && zip -q -r -9 "$(basename "$archive")" "$(basename "$package_root")")
rm -rf "$package_root"
ok "Release 压缩包:$archive"
}
build_target() {
target="$1"
case "$target" in
*/*) ;;
*) die "无效目标:$target(必须是 OS/ARCH)" ;;
esac
goos="${target%%/*}"
goarch="${target##*/}"
case "$goos" in
linux|darwin|windows) ;;
*) die "不支持的系统:$goos(支持 linux、darwin、windows)" ;;
esac
binary_name="artex"
[ "$goos" = "windows" ] && binary_name="artex.exe"
if [ -n "${ARTEX_OUTPUT:-}" ] && [ "$ARTEX_RELEASE" != "1" ]; then
output="$ARTEX_OUTPUT"
else
output="${ARTEX_OUTPUT_DIR}/artex-${goos}-${goarch}/${binary_name}"
fi
mkdir -p "$(dirname "$output")"
info "编译 ${goos}/${goarch},版本 ${ARTEX_BUILD_VERSION}"
GOSUMDB="$ARTEX_GOSUMDB" \
CGO_ENABLED=0 \
GOOS="$goos" \
GOARCH="$goarch" \
go build \
-tags embedui \
-trimpath \
-ldflags "-s -w -buildid= -X main.version=${ARTEX_BUILD_VERSION}" \
-o "$output" \
./cmd/artex
compress_binary "$output" "$goos"
if command -v file >/dev/null 2>&1; then file "$output"; fi
if [ "$ARTEX_PACKAGE" = "1" ]; then package_binary "$output" "$goos" "$goarch"; fi
ok "编译完成:$output"
}
write_checksums() {
[ "$ARTEX_PACKAGE" = "1" ] || return 0
checksum_file="$ARTEX_PACKAGE_DIR/SHA256SUMS"
if command -v sha256sum >/dev/null 2>&1; then
(cd "$ARTEX_PACKAGE_DIR" && for archive in *.zip; do sha256sum "$archive"; done > "$(basename "$checksum_file")")
elif command -v shasum >/dev/null 2>&1; then
(cd "$ARTEX_PACKAGE_DIR" && for archive in *.zip; do shasum -a 256 "$archive"; done > "$(basename "$checksum_file")")
else
warn "未检测到 sha256sum 或 shasum,跳过 SHA256SUMS"
return 0
fi
ok "校验文件:$checksum_file"
}
mkdir -p "$ARTEX_OUTPUT_DIR"
if [ "$ARTEX_PACKAGE" = "1" ]; then mkdir -p "$ARTEX_PACKAGE_DIR"; fi
old_ifs="$IFS"
IFS=','
read -r -a targets <<< "$ARTEX_TARGETS"
IFS="$old_ifs"
[ "${#targets[@]}" -gt 0 ] || die "ARTEX_TARGETS 不能为空"
for target in "${targets[@]}"; do
target="${target//[[:space:]]/}"
[ -n "$target" ] || continue
build_target "$target"
done
if [ "$ARTEX_PACKAGE" = "1" ]; then
write_checksums
info "Release 包已生成于:$ARTEX_PACKAGE_DIR"
fi