mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
An inventory group web-app-cst-<name> with applications.<id>.custom: true and
a pinned source {url, version, path} is pulled into roles/ before the play,
read raw (Jinja, Ansible tags and credential URLs refused), placed through a
staging directory under an exclusive lock, and owned by a marker that names
its own directory, so a pull or roles-clean never replaces or removes a
directory it did not place and a failed pull leaves roles/ as it was.
Directory inventories, host lists and non-UTF-8 files behave as Ansible
reads them; generated test deploys pass --skip-pull. make roles-pull,
roles-clean and roles list, INFINITO_EXTRA_MOUNTS for local sources, the
web-app-cst category, the reserved prefix lint and a central
repository_files() that also yields the gitignored custom roles let every
lint scan them like in-tree roles. Supersedes the uncommitted 045 External
Roles draft and reuses its fetch, sync and mount design.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
98 lines
3.2 KiB
Python
98 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
from cli.administration.deploy.dedicated import command
|
|
from cli.administration.deploy.dedicated.command import (
|
|
_normalize_app_ids,
|
|
pull_custom_roles,
|
|
)
|
|
|
|
|
|
class TestPullCustomRoles(unittest.TestCase):
|
|
@mock.patch("cli.administration.deploy.dedicated.command.pull_or_exit")
|
|
def test_pulls_the_inventory_by_default(self, pull_or_exit):
|
|
pull_custom_roles(SimpleNamespace(inventory="inv/devices.yml", skip_pull=False))
|
|
pull_or_exit.assert_called_once_with("inv/devices.yml")
|
|
|
|
@mock.patch("cli.administration.deploy.dedicated.command.pull_or_exit")
|
|
def test_skip_pull_keeps_the_placed_roles(self, pull_or_exit):
|
|
pull_custom_roles(SimpleNamespace(inventory="inv/devices.yml", skip_pull=True))
|
|
pull_or_exit.assert_not_called()
|
|
|
|
def test_main_pulls_before_anything_reads_roles(self):
|
|
calls: list[str] = []
|
|
args = SimpleNamespace(inventory="inv/devices.yml", skip_pull=False)
|
|
with (
|
|
mock.patch.object(command, "parse_args", return_value=(args, [], {})),
|
|
mock.patch.object(
|
|
command, "pull_or_exit", side_effect=lambda _inv: calls.append("pull")
|
|
),
|
|
mock.patch.object(
|
|
command,
|
|
"run_from_args",
|
|
side_effect=lambda *_a: calls.append("deploy") or 0,
|
|
),
|
|
):
|
|
self.assertEqual(0, command.main([]))
|
|
self.assertEqual(["pull", "deploy"], calls)
|
|
|
|
|
|
class TestNormalizeAppIds(unittest.TestCase):
|
|
def test_empty(self):
|
|
self.assertEqual(_normalize_app_ids([]), [])
|
|
|
|
def test_single(self):
|
|
self.assertEqual(_normalize_app_ids(["mailu"]), ["mailu"])
|
|
|
|
def test_space_separated(self):
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu", "keycloak", "matomo"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
def test_comma_separated(self):
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu,keycloak,matomo"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
def test_mixed_space_and_comma(self):
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu,keycloak", "matomo"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu", "keycloak,matomo"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
def test_trimming_and_empty_entries(self):
|
|
self.assertEqual(
|
|
_normalize_app_ids([" mailu , keycloak , matomo "]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu,,keycloak", ",matomo,"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
def test_deduplication_preserves_order(self):
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu", "mailu", "keycloak", "mailu"]),
|
|
["mailu", "keycloak"],
|
|
)
|
|
|
|
self.assertEqual(
|
|
_normalize_app_ids(["mailu,keycloak", "keycloak,matomo", "mailu"]),
|
|
["mailu", "keycloak", "matomo"],
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|