如何自动化实现expo预览

640 阅读5分钟

🥳📱 如果 Expo 预览能像 Vercel 部署一样丝滑该多好?🫰

Expo 是一个开源框架,用于使用 React 开发通用的原生应用程序。它支持在 Android、iOS 和 Web 平台上运行。

Vercel 是一个云平台,专为前端开发者设计,用于托管和部署 Jamstack 网站和应用程序。它提供了从开发到生产的无缝工作流程,支持自动化部署、全球 CDN 分发、以及与 Git 仓库的集成,适用于使用 React、Vue、Next.js 等框架的项目。

如果你用过 Vercel 的预览部署,你就知道那种神奇的感觉:提交 PR,瞬间就能获得一个可以分享的在线链接。无需手动部署,无需等待。

image.png

如果我们能为 Expo 移动应用也实现这样无缝的体验,岂不是很棒?

这正是我们所构建的。通过一个简单的 GitHub Action,现在每个 PR 都会自动生成一个 Expo 二维码,让你能够像 Vercel 一样在手机上即时预览变更!

最棒的是?**设置过程不到一分钟!**⏳

让我们开始吧 👇

🔧 第一步:添加必需的 GitHub Secrets

要实现这个功能,我们需要两个令牌来与 ExpoGitHub 进行认证。

1️⃣ 创建 GitHub 个人访问令牌(GH_PAT

  1. 访问 GitHub 个人访问令牌

  2. 点击 "生成新令牌(经典)"

  3. 设置过期时间(选择较长的时间以避免频繁更新)

  4. 选择以下权限范围

    • repo(私有仓库的完全控制权限)

    • workflow(允许 GitHub Actions 访问工作流)

    • read:packages(如果你使用私有 npm 包)

  5. 点击 "生成令牌"复制它

  6. 进入你的 GitHub 仓库 → 设置 → Secrets

  7. 添加一个名为 GH_PAT 的新密钥并粘贴令牌

2️⃣ 创建 Expo 令牌(EXPO_TOKEN

你可以直接在 Expo 项目设置中创建这个令牌:

  1. 打开 Expo.dev

  2. 进入 账户设置 → 访问令牌

  3. 点击 "生成新令牌"

  4. 复制令牌并将其添加为名为 EXPO_TOKENGitHub Secret

🛠️ 第二步:添加用于 Expo 预览的 GitHub Action

现在我们有了令牌,需要设置 GitHub Actions

  1. 在你的仓库中创建新文件.github/workflows/expo-preview.yml

  2. 复制并粘贴以下 GitHub Actions 工作流:

name: Expo 预览二维码

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  preview:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    timeout-minutes: 15

    steps:
      - name: 🏗 设置代码仓库
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.GH_PAT }}

      - name: 🏗 设置 Node 环境
        uses: actions/setup-node@v4
        with:
          node-version: 22.x

      - name: 📦 缓存依赖
        uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: 📦 安装依赖
        run: npm install
        continue-on-error: true
        id: npm_install

      - name: 📦 重试安装依赖
        if: steps.npm_install.outcome == 'failure'
        run: |
          rm -rf node_modules
          npm cache clean --force
          npm install --no-cache

      - name: 🏗 设置 EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

      - name: 🚀 创建预览并提取详细信息
        id: create-preview
        continue-on-error: true
        run: |
          OUTPUT=$(eas update --auto --branch ${{ github.event.pull_request.head.ref }})
          echo "$OUTPUT"

          # 提取更新组ID (groupId)
          GROUP_ID=$(echo "$OUTPUT" | grep -oP 'Update group ID\s+\K[\w-]+')

          # 提取 EAS 仪表盘 URL
          EAS_DASHBOARD_URL=$(echo "$OUTPUT" | grep -oP 'EAS Dashboard\s+\Khttps://expo.dev/accounts/[^ ]+')

          # 动态解析账户名和项目标识
          ACCOUNT_NAME=$(echo "$EAS_DASHBOARD_URL" | awk -F'/' '{print $(NF-3)}')
          PROJECT_SLUG=$(echo "$EAS_DASHBOARD_URL" | awk -F'/' '{print $(NF-1)}')

          # 使用 `eas project:info` 获取正确的 appId
          echo "正在获取 EAS 项目信息..."
          PROJECT_INFO=$(eas project:info)
          echo "EAS 项目信息输出:$PROJECT_INFO"

          # 使用 grep 提取 appId
          APP_ID=$(echo "$PROJECT_INFO" | grep -oP 'ID\s+\K[\w-]+')

          # 从提交时间戳提取最后更新时间
          COMMIT_TIMESTAMP=$(echo "$OUTPUT" | grep -oP 'Commit\s+\K[\w\d]+')

          # 转换时间戳为可读的 UTC 格式
          LAST_UPDATED=$(date -u -d "@$(git show -s --format=%ct $COMMIT_TIMESTAMP)" "+%b %-d, %Y %-I:%M%p UTC")

          if [ -z "$APP_ID" ] || [ -z "$GROUP_ID" ] || [ -z "$ACCOUNT_NAME" ] || [ -z "$PROJECT_SLUG" ] || [ -z "$LAST_UPDATED" ]; then
            echo "❌ 无法提取所需的值,请检查输出信息。"
            exit 1
          fi

          DEEP_LINK="exp+://expo-development-client/?url=https://u.expo.dev/$APP_ID/group/$GROUP_ID"
          UPDATE_LINK="https://expo.dev/accounts/$ACCOUNT_NAME/projects/$PROJECT_SLUG/updates/$GROUP_ID"
          QR_CODE_URL="https://qr.expo.dev/eas-update?slug=exp&projectId=$APP_ID&groupId=$GROUP_ID&host=u.expo.dev&scale=2"

          echo "深层链接:$DEEP_LINK"
          echo "UPDATE_LINK=$UPDATE_LINK" >> $GITHUB_ENV
          echo "QR_CODE_URL=$QR_CODE_URL" >> $GITHUB_ENV
          echo "LAST_UPDATED=$LAST_UPDATED" >> $GITHUB_ENV

      - name: 📢  PR 中更新或发布 Expo 二维码
        if: success()
        uses: actions/github-script@v7
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const updateLink = process.env.UPDATE_LINK;
            const qrCodeUrl = process.env.QR_CODE_URL;
            const lastUpdated = process.env.LAST_UPDATED;

            if (!updateLink || !qrCodeUrl || !lastUpdated) {
              console.error("❌ 缺少更新链接、二维码 URL 或最后更新时间戳。");
              return;
            }

            const { owner, repo, number } = context.issue;
            const commentIdentifier = "🚀 **Expo 预览就绪!**"; // 唯一标识符

            // 生成带有二维码的评论内容
            const commentBody = `
            🚀 **Expo 预览就绪!**  *更新时间: ${lastUpdated}*  

            📱 扫描下方二维码在 **Expo Dev Client** 中测试:  

            ![Expo 二维码](${qrCodeUrl})  

            <a href="${updateLink}" target="_blank"><strong>打开 Expo 更新 ↗︎</strong></a>  

            ---
            
            📋 **调试信息**
            - 分支: \`${context.payload.pull_request.head.ref}\`
            - 提交: \`${context.sha}\`
            - Node版本: \`22.x\`
            - Expo CLI版本: \`latest\`
            
            如遇问题,请确保已安装最新版本的 Expo Go 应用。
            `

            // 获取现有评论
            const comments = await github.rest.issues.listComments({
              owner,
              repo,
              issue_number: number
            });

            const existingComment = comments.data.find(comment =>
              comment.body.includes(commentIdentifier)
            );

            if (existingComment) {
              // 更新现有评论
              await github.rest.issues.updateComment({
                owner,
                repo,
                comment_id: existingComment.id,
                body: commentBody
              });
              console.info("✅ 已更新现有 Expo 预览评论。");
            } else {
              // 创建新评论
              await github.rest.issues.createComment({
                owner,
                repo,
                issue_number: number,
                body: commentBody
              });
              console.info("✅ 已创建新的 Expo 预览评论。");
            }

