serdairy/foothold

GitHub: serdairy/foothold

Foothold 是一个开发者工具,通过分析 Python 代码库的 import 图和 git 编辑历史,为新贡献者生成确定性的文件阅读路径和架构文档。

Stars: 0 | Forks: 1

# Foothold [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/serdairy/foothold/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/foothold?logo=pypi&logoColor=white)](https://pypi.org/project/foothold/) [![Python](https://img.shields.io/pypi/pyversions/foothold?logo=python&logoColor=white)](https://pypi.org/project/foothold/) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) [![Checked with mypy](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy-lang.org/) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) **我应该先阅读哪 20 个文件?** Foothold 能在不到一秒的时间内为一个 Python 代码库回答这个问题,而且无需 API key —— 这是你在开始攀登陌生的代码库之前所需的立足点。 ``` $ foothold map ~/src/rich 99 modules (99 source, 0 test) · 38,437 lines · 1,884 import statements Top 6 files by structural weight ┏━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ # ┃ file ┃ score ┃ in ┃ loc ┃ why ┃ ┡━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ 1 │ console.py │ 0.695 │ 49 │ 2699 │ imported by 49 modules │ │ 2 │ cells.py │ 0.542 │ 31 │ 353 │ imported by 31 modules │ │ 3 │ _unicode_data/__init__.py │ 0.380 │ 1 │ 94 │ high transitive reach │ │ 4 │ text.py │ 0.353 │ 31 │ 1364 │ imported by 31 modules │ │ 5 │ style.py │ 0.299 │ 30 │ 797 │ imported by 30 modules │ │ 6 │ segment.py │ 0.220 │ 21 │ 781 │ 781 lines │ └───┴───────────────────────────┴───────┴────┴──────┴──────────────────────────┘ ``` 这种排序并非猜测。它源自 import graph,并根据每个文件的编辑频率进行了加权。 ## 问题所在 贡献者入职是开源中最昂贵的无酬工作,而且它要被支付两次 —— 一次是由那些花整个周末去弄清楚 400 个文件中哪些重要的新手支付,另一次是由在每个 issue 中回答相同引导问题的维护者支付。 通常的缓解措施并不奏效。`ARCHITECTURE.md` 在项目初期编写一次,并在两次发布内就会产生偏差。生成的 API 参考列出了所有符号,却没有对它们进行排序。将代码库粘贴到聊天窗口中,对于 networkx 这样规模的项目大约需要耗费 46,000 个 token,并且生成的流畅文本完全没有基于实际的 import graph。 Foothold 将问题一分为二。**排序是确定性的** —— 一个 graph、一个 churn 数量、一个你可以读懂的公式。**文本说明是可选的**,并且建立在一个已经正确且已经过精简的选择之上。昂贵的部分是不需要模型的那部分。 ## 安装 ``` uv tool install foothold # or: pipx install foothold ``` 三个运行时依赖项:`typer`、`rich`、`networkx`。PageRank 使用纯 Python 实现,专门为了避免将大约 100 MB 的 scipy 和 numpy 引入 CLI。 ## 命令 | 命令 | 作用 | 网络 | |---|---|---| | `foothold map .` | 对支撑代码库的文件进行排序 | 无 | | `foothold docs . -o ARCHITECTURE.md` | 编写带有 Mermaid 图的确定性架构文档 | 无 | | `foothold issues . --max 10` | 提出不在关键路径上的 good-first-issue 候选者 | 无 | | `foothold explain . --dry-run` | 打印模型将接收到的确切 payload | 无 | | `foothold explain .` | 基于排序图的文本讲解 | OpenAI API | | `foothold docs . --narrate` | 具有概述部分的相同文档 | OpenAI API | 这两个花钱的命令会打印估算值并需要确认;对于非交互式使用,`--yes` 是必需的。 ## GitHub Action 将阅读路径放在每个 pull request 的作业摘要中。无需 token,无需写入权限,也无需任何配置: ``` - uses: actions/checkout@v7 with: fetch-depth: 0 # the churn signal needs real history - uses: serdairy/foothold@v0.1.3 with: top: "20" ``` | 输入项 | 默认值 | 作用 | |---|---|---| | `path` | `.` | 要分析的项目根目录 | | `command` | `map` | `map`、`docs` 或 `issues` | | `top` | `20` | 报告的文件数量 | | `output` | `ARCHITECTURE.md` | 当指定 `command: docs` 时写入的文件 | | `version` | latest | 固定一个 foothold 版本,例如 `0.1.3` | | `summary` | `true` | 将结果写入作业摘要 | | `python-version` | `3.12` | 运行 foothold 的 Python 版本,与分析的项目无关 | 该操作将输出公开为 `steps..outputs.result`,因此你可以将其发布到你喜欢的任何位置。它仅运行 `pip install foothold` 而不执行其他任何操作 —— 无需拉取 container,也不会执行来自被分析代码库的任何代码。 `fetch-depth: 0` 非常重要:浅克隆没有历史记录,因此 churn 值会降至零,排序也会退化为纯粹的图结构。它仍然有效,只是提供的信息较少。 ## 排序的工作原理 ``` score = 0.45·pagerank + 0.30·churn + 0.15·fan-in + 0.10·log(loc) ``` 每一项都在整个代码库内进行了 min-max 归一化,因此得分可以在代码库内进行比较,但不能跨代码库比较。权重保存在 `.foothold.toml` 中,并打印在每个生成的文档里 —— 无法被质疑的排序就是无法被信任的排序。 - 基于 project 内 import graph 的 **PageRank**。边的方向指向 *导入者 → 被导入者*,因此所有人都依赖的 module 得分很高。外部和 stdlib 的 import 被丢弃:它们增加了节点却没有增加信号。(`test_pagerank_ranks_dependencies_above_dependents` 保护了此方向 —— 将其反转会悄无声息地颠倒整个工具的逻辑。) - 来自 `git log --since=18.months` 的 **Churn**。每次发布都会被编辑的文件也就是新手必须接触的文件。没有 git 历史记录的代码库会退化为零 churn 信号,而不是直接失败。 - 作为简单、易读计数的 **Fan-in**,因此无需了解 PageRank 即可解释列表的顶部。 - **Size**,经过对数缩放,作为微弱的决胜条件。 测试被排除在排序之外,并用于检测未经测试的 module。 ## 它发送了什么,不发送什么 `foothold explain . --dry-run` 会打印完整的 payload。它包含文件路径、得分、entry point、import cycle 以及每个 module docstring 的第一行。**它不包含源代码** —— 有一个专门针对此点的测试。 结果是,上下文大小取决于 `--top`,而不是代码库大小: | 代码库 | Module | 代码行数 | 发送的上下文 | 预算 token | |---|---:|---:|---:|---:| | foothold | 31 | 1,284 | 1,999 字符 | 899 | | rich | 99 | 38,437 | 2,314 字符 | 978 | | networkx | 565 | 183,241 | 2,837 字符 | 1,109 | 一个 183,000 行的代码库被描述在不到 3 KB 的内容中。完整的数字和方法见 [docs/cost-model.md](docs/cost-model.md)。 Foothold 也从不执行它读取的代码 —— 解析使用的是不会进行求值的 stdlib `ast`。请参阅 [SECURITY.md](SECURITY.md)。 ## 架构 ``` src/foothold/ ├── cli.py # Typer entry point ├── analyze.py # orchestration: collect → graph → rank → RepoMap ├── models.py # the shared vocabulary; imported by 10 modules ├── config.py # .foothold.toml, ranking weights ├── collectors/ # python_ast · git_history · markers (offline) ├── graph/ # build (import graph) · rank (pagerank + weights) ├── issues.py # good-first-issue heuristics (offline) ├── render/ # terminal · markdown · mermaid (offline) └── narrator/ # the only module that talks to a model ``` [ARCHITECTURE.md](ARCHITECTURE.md) 由 `foothold docs` 生成,并在每次发布时刷新。它刻意没有被 CI 的相等性检查固定:churn 是一个输入项,因此排序会随着历史记录的积累而发生变动,逐字节的断言在每次提交时都会失败。CI 所断言的是,该生成器能在所有十二种 OS 和 Python 组合上针对此代码库运行。 ## 局限性 明确指出,因为替代方案会浪费您的时间: - **仅支持 Python。** 其他语言被解析为空。tree-sitter 支持将在 v0.3 版本提供。 - 动态 import(`importlib`、插件注册表、`__getattr__` 重导出)对静态分析是不可见的,这将导致重度依赖插件的架构排名偏低。 - Churn 需要真实的 git 历史记录。CI 必须使用 `fetch-depth: 0`;浅克隆会悄无声息地丢失该信号。 - 包含多个独立 package 的 Monorepo 被作为一个 graph 进行排序。将在 v0.5 版本支持。 - 得分只能在代码库内比较,绝不能跨代码库比较。 - 得分也会在同一个代码库内随着时间的推移而变化:churn 是通过滚动 18 个月的窗口来衡量的,因此相同的提交在今天和六个月后的排序可能会有所不同。排序描述的是代码库的当前状态,而不是其文件的固定属性。 ## 路线图 | 版本 | 范围 | 状态 | |---|---|---| | **v0.1** | `map`、`docs`、`issues`、`explain`;GitHub Action;Python;86% 代码覆盖率 | **已发布** | | v0.2 | 内容哈希缓存;基于 diff 的增量重新分析;`--since` | 下一步 | | v0.3 | tree-sitter 解析器:TypeScript、JavaScript、Go | 计划中 | | v0.4 | 带有角色的 `tour`;基于 PR 范围的阅读路径 | 计划中 | | v0.5 | Monorepo 支持;调用图边,而不仅仅是 import | 计划中 | | v1.0 | 稳定的 JSON schema;针对手写文档的基准测试套件 | 计划中 | 非目标:取代手写的设计文档、审查代码、作为托管服务运行。Foothold 是一个本地工具,可生成由你拥有并提交的文件。 ## 许可证 Apache-2.0 —— 选择它而不是 MIT 是因为其明确的专利授权,这对于一个解析他人代码的工具来说非常重要。请参阅 [LICENSE](LICENSE)。
标签:Petitpotam, Python, SOC Prime, 云安全监控, 代码分析, 代码阅读, 凭证管理, 开发工具, 无后门, 特权检测, 逆向工具, 静态分析