mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
fix(install): provision minitest next to ruby on Arch and the RedHat family
The Manjaro workspace job failed test-unit-ruby with "cannot load such file -- minitest". Debian vendors minitest in libruby, but Arch ships it as ruby-minitest and Fedora and CentOS Stream as rubygem-minitest, none of which ruby pulls in. ensure_ruby_present now probes `require "minitest"` and installs the distro package when the probe fails, then fails loudly if minitest is still missing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2756fd56f1
commit
e6f552ca0a
4 files changed
+115
-8
No files matched your search
@@ -893,7 +893,7 @@ test-unit-python: install
|
||||
bash scripts/tests/code/wrapper.sh scripts/tests/code/run.sh
|
||||
|
||||
.PHONY: test-unit-ruby
|
||||
# Run the Ruby unit test suite (minitest, stdlib).
|
||||
# Run the Ruby unit test suite (minitest).
|
||||
test-unit-ruby:
|
||||
@INFINITO_TEST_TYPE="unit-ruby" \
|
||||
bash scripts/tests/code/wrapper.sh scripts/tests/unit/ruby.sh
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Runs the Ruby unit suite through minitest, which ships with Ruby itself.
|
||||
# Runs the Ruby unit suite through minitest, which scripts/install/ruby.sh provisions.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
@@ -9,19 +9,75 @@ from utils.install import ruby as ruby_mod
|
||||
|
||||
|
||||
class TestEnsureRubyPresent(unittest.TestCase):
|
||||
def test_delegates_to_the_shared_provisioner(self) -> None:
|
||||
with mock.patch.object(ruby_mod, "ensure_command_present") as ensure:
|
||||
def test_provisions_ruby_then_minitest(self) -> None:
|
||||
calls = mock.Mock()
|
||||
with (
|
||||
mock.patch.object(ruby_mod, "ensure_command_present", calls.ruby),
|
||||
mock.patch.object(ruby_mod, "ensure_minitest_present", calls.minitest),
|
||||
):
|
||||
ruby_mod.ensure_ruby_present()
|
||||
ensure.assert_called_once_with("ruby")
|
||||
self.assertEqual(
|
||||
calls.mock_calls, [mock.call.ruby("ruby"), mock.call.minitest()]
|
||||
)
|
||||
|
||||
def test_propagates_the_hard_failure(self) -> None:
|
||||
with (
|
||||
mock.patch.object(
|
||||
ruby_mod, "ensure_command_present", side_effect=RuntimeError("boom")
|
||||
),
|
||||
mock.patch.object(ruby_mod, "ensure_minitest_present") as minitest,
|
||||
self.assertRaises(RuntimeError),
|
||||
):
|
||||
ruby_mod.ensure_ruby_present()
|
||||
minitest.assert_not_called()
|
||||
|
||||
|
||||
class TestEnsureMinitestPresent(unittest.TestCase):
|
||||
def run_with(self, *, loadable: list[bool], manager: str) -> mock.Mock:
|
||||
"""Run ``ensure_minitest_present`` with stubbed probe and installer.
|
||||
|
||||
Args:
|
||||
loadable: successive results of the minitest probe.
|
||||
manager: package manager the host reports.
|
||||
|
||||
Returns:
|
||||
The stubbed ``install_package_candidates``.
|
||||
"""
|
||||
with (
|
||||
mock.patch.object(ruby_mod, "minitest_loadable", side_effect=loadable),
|
||||
mock.patch.object(ruby_mod, "detect_package_manager", return_value=manager),
|
||||
mock.patch.object(ruby_mod, "install_package_candidates") as install,
|
||||
mock.patch.object(ruby_mod, "log"),
|
||||
):
|
||||
ruby_mod.ensure_minitest_present()
|
||||
return install
|
||||
|
||||
def test_a_loadable_minitest_installs_nothing(self) -> None:
|
||||
install = self.run_with(loadable=[True], manager="pacman")
|
||||
install.assert_not_called()
|
||||
|
||||
def test_arch_installs_ruby_minitest(self) -> None:
|
||||
install = self.run_with(loadable=[False, True], manager="pacman")
|
||||
install.assert_called_once_with("pacman", ["ruby-minitest"])
|
||||
|
||||
def test_the_redhat_family_installs_rubygem_minitest(self) -> None:
|
||||
install = self.run_with(loadable=[False, True], manager="dnf")
|
||||
install.assert_called_once_with("dnf", ["rubygem-minitest"])
|
||||
|
||||
def test_still_missing_after_the_install_fails_loudly(self) -> None:
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.run_with(loadable=[False, False], manager="pacman")
|
||||
|
||||
def test_an_unmapped_manager_fails_without_installing(self) -> None:
|
||||
with (
|
||||
mock.patch.object(ruby_mod, "minitest_loadable", return_value=False),
|
||||
mock.patch.object(ruby_mod, "detect_package_manager", return_value="brew"),
|
||||
mock.patch.object(ruby_mod, "install_package_candidates") as install,
|
||||
mock.patch.object(ruby_mod, "log"),
|
||||
self.assertRaises(RuntimeError),
|
||||
):
|
||||
ruby_mod.ensure_minitest_present()
|
||||
install.assert_not_called()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+54
-3
@@ -2,16 +2,67 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from utils.install.system_pkg import ensure_command_present
|
||||
import subprocess
|
||||
|
||||
from utils.install.primitives import log
|
||||
from utils.install.system_pkg import (
|
||||
detect_package_manager,
|
||||
ensure_command_present,
|
||||
install_package_candidates,
|
||||
)
|
||||
|
||||
_MINITEST_PACKAGES: dict[str, list[str]] = {
|
||||
"pacman": ["ruby-minitest"],
|
||||
"dnf": ["rubygem-minitest"],
|
||||
"yum": ["rubygem-minitest"],
|
||||
"apt-get": ["ruby-minitest"],
|
||||
}
|
||||
|
||||
|
||||
def minitest_loadable() -> bool:
|
||||
"""Return whether the installed ruby can require minitest.
|
||||
|
||||
Returns:
|
||||
``True`` when ``ruby -e 'require "minitest"'`` exits 0.
|
||||
"""
|
||||
probe = subprocess.run(
|
||||
["ruby", "-e", 'require "minitest"'], capture_output=True, check=False
|
||||
)
|
||||
return probe.returncode == 0
|
||||
|
||||
|
||||
def ensure_minitest_present() -> None:
|
||||
"""Make minitest loadable, installing its package if ruby lacks it.
|
||||
|
||||
Raises:
|
||||
RuntimeError: no package mapping for the detected package manager, or
|
||||
minitest is still not loadable after the install.
|
||||
"""
|
||||
if minitest_loadable():
|
||||
return
|
||||
|
||||
manager = detect_package_manager()
|
||||
packages = _MINITEST_PACKAGES.get(manager)
|
||||
if packages is None:
|
||||
raise RuntimeError(f"No installer mapping defined for minitest on '{manager}'.")
|
||||
|
||||
log(f"Ruby cannot load minitest. Attempting installation via {manager}.")
|
||||
install_package_candidates(manager, packages)
|
||||
|
||||
if not minitest_loadable():
|
||||
raise RuntimeError(
|
||||
f"minitest still not loadable after installing {packages} via {manager}"
|
||||
)
|
||||
|
||||
|
||||
def ensure_ruby_present() -> None:
|
||||
"""Install the Ruby interpreter through the system package manager.
|
||||
"""Install the Ruby interpreter and minitest through the system package manager.
|
||||
|
||||
Raises:
|
||||
RuntimeError: ruby is still absent after the install attempt.
|
||||
RuntimeError: ruby or minitest is still absent after the install attempt.
|
||||
"""
|
||||
ensure_command_present("ruby")
|
||||
ensure_minitest_present()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user