From cbd3ed8b4e2e855545e2c7d237d5b10606e63ee3 Mon Sep 17 00:00:00 2001 From: Aparencia Date: Fri, 31 Jul 2026 17:44:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(build):=20=E6=8F=90=E4=BA=A4=20.env.pro?= =?UTF-8?q?duction=20=E5=85=A5=E5=BA=93=E5=B9=B6=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=9C=9F=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E9=98=B2=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:client/.env.production 被根 .gitignore 忽略从未入库,CI 打包时渲染进程 VITE_SUPABASE_*/VITE_API_BASE_URL/VITE_AI_GATEWAY_URL 全部为空且构建静默通过, 内测安装包注册/登录报「云服务尚未配置」,云同步与云端 AI 网关整体失效。 - .gitignore 增加 !client/.env.production 例外(文件内均为公开值: sb_publishable_ 前端公开密钥 + 公网 URL,本就随安装包分发) - vite.config.ts electronBuildConfigPlugin 增加构建期防护: 关键 VITE_ 变量缺失/占位符时构建直接失败,杜绝静默残废包 - 沉淀知识卡片 docs/knowledge/bugs/2026-07-ci-env-production-gitignore-* --- .gitignore | 4 ++ client/.env.production | 21 ++++++ client/vite.config.ts | 10 +++ ...oduction-gitignore-supabase-placeholder.md | 67 +++++++++++++++++++ docs/knowledge/index.md | 3 +- 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 client/.env.production create mode 100644 docs/knowledge/bugs/2026-07-ci-env-production-gitignore-supabase-placeholder.md 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/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 #发布 #安装包 #性能诊断 #测量方法 From c521d3f967fca9310b2affd658324d92eca1c920 Mon Sep 17 00:00:00 2001 From: Aparencia Date: Fri, 31 Jul 2026 17:44:42 +0800 Subject: [PATCH 2/2] =?UTF-8?q?build(release):=20=E5=9B=BA=E5=8C=96=20CDN?= =?UTF-8?q?=20=E6=9B=B4=E6=96=B0=E6=BA=90=E8=87=B3=20electron-builder.yml?= =?UTF-8?q?=20=E5=B9=B6=E7=A7=BB=E9=99=A4=20CI=20sed=20=E6=94=B9=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generic 更新源直接写为 https://dl.entropydecrease.com/downloads(公开下载域名, 非密钥),本地打包与 CI 打包产物的 app-update.yml 完全一致。 release.yml 删除 DOWNLOAD_BASE_URL 注入与 sed 行锚定改写步骤:该模式在 electron-builder.yml 对应行变动后会静默失效。secret 仍被 release job 的 CDN 预热步骤使用,保留不动。 --- .github/workflows/release.yml | 23 ++++------------------- client/electron-builder.yml | 10 ++++++---- 2 files changed, 10 insertions(+), 23 deletions(-) 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/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