Skip to content

Pi Chinese Release #503

Pi Chinese Release

Pi Chinese Release #503

Workflow file for this run

name: Pi Chinese Release
# pi(earendil-works/pi)汉化版【正式版】自动构建发布流水线
# 每小时检查上游最新正式 Release:有新正式版则同步 → 校验翻译词表 100% 匹配 → 应用汉化 → 构建 → 发布 v<上游版本>-cn Release
# 实时更新版见 pi-cn-nightly.yml(跟随上游 main 每小时构建)
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
inputs:
upstream_ref:
description: "上游分支或 commit(留空为上游最新正式 Release)"
required: false
type: string
release_tag:
description: "本仓库发布 tag(留空为 v<上游版本>-cn)"
required: false
type: string
concurrency:
group: pi-cn-release
cancel-in-progress: false
permissions:
contents: write
env:
UPSTREAM_REPO: earendil-works/pi
jobs:
resolve:
name: Resolve upstream commit
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
should_build: ${{ steps.resolve.outputs.should_build }}
upstream_ref: ${{ steps.resolve.outputs.upstream_ref }}
upstream_sha: ${{ steps.resolve.outputs.upstream_sha }}
upstream_version: ${{ steps.resolve.outputs.upstream_version }}
release_tag: ${{ steps.resolve.outputs.release_tag }}
steps:
- id: resolve
env:
GH_TOKEN: ${{ github.token }}
REQUESTED_UPSTREAM: ${{ github.event.inputs.upstream_ref }}
REQUESTED_RELEASE: ${{ github.event.inputs.release_tag }}
run: |
set -euo pipefail
# 默认跟随上游最新正式 Release(tag);dispatch 可指定任意分支/commit
if [[ -n "${REQUESTED_UPSTREAM}" ]]; then
upstream="${REQUESTED_UPSTREAM}"
if [[ "$upstream" =~ ^[0-9a-f]{40}$ ]]; then
sha="$upstream"
else
sha="$(gh api "repos/${UPSTREAM_REPO}/commits/${upstream}" --jq '.sha')"
fi
else
upstream="$(gh api "repos/${UPSTREAM_REPO}/releases/latest" --jq '.tag_name')"
sha="$(gh api "repos/${UPSTREAM_REPO}/commits/${upstream}" --jq '.sha')"
fi
# 上游版本号(packages/coding-agent/package.json)
version="$(gh api "repos/${UPSTREAM_REPO}/contents/packages/coding-agent/package.json?ref=${sha}" \
--jq '.content' | base64 -d | jq -r '.version')"
[[ -n "$version" && "$version" != "null" ]] || {
echo "无法从上游解析版本号" >&2
exit 1
}
release="${REQUESTED_RELEASE:-v${version}-cn}"
should_build=true
if [[ "${{ github.event_name }}" == "schedule" ]] &&
gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${release}" >/dev/null 2>&1; then
should_build=false
fi
echo "should_build=$should_build" >>"$GITHUB_OUTPUT"
echo "upstream_ref=$upstream" >>"$GITHUB_OUTPUT"
echo "upstream_sha=$sha" >>"$GITHUB_OUTPUT"
echo "upstream_version=$version" >>"$GITHUB_OUTPUT"
echo "release_tag=$release" >>"$GITHUB_OUTPUT"
echo "上游: ${UPSTREAM_REPO}@${sha:0:12} (v${version});发布 tag: ${release};需要构建: $should_build"
translation-gate:
name: Require complete translation match
needs: resolve
if: needs.resolve.outputs.should_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Sync upstream main
run: |
set -euo pipefail
git remote add upstream "https://github.com/${UPSTREAM_REPO}.git"
git fetch --depth 1 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
git config merge.ours.driver true
printf '.github/workflows/build-binaries.yml merge=ours\n' >> .git/info/attributes
printf 'packages/ai/src/providers/cloudflare-ai-gateway.ts merge=ours\n' >> .git/info/attributes
git merge --allow-unrelated-histories --no-edit FETCH_HEAD 2>&1 || true
# Resolve add/add conflicts: take upstream for source, ours for CN files
if [ -n "$(git diff --name-only --diff-filter=U 2>/dev/null)" ]; then
echo "Resolving conflicts: taking upstream for source code"
git diff --name-only --diff-filter=U | while read -r f; do
git checkout --theirs "$f" 2>/dev/null || true
done
# Restore our fork-specific files
git checkout HEAD -- .github/workflows/ pi-i18n/ packages/ai/src/providers/cloudflare-ai-gateway.ts 2>/dev/null || true
git add -A 2>/dev/null || true
git commit --no-edit 2>&1 || echo "Merge commit complete with conflicts resolved"
fi
git log --oneline -3
- name: Verify every translation entry still matches uniquely
run: |
set -euo pipefail
python3 pi-i18n/apply_translations.py verify \
--translations pi-i18n/translations --source . --strict --min-match-rate 1
build:
name: Build translated pi (${{ matrix.platform }})
needs: [resolve, translation-gate]
if: needs.resolve.outputs.should_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
platform: [linux-x64, windows-x64]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.14
- uses: actions/setup-node@v6
with:
node-version: "22"
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev fd-find ripgrep
sudo ln -s "$(which fdfind)" /usr/local/bin/fd
- name: Sync upstream main
run: |
set -euo pipefail
git remote add upstream "https://github.com/${UPSTREAM_REPO}.git"
git fetch --depth 1 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
git config merge.ours.driver true
printf '.github/workflows/build-binaries.yml merge=ours\n' >> .git/info/attributes
printf 'packages/ai/src/providers/cloudflare-ai-gateway.ts merge=ours\n' >> .git/info/attributes
git merge --allow-unrelated-histories --no-edit FETCH_HEAD 2>&1 || true
if [ -n "$(git diff --name-only --diff-filter=U 2>/dev/null)" ]; then
echo "Resolving add/add conflicts: taking upstream for source"
git diff --name-only --diff-filter=U | while read -r f; do
git checkout --theirs "$f" 2>/dev/null || true
done
git checkout HEAD -- .github/workflows/ pi-i18n/ packages/ai/src/providers/cloudflare-ai-gateway.ts 2>/dev/null || true
git add -A 2>/dev/null || true
git commit --no-edit || true
fi
- name: Verify translation gate (100% match required)
run: |
set -euo pipefail
python3 pi-i18n/apply_translations.py verify \
--translations pi-i18n/translations --source . --strict --min-match-rate 1
- name: Apply translations
run: |
set -euo pipefail
python3 pi-i18n/apply_translations.py apply \
--translations pi-i18n/translations --source . --strict --min-match-rate 1
- name: Hydrate model data
run: |
set -euo pipefail
npm ci
npm run hydrate:model-data
- name: Build ${{ matrix.platform }} portable binary
run: |
set -euo pipefail
./scripts/build-binaries.sh \
--offline-model-data \
--platform "${{ matrix.platform }}" \
--out "${GITHUB_WORKSPACE}/dist-binaries"
- name: Package binary artifact
env:
PLATFORM: ${{ matrix.platform }}
VERSION: ${{ needs.resolve.outputs.upstream_version }}
run: |
set -euo pipefail
mkdir -p dist
if [[ "$PLATFORM" == windows-* ]]; then
(cd dist-binaries/windows-x64 && zip -r "${GITHUB_WORKSPACE}/dist/pi-cn-${VERSION}-windows-x64.zip" .)
else
tar -czf "dist/pi-cn-${VERSION}-linux-x64.tar.gz" -C dist-binaries/linux-x64 .
fi
- uses: actions/upload-artifact@v7
with:
name: pi-cn-${{ matrix.platform }}
path: dist/*
if-no-files-found: error
build-source:
name: Package translated source archive
needs: [resolve, translation-gate]
if: needs.resolve.outputs.should_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Sync upstream main and apply translations
run: |
set -euo pipefail
git remote add upstream "https://github.com/${UPSTREAM_REPO}.git"
git fetch --depth 1 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
git config merge.ours.driver true
printf '.github/workflows/build-binaries.yml merge=ours\n' >> .git/info/attributes
printf 'packages/ai/src/providers/cloudflare-ai-gateway.ts merge=ours\n' >> .git/info/attributes
git merge --allow-unrelated-histories --no-edit FETCH_HEAD 2>&1 || true
if [ -n "$(git diff --name-only --diff-filter=U 2>/dev/null)" ]; then
echo "Resolving add/add conflicts: taking upstream for source"
git diff --name-only --diff-filter=U | while read -r f; do
git checkout --theirs "$f" 2>/dev/null || true
done
git checkout HEAD -- .github/workflows/ pi-i18n/ packages/ai/src/providers/cloudflare-ai-gateway.ts 2>/dev/null || true
git add -A 2>/dev/null || true
git commit --no-edit || true
fi
python3 pi-i18n/apply_translations.py apply \
--translations pi-i18n/translations --source . --strict --min-match-rate 1
- name: Package source archive
env:
VERSION: ${{ needs.resolve.outputs.upstream_version }}
run: |
set -euo pipefail
mkdir -p dist
tar -czf "dist/pi-cn-${VERSION}-source.tar.gz" \
--exclude='.git' --exclude='node_modules' --exclude='dist' \
--transform "s,^,pi-cn-${VERSION}/," \
.
- uses: actions/upload-artifact@v7
with:
name: pi-cn-source
path: dist/*
if-no-files-found: error
release:
name: Publish verified Chinese release
needs: [resolve, translation-gate, build, build-source]
if: needs.resolve.outputs.should_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v8
with:
pattern: "pi-cn-*"
path: dist
merge-multiple: true
- name: Create checksums and release notes
env:
RELEASE_TAG: ${{ needs.resolve.outputs.release_tag }}
UPSTREAM_VERSION: ${{ needs.resolve.outputs.upstream_version }}
UPSTREAM_SHA: ${{ needs.resolve.outputs.upstream_sha }}
run: |
set -euo pipefail
(
cd dist
find . -maxdepth 1 -type f ! -name SHA256SUMS -printf '%f\n' | sort | xargs sha256sum >SHA256SUMS
)
cat >release_notes.md <<EOF
## Pi CLI/TUI 简体中文汉化版
- 上游官方版本:\`v${UPSTREAM_VERSION}\`(commit \`${UPSTREAM_SHA:0:12}\`)
- 翻译门禁:词表 \`pi-i18n/translations\` 全部条目在源码中 100% 唯一匹配后才允许发布
- 同步频率:每小时检查上游**正式 Release**,有新版自动构建发布
- 需要实时跟踪上游 \`main\` 最新提交的请使用实时更新版(\`*-cn-nightly\`)
### 下载
| 平台 | 文件 |
|---|---|
| Linux x64 | \`pi-cn-${UPSTREAM_VERSION}-linux-x64.tar.gz\` |
| Windows x64 | \`pi-cn-${UPSTREAM_VERSION}-windows-x64.zip\` |
| 汉化源码 | \`pi-cn-${UPSTREAM_VERSION}-source.tar.gz\` |
所有文件的 SHA-256 摘要见 \`SHA256SUMS\`。
### 安装与更新
### 一键安装(正式版)
Windows PowerShell:
\`\`\`powershell
powershell -Command "irm https://raw.githubusercontent.com/yuloop/pi/main/pi-i18n/install.ps1 | iex"
\`\`\`
Linux / WSL:
\`\`\`bash
curl -fsSL https://raw.githubusercontent.com/yuloop/pi/main/pi-i18n/install.sh | bash
\`\`\`
详见仓库 \`pi-i18n/README.md\`。
> 本项目是社区维护的非官方汉化构建,与 pi 官方团队无隶属关系。
EOF
- uses: softprops/action-gh-release@v3
with:
tag_name: ${{ needs.resolve.outputs.release_tag }}
target_commitish: ${{ github.sha }}
name: "Pi v${{ needs.resolve.outputs.upstream_version }} 汉化版"
body_path: release_notes.md
files: dist/*
overwrite_files: true
fail_on_unmatched_files: true
make_latest: true