build: add compressed cross-platform release artifacts
This commit is contained in:
1 parent
d06047a937
commit
dfcf33f9f5
4 files changed
+253
-48
No files matched your search
@@ -48,36 +48,32 @@ jobs:
|
||||
with:
|
||||
name: web-dist
|
||||
path: server/webui/dist
|
||||
- name: Build
|
||||
- name: Install UPX
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y upx-ucl
|
||||
- name: Build and package with build.sh
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
CGO_ENABLED: "0"
|
||||
ARTEX_TARGET_OS: ${{ matrix.goos }}
|
||||
ARTEX_TARGET_ARCH: ${{ matrix.goarch }}
|
||||
ARTEX_BUILD_VERSION: ${{ github.ref_name }}
|
||||
ARTEX_SKIP_FRONTEND: "1"
|
||||
ARTEX_SKIP_NPM_CI: "1"
|
||||
ARTEX_COMPRESS: required
|
||||
ARTEX_PACKAGE: "1"
|
||||
ARTEX_PACKAGE_DIR: dist
|
||||
run: |
|
||||
BIN=artex
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
[ "$GOOS" = "windows" ] && BIN=artex.exe
|
||||
go build -tags embedui -trimpath \
|
||||
-ldflags "-s -w -X main.version=${VERSION}" \
|
||||
-o "$BIN" ./cmd/artex
|
||||
- name: Package
|
||||
run: |
|
||||
NAME="artex-${GITHUB_REF_NAME}-${{ matrix.goos }}-${{ matrix.goarch }}"
|
||||
mkdir -p "$NAME"
|
||||
cp -r skills "$NAME"/
|
||||
cp config.example.json "$NAME"/
|
||||
if [ "${{ matrix.goos }}" = "windows" ]; then cp artex.exe "$NAME"/; else cp artex "$NAME"/; fi
|
||||
zip -r "$NAME.zip" "$NAME"
|
||||
./build.sh --target "${ARTEX_TARGET_OS}/${ARTEX_TARGET_ARCH}"
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pkg-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: "*.zip"
|
||||
path: "dist/*.zip"
|
||||
# Linux 生二进制单独上传,供 docker job 复用(免在镜像里重编前端+Go)
|
||||
- if: matrix.goos == 'linux'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bin-linux-${{ matrix.goarch }}
|
||||
path: artex
|
||||
path: dist/artex-linux-${{ matrix.goarch }}/artex
|
||||
|
||||
# ③ 汇总所有 zip → 创建 GitHub Release
|
||||
release:
|
||||
@@ -89,9 +85,14 @@ jobs:
|
||||
pattern: pkg-*
|
||||
merge-multiple: true
|
||||
path: dist
|
||||
- name: Generate checksums
|
||||
working-directory: dist
|
||||
run: sha256sum *.zip > SHA256SUMS
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: dist/*.zip
|
||||
files: |
|
||||
dist/*.zip
|
||||
dist/SHA256SUMS
|
||||
generate_release_notes: true
|
||||
|
||||
# ④ 多架构镜像 → 推 Docker Hub(复用 binaries 编好的 Linux 二进制,
|
||||
|
||||
+4
-1
@@ -4,7 +4,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
暂无未发布变更。
|
||||
### Build
|
||||
|
||||
- `build.sh --release` 支持一次构建 Linux amd64/arm64、macOS amd64/arm64 和 Windows amd64,并生成带 `skills/`、配置示例和 README 的 zip 发布包。
|
||||
- 使用 Go linker 去除调试信息,并在可用时使用 UPX 压缩二进制;Release Workflow 将 UPX 压缩结果和 `SHA256SUMS` 发布到 GitHub Release。
|
||||
|
||||
## [0.3.3] - 2026-08-21
|
||||
|
||||
|
||||
@@ -108,6 +108,22 @@ CGO_ENABLED=0 go build -tags embedui -o artex ./cmd/artex
|
||||
./artex
|
||||
```
|
||||
|
||||
### 方式五:构建跨平台 Release 压缩包
|
||||
|
||||
`build.sh` 会先构建并嵌入前端,再使用 Go linker 去除调试信息,并在本机安装了 UPX 时进一步压缩二进制。Release 模式默认生成 Linux amd64/arm64、macOS amd64/arm64 和 Windows amd64 的 zip 包:
|
||||
|
||||
```bash
|
||||
./build.sh --release
|
||||
# 产物:dist/artex-0.3.3-*.zip
|
||||
```
|
||||
|
||||
没有 UPX 时脚本仍可生成 linker 压缩后的二进制;CI Release 会将 UPX 视为必需依赖。可用 `ARTEX_TARGETS` 自定义目标,或用 `--no-compress` 禁用 UPX:
|
||||
|
||||
```bash
|
||||
ARTEX_TARGETS=linux/amd64,windows/amd64 ./build.sh --release
|
||||
./build.sh --target darwin/arm64 --no-compress
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 更新升级
|
||||
|
||||
@@ -1,59 +1,244 @@
|
||||
#!/usr/bin/env bash
|
||||
# ARTEX 单二进制构建脚本:静态导出前端并嵌入 Go 二进制。
|
||||
# ARTEX cross-platform release builder.
|
||||
#
|
||||
# 可选环境变量:
|
||||
# ARTEX_TARGET_OS=linux 目标系统,默认当前系统
|
||||
# ARTEX_TARGET_ARCH=amd64 目标架构,默认当前架构
|
||||
# ARTEX_BUILD_VERSION=0.3.3 构建版本,默认使用当前应用版本
|
||||
# ARTEX_OUTPUT=/path/to/artex 输出路径
|
||||
# ARTEX_SKIP_NPM_CI=1 跳过 npm ci,复用现有依赖
|
||||
# ARTEX_GOSUMDB=sum.golang.org Go checksum database
|
||||
# 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 或更高版本)"
|
||||
command -v npm >/dev/null 2>&1 || die "未检测到 npm(前端静态构建需要 Node.js/npm)"
|
||||
command -v rsync >/dev/null 2>&1 || die "未检测到 rsync"
|
||||
|
||||
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
|
||||
|
||||
ARTEX_BUILD_VERSION="${ARTEX_BUILD_VERSION:-0.3.3}"
|
||||
|
||||
ARTEX_BINARY_NAME="artex"
|
||||
[ "$ARTEX_TARGET_OS" = "windows" ] && ARTEX_BINARY_NAME="artex.exe"
|
||||
ARTEX_OUTPUT="${ARTEX_OUTPUT:-dist/artex-${ARTEX_TARGET_OS}-${ARTEX_TARGET_ARCH}/${ARTEX_BINARY_NAME}}"
|
||||
|
||||
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
|
||||
|
||||
info "编译 ${ARTEX_TARGET_OS}/${ARTEX_TARGET_ARCH},版本 ${ARTEX_BUILD_VERSION}"
|
||||
mkdir -p "$(dirname "$ARTEX_OUTPUT")"
|
||||
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="$ARTEX_TARGET_OS" \
|
||||
GOARCH="$ARTEX_TARGET_ARCH" \
|
||||
GOOS="$goos" \
|
||||
GOARCH="$goarch" \
|
||||
go build \
|
||||
-tags embedui \
|
||||
-trimpath \
|
||||
-ldflags "-s -w -X main.version=${ARTEX_BUILD_VERSION}" \
|
||||
-o "$ARTEX_OUTPUT" \
|
||||
-ldflags "-s -w -buildid= -X main.version=${ARTEX_BUILD_VERSION}" \
|
||||
-o "$output" \
|
||||
./cmd/artex
|
||||
|
||||
ok "编译完成:$ARTEX_OUTPUT"
|
||||
if command -v file >/dev/null 2>&1; then
|
||||
file "$ARTEX_OUTPUT"
|
||||
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
|
||||
Reference in new issue
Block a user