2026-08-23 15:54:56 -03:00
|
|
|
"""Contracts for the bounded Hermes private-voice image release lane."""
|
|
|
|
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
PIPELINE = ROOT / "ci/Jenkinsfile.hermes-voice-image"
|
|
|
|
|
JOBS = ROOT / "services/jenkins/configmap-jcasc.yaml"
|
|
|
|
|
TRIGGER = ROOT / "services/hermes/scripts/jenkins_image_build_trigger.py"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_voice_pipeline_binds_component_source_digest_and_release() -> None:
|
|
|
|
|
pipeline = PIPELINE.read_text(encoding="utf-8")
|
|
|
|
|
assert "IMAGE_COMPONENT must be stt or tts" in pipeline
|
|
|
|
|
assert "git merge-base --is-ancestor" in pipeline
|
|
|
|
|
assert "git status --porcelain" in pipeline
|
|
|
|
|
assert "Dockerfile.hermes-jetson-${component}" in pipeline
|
|
|
|
|
assert "--digest-file=" in pipeline
|
|
|
|
|
assert "hermes_oci_promote.py" in pipeline
|
|
|
|
|
assert "PUBLISH HERMES STT" in pipeline
|
|
|
|
|
assert "PUBLISH HERMES TTS" in pipeline
|
|
|
|
|
assert "archiveArtifacts(" in pipeline
|
2026-08-23 16:05:09 -03:00
|
|
|
assert '"SETFCAP"' in pipeline
|
2026-08-23 17:53:05 -03:00
|
|
|
assert "values: [titan-20]" in pipeline
|
2026-08-23 18:53:11 -03:00
|
|
|
assert "key: node.kubernetes.io/not-ready" in pipeline
|
|
|
|
|
assert "key: node.kubernetes.io/unreachable" in pipeline
|
|
|
|
|
assert pipeline.count("tolerationSeconds: 600") == 2
|
2026-08-23 17:53:05 -03:00
|
|
|
assert 'node-role.kubernetes.io/worker: "true"' not in pipeline
|
2026-08-23 16:33:17 -03:00
|
|
|
assert "ephemeral-storage: 10Gi" in pipeline
|
|
|
|
|
assert "ephemeral-storage: 20Gi" in pipeline
|
2026-08-23 20:01:55 -03:00
|
|
|
assert "memory: 2Gi" in pipeline
|
|
|
|
|
assert "memory: 6Gi" in pipeline
|
|
|
|
|
assert "--compressed-caching=false" in pipeline
|
|
|
|
|
assert "--snapshot-mode=redo" in pipeline
|
2026-08-23 16:05:09 -03:00
|
|
|
assert "privileged: true" not in pipeline
|
2026-08-23 15:54:56 -03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_jenkins_declares_one_token_guarded_voice_job() -> None:
|
|
|
|
|
jobs = JOBS.read_text(encoding="utf-8")
|
|
|
|
|
assert jobs.count("pipelineJob('hermes-voice-image')") == 1
|
|
|
|
|
block = jobs.split("pipelineJob('hermes-voice-image')", 1)[1].split(
|
|
|
|
|
"multibranchPipelineJob('titan-iac-quality-gate')", 1
|
|
|
|
|
)[0]
|
|
|
|
|
assert "authenticationToken(System.getenv('HERMES_AGENT_IMAGE_BUILD_TOKEN'))" in block
|
|
|
|
|
assert "scriptPath('ci/Jenkinsfile.hermes-voice-image')" in block
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_runtime_trigger_exposes_both_voice_components() -> None:
|
|
|
|
|
trigger = TRIGGER.read_text(encoding="utf-8")
|
|
|
|
|
for component, confirmation in (
|
|
|
|
|
("stt", "PUBLISH HERMES STT"),
|
|
|
|
|
("tts", "PUBLISH HERMES TTS"),
|
|
|
|
|
):
|
|
|
|
|
assert f'"{component}": {{' in trigger
|
|
|
|
|
assert f'"confirmation": "{confirmation}"' in trigger
|
|
|
|
|
assert f'"parameters": {{"IMAGE_COMPONENT": "{component}"}}' in trigger
|