Wasp 应用 CI/CD 实战指南:基于 GitHub Actions 的自动化测试与持续部署
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
在 Wasp 全栈框架(React + Node.js + Prisma)中,CI/CD 是"推代码即上线"的关键一环:持续集成(CI)在每次代码推送后自动验证与测试改动,尽早暴露问题;持续部署(CD)则把通过验证的代码自动发布到生产环境。本文以 Wasp 0.18 版本的官方 CI/CD 文档为核心,结合本仓库内的真实示例与源码,完整讲解如何在 GitHub Actions 中运行端到端测试、单元测试,并以 Docker 镜像或静态文件两种方式实现自动化部署,读完即可在你的 Wasp 项目里落地一套可运行的流水线。
认识 CI 与 CD:为什么 Wasp 应用需要流水线
持续集成(Continuous Integration,CI):每当代码被推送到仓库时,通过自动化流程对代码变更进行校验和测试。它帮助我们尽早捕获 bug,并确保应用始终处于可用状态。Wasp 应用往往同时包含客户端(React SPA)、服务端(Node.js)与数据库(Prisma 管理的 PostgreSQL)三部分,任何一个环节回归都会影响整体功能,因此 CI 的价值尤其明显。
持续部署(Continuous Deployment,CD):将代码变更自动部署到生产环境。这种"push to deploy"的工作方式把开发者从手动部署中解放出来:你只需要把改动推送到指定分支,流水线就会完成构建、打包、推送与上线。
设置 CI/CD 对 Wasp 应用来说是可选项,但官方强烈建议在部署应用时一并配置。
在 CI 中运行测试
Wasp 官方文档将 CI 中的测试分为两类:端到端测试(模拟真实用户)与单元测试(隔离验证代码逻辑)。两者互补:E2E 覆盖用户真实操作路径,单测则更轻更快、能在毫秒级反馈问题。
端到端(E2E)测试:模拟真实用户操作
端到端测试使用真实浏览器模拟用户使用你的应用,可以覆盖登录、加购、创建任务等完整场景。有了 E2E,你就不必在每次改动后手动回归测试整个应用。
在 CI 中运行 Wasp 应用的 E2E 测试,需要三步:
- 在 CI 环境中安装 Wasp;
- 在 CI 环境中启动你的应用(连同数据库);
- 针对运行中的应用执行 E2E 测试。
官方示例以GitHub Actions作为 CI 平台、以Playwright作为 E2E 测试框架。你可以在仓库内直接看到这类测试的真实形态:本仓库的多个示例项目都带有e2e-tests目录,例如 examples/kitchen-sink/e2e-tests(含 19 个测试文件)、examples/waspello/e2e-tests、examples/ask-the-documents/e2e-tests 等,它们都可以作为你搭建自己 E2E 测试的参照模板——把e2e-tests目录复制到你的项目里,按你的应用修改即可,这样本地也能直接跑通 E2E。
一个典型的 Wasp E2E 测试长这样(模拟"登录并添加任务"的完整用户流程):
import { expect, test } from '@playwright/test' import { generateRandomUser, logUserIn } from './utils' const user = generateRandomUser() test.describe('basic user flow test', () => { test('log in and add task', async ({ page }) => { await logUserIn({ page, user }) await expect(page).toHaveURL('/') await expect(page.locator('body')).toContainText('No tasks yet.') // Add a task await page.fill('input[name="description"]', 'First task') await page.click('input:has-text("Create task")') await expect(page.locator('body')).toContainText('First task') }) })这个测试先通过generateRandomUser()生成随机用户、用logUserIn完成登录,再断言页面 URL 与文案,随后通过选择器定位输入框提交任务,最后验证任务出现在页面上。utils中的辅助函数封装了注册、登录等重复操作,是 E2E 测试保持简洁的关键。
仓库里的 examples/kitchen-sink/e2e-tests/playwright.config.ts 展示了与 CI 深度配合的 Playwright 配置,其中有几个值得注意的设计:
export default defineConfig({ testDir: "./tests", /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, reporter: process.env.CI ? "dot" : "list", ... });forbidOnly: !!process.env.CI:在 CI 上如果误留了test.only会直接让构建失败,防止"只跑一个测试"的调试代码被合入;retries:仅在 CI 环境自动重试 2 次,规避偶发的网络抖动导致的失败;workers: 1:CI 上关闭并行,降低资源占用与相互干扰;reporter:CI 使用紧凑的dot报告,本地使用可读性更好的list。
要在 GitHub Actions 中运行这些测试,需要在仓库中创建.github/workflows/e2e-tests.yml工作流文件。参考官方文档给出的步骤,一个完整的工作流大致如下:
name: E2E Tests on: push: branches: [main] pull_request: jobs: e2e: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install Wasp run: curl -sSL https://get.wasp.sh/installer.sh | sh -s -- -v 0.18.0 # 替换为你的 Wasp 版本 - name: Install dependencies run: wasp install - name: Start the database run: wasp db start - name: Start the app run: wasp start & - name: Run E2E tests run: npx playwright test要点:CI 环境需要 Node.js 与 Wasp CLI;数据库与应用需要先于测试启动(Playwright 的webServer配置项也可以代为管理启动流程,见 examples/kitchen-sink/e2e-tests/playwright.config.ts 中的webServer配置);最后在应用运行状态下执行 Playwright 测试。
单元测试:快速验证代码逻辑
单元测试在隔离环境中测试代码逻辑的单个片段,比 E2E 更简单、更快速,但不模拟真实用户交互。两者结合使用:单测负责快速回归核心逻辑,E2E 负责验证整体用户体验。
对于客户端代码,可以使用 Wasp 内置的客户端测试支持,详见 web/versioned_docs/version-0.18/project/testing.md;服务端代码则可以自由选择任意测试框架。
在 CI 中运行单元测试的方式与 E2E 类似:
- 在 CI 环境中安装 Wasp;
- 用
wasp test client run运行客户端测试; - 用你自己的测试框架运行服务端测试。
关于客户端测试,官方文档 web/versioned_docs/version-0.18/project/testing.md 给出了更完整的细节,几个关键点直接决定了 CI 脚本怎么写:
- 底层技术栈:Wasp 基于 Vite,客户端测试通过 Vitest 运行,并内置了
jsdom(浏览器环境模拟)、@testing-library/react(渲染与断言辅助)、msw(服务端 mock)等库; - 测试文件识别规则:测试文件必须放在
src目录内,且扩展名匹配 Vitest 的 glob 模式,例如yourFile.test.ts、YourComponent.spec.jsx; - 运行命令:
wasp test client进入 watch 模式(开发用),wasp test client run只运行一次(CI 用)。wasp test client之后的所有参数都会透传给 Vitest CLI; - 注意:不要同时运行
wasp test和wasp start,两者都会尝试编译项目到.wasp/out,会产生冲突。
此外,Wasp 还提供了两个 React 测试辅助函数:renderInContext(把组件包进QueryClientProvider和Router再渲染)与mockServer(基于 msw 提供mockQuery/mockApi来模拟查询与 API 响应)。这些能力意味着客户端单测在 CI 上无需真实后端即可运行,速度很快。
持续部署(CD)的两种主流方式
Wasp 官方文档介绍了两种用 CI/CD 流水线部署应用的方式:
- 用 Docker 打包服务端和客户端;
- 将客户端部署为静态文件。
方式一:用 Docker 打包服务端与客户端
把应用打包成 Docker 镜像是目前最主流的部署方式,好处是同一份镜像可以轻松部署到不同环境(staging、production 等),环境差异被隔离在镜像内部。
要将应用构建为 Docker 镜像,需要:
- 在 CD 环境中安装 Docker;
- 用
wasp build构建应用; - 构建 Docker 镜像并推送到 Docker Registry(分别为服务端应用和客户端应用各构建一份);
- 对部分托管平台,还需要通知它们拉取并部署新版本。
什么是 Docker Registry?Docker Registry 是存放 Docker 镜像的地方,部署平台可以从这里拉取镜像。最常见的 Registry 是 Docker Hub,也可以使用 GitHub Container Registry(GHCR)等其他 Registry。
构建产物说明:在 Wasp 0.18 中,wasp build会在项目根目录生成.wasp/build文件夹,其中包含服务端与客户端两套构建产物(在更新的 Wasp 版本中,该目录改名为.wasp/out,细节以你所用版本的 CLI 参考为准)。服务端的Dockerfile就位于.wasp/build目录内,可以直接用来构建服务端镜像。
示例部署:Coolify + GitHub Actions + GHCR
官方文档以Coolify 部署示例(见 self-hosted.md 的 Coolify 章节)为例:使用 GitHub Actions 构建 Docker 镜像,使用 GitHub Container Registry(GHCR)存储镜像。整个deploy.yml工作流的六个关键步骤:
- 认证 GitHub Container Registry(GHCR):使用
docker/login-action动作完成 GHCR 登录认证; - 准备 Docker 镜像元数据:使用
docker/metadata-action动作生成后续构建与部署所需的额外信息(如镜像标签); - 构建 Wasp 应用:执行
wasp build,在.wasp/build文件夹中得到服务端和客户端产物; - 打包服务端镜像并推送到 GHCR:使用
.wasp/build目录中的Dockerfile,通过docker/build-push-action动作构建并推送服务端 Docker 镜像; - 打包客户端镜像并推送到 GHCR:为客户端编写一个
Dockerfile(官方方案是用一个简单的 Go 静态服务器来托管客户端应用),再次使用docker/build-push-action构建并推送客户端镜像; - 通知 Coolify 部署新版本:通过 Coolify 的 Webhook API 触发其拉取新镜像并完成部署。
这六步构成了一个完整的"Docker 化 + 镜像仓库 + 通知部署"闭环,同样适用于其他支持从 Registry 拉取镜像的 PaaS/自托管平台(例如 self-hosted.md 中介绍的 CapRover 也是同一种模式:CI 构建并上传镜像,平台拉取部署)。
方式二:客户端静态化部署
Wasp 的客户端是单页应用(SPA),构建后会变成纯静态的 HTML、CSS 和 JS 文件,可以上传到任何支持静态文件托管的平台。这意味着客户端不必使用 Docker 镜像——托管静态文件通常比托管 Docker 镜像更便宜。
要将客户端应用部署为静态文件,需要:
- 在 CD 环境中用
wasp build构建应用; - 构建客户端应用(进入
.wasp/build/web-app目录执行npm install && npm run build,构建产物在.wasp/build/web-app/build目录); - 把静态文件上传到你的托管平台。
Netlify / Cloudflare 的 GitHub Actions 示例
官方在 PaaS 部署文档中给出了 Netlify 和 Cloudflare 两个静态部署的完整 GitHub Actions 工作流示例,其骨架一致,可作为"客户端静态部署流水线"的通用模板:
name: Deploy Client to Netlify on: push: branches: - main # Deploy on every push to the main branch jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install Wasp run: curl -sSL https://get.wasp.sh/installer.sh | sh -s -- -v 0.18.0 # Change to your Wasp version - name: Wasp Build run: wasp build - name: Install dependencies and build the client run: | cd ./.wasp/build/web-app npm install REACT_APP_API_URL=${{ secrets.WASP_SERVER_URL }} npm run build - name: Deploy to Netlify run: | cd ./.wasp/build/web-app npx netlify-cli deploy --prod --dir=build --auth=$NETLIFY_AUTH_TOKEN --site=$NETLIFY_SITE_NAME env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_NAME: netlify-site-name其中三个环境变量需要提前配置到 GitHub Repository Secrets 中:
NETLIFY_AUTH_TOKEN:Netlify 的 Personal Access Token,在 Netlify 后台生成;NETLIFY_SITE_NAME:你的 Netlify 项目名称;WASP_SERVER_URL:服务端的 URL,一般要等服务端部署完成后才有;后端未就绪时可以跳过,但依赖后端的功能会失效。
重要提醒:Wasp 是 SPA,客户端路由由前端处理。部署到 Netlify 时,必须确保其将所有 URL 重定向到index.html。Wasp 默认会在.wasp/build/web-app/下生成netlify.toml来配置这一行为;如果你用 CI 而非 CLI 部署,务必让 Netlify 识别该文件,或手动配置重定向规则。Cloudflare Pages 会自动把所有路径重定向到index.html,因此无需额外配置。
生产环境的环境变量:与开发环境的本质区别
CI/CD 流水线中一个极易踩坑的点是环境变量。开发时,Wasp 支持用.env.client和.env.server文件管理变量;但部署时这两个文件会被忽略,必须通过其他方式提供,详见 web/versioned_docs/version-0.18/deployment/env-vars.md。
- 客户端环境变量:在构建过程中被注入到客户端 JS 代码中,对任何浏览者都是公开可见的,绝不能存放密钥(如第三方 API Secret)。生产构建时,把客户端变量直接传给构建命令即可:
REACT_APP_API_URL=<url_to_wasp_backend> npm run build。原理是 Wasp(Vite)在构建时把代码中所有import.meta.env.REACT_APP_*替换为实际值。注意:在托管平台上为客户端设置环境变量是无效的——构建完成后客户端只是静态文件,不会再读取运行时环境。 - 服务端环境变量:
DATABASE_URL、WASP_WEB_CLIENT_URL、JWT_SECRET等必须通过托管平台提供的机制设置。例如部署到 Fly.io 时用fly secrets set SOME_VAR=somevalue,在 GitHub Actions 里则使用secrets.XXX引用仓库 Secret。这正是上面 Netlify 示例中REACT_APP_API_URL出现在构建命令里、而NETLIFY_AUTH_TOKEN出现在仓库 Secret 里的原因。
上线前的最后一道保险:本地验证生产构建
在把wasp build产物交给 CI/CD 之前,推荐先用 Wasp 0.18 提供的wasp build start命令在本地"彩排"一遍生产构建。该命令会基于wasp build的输出启动一个本地服务器,用与生产一致的优化后代码运行应用,并要求你显式指定--server-env/--client-env(或对应的--*-env-file)环境变量——这会逼着你提前梳理清楚生产环境到底需要哪些变量,从而在 CI/CD 阶段减少"构建成功但运行失败"的返工。完整说明见 local-testing.md 与 CLI 参考。
小结:一套完整的 Wasp CI/CD 流水线
将上述内容串起来,一个生产可用的 Wasp CI/CD 流水线包含以下环节:
- CI 测试:安装 Wasp → 启动数据库与应用 → 运行 Playwright E2E;同时用
wasp test client run快速跑客户端单测,服务端单测用你熟悉的框架执行; - 构建:
wasp build生成.wasp/build部署产物(客户端 SPA 静态文件 + 服务端应用及Dockerfile); - CD 部署:二选一——
- Docker 路线:构建服务端与客户端镜像并推送到 Registry(如 GHCR),再通过 Webhook 通知 Coolify/CapRover 等平台拉取部署;
- 静态路线:客户端走 Netlify/Cloudflare 等静态托管(注意 SPA 重定向到
index.html),服务端仍按 Docker 方式部署;
- 环境变量:客户端变量注入到构建命令,服务端变量配置到托管平台或仓库 Secret。
这套流程与仓库内的真实工程完全对应:examples/kitchen-sink、examples/waspello、examples/ask-the-documents等示例项目均自带e2e-tests目录与 Playwright 配置可供参考,部署相关文档详见 ci-cd.md 同目录的部署文档。
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考