SeanLF/still_active

GitHub: SeanLF/still_active

一款 Ruby 依赖健康度审计工具,通过检查全传递依赖图的维护活跃度、归档状态、安全评分和漏洞等信号,帮助团队判断依赖是否仍在被积极维护。

Stars: 13 | Forks: 1

# `still_active` **如何知道你的 Ruby 依赖是否仍在维护?** `bundle outdated` 可以告诉你版本差异。`bundler-audit` 用于捕获已知的 CVE。但它们都无法告诉你是否还有人仍在维护该项目。`still_active` 会检查依赖图中每个 gem 的维护活动、版本新旧程度、安全评分、漏洞、libyear 漂移以及归档的仓库状态——默认包含直接和间接依赖(使用 `--direct-only` 可缩小范围),并支持通过 `.still_active.yml` 进行细粒度的、可提交到版本控制的抑制规则。 检测结果以**终端 / Markdown / JSON / SARIF / CycloneDX** 格式输出——SARIF 结果会显示在你的 GitHub Security 标签页以及 `Gemfile.lock` 的内联 PR 注释中;CycloneDX 则可输入给 Trivy / Dependency-Track / Snyk。PR 模式(`--baseline=FILE`)仅报告相较于 main 分支恶化的部分,这样审查者只需看到一行信息(“`vcr` 已新归档”),而不是每个依赖的绝对快照。 [![Gem Version](https://badge.fury.io/rb/still_active.svg)](https://badge.fury.io/rb/still_active) [![GitHub Action](https://img.shields.io/badge/Marketplace-still__active--action-2ea44f?logo=github)](https://github.com/marketplace/actions/still_active) ![代码质量分析](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/b1c2c7ec49175438.svg) ![RSpec](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/f0a32c3b4d175444.svg) ![Rubocop 分析](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/bb8b423639175449.svg) ``` Name Version Activity OpenSSF Vulns License ────────────────────────────────────────────────────────────────────────────── async 2.36.0 (latest) ok 7.1/10 0 MIT backbone-rails 1.2.3 (latest) archived 3.6/10 0 MIT bootstrap-slider-rails 9.8.0 (latest) critical - 0 MIT gitlab-markup 2.0.0 (latest) ok - 0 MIT local_gem 0.1.0 (path) - - 0 - nested_form 0.3.2 (git) archived 3.3/10 0 MIT remotipart 1.4.4 (git) critical 3.1/10 0 MIT 7 gems: 4 up to date, 0 outdated · 2 active, 2 stale, 2 archived · 0 vulnerabilities Ruby 4.0.1 (latest) ``` ## 为什么使用 `still_active`? `still_active` 是对现有成熟 Ruby 工具链的**补充**——而非替代品。`bundle outdated`、`bundler-audit` 和 `libyear-bundler` 在它们各自的领域都是经过专门打磨且久经沙场的。`still_active` 回答了另一个不同的问题:**是否还有人正在维护这个 gem?**——并整合了版本/CVE/libyear 等信号,让你只需查看一份报告而不是三份。 | | `bundle outdated` | `bundler-audit` | `libyear-bundler` | **`still_active`** | | ---------------------------- | ----------------- | ---------------------- | ----------------- | ------------------------ | | 过时的版本 | 是 | - | 是 | 是 | | 已知漏洞 (CVEs) | - | 是 (ruby-advisory-db) | - | 是 (deps.dev + ruby-advisory-db) | | Libyear 漂移 | - | - | 是 | 是 | | **最后一次提交活动** | - | - | - | **是** | | **归档仓库检测** | - | - | - | **是** | | **OpenSSF Scorecard** | - | - | - | **是** | | **已撤回版本检测** | - | - | - | **是** | | **Ruby 版本新旧程度** | - | - | - | **是** (EOL + libyear) | | GitLab 支持 | - | - | - | 是 | | CI 质量门禁 | - | 退出码 | - | 是 (4 个标志) | | 输出格式 | 文本 | 文本 | 文本 | 终端, JSON, Markdown, SARIF, CycloneDX | 加粗的行正是 `still_active` 填补的空白:没有其他工具能回答“维护者还在吗?”这个问题。CVE 这一列值得仔细看看:`bundler-audit` 读取 `ruby-advisory-db`,而 `still_active` 读取 `deps.dev`,它们的数据有时会产生分歧。**如果 `bundler-audit` 与 `still_active` 同时安装,我们也会读取它的 `ruby-advisory-db` 检出并合并结果**(去重后,每条建议都会标记其 `source`)——因此同时运行两者不再意味着需要手动核对两个不同的漏洞数量。 ## 安装 ``` gem install still_active ``` **需要处于积极维护期的 Ruby。** gemspec 中的 `required_ruby_version` 下限遵循 Ruby 的 [EOL 时间表](https://endoflife.date/ruby);在一个不受维护的 runtime 上运行维护审计工具未免有些讽刺。不过,你不必在*你正在审计的那个 Ruby 版本上*运行它:still_active 报告的是你的项目在 `Gemfile.lock` 中锁定的版本,因此你可以从任何当前的 Ruby 版本(本地、CI 中,或通过 [`still_active-action`](https://github.com/SeanLF/still_active-action))运行它,它依然会对 EOL 的目标版本进行标记。 ## 快速开始 ``` # 审计您的 Gemfile(自动检测输出格式) still_active # 检查特定的 gems still_active --gems=rails,nokogiri,sidekiq # CI pipeline:如果任何 gem 严重过期或存在 vulnerabilities 则失败 still_active --fail-if-critical --fail-if-vulnerable # 在 CI 检查中忽略特定的 gems still_active --fail-if-warning --ignore=legacy_gem,internal_gem # 用于 pull requests 或文档的 markdown 表格 still_active --markdown ``` ## 用法 ### 认证 `still_active` 按以下顺序发现 GitHub token: 1. `--github-oauth-token=TOKEN` CLI flag 2. `GITHUB_TOKEN` 环境变量(CI 惯例) 3. `GH_TOKEN` 环境变量(`gh` CLI 惯例) 4. `gh auth token`(如果已安装并通过了 `gh` 认证) 如果没有 token,GitHub API 调用将是未经身份验证的,并且速率限制为 60 次请求/小时——只要 gem 数量稍微多一点,你就会触及限制。如果有 token,限制将提升至 5000 次请求/小时。当返回的速率限制响应显示重置时间很近(在 60 秒内,通常是并发发散可能触发的次要/突发限制,即使有 token 也会发生)时,still_active 会等待并重试,而不是丢弃该 gem 的信号;如果重置时间还很遥远(你已经耗尽了每小时配额),则不会等待,因此请添加 token 或降低运行频率。 GitLab 级联镜像了 GitHub 的机制:`--gitlab-token` → `GITLAB_TOKEN` → `glab auth status --show-token`。对于公共仓库是可选的,对于私有仓库则是必需的。 Forgejo/Codeberg(极少数 gem 的规范 `source_code_uri` 为 `codeberg.org`)默认通过匿名方式读取;设置 `STILL_ACTIVE_FORGEJO_TOKEN`(或 `CODEBERG_TOKEN`)仅仅是为了提高速率限制或访问私有仓库。该平台没有 CLI flag,因为 Codeberg 没有像 `gh`/`glab` 那样可以借用 token 的通用 CLI。 Artifactory 认证会首先寻找 Bundler 配置中的凭据,这样如果私有仓库已经在 Bundler 中配置过,就无需额外配置即可使用。要在 Bundler 中设置这些凭据,请运行 `bundle config set credentials.my-org.jfrog.io user:pass`。如果没有设置凭据,still_active 会回退到使用通过 `--artifactory-token` 或 `STILL_ACTIVE_ARTIFACTORY_TOKEN` 提供的 token。认证期望使用 `user:password` 格式进行 Basic auth,否则它将被视为用于 Bearer auth 的裸 token。私有 JFrog gem 仓库(`*.jfrog.io`)需要有效的身份验证。 通过 flag 或环境变量提供 artifactory token 时,你还必须将 `--artifactory-host` 或 `STILL_ACTIVE_ARTIFACTORY_HOST` 设置为预期的 registry 主机名(例如 `my-org.jfrog.io`)。still_active 仅将凭据发送到该主机,从而确保包含其他 Artifactory 主机的 lockfile 不会泄露 token(这是一种安全风险)。以这种方式提供 token/host 仅适用于单个主机。要支持多个 Artifactory 主机,请使用 Bundler 的配置来设置每个主机的凭据。 ### CLI 选项 ``` Usage: still_active [options] all flags are optional --gemfile=GEMFILE path to gemfile --gems=GEM,GEM2,... Gem(s) --terminal Coloured terminal output (default in TTY) --markdown Markdown table output --json JSON output (default when piped) --alternatives Suggest maintained alternatives (Ruby Toolbox leads) for archived/critical gems --unreleased-commits Count commits on the default branch since the latest release (GitHub only; opt-in) --direct-only Audit only direct (declared) deps, not the full transitive graph --sarif[=PATH] SARIF 2.1.0 output for GitHub Code Scanning --cyclonedx[=PATH] CycloneDX SBOM output (stdout, or a file path) --cyclonedx-version=VERSION CycloneDX spec version: 1.6 (default) or 1.7 --baseline=PATH Compare current state to baseline JSON; emit markdown deltas --github-oauth-token=TOKEN GitHub OAuth token to make API calls --gitlab-token=TOKEN GitLab personal access token for API calls --artifactory-token=TOKEN Artifactory token for private gem registry API calls --artifactory-host=HOST Artifactory host allowed to receive the global token (e.g. my-org.jfrog.io) --simultaneous-requests=QTY Number of simultaneous requests made --safe-range-end=YEARS maximum years since last release considered safe, no warning (default 1.5) --warning-range-end=YEARS maximum years since last release that triggers a warning, beyond this is critical (default 3) --fail-if-critical Exit 1 if any gem has critical activity warning --fail-if-warning Exit 1 if any gem has warning or critical activity warning --fail-if-vulnerable[=SEVERITY] Exit 1 if any gem has vulnerabilities (optionally at or above SEVERITY) --fail-if-outdated=LIBYEARS Exit 1 if any gem exceeds LIBYEARS behind latest --ignore=GEM,GEM2,... Exclude gems from pass/fail checks (still shown in output) --critical-warning-emoji=EMOJI --futurist-emoji=EMOJI --success-emoji=EMOJI --unsure-emoji=EMOJI --warning-emoji=EMOJI -h, --help Show this message -v, --version Show version ``` ### 输出格式 **终端**(在 TTY 下默认)——带有汇总行的彩色表格。如上所示。 **JSON**(通过管道输出时默认)——用于自动化的结构化数据: ``` still_active --json --gemfile=spec/still_active/edge_case_gemfile/Gemfile ``` ``` { "gems": { "async": { "source_type": "rubygems", "version_used": "2.36.0", "latest_version": "2.36.0", "repository_url": "https://github.com/socketry/async", "last_commit_date": "2026-01-22 04:09:48 UTC", "archived": false, "scorecard_score": 7.1, "vulnerability_count": 0, "license": "MIT", "libyear": 0.0 }, "nested_form": { "source_type": "git", "version_used": "0.3.2", "repository_url": "https://github.com/ryanb/nested_form", "last_commit_date": "2021-12-11 21:47:02 UTC", "archived": true, "scorecard_score": 3.3, "vulnerability_count": 0 }, "local_gem": { "source_type": "path", "version_used": "0.1.0", "scorecard_score": null, "vulnerability_count": 0 } }, "ruby": { "version": "4.0.1", "eol": false, "latest_version": "4.0.1", "libyear": 0.0 } } ``` **Markdown**——用于 pull request、文档或 wiki 的表格: ``` still_active --markdown ``` | 活动 | 是否最新? | OpenSSF | 漏洞 | 名称 | 使用的版本 | 最新版本 | 最新预发布版 | 最后一次提交 | libyear | 许可证 | | -------- | ----------- | ------- | ----- | ------------------------------------------------------------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------- | ------- | ------- | | | ✅ | 7.1/10 | ✅ | [async](https://github.com/socketry/async) | [2.36.0](https://rubygems.org/gems/async/versions/2.36.0) (2026/01) | [2.36.0](https://rubygems.org/gems/async/versions/2.36.0) (2026/01) | ❓ | [2026/01](https://github.com/socketry/async) | 0.0y | MIT | | 🚩 | ✅ | 3.6/10 | ✅ | [backbone-rails](https://github.com/aflatter/backbone-rails) | [1.2.3](https://rubygems.org/gems/backbone-rails/versions/1.2.3) (2016/02) | [1.2.3](https://rubygems.org/gems/backbone-rails/versions/1.2.3) (2016/02) | ❓ | [2016/02](https://github.com/aflatter/backbone-rails) | 0.0y | MIT | | ❓ | ❓ | ❓ | ✅ | local_gem | 0.1.0 (path) | ❓ | ❓ | ❓ | - | - | | 🚩 | ❓ | 3.3/10 | ✅ | [nested_form](https://github.com/ryanb/nested_form) | 0.3.2 (git) | ❓ | ❓ | [2021/12](https://github.com/ryanb/nested_form) | - | MIT | **Ruby 4.0.1**(最新) ✅ **CycloneDX**——一种标准轨道的 SBOM,让你的依赖图和 still_active 的信号得以流入 Trivy、Dependency-Track 或 Snyk: ``` still_active --cyclonedx # CycloneDX 1.6 to stdout still_active --cyclonedx=sbom.json # write to a file still_active --cyclonedx --cyclonedx-version=1.7 ``` 默认输出 **1.6 版本**——这是当今主流消费者所接收的版本(`cyclonedx-core-java`/Dependency-Track 和 `cyclonedx-go`/Trivy 截至 2026 年均限制在 1.6);通过 `--cyclonedx-version=1.7` 可选择使用最新版本。Gem 的名称/版本/`purl`/许可证以及漏洞会映射到原生的 CycloneDX 字段;没有原生位置对应的维护信号(已归档、OpenSSF 评分、libyear、最后一次提交、已撤回)则存放在 `still_active:` 命名空间的 `properties` 中。`serialNumber` 是根据内容派生的,因此同一 lockfile 的两个 SBOM 仅在生成时间戳上有所不同。 ### SARIF 输出(GitHub Code Scanning) 以 SARIF 2.1.0 格式输出检测结果——它们将显示在 GitHub Security 标签页以及 pull request 中 `Gemfile.lock` 的内联注释上。 ``` still_active --sarif # writes still_active.sarif.json still_active --sarif=path/to/out.sarif.json still_active --sarif=- # stdout ``` **简单模式**——使用 [`still_active-action`](https://github.com/SeanLF/still_active-action) 包装器: ``` permissions: contents: read security-events: write # required for SARIF upload jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: { ruby-version: '3.4' } - uses: SeanLF/still_active-action@v0 with: github-token: ${{ github.token }} sarif: still_active.sarif.json - uses: github/codeql-action/upload-sarif@v3 if: always() with: { sarif_file: still_active.sarif.json } ``` **纯 bundle exec**,如果你想将 still_active 固定在你的 Gemfile 中: ``` - run: bundle exec still_active --sarif env: GITHUB_TOKEN: ${{ github.token }} - uses: github/codeql-action/upload-sarif@v3 if: always() with: { sarif_file: still_active.sarif.json } ``` 规则参考(SA001–SA007)及如何抑制:请参见 [`docs/rules.md`](docs/rules.md)。 ### 基线差异(PR 审查) `--baseline=FILE` 将当前运行结果与之前捕获的 JSON 快照进行比较,并输出 Markdown 格式的增量报告。专为 PR 审查者真正关心的问题而设计:**什么情况恶化了?** ``` # 本地 — 从 main 捕获,并与您的 branch 进行比较 git checkout main && still_active --json > /tmp/main.json git checkout my-branch && still_active --baseline=/tmp/main.json ``` 在 CI 中,在 main 分支上捕获基线,并在 PR 分支上进行比较。如果检测到任何回归(新漏洞、新归档的依赖、scorecard 评分跌破 7.0、未改变版本上的 libyear 增长、Ruby 新进入 EOL 等),则以状态码 1 退出。 设置后,该 diff 将取代 `--sarif`、`--terminal`、`--markdown` 和 `--json`。 当检测到某次运行是由 Dependabot 或 Renovate 发起时(通过 `GITHUB_ACTOR`、`dependabot/`/`renovate/` 分支或提交主题),报告会以一行叙述开头——“Dependabot bump: `rack` 2.0.0 → 2.0.6”——并且 `--json` 会获得一个顶层的 `pr_context`。该检测是尽力而为且保守的:它绝不会对普通的提交产生误报,而且漏报的代价仅仅是没有了那一行叙述。 ### 与 `dependency-review-action` 配合使用 GitHub 官方的 [`dependency-review-action`](https://github.com/actions/dependency-review-action) 在服务器端针对 PR 运行,并根据 GitHub 的 dependency-graph diff 显示**漏洞、许可证和 OpenSSF Scorecard** 评分。它不显示维护信号——最后一次提交活动、归档仓库、libyear、RubyOL 或已撤回的版本——并且仅限 GitHub.com / GHES 使用。`still_active` 是补充,而不是替代: | | `dependency-review-action` | `still_active` | | ---------------------------- | ---------------------------------- | ------------------------------------------- | | 平台 | 仅限 GitHub.com / GHES | 任何 CI | | 语言 | 多语言 (GitHub 依赖图) | Ruby | | 漏洞 | GHSA | deps.dev + ruby-advisory-db (已合并) | | 许可证 | 是 (允许/拒绝门禁) | 仅显示 (无门禁) | | OpenSSF Scorecard | 是 (展示) | 是 (展示 + 阈值) | | **最后一次提交活动** | - | **是** | | **归档仓库检测** | - | **是** | | **Libyear 漂移** | - | **是** | | **Ruby EOL 检测** | - | **是** | | **已撤回版本检测** | - | **是** | | 与 base 的差异 | 原生 (GitHub API) | `--baseline=FILE` | | 输出 | 内联 PR 注释 | 终端 / Markdown / JSON / SARIF / CycloneDX | 两者同时运行:让 `dependency-review-action` 对 CVE 和许可证进行门禁拦截,并让 `still_active` 在同一个 PR 上添加维护视角的补充。 ``` on: pull_request jobs: dependency-review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/dependency-review-action@v4 with: fail-on-severity: high show-openssf-scorecard: true maintenance-review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: { ruby-version: ".ruby-version", bundler-cache: true } - uses: SeanLF/still_active-action@v0 with: fail-if-critical: true ``` ### CI 质量门禁 使用退出码标志根据依赖状态使 CI pipeline 失败: ``` # 遇到严重过期或已归档的 gems 时失败 still_active --fail-if-critical --json # 遇到任何过期、关键或已归档的 gem 时失败 still_active --fail-if-warning --json # 如果任何 gem 存在已知 vulnerabilities 则失败 still_active --fail-if-vulnerable --json # 仅在遇到 high/critical 严重级别的 vulnerabilities 时失败 still_active --fail-if-vulnerable=high --json # 如果任何 gem 落后超过 3 个 libyears 则失败 still_active --fail-if-outdated=3 --json # 组合 flags 并排除已知 exceptions still_active --fail-if-warning --fail-if-vulnerable --ignore=legacy_gem --json ``` ### 配置文件 (`.still_active.yml`) `--ignore=GEM` 过于生硬:它会立即从**每一个**门禁中丢弃某个 gem,因此,接受一个无法修复的漏洞同时,也会让你对该 gem 变成归档状态或出现*新* CVE 视而不见。项目根目录中提交的 `.still_active.yml` 将其替换为细粒度的、可审计的抑制规则,并允许你将策略标志保留在版本控制中,而不是在每次调用时都将它们穿插进去。 ``` # .still_active.yml -- policy defaults 加上细粒度的 suppression fail_if_critical: true fail_if_vulnerable: high # true, or a minimum severity: low|medium|high|critical fail_if_outdated: 3 # libyears unreleased_commits: true output: json # terminal | markdown | json # 拉取 bundler-audit 的 accepted-advisory 列表,而不是维护两个文件 import: - .bundler-audit.yml ignore: # Accept ONE advisory, by id -- a different/new CVE on nokogiri still fails - advisory: CVE-2024-1234 gem: nokogiri reason: "no fix released; not reachable from our code path" expires: 2026-09-01 # re-surfaces as a normal failure after this date # Accept staleness on a vendored gem, but still fail if it gets a CVE - gem: legacy_thing signal: activity # activity | vulnerability | libyear reason: "vendored, intentionally frozen" # A bare gem name keeps the old whole-gem behaviour (mutes every signal) - some_internal_gem ``` 设计理念: - **粒度。** 按 `advisory:`(单个 CVE)和/或 `signal:`(`activity` / `vulnerability` / `libyear`)作为键,而不是整个 gem。漏洞抑制**必须**指明 advisory id,这样同一 gem 上新披露的 CVE 绝不会被预先静默。`--ignore=GEM`(以及单纯的 gem 名称条目)在设计上依然会屏蔽一切。 - **优先级。** CLI flag > 环境变量 > 配置文件 > 默认值。CLI flag 总是优先;`--ignore` 会与文件中的抑制规则合并,而不是替换它们。密钥和特定于调用的路径(`--gemfile`、`--gems`、`--baseline`、输出路径)则有意**不从**文件中读取,因此提交的配置永远不会携带凭据。 - **过期机制。** `expires:` 日期使接受的风险可见:一旦过期,该条目将停止生效,并且检测结果将再次无法通过门禁(类似 Trivy 风格),这样抑制规则就不会在不知不觉中失效。`reason:` 是可选的,但推荐填写。 - **bundler-audit,仅建议而非吸收。** still_active 绝不会默默地继承另一个工具的忽略列表:自动导入 `.bundler-audit.yml` 会抑制你仅在 bundler-audit 上下文中接受的漏洞,而在本工具中既没有理由也没有过期时间。相反,当开启 `--fail-if-vulnerable` 并且存在未导入的 `.bundler-audit.yml` 时,它会打印一行提示,建议使用 `import:` 行,将选择权留给你。 - **输出。** 抑制会改变**退出码**,并在 **SARIF** 中将检测结果标记为原生的 `suppressions[]` 条目(以你的 `reason` 作为理由,因此 GitHub Code Scanning 会将其渲染为已忽略,而不是打开状态)。该检测结果仍然会出现在 JSON/终端/Markdown 输出中;抑制意味着接受风险,而不是掩盖风险的存在。 ### 活动阈值 活动状态由发布的近期程度(最新稳定版或预发布版的日期)决定,因为发布才是你实际可以 `bundle update` 升级到的内容。最近的提交并不能抵消陈旧的发布:最后一次提交日期仅作为上下文显示,并且只有在某个 gem 完全没有发布过时才作为替代参考(例如基于 git 来源的 gem)。这些阈值是根据真实的 RubyGems 发布节奏进行校准的,因为健康的成熟 gem 两次发布之间通常会相隔一年或更长时间: - **ok**:最近一次发布在 18 个月内(可通过 `--safe-range-end` 配置) - **stale**:最近一次发布在 18 个月到 3 年前 - **critical**:最近一次发布超过 3 年(可通过 `--warning-range-end` 配置) ### 备选 gem 线索(可选) 当某个 gem 被标记为已归档或 critical 时,`--alternatives` 会从相同的 [Ruby Toolbox](https://www.ruby-toolbox.com) 类别中展示最多三个仍在维护的 gem,并按总下载量进行排名: ``` still_active --gems=paperclip --alternatives ``` ``` ↳ leads (Ruby Toolbox): shrine · carrierwave · kt-paperclip (verify fit) ``` 这些是**线索,而非建议**:同类别并不意味着可以直接替换,因此在切换之前请验证其适用性。Ruby 没有权威的“替代使用”元数据(不像 npm 的 `deprecate`,Go 的 `// Deprecated:`,或 NuGet 的 alternate-package 字段),因此这是一种尽力而为的启发式方法。当目录中没有该 gem 的条目时,该功能会保持静默,并且该功能永远不会阻塞或导致运行失败。线索会出现在终端、Markdown、JSON 和 SARIF 输出中。当该标志关闭时,终端输出会在被标记的 gem 上显示一行提示,说明存在该选项(其他格式则保持静默)。 ### 传递依赖 维护信号(陈旧的发布、归档的仓库、最后一次提交的时间、漏洞建议、libyear)默认覆盖**整个传递 lockfile 依赖图**——你通过传递依赖引入的不受维护的 gem 也是真正的风险,即使你从未直接指定过它,而且与 still_active 组合使用的 CVE 工具也已经枚举了整个解析出的依赖图。这与 libyear-bundler(默认包含传递依赖,可使用 `--only-explicit` 退出)以及每一个 CVE 扫描程序(bundler-audit、npm/cargo/pip-audit 全都是全树扫描)的行为一致。 当传递依赖触发了某个信号时,输出会指明引入它的**直接依赖**:JSON 中的 `dependency_path`,终端中变暗的 `↳ transitive, pulled in by X` 行,SARIF 消息中的 `(transitive, pulled in by X)` 后缀,以及 Markdown 中的 **Transitive findings** 列表。这将无法操作的传递依赖发现转变成了可操作的发现:你无法升级一个你没有选择过的 gem,但你可以替换或对引入它的直接依赖施加压力。 `--alternatives` 在设计上保持**仅限直接依赖**——对于你从未选择过的 gem 来说,“用 Y 替换 gem X” 是说不通的。传递 `--direct-only` 可仅审核你声明的依赖(这是 1.7 版本之前的行为),这也会大大减少 API 调用的消耗。 ### 未发布的提交(可选) `--unreleased-commits` 会在 JSON 输出中添加一个 `unreleased_commits` 计数:自最新发布标签以来在默认分支上的提交数量。它能够捕捉到发布近期度信号无法覆盖的情况:一个有*近期*发布但堆积了一大堆已合并但未发布的修复的 gem;反之,也能看出一个看起来陈旧但实际上已经*完工*(没有未发布的工作)的 gem。这是目前唯一一个没有任何 Ruby 工具能够提供的维护信号;只有 GitHub 自己的 UI 才会显示它。 它是**可选的,且仅限 GitHub**:启用它后,每个由 GitHub 托管的 gem 会额外增加一次 API 调用(通过依次尝试 `v1.2.3` 和 `1.2.3` 作为比较基准,从 RubyGems 版本中解析出 git 标签),因此对于庞大的 lockfile,请注意你的速率限制。非 GitHub 的数据源将报告 `null`(GitLab 没有等效的标量;该信号是鸭子类型的,所以一个 provider 要么实现它,要么不实现)。该计数是**信息性的,永远不会作为门禁中断运行**。请将其视为一种线索,而不是结论:对于 monorepo(该计数跨越整个仓库,例如存在于 `rubygems/rubygems` 中的 `bundler`)以及基于发布分支的项目(默认分支是下一个版本的主干,因此 `rails` 会读取到比其最新稳定标签多出约 2000 次提交)来说,该数值会偏大。 ### 数据来源 - **版本、发布日期和许可证**来自 [RubyGems.org](https://rubygems.org)、[GitHub Packages](https://docs.github.com/en/packages) 或 [JFrog Artifactory](https://jfrog.com/artifactory/) gem 仓库 - **最后一次提交日期和归档状态**来自 [GitHub](https://docs.github.com/en/rest)、[GitLab](https://docs.gitlab.com/ee/api/) 或 [Forgejo/Gitea](https://forgejo.org/docs/latest/user/api-usage/) (Codeberg) API - **OpenSSF Scorecard**、**漏洞数量**和 **CVSS 严重程度**来自 Google 的 [deps.dev](https://deps.dev) API - **额外的漏洞建议**来自 [ruby-advisory-db](https://github.com/rubysec/ruby-advisory-db),当 `bundler-audit` 也被安装时会进行合并(运行 `bundle audit update` 以保持其检出为最新状态) - **Ruby 版本新旧程度**来自 [endoflife.date](https://endoflife.date) - **备选 gem 线索**(使用 `--alternatives`)来自 [rubytoolbox/catalog](https://github.com/rubytoolbox/catalog) 类别数据 ### 配置默认值 | 选项 | 默认值 | 描述 | | ----------------------- | ----------- | ---------------------------------------------------------------- | | `output_format` | 自动检测 | 在 TTY 上显示彩色终端,通过管道输出时为 JSON | | `safe_range_end` | 1.5 年 | 最后一次发布在此范围内为“ok” | | `warning_range_end` | 3 年 | 最后一次发布在此范围内为“stale”;超出此范围则为“critical” | | `simultaneous_requests` | 10 | 并发 API 请求 | ## 开发 检出仓库后,运行 `bin/setup` 以安装依赖并配置 git hooks。然后运行 `rake` 以执行完整的 lint + 测试套件(仅运行测试可使用 `rake spec`,仅运行 lint 可使用 `rake rubocop`)。你也可以运行 `bin/console` 获取一个交互式提示符,允许你进行实验。 pre-push hook 会在每次 `git push` 之前自动运行 `rake`,因此跨文件的 rubocop 规则不会逃脱并进入 CI。如果你确实需要,可以使用 `git push --no-verify` 跳过。 要将此 gem 安装到你的本地机器上,请运行 `bundle exec rake install`。当创建 GitHub Release 时,新版本会(通过 trusted publishing)自动发布到 [rubygems.org](https://rubygems.org)。 ## 贡献 欢迎提交 Bug 报告和 pull request。 ## 许可证 该 gem 作为开源软件提供,基于 [MIT 许可证](https://opensource.org/licenses/MIT) 的条款发布。
标签:Ruby, 依赖管理, 知识库, 软件供应链