修复:CI 部署环境与 ref 错配、迁移碎片化、图片意图 source_span、章节物化脏版式、会话历史与本地语音不一致

新增:TTS 上传 COS 与分片、章节 reading_segments 物化与快照、markdown 清洗、会话消息 repository、语音 store 重构与相关测试
This commit is contained in:
Kevin
2026-03-20 16:36:42 +08:00
parent 7317bf10cd
commit 8af37e5e8e
65 changed files with 1704 additions and 504 deletions

View File

@@ -3,7 +3,7 @@ name: Android Release Build
on:
push:
tags:
- 'v*' # 推送 v1.0.0 等标签时自动触发
- 'v*.*.*' # SemVer tag v1.0.0时自动触发
workflow_dispatch: # 支持手动触发
inputs:
version_name:

View File

@@ -2,16 +2,15 @@
#
# 环境映射(按触发源自动推断):
# main → dev (开发 + 内部测试)
# staging → stage (预发布)
# v*.*.* → prod (正式发布)
#
# 手动触发workflow_dispatch 可选择环境
# 手动触发workflow_dispatch 可选择 dev / stage / prod
name: App Expo Deploy
on:
push:
branches: [main, staging]
branches: [main]
tags: ['v*.*.*']
paths:
- "app-expo/**"
@@ -41,18 +40,12 @@ env:
jobs:
deploy:
name: Build & Deploy
name: "Build & Deploy"
runs-on: ubuntu-latest
permissions:
contents: read
# GitHub Environments: 在 Repo Settings → Environments 中创建 dev/staging/production可配置独立 secrets
environment: ${{
(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'prod') && 'production' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'stage') && 'staging' ||
startsWith(github.ref, 'refs/tags/v') && 'production' ||
github.ref == 'refs/heads/staging' && 'staging' ||
'dev'
}}
environment: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'prod') && 'production' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'stage') && 'staging' || startsWith(github.ref, 'refs/tags/v') && 'production' || 'dev' }}
steps:
- name: Checkout code
@@ -65,8 +58,6 @@ jobs:
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
echo "env=prod"
elif [[ "${{ github.ref }}" == refs/heads/staging ]]; then
echo "env=stage"
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "env=${{ github.event.inputs.environment }}"
else

View File

@@ -1,132 +0,0 @@
name: App Expo Quality
on:
pull_request:
paths:
- "app-expo/**"
- ".github/workflows/app-expo-quality.yml"
push:
branches:
- main
- master
- develop
paths:
- "app-expo/**"
- ".github/workflows/app-expo-quality.yml"
workflow_dispatch:
concurrency:
group: app-expo-quality-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
name: Verify app-expo
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
defaults:
run:
working-directory: app-expo
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: app-expo/package-lock.json
- name: Install dependencies
run: npm ci
- name: Check formatting
run: npm run format:check
- name: Run linter
run: npm run lint
- name: Run Jest in CI mode
run: npm run test:ci
- name: Build coverage summary
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
id: coverage_summary
run: |
node <<'EOF'
const fs = require('fs');
const summaryPath = 'coverage/jest/coverage-summary.json';
const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8')).total;
const metrics = [
['Lines', summary.lines],
['Statements', summary.statements],
['Functions', summary.functions],
['Branches', summary.branches],
];
const body = [
'## app-expo Jest Coverage',
'',
'| Metric | Percent | Covered / Total |',
'| --- | ---: | ---: |',
...metrics.map(
([name, metric]) =>
`| ${name} | ${metric.pct}% | ${metric.covered}/${metric.total} |`
),
'',
'_Generated by App Expo Quality._',
].join('\n');
fs.appendFileSync(process.env.GITHUB_OUTPUT, `body<<EOF\n${body}\nEOF\n`);
EOF
- name: Comment coverage on PR
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
uses: actions/github-script@v7
with:
script: |
const marker = '<!-- app-expo-jest-coverage -->';
const body = `${marker}
${{ steps.coverage_summary.outputs.body }}`;
const { owner, repo } = context.repo;
const issue_number = context.issue.number;
const comments = await github.paginate(github.rest.issues.listComments, {
owner,
repo,
issue_number,
per_page: 100,
});
const existing = comments.find((comment) =>
comment.user?.login === 'github-actions[bot]' &&
comment.body?.includes(marker)
);
if (existing) {
await github.rest.issues.updateComment({
owner,
repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number,
body,
});
}
- name: Upload Jest coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: app-expo-jest-coverage
path: app-expo/coverage/jest
retention-days: 14