🎯 第三步:提交 PR 并获取 Expo 预览

现在,每当你提交或更新 PR 时,GitHub Actions 将:

✅ 启动 Expo 预览服务器

✅ 生成二维码

✅ 自动在 PR 中添加预览链接评论

image.png

🥳 就是这样,尽情享受你的新自动化 Expo PR 流程吧!🥳


🔍 为什么这个方案如此有效(以及我们为什么这样构建)

🔹 受 Vercel 预览功能启发

Vercel 改变了我们部署前端应用的方式。我们希望为 Expo 和 React Native 项目带来同样流畅的体验

🔹 移动优先的协作方式

不再需要手动启动 Expo 服务器并在 Slack 中发送链接。现在每个 PR 都自带预览链接。

🔹 更快的测试,更少的麻烦

让设计师、产品经理和利益相关者进行 QA 测试反馈变得更加容易。


⚡ 常见问题与故障排除

GitHub Actions 失败?

请确保:

  • 你已将 GH_PATEXPO_TOKEN 都添加到 GitHub Secrets

  • 你的 Expo 账户有权限生成预览

  • 尝试在本地环境中重启 Expo CLI 看看是否正常工作

PR 评论中没有显示二维码?

检查 GitHub 中的 PR Actions 日志,看看 Expo 服务器是否正确启动。

❌ 仍然遇到问题?


💡 最后的思考

通过这个简单的 GitHub Action,你可以完全自动化 PR 的 Expo 预览就像 Vercel 为网页应用所做的那样

不再需要手动分享链接。不再需要等待他人启动 Expo 服务器。只需扫描二维码即可立即测试。