关于 motoko-github-ci-workflow
motoko-github-ci-workflow 是为 Motoko 项目新增或更新 GitHub Actions CI 的技能,适用于维护 Motoko 包、Canister 和示例的开发者。它会先检查项目结构与现有工具,再按需配置测试、基准测试、格式检查和构建任务。
主要功能
- 01检查 Motoko 项目类型与现有 CI
- 02按需配置测试和基准测试
- 03加入 Prettier 格式检查
- 04构建 Canister 与示例
- 05为 PocketIC 和 wasm-opt 配置工具链
适用场景
- 01为 Motoko 包添加持续集成
- 02补齐现有 CI 中缺少的测试
- 03配置基准测试运行环境
- 04在 CI 中构建 Canister 或示例
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ research-ag/ motoko-skills --skill motoko-github-ci-workflow安装后在 Claude Code 中输入/motoko-github-ci-workflow确认已加载。
详细介绍
Motoko GitHub CI 工作流
这是什么
此技能提供了一份全面的操作指南,介绍如何为 Motoko 项目添加或更新 GitHub Actions CI 工作流。内容涵盖自动化测试、基准测试、代码格式化,以及使用 mops、moc、prettier、icp-cli 和 dfx 进行构建(canister 或示例)。它与 mops-package-maintenance 技能相得益彰。
如需了解 GitHub Actions 的一般信息,请参阅 GitHub Actions 文档。如需查看 Motoko 专属示例,可以浏览 research-ag 组织中的公开代码仓库。
先决条件
mopsCLI(本地:npm i -g ic-mops,CI:caffeinelabs/setup-mops@v1)node(建议使用最新 LTS 版本,Prettier 至少需要 v22)- 用于运行
mops bench的pocket-ic。从 mops CLI v3.0.0 起,已不再支持 dfx-replica——PocketIC 是mops bench(以及副本测试和--check-deploy)唯一可用的运行时,并且必须通过mops.toml中的[toolchain] pocket-ic = "<version>"显式固定版本(例如mops toolchain use pocket-ic 15.0.0)。如果未固定版本,mops bench会直接报错。CI 会通过mops toolchain bin pocket-ic安装固定版本的二进制文件(见步骤 1)。 - 用于反映优化后(接近生产环境)的 Wasm 的基准测试所需的
wasm-opt。只有当mops.toml包含[optimize]部分时,mops bench/mops build才会运行 Binaryen 的wasm-opt——并且该部分要求像pocket-ic一样,在[toolchain] wasm-opt = "<version>"中显式固定版本(见步骤 1)。
工作原理
- 分析代码库:
- 确定它是 Motoko 包(
mops.toml中包含[package])还是 canister 项目(mops.toml中包含[canister],或存在dfx.json)。 - 检查测试:查找
test/目录和*.test.mo文件。如果都没有找到,则不要加入mops test步骤。 - 检查基准测试:查找
bench/目录和*.bench.mo文件。如果都没有找到,则不要加入mops bench步骤。
- 确定它是 Motoko 包(
- 分析现有 CI:
- 如果
.github/workflows/中已有 CI 文件,分析它们使用了哪些工具(dfx、icp-cli或都没有)。 - 如果代码库中有测试或基准测试,但 CI 中未包含它们,则将其加入。
- 如果
- 选择 Canister 构建工具:
- 如果项目是库包(没有 canister,也没有需要 canister 构建工具的示例),则完全跳过
dfx或icp-cli的安装。请注意,测试和基准测试仍需要moc(Motoko 编译器),通常由mops管理。 - 对于包含 canister 或示例的新项目,优先使用较新且更轻量的
icp-cli。 - 对于现有项目,沿用现有工具集(
dfx或icp-cli)。 - 重要:不要同时安装
dfx和icp-cli。
- 如果项目是库包(没有 canister,也没有需要 canister 构建工具的示例),则完全跳过
- 检查
mops.toml:读取现有的[toolchain]部分。与moc不同,当包包含基准测试(或副本测试 /--check-deploy)时,pocket-ic并非可选项——mops bench要求固定其版本。如果缺少[toolchain] pocket-ic,且包包含基准测试,请提醒用户并添加版本固定项(mops toolchain use pocket-ic <version>,例如15.0.0),不要等到 CI 运行失败。如果检查发现[optimize]缺失,且包包含基准测试,则添加该部分(并固定[toolchain] wasm-opt),以确保基准测试数据反映优化后的 Wasm;如果已运行mops-package-maintenance技能,通常已完成此操作。其他[toolchain]条目和files字段不要修改(参见步骤 1)。 - 创建或更新工作流文件:确定
.github/workflows/中现有的工作流文件(例如pull_request_build.yml、test.yml、ci.yml)。如果没有,则创建.github/workflows/ci.yml。确保使用并行任务以提高效率。 - 配置并行任务:
test任务:负责安装依赖、运行mops test和mops bench。fmt任务:负责检查prettier格式。
- 添加构建步骤:如果存在示例或 canister,则添加步骤,使用所选的 canister 构建工具构建它们。
实现
第 1 步 — 检查 mops.toml,必要时固定 pocket-ic/wasm-opt 版本
添加 CI 之前,先阅读 mops.toml。保持 [toolchain] moc 和其他条目不变。
基准测试必须使用 pocket-ic(mops CLI v3.0.0+):mops bench 已完全移除对 dfx-replica 的支持,PocketIC 现在是唯一的运行时,并且必须在 [toolchain] 中固定版本,否则 mops bench 会立即失败,并报错指出修复命令(mops toolchain use pocket-ic <version>)。mops test 仍然不需要 pocket-ic(它通过 Motoko 解释器或 WASI 运行);只有当软件包包含基准测试、Replica 测试或使用 --check-deploy 时,才需要固定该版本。
根据当前 [toolchain] 内容,确定要使用的 CI 策略:
- 软件包包含基准测试,且
[toolchain]中已固定pocket-ic:在 CI 中通过mops toolchain bin pocket-ic安装,并运行mops bench。确切代码片段请参阅第 2 步模板和陷阱 #2。 - 软件包包含基准测试,但
[toolchain]中没有pocket-ic:告知用户,如果没有固定版本,mops bench将在 CI 中失败,并添加固定版本(mops toolchain use pocket-ic <version>— mops 自己的错误消息会指出一个具体版本,例如15.0.0)。与files字段不同,这项配置不能置之不理;未固定版本的软件包根本无法运行mops bench。 - 没有基准测试:无需处理 —
mops test不需要pocket-ic,也不需要基准测试运行时。
**[optimize] + wasm-opt — 基准测试应反映优化后的 Wasm。**只有当 mops.toml 中存在 [optimize] 节时,mops bench 才会运行 Binaryen 的 wasm-opt(即使该节为空,默认也会使用 level = "O3");如果没有该节,基准测试会在不提示的情况下测量未优化的代码。与 pocket-ic 一样,一旦配置了 [optimize],就必须固定 [toolchain] wasm-opt 版本,否则构建会在编译前失败。
- 软件包包含基准测试,且已配置
[optimize]:在运行mops bench前,通过mops toolchain bin wasm-opt在 CI 中安装wasm-opt,并同时安装pocket-ic。 - 软件包包含基准测试,但缺少
[optimize]:按照上述固定pocket-ic版本的方式添加该节(并固定[toolchain] wasm-opt版本,例如mops toolchain use wasm-opt <version>)— 通常由mops-package-maintenance技能在维护过程中处理;如果尚未处理,也请在此添加,以免 CI 发布测量未优化代码的基准测试结果。 - 没有基准测试:无需处理。
此外,检查 mops.toml 的 [package] 下是否包含 files 字段。如果有,请勿修改。试图自行调整 files 字段可能导致意外副作用。请改为向用户报告其当前配置,并在它似乎缺少重要目录(如 examples/)或包含不受支持的扩展名(mops publish 仅允许 .mo、.did、.md 和 .toml)时提醒用户。
第 2 步 — 创建或更新 GitHub CI 工作流
检查 .github/workflows/ 中是否已有工作流文件。如果有,请更新;否则,创建 .github/workflows/ci.yml。使用最新版本的 Actions(例如 actions/checkout@v6、actions/setup-node@v6),并行执行任务。
注意:使用最新的主要版本 Actions,可确保获得最新功能和安全更新。
name: CI
on:
push:
branches: [main, master]
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
test:
name: Tests and Benchmarks
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: latest
- name: Install mops
uses: caffeinelabs/setup-mops@v1
- name: Make sure moc is installed
run: mops toolchain bin moc
- name: Show versions
run: |
mops --version
$(mops toolchain bin moc) --version
- name: Install dependencies
run: mops install
# Bench runtime: PocketIC is the only runtime `mops bench` supports
# (dfx-replica support was removed in mops CLI v3.0.0). `[toolchain]
# pocket-ic` must already be pinned in mops.toml (see Step 1) —
# `mops toolchain bin pocket-ic` downloads that pinned version.
# `mops bench` starts (and stops) PocketIC itself, so there is no
# separate "start" step. Omit both steps if the package has no
# benchmarks.
- name: Make sure pocket-ic is installed
run: mops toolchain bin pocket-ic
# Only if mops.toml has an [optimize] section (see Step 1) — it
# requires a [toolchain] wasm-opt pin, downloaded the same way.
# Omit this step if the package has no [optimize] section.
- name: Make sure wasm-opt is installed
run: mops toolchain bin wasm-opt
- name: Run tests
run: mops test # Omit this step if the package has no tests. Note: `mops test` does not use pocket-ic.
- name: Run benchmarks
run: mops bench # Omit this step if the package has no benchmarks.
fmt:
name: Formatting Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: latest
- name: Prettier Check
run: |
npm install prettier prettier-plugin-motoko --no-save
npx -y prettier --plugin prettier-plugin-motoko --check '**/*.{mo,json,md}'
第 3 步 — 处理示例和 Canister(可选)
如果仓库有作为 Canister 的示例:
在 test 任务中添加一个步骤(或新建一个任务)来构建示例。使用项目中已有的工具;对于新项目,则使用 icp-cli。如果没有需要构建的示例/Canister,请省略本节。
- name: Build examples
run: |
# Detect and build examples/example
EXAMPLES_DIR=""
if [ -d "examples" ]; then
EXAMPLES_DIR="examples"
elif [ -d "example" ]; then
EXAMPLES_DIR="example"
fi
if [ -n "$EXAMPLES_DIR" ]; then
cd "$EXAMPLES_DIR"
if [ -f "mops.toml" ]; then
mops install
fi
# Use icp if it exists, otherwise fallback to dfx if dfx.json is present
if [ -f "icp.yaml" ] && command -v icp >/dev/null; then
icp build --all
elif [ -f "dfx.json" ]; then
# Only run dfx build if dfx is already installed in this job
if command -v dfx >/dev/null; then
dfx build
fi
fi
cd ..
fi
如果仓库是一个 Canister(而非软件包):
如果项目是一个 Canister,你应构建这些 Canister,并可选择比较 .did 文件。
安装工具(任选其一):
# Option A: For new projects or those already using icp
- name: Install icp-cli
run: npm i -g icp-cli
# Option B: For existing projects already using dfx
- name: Install dfx
uses: dfinity/setup-dfx@main
构建并比较 DID:
比较 .did 文件通常需要 didc 来检查最新接口。
- name: Build canisters
run: |
if [ -f "icp.yaml" ] && command -v icp >/dev/null; then
icp build
else
dfx build
fi
- name: Get didc
run: |
mkdir -p /home/runner/bin
release=$(curl --silent "https://api.github.com/repos/dfinity/candid/releases/latest" | awk -F\" '/tag_name/ { print $4 }')
curl -fsSL https://github.com/dfinity/candid/releases/download/$release/didc-linux64 > /home/runner/bin/didc
chmod +x /home/runner/bin/didc
echo "/home/runner/bin" >> $GITHUB_PATH
- name: Check Candid interface
run: |
# Compare generated .did with tracked .did
# e.g., didc check -s did/my_canister.did .dfx/local/canisters/my_canister/my_canister.did
didc check -s did/backend.did .dfx/local/canisters/backend/backend.did
步骤 4 — 运行端到端测试
如果仓库包含端到端测试(例如位于 test/e2e 或类似目录中),请添加一个运行这些测试的步骤。这通常需要 dfx 和正在运行的本地副本或 pocket-ic。
- name: Run E2E tests
run: |
# Your E2E test command here
# e.g., npm run test:e2e
常见问题
-
Node 版本过旧。 Prettier 和某些 Motoko 工具需要较新的 Node.js 版本。在 CI 中始终使用
latest或至少v22。 -
基准测试需要固定
pocket-ic工具链版本——没有dfx后备方案。 mops CLI v3.0.0 完全移除了对 dfx-replica 的支持(mops test/mops bench上的--replica标志也已移除——自 2.14 起该标志就已弃用)。PocketIC 现在是唯一的运行时。如果包中有基准测试,请确保在mops.toml中固定了[toolchain] pocket-ic(如果缺少,请使用mops toolchain use pocket-ic <version>添加——未固定版本的项目会报错,并指出具体的修复方法),然后安装固定版本的二进制文件并运行 bench(仅在运行mops bench的任务中)。无需单独的“启动”步骤——mops bench会自行启动(并停止)PocketIC:- name: Make sure pocket-ic is installed run: mops toolchain bin pocket-ic - run: mops bench注意:
mops test不使用pocket-ic(它通过 Motoko 解释器或 WASI 运行),因此只有在运行mops bench/ 副本测试 /--check-deploy时才需要固定版本。移除所有遗留的
dfx基准测试运行时步骤。 如果你正在更新一个仍会安装dfx(dfinity/setup-dfx@main)或仅为支持mops bench而运行dfx start --background --clean的现有工作流,请删除这些步骤——它们对mops bench已不起作用,也不能替代固定pocket-ic版本:# 如果这些步骤仅用于 `mops bench`,且存在,请将其移除——mops CLI v3.0.0 # 已移除对 dfx-replica 的支持: - name: Install dfx uses: dfinity/setup-dfx@main - name: Start dfx run: dfx start --background --clean(对于其他任务,仍然可以安装
dfx,例如构建 canister/示例或运行 E2E 测试,参见步骤 3/4。此处的陷阱仅针对mops bench运行时。) -
未固定
pocket-ic版本。 如果mops bench(或mops toolchain bin pocket-ic)因工具链未固定而立即失败,说明mops.toml中缺少[toolchain] pocket-ic——请使用mops toolchain use pocket-ic <version>固定版本(错误消息会给出具体版本),不要尝试用dfx绕过问题。 -
未配置
[optimize]就运行基准测试。 只有当mops.toml中包含[optimize]部分时,mops bench才会运行wasm-opt——如果省略该部分,CI 会照常报告未优化 Wasm 的数据,而这些数据并不能反映实际发布到生产环境的版本。如果包中有基准测试但缺少[optimize],请添加该部分(并固定[toolchain] wasm-opt版本),不要把“CI 通过”当作“CI 测量结果准确”——参见步骤 1。 -
未显示版本。 务必添加显示
mops和moc版本的步骤。这有助于调试 CI 问题。 -
未使用并行作业。 在同一作业中运行格式检查和测试会更慢。请使用单独的作业,让 GitHub 并行运行它们。
-
在没有测试时包含
mops test。 务必检查包中是否有包含*.test.mo文件的test/目录。如果没有,请省略mops test步骤,以避免 CI 失败。 -
在没有基准测试时包含
mops bench。 务必检查包中是否有包含*.bench.mo文件的bench/目录。如果没有,请省略mops bench步骤。 -
安装旧版 mops。 避免使用
npm i -g ic-mops或ZenVoich/setup-mops@v1。请改用caffeinelabs/setup-mops@v1。 -
同时安装多种工具。 不要同时安装
dfx和icp-cli。只选用其中一种。 -
跳过现有测试。 如果要更新现有 CI,请确保它运行仓库中的所有测试和基准测试。
-
安装不必要的工具。 对于只运行
mops test且没有基准测试的库包,不要安装dfx或icp-cli。如果包中有基准测试,则必须使用pocket-ic(它是唯一的mops bench运行时)——仅在基准测试作业中通过mops toolchain bin pocket-ic安装它(如果设置了[optimize],还要安装wasm-opt)。不要仅为了运行mops bench而安装dfx或icp-cli。
验证是否正常运行
- 检查 GitHub Actions 选项卡。 确保
test和fmt两个作业都存在且正在运行。 - 检查日志。 确认
mops install正确拉取依赖项,且mops bench使用[toolchain] pocket-ic中固定的版本对 PocketIC 运行(从 mops CLI v3.0.0 起,--replica标志已不存在)。如果mops.toml中有[optimize]部分,还要确认运行了wasm-opt步骤——其输出应出现在基准测试结果之前。 - 检查格式检查。 故意将文件格式弄乱并提交 PR,确保
fmt作业按预期失败。