View File

@@ -1,20 +1,36 @@
# API Dockermain → DevGitHub Environment: devTag v*.*.* → Productionenvironment: production
# 在 Repo Settings → Environments 中为 dev / production 分别配置 SSH、DEPLOY_PATH、迁移 DB 等 Secrets。
#
# 发布策略:
# - merge / push 到 main构建并部署到 Dev / 内部测试
# - 手动创建并推送 tag vMAJOR.MINOR.PATCH构建并部署到 Production
#
# 注意paths 过滤在 tag push 时按「被指向的 commit」判断若该 commit 未改 api/ 与本 workflow不会触发。
# 此时可用 workflow_dispatch 选择对应 tag/ref 手动部署。
name: Docker Build and Deploy
on:
push:
branches:
- dev/add-agent
- main
tags:
- 'v*.*.*'
paths:
- 'api/**'
- '.github/workflows/**'
workflow_dispatch:
inputs:
branch:
description: '部署分支'
description: '部署 ref分支名或 tag如 main / v1.0.0);留空则使用当前运行所选 ref'
required: false
type: string
default: ''
concurrency:
group: docker-api-${{ github.ref }}
cancel-in-progress: false
env:
IMAGE_NAME: lifecho-api
REGISTRY: crpi-u2903xccyzd6nqnc.cn-shanghai.personal.cr.aliyuncs.com
@@ -81,6 +97,9 @@ jobs:
runs-on: ubuntu-latest
needs: build-and-push
if: github.event_name != 'pull_request'
# workflow_dispatch 下若填写了 branch 输入,以输入为准选择 environment避免仅 UI 选了 tag 但部署 main 时误用 production
environment:
name: ${{ ((github.event_name == 'workflow_dispatch' && github.event.inputs.branch != '' && startsWith(github.event.inputs.branch, 'v')) || (github.event_name == 'workflow_dispatch' && github.event.inputs.branch == '' && startsWith(github.ref, 'refs/tags/v')) || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v'))) && 'production' || 'dev' }}
steps:
- name: Checkout code
@@ -101,12 +120,19 @@ jobs:
- name: Determine image tag
id: image_tag
run: |
DEPLOY_BRANCH="${{ github.event.inputs.branch || github.ref_name }}"
echo "deploy_branch=$DEPLOY_BRANCH" >> "$GITHUB_OUTPUT"
if [ "$DEPLOY_BRANCH" == "main" ] || [ "$DEPLOY_BRANCH" == "master" ]; then
# 与 docker/metadata-action 的 semver 标签一致v1.2.3 → 镜像 :1.2.3
if [ -n "${{ github.event.inputs.branch }}" ]; then
REF_NAME="${{ github.event.inputs.branch }}"
else
REF_NAME="${{ github.ref_name }}"
fi
echo "deploy_ref=$REF_NAME" >> "$GITHUB_OUTPUT"
if [[ "$REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "tag=${REF_NAME#v}" >> "$GITHUB_OUTPUT"
elif [ "$REF_NAME" == "main" ] || [ "$REF_NAME" == "master" ]; then
echo "tag=latest" >> "$GITHUB_OUTPUT"
else
BRANCH_TAG=$(echo "$DEPLOY_BRANCH" | sed 's/\//-/g')
BRANCH_TAG=$(echo "$REF_NAME" | sed 's/\//-/g')
echo "tag=$BRANCH_TAG" >> "$GITHUB_OUTPUT"
fi