Repository navigation
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·263 lines (239 loc) · 8.86 KB
/
Copy pathbuild.sh
File metadata and controls
executable file
·263 lines (239 loc) · 8.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#!/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=v0.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=off UPX mode: off, auto, or required.
# 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
--upx 强制使用 UPX 压缩二进制(可能影响部分 Linux 环境兼容性)
--no-compress 不使用 UPX,仅使用 Go linker 裁剪并压缩 zip
--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:-off}"
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
;;
--upx)
ARTEX_COMPRESS=required
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}"
if [ -z "${ARTEX_BUILD_VERSION:-}" ]; then
if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
ARTEX_BUILD_VERSION="$(git describe --tags --always --dirty)"
else
ARTEX_BUILD_VERSION="dev"
fi
fi
# 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 必须是 off、auto 或 required" ;;
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/"
# 守护启动脚本是正式入口:页面上的一键更新要靠它在进程退出后重新拉起,
# 直接跑 artex 本体的话更新完就再也起不来了。按目标系统只带对应的那一份。
if [ "$goos" = "windows" ]; then
cp start.bat "$package_root/"
else
cp start.sh "$package_root/"
chmod +x "$package_root/start.sh"
fi
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