implement-prepare-environment-script
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplement Prepare Environment Script
实现Prepare Environment脚本
This skill produces a single executable script that performs the one-time per-build setup that precedes a conformance-test run, following a consistent, language-agnostic pattern.
The reference implementation is assets/prepare_environment_java.sh and assets/prepare_environment_python.sh. Read it first — every script you produce must be a faithful translation of that pattern into the target language's tooling and the user's shell environment.
本方案会生成一个可执行脚本,用于在一致性测试运行前执行单次构建前置准备,遵循一套与语言无关的统一模式。
参考实现为assets/prepare_environment_java.sh和assets/prepare_environment_python.sh。请先阅读这些脚本——你生成的每一个脚本都必须将该模式忠实地转换为目标语言的工具链以及用户的Shell环境。
What a prepare-environment script is for
Prepare Environment脚本的用途
prepare_environment_<lang>run_conformance_tests_<lang>- Stage the build into a working folder (the same one the conformance script will use).
- Pre-warm the dependency cache / build artifacts so the conformance script can start cold without re-downloading or re-compiling anything.
When this script exists for a project, the corresponding conformance script's own dependency-install phase degrades to "activate only" (see → "Skipping setup when exists"). When it doesn't exist, the conformance script does the setup inline.
implement-conformance-testing-script/SKILL.mdprepare_environmentprepare_environment_<lang>run_conformance_tests_<lang>- 将构建内容暂存到工作目录(与一致性脚本将使用的目录相同)。
- 预加载依赖缓存/构建产物,这样一致性脚本可以直接启动,无需重新下载或重新编译任何内容。
当项目中存在该脚本时,对应的一致性脚本自身的依赖安装阶段会降级为“仅激活”模式(详见 → "当存在时跳过设置")。如果不存在该脚本,一致性脚本会在内部完成所有设置。
implement-conformance-testing-script/SKILL.mdprepare_environmentWhy this script exists at all (the structural reason)
该脚本存在的核心原因
The conformance test runner is invoked once per functional spec by the renderer — not once per render. Each functional spec in a module has its own folder, and after the renderer finishes generating code for a new spec, it runs the conformance tests of every previous spec in the same module to detect regressions. For a module with N functional specs, the conformance script is invoked roughly N times on every render.
conformance_tests/<module>/<spec>/Without a prepare script, every one of those N invocations does the full dependency install (Python venv + , full Maven dependency tree, , , ...) from scratch. That cost — paid N times per render — dominates the wall-clock time of rendering a non-trivial project.
pip installnpm cicargo buildprepare_environment_<lang>- Prepare runs once, installs everything, populates the project-local isolation location inside (
.tmp/<lang>_<arg>/,./.venv,./node_modules,./.m2,./.gocache,./.cargo)../.pub-cache - The conformance script then runs N times, each invocation in its activate-only variant, attaching to the already-populated working folder and skipping the install step entirely.
- Net effect: install cost goes from to
N × install-cost.1 × install-cost + N × cheap-attach-cost
This is the whole reason the prepare-then-conformance split exists. If a project has so few functional specs that the install overhead is negligible, generating a prepare script is wasted effort — the install-inline variant of the conformance script is fine. If a project has many specs (or expensive dependencies, GPU builds, browser binaries, etc.), prepare is mandatory in practice. The user decides per project; this skill is the tool to execute that decision.
一致性测试运行器会被渲染器针对每个功能规范调用一次,而非针对每次渲染调用一次。模块中的每个功能规范都有自己的目录,当渲染器完成新规范的代码生成后,它会运行同一模块中所有之前的规范的一致性测试,以检测回归问题。对于一个包含N个功能规范的模块,每次渲染时一致性脚本会被调用大约N次。
conformance_tests/<module>/<spec>/如果没有prepare脚本,这N次调用中的每一次都会从头开始完成完整的依赖安装(Python虚拟环境+、完整的Maven依赖树、、等)。这种成本——每次渲染支付N次——在非 trivial 项目的渲染耗时中占主导地位。
pip installnpm cicargo buildprepare_environment_<lang>- Prepare脚本运行一次,安装所有依赖,将项目本地隔离位置填充到(如
.tmp/<lang>_<arg>/、./.venv、./node_modules、./.m2、./.gocache、./.cargo)。./.pub-cache - 随后一致性脚本运行N次,每次调用都采用仅激活变体,连接到已填充的工作目录并完全跳过安装步骤。
- 最终效果:安装成本从变为
N × 安装成本。1 × 安装成本 + N × 低成本连接成本
这就是prepare与一致性脚本拆分存在的全部原因。如果项目的功能规范极少,安装开销可以忽略不计,那么生成prepare脚本就是浪费精力——一致性脚本的内联安装变体就足够了。如果项目有很多规范(或者依赖安装成本很高,比如GPU构建、浏览器二进制文件等),那么prepare脚本在实践中是必不可少的。用户可根据项目情况决定是否使用,本方案就是执行该决策的工具。
prepare_environment
is conformance-only — NOT for unit tests
prepare_environmentprepare_environment
仅服务于一致性测试——与单元测试无关
prepare_environmentCommon and costly mistake: assumingis a generic "warm up the environment for all the testing scripts" step that the unit-test runner also depends on. It is not.prepare_environment_<lang>
prepare_environment_<lang>run_conformance_tests_<lang>run_unittests_<lang>- The unit-test runner is fully self-contained. It does its own staging into its own working folder, and it installs its own dependencies inline (
.tmp/<lang>_<arg>/,pip install -r requirements.txt,npm ci,mvn, ...) every run.cargo fetch - The unit-test runner never reads from the working folder populates. The two scripts use independent working folders even when they happen to share a
prepare_environmentnaming convention — each script wipes and rebuilds its own copy..tmp/<lang>_<arg>/ - The unit-test runner does not require to have run first. Users and CI systems routinely run unit tests as a smoke check without ever invoking conformance, and that must keep working.
prepare_environment - There is no activate-only variant of the unit-test runner. emits a self-contained script every time — the two-variant pattern is exclusive to the conformance runner.
implement-unit-testing-script
When authoring , scope it strictly to what the conformance script needs. Do not bake in dependency installs the unit-test runner needs but conformance doesn't; do not stage files the unit-test runner reads; do not assume the unit-test runner will be the one consuming what you produce. If you find yourself reaching for those, stop — the right answer is to leave alone and let the unit-test runner handle its own dependencies inline.
prepare_environment_<lang>prepare_environment常见且代价高昂的错误: 认为是一个通用的“为所有测试脚本预热环境”的步骤,单元测试运行器也依赖它。事实并非如此。prepare_environment_<lang>
prepare_environment_<lang>run_conformance_tests_<lang>run_unittests_<lang>- 单元测试运行器是完全独立的。它会将内容暂存到自己的工作目录,并且每次运行都会在内部安装自己的依赖(
.tmp/<lang>_<arg>/、pip install -r requirements.txt、npm ci、mvn等)。cargo fetch - 单元测试运行器从不读取填充的工作目录。即使两个脚本碰巧共享
prepare_environment命名约定,它们也使用独立的工作目录——每个脚本都会清空并重建自己的副本。.tmp/<lang>_<arg>/ - 单元测试运行器不需要先运行。用户和CI系统通常会在不调用一致性测试的情况下运行单元测试作为冒烟检查,这种场景必须能够正常工作。
prepare_environment - 单元测试运行器没有仅激活变体。每次都会生成一个独立的脚本——双变体模式仅适用于一致性运行器。
implement-unit-testing-script
编写时,严格将其范围限定为一致性脚本所需的内容。不要添加单元测试运行器需要但一致性测试不需要的依赖安装;不要暂存单元测试运行器读取的文件;不要假设单元测试运行器会使用你生成的内容。如果你发现自己正在做这些事情,请立即停止——正确的做法是让保持原样,让单元测试运行器自行处理其依赖。
prepare_environment_<lang>prepare_environmentHow prepare-environment scripts differ from the others
Prepare Environment脚本与其他脚本的区别
This script shares most of its structure with its two siblings ( and ) but with these differences:
run_unittests_<lang>run_conformance_tests_<lang>- One positional argument: . No conformance tests folder — that's the conformance script's input, not this one's.
<build_folder> - No test execution step. This script only stages and installs/builds. It never runs unit tests or conformance tests.
- No "no tests discovered" guard. Same reason — no tests are run here.
- Side effects must be visible to the conformance script. Anything this script does (working-folder name, dependency isolation location, installed artifacts) must match exactly what the conformance script expects to find. See Coordination contract.
Everything else — toolchain check, build staging, dependency isolation, exit codes — is the same.
该脚本与它的两个兄弟脚本(和)共享大部分结构,但存在以下差异:
run_unittests_<lang>run_conformance_tests_<lang>- 一个位置参数:。不需要一致性测试目录——这是一致性脚本的输入,而非本脚本的输入。
<build_folder> - 无测试执行步骤。本脚本仅执行暂存和安装/构建操作,从不运行单元测试或一致性测试。
- 无“未发现测试”检查。原因同上——本脚本不运行任何测试。
- 副作用必须对一致性脚本可见。本脚本执行的任何操作(工作目录名称、依赖隔离位置、已安装产物)必须与一致性脚本期望找到的内容完全匹配。详见协调约定。
其他所有部分——工具链检查、构建暂存、依赖隔离、退出码——都是相同的。
Pick the Shell First
先选择Shell类型
Before writing anything, decide which shell flavor the script must target — it depends on the host machine running this skill, not on the language. Detect the host OS proactively; do not default to Bash.
| Host OS | Emit | How to detect |
|---|---|---|
| macOS | | |
| Linux (incl. WSL, CI runners) | | |
| Native Windows (PowerShell, cmd) | | |
Run if unsure — don't ask the user before checking.
uname -s 2>/dev/null || echo "$OS"If the project will be used on both macOS/Linux and Windows by different team members or CI runners, generate both and versions of this script — they're mechanical translations of each other, share exit codes and isolation paths, and the orchestrator (or the user's CI) picks the right one at runtime. The corresponding script must be produced in matching pairs too.
.sh.ps1run_conformance_tests_<lang>If you genuinely can't tell (e.g. running in a sandbox with no shell access), ask the user — but only after the detection above failed.
The same pattern applies to both shell flavors. Only the syntax changes.
在编写任何内容之前,先确定脚本必须针对的Shell类型——这取决于运行本方案的主机机器,而非目标语言。主动检测主机操作系统;不要默认使用Bash。
| 主机操作系统 | 生成脚本类型 | 检测方式 |
|---|---|---|
| macOS | | |
| Linux(包括WSL、CI运行器) | | |
| 原生Windows(PowerShell、cmd) | | |
如果不确定,运行——在检查之前不要询问用户。
uname -s 2>/dev/null || echo "$OS"如果项目将被不同团队成员或CI运行器同时在macOS/Linux和Windows上使用,请生成该脚本的**.sh和.ps1两个版本**——它们是彼此的机械翻译,共享退出码和隔离路径,编排器(或用户的CI)会在运行时选择正确的版本。对应的脚本也必须生成匹配的成对版本。
run_conformance_tests_<lang>如果你确实无法检测到(例如在没有Shell访问权限的沙箱中运行),请询问用户——但仅在上述检测失败后。
两种Shell类型遵循相同的模式,仅语法不同。
The Pattern
脚本模式
Every prepare-environment script must implement these steps in this order:
- Toolchain check. Verify that the required language runtime / build tool (and the required version, if any) is installed. If not, print an error and exit with code .
69 - Argument validation. Require one positional argument: . If missing, print usage and exit with code
<build_folder>.69 - Working directory setup. Define a working folder at — identical to the path the conformance script will use. Wipe it (
.tmp/<lang>_<arg>/rm -rf) and recreate it. This folder — and only this folder — is where every subsequent write must land.Remove-Item -Recurse -Force - Copy the build. Recursively copy everything from (
<build_folder>) into the working folder. After this step the source folder ($1) is treated as read-only for the rest of the script.$1 - Enter the working directory. /
cdintoSet-Location. If that fails, exit with code.tmp/<lang>_<arg>. All remaining steps run from inside the working folder; they must never write back to69.$1 - Install dependencies / pre-build artifacts into an isolated environment inside . Set up a per-working-folder dependency location (a Python venv at
.tmp/<lang>_<arg>, a local./.venv, a project-scoped Maven repo at./node_modules, etc.) and install/resolve all dependencies into it. Where the language requires building before tests can run (Java, Rust, Go), also produce the build artifact and place it where the conformance script can find it — inside the working folder, never inside./.m2and never in the user's home directory. Never install into the source build folder ($1), the user's global cache ($1, system-wide~/.m2,pip,~/.cargo, ...), or anywhere outside~/.npm. If any sub-step fails, exit with code.tmp/<lang>_<arg>(do not propagate Maven/pip/npm exit codes — a half-prepared environment is itself an unrecoverable error). See Dependency isolation for per-language specifics.69
That's it. There is no step 7. Once dependencies are installed and (where applicable) the build artifact is in the local repo, this script's job is done.
每个prepare-environment脚本必须按以下顺序实现这些步骤:
- 工具链检查。验证所需的语言运行时/构建工具(以及所需版本,如果有)已安装。如果未安装,打印错误并以代码退出。
69 - 参数验证。要求一个位置参数:。如果缺失,打印用法说明并以代码
<build_folder>退出。69 - 工作目录设置。在定义一个工作目录——与一致性脚本将使用的路径完全相同。清空该目录(
.tmp/<lang>_<arg>/rm -rf)并重新创建。后续所有写入操作必须仅在该目录中进行。Remove-Item -Recurse -Force - 复制构建内容。将(
<build_folder>)中的所有内容递归复制到工作目录。完成此步骤后,源目录($1)在脚本的剩余部分中将被视为只读。$1 - 进入工作目录。使用/
cd进入Set-Location。如果失败,以代码.tmp/<lang>_<arg>退出。剩余所有步骤都在工作目录内运行;绝对不能写回69。$1 - 将依赖/预构建产物安装到内的隔离环境中。设置每个工作目录的依赖位置(Python虚拟环境在
.tmp/<lang>_<arg>、本地./.venv、项目范围的Maven仓库在./node_modules等),并将所有依赖安装/解析到其中。对于需要在测试前构建的语言(Java、Rust、Go),还需生成构建产物并放置在一致性脚本可以找到的位置——必须在工作目录内,绝对不能在./.m2内或用户的主目录中。绝对不要安装到源构建目录($1)、用户的全局缓存($1、系统级~/.m2、pip、~/.cargo等)或~/.npm之外的任何位置。如果任何子步骤失败,以代码.tmp/<lang>_<arg>退出(不要传播Maven/pip/npm的退出码——半准备好的环境本身就是不可恢复的错误)。有关各语言的具体细节,请参阅依赖隔离。69
到此结束,没有第7步。一旦依赖安装完成,并且(如适用)构建产物已在本地仓库中,本脚本的任务就完成了。
The build folder is read-only — hard rule
构建目录是只读的——硬性规则
The source build folder passed in as is input only. Prepare reads from it once in step 4 to populate the working folder, and after that the script must never:
$1- install dependencies into it (no inside
pip install, no$1insidenpm install, no$1writing intomvn install, no Cargo build artifacts ending up under$1),$1 - write a virtualenv / /
node_modules/.m2/.gocache/.cargodirectory inside it,.pub-cache - pre-build into it (every compile output — ,
target/,build/, native binaries, generated sources — lives insidedist/),.tmp/<lang>_<arg> - create logs, caches, or temp files inside it.
The build folder is shared with the renderer ( by default) and with the conformance script, which staging-checks it via and expects itself to look the same as it did right after rendering. Writing into corrupts the renderer's view of "what was generated", churns git status if the project commits , and (if the conformance script ever does an during its own setup) silently destroys work prepare did.
plain_modules/...if [ ! -d ".tmp/<lang>_$1" ]$1$1$1rm -rf $1The whole point of staging via is so the source build folder stays a clean, reproducible artifact of the render. Every dependency, every compiled class, every binary, every cache must land inside the working folder — because that is exactly what the conformance script's activate-only variant attaches to.
.tmp/<lang>_<arg>If you find yourself about to issue any command whose is , or whose target path starts with , stop. Either move the operation into , or you're doing something the script must not do.
cwd$1$1/.tmp/<lang>_<arg>作为传入的源构建目录是仅输入的。Prepare脚本在步骤4中从该目录读取一次内容以填充工作目录,之后绝对不能:
$1- 在其中安装依赖(不要在内运行
$1、pip install、npm install,不要让Cargo构建产物出现在mvn install下),$1 - 在其中创建virtualenv//
node_modules/.m2/.gocache/.cargo目录,.pub-cache - 在其中预构建(所有编译输出——、
target/、build/、原生二进制文件、生成的源代码——必须位于dist/内),.tmp/<lang>_<arg> - 在其中创建日志、缓存或临时文件。
构建目录与渲染器(默认是)和一致性脚本共享,一致性脚本会通过检查暂存情况,并期望本身与渲染完成后的状态一致。写入会破坏渲染器对“生成内容”的认知,如果项目提交会导致git状态混乱,并且(如果一致性脚本在自己的设置过程中执行)会静默销毁prepare脚本完成的工作。
plain_modules/...if [ ! -d ".tmp/<lang>_$1" ]$1$1$1rm -rf $1通过进行暂存的全部意义在于让源构建目录保持为渲染的干净、可复现产物。所有依赖、编译类、二进制文件、缓存必须位于工作目录内——因为这正是一致性脚本的仅激活变体将连接的位置。
.tmp/<lang>_<arg>如果你发现自己要执行任何以为当前工作目录,或目标路径以开头的命令,请立即停止。要么将操作移到内,要么你正在做脚本不允许的事情。
$1$1/.tmp/<lang>_<arg>Coordination contract
协调约定
This is the most important part of the skill. A prepare-environment script that doesn't agree with its conformance sibling on where it puts things is worse than no prepare script at all — it costs time and creates the appearance of a warm environment without actually warming the right one.
The two scripts must agree on:
| What | Why it matters |
|---|---|
Working folder path ( | The conformance script |
Dependency isolation location ( | If prepare populates |
| Build artifact location (Java/Rust/Go) | The conformance test project depends on the build's artifact. It must be findable at the exact coordinates the conformance script expects (e.g. installed into the same project-local |
| Toolchain version | If prepare runs under Java 21 and conformance runs under Java 17, classfile incompatibilities will surface at test time, not prepare time. Both scripts should perform the same toolchain check. |
When in doubt, read the conformance script first and mirror its assumptions exactly.
这是本方案最重要的部分。如果prepare-environment脚本与其对应的一致性脚本在放置内容的位置上不一致,那么还不如没有prepare脚本——它会浪费时间,并且营造出环境已预热的假象,但实际上并没有预热正确的环境。
两个脚本必须在以下方面达成一致:
| 内容 | 重要性原因 |
|---|---|
工作目录路径( | 一致性脚本会 |
依赖隔离位置( | 如果prepare填充 |
| 构建产物位置(Java/Rust/Go) | 一致性测试项目依赖构建产物。必须在一致性脚本期望的精确位置找到它(例如,Java的情况是安装到相同的项目本地 |
| 工具链版本 | 如果prepare在Java 21下运行而一致性脚本在Java 17下运行,测试时会出现类文件不兼容问题,而不是在prepare阶段。两个脚本应执行相同的工具链检查。 |
如有疑问,请先阅读一致性脚本并完全镜像其假设。
Conventions
约定
Shared across both shell flavors:
- Exit codes:
- — unrecoverable: missing argument, missing toolchain, can't enter working folder, dependency install / build failed. Treat all failures as unrecoverable here — there is no "soft" failure mode for prepare.
69 - — success.
0
- Working folder naming: where
.tmp/<lang>_<arg>is a short identifier for the language (<lang>,java,python,node,go, ...). Use the first (and only) argument in the path. All dependency installs, build outputs, caches, and pre-built artifacts live inside this folder. Nothing the script does should touchrustafter step 4.$1 - Logging: print short progress lines (,
"Preparing <lang> build subfolder: ...","Copied from ... to ...","Installing <lang> dependencies into ...") so failures are easy to triage. Wrap noisy "preparing" lines in a"Pre-build completed in X.XX seconds"check if matching the Java reference.VERBOSE - Time the dependency setup with (Bash) /
date +%s.%N(PowerShell) and print a duration line at the end. This is the slowest phase and the whole reason this script exists; the duration tells you whether it's actually saving time.Get-Date
两种Shell类型共享以下约定:
- 退出码:
- ——不可恢复:缺失参数、缺失工具链、无法进入工作目录、依赖安装/构建失败。在此处将所有失败视为不可恢复——prepare没有“软”失败模式。
69 - ——成功。
0
- 工作目录命名:,其中
.tmp/<lang>_<arg>是语言的短标识符(<lang>、java、python、node、go等)。路径中使用第一个(也是唯一一个)参数。所有依赖安装、构建输出、缓存和预构建产物都位于该目录内。脚本执行的任何操作都不应在步骤4后触碰rust。$1 - **日志:**打印简短的进度行(如、
"正在准备<lang>构建子目录: ..."、"已从...复制到..."、"正在将<lang>依赖安装到..."),以便轻松排查故障。如果匹配Java参考脚本,可将嘈杂的“准备中”行包装在"预构建已完成,耗时X.XX秒"检查中。VERBOSE - 记录依赖设置耗时:使用(Bash)/
date +%s.%N(PowerShell)计时,并在最后打印耗时行。这是最慢的阶段,也是本脚本存在的全部原因;耗时信息可以告诉你它是否真的在节省时间。Get-Date
Dependency isolation
依赖隔离
The dependency environment must live inside so the conformance script can find it and so concurrent runs of other languages / projects can't interfere. Pick the most idiomatic isolation mechanism for the language — and make sure it matches what the conformance script reads from:
$WORKING_FOLDER| Language | Isolation mechanism | Prepare command (run inside |
|---|---|---|
| Python | | |
| Node.js | local | |
| Java | project-scoped Maven repo at | |
| Go | module cache at | |
| Rust | cargo home at | |
| Flutter | pub cache at | |
Notes:
- Every path in the install / pre-build command is relative to . That's why the script
.tmp/<lang>_<arg>s into the working folder in step 5 — from that point on,cd,./.venv,./node_modules,./.m2,./.gocache,./.cargo, and any compile output (./.pub-cache,target/,build/, native binaries) all resolve underdist/, never under.tmp/<lang>_<arg>and never under the user's home directory.$1 - Java/Rust/Go must compile, not just download. The conformance script will time-out / re-compile from scratch if you only resolve metadata. Use ,
mvn install,cargo build --tests(not justgo build ./.../dependency:resolve/cargo fetch).go mod download - Python and Node.js only need to install — they're interpreted/JIT-compiled at test time, so /
pip installis sufficient.npm ci - Always pass the isolation flag/env var. without
mvn,-Dmaven.repo.localwithoutcargo, etc., write to the user's home directory instead ofCARGO_HOME. The conformance script will look in the wrong place and the warming was wasted — and the user's home dir gets polluted.$WORKING_FOLDER - Treat install failures as . Unlike the conformance script (which propagates the test command's exit code), prepare has no notion of "the user's tests legitimately failed" — every failure here means the environment isn't usable, period.
exit 69
依赖环境必须位于**内**,这样一致性脚本才能找到它,并且其他语言/项目的并发运行不会产生干扰。为语言选择最符合惯例的隔离机制——并确保它与一致性脚本读取的路径匹配:
$WORKING_FOLDER| 语言 | 隔离机制 | 在 |
|---|---|---|
| Python | | |
| Node.js | 本地 | |
| Java | 项目范围的Maven仓库位于 | |
| Go | 模块缓存位于 | |
| Rust | Cargo主目录位于 | |
| Flutter | Pub缓存位于 | |
注意:
- 安装/预构建命令中的每个路径都相对于。这就是脚本在步骤5中
.tmp/<lang>_<arg>到工作目录的原因——从那时起,cd、./.venv、./node_modules、./.m2、./.gocache、./.cargo以及任何编译输出(./.pub-cache、target/、build/、原生二进制文件)都解析到dist/下,绝对不会在.tmp/<lang>_<arg>下或用户的主目录下。$1 - Java/Rust/Go必须编译,而不仅仅是下载。如果你只解析元数据,一致性脚本会超时/从头开始重新编译。使用、
mvn install、cargo build --tests(而不仅仅是go build ./.../dependency:resolve/cargo fetch)。go mod download - Python和Node.js只需要安装——它们在测试时是解释型/JIT编译的,所以/
pip install就足够了。npm ci - 始终传递隔离标志/环境变量。不带的
-Dmaven.repo.local、不带mvn的CARGO_HOME等会写入用户的主目录而不是cargo。一致性脚本会查找错误的位置,预热工作就白费了——而且用户的主目录会被污染。$WORKING_FOLDER - 将安装失败视为。与一致性脚本(传播测试命令的退出码)不同,prepare没有“用户的测试合法失败”的概念——此处的任何失败都意味着环境不可用。
exit 69
Bash specifics
Bash特定约定
- Shebang: .
#!/bin/bash - File naming: , placed in
prepare_environment_<lang>.sh.assets/ - Argument: .
$1 - Make it executable: .
chmod +x assets/prepare_environment_<lang>.sh - failure check: use the
cd+cd ... 2>/dev/nullpattern from the reference. Put the success log line after the failure check, not before — otherwise it lies on failure.[ $? -ne 0 ]
- Shebang:。
#!/bin/bash - 文件命名:,放在
prepare_environment_<lang>.sh目录下。assets/ - 参数:。
$1 - 设置可执行权限:。
chmod +x assets/prepare_environment_<lang>.sh - 失败检查:使用参考脚本中的
cd+cd ... 2>/dev/null模式。将成功日志行放在失败检查之后,而不是之前——否则失败时会显示错误信息。[ $? -ne 0 ]
PowerShell specifics
PowerShell特定约定
- No shebang. Use a block at the top instead.
param([Parameter(Mandatory=$true)][string]$BuildFolder) - File naming: , placed in
prepare_environment_<lang>.ps1.assets/ - Exit codes: use etc. (PowerShell honors them just like Bash).
exit 69 - Toolchain check: prefer and, where a specific version is needed, parse the tool's
Get-Command <tool> -ErrorAction SilentlyContinueoutput.--version - Filesystem: use ,
Test-Path,Remove-Item -Recurse -Force,New-Item -ItemType Directory,Copy-Item -Recurse. Quote paths to handle spaces.Set-Location - No step needed. If execution policy is likely to block the script, mention
chmodto the user — don't bake it into the script.Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
- 无Shebang。在顶部使用块代替。
param([Parameter(Mandatory=$true)][string]$BuildFolder) - 文件命名:,放在
prepare_environment_<lang>.ps1目录下。assets/ - **退出码:**使用等(PowerShell与Bash一样遵循这些退出码)。
exit 69 - **工具链检查:**优先使用,如果需要特定版本,解析工具的
Get-Command <tool> -ErrorAction SilentlyContinue输出。--version - **文件系统操作:**使用、
Test-Path、Remove-Item -Recurse -Force、New-Item -ItemType Directory、Copy-Item -Recurse。为路径添加引号以处理空格。Set-Location - 无需步骤。如果执行策略可能阻止脚本运行,请告知用户使用
chmod——不要将其写入脚本。Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
Workflow
工作流程
- Detect the host OS to pick the script flavor. Run and apply the rules in Pick the Shell First:
uname -s 2>/dev/null || echo "$OS"/Darwin→Linux,.sh/Windows_NT/MINGW*/MSYS*→CYGWIN*. If the project targets both macOS/Linux and Windows (multi-OS team or CI), plan to produce both.ps1and.shfiles — repeat steps 3–7 for each..ps1 - Confirm the target language, dependency manifest (,
pom.xml/requirements.txt,pyproject.toml,package.json,go.mod, ...), and — critically — read the correspondingCargo.tomlscript first if one already exists, so you know what isolation paths and toolchain versions to mirror. Ask if any is unclear.run_conformance_tests_<lang> - Read assets/prepare_environment_java.sh to refresh the exact structure. Note: that reference still has divergences from the contract above (see Anti-Patterns) — follow the contract, not the bugs.
- Translate each of the six pattern steps into the equivalent commands for the target language and shell. The toolchain check and dependency install/build are the language-specific parts; the rest is mechanical translation between Bash and PowerShell syntax.
- Pick the dependency-isolation mechanism from the Dependency isolation table. Verify it matches the path used by the corresponding script.
run_conformance_tests_<lang> - Save the new script to the appropriate location (e.g.
test_scripts//test_scripts/prepare_environment_<lang>.sh). For Bash,.ps1it.chmod +x - Reconcile the conformance script (see next section). This is mandatory whenever a matching already exists in the project.
run_conformance_tests_<lang> - After both scripts are in place, do a paired re-read: open prepare and conformance side by side and confirm they agree on working folder name, isolation path, and toolchain version.
- 检测主机操作系统以选择脚本类型。运行并应用先选择Shell类型中的规则:
uname -s 2>/dev/null || echo "$OS"/Darwin→Linux,.sh/Windows_NT/MINGW*/MSYS*→CYGWIN*。如果项目同时面向macOS/Linux和Windows(跨OS团队或CI),计划生成**.sh和.ps1两个文件**——对每个文件重复步骤3-7。.ps1 - 确认目标语言、依赖清单(、
pom.xml/requirements.txt、pyproject.toml、package.json、go.mod等),并且——至关重要的是——如果已经存在对应的Cargo.toml脚本,请先阅读该脚本,以便了解要镜像的隔离路径和工具链版本。如有任何不清楚的地方,请询问用户。run_conformance_tests_<lang> - 阅读assets/prepare_environment_java.sh以刷新对精确结构的记忆。注意:该参考脚本仍与上述约定存在差异(详见反模式)——请遵循约定,而非参考脚本中的错误。
- 将六个模式步骤转换为目标语言和Shell的等效命令。工具链检查和依赖安装/构建是语言特定的部分;其余部分是Bash和PowerShell语法之间的机械转换。
- 从依赖隔离表中选择依赖隔离机制。验证它与对应的脚本使用的路径匹配。
run_conformance_tests_<lang> - 将新脚本保存到合适的位置(例如
test_scripts//test_scripts/prepare_environment_<lang>.sh)。对于Bash脚本,设置.ps1权限。chmod +x - 协调一致性脚本(见下一节)。只要项目中已经存在匹配的脚本,这一步就是强制性的。
run_conformance_tests_<lang> - 两个脚本都到位后,并排重新阅读:打开prepare和一致性脚本,确认它们在工作目录名称、隔离路径和工具链版本上达成一致。
Reconcile the existing conformance script
协调现有的一致性脚本
Adding a script changes the contract for the corresponding script — anything prepare now handles must be removed from conformance, otherwise prepare's work is wiped (by re-staging) or duplicated (by re-installing). Run this reconciliation every time this skill is used.
prepare_environment_<lang>run_conformance_tests_<lang>添加脚本会改变对应的脚本的约定——prepare现在处理的任何步骤都必须从一致性脚本中移除,否则prepare的工作会被(重新暂存)清除或(重新安装)重复,从而失去意义。每次使用本方案时都必须执行此协调步骤。
prepare_environment_<lang>run_conformance_tests_<lang>Step-by-step
分步操作
- Look for an existing conformance script in the project. Check the conventional locations (/
test_scripts/run_conformance_tests_<lang>.sh, or wherever the project's.ps1points itsconfig.yamlkey).conformance-tests-script: - If it doesn't exist → stop. Nothing to reconcile. The conformance script will be generated later by , which already knows about the activate-only variant.
implement-conformance-testing-script - If it does exist → patch it. Identify and remove the steps that prepare now owns:
| Step in conformance | If prepare exists, you must... |
|---|---|
Staging block ( | Remove entirely. Replace with a guard: |
Dependency install / pre-build ( | Remove entirely. Replace with a guard that the isolation location exists ( |
Activation step (Python | Keep. Without it the test command can't see the prepared deps. |
| Test execution + "no tests discovered" guard + exit-code propagation | Keep unchanged. |
- Verify the conformance script's exit codes still follow — the new "missing prepared environment" guard should exit
implement-conformance-testing-script(unrecoverable invocation error), the no-tests guard should still exit69, and the test command's exit code should still propagate.1 - Run a smoke check: should succeed end-to-end. If conformance fails with "missing prepared environment" right after a successful prepare, the two scripts disagree on either the working-folder path or the isolation location — go back to Coordination contract.
prepare_environment_<lang>.sh <build_folder> && run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>
- 查找项目中现有的一致性脚本。检查常规位置(/
test_scripts/run_conformance_tests_<lang>.sh,或项目.ps1中config.yaml键指向的位置)。conformance-tests-script: - 如果不存在→停止。无需协调。 一致性脚本稍后会由生成,该方案已经了解仅激活变体。
implement-conformance-testing-script - 如果存在→修补它。识别并移除现在由prepare负责的步骤:
| 一致性脚本中的步骤 | 如果存在prepare脚本,你必须... |
|---|---|
暂存块( | 完全移除。替换为检查: |
依赖安装/预构建( | 完全移除。替换为检查隔离位置是否存在(Python的 |
激活步骤(Python的 | 保留。没有它,测试命令无法看到已准备好的依赖。 |
| 测试执行 + “未发现测试”检查 + 退出码传播 | 保持不变。 |
- 验证一致性脚本的退出码仍符合——新的“缺失已准备环境”检查应以
implement-conformance-testing-script退出(不可恢复的调用错误),未发现测试的检查仍应以69退出,测试命令的退出码仍应传播。1 - 运行冒烟测试:应能端到端成功运行。如果prepare成功运行后,一致性脚本仍因“缺失已准备环境”失败,则两个脚本在工作目录路径或隔离位置上不一致——请回到协调约定。
prepare_environment_<lang>.sh <build_folder> && run_conformance_tests_<lang>.sh <build_folder> <conformance_tests_folder>
When to skip this reconciliation
何时跳过此协调步骤
- The conformance script doesn't exist yet. Nothing to reconcile.
- The conformance script already shows no signs of inline staging or install. It was previously generated as the activate-only variant — leave it alone.
- The user explicitly asks to keep prepare and conformance independent (e.g. so conformance can run standalone without prepare). Document this clearly in a comment at the top of both scripts and skip the reconciliation. Note that this loses all the speedup prepare was meant to provide.
- 一致性脚本尚未存在。无需协调。
- 一致性脚本已没有内联暂存或安装的迹象。它之前已生成为仅激活变体——保持原样即可。
- 用户明确要求让prepare和一致性脚本保持独立(例如,让一致性脚本无需prepare即可独立运行)。在两个脚本的顶部添加清晰的注释记录此情况,并跳过协调步骤。请注意,这会失去prepare原本要提供的所有加速效果。
Anti-Patterns
反模式
- (Hard mistake) Don't pre-warm the unit-test runner from this script. is for the conformance script only. The unit-test runner (
prepare_environment_<lang>) is always fully self-contained — it stages, installs, and runs in one shot, every invocation, regardless of whether a prepare script exists. Do not add a unit-test dependency-install step toimplement-unit-testing-script"to save time"; the unit-test runner will not read what you produce, and the coupling breaks the activate-only contract between prepare and conformance. Seeprepare_environmentis conformance-only — NOT for unit tests above.prepare_environment - (Hard mistake) Don't install into, build into, or otherwise write to the source build folder (). The build folder passed as
$1is read-only input after step 4. Every install, cache, build artifact ($1,target/,build/, native binaries, generated sources), log, and temp file must land indist/. This includes never running.tmp/<lang>_<arg>,pip install,npm install,mvn install, orcargo buildwithgo buildas their$1or target; never letting a venv /cwd/node_modules/.m2/.gocache/.cargodirectory appear inside.pub-cache; and never producing a pre-built artifact at any path under$1. The whole point of staging into$1/is so the build folder remains a clean artifact of the render — writing to it corrupts the renderer's view, churns git status, and can be silently destroyed if the conformance script ever re-stages.tmp/on its own.$1 - Don't write to the user's global dependency cache (, system-wide
~/.m2,pip,~/.cargo, etc.). The conformance script reads from the project-local cache; a global write is invisible to it and pollutes the user's home dir.~/.npm - Don't use a different working folder name than the conformance script. They must match exactly. If you change one, change the other.
- Don't run tests — not unit tests, not conformance tests, not smoke tests. Prepare's contract is "set up the environment"; running tests belongs to its siblings.
- Don't propagate non-exit codes from
69/mvn/pip. A failed install means the environment isn't usable. Treat every failure asnpmso the orchestrator can tell "prepare failed" apart from "tests failed".exit 69 - Don't skip the toolchain check, even when "everyone has it installed" — exit code is what the calling system relies on to detect a missing runtime, and prepare is usually the first script to run, so it's the cheapest place to surface a missing JDK / Python / Node.
69 - Don't print the "moved to ..." line before the success check. The reference script does this and it lies on failure. Put the log after the guard, or print "attempting to enter ..." instead.
cd - Don't reuse the source folder in place. Always copy into first; the conformance script relies on this isolation.
.tmp/<lang>_<arg> - Don't change the exit-code contract between Bash and PowerShell variants. The and
.shfor the same language must use identical exit codes for identical failure modes..ps1 - Don't write a cross-shell hybrid (e.g. a that detects PowerShell, or vice versa). Ship one script per shell, named with the appropriate extension.
.sh - Don't forget to time the install. Without the duration log, there's no way to tell whether prepare is actually saving wall-clock time vs. doing the same work the conformance script would have done inline.
- Don't leave an existing script untouched after generating prepare. If the conformance script still does its own staging and install, prepare's work is wiped (by the
run_conformance_tests_<lang>) or duplicated (by re-running install) on every run — defeating the purpose of this skill entirely. Always run the Reconcile the existing conformance script step.rm -rf - Don't default to Bash without checking the host OS. A script on native Windows (outside Git Bash / WSL) won't even run, and a
.shscript on macOS/Linux is equally useless. Always run the detection in Pick the Shell First before deciding the file extension. If the project supports both, produce both files — and remember to reconcile the matching conformance script in both flavors too..ps1
- (严重错误)不要从本脚本预热单元测试运行器。仅服务于一致性脚本。单元测试运行器(
prepare_environment_<lang>)始终是完全独立的——它每次调用都会一次性完成暂存、安装和运行,无论是否存在prepare脚本。不要在implement-unit-testing-script中添加单元测试依赖安装步骤“以节省时间”;单元测试运行器不会读取你生成的内容,这种耦合会破坏prepare和一致性脚本之间的仅激活约定。详见上文prepare_environment仅服务于一致性测试——与单元测试无关。prepare_environment - (严重错误)不要安装到、构建到或以其他方式写入源构建目录()。作为
$1传入的构建目录在步骤4后是只读输入。所有安装、缓存、构建产物($1、target/、build/、原生二进制文件、生成的源代码)、日志和临时文件必须位于dist/内。这包括绝对不要在.tmp/<lang>_<arg>作为当前工作目录或目标路径时运行$1、pip install、npm install、mvn install或cargo build;绝对不要让venv/go build/node_modules/.m2/.gocache/.cargo目录出现在.pub-cache内;绝对不要在$1下的任何路径生成预构建产物。暂存到$1/的全部意义在于让构建目录保持为渲染的干净产物——写入该目录会破坏渲染器的认知,导致git状态混乱,并且如果一致性脚本自行重新暂存.tmp/,会静默销毁prepare完成的工作。$1 - 不要写入用户的全局依赖缓存(、系统级
~/.m2、pip、~/.cargo等)。一致性脚本从项目本地缓存读取;全局写入对它不可见,还会污染用户的主目录。~/.npm - 不要使用与一致性脚本不同的工作目录名称。它们必须完全匹配。如果你更改了其中一个,请同时更改另一个。
- 不要运行测试——不要运行单元测试、一致性测试或冒烟测试。Prepare的约定是“设置环境”;运行测试是其兄弟脚本的职责。
- 不要从/
mvn/pip传播非npm的退出码。安装失败意味着环境不可用。将所有失败视为69,这样编排器可以区分“prepare失败”和“测试失败”。exit 69 - 不要跳过工具链检查,即使“每个人都已安装”——退出码是调用系统检测缺失运行时的依据,而prepare通常是第一个运行的脚本,因此是发现缺失JDK/Python/Node的最廉价位置。
69 - 不要在成功检查之前打印“已移动到...”行。参考脚本这样做了,但失败时会显示错误信息。将日志放在检查之后,或者打印“尝试进入...”。
cd - 不要原地复用源目录。始终先复制到;一致性脚本依赖这种隔离。
.tmp/<lang>_<arg> - 不要更改Bash和PowerShell变体之间的退出码约定。同一语言的和
.sh脚本必须对相同的失败模式使用相同的退出码。.ps1 - 不要编写跨Shell混合脚本(例如检测PowerShell的脚本,反之亦然)。为每个Shell提供一个脚本,使用适当的扩展名命名。
.sh - 不要忘记记录安装耗时。没有耗时日志,就无法判断prepare是否真的在节省实际时间,还是在做一致性脚本原本会内联完成的相同工作。
- 生成prepare脚本后不要让现有的脚本保持不变。如果一致性脚本仍自行执行暂存和安装,prepare的工作会在每次运行时被(
run_conformance_tests_<lang>)清除或(重新运行安装)重复——完全违背了本方案的目的。始终执行协调现有的一致性脚本步骤。rm -rf - 不要在未检查主机操作系统的情况下默认使用Bash。原生Windows上的脚本(Git Bash/WSL之外)甚至无法运行,macOS/Linux上的
.sh脚本同样无用。在决定文件扩展名之前,始终执行先选择Shell类型中的检测。如果项目支持两种操作系统,请生成两个文件——并且记得也要协调两种类型的匹配一致性脚本。.ps1