mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 04:36:40 +00:00
The lint installer imports this module, and it runs on the runner's bare interpreter before a single dependency exists. Sharing the version check with it therefore handed the bootstrap a PyYAML import, which killed it: 'ModuleNotFoundError: No module named yaml' raised through utils/install/lint/__main__.py:35. Routing the read through utils.cache.yaml would not have helped, that imports yaml too. The requirements files are this repository's own and hold one shape, so the pins are read with two regexes and the standard library. A line that does not match the shape leaves the entry unpinned, and unpinned already counts as unsatisfied, so the parser errs towards installing rather than towards skipping. Quoted versions are unwrapped: yaml accepts version: "13.4.0" and a formatter may introduce it, and carrying the quotes into the comparison would silently defeat the check instead of failing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
119 lines
4.1 KiB
Python
119 lines
4.1 KiB
Python
import json
|
|
import unittest
|
|
from pathlib import Path
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from utils.install.collections import declared_pins, unsatisfied
|
|
|
|
REQUIREMENTS = """---
|
|
collections:
|
|
- name: community.general
|
|
version: 13.4.0
|
|
- name: hetzner.hcloud
|
|
version: 7.0.1
|
|
"""
|
|
|
|
|
|
def _write_requirements(root: Path, body: str = REQUIREMENTS) -> Path:
|
|
path = root / "requirements.galaxy.yml"
|
|
path.write_text(body, encoding="utf-8")
|
|
return path
|
|
|
|
|
|
def _install(collections_dir: Path, fqcn: str, version: str) -> None:
|
|
namespace, _, name = fqcn.partition(".")
|
|
target = collections_dir / "ansible_collections" / namespace / name
|
|
target.mkdir(parents=True)
|
|
(target / "MANIFEST.json").write_text(
|
|
json.dumps({"collection_info": {"version": version}}), encoding="utf-8"
|
|
)
|
|
|
|
|
|
class TestDeclaredPins(unittest.TestCase):
|
|
def test_a_quoted_version_is_read_without_its_quotes(self):
|
|
body = (
|
|
"---\ncollections:\n"
|
|
' - name: double.quoted\n version: "13.4.0"\n'
|
|
" - name: single.quoted\n version: '1.0.0'\n"
|
|
" - name: commented\n version: 2.2.2 # pinned\n"
|
|
)
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root, body)
|
|
|
|
self.assertEqual(
|
|
declared_pins(requirements),
|
|
[
|
|
("double.quoted", "13.4.0"),
|
|
("single.quoted", "1.0.0"),
|
|
("commented", "2.2.2"),
|
|
],
|
|
)
|
|
|
|
def test_an_entry_without_a_version_stays_unpinned(self):
|
|
body = "---\ncollections:\n - name: bare.entry\n"
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
|
|
self.assertEqual(
|
|
declared_pins(_write_requirements(root, body)), [("bare.entry", None)]
|
|
)
|
|
|
|
def test_a_missing_file_declares_nothing(self):
|
|
with TemporaryDirectory() as tmp:
|
|
self.assertEqual(declared_pins(Path(tmp) / "absent.yml"), [])
|
|
|
|
|
|
class TestUnsatisfied(unittest.TestCase):
|
|
def test_every_pin_installed_at_its_version_is_satisfied(self):
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root)
|
|
_install(root, "community.general", "13.4.0")
|
|
_install(root, "hetzner.hcloud", "7.0.1")
|
|
|
|
self.assertEqual(unsatisfied(requirements, root), [])
|
|
|
|
def test_absent_collection_is_reported(self):
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root)
|
|
_install(root, "community.general", "13.4.0")
|
|
|
|
self.assertEqual(unsatisfied(requirements, root), ["hetzner.hcloud:7.0.1"])
|
|
|
|
def test_wrong_version_is_reported_although_the_directory_exists(self):
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root)
|
|
_install(root, "community.general", "13.4.0")
|
|
_install(root, "hetzner.hcloud", "6.9.0")
|
|
|
|
self.assertEqual(unsatisfied(requirements, root), ["hetzner.hcloud:7.0.1"])
|
|
|
|
def test_unpinned_entry_can_never_be_confirmed(self):
|
|
body = "---\ncollections:\n - name: community.general\n"
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root, body)
|
|
_install(root, "community.general", "13.4.0")
|
|
|
|
self.assertEqual(unsatisfied(requirements, root), ["community.general"])
|
|
|
|
def test_unreadable_manifest_is_reported(self):
|
|
with TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
requirements = _write_requirements(root)
|
|
_install(root, "community.general", "13.4.0")
|
|
_install(root, "hetzner.hcloud", "7.0.1")
|
|
manifest = (
|
|
root / "ansible_collections" / "hetzner" / "hcloud" / "MANIFEST.json"
|
|
)
|
|
manifest.write_text("{not json", encoding="utf-8")
|
|
|
|
self.assertEqual(unsatisfied(requirements, root), ["hetzner.hcloud:7.0.1"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|