Files
jiu/scripts/ci/release-client.sh
T
wangjia aa7099ba94 feat(devops): 发版拆分为 client/site/server 三条独立流水线
将单一 CI/CD 流水线拆成三条互不影响的发布流水线,各有 tag 前缀、独立版本
序列与独立 CHANGELOG:

- client(client-v*):Flutter 全平台 + version.yaml 自更新清单
- site(site-v*):web/ Eleventy 营销站,不含 web 版 app
- server(server-v*):backend Go 服务 + 共享基建 nginx/systemd

新增 3 个 workflow(deploy-client/site/server.yml)替换 deploy.yml;CI 脚本
按 part 拆分为 compile-/release-/deploy-{client,site,server}.sh,抽出公共函数
lib-forgejo.sh;compile-{macos,android,ios,windows}.sh 改去 client-v 前缀;
manual.yml 按前缀路由回滚。

跨流水线解耦:version.yaml 归 client,后端每请求实时读取(不重启、不触发
server 流水线);官网下载页的版本徽章/下载链接/更新日志时间线运行时经
/api/v1/public/release 动态拉取(API 不可达回退构建时静态内容)。为此一次性
扩展后端 changelog 字段(version.go/public.go)与 download.njk 动态渲染。

CHANGELOG.md 重命名为 CHANGELOG-client.md,新增 CHANGELOG-site/server.md;
重写 /release 命令为 /release <part> [version];同步更新 CLAUDE.md 与部署文档。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 08:21:28 +08:00

120 lines
4.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# release-client.sh <tag> — client pipeline release.
# 1. Update backend/config/version.yaml: version / build_number / release_notes
# / macos+windows+android download URLs / changelog[] (latest 3 from
# CHANGELOG-client.md).
# 2. Create the Forgejo Release client-vX and upload the app artifacts +
# version.yaml (so manual rollback can re-fetch the exact manifest).
#
# version.yaml is client-owned: the backend reads it per-request, so deploying
# it does NOT require a backend restart or a server-pipeline run.
set -euo pipefail
# shellcheck source=scripts/ci/lib-forgejo.sh
. "$(dirname "$0")/lib-forgejo.sh"
TAG="$1"
VER="$(ver_from_tag "$TAG")"
echo "==> release-client: tag=${TAG} version=${VER}"
NOTES=$(extract_release_notes CHANGELOG-client.md)
echo "==> release-client: release_notes=${NOTES}"
# --- Update version.yaml (scalars + download URLs) and append changelog[] ---
python3 - "$VER" "$NOTES" "CHANGELOG-client.md" <<'PYEOF'
import sys, re, json
ver = sys.argv[1]
notes = sys.argv[2]
changelog_file = sys.argv[3]
base = "https://jiu.51yanmei.com/downloads"
mac_url = f"{base}/jiu-macos-x64.zip"
win_url = f"{base}/jiu-windows-x64-setup.exe"
android_url = f"{base}/jiu-android.apk"
# Read current version.yaml, dropping any existing trailing `changelog:` block
# (changelog is always emitted last, so everything from it to EOF is regenerated).
with open('backend/config/version.yaml', encoding='utf-8') as f:
raw = f.read()
raw = re.split(r'\nchangelog:', raw, maxsplit=1)[0].rstrip('\n') + '\n'
out = []
for line in raw.splitlines(keepends=True):
if line.startswith('version:'):
out.append('version: "%s"\n' % ver)
elif line.startswith('build_number:'):
try:
b = int(line.split(':', 1)[1].strip()) + 1
except Exception:
b = 1
out.append('build_number: %d\n' % b)
elif line.startswith('release_notes:'):
safe = notes.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '')
out.append('release_notes: "%s"\n' % safe)
elif line.startswith(' macos:'):
out.append(' macos: "%s"\n' % mac_url)
elif line.startswith(' windows:'):
out.append(' windows: "%s"\n' % win_url)
elif line.startswith(' android:'):
out.append(' android: "%s"\n' % android_url)
else:
out.append(line)
# Parse CHANGELOG-client.md -> latest 3 entries (same shape as web/_data/changelog.js)
versions = []
cur = None
sec = None
for line in open(changelog_file, encoding='utf-8'):
line = line.rstrip('\n')
m = re.match(r'^## \[([^\]]+)\] - (\d{4}-\d{2}-\d{2})', line)
if m:
if cur:
versions.append(cur)
if len(versions) >= 3:
cur = None
break
cur = {'version': m.group(1), 'date': m.group(2), 'intro': '', 'sections': []}
sec = None
continue
ms = re.match(r'^### (.+)', line)
if ms and cur is not None:
sec = {'type': ms.group(1).strip(), 'items': []}
cur['sections'].append(sec)
continue
mi = re.match(r'^- (.+)', line)
if mi and cur is not None and sec is not None:
sec['items'].append(mi.group(1).strip())
continue
if line.strip() and cur is not None and sec is None and not line.startswith('#'):
cur['intro'] = (cur['intro'] + ' ' + line.strip()).strip()
if cur is not None and len(versions) < 3:
versions.append(cur)
# Append changelog as a JSON array (valid YAML flow style — no pyyaml needed).
text = ''.join(out).rstrip('\n') + '\n'
text += 'changelog: ' + json.dumps(versions, ensure_ascii=False) + '\n'
with open('backend/config/version.yaml', 'w', encoding='utf-8') as f:
f.write(text)
print('version.yaml updated to', ver, '|', len(versions), 'changelog entries')
PYEOF
# Stage version.yaml as a release asset so manual rollback can re-fetch it.
mkdir -p dist
cp backend/config/version.yaml dist/version.yaml
BODY=$(python3 - "$TAG" "$NOTES" <<'PYEOF'
import sys, json
print(json.dumps('## ' + sys.argv[1] + '\n\n' + sys.argv[2]))
PYEOF
)
create_release "$TAG" "$BODY"
upload_asset dist/web.tar.gz
upload_asset dist/version.yaml
[ -f dist/jiu-macos-x64.zip ] && upload_asset dist/jiu-macos-x64.zip || true
[ -f dist/jiu-windows-x64-setup.exe ] && upload_asset dist/jiu-windows-x64-setup.exe || true
[ -f dist/jiu-android.apk ] && upload_asset dist/jiu-android.apk || true
echo "==> release-client: done — Release ${TAG} created"