Consolidates the last duplicated derivations, and removes the ansible
tree along with the CLI surface that existed only to drive it.
Naming rules:
* agebd/names.py holds ServiceName -- the single definition of how a
service name is spelled in each context: dir_name, module_name,
ioc_name, systemd_unit_name and the case forms. The environment
suffix is deliberately not part of it; that stays a deploy-time
token applied to these base names.
* cli's Service model delegates every name_* property to it, so the
CLI and the CI scripts derive names the same way.
* scripts/ci/service_names.py emits the derived forms as shell
assignments, and deploy-and-restart-service.sh evals them instead of
carrying its own tr calls and its own copy of the systemd naming
convention. Values are shell-quoted, and an invalid name exits
non-zero emitting nothing, so an eval cannot leave empty variables
behind.
Registry lookups:
* agebd.subs.find_service_entry is now the only place that answers
"which registry entry is this service", used by read_ioc_port,
get_ioc_port.py and cli's ServiceRegistry. get_ioc_port.py no longer
parses the registry itself.
* That also settles a semantic split: ServiceRegistry matched names
case-sensitively while read_ioc_port did not. Both now match
case-insensitively via ServiceName, so 'tune', 'Tune' and 'TUNE'
resolve to the same service everywhere.
Ansible removal:
* ansible/ is deleted, along with cli/src/ioc.py, cli/src/core/
ansible.py, the IOC_ENV enum, the ansible and ansible-runner
dependencies, and docs/developer/ansible.md.
* This removes the 'agebd ioc install|start|restart' commands. They
were pure playbook wrappers, so they could not outlive the
playbooks; IOC install, start and restart now have no automation
path in this repo.
bin/start_service.sh keeps deriving its two name forms in bash, with a
comment saying why: it runs on the deploy host before the service venv
exists, so it cannot import agebd. It is the one deliberate exception.
Verified: get_ioc_port.py, cli's ServiceRegistry and agebd's renderer all
return 50007 for 'tune', 'Tune' and 'TUNE'; the shell-emitted names match
the committed on-disk layout for real services including the dashed
OpticsFF-X02S; the CLI runs with only the 'service' command left.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
2.0 KiB
Python
73 lines
2.0 KiB
Python
import subprocess
|
|
import sys
|
|
|
|
import pytest
|
|
from agebd.names import InvalidServiceName
|
|
from agebd.utils import get_git_root
|
|
|
|
from ci.service_names import EXPORTED, shell_assignments
|
|
|
|
SCRIPT = get_git_root(__file__) / "scripts" / "ci" / "service_names.py"
|
|
|
|
|
|
def run_in_shell(name):
|
|
"""Eval the output in a real shell and read the variables back."""
|
|
assignments = shell_assignments(name)
|
|
readback = "\n".join(f'printf "%s=%s\\n" {var} "${var}"' for var in EXPORTED)
|
|
out = subprocess.run(
|
|
["bash", "-c", f"{assignments}\n{readback}"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
).stdout
|
|
return dict(line.split("=", 1) for line in out.strip().splitlines())
|
|
|
|
|
|
def test_emits_every_derived_form():
|
|
values = run_in_shell("TauBPM")
|
|
|
|
assert values == {
|
|
"SERVICE_NAME_CAMEL": "TauBPM",
|
|
"SERVICE_NAME_UPPER": "TAUBPM",
|
|
"SERVICE_NAME_LOWER": "taubpm",
|
|
"SERVICE_DIR_NAME": "taubpm",
|
|
"SERVICE_MODULE_NAME": "agebd_taubpm",
|
|
"IOC_NAME": "AGEBD-CPCL-TAUBPM",
|
|
"SYSTEMD_UNIT_NAME": "AGEBD-SERVICE-TAUBPM.service",
|
|
}
|
|
|
|
|
|
def test_dashed_name_survives_the_shell():
|
|
values = run_in_shell("OpticsFF-X02S")
|
|
|
|
assert values["SERVICE_DIR_NAME"] == "opticsff-x02s"
|
|
assert values["SERVICE_MODULE_NAME"] == "agebd_opticsff_x02s"
|
|
assert values["IOC_NAME"] == "AGEBD-CPCL-OPTICSFF-X02S"
|
|
|
|
|
|
def test_invalid_name_raises():
|
|
with pytest.raises(InvalidServiceName):
|
|
shell_assignments("Tune; rm -rf /")
|
|
|
|
|
|
def test_cli_exits_non_zero_and_emits_nothing_on_a_bad_name():
|
|
result = subprocess.run(
|
|
[sys.executable, str(SCRIPT), "Tune Bump"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
|
|
assert result.returncode == 1
|
|
assert result.stdout == ""
|
|
assert "Invalid service name" in result.stderr
|
|
|
|
|
|
def test_cli_requires_exactly_one_argument():
|
|
result = subprocess.run(
|
|
[sys.executable, str(SCRIPT)], capture_output=True, text=True, check=False
|
|
)
|
|
|
|
assert result.returncode == 2
|
|
assert "usage:" in result.stderr
|