news 2026/7/21 22:01:40

【Bug已解决】CI fails for code quality check: No such file or directory: ‘ruff‘ 解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Bug已解决】CI fails for code quality check: No such file or directory: ‘ruff‘ 解决方案

【Bug已解决】CI fails for code quality check: No such file or directory: 'ruff' 解决方案

一、现象长什么样

仓库的 CI 里有一条代码质量检查(lint),调用ruff做静态检查。某次改动后,这条 CI 直接挂:

/bin/sh: 1: ruff: not found No such file or directory: 'ruff'

或者更隐蔽地(用subprocess.run(["ruff", ...])时):

FileNotFoundError: [Errno 2] No such file or directory: 'ruff'

现象特征:

  • 只在 CI 失败,本地ruff跑得好好的——因为本地你用pipx/pip install ruff装过,PATH 里有;CI 的干净环境里没装;
  • 有时是"偶发":CI 用了缓存或某个 base image 里刚好有 ruff 的旧版本,换了 runner 后又没了;
  • 报错发生在"执行 ruff"这一步,说明调用命令本身没问题,是运行环境里找不到这个可执行文件

这是典型的"把 CLI 工具当成环境自带"的 CI 配置缺漏:脚本假定ruff一定在 PATH 上,但没保证它真的被安装。

二、背景

ruff是 Python 的 lint/格式化工具,有两种常见调用方式:

  1. 直接当可执行ruff check ./ruff format --check .——要求ruff在 PATH 上;
  2. 作为 Python 模块python -m ruff check .——要求ruff作为包被安装到当前 Python 环境。

CI 里常见两种失败原因:

  • 依赖没装:CI 的requirements/pyproject的 dev 依赖里没列ruff,runner 干净环境就没有它;
  • 用了可执行但装成了模块:只pip install ruff(装成模块,python -m ruff可用),脚本却写ruff(依赖 PATH 上的可执行),而pip install不一定把可执行链接进 PATH(取决于环境/隔离),于是ruff找不到;
  • 版本漂移:本地 ruff 新版,CI 旧版或反之,规则不一致,甚至某些规则名变了导致"找不到规则"式报错——但本题是更直接的"文件不存在"。

核心问题:CI 把ruff的存在当成前提,却没在 CI 流程里显式保证它存在

三、根因

根因一句话:CI 的代码质量检查脚本直接调用ruff可执行文件,但 CI 环境没有安装/暴露ruff(既没列进依赖,也没用python -m ruff这种"依赖已安装包"的方式调用),导致执行时FileNotFoundError: ruff

具体:

  1. 依赖缺口ruff不在 CI 安装的依赖清单里(或只在本地 dev 环境,没进 CI 用的 lockfile);
  2. 调用方式脆弱:脚本写死ruff ...依赖 PATH 可执行,而不是python -m ruff ...(后者只要包装了就可用,不依赖 PATH 链接);
  3. 环境不可复现:本地有、CI 没有,因为本地和 CI 的依赖来源不同步;
  4. 报错早、阻塞大:lint 是门禁,ruff 找不到直接整条质量检查红,连带可能阻断合并。

本质是"工具可用性没在 CI 里被显式保证"。

四、最小可运行复现

下面用纯 Python 模拟"可执行不存在"的两种调用方式的差异:

import subprocess import sys def run_as_executable(): """旧方式:直接调 ruff 可执行,依赖 PATH。""" try: return subprocess.run(["ruff", "--version"], check=True, capture_output=True) except FileNotFoundError as e: return f"失败: {e}" def run_as_module(): """更稳方式:python -m ruff,依赖包已安装。""" try: return subprocess.run([sys.executable, "-m", "ruff", "--version"], check=True, capture_output=True) except FileNotFoundError as e: return f"失败: {e}(说明包也没装)" def demo(): print("直接可执行:", run_as_executable()) print("python -m:", run_as_module()) if __name__ == "__main__": demo()

如果环境里ruff没装:第一行抛FileNotFoundError: ruff(和线上一致);第二行如果包装了也会成功,没装才失败。复现了"直接调可执行在干净环境里找不到"的核心问题,也提示了修复方向——用python -m ruff或确保安装。

五、解决方案(第一层):保证 CI 安装 ruff(进依赖清单)

第一层最稳妥:把ruff显式加进 CI 用的依赖,保证它一定存在:

# pyproject.toml 的 dev 依赖(或 requirements-dev.txt) [project.optional-dependencies] dev = [ "ruff>=0.5.0", # 显式固定下限,避免版本漂移 ]

CI 步骤里先装:

# .github/workflows/quality.yml - name: Install dev deps run: pip install -e ".[dev]" # 或 pip install ruff - name: Lint run: ruff check .

或者最小改动,单独装:

- name: Install ruff run: pip install "ruff>=0.5.0" - name: Lint run: ruff check .

关键是:ruff 不再依赖"本地恰好装过",而是 CI 自己安装,环境可复现。

六、解决方案(第二层):改用python -m ruff,去掉 PATH 依赖

第一层保证了"包被安装",第二层再让调用方式不依赖 PATH 链接,更稳:

# 用 python -m ruff,只要包装了就能调,不要求可执行在 PATH - name: Lint run: python -m ruff check . - name: Format check run: python -m ruff format --check .

同时在项目脚本里也统一:

import subprocess, sys def run_ruff(path="."): # 统一用 python -m ruff,避免 PATH 上找不到可执行 return subprocess.run([sys.executable, "-m", "ruff", "check", path], check=True) if __name__ == "__main__": run_ruff()

