重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
shadow-testing by rysweet/amplihack
npx skills add https://github.com/rysweet/amplihack --skill shadow-testing影子测试创建隔离的容器环境,您可以在其中测试本地未提交的更改,而不会影响您的主机系统或推送到远程仓库。
核心原则:在干净、隔离且镜像 CI 的环境中,精确测试您机器上的内容(包括未提交的更改)。
影子环境是一个包含以下内容的 Docker/Podman 容器:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
insteadOf 规则将特定仓库重定向到本地 Gitea┌─────────────────────────────────────────────────────────┐
│ 影子容器 │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Gitea 服务器 (localhost:3000) │ │
│ │ - myorg/my-library (您的快照) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Git URL 重写: │
│ github.com/myorg/my-library → Gitea (本地) │
│ github.com/myorg/other-repo → 真实的 GitHub │
│ │
│ /workspace (预克隆的本地源码) │
└─────────────────────────────────────────────────────────┘
当您使用 ~/repos/my-lib:myorg/my-lib 创建影子时:
您的工作目录被原样捕获(包括未提交的更改)
快照与完整的 git 历史记录一起打包
容器启动并运行 Gitea 服务器
快照作为 myorg/my-lib 推送到 Gitea
Git 配置添加 insteadOf 规则:
[url "http://shadow:shadow@localhost:3000/myorg/my-lib.git"] insteadOf = https://github.com/myorg/my-lib.git
任何 git clone https://github.com/myorg/my-lib → 使用您的本地快照
所有其他 GitHub URL → 从真实的 GitHub 获取
结果:只有您指定的仓库是本地版本;其他所有内容都使用生产源。
对于 Amplifier 用户(原生集成):
# Shadow 工具是内置的 - 无需安装
amplifier run --bundle amplihack
对于其他代理(独立 CLI):
# 通过 uvx 安装(推荐)
uvx amplifier-shadow --version
# 或通过 pip 安装
pip install amplifier-bundle-shadow
# 验证安装
amplifier-shadow --version
先决条件:
# 使用您的本地库更改创建影子
amplifier-shadow create --local ~/repos/my-library:myorg/my-library --name test-lib
# 在影子内部,通过 git URL 安装
# → my-library 使用您的本地快照
# → 所有其他依赖项从真实的 GitHub 获取
amplifier-shadow exec test-lib "uv pip install git+https://github.com/myorg/my-library"
# 运行测试
amplifier-shadow exec test-lib "cd /workspace && pytest"
# 查看更改内容
amplifier-shadow diff test-lib
# 完成后清理
amplifier-shadow destroy test-lib
# 使用本地更改创建影子
shadow.create(local_sources=["~/repos/my-library:myorg/my-library"])
# 执行命令
shadow.exec(shadow_id, "uv pip install git+https://github.com/myorg/my-library")
shadow.exec(shadow_id, "pytest tests/")
# 提取结果
shadow.extract(shadow_id, "/workspace/test-results", "./results")
# 清理
shadow.destroy(shadow_id)
最佳体验 - shadow 是一等工具,具有自动设置:
# 所有操作都通过 shadow 工具
result = shadow.create(
local_sources=["~/repos/lib:org/lib"],
verify=True # 自动冒烟测试
)
# 集成的错误处理和可观测性
if result.ready:
shadow.exec(result.shadow_id, "pytest")
功能:
直接从 bash 工具使用 CLI:
# 所有操作都通过 amplifier-shadow CLI
uvx amplifier-shadow create --local ~/repos/my-lib:org/my-lib --name test
uvx amplifier-shadow exec test "pip install -e /workspace/org/my-lib"
uvx amplifier-shadow exec test "pytest"
uvx amplifier-shadow destroy test
与 Claude Code 相同的 CLI 接口:
# 安装一次
pip install amplifier-bundle-shadow
# 在工作流中使用
amplifier-shadow create --local ~/repos/lib:org/lib
amplifier-shadow exec shadow-xxx "npm install && npm test"
使用提供的 shell 脚本和 Docker Compose 示例(参见 Level 3)。
# 使用依赖项目测试您的库
amplifier-shadow create --local ~/repos/my-library:myorg/my-library --name lib-test
# 克隆依赖项目并安装
amplifier-shadow exec lib-test "
cd /workspace &&
git clone https://github.com/myorg/dependent-app &&
cd dependent-app &&
uv venv && . .venv/bin/activate &&
uv pip install git+https://github.com/myorg/my-library &&
pytest
"
# 跨多个仓库测试更改
amplifier-shadow create \
--local ~/repos/core-lib:myorg/core-lib \
--local ~/repos/cli-tool:myorg/cli-tool \
--name multi-test
# 两个本地源都将被使用
amplifier-shadow exec multi-test "uv pip install git+https://github.com/myorg/cli-tool"
# 1. 创建影子并运行测试
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest" # 失败
# 2. 在主机上本地修复代码
# 3. 销毁并重新创建(获取您的本地更改)
amplifier-shadow destroy test
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest" # 通过
# 4. 放心提交!
git commit -m "修复问题"
# 在推送前在影子中运行您的 CI 脚本
amplifier-shadow create --local ~/repos/project:org/project --name ci-check
amplifier-shadow exec ci-check "
cd /workspace/org/project &&
./scripts/ci.sh
"
# 如果 CI 脚本通过,您的推送很可能成功
创建影子后,确认您的本地代码确实被使用:
# 步骤 1:检查快照提交(从创建输出中)
amplifier-shadow create --local ~/repos/lib:org/lib
# 输出显示:snapshot_commits: {"org/lib": "abc1234..."}
# 步骤 2:与安装输出比较
amplifier-shadow exec shadow-xxx "uv pip install git+https://github.com/org/lib"
# 查找:lib @ git+...@abc1234
# 如果提交匹配,您的本地代码正在被使用!
本地源自动克隆到 /workspace/{org}/{repo}:
# 您的本地源 microsoft/my-library 位于:
/workspace/microsoft/my-library
# 用于可编辑安装(Python)
amplifier-shadow exec shadow-xxx "pip install -e /workspace/microsoft/my-library"
# 或用于 Node.js
amplifier-shadow exec shadow-xxx "cd /workspace/microsoft/my-package && npm install"
始终首先检查此位置 - 仓库已经在那里了。
# 不要假设 - 验证 API 密钥是否存在!
amplifier-shadow exec shadow-xxx "env | grep API_KEY"
# 检查所有传递的变量
amplifier-shadow status shadow-xxx
# 显示:env_vars_passed: ["ANTHROPIC_API_KEY", ...]
"UV tool install" 使用缓存而不是本地源:
问题:UV 可能会绕过 git URL 重写来使用缓存的包。
解决方案:
# 选项 1:从预克隆的工作区安装(推荐)
amplifier-shadow exec xxx "pip install -e /workspace/org/lib"
# 选项 2:首先清除 UV 缓存
amplifier-shadow exec xxx "rm -rf /tmp/uv-cache && uv tool install git+https://github.com/org/lib"
"PEP 668: 外部管理环境":
解决方案:始终在影子内部使用虚拟环境:
amplifier-shadow exec xxx "
cd /workspace &&
uv venv &&
. .venv/bin/activate &&
uv pip install ...
"
"容器镜像未找到":
解决方案:本地构建镜像:
amplifier-shadow build
"/workspace 权限被拒绝":
解决方案:使用 $HOME 或 /tmp 作为替代:
amplifier-shadow exec xxx "cd $HOME && git clone ..."
构建您自己的带有附加工具的 shadow 镜像:
FROM ghcr.io/microsoft/amplifier-shadow:latest
# 添加您的工具
RUN apt-get update && apt-get install -y \
postgresql-client \
redis-tools
# 添加自定义脚本
COPY my-test-script.sh /usr/local/bin/
构建并使用:
docker build -t my-shadow:latest .
amplifier-shadow create --image my-shadow:latest --local ~/repos/lib:org/lib
对于没有 Amplifier 访问权限的代理,使用这些独立脚本:
脚本 1:创建 Git 包 (scripts/create-bundle.sh):
#!/bin/bash
# 创建工作树的 git 包快照
REPO_PATH=$1
OUTPUT_PATH=$2
cd "$REPO_PATH"
# 获取所有引用以确保历史完整
git fetch --all --tags --quiet 2>/dev/null || true
# 检查未提交的更改
if [[ -n $(git status --porcelain) ]]; then
# 创建临时克隆并提交更改
TEMP_DIR=$(mktemp -d)
git clone --quiet "$REPO_PATH" "$TEMP_DIR"
# 同步工作树(包括删除)
rsync -a --delete --exclude='.git' "$REPO_PATH/" "$TEMP_DIR/"
cd "$TEMP_DIR"
git add -A
git commit --allow-empty -m "Shadow snapshot" --author="Shadow <shadow@localhost>"
# 创建包
git bundle create "$OUTPUT_PATH" --all
cd /
rm -rf "$TEMP_DIR"
else
# 干净的仓库 - 直接打包
git bundle create "$OUTPUT_PATH" --all
fi
echo "Bundle created: $OUTPUT_PATH"
脚本 2:设置影子容器 (scripts/setup-shadow.sh):
#!/bin/bash
# 启动带有 Gitea 的容器并配置 git URL 重写
CONTAINER_NAME=$1
BUNDLE_PATH=$2
ORG=$3
REPO=$4
# 启动容器
docker run -d \
--name "$CONTAINER_NAME" \
-v "$BUNDLE_PATH:/snapshots/bundle.git:ro" \
ghcr.io/microsoft/amplifier-shadow:latest
# 等待 Gitea 启动
echo "Waiting for Gitea to start..."
until docker exec "$CONTAINER_NAME" curl -sf http://localhost:3000/api/v1/version > /dev/null; do
sleep 1
done
# 在 Gitea 中创建组织和仓库
docker exec "$CONTAINER_NAME" bash -c "
curl -s -u shadow:shadow \
-H 'Content-Type: application/json' \
-d '{\"username\":\"$ORG\"}' \
http://localhost:3000/api/v1/orgs
curl -s -u shadow:shadow \
-H 'Content-Type: application/json' \
-d '{\"name\":\"$REPO\",\"private\":false}' \
http://localhost:3000/api/v1/orgs/$ORG/repos
"
# 将包推送到 Gitea
docker exec "$CONTAINER_NAME" bash -c "
cd /tmp &&
git init --bare repo.git &&
cd repo.git &&
git fetch /snapshots/bundle.git refs/heads/*:refs/heads/* &&
git remote add origin http://shadow:shadow@localhost:3000/$ORG/$REPO.git &&
git push origin --all --force
"
# 配置 git URL 重写
docker exec "$CONTAINER_NAME" bash -c "
git config --global url.'http://shadow:shadow@localhost:3000/$ORG/$REPO.git'.insteadOf 'https://github.com/$ORG/$REPO.git'
"
echo "Shadow container ready: $CONTAINER_NAME"
echo "Local source: $ORG/$REPO"
用法:
# 从您的仓库创建包
./scripts/create-bundle.sh ~/repos/my-lib /tmp/my-lib.bundle
# 设置影子容器
./scripts/setup-shadow.sh shadow-test /tmp/my-lib.bundle myorg my-lib
# 测试
docker exec shadow-test bash -c "
git clone https://github.com/myorg/my-lib /tmp/test &&
cd /tmp/test &&
git log -1 --oneline
"
示例 1:单仓库 (docker-compose/single-repo.yml):
version: "3.8"
services:
shadow:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: shadow-single
volumes:
- ./snapshots:/snapshots:ro
- ./workspace:/workspace
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
tail -f /dev/null
"
示例 2:多仓库测试 (docker-compose/multi-repo.yml):
version: "3.8"
services:
shadow-multi:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: shadow-multi
volumes:
# 挂载多个包
- ./snapshots/core-lib.bundle:/snapshots/org/core-lib.bundle:ro
- ./snapshots/cli-tool.bundle:/snapshots/org/cli-tool.bundle:ro
- ./workspace:/workspace
environment:
# 从主机传递 API 密钥
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# UV 缓存隔离
- UV_CACHE_DIR=/tmp/uv-cache
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
/usr/local/bin/setup-repos.sh org/core-lib org/cli-tool &&
tail -f /dev/null
"
用法:
# 为您的仓库创建包
git -C ~/repos/core-lib bundle create snapshots/core-lib.bundle --all
git -C ~/repos/cli-tool bundle create snapshots/cli-tool.bundle --all
# 启动影子
docker-compose -f docker-compose/multi-repo.yml up -d
# 运行测试
docker-compose exec shadow-multi bash -c "
cd /workspace &&
git clone https://github.com/org/cli-tool &&
cd cli-tool &&
uv pip install -e .
pytest
"
# 清理
docker-compose down
示例 3:CI 集成 (docker-compose/ci-shadow.yml):
version: "3.8"
services:
ci-shadow:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: ci-shadow
volumes:
- ./snapshots:/snapshots:ro
- ./test-results:/test-results
environment:
- CI=true
- GITHUB_ACTIONS=true
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
/usr/local/bin/run-ci-tests.sh > /test-results/output.log 2>&1
"
GitHub Actions 集成:
# .github/workflows/shadow-test.yml
name: Shadow Test
on: [push, pull_request]
jobs:
shadow-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Create git bundle
run: git bundle create snapshot.bundle --all
- name: Run shadow tests
run: |
docker run --rm \
-v $PWD/snapshot.bundle:/snapshots/bundle.git:ro \
ghcr.io/microsoft/amplifier-shadow:latest \
/usr/local/bin/test-in-shadow.sh org/repo
将影子环境与代理式外部测试结合:
# 使用本地更改创建影子
amplifier-shadow create --local ~/repos/lib:org/lib --name test
# 在影子内部运行外部测试场景
amplifier-shadow exec test "gadugi-agentic-test run test-scenario.yaml"
# 提取证据
amplifier-shadow extract test /evidence ./test-evidence
有关完整集成示例,请参阅 qa-team 技能(outside-in-testing 仍为别名)。
不要假设 - 验证影子确实在使用您的本地代码:
# 检查快照提交
amplifier-shadow status shadow-xxx | grep snapshot_commit
# 验证安装解析到该提交
amplifier-shadow exec shadow-xxx "pip install git+https://github.com/org/lib" | grep "org/lib @"
本地源自动位于 /workspace/{org}/{repo}:
# ✅ 快速:使用预克隆的仓库
amplifier-shadow exec xxx "pip install -e /workspace/org/lib"
# ❌ 较慢:再次克隆
amplifier-shadow exec xxx "git clone https://github.com/org/lib && pip install -e lib"
影子环境自动隔离缓存以防止使用过时的包:
/tmp/uv-cache/tmp/pip-cache/tmp/npm-cache/tmp/cargo-home/tmp/go-mod-cache这些会自动设置 - 无需操作。
# Amplifier(常见 API 密钥自动传递)
shadow.create(local_sources=["~/repos/lib:org/lib"])
# CLI(显式)
amplifier-shadow create \
--local ~/repos/lib:org/lib \
--env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
--env CUSTOM_VAR=value
# 完成后始终销毁影子
amplifier-shadow destroy shadow-xxx
# 或销毁所有
amplifier-shadow destroy-all
# ✅ 良好:描述性名称
amplifier-shadow create --local ~/repos/lib:org/lib --name test-breaking-change
# ❌ 不佳:自动生成
amplifier-shadow create --local ~/repos/lib:org/lib
# 创建 shadow-a3f2b8c1(难以记忆)
将影子隔离与声明式测试场景结合:
# test-scenario.yaml
scenario:
name: "Library Integration Test"
type: cli
steps:
- action: launch
target: "/workspace/org/lib/cli.py"
- action: verify_output
contains: "Success"
在影子中运行:
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "gadugi-agentic-test run test-scenario.yaml"
amplifier-shadow create --local ~/repos/lib:org/lib --name pytest-run
amplifier-shadow exec pytest-run "
cd /workspace/org/lib &&
uv venv && . .venv/bin/activate &&
pip install -e '.[dev]' &&
pytest --cov=src --cov-report=html
"
# 提取覆盖率报告
amplifier-shadow extract pytest-run /workspace/org/lib/htmlcov ./coverage-report
amplifier-shadow create --local ~/repos/pkg:org/pkg --name npm-test
amplifier-shadow exec npm-test "
cd /workspace/org/pkg &&
npm install &&
npm test
"
amplifier-shadow create --local ~/repos/crate:org/crate --name cargo-test
amplifier-shadow exec cargo-test "
cd /workspace/org/crate &&
cargo build &&
cargo test
"
此技能遵循 amplihack 的核心原则:
# 创建影子环境
amplifier-shadow create [OPTIONS]
--local, -l TEXT 本地源映射:/path/to/repo:org/name(可重复)
--name, -n TEXT 环境名称(如未提供则自动生成)
--image, -i TEXT 容器镜像(默认:amplifier-shadow:local)
--env, -e TEXT 环境变量:KEY=VALUE 或 KEY 以继承(可重复)
--env-file FILE 包含环境变量的文件(每行一个)
--pass-api-keys 自动传递常见 API 密钥环境变量(默认:启用)
# 在影子中执行命令
amplifier-shadow exec SHADOW_ID COMMAND
--timeout INTEGER 超时时间(秒)(默认:300)
# 显示更改的文件
amplifier-shadow diff SHADOW_ID [PATH]
# 从影子中提取文件
amplifier-shadow extract SHADOW_ID CONTAINER_PATH HOST_PATH
# 将文件注入影子
amplifier-shadow inject SHADOW_ID HOST_PATH CONTAINER_PATH
# 列出所有影子
amplifier-shadow list
# 显示影子状态
amplifier-shadow status SHADOW_ID
# 销毁影子
amplifier-shadow destroy SHADOW_ID
--force 即使出错也强制销毁
# 销毁所有影子
amplifier-shadow destroy-all
--force 即使出错也强制销毁
# 本地构建影子镜像
amplifier-shadow build
# 打开交互式 shell
amplifier-shadow shell SHADOW_ID
# 典型工作流
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest"
amplifier-shadow destroy test
# 多仓库
amplifier-shadow create \
--local ~/repos/lib1:org/lib1 \
--local ~/repos/lib2:org/lib2 \
--name multi
# 带环境变量
amplifier-shadow create \
--local ~/repos/lib:org/lib \
--env API_KEY=$API_KEY \
--name test
# 交互式 shell
amplifier-shadow shell test
# 提取结果
amplifier-shadow extract test /workspace/results ./local-results
outside-in-testing)当影子测试失败时:
/workspace/{org}/{repo}env)git config --list)curl http://localhost:3000/api/v1/version)amplifier-shadow status)请记住:影子环境让您可以在干净、隔离且镜像 CI 的环境中,测试您机器上的精确内容(包括所有未提交的更改)。在每次重要推送前使用它们,以便及早发现问题。
每周安装次数
70
仓库
GitHub 星标数
45
首次出现
2026年2月7日
安全审计
已安装于
opencode68
codex67
cursor66
kimi-cli64
gemini-cli64
github-copilot64
Shadow testing creates isolated container environments where you can test local uncommitted changes without affecting your host system or pushing to remote repositories.
Key Principle : Test exactly what's on your machine (including uncommitted changes) in a clean, isolated environment that mirrors CI.
A shadow environment is a Docker/Podman container with:
insteadOf rules redirect specific repos to local Gitea┌─────────────────────────────────────────────────────────┐
│ Shadow Container │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Gitea Server (localhost:3000) │ │
│ │ - myorg/my-library (your snapshot) │ │
│ └───────────────────────────────────────────────────┘ │
│ │
│ Git URL Rewriting: │
│ github.com/myorg/my-library → Gitea (local) │
│ github.com/myorg/other-repo → Real GitHub │
│ │
│ /workspace (pre-cloned local sources) │
└─────────────────────────────────────────────────────────┘
When you create a shadow with ~/repos/my-lib:myorg/my-lib:
Your working directory is captured exactly as-is (uncommitted changes included)
Snapshot is bundled with full git history
Container starts with Gitea server
Snapshot pushed to Gitea as myorg/my-lib
Git config adds insteadOf rules:
[url "http://shadow:shadow@localhost:3000/myorg/my-lib.git"] insteadOf = https://github.com/myorg/my-lib.git
Any git clone https://github.com/myorg/my-lib → uses YOUR local snapshot
All other GitHub URLs → fetch from real GitHub
Result : Only your specified repos are local; everything else uses production sources.
For Amplifier Users (native integration):
# Shadow tool is built-in - no installation needed
amplifier run --bundle amplihack
For Other Agents (standalone CLI):
# Install via uvx (recommended)
uvx amplifier-shadow --version
# Or via pip
pip install amplifier-bundle-shadow
# Verify installation
amplifier-shadow --version
Prerequisites :
# Create shadow with your local library changes
amplifier-shadow create --local ~/repos/my-library:myorg/my-library --name test-lib
# Inside the shadow, install via git URL
# → my-library uses YOUR LOCAL snapshot
# → all other dependencies fetch from REAL GitHub
amplifier-shadow exec test-lib "uv pip install git+https://github.com/myorg/my-library"
# Run tests
amplifier-shadow exec test-lib "cd /workspace && pytest"
# See what changed
amplifier-shadow diff test-lib
# Clean up when done
amplifier-shadow destroy test-lib
# Create shadow with local changes
shadow.create(local_sources=["~/repos/my-library:myorg/my-library"])
# Execute commands
shadow.exec(shadow_id, "uv pip install git+https://github.com/myorg/my-library")
shadow.exec(shadow_id, "pytest tests/")
# Extract results
shadow.extract(shadow_id, "/workspace/test-results", "./results")
# Cleanup
shadow.destroy(shadow_id)
Best experience - shadow is a first-class tool with automatic setup:
# All operations via shadow tool
result = shadow.create(
local_sources=["~/repos/lib:org/lib"],
verify=True # Automatic smoke test
)
# Integrated error handling and observability
if result.ready:
shadow.exec(result.shadow_id, "pytest")
Features :
Use the CLI directly from bash tool:
# All operations via amplifier-shadow CLI
uvx amplifier-shadow create --local ~/repos/my-lib:org/my-lib --name test
uvx amplifier-shadow exec test "pip install -e /workspace/org/my-lib"
uvx amplifier-shadow exec test "pytest"
uvx amplifier-shadow destroy test
Same CLI interface as Claude Code:
# Install once
pip install amplifier-bundle-shadow
# Use in workflow
amplifier-shadow create --local ~/repos/lib:org/lib
amplifier-shadow exec shadow-xxx "npm install && npm test"
Use the provided shell scripts and Docker Compose examples (see Level 3).
# Test your library with its dependents
amplifier-shadow create --local ~/repos/my-library:myorg/my-library --name lib-test
# Clone dependent project and install
amplifier-shadow exec lib-test "
cd /workspace &&
git clone https://github.com/myorg/dependent-app &&
cd dependent-app &&
uv venv && . .venv/bin/activate &&
uv pip install git+https://github.com/myorg/my-library &&
pytest
"
# Testing changes across multiple repos
amplifier-shadow create \
--local ~/repos/core-lib:myorg/core-lib \
--local ~/repos/cli-tool:myorg/cli-tool \
--name multi-test
# Both local sources will be used
amplifier-shadow exec multi-test "uv pip install git+https://github.com/myorg/cli-tool"
# 1. Create shadow and run tests
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest" # Fails
# 2. Fix code locally on host
# 3. Destroy and recreate (picks up your local changes)
amplifier-shadow destroy test
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest" # Passes
# 4. Commit with confidence!
git commit -m "Fix issue"
# Run your CI script in shadow before pushing
amplifier-shadow create --local ~/repos/project:org/project --name ci-check
amplifier-shadow exec ci-check "
cd /workspace/org/project &&
./scripts/ci.sh
"
# If CI script passes, your push will likely succeed
After creating a shadow, confirm your local code is actually being used:
# Step 1: Check snapshot commits (from create output)
amplifier-shadow create --local ~/repos/lib:org/lib
# Output shows: snapshot_commits: {"org/lib": "abc1234..."}
# Step 2: Compare with install output
amplifier-shadow exec shadow-xxx "uv pip install git+https://github.com/org/lib"
# Look for: lib @ git+...@abc1234
# If commits match, your local code is being used!
Local sources are automatically cloned to /workspace/{org}/{repo}:
# Your local source microsoft/my-library is available at:
/workspace/microsoft/my-library
# Use for editable installs (Python)
amplifier-shadow exec shadow-xxx "pip install -e /workspace/microsoft/my-library"
# Or for Node.js
amplifier-shadow exec shadow-xxx "cd /workspace/microsoft/my-package && npm install"
Always check this location first - the repo is already there.
# Don't assume - verify API keys are present!
amplifier-shadow exec shadow-xxx "env | grep API_KEY"
# Check all passed variables
amplifier-shadow status shadow-xxx
# Shows: env_vars_passed: ["ANTHROPIC_API_KEY", ...]
"UV tool install" uses cache instead of local source :
Problem: UV may bypass git URL rewriting for cached packages.
Solution:
# Option 1: Install from pre-cloned workspace (recommended)
amplifier-shadow exec xxx "pip install -e /workspace/org/lib"
# Option 2: Clear UV cache first
amplifier-shadow exec xxx "rm -rf /tmp/uv-cache && uv tool install git+https://github.com/org/lib"
"PEP 668: Externally-Managed Environment" :
Solution: Always use virtual environments inside shadow:
amplifier-shadow exec xxx "
cd /workspace &&
uv venv &&
. .venv/bin/activate &&
uv pip install ...
"
"Container image not found" :
Solution: Build the image locally:
amplifier-shadow build
"/workspace permission denied" :
Solution: Use $HOME or /tmp as alternatives:
amplifier-shadow exec xxx "cd $HOME && git clone ..."
Build your own shadow image with additional tools:
FROM ghcr.io/microsoft/amplifier-shadow:latest
# Add your tools
RUN apt-get update && apt-get install -y \
postgresql-client \
redis-tools
# Add custom scripts
COPY my-test-script.sh /usr/local/bin/
Build and use:
docker build -t my-shadow:latest .
amplifier-shadow create --image my-shadow:latest --local ~/repos/lib:org/lib
For agents without Amplifier access, use these standalone scripts:
Script 1: Create Git Bundle (scripts/create-bundle.sh):
#!/bin/bash
# Create git bundle snapshot of working tree
REPO_PATH=$1
OUTPUT_PATH=$2
cd "$REPO_PATH"
# Fetch all refs to ensure complete history
git fetch --all --tags --quiet 2>/dev/null || true
# Check for uncommitted changes
if [[ -n $(git status --porcelain) ]]; then
# Create temp clone and commit changes
TEMP_DIR=$(mktemp -d)
git clone --quiet "$REPO_PATH" "$TEMP_DIR"
# Sync working tree (including deletions)
rsync -a --delete --exclude='.git' "$REPO_PATH/" "$TEMP_DIR/"
cd "$TEMP_DIR"
git add -A
git commit --allow-empty -m "Shadow snapshot" --author="Shadow <shadow@localhost>"
# Create bundle
git bundle create "$OUTPUT_PATH" --all
cd /
rm -rf "$TEMP_DIR"
else
# Clean repo - just bundle it
git bundle create "$OUTPUT_PATH" --all
fi
echo "Bundle created: $OUTPUT_PATH"
Script 2: Setup Shadow Container (scripts/setup-shadow.sh):
#!/bin/bash
# Start container with Gitea and configure git URL rewriting
CONTAINER_NAME=$1
BUNDLE_PATH=$2
ORG=$3
REPO=$4
# Start container
docker run -d \
--name "$CONTAINER_NAME" \
-v "$BUNDLE_PATH:/snapshots/bundle.git:ro" \
ghcr.io/microsoft/amplifier-shadow:latest
# Wait for Gitea
echo "Waiting for Gitea to start..."
until docker exec "$CONTAINER_NAME" curl -sf http://localhost:3000/api/v1/version > /dev/null; do
sleep 1
done
# Create org and repo in Gitea
docker exec "$CONTAINER_NAME" bash -c "
curl -s -u shadow:shadow \
-H 'Content-Type: application/json' \
-d '{\"username\":\"$ORG\"}' \
http://localhost:3000/api/v1/orgs
curl -s -u shadow:shadow \
-H 'Content-Type: application/json' \
-d '{\"name\":\"$REPO\",\"private\":false}' \
http://localhost:3000/api/v1/orgs/$ORG/repos
"
# Push bundle to Gitea
docker exec "$CONTAINER_NAME" bash -c "
cd /tmp &&
git init --bare repo.git &&
cd repo.git &&
git fetch /snapshots/bundle.git refs/heads/*:refs/heads/* &&
git remote add origin http://shadow:shadow@localhost:3000/$ORG/$REPO.git &&
git push origin --all --force
"
# Configure git URL rewriting
docker exec "$CONTAINER_NAME" bash -c "
git config --global url.'http://shadow:shadow@localhost:3000/$ORG/$REPO.git'.insteadOf 'https://github.com/$ORG/$REPO.git'
"
echo "Shadow container ready: $CONTAINER_NAME"
echo "Local source: $ORG/$REPO"
Usage :
# Create bundle from your repo
./scripts/create-bundle.sh ~/repos/my-lib /tmp/my-lib.bundle
# Setup shadow container
./scripts/setup-shadow.sh shadow-test /tmp/my-lib.bundle myorg my-lib
# Test
docker exec shadow-test bash -c "
git clone https://github.com/myorg/my-lib /tmp/test &&
cd /tmp/test &&
git log -1 --oneline
"
Example 1: Single Repository (docker-compose/single-repo.yml):
version: "3.8"
services:
shadow:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: shadow-single
volumes:
- ./snapshots:/snapshots:ro
- ./workspace:/workspace
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
tail -f /dev/null
"
Example 2: Multi-Repository Testing (docker-compose/multi-repo.yml):
version: "3.8"
services:
shadow-multi:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: shadow-multi
volumes:
# Mount multiple bundles
- ./snapshots/core-lib.bundle:/snapshots/org/core-lib.bundle:ro
- ./snapshots/cli-tool.bundle:/snapshots/org/cli-tool.bundle:ro
- ./workspace:/workspace
environment:
# Pass API keys from host
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
# UV cache isolation
- UV_CACHE_DIR=/tmp/uv-cache
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
/usr/local/bin/setup-repos.sh org/core-lib org/cli-tool &&
tail -f /dev/null
"
Usage :
# Create bundles for your repos
git -C ~/repos/core-lib bundle create snapshots/core-lib.bundle --all
git -C ~/repos/cli-tool bundle create snapshots/cli-tool.bundle --all
# Start shadow
docker-compose -f docker-compose/multi-repo.yml up -d
# Run tests
docker-compose exec shadow-multi bash -c "
cd /workspace &&
git clone https://github.com/org/cli-tool &&
cd cli-tool &&
uv pip install -e .
pytest
"
# Cleanup
docker-compose down
Example 3: CI Integration (docker-compose/ci-shadow.yml):
version: "3.8"
services:
ci-shadow:
image: ghcr.io/microsoft/amplifier-shadow:latest
container_name: ci-shadow
volumes:
- ./snapshots:/snapshots:ro
- ./test-results:/test-results
environment:
- CI=true
- GITHUB_ACTIONS=true
command: >
bash -c "
/usr/local/bin/gitea-init.sh &&
/usr/local/bin/run-ci-tests.sh > /test-results/output.log 2>&1
"
GitHub Actions Integration :
# .github/workflows/shadow-test.yml
name: Shadow Test
on: [push, pull_request]
jobs:
shadow-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Create git bundle
run: git bundle create snapshot.bundle --all
- name: Run shadow tests
run: |
docker run --rm \
-v $PWD/snapshot.bundle:/snapshots/bundle.git:ro \
ghcr.io/microsoft/amplifier-shadow:latest \
/usr/local/bin/test-in-shadow.sh org/repo
Combine shadow environments with agentic outside-in tests:
# Create shadow with local changes
amplifier-shadow create --local ~/repos/lib:org/lib --name test
# Run outside-in test scenarios inside shadow
amplifier-shadow exec test "gadugi-agentic-test run test-scenario.yaml"
# Extract evidence
amplifier-shadow extract test /evidence ./test-evidence
See the qa-team skill for complete integration examples (outside-in-testing remains an alias).
Don't assume - verify that the shadow is actually using your local code:
# Check snapshot commits
amplifier-shadow status shadow-xxx | grep snapshot_commit
# Verify install resolves to that commit
amplifier-shadow exec shadow-xxx "pip install git+https://github.com/org/lib" | grep "org/lib @"
Local sources are automatically at /workspace/{org}/{repo}:
# ✅ FAST: Use pre-cloned repo
amplifier-shadow exec xxx "pip install -e /workspace/org/lib"
# ❌ SLOWER: Clone again
amplifier-shadow exec xxx "git clone https://github.com/org/lib && pip install -e lib"
Shadow environments automatically isolate caches to prevent stale packages:
/tmp/uv-cache/tmp/pip-cache/tmp/npm-cache/tmp/cargo-home/tmp/go-mod-cacheThese are set automatically - no action needed.
# Amplifier (automatic for common API keys)
shadow.create(local_sources=["~/repos/lib:org/lib"])
# CLI (explicit)
amplifier-shadow create \
--local ~/repos/lib:org/lib \
--env ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
--env CUSTOM_VAR=value
# Always destroy shadows when done
amplifier-shadow destroy shadow-xxx
# Or destroy all
amplifier-shadow destroy-all
# ✅ GOOD: Descriptive name
amplifier-shadow create --local ~/repos/lib:org/lib --name test-breaking-change
# ❌ BAD: Auto-generated
amplifier-shadow create --local ~/repos/lib:org/lib
# Creates shadow-a3f2b8c1 (hard to remember)
Combine shadow isolation with declarative test scenarios:
# test-scenario.yaml
scenario:
name: "Library Integration Test"
type: cli
steps:
- action: launch
target: "/workspace/org/lib/cli.py"
- action: verify_output
contains: "Success"
Run in shadow:
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "gadugi-agentic-test run test-scenario.yaml"
amplifier-shadow create --local ~/repos/lib:org/lib --name pytest-run
amplifier-shadow exec pytest-run "
cd /workspace/org/lib &&
uv venv && . .venv/bin/activate &&
pip install -e '.[dev]' &&
pytest --cov=src --cov-report=html
"
# Extract coverage report
amplifier-shadow extract pytest-run /workspace/org/lib/htmlcov ./coverage-report
amplifier-shadow create --local ~/repos/pkg:org/pkg --name npm-test
amplifier-shadow exec npm-test "
cd /workspace/org/pkg &&
npm install &&
npm test
"
amplifier-shadow create --local ~/repos/crate:org/crate --name cargo-test
amplifier-shadow exec cargo-test "
cd /workspace/org/crate &&
cargo build &&
cargo test
"
This skill follows amplihack's core principles:
# Create shadow environment
amplifier-shadow create [OPTIONS]
--local, -l TEXT Local source mapping: /path/to/repo:org/name (repeatable)
--name, -n TEXT Name for environment (auto-generated if not provided)
--image, -i TEXT Container image (default: amplifier-shadow:local)
--env, -e TEXT Environment variable: KEY=VALUE or KEY to inherit (repeatable)
--env-file FILE File with environment variables (one per line)
--pass-api-keys Auto-pass common API key env vars (default: enabled)
# Execute command in shadow
amplifier-shadow exec SHADOW_ID COMMAND
--timeout INTEGER Timeout in seconds (default: 300)
# Show changed files
amplifier-shadow diff SHADOW_ID [PATH]
# Extract file from shadow
amplifier-shadow extract SHADOW_ID CONTAINER_PATH HOST_PATH
# Inject file into shadow
amplifier-shadow inject SHADOW_ID HOST_PATH CONTAINER_PATH
# List all shadows
amplifier-shadow list
# Show shadow status
amplifier-shadow status SHADOW_ID
# Destroy shadow
amplifier-shadow destroy SHADOW_ID
--force Force destruction even on errors
# Destroy all shadows
amplifier-shadow destroy-all
--force Force destruction even on errors
# Build shadow image locally
amplifier-shadow build
# Open interactive shell
amplifier-shadow shell SHADOW_ID
# Typical workflow
amplifier-shadow create --local ~/repos/lib:org/lib --name test
amplifier-shadow exec test "pytest"
amplifier-shadow destroy test
# Multi-repo
amplifier-shadow create \
--local ~/repos/lib1:org/lib1 \
--local ~/repos/lib2:org/lib2 \
--name multi
# With environment variables
amplifier-shadow create \
--local ~/repos/lib:org/lib \
--env API_KEY=$API_KEY \
--name test
# Interactive shell
amplifier-shadow shell test
# Extract results
amplifier-shadow extract test /workspace/results ./local-results
outside-in-testing)When shadow tests fail:
/workspace/{org}/{repo}env inside shadow)git config --list)curl http://localhost:3000/api/v1/version)amplifier-shadow status)Remember : Shadow environments let you test exactly what's on your machine (uncommitted changes and all) in a clean, isolated environment that mirrors CI. Use them before every significant push to catch issues early.
Weekly Installs
70
Repository
GitHub Stars
45
First Seen
Feb 7, 2026
Security Audits
Installed on
opencode68
codex67
cursor66
kimi-cli64
gemini-cli64
github-copilot64
Azure 升级评估与自动化工具 - 轻松迁移 Functions 计划、托管层级和 SKU
111,700 周安装