Skip to content

Pi Chinese Nightly #512

Pi Chinese Nightly

Pi Chinese Nightly #512

Workflow file for this run

name: Pi Chinese Nightly
# pi(earendil-works/pi)汉化版【实时更新版】自动构建发布流水线(nightly)
# 每小时检查上游 main 最新提交:有变化则同步 → 校验翻译词表 100% 匹配 → 应用汉化 → 构建 → 发布 v<上游版本>-cn-nightly-<sha12> 预览版
# tag 唯一化:每次上游新提交发布全新 tag(含上游 commit sha 前 12 位),发布后自动删除旧预览版,Releases 页面只保留最新 1 个预览条目
# 正式版见 pi-cn-release.yml(跟随上游正式 Release)
on:
schedule:
- cron: "47 * * * *"
workflow_dispatch:
inputs:
upstream_ref:
description: "上游分支或 commit(留空为 main 最新提交)"
required: false
type: string
release_tag:
description: "本仓库发布 tag(留空为 v<上游版本>-cn-nightly-<sha12>)"
required: false
type: string
concurrency:
group: pi-cn-nightly
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
# 跟随上游 main 最新提交;dispatch 可指定任意分支/commit
upstream="${REQUESTED_UPSTREAM:-main}"
if [[ "$upstream" =~ ^[0-9a-f]{40}$ ]]; then
sha="$upstream"
else
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-nightly-${sha:0:12}}"
# tag 字符校验:仅允许字母数字与 . _ -(sha 后缀均为合法字符;防 dispatch 注入非法 tag)
[[ "$release" =~ ^[A-Za-z0-9._-]+$ ]] || {
echo "release tag 含非法字符: ${release}" >&2
exit 1
}
should_build=true
if [[ "${{ github.event_name }}" == "schedule" ]] &&
gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${release}" >/dev/null 2>&1; then
# tag 内含上游 commit sha(12 位 hex),天然防重复构建:同名 tag 已存在即跳过
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 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
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 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
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 upstream "${{ needs.resolve.outputs.upstream_ref }}"
git config user.name "yuloop"
git config user.email "yuloop@users.noreply.github.com"
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 nightly 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 简体中文汉化版(实时更新版)
- 上游最新提交:\`${UPSTREAM_SHA:0:12}\`(main 分支,对应版本 v${UPSTREAM_VERSION})
- 翻译门禁:词表 \`pi-i18n/translations\` 全部条目在源码中 100% 唯一匹配后才允许发布
- **实时更新版**:每小时跟随上游 \`main\` 最新提交构建,每次上游新提交发布新 tag,自动清理旧预览版(页面只保留最新 1 个预览条目)
- 追求稳定请下载正式版(\`*-cn\`,见仓库 Releases 最新版)
### 下载
| 平台 | 文件 |
|---|---|
| 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-preview.ps1 | iex"
\`\`\`
Linux / WSL:
\`\`\`bash
curl -fsSL https://raw.githubusercontent.com/yuloop/pi/main/pi-i18n/install.sh | bash -s -- --preview
\`\`\`
详见仓库 \`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
prerelease: true
- name: Delete outdated preview releases
env:
GH_TOKEN: ${{ secrets.YU_TOKEN }}
CURRENT_TAG: ${{ needs.resolve.outputs.release_tag }}
run: |
set -euo pipefail
gh api "repos/${GITHUB_REPOSITORY}/releases?per_page=100" --paginate \
--jq '.[] | select(.prerelease == true and .draft == false and .tag_name != env.CURRENT_TAG and (.tag_name | contains("-cn-nightly"))) | .id' \
| while read -r id; do
echo "deleting outdated preview release id=$id"
gh api -X DELETE "repos/${GITHUB_REPOSITORY}/releases/$id" || echo "delete failed id=$id"
done