资讯动态

Midscene.js + GitHub Actions:搭建无人值守的定时巡检系统

发布时间:2026/10/20 17:11:27 来源:尧图企业网站定制
1. 项目思路与场景拆解1.1 定时巡检到底解决什么问题先说个很现实的场景。我维护的一个小型搜索应用平时看着挺正常但总有那么几次是用户跑来跟我说“收藏列表打不开了”我才知道线上出了问题。你回头查的时候可能是前天发版把某个接口字段改了也可能是某个活动下线后遗留了一段无效代码前端页面没报错但核心交互就是静默地挂了。这种问题最头疼因为它不崩、不红、不报错就是功能没反应。人工巡检确实能发现一部分但靠人肉每天早上点一遍页面坚持一周就烦了坚持一个月基本就变成“例行打开看一眼”根本点不到深层交互。这个痛点正好是自动化巡检的用武之地把核心用户路径固化成一堆脚本让它在固定时间自己跑到线上走一圈挂了就截图留证据、发通知。这次我把 Midscene.js 的自动化任务接进了 GitHub Actions用定时调度实现了一套无人值守的巡检闭环整个方案跑了一个多月确实帮我提前抓住了几次问题。这套方案适合谁适合手上有线上业务、又不想维护一台专门跑巡检的服务器的人。特别是用 GitHub 管理代码的团队前端、测试、独立开发者都可以直接照着搭。Midscene.js 负责“怎么测”GitHub Actions 负责“什么时候测、测完怎么通知”两者一组合就是一个性价比很高的线上质量小卫兵。1.2 为什么选 Midscene.js 而不是纯 Playwright其实我最早做巡检用的是纯 Playwright 写脚本选择器、等待、断言全手写。问题在于页面一旦轻微改版定位器就失效脚本三天两头要改。我维护的页面虽然不大但只要前端结构调整巡检脚本的维护成本立刻压过来。Midscene.js 的差异在于它允许你用近乎自然语言的方式描述测试意图底层再通过视觉理解和模型能力去匹配页面元素。我写“在顶部的搜索框输入关键词点击搜索按钮断言结果列表出现”脚本内部自己去找对应元素而不是依赖死板的 xpath 或 text 选择器。页面上按钮位置挪了、文案变了只要视觉语义没变它大概率还能跑通。这一点对我这种小团队太重要了因为没人愿意每周花半天去修巡检脚本。另外 Midscene.js 支持生成可视化报告和截图我会在后面的配置里详细说。GitHub Actions 则是天然契合仓库就在上面定时任务直接用 cron 语法配置跑完的产物可以直接上传到 Actions 页面留存还支持手动触发排查问题非常方便。不需要额外服务器不占用本地电脑免费额度对个人项目足够用。2. 环境准备与脚本改造2.1 最小项目结构与依赖安装要让整个流程跑起来先把项目结构梳理清楚。我建议把巡检脚本独立成一个子目录不要和你业务代码混在一起。我的典型结构长这样daily-inspect/ ├── .github/ │ └── workflows/ │ └── daily-inspect.yml ├── scripts/ │ ├── search-flow.ts │ └── utils.ts ├── reports/ # 本地跑的时候生成报告 ├── package.json ├── tsconfig.json └── .env.local # 本地用的环境变量别提交到仓库依赖安装方面我用的是最常规的组合npm init -y npm install midscene/cli playwright midscene/playwright npm install -D typescript tsx npx playwright install chromium这里有两个点需要解释一下。第一浏览器我只装了 chromium没有装全量的 chrome、firefox、webkit因为巡检场景里 chromium 一个就够了装多了只会拖慢 GitHub Actions 的执行时间。第二本地调试时我用.env.local存模型服务的 API KeyGitHub Actions 里则用 Repository Secrets两边互不干扰。Midscene.js 的核心能力需要调用大模型做页面语义识别所以要准备一个模型服务的 API Key。这类 Key 一定要走环境变量绝不能硬编码进脚本。我在配置里统一命名为MIDSCENE_MODEL_API_KEY本地和 CI 环境都读这个变量省得脚本里写两套逻辑。2.2 用自然语言写巡检用例接下来是核心部分把你要巡检的业务流程翻译成 Midscene.js 能执行的脚本。我拿自己那个搜索应用举例巡检目标是验证“用户搜索关键词后结果列表能正确渲染并且第一条结果的标题非空”。用代码写的思路大致长这样import { Agent } from midscene/core; const agent new Agent({ modelApiKey: process.env.MIDSCENE_MODEL_API_KEY!, browser: chromium, }); async function runSearchFlow() { await agent.openPage(https://example-test-site.com/search); await agent.run( 请在页面顶部的搜索框中输入自动化测试工具 然后点击旁边的搜索按钮 等待结果列表加载完成 断言页面上出现了至少一个带标题的结果项 如果断言通过把整个页面的截图保存为搜索流程-成功.png 如果断言失败截图保存为搜索流程-失败.png。 ); await agent.exportReport(); } runSearchFlow().catch((e) { console.error(巡检失败: , e); process.exit(1); });这段代码的关键在于agent.run()里那段中文描述。Midscene.js 的模型能力会把这段描述拆解成一系列动作指令自己完成“找输入框、点击、等待、断言”的操作。你不需要写page.locator(#search-input)这种代码只要把用户视角的“行为意图”写清楚就行。这里我踩过一个坑指令描述尽量拆成短句每一步都明确是“做什么”和“等待什么”。如果一句话写太长模型拆解的稳定性会下降偶尔会把“点击搜索”和“等待结果”合并成一步导致断言时机太早。改成上面那种分步骤写法之后稳定性明显上来了。另外我习惯在每个关键步骤都安排一次截图。机器的巡检和人工巡检一样得有“证据”。截图不仅能确认结果一旦巡检失败你翻日志时能直接看到页面当时长什么样而不是靠猜。Midscene.js 的导出报告会把所有动作、截图、耗时整理成一个 HTML 文件我后面会让 Actions 把它作为构建产物保存下来。2.3 本地先跑通这一关必须较真接入 CI 之前我强烈建议你先在本地把整套脚本跑通。因为 GitHub Actions 环境虽然和本地接近但它是个干净的虚拟机任何没在本地验证过的问题到 CI 里都会被放大。常见如浏览器二进制缺失、依赖没装全、绝对路径写错、环境变量没传都会让你多调试好几轮。本地跑的命令很简单set -a source .env.local set a npx tsx scripts/search-flow.ts这里用tsx直接跑 TypeScript 脚本不用先编译再执行调试时改一行立刻就能重跑。如果本地能顺利生成报告和截图再进入 Git 操作。本地调试时我会开非无头模式让浏览器窗口弹出来肉眼看着它一步步操作。虽然 CI 里肯定是 headless但本地调试阶段建议先关掉 headless因为自然语言驱动的自动化在出错时你光看日志不一定能理解模型为什么做出某个行为眼看一遍最快。确认每一步动作符合预期后再把 headless 打开跑一遍完整流程确保无头模式下结果一致。提示本地和 CI 的 base URL 要区分。我习惯通过环境变量INSPECT_BASE_URL控制目标地址而不是把域名硬编码进脚本。本地用测试站CI 用线上真实地址同一套脚本两端复用减少维护量。3. 接入 GitHub Actions 流水线3.1 workflow 文件怎么写现在到了重头戏把巡检脚本接入 GitHub Actions做成定时任务。核心就是一个 workflow 的 YAML 文件放在.github/workflows/目录下。我用的配置如下name: daily-inspect on: schedule: - cron: 0 0 * * * workflow_dispatch: jobs: inspect: runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Checkout uses: actions/checkoutv4 - name: Setup Node uses: actions/setup-nodev4 with: node-version: 20 cache: npm - name: Download Playwright Browser run: npx playwright install --with-deps chromium - name: Install Dependencies run: npm ci - name: Run Inspection env: MIDSCENE_MODEL_API_KEY: ${{ secrets.MIDSCENE_MODEL_API_KEY }} INSPECT_BASE_URL: ${{ secrets.INSPECT_BASE_URL }} run: npx tsx scripts/search-flow.ts - name: Upload Reports if: always() uses: actions/upload-artifactv4 with: name: inspect-reports path: reports/我先重点讲一下on里面的配置逻辑。schedule.cron定义定时触发的时间点GitHub Actions 使用的 cron 表达式是 UTC 时区的标准五段格式分、时、日、月、周。0 0 * * *表示每天 UTC 零点换算成北京时间就是早上 8 点。如果你不关心必须几点跑其实定时时间的精度要求并不高关键是理解时区换算否则你会发现自己配置的“晚上 8 点”实际是在“凌晨 4 点”跑的。workflow_dispatch一定要加。它允许你在 Actions 页面上手动点击“Run workflow”按钮调试时不用干等定时触发一次手动执行就能验证整条链路是否通。我第一次接好后就是用这个按钮直接触发的省了整整一天的等待时间。3.2 运行环境里的坑automation 任务在云端虚拟机跑很多东西和本地不完全一样。我总结几个一定会遇到的点提前说能帮你少走弯路。第一浏览器必须提前装好。我用了npx playwright install --with-deps chromium重点在--with-deps。它不只是下载 chromium 二进制还会安装一堆系统级依赖库。GitHub Actions 的 ubuntu 镜像虽然比较全但偶尔缺一两个库就会导致启动浏览器时崩溃报错信息又不太直观。加上--with-deps之后问题少很多。第二依赖安装顺序有讲究。我先执行playwright install再执行npm ci。因为 Midscene.js 或 Playwright 相关的 postinstall 脚本可能会触发浏览器下载如果包都没装好就直接下载容易遇到找不到目标目录的问题。反过来如果先npm ci再playwright install逻辑上也通但 install 必须发生在启动脚本之前这一点顺序别搞反就行。第三timeout-minutes: 30不是随便写的。GitHub Actions 的 job 默认超时是 6 小时听着很宽松但如果脚本真的卡住你会白白等上很久才算失败。巡检任务通常是轻量的正常情况 3 分钟之内就能跑完。我设成 30 分钟既留足余量又能保证一旦挂起不会拖太久。第四Upload Reports里的if: always()不能省。默认情况下只有任务成功才会执行后续步骤。但巡检失败时恰恰是最需要报告的时候if: always()保证不管脚本退出码是什么截图和报告都会上传到 Actions 的 Artifact 区你可以随时下载查看当时的页面状态。注意GitHub Actions 对schedule任务有个限制——最短触发间隔是 5 分钟而且不保证严格准点执行特别是免费账号排队延迟几秒到几分钟都正常。巡检任务没必要追求秒级精度能容忍这个不确定性就完全没问题。3.3 结果反馈要做闭环定时任务跑完不能只是“日志里显示失败”就算完得有主动通知。这里我做了两层闭环分享出来供你参考。第一层是构建产物留档。每次跑完报告和截图上传到 Artifacts哪怕用户没看数据也沉淀在那里。后续想回溯某天的巡检情况直接按日期翻 Artifacts 就行非常方便。这一层相当于“留痕”。第二层是失败通知。我在 workflow 里加了一个专门的通知步骤照样写在同一个 YAML 文件里- name: Notify on Failure if: failure() run: | curl -s -X POST ${{ secrets.INSPECT_WEBHOOK_URL }} \ -H Content-Type: application/json \ -d {text:线上巡检失败请查看 Actions 日志确认问题。}这个 webhook 可以指向任何支持入站消息的群机器人地址。我目前用的是工作群里接的一个机器人巡检失败时会弹一条消息团队成员都能看到。设置好后我基本不需要主动去刷 Actions 页面有问题系统自己来找我。顺带一提如果有天你希望“失败后自动提一个 Issue”也可以接一个创建 Issue 的 action但要注意防止重复提跑之前先搜索是否存在未关闭的同名 Issue存在就跳过。我的经验是对个人项目来说群通知够了Issue 自动化更适合多人团队的任务管理。4. 实际运行与问题排查4.1 定时任务没触发先查这四件事接入一个月来我遇到过几次“到点了没跑”的情况排查思路基本固定在四件事上。第一先看 Actions 页面左侧有没有这个 workflow再点进去看有没有 Schedule 标签页。如果页面压根没有说明 YAML 语法有错仓库默认分支上可能也存在问题。GitHub 解析 workflow 时遇到语法错误会静默忽略不给你明显的报错提示很容易漏掉。第二确认 cron 表达式的时间基准。如果你按北京时间写30 8 * * *实际执行点却是北京时间下午 4 点那就是时区换算的锅。GitHub Actions 的 cron 严格按 UTC 处理没有时区配置项只有自己换算。第三看触发记录里对应时间点有没有“跳过”或“失败”的标记。有时候定时任务确实触发了但之前的 job 排队长实际运行时间偏移了不少。Actions 页面的 “Run history” 列能按时间筛选仔细看一眼就明白。第四检查仓库的 actions 权限设置。仓库 Settings 里的 Actions 权限如果被限制第三方 action 和部分系统行为会被阻断表现就是 job 一启动就失败。这个默认是正常的但如果你从某个模板仓库复制了一个带奇怪权限配置的 workflow就要留意。4.2 跑起来但秒失败怎么定位定时任务触发了job 也启动了但脚本几秒内就报错退出。这种“秒失败”大多集中在三个原因。最常见的是浏览器没装好。报错信息通常是类似browserType.launch: Executable doesnt exist at ...这样解决方案就是回到 workflow 里确认npx playwright install --with-deps chromium这步执行成功。有一次我发现这步没有输出“download complete”原因是 npm 缓存命中了旧包install 命令被跳过了。后来我给 install 步骤加了npx playwright install --force chromium强制重新下载问题解决。其次是 API Key 没传对。自然语言驱动依赖模型服务如果MIDSCENE_MODEL_API_KEY为空或者过期的 Key脚本会在第一次调用模型时直接抛认证错误。排查方法是把 Key 重新设置一遍然后在本地先验证 Key 的有效性再推到 GitHub Secrets。注意 Secrets 的值更新后不会立刻全局缓存可能几分钟后再跑才生效。然后是超时问题。Midscene.js 默认会等待元素出现但如果页面上某个东西一直加载不出来会卡到超时才失败。我之前在脚本里写了一个“等待结果列表出现”的步骤结果某次线上接口响应特别慢整个巡检任务跑了 20 多分钟才失败。后来我把关键等待步骤拆开每一步单独设置超时整体失败速度大幅提升定位也更容易。4.3 常见问题速查表症状可能原因解决方案定时任务完全不触发YAML 语法错误 / 分支不对检查 workflow 文件是否在默认分支尝试手动 dispatch 触发触发时间和你预期不一致UTC 与时区换算错误统一按 UTC 计算或改成需求不敏感的宽松时段job 启动后瞬间失败浏览器二进制缺失加上--force重新 install chromium脚本第一次调模型就报错API Key 无效或未传在本地验证 Key更新到 GitHub Secrets页面元素加载慢导致超时未拆分布骤超时对关键步骤设置独立 timeout 参数失败时拿不到报告上传步骤未设always()给 upload-artifact 步骤加if: always()免费额度被跑满定时频率过高降低 cron 频率去掉不必要的运行这张表我直接贴在项目 README 里了每次出问题先查一遍大多数都能快速定位。5. 经验总结与进阶建议这套“Midscene.js GitHub Actions”的定时巡检方案落地之后我最大的体会是脚本本身只占三成功劳另外七成在任务调度和结果可达性上。GitHub Actions 提供了免费的 cron 调度Midscene.js 降低了脚本维护成本但真正让你“睡得着觉”的是失败通知和报告留档这两条链路。通知能让你第一时间知道报告能让你知道当时发生了什么这两者缺一不可。最后再分享一个小技巧我后来在脚本开头加了一行执行日志记录“开始巡检 时间 目标环境”脚本结尾再打一行“巡检结束 通过/失败 总耗时”。这样每次跑完Actions 日志第一眼就能看到整体状态不用翻到最后才知道结论。配合通知机器人整个巡检体系基本做到了无人值守。如果你想在这个基础上继续扩展可以做的事还挺多的多环境轮询预发、正式环境同一套脚本、全链路场景拆分不只是一个搜索流程把注册、登录、下单都拆成独立脚本分别调度、失败自动重跑一次确认是否偶发等。把这些任务都接进同一套 workflow巡检会从“单点检查”变成一张质量防护网价值会大很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