diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb3976d8..3df68d96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 }} diff --git a/.gitignore b/.gitignore index 553912a4..319760ce 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/client/.env.production b/client/.env.production new file mode 100644 index 00000000..d5785947 --- /dev/null +++ b/client/.env.production @@ -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 diff --git a/client/electron-builder.yml b/client/electron-builder.yml index be9d673b..8f7e8d27 100644 --- a/client/electron-builder.yml +++ b/client/electron-builder.yml @@ -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 diff --git a/client/vite.config.ts b/client/vite.config.ts index a40a1b21..c01a4352 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -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 || '', diff --git a/docs/knowledge/bugs/2026-07-ci-env-production-gitignore-supabase-placeholder.md b/docs/knowledge/bugs/2026-07-ci-env-production-gitignore-supabase-placeholder.md new file mode 100644 index 00000000..7c488fc4 --- /dev/null +++ b/docs/knowledge/bugs/2026-07-ci-env-production-gitignore-supabase-placeholder.md @@ -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 坏」环境差异类) diff --git a/docs/knowledge/index.md b/docs/knowledge/index.md index e9e67036..c164b12d 100644 --- a/docs/knowledge/index.md +++ b/docs/knowledge/index.md @@ -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 | @@ -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 #发布 #安装包 #性能诊断 #测量方法