PostHog 无 flox 环境后端测试搭建指南:Claude Code for web 中从零运行 pytest 的完整方案
本篇指南面向需要在 Claude Code for web(或其他没有 flox 的开发容器)中运行 PostHog 后端测试的开发者,核心解决 uv sync 因 Python 版本不匹配而失败的问题。读完你将掌握:如何从 python-build-standalone 下载与 pyproject.toml 锁定版本一致的 Python 解释器、如何完成 uv sync 依赖安装、如何启动 Docker 测试依赖服务、如何配置 /etc/hosts 与测试环境变量,以及如何用 hogli test / pytest 精确运行单个测试或整个目录。文中所有步骤均已对照当前仓库的 pyproject.toml、pytest.ini、.github/workflows/ci-backend.yml 等真实配置逐一验证。
问题背景:为什么 uv sync 会在受限环境中失败
PostHog 在 pyproject.toml 中通过 requires-python = "==3.13.13" 将 Python 版本精确锁定到完整的 主版本.次版本.补丁版本,注释明确写道:"Pin full version to control Python upgrades explicitly"(显式固定完整版本以控制 Python 升级)。这一策略带来三个连锁约束:
- 系统 Python 版本不匹配:Claude Code for web 等托管容器中预装的系统 Python 往往是通用版本,几乎不可能恰好是 3.13.13。
uv python install可能失效:uv sync依赖 uv 的 Python 索引,当所需版本尚未被 uv 收录(例如版本过新)时,uv python install <version>会直接失败。uv sync强制执行精确约束:uv 会严格校验解释器版本与requires-python的一致性,只要系统 Python 不是 3.13.13,uv sync就会报错退出,导致整个测试环境无法建立。
因此,问题的本质是:在这个仓库里,"找到恰好 3.13.13 的解释器" 是运行任何后端测试的前置条件。这也解释了为什么仓库的 CI(.github/workflows/ci-backend.yml)中所有测试 Job 都通过 setup-python-cached action 显式安装 python-version: 3.13.13,例如 product 测试矩阵中的 "Set up Python" 步骤。本地与 CI 使用完全一致的解释器版本,是保证测试行为一致的前提。
解决方案:从 python-build-standalone 下载精确版本的 Python
由于没有 flox(PostHog 团队常用的环境管理工具)可用,替代方案是直接从 astral-sh/python-build-standalone 的 GitHub Releases 下载与 pyproject.toml 精确匹配的 CPython 独立构建(install_only 精简包)。下面的脚本会自动完成版本探测、Release 查询、匹配与下载解压的全过程:
# Auto-detect the required version from pyproject.toml
REQUIRED_VERSION=$(grep requires-python pyproject.toml | grep -oP '[\d.]+')
echo "Required Python: $REQUIRED_VERSION"
# Get the latest release tag from python-build-standalone
RELEASE_TAG=$(curl -sL "https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest" | grep '"tag_name"' | cut -d'"' -f4)
# Find and download the matching build
DOWNLOAD_URL=$(curl -sL "https://api.github.com/repos/astral-sh/python-build-standalone/releases/latest" | \
grep "browser_download_url" | grep "$REQUIRED_VERSION" | grep "x86_64-unknown-linux-gnu-install_only.tar.gz" | head -1 | cut -d'"' -f4)
mkdir -p /tmp/python-install && cd /tmp/python-install
curl -L -o python.tar.gz "$DOWNLOAD_URL"
tar -xzf python.tar.gz
# Verify
/tmp/python-install/python/bin/python3 --version
脚本各步骤的说明:
- 版本探测:
grep requires-python pyproject.toml提取pyproject.toml中的requires-python行(当前为==3.13.13),再用正则[\d.]+抽出3.13.13这一精确版本号,保证与锁定版本零偏差。 - Release 查询:调用 GitHub API 获取 python-build-standalone 的最新 Release 元数据。
- 资产匹配:从
browser_download_url列表中筛选同时满足"包含目标版本号"与"x86_64-unknown-linux-gnu-install_only.tar.gz后缀"的资产,这正是 x86_64 Linux 上的精简安装包。 - 验证:下载解压后执行
python3 --version确认解释器可用。
手动兜底:如果自动探测没有找到匹配的 URL(例如锁定版本太新、尚未发布对应构建),可以打开 python-build-standalone 的 Releases 页面手动查找形如
cpython-<version>+<tag>-x86_64-unknown-linux-gnu-install_only.tar.gz的资产,把 URL 填入DOWNLOAD_URL后重跑下载解压部分即可。本仓库当前锁定 3.13.13,通常能直接命中。
安装依赖并运行测试
拿到正确的解释器后,用 --python 参数让 uv 使用该解释器完成同步,然后激活虚拟环境并运行测试:
cd /home/user/posthog
uv sync --python /tmp/python-install/python/bin/python3
source .venv/bin/activate
# Run a specific test (if hogli is available)
hogli test path/to/test.py::TestClass::test_method -v
# Or use pytest directly
pytest path/to/test.py::TestClass::test_method -v
# Run all tests in a directory
hogli test posthog/hogql/test/ -v
几点实践细节:
uv sync --python <path>显式指定解释器路径,绕过了 uv 自带的 Python 下载/发现逻辑,直接把 python-build-standalone 的构建作为项目解释器;该命令同时会依据 pyproject.toml 的[dependency-groups]安装 dev 依赖组(pytest~=8.4.2、pytest-django~=4.14.0、hogli等均在 dev 组中)。hogli是 PostHog 的开发者 CLI(声明于[dependency-groups] dev中,工作区成员见 pyproject.toml 的[tool.uv.workspace]:members = ["tools/hogli", "tools/owners"])。如果hogli不可用,直接用pytest是等价的替代路径。- 运行整目录测试时,posthog/hogql 是典型的可执行用例,
hogli test posthog/hogql/test/ -v会递归收集该目录下所有测试。 - CI 中的等价做法(.github/workflows/ci-backend.yml)是
UV_PROJECT_ENVIRONMENT=$pythonLocation uv sync --frozen --dev,其中--frozen表示严格按uv.lock锁定文件安装,不重新解析依赖——本地如果希望与 CI 完全一致,也可以加上--frozen。
Docker 服务:大多数测试的前置依赖
PostHog 的后端测试大量依赖外部中间件(Kafka、ClickHouse、Redis、对象存储、Temporal 等)。如果环境中有 Docker,用以下命令一次性拉起测试所需的全部服务:
docker compose -f docker-compose.dev.yml up -d
服务清单以 docker-compose.dev.yml 为准(该文件头部注释即写明 "docker-compose file used ONLY for local development")。从文件的服务定义可以看到完整的测试依赖栈:
- proxy:Caddy 反向代理,将
/e、/batch、/i/v0/*等路径路由到 capture 等服务(docker-compose.dev.yml)。 - capture / capture-ai / capture-logs / capture-apm-metrics:PostHog 的 Rust 事件采集服务(
image: ghcr.io/posthog/posthog/capture:${POSTHOG_CAPTURE_TAG:-master})。 - posthog-node 系列:Node 侧能力服务(
ghcr.io/posthog/posthog-node:${POSTHOG_NODE_TAG:-master})。 - browserless:Chromium 容器(
ghcr.io/browserless/chromium:v2.51.2),用于热图截图与图片导出类测试。 - valkey / redis:缓存与消息队列(
valkey/valkey:8.1-alpine与redis:7.2)。 - redpanda:Kafka 协议兼容的消息代理(
docker.io/redpandadata/redpanda:v25.1.9),测试中的kafka主机名即指向它。 - webhook-tester / curl / otel-collector-local / jaeger-local / dynamodb-main / seaweedfs-main / etcd 等辅助服务。
部分测试目录还有自己特殊的服务要求,通常记录在各测试目录自身的配置文件或对应 CI 中,例如 ci-backend.yml 中 COMPOSE_PROFILES: temporal,azure 表明 Temporal 与 Azure 相关服务通过 profile 按需启用。
Hosts 文件配置
测试代码中会以固定的主机名访问上述服务,因此这些主机名必须解析到 localhost:
echo "127.0.0.1 kafka clickhouse clickhouse-coordinator objectstorage" | sudo tee -a /etc/hosts
这一行为与 CI 完全一致:在 .github/workflows/ci-backend.yml 中可以看到 CI 执行的是 echo "127.0.0.1 db redis7 kafka clickhouse clickhouse-coordinator objectstorage temporal" | sudo tee -a /etc/hosts——本地环境可以按需对照补充 db、redis7、temporal 等主机名,具体以你要运行的测试所连接的服务为准。
环境变量:对齐 CI 与本地默认值
测试运行所需的绝大多数环境变量都定义在 .github/workflows/ci-backend.yml 顶部的 env: 区块。以下是当前仓库 CI 中实际设置的关键变量(可作为本地测试的最小可运行参考集):
| 变量 | 值(CI) | 用途 |
|---|---|---|
SECRET_KEY |
'6b01eee4...'(测试专用假密钥) |
Django 签名密钥,仅测试用 |
DATABASE_URL |
postgres://posthog:posthog@localhost:5432/posthog |
PostgreSQL 连接串 |
REDIS_URL |
redis://localhost |
Redis 连接 |
CLICKHOUSE_HOST / CLICKHOUSE_SECURE / CLICKHOUSE_VERIFY |
localhost / False / False |
ClickHouse 连接与 TLS 开关 |
CLICKHOUSE_TEST_CLUSTER_HOST / _DATABASE / _USER / _PASSWORD |
localhost / posthog_test / autoresearch / autoresearchpass |
测试专用 ClickHouse 集群(用户定义于 docker/clickhouse/users-dev.xml) |
TEST |
1 |
测试模式标志 |
OBJECT_STORAGE_ENABLED / _ENDPOINT / _ACCESS_KEY_ID / _SECRET_ACCESS_KEY |
True / http://localhost:19000 / object_storage_root_user / object_storage_root_password |
对象存储(SeaweedFS)配置 |
DISPLAY |
':99.0' |
无头显示(CI 用于规避退出码 134 的偶发问题) |
OIDC_RSA_PRIVATE_KEY / SANDBOX_JWT_PRIVATE_KEY |
假密钥 | OIDC / 沙箱 JWT 签名 |
此外,可以复制 .env.example 为 .env 获取本地开发默认值。需要特别留意的是,.env.example 头部有醒目警告:其中包含的 OIDC_RSA_PRIVATE_KEY、SANDBOX_JWT_PRIVATE_KEY 是公开已知的、仅限本地开发的假密钥,任何人拿到它都可以在未保护环境下伪造合法 token,严禁在任意生产环境复用。
附加设置:前端产物占位与 SAML 依赖
前端 dist 占位文件
部分测试(例如涉及模板渲染、URL 反转的 Django 测试)要求 frontend/dist 下存在构建产物文件,即便内容为空也可以。这与 CI 的做法(.github/workflows/ci-backend.yml 中的 "Set up needed files" 步骤)一致:
mkdir -p frontend/dist
touch frontend/dist/index.html
touch frontend/dist/layout.html
touch frontend/dist/exporter.html
SAML 依赖(按需)
仅当要运行与 SAML 单点登录相关的功能(涉及 python3-saml、xmlsec,二者均为 pyproject.toml 的运行时依赖)时才需要安装系统级 XML 安全库。CI 中的安装命令为(.github/workflows/ci-backend.yml):
sudo apt-get update
sudo apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl
pytest 配置:pythonpath、Django 与默认忽略项
测试行为由 pytest.ini 统一控制,其关键配置如下:
pythonpath = . common tools/hogli-commands tools/owners tools/query-performance-ai:将仓库根目录、common以及多个工具目录加入模块搜索路径,保证posthog、ee、products等顶层包可被导入。env = DEBUG=1, TEST=1:pytest 启动时自动注入DEBUG=1与TEST=1两个环境变量。DJANGO_SETTINGS_MODULE = posthog.settings:指定 Django 设置模块,pytest-django 据此完成django.setup()。- 默认忽略项:
addopts中通过--ignore=排除了posthog/user_scripts、services/llm-gateway、services/stripe-mock、common/ingestion/acceptance_tests、tools/hogli、tools/hogli-commands、tools/owners、tools/traffic-sim、tools/query-performance-ai、products/desktop、products/stamphog/packages等目录,这些目录要么是独立维护的测试套件,要么不适合进入主 Django 测试流程。 - 其他值得了解的 addopts:
--reuse-db(复用测试数据库,加速多次运行)、--pytest-durations=0(默认关闭耗时统计插件)、-p pytest_boot_gc(在 pytest-django 执行django.setup()前开启引导期 GC 窗口),以及-p no:warnings -p no:tach -p no:langsmith_plugin -p no:faker等插件禁用项(各禁用项均有注释说明性能原因)。 - markers 与 asyncio:定义了
ee、clickhouse_only、skip_on_multitenancy、async_migrations、requires_secrets、quarantine、persons_db_direct等标记;asyncio_mode = auto使 async 测试自动按 asyncio 运行。
排错指南:以 CI 配置为权威参照
如果测试环境搭建遇到问题,第一参照物是 .github/workflows/ci-backend.yml——它是 PostHog 后端测试在 CI 中的权威配置来源,从中可以查到:
- CI 使用的精确 Python 版本:
3.13.13(与pyproject.toml的requires-python完全一致),通过setup-python-cached安装。 - 系统依赖:
libxml2-dev libxmlsec1-dev libxmlsec1-openssl(SAML 相关)、sqlx-cli(Rust 迁移工具)等。 - 环境变量全集:上文表格中列出的全部
env:项。 - Docker 服务配置:
COMPOSE_FILE: docker-compose.dev.yml:docker-compose.profiles.yml、COMPOSE_PROFILES: temporal,azure,以及bin/ci-wait-for-docker launch --background --down db redis7 clickhouse zookeeper kafka objectstorage temporal elasticsearch objectstorage-azure的服务启动清单。 - 测试执行命令:各 Job 中
UV_PROJECT_ENVIRONMENT=$pythonLocation uv sync --frozen --dev之后执行的 pytest 命令(含--reuse-db、junit 输出、pytest-split 分片等参数)。
排查顺序建议:先对照 pyproject.toml 确认解释器版本 → 用 python3 --version 验证下载的 Python → uv sync 是否成功 → Docker 服务是否健康(docker compose ps)→ hosts 是否包含测试所需主机名 → 环境变量是否齐全 → 最后再看 pytest 报错本身。
已知限制
在 Claude Code for web 这类受限环境中搭建测试环境,需要清醒认识以下边界:
- Docker 不可用:若环境未提供 Docker,所有依赖外部服务的测试(Kafka、ClickHouse、Redis 等)都无法运行,只能执行不触碰外部中间件的纯单元测试。
- 网络限制:从 python-build-standalone 下载 Python、从 Docker Hub 拉取镜像、从 PyPI 安装依赖都要求能访问外网;网络受限时整个流程无法进行。
- Temporal 相关测试:需要额外的 Temporal 服务搭建(对应 CI 中
COMPOSE_PROFILES: temporal的 profile 与python manage.py register_temporal_search_attributes等步骤),默认的 dev compose 栈不足以支撑这类测试。
若在按本指南操作时卡住,建议把遇到的问题连同报错信息反馈给用户,并推动更新 .agents/skills/setup-web-tests/SKILL.md 本身,让这份指南随环境演进持续完善。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00