python -m ruff的好处:它解析的是"当前sys.executable对应的环境里装的 ruff 包",只要pip install ruff进了这个环境就 100% 可用,不受 PATH 链接、venv 激活状态影响。配合第一层的显式安装,双重保险。

七、解决方案(第三层):CI 预检 + 锁版本 + 缓存

第三层加健壮性,避免"偶发找不到"和"版本漂移":

# 1) 锁版本,避免本地新/CI 旧导致规则不一致 - name: Install pinned ruff run: pip install "ruff==0.6.9" # 2) 预检:真正执行前先确认可用,给出友好报错 - name: Preflight ruff run: python -m ruff --version # 3) 缓存 pip,加速且避免偶发网络失败导致没装上 - uses: actions/setup-python@v5 with: python-version: "3.11" cache: pip

本地也加个Makefile/tox统一入口,保证"本地跑的和 CI 跑的是同一套":

.PHONY: lint lint: python -m ruff check . python -m ruff format --check .

这样开发者本地make lint与 CI 调用完全一致,本地能过 CI 必过(在同样版本下),消除"本地绿 CI 红"的落差。

八、落地建议

如果你在 CI 遇到 "No such file or directory: 'ruff'",建议:

  1. 加依赖:把ruff显式写进 dev 依赖 / requirements,固定版本下限。
  2. 改调用:脚本和 CI 都用python -m ruff,去掉 PATH 依赖。
  3. 锁版本:固定ruff==x.y.z,避免规则漂移。
  4. 加缓存:pip 缓存加速,避免偶发安装失败。
  5. 统一入口Makefile/tox让本地与 CI 一致。
  6. 预检:CI 先python -m ruff --version确认可用。

九、排查清单

如果 CI 报 "No such file or directory: 'ruff'",按顺序查:

  1. 确认 ruff 是否在 CI 安装的依赖里:没有就加进 dev 依赖/requirements。
  2. 看调用方式:脚本是ruff还是python -m ruff?前者依赖 PATH,改后者更稳。
  3. 确认 CI 装了依赖:pip install 步骤有没有包含 ruff,或单独pip install ruff
  4. 锁版本:本地与 CI ruff 版本是否一致,规则差异会导致误报。
  5. 加预检:CI 先python -m ruff --version, early fail。
  6. 本地复现:用干净 venvpython -m ruff check .,确认本地和 CI 一致。
  7. 看缓存/网络:偶发失败可能是安装阶段网络问题,加 pip 缓存。

十、小结

CI 报No such file or directory: 'ruff',根因是代码质量检查脚本直接调用ruff可执行文件,但 CI 的干净环境里既没有把ruff装进依赖清单,也没有用python -m ruff这种"依赖已安装包"的方式调用,于是执行时找不到这个可执行文件。它只在 CI 暴露(本地你装过所以有),属于"工具可用性没在 CI 里被显式保证"的配置缺漏。

修复分三层:第一层把ruff显式加进 CI 依赖并固定版本下限,保证一定被安装;第二层把调用统一改成python -m ruff,去掉对 PATH 上可执行链接的依赖,只要包装了就可用;第三层加版本锁定、pip 缓存和make lint统一入口,消除"本地绿 CI 红"的版本/环境落差,并加预检 early fail。核心心法是:CI 里调用的任何 CLI 工具,都不能假设它"环境自带"——必须显式安装(进依赖)、并用不依赖 PATH 的方式调用(python -m),才能保证环境可复现、构建稳定

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 22:00:48

UE5回合制游戏摄像机控制:蓝图实现与状态机设计

1. 项目概述:为什么回合制游戏的摄像机控制是门学问?做回合制游戏,尤其是用UE5蓝图来做,很多人觉得摄像机控制不就是“镜头跟着角色走”吗?刚开始我也是这么想的,直到实际动手,才发现这里面的坑…

作者头像 李华
网站建设 2026/7/21 21:55:53

3步快速打造你的专属Windows 11精简系统:tiny11builder终极指南

3步快速打造你的专属Windows 11精简系统:tiny11builder终极指南 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder tiny11builder是一个强大的PowerShell…

作者头像 李华
网站建设 2026/7/21 21:51:31

终极指南:如何通过Magisk实现Android深度定制与Root权限管理

终极指南:如何通过Magisk实现Android深度定制与Root权限管理 【免费下载链接】Magisk The Magic Mask for Android 项目地址: https://gitcode.com/GitHub_Trending/ma/Magisk Magisk是一款功能强大的Android系统定制工具套件,为技术爱好者和开发…

作者头像 李华
网站建设 2026/7/21 21:47:29

Docker-Android容器化Android模拟器架构深度解析与实践指南

Docker-Android容器化Android模拟器架构深度解析与实践指南 【免费下载链接】docker-android Android in docker solution with noVNC supported and video recording 项目地址: https://gitcode.com/GitHub_Trending/do/docker-android Docker-Android是一个革命性的开…

作者头像 李华
网站建设 2026/7/21 21:45:45

大模型就业热了,为什么你的简历和 Demo 还是拿不到面试?

如果你正准备往大模型方向转,《一份看似完整的计算机专业就业方案,为什么投递时没效果?》这类问题别只看热度。更重要的是判断自己该补哪块能力,以及怎么证明你真的会。 摘要 先把这篇文章的目标说清楚:看完之后&…

作者头像 李华