Folo(follow)开源贡献指南:从 Corepack 环境准备到四端开发工作流与质量门禁
本文基于 Folo(AI RSS 阅读器,仓库 follow)的官方贡献文档 CONTRIBUTING.md 编写,覆盖从 Corepack + pnpm 环境准备、四种开发入口(浏览器 / Electron / 外部 SSR / 移动端)的完整操作步骤,到提交前必须通过的质量门禁(typecheck → lint → test)。读完本篇,你可以独立把该 monorepo 跑起来,并对每一步命令背后的实现(如 __debug_proxy 在线调试机制、移动端的 App Check 调试令牌)有源码级的理解,具备直接向该项目提交代码的基础。
一、准备工作:monorepo 结构与包管理器
Folo 是一个由 pnpm workspaces + Turbo 管理的 monorepo。在动手前需要先理解仓库的组织方式,这决定了后续每条命令的作用范围:
- 根工作区定义在 pnpm-workspace.yaml,包含
apps/*、packages/**/*、apps/desktop/layer/*以及apps/mobile/web-app/html-renderer,并排除了**/example目录; - 四个主要应用分别是 apps/desktop(Electron 桌面端,Vite + React 渲染层)、
apps/mobile(Expo / React Native 移动端)、apps/ssr(用于外部分享的极简 SSR 站点)、apps/landing(落地页),共享逻辑沉淀在packages/internal/*(components、store、hooks、database 等)。
启用 Corepack
官方要求贡献者先启用 Corepack,它会根据根 package.json 中声明的 packageManager 字段(当前仓库锁定为 pnpm@10.17.0)自动匹配正确的 pnpm 版本,避免团队内包管理器版本漂移:
corepack enable && corepack prepare
从源码结构看,这一机制被双重加固:根 package.json 的 prepare 脚本为 simple-git-hooks && corepack prepare,即 pnpm install 后会自动再次执行 corepack prepare,确保锁定的 pnpm 版本与 git hooks 同步就绪。
安装依赖
pnpm install
需要注意 package.json 中配置了 postinstall 钩子:
"postinstall": "pnpm run build:packages",
"build:packages": "turbo run build --filter=\"./packages/**/*\""
也就是说,安装依赖的同时会用 Turbo 把所有 packages/** 下的内部包预先构建一遍。这也解释了为什么 AGENTS.md 中给出的"Setup commands"以 pnpm install 作为第一条——安装命令本身就是环境准备的一部分,首次安装耗时会明显长于后续安装。
二、四种开发入口
CONTRIBUTING.md 提供了四条开发路径:浏览器、Electron、外部 SSR Web、移动端。下面逐一展开,并给出源码层面的佐证。
2.1 浏览器开发(推荐):__debug_proxy 调试入口
官方推荐"在浏览器中开发",因为它体验更轻、更接近最终 Web 形态:
cd apps/desktop && pnpm run dev:web
该命令的实际定义在 apps/desktop/package.json 中:
"dev:web": "cross-env WEB_BUILD=1 vite"
WEB_BUILD=1 标记这是一个纯 Web 构建(不带 Electron 主进程)。文档说明启动后会引导你访问 https://app.folo.is/__debug_proxy,从而复用线上 API 环境进行开发调试。这个 __debug_proxy 机制在源码中可以直接验证:
- 桌面端的 vite.config.ts 在构建时会把调试页写出到
dist/__debug_proxy.html与dist/__debug_proxy/index.html,并在开发模式打印两条调试地址——生产调试页https://app.folo.is/__debug_proxy.html与开发调试页https://dev.folo.is/__debug_proxy.html; - 部署侧,vercel.json 配置了重写规则,把
/__debug_proxy与/__debug_proxy/:path*请求统一落到__debug_proxy.html,保证线上路径可用; - 路由侧,apps/desktop/layer/renderer/src/router.web.tsx 通过
globalThis["__DEBUG_PROXY__"]或 pathname 前缀/__debug_proxy判断是否处于调试代理运行时,此时改用createHashRouter(因为线上页面并非部署在站点根路径下,hash 路由可避免刷新 404)。
也就是说:本地 Vite dev server 提供 JS 资源,线上 __debug_proxy 页面提供 API 与域名环境,二者结合即构成"浏览器里开发、打线上后端"的完整链路。
2.2 Electron 开发
若需要在桌面壳内开发,CONTRIBUTING.md 给出的步骤为:
# 0. 进入目录
cd apps/desktop
# 1. 复制示例环境变量文件
cp .env.example .env
# 2. 将 .env 中的 VITE_API_URL 设置为 https://api.follow.is
# 3. 启动开发服务器
pnpm run dev:electron
dev:electron 实际执行的是 electron-vite dev(见 apps/desktop/package.json),即同时启动 Electron 主进程与渲染进程。
关于 .env 文件,仓库内已提供 apps/desktop/.env.example,包含以下变量:
| 变量 | 示例值 | 说明 |
|---|---|---|
VITE_WEB_URL |
http://localhost:5173 |
本地 Web 渲染地址 |
VITE_API_URL |
http://localhost:3000 |
API 后端地址(文档要求改为 https://api.follow.is) |
VITE_IMGPROXY_URL |
http://localhost:2873 |
图片代理地址 |
VITE_SENTRY_DSN |
空 | 错误上报 DSN |
VITE_BUILD_TYPE |
production |
构建类型 |
VITE_INBOXES_EMAIL |
@follow.re |
收件箱邮箱 |
VITE_PUBLIC_POSTHOG_KEY / VITE_PUBLIC_POSTHOG_HOST |
空 | 埋点配置 |
文档中的排障提示:如果遇到登录问题,可以把浏览器 Cookie 里的
__Secure-better-auth.session_token复制到应用中,复用已有的浏览器会话完成登录态。
2.3 外部 SSR Web 应用开发
SSR 站点对应 apps/ssr 目录,用于外部分享场景(例如分享内容页的服务端渲染版本)。启动方式最简单:
cd apps/ssr
pnpm run dev
另外,根 package.json 还提供了一条 dev:web 脚本(turbo run @follow/web#dev @follow/ssr#dev),可并行拉起 desktop 渲染端与 SSR 端,适合需要两侧同时联动的改动。
2.4 移动端开发(需要 Mac)
CONTRIBUTING.md 明确标注:移动端开发需要 Mac 设备,并已安装 Xcode 及相应依赖。步骤如下:
# 1. 进入目录
cd apps/mobile
# 2. 复制环境变量文件,并写入调试令牌
cp .env.example .env
echo 'EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN="xxx"' >> .env
# 也可以手动在 .env 中添加 EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN="xxx"
# 注意:令牌值可以是任意字符串
# 3. 从源码构建并安装 Folo(dev) 应用(耗时较长,只需做一次)
pnpm expo prebuild --clean # 可选
pnpm run ios
# 4. 启动开发服务器
pnpm run dev
EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN 并不是随意要求——从源码 apps/mobile/src/initialize/app-check.ts 可以看到它的用途:
provider.configure({
apple: {
provider: __DEV__ ? "debug" : "appAttest",
debugToken: env.APP_CHECK_DEBUG_TOKEN,
},
android: {
provider: __DEV__ ? "debug" : "playIntegrity",
debugToken: env.APP_CHECK_DEBUG_TOKEN,
},
isTokenAutoRefreshEnabled: true,
})
即开发态(__DEV__)下 Firebase App Check 使用 debug provider 并注入该调试令牌,生产态则切换为 iOS 的 appAttest / Android 的 playIntegrity。这就是为什么该令牌"值可以是任意字符串":它在 debug provider 下由开发者自行生成并配置到 Firebase 控制台即可。仓库中的 apps/mobile/.env.example 也同时预留了 SENTRY_AUTH_TOKEN 变量位。
开发 iOS 原生模块
需要修改 FollowNative 原生模块时(对应 apps/mobile/native 下的 Expo 原生模块),按文档操作:
# 1. 进入 iOS 工程目录
cd apps/mobile/ios
# 2. 在 Xcode 中打开工作区
open Folo.xcworkspace
# 3. 在左侧 Pods 目录中选择 FollowNative 后直接 Build & Run
原生模块本身是一个 Expo module(见 apps/mobile/native/expo-module.config.json),native/ios 下包含 Swift 实现,通过 CocoaPods 以 FollowNative 名义接入工程,因此改动后需在 Xcode 中重新编译 Pods 才会生效。
三、质量门禁与代码规范
CONTRIBUTING.md 的 "Contribution Guidelines" 要求:代码遵循项目编码规范、提交信息清晰简洁、为改动附带相关测试、按需更新文档。落到可执行层面,AGENTS.md 给出了明确的"提交前必过"质量门禁,并且顺序是固定的:
# 1) 先跑类型检查(必须)
pnpm run typecheck
# 2) 再跑 Lint 并自动修复
pnpm run lint:fix
# 3) 最后跑测试
pnpm run test
对应的根 package.json 脚本值得细看:
"typecheck": "turbo typecheck",
"lint": "pnpm run lint:tsl && eslint",
"lint:fix": "eslint --fix",
"lint:tsl": "tsslint --project apps/*/tsconfig.json",
"test": "cross-env CI=1 pnpm --recursive run test"
即 lint 实际包含两层:tsslint(基于 ts 项目的类型级 lint,作用于所有 app 的 tsconfig)加 eslint;测试则以 CI=1 递归执行各包的测试(测试框架为 Vitest,测试文件与源码就近放置,如 apps/cli/src/args.test.ts 这类同名 *.test.ts 文件)。
此外仓库还配了自动化护栏,提交时会额外触发:
simple-git-hooks的 pre-commit 钩子执行pnpm exec lint-staged;lint-staged对暂存文件统一执行eslint --fix与prettier --write,并对locales/**/*.json额外运行dedupe:locales(即eslint --fix locales/**,保持多语言词条排序一致),对apps/mobile/src/**触发构建号自增脚本。
代码风格上的硬性约定(同样来自 AGENTS.md):TypeScript strict 模式、避免 any、注释使用英文;跨平台路径用 pathe 而非 node:path;通用可复用组件放在 packages/internal/components,应用专属 UI 留在各自 app 内;测试用 Vitest 并与源码同目录。
四、社区与许可证
- 社区交流渠道:官方 Discord 与 Twitter/X(
@folo_is),用于讨论想法、提问和分享贡献。 - 许可证:根据 CONTRIBUTING.md 与根 package.json(
"license": "AGPL-3.0-only"),向 Folo 提交代码即表示同意其贡献以 GNU Affero General Public License v3 授权发布,特殊例外条款见 README.md。
五、贡献流程速查清单
corepack enable && corepack prepare准备包管理器环境;pnpm install安装依赖(postinstall 会自动构建所有内部包);- 按改动目标选择开发入口:
- 改 Web/渲染层:
cd apps/desktop && pnpm run dev:web,通过线上__debug_proxy页面调试; - 改 Electron 主进程/桌面能力:
cp .env.example .env、设置VITE_API_URL后pnpm run dev:electron; - 改分享页 SSR:
cd apps/ssr && pnpm run dev; - 改移动端:配置
EXPO_PUBLIC_APP_CHECK_DEBUG_TOKEN,pnpm run ios(一次)+pnpm run dev;改原生模块则进apps/mobile/ios用 Xcode 编译FollowNative;
- 改 Web/渲染层:
- 提交前按固定顺序通过门禁:
pnpm run typecheck→pnpm run lint:fix→pnpm run test; - 保持提交信息清晰、附测试、更新文档,并遵守 AGPL-3.0 授权约定。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00