
Electric Agents 移动端 CI 与发布流水线实战指南基于 Expo/EAS 的 PR 预览、Canary 与稳定版发布【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric本篇技术指南围绕electric仓库中的移动端包packages/agents-mobile一个基于 Expo SDK 54、Expo Router 与 Expo DOM Components 构建的 Agent 客户端应用展开完整讲解其 CI 与发布流水线设计从 PR 预览构建、main分支 canary 构建到由 Changesets 发布流程触发的稳定版 Google Play 提交。读者将掌握 EAS 构建配置eas.json六个 profile、app.config.ts的版本与元数据管理、mobile-affected.mjs的影响面检测、四个 GitHub Actions 工作流的协作方式以及一套可复制的移动端四通道发布落地模板。目标让移动端在 CI 中可靠构建并随 Changesets 发布该计划的核心目标是让packages/agents-mobile在 CI 中可靠构建并通过 Expo/EAS 发布工作流结构对齐桌面端packages/agents-desktop其影响面检测脚本scripts/ci/desktop-affected.mjs是移动端脚本的建模蓝本PR 构建用于评审与冒烟测试产出可安装的审查制品Canary 构建从main分支持续产出分发给内部测试人员稳定版发布由现有 Changesets 发布流程触发直接面向应用商店提交。移动端应用是 Expo 应用因此原生构建、签名、提审与预览分发都应依托 Expo 工具链。Expo Go 在本地开发中仍然可用凡支持之处继续作为开发者路径保留但由于应用使用了 Expo DOM ComponentsCI 必须证明生产构建路径本身可行而非仅停留在 Expo Go 或 EAS Update 预览层面。现状盘点已落地的能力与关键配置基线electric-ax/agents-mobile已是一个 workspace 包也是 Expo SDK 54 应用内部通过 Expo Router、React Native 与 Expo DOM Components 嵌入agents-server-ui的选定界面。目前已经实现Android PR 预览构建EAS internal distribution基于main的 Android canary 构建EAS internal distribution基于 Changesets 的 Android 稳定版构建EAS production profile Google Play Submit手动 iOS 模拟器构建EAS无需 Apple Developer 签名Expo 项目、GitHubEXPO_TOKEN、Google Play 应用与GOOGLE_PLAY_SERVICE_ACCOUNT_JSON均已配置。当前应用的健康基线可直接在本地复验pnpm --filter electric-ax/agents-mobile run ci:check在仓库配置的 Node 版本下通过pnpm --filter electric-ax/agents-mobile run export:ios成功expo-doctor通过 18/18 项检查expo config --type public解析结果为ownerelectric-ax、slugagents-mobile、version/runtimeVersion 取自packages/agents-mobile/package.json、project id11a024df-c681-4374-867a-5c5905be9133、Android package / iOS bundle id 均为com.electricsql.agents.mobile。已完成的包与配置变更包括packages/agents-mobile/app.config.ts全权负责发布元数据陈旧的静态app.json已移除packages/agents-mobile/eas.json定义了 development、preview、preview-ios-simulator、canary、canary-store、production 六类 profileReact、WebView、TypeScript 版本在 mobile/server-ui/runtime 依赖图中对齐使 Expo doctor 与移动端类型检查全部通过。一个重要的安全约束未受信任的 fork PR 不会运行带认证的 EAS 构建因为 GitHub 不会向这类任务暴露仓库 secrets。fork 只能获得本地检查EAS 构建仅针对同仓库 PR、带信任标签的 PR 或手动 dispatch 运行。为什么 CI 信号必须来自 EAS Build 而非 Expo GoExpo Go 适合快速本地迭代应继续作为开发者路径保留。但生产级 CI 信号必须来自EAS Build原因与应用实现直接相关应用通过use dom使用 Expo DOM ComponentsDOM Components 在 Expo Go 中可用但 Expo 官方文档明确DOM Components 是嵌入式导出embedded exports不支持常规意义上的 EAS Update OTA 更新DOM Components 具有大量与生产构建强相关的行为因此预览与发布二进制必须作为原生应用真正构建并安装验证。对应的推荐实践是本地/手动开发用 Expo GoPR 校验在仓库 secrets 可用时用 EAS internal distribution 构建canary 验证在刻意启用 Google Play internal-track 提交之前同样用 EAS internal distribution商店提交流程用 EAS Submit先 Google Play后续接 App Store Connect/TestFlight。发布通道设计PR / Canary / 稳定版 / iOS 模拟器PR 通道目的证明移动应用可构建并产出可安装的评审制品。初始范围仅 Android对同仓库或受信任 PR 执行 EAS internal distribution 构建所有 PR含 fork PR均执行本地 CI 检查不做商店提交在 PR 上以 sticky 评论形式给出 EAS 构建 URL仅当移动端相关文件变更时触发。后续可选在 Apple Developer 就绪前添加手动 iOS 模拟器构建Apple Developer 就绪后添加签名 iOS internal 构建可选地添加 EAS Update 预览评论服务于 Expo Go/dev-build 便利性但不作为主要 CI 制品。Canary 通道目的持续把main的最新移动端构建分发给内部测试者。初始范围从main构建 Android发布 EAS internal distribution 构建使用 EAScanaryprofile仅在移动端相关文件变更时触发。后续可选增加第二个canary-store提交任务把构建发布到 Google Play internal trackApple Developer 账户可用后增加 iOS TestFlight。稳定版通道目的当 Changesets 发布electric-ax/agents-mobile时产出可上架构建。初始范围以现有 Changesets 发布流程为事实来源捕获已发布的版本与 tag用 EAS production profile 构建 Android用 EAS Submit 提交 Google Play。后续从同一发布触发器追加 iOS App Store/TestFlight 提交。iOS 模拟器通道目的在 Apple Developer 签名可用之前证明应用能以原生 iOS 应用编译。初始范围使用 EASpreview-ios-simulatorprofileios.simulator: true通过agents_mobile_ios_simulator.yml手动触发在启动 EAS 构建前执行与移动端相同的依赖构建、类型检查、Expo doctor、Android export、iOS export 检查不要求 Apple Developer 凭据、provisioning profile、App Store Connect 或 TestFlight。明确限制模拟器制品仅供本地模拟器测试不能安装到物理设备它不校验 App Store 签名、entitlements、TestFlight 提交或 App Review 元数据签名设备/TestFlight 构建仍然依赖 Apple Developer 账户。前置环境Expo、Google Play 与 Apple 的配置清单ExpoExpo 项目已关联ownerelectric-ax、slugagents-mobile、project id11a024df-c681-4374-867a-5c5905be9133EXPO_TOKEN已作为 GitHub Actions 仓库 secret 配置使用 Expo Developer robot token为 Android 签名配置 EAS Build credentials。Google Play仓库已有 Google Play 账户因此 Android 可成为首个发布目标。已完成项最终 Android package id 为com.electricsql.agents.mobileGoogle Play 应用已创建服务账号已创建并邀请进 Play Console 应用服务账号 JSON 已存为 GitHub Actions 仓库 secretGOOGLE_PLAY_SERVICE_ACCOUNT_JSON。仍需注意如果 Google 阻止基于 API 的提交直到首个 AAB 通过 Play Console 手动上传可能需要一次性的手动初始上传。轨道决策方面canary 先用 EAS internal distributionGoogle Play internal track 可通过canary-store启用但暂未在 CI 中开启稳定版走 production track也可视情况采用 staged rolloutPlay 应用的商店设置问卷可能仍需完成才能进入生产发布。AppleApple 发布保持规划但暂禁用直到账户存在iOS 模拟器构建可在此之前运行无需签名。后续需要Apple Developer 账户与 Android 一致的 bundle idcom.electricsql.agents.mobileApp Store Connect 应用EAS 托管 iOS 凭据签名 iOS internal distribution profileTestFlight submit profile。包与配置变更app.config.ts 与 eas.json 的完整解读App Config单一事实来源packages/agents-mobile/app.config.ts拥有从 CI 环境变量与package.json派生的全部构建元数据。核心字段与仓库实现一一对应配置项取值说明nameElectric Agents应用显示名slugagents-mobileEAS 项目 slugschemeelectric-agentsdeep link schemeversion取自package.json由 Changesets 维护的语义化版本runtimeVersion取自package.json与原生版本一致初始不使用 EAS Updateextra.eas.projectId11a024df-c681-4374-867a-5c5905be9133EAS 项目 IDownerelectric-axExpo 组织android.packagecom.electricsql.agents.mobileAndroid 应用 IDandroid.versionCode来自 CI.build-info.json或环境变量单调递增ios.bundleIdentifiercom.electricsql.agents.mobileiOS bundle idios.buildNumber来自 CI与 versionCode 同源ios.infoPlist.ITSAppUsesNonExemptEncryptionfalse仅使用 HTTPS 等豁免加密时的声明resolveVersionCode()的实现见 app.config.ts揭示了一个关键工程细节versionCode/buildNumber必须在所有上传到 Google Play 与 App Store Connect 的操作中单调递增包括跨不同 CI 工作流canary、production。因此 CI 在调用eas build前会写.build-info.json保证无论是在 GitHub runner 上还是 EAS Build 服务器上求值app.config.ts都得到一致值该文件随项目 tarball 一起上传。解析顺序为环境变量ELECTRIC_AGENTS_MOBILE_VERSION_CODE→.build-info.json中的versionCode→ 兜底使用Math.floor(Date.now() / 1000)作为本地开发回退。从源码还可以看到该配置并非空壳它通过withSentry接入 SentryEU 区域https://de.sentry.io/organizationelectricsql-04projectagents-mobile通过expo-build-properties将 iOS deploymentTarget 设为16.4因为聊天 WebView 的 DOM bundle 使用正则 lookbehindJavaScriptCore 仅 iOS 16.4 可解析通过expo-image-picker注入照片/相机权限文案并关闭麦克风权限还包含针对 Android OAuth deep link 回传的with-android-on-new-intent插件以及为 Apple ITMS-91053 审核准备的privacyManifests声明。这些细节说明一份真正构建就绪的移动端配置其工作量远超几个 key-value。EAS Config六个 profile 的完整形态packages/agents-mobile/eas.json 定义了六个构建 profile 与两组提交配置。仓库中的实际形态如下{ cli: { version: 15.0.0, appVersionSource: local }, build: { development: { node: 24.11.1, developmentClient: true, distribution: internal, environment: development }, preview: { node: 24.11.1, distribution: internal, environment: preview, android: { buildType: apk } }, preview-ios-simulator: { node: 24.11.1, distribution: internal, environment: preview, ios: { simulator: true } }, canary: { node: 24.11.1, distribution: internal, environment: preview, android: { buildType: apk } }, canary-store: { node: 24.11.1, distribution: store, environment: production }, production: { node: 24.11.1, distribution: store, environment: production } }, submit: { canary-store: { android: { serviceAccountKeyPath: ./google-service-account.json, track: internal } }, production: { android: { serviceAccountKeyPath: ./google-service-account.json, track: production } } } }各 profile 的分工一目了然development用于本地开发构建developmentClient: truepreview产出内部分发的 Android APKbuildType: apk便于直接侧载preview-ios-simulator产出免签名的 iOS 模拟器构建ios.simulator: truecanary是内部 APKcanary-store与production都是可提交商店的构建区别仅在提交轨道internal vs production。cli.appVersionSource: local表示使用本地应用版本——初始阶段由 Changesets 拥有包版本若未来想要 EAS 远程管理versionCode/buildNumber需在确认不与 Changesets 发布流程冲突后刻意切换。Scriptspackage.json 中的 CI 入口packages/agents-mobile/package.json 中的脚本是 CI 的直接入口doctor运行expo-doctorexport:androidexpo export --platform android --output-dir dist/androidexport:iosexpo export --platform ios --output-dir dist/iosci:checktypechecktsc --noEmit doctor Android export是本地检查的核心门禁。在 GitHub Actions 中必须写成pnpm --filter electric-ax/agents-mobile run script。裸写pnpm --filter ... doctor会被 pnpm 解析成自己的doctor命令而非包脚本这是实践中极易踩到的坑。依赖健康在把 EAS 构建设为必需之前依赖对齐是前置条件react/react-dom已在agents-mobile与agents-server-ui之间对齐react19.1.0、react-dom19.1.0解决了 Expo SDK 期望的react-native-webview版本13.15.0对齐了 runtime/server-ui 共享 TanStack DB 类型所用的 TypeScript peer graph重新运行expo-doctor后 18/18 全部通过。CI 工作流设计四个工作流 一个影响检测脚本工作流结构镜像桌面端.github/workflows/agents_mobile_pr.yml .github/workflows/agents_mobile_canary.yml .github/workflows/agents_mobile_build.yml .github/workflows/agents_mobile_ios_simulator.yml scripts/ci/mobile-affected.mjs影响检测scripts/ci/mobile-affected.mjs该脚本以scripts/ci/desktop-affected.mjs为蓝本两者同目录输入BASE_SHA。判定逻辑分为两类全局变更直接触发.github/workflows/agents_mobile_*.yml、.npmrc、.tool-versions、package.json、patches/**、pnpm-lock.yaml、pnpm-workspace.yaml、tsconfig.base.json、tsconfig.build.json工作区影响按移动端包闭包判定electric-ax/agents-mobile...自然覆盖packages/agents-mobile、packages/agents-server-ui、packages/agents-runtime以及移动端共享的 workspace 依赖。从实现看脚本通过 pnpm 的 workspace 解析listWorkspaces得到移动端闭包再与...[BASE_SHA]的变更闭包求交集最终输出should_build与受影响的 workspace 列表。可复用工作流agents_mobile_build.yml这是所有通道共用的底层工作流入参包括channelpr/canary/stable/ios-simulator、version、git_ref、platformandroid/ios/all、profilepreview/preview-ios-simulator/canary/canary-store/production、submitboolean、release_tag与release_name可选。标准步骤序列与仓库实现一致见 agents_mobile_build.yml按git_ref检出代码fetch-depth: 0用pnpm/action-setupv4配置 pnpm用actions/setup-nodev4从.tool-versions读取 Node 版本并启用 pnpm 缓存安装移动端依赖闭包pnpm --filter electric-ax/agents-mobile... install --frozen-lockfile构建移动端依赖闭包让干净 runner 能解析 workspace 导出pnpm -r --filter electric-ax/agents-mobile^... --if-present run build运行pnpm --filter electric-ax/agents-mobile run ci:check当平台为ios或all时额外运行 iOS export 检查通过expo/expo-github-actionv8配置 EASeas-version: latesttoken 用EXPO_TOKEN若submit为 true 且平台为 Android把GOOGLE_PLAY_SERVICE_ACCOUNT_JSON写到packages/agents-mobile/google-service-account.json实现中先用JSON.parse校验合法 JSON再chmod 600收紧权限并显式失败于空 secret写入.build-info.jsondate -u %s作为单调递增的 versionCode运行eas build --platform platform --profile profile --non-interactive --wait --json输出到eas-build.json若submit为 true 追加--auto-submit解析eas-build.json捕获build_id与build_url实现中对构建详情页 URL 做了多层兜底解析对 PR 通道用 marker 注释!-- electric-agents-mobile-pr-build --更新 sticky PR 评论先查找已有评论则 update否则 create并在构建失败时以退出码 1 让任务失败。PR 工作流agents_mobile_pr.yml触发方式为pull_requesttypes 含 opened/synchronize/reopened/labeled与workflow_dispatchpaths过滤与mobile-affected.mjs的全局模式保持一致。labeled类型的存在是为了让mobile-eas-build标签能立即触发构建同时paths约束保证标签只对触及相关路径的 PR 生效。行为要点先运行detect任务BASE_SHA取pull_request.base.sha或event.before判定移动端影响受影响时local-checks任务对所有 PR含 fork运行未认证的本地检查依赖安装、闭包构建、ci:check、iOS exporteas-preview任务在同仓库且带mobile-eas-build标签的 PR 上调用agents_mobile_build.yml参数为channel: pr、profile: preview、platform: android、submit: false、version: pr-number-shaworkflow_dispatch总是构建当构建相关但被跳过fork PR 无 secrets或缺少标签时eas-preview-skipped任务发布说明原因的评论fork 提示需维护者从受信任分支手动运行无标签则提示添加mobile-eas-build标签。并发控制上concurrency组按工作流 PR 号/ref 分组且刻意避免标签变更取消进行中的构建cancel-in-progress在 labeled 事件时为 false只有新 push 才会取代。Canary 工作流agents_mobile_canary.yml触发方式为 push 到main与workflow_dispatch。工作流提供profile选项canary、canary-store、preview默认canary与可选的git_ref调用agents_mobile_build.yml时使用channel: canary、version: canary-run_number-shaCI 标签用应用版本仍来自包/配置、platform: android且仅在 profile 为canary-store时submit: true因为只有 canary-store 有提交配置并产出商店 AAB。发布目标策略优先 EAS internal distribution若决定启用 canary 商店提交再通过canary-storeprofile 走 Google Play internal track。稳定版触发changesets_release.yml 的扩展在既有 Changesets 任务上增加输出mobile_release_version与mobile_release_tag从steps.changesets.outputs.publishedPackages中捕获electric-ax/agents-mobile实现用jq按包名筛选版本并拼出electric-ax/agents-mobileversion形式的 tag。随后新增publish-agents-mobile任务needs: changesets仅当mobile_release_tag非空时运行调用agents_mobile_build.yml参数为channel: stable、profile: production、platform: android、git_ref: mobile_release_tag、submit: true并携带release_tag/release_name用于发布展示。一个值得注意的工程细节可复用工作流即使跳过 PR 评论步骤也声明了相关权限而 GitHub 会在评估条件之前先校验可复用任务的声明权限因此调用方如 canary 与 iOS 模拟器工作流必须显式声明issues: write、pull-requests: write才能加载它——仓库中的注释明确记录了这一点。iOS 模拟器工作流agents_mobile_ios_simulator.yml手动工作流入参git_ref可选。调用agents_mobile_build.yml时使用channel: ios-simulator、profile: preview-ios-simulator、platform: ios、submit: false。要求EXPO_TOKEN不要求 Apple Developer 凭据。刻意保持手动一是控制 EAS 构建量二是模拟器制品更适合针对性的 iOS 冒烟测试而非每个 PR。注意手动工作流只有在文件存在于默认分支后才能在 GitHub 上 dispatch。版本策略Changesets 拥有语义版本CI 拥有单调构建号推荐的分工是package.json中的version作为应用显示版本由 Changesets 维护语义化版本app.config.ts读取该版本作为version与runtimeVersionAndroidversionCode单调递增由 CI 生成当前实现是date -u %s写入.build-info.json或由显式的 EAS versioning 决策接管iOSbuildNumber启用后采用同一策略。文档特别警示不要把 AndroidversionCode直接绑定到 semver 的组成段如用 major/minor/patch 拼接除非能确信它永远不会碰撞或回退——商店要求 versionCode 在每次上传中严格单调递增。实施阶段回顾从构建就绪到 iOS 的五步路线Phase 1让应用可构建已全部完成新增app.config.ts、新增eas.json、确定最终 Android package id、预留 iOS bundle id、新增移动端 CI/导出脚本、修复expo-doctor问题并确认移动端 typecheck、Expo doctor、Android export、iOS export、生成的 Expo public config 全部通过。Phase 2PR 构建已全部完成新增scripts/ci/mobile-affected.mjs、可复用agents_mobile_build.yml、agents_mobile_pr.ymlAndroid preview APK 走 EAS internal distributionPR 工作流对所有受影响 PR 运行未认证本地检查同仓库/受信任 PR 获得 sticky EAS 构建评论fork PR 获得跳过说明评论。Phase 3Canary 构建已完成agents_mobile_canary.yml从main用 EAScanaryinternal distribution profile 构建 Android。未启用通过canary-storeprofile 与GOOGLE_PLAY_SERVICE_ACCOUNT_JSON向 Google Play internal track 提交 canaryiOS canary 在 Apple Developer/TestFlight 就绪前保持阻塞。Phase 4稳定版发布已完成扩展changesets_release.yml从publishedPackages捕获移动端发布 tag/versionelectric-ax/agents-mobile发布时触发 Android production EAS 构建将GOOGLE_PLAY_SERVICE_ACCOUNT_JSON写入 CI 临时文件供 EAS Submit 使用通过 EAS Submit 把稳定版 Android 构建提交到 Google Play production track。仍可能遇到Google Play 要求首次 API 提交前先在 Play Console 手动上传一个 AAB或需要额外商店配置。Phase 5iOSApple Developer 账户就绪前已完成iOS export 检查在本地与移动端 CI 中通过新增preview-ios-simulatorEAS profile新增手动agents_mobile_ios_simulator.yml设置ITSAppUsesNonExemptEncryption为false避免标准 HTTPS-only 加密场景下被 App Store Connect 加密元数据阻塞确认原生 iOS 模拟器编译成功。Apple Developer 账户就绪后添加 bundle id 与 App Store Connect 应用配置 EAS iOS 凭据启用签名 iOS preview/canary 构建添加 TestFlight 提交启用稳定版 iOS 发布提交。开放决策与后续步骤尚未拍板的决策包括稳定版 Android 直接进 production 还是先 staged rolloutPR 的 EAS 构建是每个受影响 PR 都跑还是用标签控制构建量与成本是否保留electric-ax/agents-mobile的private: true本仓库 Changesets 可以为私有包打版本/tag因此不阻塞发布编排以及先保持 canary 在 EAS internal distribution、还是待首个稳定版提交验证 Play 路径后再启用 Google Play internal-track canary。后续行动分两段近期先完成 Google Play 商店设置问卷/上架要求以避免首个生产提交被 Play Console 元数据阻塞决定稳定版提交方式与canary-store是否启用落地移动端 PR让手动 iOS 模拟器工作流进入默认分支。取得 Apple Developer 权限后创建 bundle id 与 App Store Connect 应用、配置 EAS 托管 iOS 签名凭据、增加签名 iOS preview/canary/TestFlight profile并把 Changesets 发布扩展覆盖 iOS/TestFlight/App Store。对希望复刻这套方案的团队值得带走的设计要点是用一层可复用的构建工作流agents_mobile_build.yml承载所有通道用影响检测脚本精确控制触发范围用本地检查typecheck/doctor/export与云端原生构建EAS Build分层提供信号让 Changesets 继续作为版本与发布的唯一事实来源同时由 CI 独立管理单调递增的构建号——这样 PR、canary、稳定版三个通道可以共享同一套可靠路径且各自互不干扰。【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考