
Testany Import Git
- 8 installs
- 79 repo stars
- Updated May 6, 2026
- testany-io/testany-agent-skills
Helps with testing & qa tasks.
About
testany-import-git is a Claude Code skill for testing & qa. It helps solo builders move faster with AI-assisted development.
- testany-import-git
- Testing & QA
- AI-coding skill
Testany Import Git by the numbers
- 8 all-time installs (skills.sh)
- Ranked #1,571 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/testany-io/testany-agent-skills --skill testany-import-gitAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 79 |
| Last updated | May 6, 2026 |
| Repository | testany-io/testany-agent-skills ↗ |
What it does
Helps with testing & qa tasks.
Files
Testany Git Import
把一个 Git 仓库里的测试脚本批量注册成 Testany platform cases,并在后续以 sync / switch / relation 的方式保持与仓库同步。
用户输入: $ARGUMENTS
---
核心概念
| 对象 | 说明 |
|---|---|
| Connection | 一次 OAuth 授权后得到的 Git 平台身份(当前支持 GitHub)。GitHub 连接下有若干 installation_bindings,每个绑定代表一个 GitHub App 安装(owner/org + 可访问 repo 范围)。installation_id 是后续浏览仓库的必填入参 |
| Import History | 一次导入配置:绑定到某个 connection、某个 repo、某个 ref,决定了"这些文件 ↔ 这些 case"的映射 |
| File Binding | import history 下"一个文件 ↔ 一个 case"的绑定记录,是 Testany 上那批 case 的真源 |
| Sync Record | 一次同步动作的审计记录(per-file 成功/失败/跳过) |
import_mode(枚举值必须严格匹配)
本质差别:后端 auto-diff vs 用户手动选 —— 不是"仓库内容多少是测试"之类的场景判断。
managed_import:镜像仓库、自动 diff。confirm_git_sync触发一次 mirror→binding 的增量落地;新增文件还可以另走 addFiles,上游被删走 sourceDeleted 两阶段确认sync_link:显式管理 binding 集。current phase 下 `confirm_git_sync` 不可用,binding 集演化统一走 addFiles(新增文件)和 sourceDeleted(上游删除)两条关系流。preview_git_sync在这里仅用于查看"已绑定文件相对 last_synced_commit 的变化"
选择建议:想省心 + 信任仓库当 source-of-truth → managed_import;想精细控制每次哪些文件入 binding 集 → sync_link。不要基于"仓库是否纯测试"这类理由推断。
sync_mode(枚举值必须严格匹配)
latest:跟随tracked_branch的 HEAD,每次 sync 自动前进pinned_commit:钉在某个 commit;要前进必须走 switchCommit
---
操作速查
| 意图 | 工具链 |
|---|---|
| 看我有哪些 Git 连接 | testany_list_git_connections |
| 新建 GitHub 连接 | testany_initiate_git_oauth → 用户浏览器完成 → 轮询 list |
| Access token 快过期 / 已过期但 refresh_token 还在 | testany_refresh_git_connection_scope(server-to-server,无需浏览器) |
| Refresh_token 也挂了 / 用户主动断连 / 换账号 | testany_reauthorize_git_connection(返回 authorize_url,需用户浏览器完成) |
| 连接侧 repo 选择 / scope 变更 | testany_refresh_git_connection_scope(同入口;它既刷 token 又校对 scope) |
| 删连接 | testany_disconnect_git_connection |
| 列可访问仓库 | testany_list_git_repositories(必填 installation_id) |
| 选分支 / commit | testany_list_git_branches / testany_list_git_commits |
| 看仓库目录 / 预览文件 | testany_browse_git_tree / testany_preview_git_file |
| 列 / 查 / 删 import | testany_list_git_imports / testany_get_git_import / testany_delete_git_import |
| 新建导入 | testany_create_git_import |
| 看 / 清 bindings | testany_list_git_import_file_bindings / testany_delete_git_import_file_bindings |
| 周期同步(managed_import 专属) | testany_preview_git_sync → testany_confirm_git_sync(sync_link 在 current phase 不支持 confirm_sync;改走 addFiles/sourceDeleted) |
| 重放失败 sync(sync_link 专属) | testany_retry_git_sync({sync_record_id}) |
| 切 commit(要求 sync_mode=pinned_commit) | testany_preview_git_switch_commit → testany_confirm_git_switch_commit |
| 解除 pinned、回 latest(要求 sync_mode=pinned_commit) | testany_preview_git_switch_mode → testany_confirm_git_switch_mode |
| 审计同步历史 | testany_list_git_sync_records → testany_get_git_sync_record |
| 加新文件(managed_import 与 sync_link 均可) | testany_get_git_add_files_summary → _list_git_add_files_candidates → _confirm_git_add_files |
| 标记源删除(managed_import 与 sync_link 均可) | testany_get_git_source_deleted_summary → _list_..._candidates → _confirm_git_source_deleted |
| Webhook 读 / 开关 / 轮换 | testany_get_git_webhook_config / _update_git_webhook_config / _disable_git_webhook / _regenerate_git_webhook_secret |
---
Phase 1:连接 Git 平台
1. testany_list_git_connections,找目标平台的连接 2. 如果没有连接 → testany_initiate_git_oauth({platform: "github"}) 拿 authorize_url,交给用户浏览器完成 GitHub App 安装授权(agent 不能替点击;明确告知"完成后回来说一声"),然后轮询 testany_list_git_connections 到新连接出现 3. 如果有连接,做健康检查(详见 connection-health.md):
- 配置层:
status == "connected_scope_ready"才是健康 ⚠️ 不是字面"ready" - 凭证层:
token_expires_at必须 >now + 60s;否则先调testany_refresh_git_connection_scope做 server-to-server 刷新(不打扰用户) - 看各
installation_bindings[i].scope_verified_at是否太旧(>24h 可选刷)
4. 健康后,从 connection.installation_bindings[] 挑目标 installation_id
常见不健康状态 → 修复路径速查(完整枚举见 connection-health.md):
status / 症状 | 处理 |
|---|---|
connected_scope_ready 但 token_expires_at 过期或临近 | refresh_connection_scope(server-to-server) |
token_expired | reauthorize_git_connection → 浏览器 |
permission_changed | 先 refresh_connection_scope;失败再 reauthorize |
authorized_no_installation | 用 scope_management_entry_url 让用户浏览器装 installation |
disconnected | initiate_git_oauth 重新授权 |
关键纪律:不要只看status。GitHub App access_token 默认 8h 过期,到期时status仍然是connected_scope_ready(滞后指标),下次 GitHub-API 调用才会让后端把它翻到token_expired。主动用token_expires_at判断能避免一次多余的失败重试。
---
Phase 2:选仓库与 ref
1. testany_list_git_repositories({connection_id, installation_id, search?}) 选出 owner/repo + repo_full_name 2. 再选参照系:
sync_mode=latest→testany_list_git_branches选tracked_branchsync_mode=pinned_commit→ 还要testany_list_git_commits选出具体pinned_commit
3. 需要核对文件的话,用 testany_browse_git_tree + testany_preview_git_file 4. 选 runtime:直接调 `testany_filter_case_runtimes` 拿列表给用户选,不要先问用户测试脚本是什么语言。大多数 runtime 的 executor 是一致的,默认推荐 CloudPrime-Default(或其它 CloudPrime 系列)。用户挑完用对应 runtime_uuid。不要让用户背 UUID。 5. 调 testany_get_tenant_config 拿 deployment_type,供 Phase 3 决定 selected_files 的 visibility 默认值。Session 级缓存即可。
---
Phase 3:组装 selected_files 并创建 import
必填字段
调 testany_create_git_import 时:
| 字段 | 何时必填 |
|---|---|
connection_id | 总是 |
installation_id | 总是 |
import_mode | 总是(managed_import 或 sync_link) |
sync_mode | 总是(latest 或 pinned_commit) |
tracked_branch | 总是(即使 pinned_commit 模式,也要带分支上下文) |
pinned_commit | 仅 sync_mode=pinned_commit |
runtime_uuid | 总是 |
repo_full_name | 总是,形如 owner/repo |
selected_files | 两种 import_mode 都必填,作为 binding 集的首轮种子 |
root_path | 必填,取仓库根时传 "/";取子目录传相对路径 |
workspace_key | 可选。作为兼容性默认:当 per-file 没给 workspace_keys 且该 case 是 restricted 时,用这个兜底。不要当成必填让用户从几十个 workspace 里挑 |
selected_files 怎么组
对每个要纳入的文件给一个 FileSelectionInput:
file_path:相对仓库根(不是相对root_path)name:case 名,不给就用文件名executor:testany_browse_git_tree返回的entry.executor是后端推荐值,直接用trigger_method:多 config 的 executor(playwright / maven / gradle 等)必填;结构见testany://schema/import-gitcase_labels:先testany_list_labels确认存在(不存在先testany_create_label)visibility/is_private:见下方「Case 可见性策略」
Case 可见性策略
visibility / is_private / workspace_keys 规则受租户 deployment_type 约束。完整规则、错误码、获取方式见 [case-visibility-policy.md](../testany-guide/references/case-visibility-policy.md)。
Import 路径特有的点:
1. Phase 2 已经调过 testany_get_tenant_config;这里直接用缓存的 deployment_type 决定 per-file 的 visibility 默认值 2. 默认策略:
deployment_type=1→ per-filevisibility: "global",不需要workspace_keys,也不需要顶层workspace_keydeployment_type=2→ per-filevisibility: "restricted"+workspace_keys: ["<key>"](或用顶层workspace_key兜底),因为 global 不被允许
3. 顶层 `workspace_key` 是 per-file 没给 `workspace_keys` 时的兜底;type=1 走 global 的情况下完全不用传 4. 用户在 type=2 下硬要 global:向用户解释限制,降级到 restricted;不要盲目重试
后端当前把 private / workspace 也视为 restricted 的兼容别名,但对 agent 文案和构造 payload,统一使用 global | restricted 更稳。
导入后:提示凭证类变量改用 secrets 声明
Import 会把脚本文件上传成 case,但不负责为 case 填 environment_variables(那是 testany-case / testany-sync-case-env-from-source 的职责)。
导入完成后,如果脚本里有这些命名模式,主动提示用户在 case_meta.environment_variables 中把它们声明为 type: secrets + secret_ref,而不是明文 type: env:
*_PASSWORD/*_PWD*_TOKEN/*_APIKEY/*_API_KEY*_SECRET/*_KEY
用户确认 secret_ref 的三个字段(workspace_key / credential_safe_key / credential_key)后,脚本里就可以直接读同名环境变量拿到凭证值。
---
Phase 4:周期性同步
current-phase 约束:testany_confirm_git_sync当前只对managed_import生效;sync_link的 binding 演化统一走 addFiles / sourceDeleted。
idempotency_key 由 MCP 自动生成,不要手传 UUID——除非你明确要跨多次调用做去重。managed_import
直接 confirm,后端自动对比镜像:
testany_confirm_git_sync({ import_history_id })如用户想先看 diff,可先 testany_preview_git_sync。
sync_link
不走 confirm_git_sync。要演化 binding 集:
| 意图 | 工具链 |
|---|---|
| 仓库里多出文件、要新增 case | Phase 6 addFiles 流 |
| 仓库里删掉文件、要下线 binding | Phase 6 sourceDeleted 流 |
| 重放已有 sync record 里的失败项 | testany_retry_git_sync({sync_record_id}) |
testany_preview_git_sync 在 sync_link 上仍可调,作为"当前绑定文件相对 last_synced_commit 的变化"审计——但它不会把仓库里尚未选入的新文件作为候选,新文件由 addFiles 候选列表负责。
失败处理
SyncResult.failed_items 非空 → testany_retry_git_sync({sync_record_id})。注意:retry 仅 sync_link 可用,managed_import 的 confirm 内部已自行处理 per-file 失败。
---
Phase 5:Switch(只在 sync_mode 维度上操作)
switch 工具不改 `import_mode`(managed_import ↔ sync_link);只操作 sync_mode。 都是 preview → confirm 两阶段,建议总是先 preview 把 diff 给用户看。
switchCommit — 换钉住的 commit
- 前置条件:`sync_mode=pinned_commit`(managed_import 和 sync_link 都可以)
testany_preview_git_switch_commit({target_commit})→_confirm_git_switch_commit({target_commit, file_selections?})- sync_link 的 confirm 从 preview.changes 派生
file_selections;managed_import 的 confirm 不接受file_selections(会 400) - 前置条件不满足(例如当前已是 latest)返回
ERR_SWITCH_NOT_ALLOWED
switchMode — 解除 pinned,回到跟随 branch HEAD
- 语义:
sync_mode: pinned_commit → latest。不是 managed_import ↔ sync_link 之间切换 - 前置条件:
sync_mode=pinned_commit - 已经是
latest:返回ERR_SWITCH_MODE_UNCHANGED - 非 pinned:返回
ERR_SWITCH_NOT_ALLOWED - Payload:
- managed_import:
_confirm_git_switch_mode({})(带 file_selections 会被后端拒绝) - sync_link:
_confirm_git_switch_mode({file_selections?})(可选,从 preview 派生)
---
Phase 6:关系演化(managed_import 和 sync_link 都支持)
这是 sync_link 在 current phase 下唯一的 binding 集演化路径(managed_import 的 addFiles 只是和 confirm_git_sync 并列的可选手段)。
新增文件(addFiles)
仓库里多出的、Testany 上还没有 binding 的文件:
1. testany_get_git_add_files_summary — available=false 就不能走,看 blocked_reason 2. testany_list_git_add_files_candidates — 拿到 snapshot_commit 和候选列表 3. testany_confirm_git_add_files({selected_files, snapshot_commit}) — idempotency_key 由 MCP 自动生成
- `snapshot_commit` 必须原样回传 list 时拿到的值;值对不上时后端会拒绝,防止竞态下把过期候选集落地
源文件被删(sourceDeleted)
仓库里已经删掉、但 Testany 上 binding 还在的:summary → list → confirm 同一套,但 confirm 的参数是 file_binding_ids(不是 file_path)。
---
Phase 7:Sync 审计
testany_list_git_sync_records({import_history_id, page?, per_page?})testany_get_git_sync_record({record_id, detail_page?, detail_per_page?})翻 per-file 结果
---
Phase 8:Webhook
| 动作 | 工具 |
|---|---|
| 查看配置 | testany_get_git_webhook_config |
| 启用 / 改 track_scope | testany_update_git_webhook_config({webhook_enabled: true, track_scope?}) |
| 关闭 | testany_disable_git_webhook |
| 轮换 secret | testany_regenerate_git_webhook_secret |
关键纪律:webhook_secret 只有首次启用或刚 regenerate 的响应里是明文,之后读全是 masked。一旦拿到明文:
1. 立即让用户在 Git 平台 hook 配置里填这个 secret 2. 同时把 platform_setup_guide.steps 完整交给用户 3. 让用户触发一次测试事件,观察 webhook_status 从 pending_verification → verified
---
必须提醒用户的事
1. OAuth 要浏览器完成:agent 不能替用户点授权;返回 authorize_url 后要明确告知并承担轮询状态。initiate_git_oauth 的 frontend_return_uri 必填(绝对 URL),由后端强校验 2. webhook_secret 只出现一次:首次启用或 regenerate 后必须立刻记下并同步到 Git 侧,否则只能再轮换 3. addFiles / sourceDeleted 依赖 `snapshot_commit`(managed_import 和 sync_link 都是如此):list 阶段拿到的值原样回传到 confirm 4. sync_link 在 current phase 下不走 `confirm_git_sync`:后端 422。要演化 binding 集就走 addFiles / sourceDeleted 5. 删除连接不会级联删 import history:那些 import history 会变 not_ready,需重新授权 6. 大批量改动先 preview:任何 sync / switch / relation 的 confirm 之前,把 diff / 候选集给用户确认 7. 不要让用户挑 workspace 除非必要:顶层 workspace_key 是可选兼容字段,不是 UX 必选项。只有当用户明确要建 restricted case,且 per-file 没给 workspace_keys 时才问 8. 不要预设 `deployment_type`:在允许 global case 前,用 tenantClient.getCreditStatus 或向用户确认环境类型;deployment_type=2 环境下 global 会被后端直接拒(E400001) 9. Runtime 选择不要盘问语言:直接列 testany_filter_case_runtimes 结果让用户选,默认推荐 CloudPrime-Default 10. 不要手传 `idempotency_key`:MCP 已在 preview_sync / confirm_sync / retry_sync / confirm_add_files / confirm_source_deleted 内部自动生成 UUID,传旧风格的 UUID 只会让上层多一步无谓代码。仅在明确要跨多次调用去重时才显式传一个合法 UUID 11. `installation_id` 必须从 connection 里挑:connection.installation_bindings[*].installation_id,且对应 binding 的 account_login 要和 repo_full_name 的 owner 一致,否则 400
---
常见疑问
| 用户问题 | 处理方式 |
|---|---|
| "为什么没同步到新文件?" | testany_get_git_import 看 remote_latest_commit vs last_synced_commit;managed 模式再看 addFiles summary |
| "能回滚一次 sync 吗?" | V2 不支持回滚;最接近的是用 switchCommit 回到旧 commit |
| "这些 case 是哪次导入建的?" | testany_list_git_import_file_bindings 里的 test_case_key 反查 |
| "webhook 收不到事件" | 看 webhook_status(pending_verification / error)+ platform_setup_guide |
| "连接变 not_ready 了" | 读 not_ready_reason,用 reauthorize 或 refreshScope 恢复 |
---
参考
- 详细 payload 约定与错误码
- MCP schema resource:
testany://schema/import-git(枚举、类型、流程图、工具归类) - Testany 自动化对象模型
Git Connection 健康检查与修复
Testany Git 连接的健康状态由两条独立维度决定:
1. 配置层:status(Testany 内部的 OAuth / scope 状态机) 2. 凭证层:token_expires_at(GitHub 侧颁发的 access_token 有效期)
必须两条都检查。只看 status 是滞后指标 —— access_token 单纯到期时 status 仍显示 connected_scope_ready,直到下一次实际用 token 的 API 失败后才翻成 token_expired。
---
1. ConnectionView 响应字段
顶层字段:
| 字段 | 说明 |
|---|---|
id | 连接 uuid |
user_id | Testany 操作者 |
platform | github(Phase 1 只支持 GitHub) |
platform_user_id / platform_username / platform_avatar_url | GitHub 侧身份信息 |
status | 配置层健康状态,枚举见 §2 |
not_ready_reason | status 非 ready 时的可读原因(可为 null) |
scope_management_entry_url | 平台侧 scope 管理入口(通常是 GitHub App installations 页面) |
installation_bindings[] | GitHub App installation 列表,见 §3 |
token_expires_at | 凭证层 —— access_token 过期时刻(ISO-8601) |
created_at / updated_at | 创建 / 最近更新时间 |
---
2. status 枚举
| 值 | 含义 | 对应修复路径 |
|---|---|---|
connected_scope_ready | ✅ 一切就绪(健康值) | — |
authorized_no_installation | OAuth 完成但还没装 GitHub App installation | 用户去 scope_management_entry_url 浏览器装 installation |
token_expired | Token refresh 失败,access_token 无法续期 | testany_reauthorize_git_connection(浏览器) |
permission_changed | GitHub 侧 scope / repo 选择被改 / 撤销 | 优先 refresh_connection_scope;若不行再 reauthorize |
disconnected | 用户主动断开 | testany_initiate_git_oauth 重新授权 |
⚠️ 注意:健康值是 `connected_scope_ready`,不是字面 `"ready"`。代码里对字符串做相等比较时必须用完整值。
---
3. installation_bindings[] 每项字段
| 字段 | 说明 |
|---|---|
installation_id | GitHub App installation 的数字 id,浏览仓库时必填 |
account_login / account_type | 账号名 / User or Organization |
account_avatar_url | 头像 URL |
repository_selection | all 或 selected(GitHub App 安装时的 repo 选择模式) |
selected_repository_count / effective_repository_count | 选择数量 / 实际可达数量 |
installation_html_url | GitHub 侧 installation 管理页 |
scope_verified_at | 最近一次 Testany 与 GitHub 校对 scope 的时间 |
---
4. 健康检查决策树
每次要访问需要实际调用 GitHub API 的 tool 前(repo browse / sync / addFiles 等),先做三项检查:
┌─ token_expires_at 是否存在且 > now + 60s?
│ ├─ 否 ─→ 调 refresh_connection_scope 续命(server-to-server)
│ │ ├─ 成功 ─→ 继续
│ │ └─ 失败(status 翻到 token_expired)─→ 调 reauthorize_git_connection 给用户浏览器 URL
│ └─ 是 ─→ 下一步
│
├─ status 是否 == "connected_scope_ready"?
│ ├─ authorized_no_installation ─→ 把 scope_management_entry_url 交给用户浏览器装 installation
│ ├─ token_expired ─→ reauthorize_git_connection
│ ├─ permission_changed ─→ refresh_connection_scope(优先),失败则 reauthorize
│ ├─ disconnected ─→ initiate_git_oauth 重新授权
│ └─ connected_scope_ready ─→ 继续
│
└─ 准备使用的 installation 的 scope_verified_at 是否太旧(>24h)?
├─ 是 ─→ 可选:refresh_connection_scope 刷新校对
└─ 否 ─→ 继续60 秒缓冲是为了避免"调用发出时还没过期、到达后端时过期"的边界窗口。
---
5. reauthorize vs refresh_connection_scope 的分工
这是 skill 里最容易搞混的一对。行为本质不同:
| Tool | 流程 | 是否需要浏览器 | 典型触发场景 |
|---|---|---|---|
testany_reauthorize_git_connection | 生成新的 authorize_url,响应里只返回 URL | 需要 —— agent 必须把 URL 交给用户 | status=token_expired;refresh_token 也挂了;用户主动断开后重建 |
testany_refresh_git_connection_scope | 用 refresh_token 向 GitHub 换新 access_token + 重新校对 installation scope | 不需要 —— server-to-server,agent 调完即可直接继续 | token_expires_at 临近过期;status=permission_changed;周期性健康检查 |
推论:
- Token 单纯到期时(refresh_token 还在),
refresh_connection_scope是优先选择 —— 不打扰用户 - 只有 refresh 本身失败(
refresh_token挂了 / scope 撤销太严重 / disconnected),才需要走reauthorize让用户浏览器重新授权
---
6. GitHub App token 生命周期参考
- Access token:默认 8 小时 过期
- Refresh token:6 个月 过期
只要在 refresh token 有效期内至少触发一次 refresh_connection_scope(或任何间接触发 server-to-server refresh 的操作),这条连接可以长期免浏览器维护。
Git Import 细节补充
本文档列出 MCP 工具 payload 的具体字段、枚举值、错误码。与 SKILL.md 配合使用。
---
枚举值速查
import_mode
| 值 | 含义 | 创建时 selected_files | confirm_git_sync | addFiles / sourceDeleted |
|---|---|---|---|---|
managed_import | 镜像仓库并自动维护 binding 集 | 必填(首轮 seed) | 可用(file_selections 会被忽略,由 mirror diff 决定变更) | 可用 |
sync_link | 显式管理 binding 集 | 必填(首轮 seed) | current phase 不支持;演化走 addFiles / sourceDeleted | 可用 |
current-phase 约束:testany_confirm_git_sync仅 managed_import 生效。sync_link 要加新文件 / 标记源删除,走testany_confirm_git_add_files/testany_confirm_git_source_deleted。
sync_mode
| 值 | 含义 |
|---|---|
latest | tracked_branch 的 HEAD 就是 to_commit,每次 sync 自动前进 |
pinned_commit | 钉在 pinned_commit;想前进只能走 switchCommit |
两种 sync_mode 下创建都要带 tracked_branch;pinned_commit 模式额外必填 pinned_commit。
平台支持
当前只能对 GitHub 仓库进行浏览与导入。非 GitHub 的 connection 调用 testany_list_git_repositories / _branches / _commits / _tree / _blob 会返回 ERR_REPO_UNREACHABLE。
---
FileSelectionInput 字段
用于 create_git_import.selected_files、confirm_git_add_files.selected_files、confirm_git_switch_commit.file_selections(sync_link 时)、confirm_git_switch_mode.file_selections(sync_link 时)。confirm_git_sync 在 current phase 下仅 managed_import 可用且忽略 file_selections。
{
"file_path": "e2e/login.spec.ts", // 必填,相对仓库根(不是 root_path)
"name": "Login smoke", // 可选,不给就取文件名
"executor": "playwright", // 可选;browse_git_tree 的 entry.executor 是后端推荐值
"trigger_method": { // playwright / maven / gradle 等多 config 执行器必填
"executor": "playwright",
"trigger_path": "e2e/login.spec.ts",
"playwright_config_path": "playwright.config.ts",
"trigger_command": ["npx", "playwright", "test"]
},
"case_labels": ["smoke", "auth"], // 标签必须已存在(testany_list_labels / _create_label)
"visibility": "restricted", // 首选: "restricted" | "global";后端兼容 "private"
"description": "...",
"environments": ["staging"],
"environment_variables": [
{ "key": "BASE_URL", "value": "https://..." }
],
"is_private": true, // 和 visibility 二选一(设一个即可)
"workspace_keys": ["Y2K"] // visibility=restricted 时必填
}---
Sync 流程按 import_mode 分叉
managed_import
1. testany_preview_git_sync({import_history_id}) (可选)— 返回 no_changes + diff。 2. testany_confirm_git_sync({import_history_id}) — 由 mirror diff 决定变更,file_selections 会被忽略。 3. 失败项:testany_retry_git_sync 仅对 sync_link 生效(managed 的 confirm 已在内部做 per-file 容错)。
sync_link(不走 confirm_git_sync)
testany_confirm_git_sync 对 sync_link 会失败,current phase 下不支持。要在 sync_link 上演化 binding:
| 意图 | 工具链 |
|---|---|
| 仓库里多出文件、要新增 case | testany_list_git_add_files_candidates → testany_confirm_git_add_files({snapshot_commit, selected_files}) |
| 仓库里删掉文件、要下线 binding | testany_list_git_source_deleted_candidates → testany_confirm_git_source_deleted({snapshot_commit, file_binding_ids}) |
| 重放失败的 sync record | testany_retry_git_sync({sync_record_id}) |
testany_preview_git_sync 在 sync_link 上仍可用来审计当前已绑定文件相对 last_synced_commit 的变化(不会冒出未选入的新文件)。
idempotency_key
MCP 已在 preview_git_sync / confirm_git_sync / retry_git_sync / confirm_git_add_files / confirm_git_source_deleted 内部自动生成 UUID。不要再让用户传——除非上层想跨多次调用去重(例如自己做幂等重试),此时显式传一个合法 UUID 即可。
---
Switch 流程细节
注意:switch 工具不改 import_mode(managed_import ↔ sync_link)——只在 sync_mode 维度上操作。
switchCommit — 换钉住的 commit
- 前置条件:`sync_mode=pinned_commit`(
import_mode可以是 managed_import 或 sync_link) - Payload:
{ target_commit: "<sha>" }(preview)/{ target_commit, file_selections? }(confirm) - 对 sync_link,
file_selections从 preview.changes 派生;对 managed_import,不要传(会被拒绝) - 前置条件不满足返回
ERR_SWITCH_NOT_ALLOWED
switchMode — 解除 pinned,回到跟随 branch HEAD
- 语义:把
sync_mode从pinned_commit→latest。不是 managed_import ↔ sync_link 的切换。 - 前置条件:
sync_mode=pinned_commit;已经是latest的会拿到ERR_SWITCH_MODE_UNCHANGED - Payload:
- managed_import:
{}(confirm 不要传file_selections,会被拒绝) - sync_link:confirm 可选地传
file_selections(从 preview 派生) - 非法调用返回
ERR_SWITCH_NOT_ALLOWED
---
Relation(managed_import 和 sync_link 都支持)
addFiles
1. testany_get_git_add_files_summary
available=false→ 看blocked_reason,常见:镜像未 ready / 状态机不允许
2. testany_list_git_add_files_candidates(page, per_page, keyword?)
- 返回
{ snapshot_commit, items: [...] }
3. testany_confirm_git_add_files({ snapshot_commit, selected_files }) — idempotency_key 由 MCP 自动生成
- `snapshot_commit` 必须原样回传 list 时拿到的值(防止 list 和 confirm 之间仓库又变了)
sourceDeleted
同构于 addFiles,区别在候选单位是 file_binding_id(不是 file_path)。confirm 的参数叫 file_binding_ids。
这条关系流是 sync_link 在 current phase 下唯一的 binding 演化路径(因为 confirm_git_sync 暂不支持 sync_link)。
---
Webhook 字段
update payload
{ "webhook_enabled": true, "track_scope": { "...platform-specific..." } }WebhookConfigView 返回字段
webhook_enabled:是否启用webhook_url:给用户在 Git 平台填的目标地址webhook_secret:仅 首次启用 / regenerate 后这一次是明文;之后一律 maskedwebhook_status:not_configured/pending_verification/verified/errorwebhook_last_received_at:最近一次收到事件的时间platform_setup_guide.steps:按平台生成的一次性配置步骤清单,整段交给用户执行
轮换纪律
1. 调 testany_regenerate_git_webhook_secret → 拿到 NEW webhook_secret 2. 让用户立刻在 Git 侧 hook 设置里替换 secret 3. 让用户发一次测试事件,验证 webhook_status=verified
漏掉第 2 步 = 旧 secret 失效、Git 平台那边的推送会全部被 Testany 拒收,且 secret 再也拿不回来 —— 只能再 regenerate。
---
错误码速查
后端错误码来自 ImportGitErrorCodes(ERR_*)。常见分类:
补充:visibility 的规范写法建议用 global / restricted。后端仍兼容 private / workspace 作为 restricted 别名。
| 场景 | 典型 code | 含义 / 建议 |
|---|---|---|
| 连接不可用 | ERR_CONNECTION_* | 连接 not_ready:重授权 / refresh scope |
| 仓库无法访问 | ERR_REPO_UNREACHABLE | 非 GitHub 平台 / installation 权限不足 |
| Payload 不合法 | ERR_PAYLOAD_INVALID | 必填字段缺失、格式错(例如 pinned 模式没给 pinned_commit) |
| 不允许 switch | ERR_SWITCH_NOT_ALLOWED | 组合被状态机拦截(见 switchCommit / switchMode 前置条件) |
| 并发冲突 | 去重 / 占用相关码 | 有同步在进行中;用同一个 idempotency_key 重试或等前序完成 |
遇到错误时优先按 ApiResult.error.code 分类反馈,不要只复读 message。