Skip to content

Add provider detection for more managers - #28

Merged
akriaueno merged 18 commits into
developfrom
feat/add-more-version-managers
Dec 20, 2025
Merged

akriaueno merged 18 commits into
developfrom
feat/add-more-version-managers

Conversation

@akriaueno

@akriaueno akriaueno commented Dec 20, 2025

Copy link
Copy Markdown
Owner

Summary

  • normalize provider path matching to handle Windows separators without duplicated rules
  • build a single Linux binary for e2e and COPY into each distro image
  • add path-based provider detection for MacPorts, Nix, Scoop, Chocolatey, winget
  • add package manager lookup for apk, pacman, Portage, zypper (rpm once)
  • refactor system package manager detection into iterable checks for easier extension
  • add Docker-based e2e harness under tests/e2e (builder uses nim:2.2.4-ubuntu-regular)
  • add Gentoo e2e coverage via stage3 + portage-utils
  • run e2e in parallel with streaming output
  • extend tests and README

Issues

Close #8
Close #9
Close #10
Close #12
Close #13
Close #14
Close #15
Close #16
Close #17
Close #18
Close #19
Close #20
Close #21
Close #22
Close #23
Close #24
Close #25
Close #26

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for detecting additional package managers: MacPorts, Nix, zypper/rpm, apk, pacman, and Portage.
    • Added support for detecting additional version managers: asdf, SDKMAN!, nvm, fnm, pyenv, rbenv, rvm, Rustup, Conda, and others.
  • Documentation

    • Updated README to reflect expanded provider coverage.
  • Tests

    • Expanded test coverage for package manager and version manager detection across multiple Linux distributions.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 20, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@akriaueno has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 58 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 530c14a and 15037f2.

📒 Files selected for processing (2)
  • .dockerignore (1 hunks)
  • tests/e2e/run.sh (1 hunks)

Walkthrough

Adds many new provider-detection rules (version managers, MacPorts, Nix, Windows managers, multiple distro package managers), refactors system-package-manager checks into a pluggable PkgManagerStrategy list, expands README, adds extensive unit tests, and introduces Docker-based end-to-end test infrastructure and per-distro e2e scripts.

Changes

