diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 6dd7df8..37a856b 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -153,6 +153,8 @@ jobs: "${DEPLOY_USER}@${DEPLOY_HOST}:${SERVER_DIR}/" - name: 注入配置并部署 + env: + APP_VERSION: ${{ github.sha }} run: | emit_record() { local key=$1 @@ -161,6 +163,7 @@ jobs: { for key in \ ACR_AK_ID ACR_AK_SECRET \ + APP_VERSION \ CHAT_API_KEY CHAT_BASE_URL CHAT_MODEL CHAT_THINKING \ IMAGE_API_KEY IMAGE_BASE_URL IMAGE_MODEL IMAGE_SIZE IMAGE_ENABLED \ VOICE_API_KEY VOICE_PROVIDER VOICE_BASE_URL VOICE_TTS_URL VOICE_ASR_URL \ @@ -197,7 +200,7 @@ jobs: case "$key" in ACR_AK_ID) ACR_USER=$value ;; ACR_AK_SECRET) ACR_PASSWORD=$value ;; - CHAT_*|IMAGE_*|VOICE_*|TTS_*|ASR_MODEL|VIDEO_*|APP_LOG_*|LOG_LEVEL|MONGO_URI|OSS_*|LANGFUSE_*) + APP_VERSION|CHAT_*|IMAGE_*|VOICE_*|TTS_*|ASR_MODEL|VIDEO_*|APP_LOG_*|LOG_LEVEL|MONGO_URI|OSS_*|LANGFUSE_*) if printf "%s" "$value" | LC_ALL=C grep -q "[[:cntrl:]]"; then echo "control character in runtime value: $key" >&2 exit 1 @@ -243,6 +246,8 @@ jobs: ' - name: Smoke check + env: + EXPECTED_VERSION: ${{ github.sha }} run: | set -euo pipefail runner_temp_resolved=$(cd "$RUNNER_TEMP" && pwd -P) @@ -254,17 +259,25 @@ jobs: } trap cleanup_probe EXIT HUP INT TERM # 容器 healthcheck 已就绪才会到这;探 /api/health(不打 Mongo),仍重试覆盖网关刷新窗口 + smoke_ok=false for i in 1 2 3 4 5 6; do if curl -fsS -m 15 "https://${DOMAIN}/api/health" -o "$probe_file"; then echo "smoke ok (attempt $i)" head -c 200 "$probe_file"; echo - exit 0 + smoke_ok=true + break fi echo "smoke attempt $i failed, sleep 5" sleep 5 done - echo "smoke check failed after 6 attempts" - exit 1 + if [ "$smoke_ok" != true ]; then + echo "smoke check failed after 6 attempts" + exit 1 + fi + curl -fsS -m 15 "https://${DOMAIN}/api/version" -o "$probe_file" + jq -e --arg expected "$EXPECTED_VERSION" \ + '.status == "ok" and .version == $expected' "$probe_file" >/dev/null + echo "version smoke ok" - name: 清理临时 SSH 文件 if: always() diff --git a/CHANGELOG.md b/CHANGELOG.md index 7be3faa..838b6ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · versi > 时间戳精确到分钟(`YYYY-MM-DD HH:MM`,北京时间 UTC+8)。同一天多次改动按时间倒序——最新在最上。每段下面扁平列表,前缀标类型(`add` / `change` / `fix` / `test` / `chore`)。 +### 2026-08-23 20:01 + +- **fix(deploy)**:CI 将当前 commit SHA 注入容器 `APP_VERSION`,部署烟测同时校验 `/api/version`,不再把返回 `dev` 的旧版本探针当作上线成功 + ### 2026-08-23 19:52 - **change(notes)**:删除 AI 自动笔记;结果页和本人历史详情支持选中文字后手动加入笔记,保留来源上下文,公开分享页只读;同一表达可分别保留错题与笔记并在各类型内去重 diff --git a/docker-compose.yml b/docker-compose.yml index 7a90a0a..3b88fe3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,7 @@ services: restart: unless-stopped environment: - APP_ENV=production + - APP_VERSION=${APP_VERSION:-dev} - PORT=3001 - APP_LOG_DIR=/app/logs - APP_LOG_ROTATE_WHEN=${APP_LOG_ROTATE_WHEN:-midnight} diff --git a/docs/deploy.md b/docs/deploy.md index 53c8c03..6dd0c5f 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -21,6 +21,8 @@ AI 能力按环境变量解耦:文字使用 `CHAT_*`,语音使用 `VOICE_*` 更新 Infisical 后,从 Actions 页手动运行 `CI / CD` 工作流。任务只在部署期间创建受限临时文件,`docker compose` 完成环境解析后立即删除;生产机不再保留应用 `.env`。不要重跑更新前的旧任务,旧任务可能仍使用当时的密钥快照。 +每次部署会把当前 GitHub commit SHA 作为非敏感的 `APP_VERSION` 注入容器;`GET /api/version` 必须返回该 SHA。部署烟测同时检查健康接口和版本一致性,避免镜像拉取或容器替换异常时把旧版本误报为上线成功。 + ## 功能完成邮件通知 `.github/workflows/feature-notify.yml` 是与部署解耦的手动工作流:只允许从默认分支运行,输入一行标题、简短说明、最多 6 个功能点与可选链接,然后发送手机友好的 HTML + 纯文本邮件。它不随 deploy 自动触发,防止失败重跑或回滚时重复打扰收件人。