如何在GitLab CI/CD中正确缓存Poetry虚拟环境?
问题描述
我需要在GitLab CI/CD的Python应用构建之间缓存Poetry虚拟环境,已配置流水线仅在lego/pyproject.toml变更时重建虚拟环境(未提交poetry.lock),当前缓存配置如下:
default: cache: - key: files: - lego/pyproject.toml paths: - lego/.venv - artifacts/build/layer/build/.venv - paths: - artifacts/pypoetry
创建虚拟环境的命令为:
poetry config virtualenvs.in-project true poetry install
本地测试正常,但第二次CI流水线运行时出现错误:
$ LOG_LEVEL=ERROR poetry run pytest tests -n auto -v --cov=./src --junitxml=lego-report.xml ImportError while loading conftest '/builds/5565557/lddpro-bff/lego/tests/conftest.py'. tests/conftest.py:22: in <module> from lego.common.aws import S3, EventBridge, SSM, Secrets E ModuleNotFoundError: No module named 'lego'
排查发现,虚拟环境.venv中存在根包的lego.pth文件,但文件内的路径为上一次Job的绝对路径:
$ cat .venv/lib/python*/site-packages/lego.pth /builds/5565557/lddpro-bff/lego/src
由于GitLab CI每次Job的工作目录路径可能因Job ID不同而变化,缓存的虚拟环境中.pth文件的绝对路径与当前Job路径不匹配,导致pytest无法找到根包。
解决方案
方案1:提交poetry.lock并优化缓存键
提交poetry.lock到仓库,不仅能确保依赖版本一致性,还能让缓存键更精准(只有依赖配置变更时才重建缓存),同时Poetry在恢复缓存后的安装步骤中会自动处理路径问题。
修改缓存配置:
default: cache: - key: files: - lego/pyproject.toml - lego/poetry.lock paths: - lego/.venv - artifacts/build/layer/build/.venv - paths: - artifacts/pypoetry
方案2:缓存恢复后更新根包路径
无论缓存是否存在,在poetry install前添加路径更新逻辑。由于Poetry的可编辑安装依赖绝对路径,缓存恢复后只需重新触发根包的链接即可,无需重新安装所有依赖。
修改Job的script部分:
script: - cd lego - poetry config virtualenvs.in-project true # 缓存存在时仅更新根包路径,不存在则完整安装 - if [ -d .venv ]; then poetry install --no-root; else poetry install; fi - poetry env info - env LOG_LEVEL=ERROR poetry run pytest tests -n auto -v --cov=./src --junitxml=${REPORT_NAME} - mv -f ${REPORT_NAME} ${CI_PROJECT_DIR}/ || echo "" - cd ${CI_PROJECT_DIR} - mkdir -p artifacts - cd tools - . ./build_deploy.sh --command=build --debug
注:
poetry install --no-root会跳过根包的安装,但会更新可编辑安装的路径配置,比完整安装更快。如果不确定依赖是否有变更,直接执行poetry install也可,Poetry会自动校验依赖并仅更新必要部分。
方案3:手动修正.pth文件路径(临时hack)
如果无法提交poetry.lock,可在缓存恢复后手动替换.pth文件中的绝对路径为当前Job的路径:
修改script部分:
script: - cd lego - poetry config virtualenvs.in-project true # 若缓存存在,修正.pth文件中的路径 - if [ -d .venv ]; then CURRENT_SRC=$(realpath src) find .venv/lib/python*/site-packages -name "*.pth" -exec sed -i "s|^/builds/.*lego/src|$CURRENT_SRC|" {} \; fi - poetry install - poetry env info - env LOG_LEVEL=ERROR poetry run pytest tests -n auto -v --cov=./src --junitxml=${REPORT_NAME} # 后续命令不变
内容的提问来源于stack exchange,提问作者Shuzheng
相关产品推荐
相关产品推荐