Cohort / File(s) Summary
Documentation
README.md
Expanded supported providers and version managers in overview and "Supported Providers" (adds asdf, SDKMAN!, zypper/rpm, apk, pacman, Nix, MacPorts, Scoop, Chocolatey, winget, and others).
Core detection logic
src/why_core.nim
Added path rules for MacPorts and Nix; extended mkContains rules for asdf, SDKMAN!, nvm, fnm, pyenv, rbenv, rvm, Rustup, Conda, Scoop, Chocolatey, winget; removed standalone Volta block; introduced PkgManagerStrategy type and multiple checkPkgManager* procs (Dpkg, Rpm, Apk, Pacman, Portage qfile/equery), refactored checkSystemPackageManager to iterate strategies, and normalized path slash handling in detectProviderByPath.
Unit tests
tests/test_why_core.nim
Added many tests covering zypper/rpm, apk, pacman, portage (qfile/equery), MacPorts, Nix, Scoop, Chocolatey, winget and multiple version-manager path detections; asserts expected provider strings.
E2E infra & tests
tests/e2e/*
tests/e2e/run.sh, tests/e2e/README.md, tests/e2e/common.sh, tests/e2e/Builder.Dockerfile, tests/e2e/why-linux-amd64, tests/e2e/{alpine,arch,fedora,opensuse,ubuntu,gentoo}/*
Added e2e runner and README, builder Dockerfile, distro runtime Dockerfiles and per-distro test scripts, common test helper, and Ubuntu e2e provider shims to simulate many version managers; added tests for Alpine/Arch/Fedora/openSUSE/Gentoo/Ubuntu runtime images.
CI & misc
.github/workflows/ci.yml, .gitignore
Added an E2E job invoking tests/e2e/run.sh; updated .gitignore to ignore tests/e2e/why-linux-amd64 and .serena/.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to:
    • src/why_core.nim — the new PkgManagerStrategy type, checkPkgManager* implementations, iteration/precedence and parsing for pacman/apk/zypper/portage.
    • Path normalization changes in detectProviderByPath.
    • tests/test_why_core.nim and tests/e2e/ubuntu/test.sh — mocked CLI outputs, PATH shim logic, and expected provider strings.
    • tests/e2e/run.sh and Dockerfiles — build/run correctness and parallel execution behavior.

Possibly related PRs

Poem

🐰 I hopped through PATH and bin,
smelled Nix and MacPorts tucked within,
zypper, apk and pacman, too —
asdf, sdk, rust came into view,
I found your provider and munched a carrot. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add provider detection for more managers' accurately summarizes the main change, which adds detection for multiple new package and version managers.
Linked Issues check ✅ Passed All 19 linked issues (8-10, 12-26) requesting provider detection for specific managers are addressed through path-based detection rules and system package manager checks.
Out of Scope Changes check ✅ Passed All changes relate directly to the linked objectives: provider detection implementation, system package manager refactoring, e2e test infrastructure, and documentation updates aligned with the new providers.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/why_core.nim (1)

188-196: Consider combining the zypper/rpm and yum/rpm checks to avoid redundant rpm calls.

When both zypper and rpm exist but rpm -qf fails (e.g., file not from a package), the code falls through to run rpm -qf again at line 194. This is functionally correct but executes the same external command twice.

🔎 Proposed optimization
-  if ctx.findExe("zypper").len > 0 and ctx.findExe("rpm").len > 0:
-    let (outp, exitCode) = ctx.execCmd("rpm -qf " & quoteShell(path))
-    if exitCode == 0:
-      return "zypper/rpm (" & outp.strip() & ")"
-
-  if ctx.findExe("rpm").len > 0:
+  if ctx.findExe("rpm").len > 0:
     let (outp, exitCode) = ctx.execCmd("rpm -qf " & quoteShell(path))
     if exitCode == 0:
-      return "yum/rpm (" & outp.strip() & ")"
+      if ctx.findExe("zypper").len > 0:
+        return "zypper/rpm (" & outp.strip() & ")"
+      else:
+        return "yum/rpm (" & outp.strip() & ")"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3800853 and ddc1971.

📒 Files selected for processing (3)
  • README.md (2 hunks)
  • src/why_core.nim (3 hunks)
  • tests/test_why_core.nim (2 hunks)
🔇 Additional comments (10)
README.md (1)

80-81: Documentation accurately reflects implementation.

The supported providers list is comprehensive and aligns well with the detection rules added in src/why_core.nim. The version managers and package managers are correctly categorized.

src/why_core.nim (4)

53-58: MacPorts and Nix detection patterns look correct.

The paths cover standard installation locations. Minor observation: Nix patterns don't have trailing slashes unlike MacPorts (/opt/local/), but this is acceptable since /nix/store paths are followed by hash-based directories that won't conflict.


233-241: The equery parsing logic correctly handles the output format.

The logic properly extracts the package name from lines like "category/package-version (/path/to/file)" and skips status lines prefixed with *.


198-203: The apk info -W <file> command output is documented to print only the package name, not a formatted string. The test mock at line 162 correctly returns "busybox-1.36.1-r0\n", and the code correctly processes this by taking the first line and stripping whitespace. No format mismatch exists.

Likely an incorrect or invalid review comment.


93-95: Rustup and Cargo rules are correctly distinguished via real path resolution.

The Rustup patterns (".rustup", "rustup/toolchains") don't include ".cargo/bin", which both Rustup and Cargo use as their binary location. However, the current implementation correctly detects Rustup-managed binaries because detectProviderByPath checks the real symlink target (realPath) first before checking the origin path. A Rustup-managed binary like rustc at ~/.cargo/bin/rustc will resolve to a real path containing rustup/toolchains, matching the Rustup pattern before the Cargo rule is evaluated. The test at line 124 validates this behavior.

tests/test_why_core.nim (5)

79-103: Good test coverage for zypper/rpm detection.

The test correctly mocks both zypper and rpm executables and validates the expected provider format.


114-130: Comprehensive test coverage for version manager detection.

Tests cover all newly added version managers with realistic path patterns. The test structure using a table of cases is clean and maintainable.


132-145: Good cross-platform package manager detection tests.

Tests appropriately cover both Unix-style paths (MacPorts, Nix) and Windows-style paths (Scoop, Chocolatey, winget).


147-169: Verify apk test mock matches real output format.

The test mock returns "busybox-1.36.1-r0\n" directly, but if apk info -W returns a format like "/bin/ls is owned by busybox-1.36.1-r0", the parsing logic and test would need adjustment.

Ensure the mock output matches actual apk info -W behavior on Alpine Linux.


219-241: Portage qfile test correctly validates the parsing logic.

The mock output "sys-apps/coreutils-9.2 /usr/bin/ls\n" matches the expected qfile -qv format, and the expected provider "portage (sys-apps/coreutils-9.2)" aligns with the parsing implementation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
tests/e2e/opensuse/Dockerfile (1)

14-14: Bash path inconsistency (see fedora/Dockerfile comment).

This uses /bin/bash while fedora/Dockerfile uses /usr/bin/bash. Consider standardizing across all E2E Dockerfiles as noted in the fedora/Dockerfile review.

tests/e2e/arch/Dockerfile (1)

14-14: Bash path inconsistency (see fedora/Dockerfile comment).

This uses /usr/bin/bash while ubuntu/Dockerfile and opensuse/Dockerfile use /bin/bash. Consider standardizing across all E2E Dockerfiles as noted in the fedora/Dockerfile review.

tests/e2e/opensuse/test.sh (1)

6-15: Extract duplicated helper (see arch/test.sh comment).

Same assert_contains duplication as flagged in arch/test.sh. Apply the same refactor to extract this helper to a shared utility file.

tests/e2e/alpine/test.sh (1)

6-15: Extract duplicated helper (see arch/test.sh comment).

Same assert_contains duplication as flagged in arch/test.sh. Apply the same refactor to extract this helper to a shared utility file.

tests/e2e/ubuntu/test.sh (1)

6-15: Same duplication as in other test files.

This assert_contains function is duplicated across multiple test files. See the comment on tests/e2e/fedora/test.sh lines 6-15 for the refactoring suggestion.

🧹 Nitpick comments (3)
tests/e2e/fedora/Dockerfile (1)

14-14: Consider standardizing bash paths across all E2E Dockerfiles.

This Dockerfile uses /usr/bin/bash while ubuntu/Dockerfile uses /bin/bash. Both are valid, but standardizing on one path across all E2E Dockerfiles would improve consistency and reduce cognitive load. Note that arch/Dockerfile also uses /usr/bin/bash while opensuse/Dockerfile uses /bin/bash.

tests/e2e/fedora/test.sh (1)

6-15: Consider extracting the duplicated helper to a shared file.

The assert_contains function is duplicated identically across five test files (alpine, arch, fedora, opensuse, ubuntu). Extracting it to a shared helper file (e.g., tests/e2e/common.sh) would improve maintainability.

Example refactor

Create tests/e2e/common.sh:

#!/usr/bin/env bash

assert_contains() {
  local haystack="$1"
  local needle="$2"
  if ! grep -qF "$needle" <<<"$haystack"; then
    echo "Expected output to contain: $needle" >&2
    echo "Actual output:" >&2
    echo "$haystack" >&2
    exit 1
  fi
}

Then source it in each test file:

+source "$(dirname "$0")/../common.sh"
+
-assert_contains() {
-  local haystack="$1"
-  local needle="$2"
-  if ! grep -qF "$needle" <<<"$haystack"; then
-    echo "Expected output to contain: $needle" >&2
-    echo "Actual output:" >&2
-    echo "$haystack" >&2
-    exit 1
-  fi
-}
tests/e2e/run.sh (1)

7-9: Optional: Address the Shellcheck warning for more robust argument handling.

Shellcheck flags line 8 for potential word splitting issues. While the current pattern works for simple cases, you could make it more robust:

Alternative approach
-if [[ $# -gt 0 ]]; then
-  DISTROS=($@)
-fi
+if [[ $# -gt 0 ]]; then
+  DISTROS=("$@")
+fi

This properly preserves argument boundaries, though in practice both work for simple distro names.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddc1971 and 8cfd0f1.

📒 Files selected for processing (12)
  • tests/e2e/README.md (1 hunks)
  • tests/e2e/alpine/Dockerfile (1 hunks)
  • tests/e2e/alpine/test.sh (1 hunks)
  • tests/e2e/arch/Dockerfile (1 hunks)
  • tests/e2e/arch/test.sh (1 hunks)
  • tests/e2e/fedora/Dockerfile (1 hunks)
  • tests/e2e/fedora/test.sh (1 hunks)
  • tests/e2e/opensuse/Dockerfile (1 hunks)
  • tests/e2e/opensuse/test.sh (1 hunks)
  • tests/e2e/run.sh (1 hunks)
  • tests/e2e/ubuntu/Dockerfile (1 hunks)
  • tests/e2e/ubuntu/test.sh (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
tests/e2e/fedora/test.sh (4)
tests/e2e/alpine/test.sh (1)
  • assert_contains (6-15)
tests/e2e/arch/test.sh (1)
  • assert_contains (6-15)
tests/e2e/opensuse/test.sh (1)
  • assert_contains (6-15)
tests/e2e/ubuntu/test.sh (1)
  • assert_contains (6-15)
tests/e2e/arch/test.sh (4)
tests/e2e/alpine/test.sh (1)
  • assert_contains (6-15)
tests/e2e/fedora/test.sh (1)
  • assert_contains (6-15)
tests/e2e/opensuse/test.sh (1)
  • assert_contains (6-15)
tests/e2e/ubuntu/test.sh (1)
  • assert_contains (6-15)
tests/e2e/alpine/test.sh (4)
tests/e2e/arch/test.sh (1)
  • assert_contains (6-15)
tests/e2e/fedora/test.sh (1)
  • assert_contains (6-15)
tests/e2e/opensuse/test.sh (1)
  • assert_contains (6-15)
tests/e2e/ubuntu/test.sh (1)
  • assert_contains (6-15)
tests/e2e/ubuntu/test.sh (4)
tests/e2e/alpine/test.sh (1)
  • assert_contains (6-15)
tests/e2e/arch/test.sh (1)
  • assert_contains (6-15)
tests/e2e/fedora/test.sh (1)
  • assert_contains (6-15)
tests/e2e/opensuse/test.sh (1)
  • assert_contains (6-15)
🪛 Shellcheck (0.11.0)
tests/e2e/run.sh

[warning] 8-8: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a.

(SC2206)

🔇 Additional comments (13)
tests/e2e/arch/test.sh (1)

17-18: LGTM! Test logic is correct.

The test correctly executes the why command and asserts that the output contains the expected pacman provider string.

tests/e2e/opensuse/test.sh (1)

17-18: LGTM! Test logic is correct.

The test correctly validates zypper/rpm provider detection for OpenSUSE.

tests/e2e/alpine/test.sh (1)

17-18: LGTM! Test logic is correct.

The test correctly validates apk provider detection for Alpine Linux.

tests/e2e/ubuntu/Dockerfile (1)

1-15: LGTM! Dockerfile follows best practices.

The multi-stage build pattern is correct, with appropriate cleanup of package caches and use of specific version tags. The build efficiently separates the builder and runtime stages.

tests/e2e/fedora/Dockerfile (1)

1-14: LGTM! Dockerfile is well-structured.

The multi-stage build pattern is correct with appropriate package cleanup. The Fedora-specific package manager commands are properly used.

tests/e2e/opensuse/Dockerfile (1)

1-14: LGTM! Dockerfile is well-structured.

The multi-stage build follows best practices with proper package cleanup using zypper. The OpenSUSE-specific commands are correctly applied.

tests/e2e/arch/Dockerfile (1)

1-14: LGTM! Dockerfile is well-structured.

The multi-stage build follows best practices with Arch-specific package manager commands and proper cleanup. The use of --noconfirm flags is appropriate for automated builds.

tests/e2e/README.md (1)

19-19: No action needed. The Ubuntu test script at tests/e2e/ubuntu/test.sh validates all documented providers (apt/dpkg, MacPorts, Nix, asdf, SDKMAN!, nvm, fnm, pyenv, rbenv, rvm, Rustup, Conda, Mise, and Volta) through individual provider detection assertions. The pattern observed in other distros (testing only the primary package manager) does not apply to Ubuntu, which intentionally provides comprehensive multi-provider coverage.

Likely an incorrect or invalid review comment.

tests/e2e/alpine/Dockerfile (1)

1-13: LGTM!

The multi-stage Dockerfile is well-structured. The builder stage compiles the Nim binary, and the runtime stage correctly installs compatibility libraries (gcompat, libc6-compat, libstdc++) needed to run the Nim binary on Alpine.

tests/e2e/fedora/test.sh (1)

17-20: LGTM!

The test correctly verifies that the Why CLI detects yum/rpm as the provider on Fedora systems.

tests/e2e/run.sh (1)

11-24: LGTM!

The main loop correctly validates Dockerfile existence, builds each image, and runs the tests. Error handling is appropriate.

tests/e2e/ubuntu/test.sh (2)

17-33: LGTM!

The helper functions are well-designed. make_bin creates mock executables cleanly, and run_and_assert_provider provides a nice abstraction for testing multiple providers.


35-92: Excellent comprehensive provider coverage!

The test suite thoroughly validates detection of multiple version managers and package managers (MacPorts, Nix, asdf, SDKMAN!, nvm, fnm, pyenv, rbenv, rvm, Rustup, Conda, Mise, Volta). The Rustup test correctly simulates the symlink pattern from cargo bin to rustup toolchain. All tests follow a consistent, maintainable pattern.

Comment thread tests/e2e/arch/test.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/e2e/arch/Dockerfile (2)

1-1: Consider pinning the Nim image version for reproducibility.

Using :latest can introduce non-deterministic builds if the upstream image changes. Pinning to a specific version (e.g., nimlang/nim:2.0.0) ensures consistent test environments.

🔎 Suggested change
-FROM nimlang/nim:latest AS builder
+FROM nimlang/nim:2.0.0 AS builder

9-11: LGTM with optional optimization note.

The package installation and cache cleanup are appropriate for a test container. The -Syu flag upgrades all existing packages, which ensures the latest versions but adds build time. If reproducibility is a concern, consider using -Sy (sync only) instead of -Syu.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cfd0f1 and a55a2e2.

📒 Files selected for processing (6)
  • tests/e2e/alpine/Dockerfile (1 hunks)
  • tests/e2e/arch/Dockerfile (1 hunks)
  • tests/e2e/fedora/Dockerfile (1 hunks)
  • tests/e2e/opensuse/Dockerfile (1 hunks)
  • tests/e2e/ubuntu/Dockerfile (1 hunks)
  • tests/e2e/ubuntu/test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/e2e/fedora/Dockerfile
  • tests/e2e/ubuntu/test.sh
  • tests/e2e/alpine/Dockerfile
  • tests/e2e/opensuse/Dockerfile
  • tests/e2e/ubuntu/Dockerfile
🔇 Additional comments (1)
tests/e2e/arch/Dockerfile (1)

12-14: LGTM!

The binary and test script placement follow standard conventions, and the entrypoint configuration correctly executes the Arch-specific e2e test.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
tests/e2e/README.md (1)

19-19: Consider breaking up the ubuntu provider list for readability.

Line 19 is quite long and cramped, making it harder to scan the list of providers tested on Ubuntu. Consider wrapping or formatting it into multiple lines or a more structured layout.

🔎 Proposed formatting improvement
-- ubuntu: apt/dpkg + path-based providers (asdf/SDKMAN!/nvm/fnm/pyenv/rbenv/rvm/rustup/conda/mise/volta/macports/nix)
+- ubuntu: apt/dpkg + path-based providers:
+  - asdf, SDKMAN!, nvm, fnm, pyenv, rbenv, rvm, rustup, conda, mise, volta, macports, nix

Alternatively, use a table or bullet list to make it visually clearer.

src/why_core.nim (2)

185-264: Consider adding timeouts for external command execution.

The package manager check functions are well-structured and use quoteShell for security. However, the execCmd calls have no timeout mechanism, which could cause the tool to hang if a package manager command becomes unresponsive.

Consider adding a timeout mechanism to ctx.execCmd or implementing timeout wrappers for these package manager checks to improve reliability and user experience.


196-206: Consider checking for dnf in the rpm-based system detection.

The current implementation distinguishes between "zypper/rpm" and "yum/rpm" based solely on zypper's presence. Modern Fedora systems use dnf (not yum), so you might want to also check for dnf to provide "dnf/rpm" output for better accuracy.

Optional enhancement to detect dnf
 proc checkPkgManagerRpm(path: string, ctx: WhyCtx): string =
   let hasRpm = ctx.findExe("rpm").len > 0
   let hasZypper = ctx.findExe("zypper").len > 0
+  let hasDnf = ctx.findExe("dnf").len > 0
   if not hasRpm:
     return ""
   let (outp, exitCode) = ctx.execCmd("rpm -qf " & quoteShell(path))
   if exitCode != 0:
     return ""
   if hasZypper:
     return "zypper/rpm (" & outp.strip() & ")"
+  if hasDnf:
+    return "dnf/rpm (" & outp.strip() & ")"
   return "yum/rpm (" & outp.strip() & ")"
tests/e2e/gentoo/Dockerfile (1)

9-9: Pin the Gentoo base image to a specific date or digest for reproducibility.

Using gentoo/stage3:latest makes the build non-deterministic. The Gentoo stage3 images change over time, which could cause tests to break or behave inconsistently without any code changes.

Consider using a dated tag or image digest for reproducibility:

-FROM gentoo/stage3:latest
+FROM gentoo/stage3:20241220

Or use a digest:

-FROM gentoo/stage3:latest
+FROM gentoo/stage3@sha256:...
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9f7995 and a6493d3.

📒 Files selected for processing (16)
  • src/why_core.nim (3 hunks)
  • tests/e2e/README.md (1 hunks)
  • tests/e2e/alpine/Dockerfile (1 hunks)
  • tests/e2e/alpine/test.sh (1 hunks)
  • tests/e2e/arch/Dockerfile (1 hunks)
  • tests/e2e/arch/test.sh (1 hunks)
  • tests/e2e/common.sh (1 hunks)
  • tests/e2e/fedora/Dockerfile (1 hunks)
  • tests/e2e/fedora/test.sh (1 hunks)
  • tests/e2e/gentoo/Dockerfile (1 hunks)
  • tests/e2e/gentoo/test.sh (1 hunks)
  • tests/e2e/opensuse/Dockerfile (1 hunks)
  • tests/e2e/opensuse/test.sh (1 hunks)
  • tests/e2e/run.sh (1 hunks)
  • tests/e2e/ubuntu/Dockerfile (1 hunks)
  • tests/e2e/ubuntu/test.sh (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • tests/e2e/opensuse/test.sh
  • tests/e2e/fedora/Dockerfile
  • tests/e2e/arch/test.sh
  • tests/e2e/run.sh
  • tests/e2e/arch/Dockerfile
  • tests/e2e/alpine/test.sh
  • tests/e2e/fedora/test.sh
  • tests/e2e/alpine/Dockerfile
🧰 Additional context used
🧬 Code graph analysis (2)
tests/e2e/ubuntu/test.sh (1)
tests/e2e/common.sh (1)
  • assert_contains (4-13)
tests/e2e/gentoo/test.sh (1)
tests/e2e/common.sh (1)
  • assert_contains (4-13)
🔇 Additional comments (9)
tests/e2e/README.md (1)

1-30: Documentation structure and content look good.

This README clearly explains the purpose of E2E tests, how to run them (with examples), distro coverage, and important operational notes. The references to Alpine's glibc compat layer and Gentoo's stage3 approach are helpful for maintainers.

tests/e2e/gentoo/test.sh (1)

1-10: LGTM! Clean and focused Gentoo e2e test.

The test script follows the established e2e testing pattern with strict shell options, proper sourcing of common utilities, and clear provider assertion.

tests/e2e/ubuntu/Dockerfile (1)

1-16: LGTM! Well-structured multi-stage Dockerfile.

The two-stage build properly separates compilation from runtime, uses specific version tags, cleans up package lists, and follows Docker best practices.

tests/e2e/opensuse/Dockerfile (1)

1-15: LGTM! Consistent with the established Dockerfile pattern.

The multi-stage build follows the same structure as other distro Dockerfiles, uses a specific OpenSUSE version, and properly cleans up after package installation.

src/why_core.nim (2)

53-107: Excellent expansion of provider detection rules.

The new provider rules cover a comprehensive set of package and version managers with appropriate path patterns. The dual Windows path separators (both / and \) ensure cross-platform compatibility.


266-280: Excellent refactoring to a pluggable package manager detection system.

The modular design with PkgManagerRef makes the code more maintainable and extensible. The first-match strategy is appropriate for the use case.

tests/e2e/common.sh (1)

1-13: LGTM! Clean and focused test helper.

The assert_contains function uses grep -qF for literal matching and provides clear diagnostic output on failure. The strict shell options ensure robust execution.

tests/e2e/ubuntu/test.sh (2)

7-23: Well-designed test helpers for comprehensive provider testing.

The make_bin and run_and_assert_provider helpers provide clean abstractions for creating mock executables and validating provider detection. This makes the test cases below very readable.


29-82: Excellent comprehensive coverage of provider detection.

The test suite covers 13 different version managers and package managers with realistic path structures. The rustup test (lines 65-69) is particularly well-designed, correctly simulating rustup's symlink structure from .cargo/bin to the actual toolchain directory.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
tests/e2e/Builder.Dockerfile (2)

3-5: Consider adding --no-install-recommends flag.

Adding the --no-install-recommends flag to the apt-get install command would reduce the image size by avoiding unnecessary recommended packages.

🔎 Proposed optimization
 RUN apt-get update \
-  && apt-get install -y --no-install-recommends git ca-certificates \
+  && apt-get install -y --no-install-recommends git ca-certificates \
   && rm -rf /var/lib/apt/lists/*

Wait, I see it already has --no-install-recommends on line 4. Let me re-check the original code.


6-6: Consider using .dockerignore to optimize context size.

The COPY . . command copies the entire project directory, which may include unnecessary files like .git, test artifacts, or local build outputs. Using a .dockerignore file would reduce build context size and improve build performance.

tests/e2e/run.sh (1)

16-31: Consider adding binary validation after extraction.

The binary extraction logic is correct and includes proper cleanup. However, adding a quick validation step would catch issues early if the wrong architecture is built or the binary is corrupted.

Optional: Add basic validation
  docker cp "$container_id":/usr/local/bin/why "$BIN_PATH"
  docker rm -v "$container_id" >/dev/null
  chmod +x "$BIN_PATH"
+  if ! file "$BIN_PATH" | grep -q "ELF 64-bit"; then
+    echo "Error: Binary is not a valid Linux 64-bit executable" >&2
+    return 1
+  fi
  echo "==> Binary ready: ${BIN_PATH}"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6493d3 and 530c14a.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml (1 hunks)
  • .gitignore (2 hunks)
  • src/why_core.nim (3 hunks)
  • tests/e2e/Builder.Dockerfile (1 hunks)
  • tests/e2e/README.md (1 hunks)
  • tests/e2e/alpine/Dockerfile (1 hunks)
  • tests/e2e/arch/Dockerfile (1 hunks)
  • tests/e2e/fedora/Dockerfile (1 hunks)
  • tests/e2e/gentoo/Dockerfile (1 hunks)
  • tests/e2e/opensuse/Dockerfile (1 hunks)
  • tests/e2e/run.sh (1 hunks)
  • tests/e2e/ubuntu/Dockerfile (1 hunks)
  • tests/test_why_core.nim (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/e2e/alpine/Dockerfile
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/gentoo/Dockerfile
  • tests/e2e/README.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test (macos-latest)
  • GitHub Check: e2e
🔇 Additional comments (23)
.gitignore (1)

6-6: LGTM!

The ignore patterns are appropriate: the e2e binary artifact aligns with the Builder.Dockerfile output, and the .serena/ directory pattern follows standard conventions.

Also applies to: 15-18

tests/e2e/Builder.Dockerfile (1)

7-8: LGTM!

The build and installation commands are correct. Using -d:release for optimization and setting appropriate executable permissions (0755) are good practices.

tests/test_why_core.nim (6)

79-103: LGTM!

The zypper detection test is well-structured with appropriate mocking and assertions. It correctly validates the new zypper/rpm package manager detection strategy.


123-139: LGTM!

The parameterized test effectively validates path-based detection for all the newly added version managers. The test cases use realistic paths and cover the full scope of changes.


141-154: LGTM!

The package manager path detection test provides excellent coverage, including Windows-specific paths with backslashes and spaces. This validates the path normalization logic introduced in the core implementation.


156-178: LGTM!

The APK package manager detection test correctly validates Alpine Linux support with appropriate mocking and expected output format.


180-202: LGTM!

The pacman test accurately simulates Arch Linux package manager behavior with realistic output format and proper parsing expectations.


204-226: LGTM!

The Portage qfile test correctly validates Gentoo package manager support with appropriate output format and parsing logic.

src/why_core.nim (8)

53-58: LGTM!

The MacPorts and Nix detection rules use appropriate patterns and match types. The paths align with standard installation locations for these package managers.


72-107: LGTM!

Excellent comprehensive coverage of version and package managers. The patterns are accurate and include common installation paths and variations (e.g., fnm's multiple locations, Conda's various distributions, rvm's user/system paths).


161-181: LGTM!

The path normalization approach elegantly handles cross-platform path separators without duplicating rules. Normalizing all paths and patterns to forward slashes ensures consistent matching across Windows and Unix systems.


183-187: LGTM!

The PkgManagerStrategy type provides a clean, extensible abstraction for package manager detection. This design makes it easy to add new package managers in the future.


188-197: LGTM!

The dpkg detection implementation is secure (uses quoteShell) and robust (checks for command availability, exit code, and output format). The error handling is appropriate.


199-209: LGTM!

The RPM-based detection intelligently distinguishes between zypper (openSUSE) and yum (RHEL/Fedora/CentOS) frontends while using the underlying rpm database. Security and error handling are appropriate.


211-267: LGTM!

All four package manager detection functions (apk, pacman, Portage qfile, Portage equery) follow consistent patterns with proper security (quoteShell), error handling, and output parsing. Having both qfile and equery for Portage provides fallback coverage since qfile comes from portage-utils which might not always be installed.


269-283: LGTM!

The refactored checkSystemPackageManager function effectively implements the pluggable strategy pattern. The function iterates through strategies and returns the first successful detection, making it straightforward to add new package managers.

tests/e2e/fedora/Dockerfile (1)

1-7: LGTM!

The Fedora e2e Dockerfile follows the same clean pattern as other distro test images: minimal package installation, cache cleanup, and straightforward test execution setup.

.github/workflows/ci.yml (1)

46-55: LGTM!

The e2e job is well-configured with an appropriate timeout and parallel execution. Running alongside the existing test job provides comprehensive CI coverage.

tests/e2e/ubuntu/Dockerfile (1)

1-8: LGTM!

The Ubuntu e2e Dockerfile follows Docker best practices: uses LTS version, minimizes installed packages with --no-install-recommends, and properly cleans the apt cache to reduce image size.

tests/e2e/arch/Dockerfile (1)

1-7: LGTM!

The Arch Linux e2e Dockerfile correctly uses pacman commands with appropriate flags for package installation and cache cleanup. The pattern is consistent with other distribution test images.

tests/e2e/opensuse/Dockerfile (1)

1-7: LGTM!

The Dockerfile follows best practices: installs necessary packages, cleans up caches, copies test artifacts, and sets a clear entrypoint. The structure is consistent with the e2e test harness pattern.

tests/e2e/run.sh (2)

1-14: LGTM!

The script setup follows best practices: strict error handling with set -euo pipefail, robust root directory discovery, configurable distro list, and sensible parallelism defaults.


33-48: LGTM!

The run_one function is well-structured with clear validation, informative progress messages, and appropriate error handling.

Comment thread tests/e2e/run.sh
@akriaueno
akriaueno merged commit 9a87f67 into develop Dec 20, 2025
6 checks passed
@akriaueno
akriaueno deleted the feat/add-more-version-managers branch May 30, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant