Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 4 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,12 @@ jobs:
# --publish never:发布由下方 release job 统一负责,避免 electron-builder
# 因检测到 tag 而自行尝试发布(github provider 无 GH_TOKEN 会报错)
#
# DOWNLOAD_BASE_URL 已配置时,用 -c.publish.0.url 覆盖 generic 更新源为 CDN,
# 使新包内的 app-update.yml 指向 CDN;未配置时沿用 electron-builder.yml
# 中的自建服务器地址。注:存量客户端的更新源已固化在已安装的
# app-update.yml 中,故 ECS 与 GitHub 两侧资产均需继续发布。
# 更新源(CDN)已直接固化在 electron-builder.yml 的 generic provider 中,
# 本地打包与 CI 打包产物的 app-update.yml 完全一致,不再用
# DOWNLOAD_BASE_URL sed 改写(行锚定模式在 yml 变动后会静默失效,已移除)
- name: Build Electron app
shell: bash
env:
DOWNLOAD_BASE_URL: ${{ secrets.DOWNLOAD_BASE_URL }}
run: |
cd client
if [ -n "$DOWNLOAD_BASE_URL" ]; then
# 不能用 -c.publish.0.url 覆盖:electron-builder 会将其解析成
# 对象 {publish:{0:{url}}} 而非数组元素,触发 schema 校验失败。
# 故直接改写配置文件中 generic provider 的 url(行锚定精确匹配)。
sed -i "s|^ url: https://entropydecrease.com/downloads$| url: ${DOWNLOAD_BASE_URL%/}|" electron-builder.yml
echo 'generic provider after rewrite:'
grep -A1 'provider: generic' electron-builder.yml
else
echo "Update feed -> 默认(electron-builder.yml 中的自建服务器)"
fi
npm run electron:build -- --publish never
run: cd client && npm run electron:build -- --publish never
- uses: actions/upload-artifact@v4
with:
name: release-${{ matrix.os }}
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Thumbs.db
.env.*.local
.env.production
.env.test
# 例外:client/.env.production 全部为公开值(Supabase publishable anon key + 公网 URL,
# 本就随安装包分发)。CI 打包(release.yml)依赖它注入渲染进程 VITE_ 变量,必须入库,
# 否则产物中 Supabase/同步/AI 网关地址全为空(见 docs/knowledge/bugs/ 2026-07 记录)
!client/.env.production

# Brainstorm scratch (personal notes)
brainstorm*.md
Expand Down
21 changes: 21 additions & 0 deletions client/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ============================================
# 熵减 Entropydecrease - 生产环境变量配置
# 用于 Web/PWA 生产构建: vite build --mode production
# ============================================

# ---- Supabase 配置 ----
VITE_SUPABASE_URL=https://oinwxwiqiruxrnsxtkyk.supabase.co
VITE_SUPABASE_ANON_KEY=sb_publishable_bahxLn7MxT59-AfT8aIr7A_yEILvgj9

# ---- API 服务地址(指向生产服务器)----
# 同步服务 — apiClient 在代码中拼接完整路径(如 /api/v1/sync/push)
# Nginx 将 /api/v1/sync/ 反向代理到 sync-service:8080
VITE_API_BASE_URL=https://entropydecrease.com

# 同步服务健康检查端点(Nginx /health 直接返回 200)
VITE_API_HEALTH_URL=https://entropydecrease.com/health

