# 发版工作流:推送 v* 标签 → 构建前端并嵌入 → 交叉编译多平台二进制 # → 生成发布说明 → 通过 Gitea API 创建 Release 并上传产物。 # # 发版操作: # git tag -a v0.0.1 -m "v0.0.1" # git push origin v0.0.1 # # 实现说明:刻意不使用任何 JS 类 action(checkout/setup-*/release-*)—— # 自托管 runner 常把实例地址注册成 localhost,job 容器内不可达, # 且默认容器镜像可能没有 node。此处全程纯 run 步骤 + 公网域名访问实例, # 容器固定 node:24-bookworm(自带 node 与 git,npm 构建前端所需)。 name: Release on: push: tags: - 'v*' permissions: contents: write env: SITE: https://git.airnan.cn REPO: wangairnan/pure-note jobs: release: runs-on: ubuntu-latest container: image: node:24-bookworm env: # 这台 runner 只保证 GITHUB_* 兼容层(GITEA_REF_NAME 等别名不可靠), # 一律经表达式显式注入,不直接读 GITEA_* 环境变量 TAG: ${{ github.ref_name }} TOKEN: ${{ secrets.GITHUB_TOKEN || secrets.GITEA_TOKEN || secrets.FORGEJO_TOKEN }} steps: # 完整历史:供 git log 生成上一个 tag 以来的变更日志 - name: 检出代码 run: | git clone "$SITE/$REPO.git" . git checkout --detach "$TAG" git log --oneline -1 - name: 安装 Go(版本取自 go.mod) run: | set -euo pipefail GOVER=$(awk '$1 == "go" {print $2}' go.mod) echo "==> 安装 Go $GOVER" # go.dev 在部分网络不可达,回退官方中国镜像 curl -fsSL "https://go.dev/dl/go${GOVER}.linux-amd64.tar.gz" -o /tmp/go.tgz \ || curl -fsSL "https://golang.google.cn/dl/go${GOVER}.linux-amd64.tar.gz" -o /tmp/go.tgz tar -C /usr/local -xzf /tmp/go.tgz /usr/local/go/bin/go version - name: 构建前端并嵌入(web/dist → internal/webui/dist) run: make web sync-assets || (npm config set registry https://registry.npmmirror.com && make web sync-assets) - name: 交叉编译发布产物 run: | set -euo pipefail export PATH=/usr/local/go/bin:$PATH # 模块代理双层回退:官方代理不可达时走 goproxy.cn export GOPROXY=https://proxy.golang.org,https://goproxy.cn,direct scripts/build-release.sh "$TAG" # 首个 tag 无上一版本,变更日志段自动省略 - name: 生成发布说明 run: | set -euo pipefail prev=$(git describe --tags --abbrev=0 "HEAD^" 2>/dev/null || true) short=$(git rev-parse --short HEAD) { echo "## pn ${TAG}" echo echo "- commit: \`$short\`" echo "- 构建时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ')" if [ -n "$prev" ]; then echo echo "### 自 ${prev} 以来的变更" echo git log --oneline --no-decorate "${prev}..HEAD" | sed 's/^/- /' fi } > release-notes.md cat release-notes.md # 重跑幂等:release 已存在时更新说明、清旧附件后重传 - name: 创建 Release 并上传产物 run: | set -euo pipefail API="$SITE/api/v1/repos/$REPO" AUTH="Authorization: token ${TOKEN}" REQ=$(node -e 'const fs=require("fs");process.stdout.write(JSON.stringify({tag_name:process.env.TAG,name:"pn "+process.env.TAG,body:fs.readFileSync("release-notes.md","utf8")}))') # 同 tag release 已存在(如失败重跑)时回退为查询 REL=$(curl -fsS -X POST "$API/releases" -H "$AUTH" -H 'Content-Type: application/json' -d "$REQ") \ || REL=$(curl -fsS "$API/releases/tags/${TAG}" -H "$AUTH") RID=$(REL="$REL" node -e 'process.stdout.write(String(JSON.parse(process.env.REL).id))') echo "==> release id=$RID" # 发布说明以本次构建为准(回退路径下 POST 已冲突,须补写) curl -fsS -X PATCH "$API/releases/$RID" -H "$AUTH" -H 'Content-Type: application/json' -d "$REQ" >/dev/null # 清除旧附件,避免重跑产生同名副本 ASSETS=$(curl -fsS "$API/releases/$RID/assets" -H "$AUTH") ASSETS="$ASSETS" node -e 'JSON.parse(process.env.ASSETS).forEach(a=>console.log(a.id))' | while read -r aid; do curl -fsS -X DELETE "$API/releases/$RID/assets/$aid" -H "$AUTH" >/dev/null done for f in dist/*; do echo "==> 上传 $(basename "$f")" curl -fsS -X POST "$API/releases/$RID/assets?name=$(basename "$f")" -H "$AUTH" -F "attachment=@$f" >/dev/null done echo "==> 完成:$SITE/$REPO/releases/tag/${TAG}"