diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..d2649d5 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,66 @@ +# 发版工作流:推送 v* 标签 → 构建前端并嵌入 → 交叉编译多平台二进制 +# → 生成发布说明 → 创建 Gitea Release 并上传全部产物。 +# +# 发版操作: +# git tag -a v0.0.1 -m "v0.0.1" +# git push origin v0.0.1 +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + # 完整历史:供 git log 生成上一个 tag 以来的变更日志 + - name: 检出代码 + uses: https://gitea.com/actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 安装 Go(版本取自 go.mod) + uses: https://gitea.com/actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: 安装 Node + uses: https://gitea.com/actions/setup-node@v4 + with: + node-version: 24 + + - name: 构建前端并嵌入(web/dist → internal/webui/dist) + run: make web sync-assets + + - name: 交叉编译发布产物 + run: scripts/build-release.sh "${GITEA_REF_NAME}" + + # 首个 tag 无上一版本,变更日志段自动省略 + - name: 生成发布说明 + run: | + prev=$(git describe --tags --abbrev=0 "${GITEA_SHA}^" 2>/dev/null || true) + { + echo "## pn ${GITEA_REF_NAME}" + echo + echo "- commit: \`${GITEA_SHA}\`" + echo "- 构建时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + if [ -n "$prev" ]; then + echo + echo "### 自 ${prev} 以来的变更" + echo + git log --oneline --no-decorate "${prev}..${GITEA_SHA}" | sed 's/^/- /' + fi + } > release-notes.md + cat release-notes.md + + - name: 创建 Release 并上传产物 + uses: https://gitea.com/actions/release-action@main + with: + files: |- + dist/* + body_path: release-notes.md diff --git a/.gitignore b/.gitignore index d8da080..41b6562 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,8 @@ internal/webui/dist/* /pn /pn-linux-* +# 发布产物(make dist / CI 发版构建输出) +/dist/ + # 系统文件 .DS_Store \ No newline at end of file diff --git a/Makefile b/Makefile index 8303261..78b098b 100644 --- a/Makefile +++ b/Makefile @@ -5,17 +5,21 @@ # make sync-assets 拷贝 web/dist → internal/webui/dist(go:embed 约束,§8.3-1) # make build 全量构建(前端 + 单二进制 pn) # make linux 交叉编译 linux/amd64 +# make dist 全平台发布产物 → dist/(VERSION ?= git describe,与 CI 发版共用脚本) # make test Go 测试 + 前端测试 # make smoke 真实构建冒烟测试(§8.3-6) # make dev 双进程热重载(air + vite dev) # make vulncheck govulncheck +# +# 发版:git tag -a v0.0.1 -m "v0.0.1" && git push origin v0.0.1 +# (推送 v* 标签触发 .gitea/workflows/release.yml 自动构建并创建 Release) VERSION ?= $(shell git describe --tags --always 2>/dev/null || echo dev) COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo none) DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) LDFLAGS := -s -w -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(DATE) -.PHONY: web sync-assets build linux test smoke dev vulncheck clean +.PHONY: web sync-assets build linux dist test smoke dev vulncheck clean web: cd web && npm ci && npm run build @@ -33,6 +37,10 @@ linux: web sync-assets CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="$(LDFLAGS)" \ -o pn-linux-amd64 ./cmd/pn +# 全平台发布产物(版本注入逻辑与 CI 发版共用 scripts/build-release.sh) +dist: web sync-assets + scripts/build-release.sh "$(VERSION)" + test: go vet ./... go test ./... @@ -51,3 +59,4 @@ vulncheck: clean: rm -f pn pn-linux-amd64 + rm -rf dist diff --git a/README.md b/README.md index 85c2090..a11a84d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,22 @@ sudo systemctl enable --now pn pn-maint.timer **恢复 SOP**:停服 → 删除 `data/pn.db-wal` 与 `-shm` → 以备份文件替换 `pn.db` → 起服。 +## 版本与发版 + +版本号以 git tag 为唯一来源(语义化版本,从 `v0.0.1` 开始),构建时经 ldflags 注入, +`./pn version` 可查看: + +```bash +git tag -a v0.0.1 -m "v0.0.1" +git push origin v0.0.1 # 推送 v* 标签即触发自动发版 +``` + +推送 `v*` 标签后,Gitea Actions([.gitea/workflows/release.yml](.gitea/workflows/release.yml)) +自动构建前端并嵌入,交叉编译 linux/darwin(amd64 + arm64)共 4 个产物, +连同 SHA256SUMS 与上一版本以来的变更日志一起发布到仓库的 Release 页面。 + +本地验证发布产物:`make dist`(版本缺省 `git describe --tags --always`,输出到 `dist/`)。 + ## 架构 ``` diff --git a/scripts/build-release.sh b/scripts/build-release.sh new file mode 100755 index 0000000..2a79279 --- /dev/null +++ b/scripts/build-release.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# 发布产物构建:交叉编译全部目标平台 → dist/,打包并生成 SHA256SUMS。 +# 本地 `make dist` 与 Gitea Actions 发版工作流共用本脚本,保证产物一致。 +# +# 用法: scripts/build-release.sh [版本号] +# 版本号缺省取 `git describe --tags --always`(无 tag 时回退 dev)。 +# 前置条件:internal/webui/dist 已就绪(make web sync-assets)。 +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +VERSION="${1:-$(git describe --tags --always 2>/dev/null || echo dev)}" +COMMIT="$(git rev-parse --short HEAD 2>/dev/null || echo none)" +DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +LDFLAGS="-s -w -X main.version=$VERSION -X main.commit=$COMMIT -X main.buildDate=$DATE" + +# 纯静态(CGO_ENABLED=0,见 Makefile),单机即可交叉编译全部目标。 +# 不含 windows:internal/store 对 syscall.Umask 等 Unix API 有硬依赖(备份权限加固)。 +TARGETS=( + linux/amd64 + linux/arm64 + darwin/amd64 + darwin/arm64 +) + +rm -rf dist +mkdir -p dist + +for t in "${TARGETS[@]}"; do + os="${t%/*}" arch="${t#*/}" + name="pn-${VERSION}-${os}-${arch}" + echo "==> 构建 $name" + CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" \ + go build -trimpath -ldflags="$LDFLAGS" -o "dist/$name" ./cmd/pn + # tar.gz 保留可执行位 + (cd dist && tar czf "$name.tar.gz" "$name" && rm "$name") +done + +# 生成校验和(Linux 为 sha256sum,macOS 回退 shasum) +echo "==> 生成 SHA256SUMS" +( + cd dist + if command -v sha256sum >/dev/null 2>&1; then + sha256sum -- ./* > SHA256SUMS + else + shasum -a 256 -- ./* > SHA256SUMS + fi +) + +echo "==> 完成:dist/" +ls -lh dist/