From 8e94e7e1f609b45377f5649acc774bb21d508268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=8E=E7=94=9F=E6=B4=BECoder=EF=BD=9E?= Date: Mon, 2 Mar 2026 23:46:03 +0800 Subject: [PATCH] ci: build linux release artifacts with slimmer flags --- .github/workflows/release.yml | 49 ++++++++++++++++++++++++++++++++--- Makefile | 47 +++++++++++++++++++++++++++++---- README.md | 21 +++++++++++++++ README_EN.md | 21 +++++++++++++++ pkg/nodes/registry_server.go | 2 -- pkg/tools/process_manager.go | 2 ++ pkg/tools/sessions_tool.go | 1 - 7 files changed, 132 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a986823..5f2d964 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,12 +47,55 @@ jobs: echo "version=$version" >> "$GITHUB_OUTPUT" echo "Build version: $version (from $raw)" - - name: Build all platforms + - name: Build release binaries (Linux minimal, others default) run: | - make build-all VERSION="${{ steps.ver.outputs.version }}" BUILD_TARGETS="linux/amd64 linux/arm64 linux/riscv64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64" + set -euo pipefail + mkdir -p build + make sync-embed-workspace + trap 'make cleanup-embed-workspace' EXIT + + # Build non-Linux targets with default flags. + for target in darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do + goos="${target%/*}" + goarch="${target#*/}" + out="build/clawgo-${goos}-${goarch}" + if [ "${goos}" = "windows" ]; then out="${out}.exe"; fi + CGO_ENABLED=0 GOOS="${goos}" GOARCH="${goarch}" go build \ + -trimpath -buildvcs=false \ + -ldflags "-X main.version=${{ steps.ver.outputs.version }} -X main.buildTime=$(date +%FT%T%z) -s -w" \ + -o "${out}" ./cmd/clawgo + done + + # Linux targets use slimmer flags without disabling channel capabilities. + for arch in amd64 arm64 riscv64; do + out="build/clawgo-linux-${arch}" + CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" go build \ + -trimpath -buildvcs=false -tags "purego,netgo,osusergo" \ + -ldflags "-X main.version=${{ steps.ver.outputs.version }} -X main.buildTime=$(date +%FT%T%z) -s -w -buildid=" \ + -o "${out}" ./cmd/clawgo + done - name: Package artifacts - run: make package-all VERSION="${{ steps.ver.outputs.version }}" BUILD_TARGETS="linux/amd64 linux/arm64 linux/riscv64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64" + run: | + set -euo pipefail + rm -f build/checksums.txt + for target in linux/amd64 linux/arm64 linux/riscv64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do + goos="${target%/*}" + goarch="${target#*/}" + bin="clawgo-${goos}-${goarch}" + if [ "${goos}" = "windows" ]; then + bin="${bin}.exe" + zip -q -j "build/clawgo-${goos}-${goarch}.zip" "build/${bin}" + else + tar -czf "build/clawgo-${goos}-${goarch}.tar.gz" -C build "${bin}" + fi + done + + if command -v sha256sum >/dev/null 2>&1; then + (cd build && sha256sum *.tar.gz *.zip | tee checksums.txt) + else + (cd build && shasum -a 256 *.tar.gz *.zip | tee checksums.txt) + fi - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/Makefile b/Makefile index c8977d1..5499b82 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all build build-all package-all install install-win uninstall clean help test install-bootstrap-docs sync-embed-workspace cleanup-embed-workspace test-only clean-test-artifacts +.PHONY: all build build-linux-slim build-all package-all install install-win uninstall clean help test install-bootstrap-docs sync-embed-workspace cleanup-embed-workspace test-only clean-test-artifacts # Build variables BINARY_NAME=clawgo @@ -15,11 +15,21 @@ EXTRA_LDFLAGS= ifeq ($(STRIP_SYMBOLS),1) EXTRA_LDFLAGS+= -s -w endif -LDFLAGS=-ldflags "$(BASE_LDFLAGS)$(EXTRA_LDFLAGS)" +LDFLAGS=-ldflags "$(BASE_LDFLAGS) $(EXTRA_LDFLAGS)" # Go variables GO?=go -GOFLAGS?=-v +GOFLAGS?= +BUILD_FLAGS?=-trimpath -buildvcs=false +COMPRESS_BINARY?=0 +UPX_FLAGS?=--best --lzma + +# Linux slim build knobs (opt-in, keeps default behavior unchanged) +LINUX_SLIM_TAGS?=purego,netgo,osusergo +LINUX_SLIM_CGO?=0 +LINUX_SLIM_LDFLAGS?=-buildid= +LINUX_SLIM_BUILD_FLAGS?=-trimpath -buildvcs=false -tags "$(LINUX_SLIM_TAGS)" +LINUX_SLIM_PATH=$(BUILD_DIR)/$(BINARY_NAME)-linux-$(ARCH)-slim # Cross-platform build matrix (space-separated GOOS/GOARCH pairs) BUILD_TARGETS?=linux/amd64 linux/arm64 linux/riscv64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 @@ -78,10 +88,34 @@ build: sync-embed-workspace @echo "Building $(BINARY_NAME) for $(PLATFORM)/$(ARCH)..." @mkdir -p $(BUILD_DIR) @set -e; trap '$(MAKE) cleanup-embed-workspace' EXIT; \ - $(GO) build $(GOFLAGS) $(LDFLAGS) -o $(BINARY_PATH) ./$(CMD_DIR) + $(GO) build $(GOFLAGS) $(BUILD_FLAGS) $(LDFLAGS) -o $(BINARY_PATH) ./$(CMD_DIR); \ + if [ "$(COMPRESS_BINARY)" = "1" ]; then \ + if command -v upx >/dev/null 2>&1; then \ + upx $(UPX_FLAGS) "$(BINARY_PATH)" >/dev/null; \ + echo "Compressed binary with upx ($(UPX_FLAGS))"; \ + else \ + echo "Warning: COMPRESS_BINARY=1 but upx not found, skipping compression"; \ + fi; \ + fi @echo "Build complete: $(BINARY_PATH)" @ln -sf $(BINARY_NAME)-$(PLATFORM)-$(ARCH) $(BUILD_DIR)/$(BINARY_NAME) +## build-linux-slim: Build a Linux-only slim binary (no feature trimming, no channel disabling) +build-linux-slim: sync-embed-workspace + @echo "Building $(BINARY_NAME) slim profile for linux/$(ARCH)..." + @mkdir -p $(BUILD_DIR) + @set -e; trap '$(MAKE) cleanup-embed-workspace' EXIT; \ + CGO_ENABLED=$(LINUX_SLIM_CGO) GOOS=linux GOARCH=$(ARCH) $(GO) build $(GOFLAGS) $(LINUX_SLIM_BUILD_FLAGS) -ldflags "$(BASE_LDFLAGS) $(EXTRA_LDFLAGS) $(LINUX_SLIM_LDFLAGS)" -o $(LINUX_SLIM_PATH) ./$(CMD_DIR); \ + if [ "$(COMPRESS_BINARY)" = "1" ]; then \ + if command -v upx >/dev/null 2>&1; then \ + upx $(UPX_FLAGS) "$(LINUX_SLIM_PATH)" >/dev/null; \ + echo "Compressed slim binary with upx ($(UPX_FLAGS))"; \ + else \ + echo "Warning: COMPRESS_BINARY=1 but upx not found, skipping compression"; \ + fi; \ + fi + @echo "Build complete: $(LINUX_SLIM_PATH)" + ## build-all: Build clawgo for all configured platforms (override with BUILD_TARGETS="linux/amd64 ...") build-all: sync-embed-workspace @echo "Building for multiple platforms: $(BUILD_TARGETS)" @@ -93,7 +127,10 @@ build-all: sync-embed-workspace out="$(BUILD_DIR)/$(BINARY_NAME)-$$goos-$$goarch"; \ if [ "$$goos" = "windows" ]; then out="$$out.exe"; fi; \ echo " -> $$goos/$$goarch"; \ - CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch $(GO) build $(GOFLAGS) $(LDFLAGS) -o "$$out" ./$(CMD_DIR); \ + CGO_ENABLED=0 GOOS=$$goos GOARCH=$$goarch $(GO) build $(GOFLAGS) $(BUILD_FLAGS) $(LDFLAGS) -o "$$out" ./$(CMD_DIR); \ + if [ "$(COMPRESS_BINARY)" = "1" ] && command -v upx >/dev/null 2>&1; then \ + upx $(UPX_FLAGS) "$$out" >/dev/null; \ + fi; \ done @echo "All builds complete" diff --git a/README.md b/README.md index 411ff80..ecabd99 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,16 @@ http://:/webui?token= make build-all ``` +### Linux 专项瘦身构建(不禁用通道) + +```bash +make build-linux-slim +``` + +说明(仅 Linux): +- 在不禁用任何通道能力前提下,启用 `purego,netgo,osusergo` 与 `CGO_ENABLED=0`,降低体积并减少动态库依赖。 +- 可选叠加 `COMPRESS_BINARY=1`(若安装 upx)做进一步压缩。 + 默认矩阵: - linux/amd64 - linux/arm64 @@ -158,6 +168,17 @@ make build-all make build-all BUILD_TARGETS="linux/amd64 linux/arm64 darwin/arm64 windows/amd64" ``` +### 极致瘦身构建(目标 <10MB) + +```bash +make build COMPRESS_BINARY=1 +``` + +说明: +- 默认已启用 `-trimpath -buildvcs=false -s -w`,可减少路径与符号信息。 +- `COMPRESS_BINARY=1` 时会尝试使用 `upx --best --lzma` 进一步压缩可执行文件。 +- 若环境未安装 `upx`,会自动跳过并给出提示,不影响构建成功。 + ### 打包与校验 ```bash diff --git a/README_EN.md b/README_EN.md index ea760c6..aab36b3 100644 --- a/README_EN.md +++ b/README_EN.md @@ -143,6 +143,16 @@ Main pages: make build-all ``` +### Linux slim build (without disabling channels) + +```bash +make build-linux-slim +``` + +Notes (Linux only): +- Keeps all channel capabilities while enabling `purego,netgo,osusergo` with `CGO_ENABLED=0` to reduce size and dynamic library coupling. +- Optionally combine with `COMPRESS_BINARY=1` (if `upx` is installed) for additional compression. + Default matrix: - linux/amd64 - linux/arm64 @@ -158,6 +168,17 @@ Default matrix: make build-all BUILD_TARGETS="linux/amd64 linux/arm64 darwin/arm64 windows/amd64" ``` +### Ultra-slim build (target <10MB) + +```bash +make build COMPRESS_BINARY=1 +``` + +Notes: +- Default build now uses `-trimpath -buildvcs=false -s -w` to remove path/symbol overhead. +- With `COMPRESS_BINARY=1`, the build will try `upx --best --lzma` for further executable compression. +- If `upx` is unavailable, build still succeeds and prints a warning. + ### Package + checksums ```bash diff --git a/pkg/nodes/registry_server.go b/pkg/nodes/registry_server.go index 841f41e..ed6e28a 100644 --- a/pkg/nodes/registry_server.go +++ b/pkg/nodes/registry_server.go @@ -980,7 +980,6 @@ func buildSkillMarkdown(name, desc string, tools []string, systemPrompt string) } toolLines := make([]string, 0, len(tools)) for _, t := range tools { - t = t if t == "" { continue } @@ -1188,7 +1187,6 @@ func normalizeCronJobs(v interface{}) []map[string]interface{} { } func queryClawHubSkillVersion(ctx context.Context, skill string) (found bool, version string, err error) { - skill = skill if skill == "" { return false, "", fmt.Errorf("skill empty") } diff --git a/pkg/tools/process_manager.go b/pkg/tools/process_manager.go index 304dab1..01b12d0 100644 --- a/pkg/tools/process_manager.go +++ b/pkg/tools/process_manager.go @@ -79,10 +79,12 @@ func (m *ProcessManager) Start(parent context.Context, command, cwd string) (str } stdout, err := cmd.StdoutPipe() if err != nil { + cancel() return "", err } stderr, err := cmd.StderrPipe() if err != nil { + cancel() return "", err } s := &processSession{ID: id, Command: command, StartedAt: time.Now().UTC(), cmd: cmd, cancel: cancel, done: make(chan struct{}), logQueue: make(chan []byte, 128)} diff --git a/pkg/tools/sessions_tool.go b/pkg/tools/sessions_tool.go index 3cf7c63..ad23862 100644 --- a/pkg/tools/sessions_tool.go +++ b/pkg/tools/sessions_tool.go @@ -163,7 +163,6 @@ func (t *SessionsTool) Execute(ctx context.Context, args map[string]interface{}) return "sessions history unavailable", nil } key, _ := args["key"].(string) - key = key if key == "" { return "key is required for history", nil }