# ---- AI 网关 ----
# aiClient 在代码中拼接完整路径(如 /api/v1/ai/summarize)
# Nginx 将 /api/v1/ai/ 反向代理到 ai-gateway:8000
VITE_AI_GATEWAY_URL=https://entropydecrease.com
10 changes: 6 additions & 4 deletions client/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Electron Builder 打包配置 - 熵减桌面端
appId: com.entropydecrease.app
productName: "Entropy decrease"
# 双发布源:第一项(generic = 自建服务器)被写入 app-update.yml 作为
# 双发布源:第一项(generic = CDN,dl 域名,源站 OSS)被写入 app-update.yml 作为
# electron-updater 的自动更新源;GitHub 保留为开源分发/灾备源。
# 注意:存量客户端的 app-update.yml 仍指向 GitHub,过渡期内
# GitHub Release 资产(含 latest.yml)不可停发,否则存量用户无法升级到新源。
# 此处即最终值:CI 不再改写(历史上的 DOWNLOAD_BASE_URL sed 改写已移除),
# 本地打包与 CI 打包产物的更新源完全一致;换域名直接改本文件。
# 注意:存量客户端的 app-update.yml 仍指向 GitHub/ECS 旧源,过渡期内
# GitHub Release 资产(含 latest.yml)与 ECS 侧不可停发,否则存量用户无法升级到新源。
publish:
- provider: generic
url: https://entropydecrease.com/downloads
url: https://dl.entropydecrease.com/downloads
- provider: github
owner: Aparencia
repo: Entropydecrease
Expand Down
10 changes: 10 additions & 0 deletions client/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ function electronBuildConfigPlugin(): Plugin {
// 使用 Vite 的 loadEnv 加载 .env 文件(与 Vite 构建行为一致)
// loadEnv 按约定加载:.env → .env.production → 系统环境变量(后者覆盖前者)
const env = loadEnv('production', process.cwd(), '');
// 构建期防护:关键 VITE_ 变量缺失/占位符时立即终止构建,
// 防止产出「云服务尚未配置」的静默残废安装包
//(曾因 .env.production 被 gitignore、CI checkout 缺失而发生)
const required = ['VITE_SUPABASE_URL', 'VITE_SUPABASE_ANON_KEY', 'VITE_API_BASE_URL', 'VITE_AI_GATEWAY_URL'];
const missing = required.filter((key) => !env[key] || env[key].includes('your-'));
if (missing.length > 0) {
throw new Error(
`[electron-build-config] 缺少必需环境变量: ${missing.join(', ')},请检查 client/.env.production 是否存在且完整`,
);
}
const config = {
VITE_AI_GATEWAY_URL: env.VITE_AI_GATEWAY_URL || '',
VITE_API_BASE_URL: env.VITE_API_BASE_URL || '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 知识卡片 · 踩坑记录

## 基本信息

| 字段 | 内容 |
|------|------|
| 标题 | `.env.production` 被 gitignore 致 CI 安装包 Supabase/同步/AI 网关地址全为空,用户见「云服务尚未配置」 |
| 日期 | 2026-07-31 |
| 类型 | 踩坑记录 |
| 标签 | #CI #环境变量 #Vite #Supabase #GitHubActions #发布 #安装包 |

---

## 症状

内测用户使用 CI(GitHub Actions `release.yml`)打包的 Windows 安装包,在注册/登录页提交时报错:

```
云服务尚未配置,请先在 .env 中设置 VITE_SUPABASE_URL 和 VITE_SUPABASE_ANON_KEY
```

关键陷阱:**本地打包的安装包完全正常,只有 CI 产物必现**——本地开发机上存在 `.env.production`,问题只在 CI 环境暴露。且连带影响不止 Supabase:CI 产物中 `VITE_API_BASE_URL`、`VITE_AI_GATEWAY_URL` 同样为空(`build-config.json` 生成空值),意味着**云同步与云端 AI 网关整体失效**,只是 Supabase 报错最先被用户看到。

## 环境

| 项目 | 版本/信息 |
|------|----------|
| CI | GitHub Actions(windows-latest 打包) |
| 构建 | Vite 8 + electron-builder,`npm run electron:build` |
| 相关文件 | `.gitignore`、`client/.env.production`、`client/vite.config.ts`、`client/src/lib/auth/supabaseClient.ts`、`.github/workflows/release.yml` |

## 排查过程(5 Whys)

1. 为什么报错?→ `AuthContext.tsx` 中 `signUp/signIn` 检测到 `isPlaceholder === true`
2. 为什么是占位符?→ `supabaseClient.ts` 在**构建时**读取 `import.meta.env.VITE_SUPABASE_URL`,缺失则回落到 `'https://your-project.supabase.co'`
3. 为什么构建时缺失?→ CI checkout 的工作区里没有 `client/.env.production`
4. 为什么没有?→ `git check-ignore -v` 显示根 `.gitignore:37` 的 `.env.production` 规则将其忽略,该文件**从未入库**
5. 为什么没兜底?→ `release.yml` 的 Build 步骤也未通过 secrets 注入任何 `VITE_*` 变量

→ **根因:渲染进程环境变量的唯一来源(`.env.production`)被 gitignore,CI 打包环境无任何 `VITE_*` 注入渠道,且构建全程静默通过。**

## 根因

- Vite 在构建时将 `import.meta.env.VITE_*` **静态替换**进渲染包;`.env.production` 缺失时不报错,直接替换为 `undefined`,代码回落到占位符默认值
- 主进程侧 `electronBuildConfigPlugin` 生成的 `build-config.json` 同样写入空字符串,同样静默
- 本地正常 / CI 异常的经典「环境差异」问题:gitignore 的文件只存在于开发机

## 解决方案

1. **提交 `client/.env.production` 入库**:根 `.gitignore` 添加 `!client/.env.production` 例外。安全性评估:文件内全部为公开值——Supabase anon key 是 `sb_publishable_` 前缀的前端公开密钥(本就烘焙进每个安装包分发,由 RLS 保护),其余为公网 URL
2. **构建期防护**:`vite.config.ts` 的 `electronBuildConfigPlugin` 中校验 `VITE_SUPABASE_URL / VITE_SUPABASE_ANON_KEY / VITE_API_BASE_URL / VITE_AI_GATEWAY_URL`,缺失或含占位符时 `throw`,让 CI 构建**红灯失败**而非产出静默残废包

验证:正向构建通过且渲染产物中含真实 Supabase 域名;负向测试(临时移走两个 env 文件)构建以 exit 1 终止并输出 `[electron-build-config] 缺少必需环境变量: ...`。

## 教训

- **下次如何避免**:任何「构建时烘焙」的配置文件若被 gitignore,CI 产物必然缺失。新增 gitignore 规则时检查:这个文件是否被 CI 构建依赖?
- **配置缺失必须显式失败**:Vite 对缺失 env 静默替换 `undefined` 是产出「残废包」的温床。所有关键构建期变量都应有 fail-fast 校验,让问题死在 CI 而非用户手里。
- **「本地能跑、CI 挂/产物坏」的嫌疑清单**(与 Git LFS 卡片互补):LFS 指针未拉取、**gitignore 的配置文件**、平台可选依赖、secrets/环境变量缺失。
- **公开值不必按密钥管理**:Supabase publishable key 设计上就是公开的,为其引入 secrets 流程反而增加维护成本与静默失败面;区分「真密钥」与「公开配置」再选存放位置。

## 参考

- 错误触发点:`client/src/lib/auth/AuthContext.tsx`(`isPlaceholder` 检查)
- 占位符判定:`client/src/lib/auth/supabaseClient.ts`
- 防护实现:`client/vite.config.ts` `electronBuildConfigPlugin`
- 关联卡片:[Git LFS 图标未在 CI 拉取致 electron-builder 打包失败](./2026-07-git-lfs-icon-electron-builder-ci-failure.md)(同为「本地好 / CI 坏」环境差异类)
3 changes: 2 additions & 1 deletion docs/knowledge/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

| 日期 | 标题 | 标签 |
|------|------|------|
| 2026-07-31 | [`.env.production` 被 gitignore 致 CI 安装包云服务地址全为空(「云服务尚未配置」)](./bugs/2026-07-ci-env-production-gitignore-supabase-placeholder.md) | #CI #环境变量 #Vite #Supabase #GitHubActions #发布 |
| 2026-07-31 | [下载慢诊断中的三次误判:对照实验缺失与观测行为污染测量](./bugs/2026-07-download-slow-misdiagnosis.md) | #性能诊断 #CDN #测量方法 #对照实验 #复盘 |
| 2026-07-30 | [Git LFS 图标未在 CI 拉取致 electron-builder 打包报 `ERR_ELECTRON_BUILDER_CANNOT_EXECUTE`](./bugs/2026-07-git-lfs-icon-electron-builder-ci-failure.md) | #CI #GitLFS #electron-builder #GitHubActions #发布 |
| 2026-07-30 | [reduced-motion 用 `animation-play-state: paused` 冻结入场动画致内容不可见](./bugs/2026-07-reduced-motion-fade-in-up-invisible.md) | #CSS #a11y #reduced-motion #animation |
Expand All @@ -24,6 +25,6 @@ _(暂无)_

## 标签速查

- **技术**:#CSS #a11y #React #Vite #Tailwind #CI #GitLFS #electron-builder #GitHubActions #CDN #阿里云
- **技术**:#CSS #a11y #React #Vite #Tailwind #CI #GitLFS #electron-builder #GitHubActions #CDN #阿里云 #环境变量 #Supabase
- **类型**:#bug #方案 #学习 #复盘
- **模块**:#启动仪式 #reduced-motion #animation #发布 #安装包 #性能诊断 #测量方法
Loading