fix(hermes): version chat release assets

This commit is contained in:
jenkins 2026-08-23 19:22:26 -03:00
parent 55e21c9e08
commit a68568d0c9
6 changed files with 114 additions and 4 deletions

View File

@ -226,6 +226,7 @@ spec:
--context="dir://${WORKSPACE}" \
--dockerfile="${WORKSPACE}/dockerfiles/Dockerfile.hermes-webui" \
--destination="${destination}" \
--build-arg="HERMES_WEBUI_RELEASE_ID=git-${source_revision}-build-${BUILD_NUMBER}" \
--digest-file="${WORKSPACE}/build/hermes-webui.digest" \
--image-name-tag-with-digest-file="${WORKSPACE}/build/hermes-webui.image" \
--label="org.opencontainers.image.revision=${source_revision}" \

View File

@ -4,6 +4,8 @@ FROM ghcr.io/nesquena/hermes-webui@sha256:a83a3893111dcb250e7aa7aa657d3d6f4570b0
FROM registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
ARG HERMES_WEBUI_RELEASE_ID
USER root
# Keep WebUI and Hermes pinned together. The WebUI imports Hermes internals,
@ -23,6 +25,7 @@ COPY dockerfiles/hermes-webui-router-patch.py /tmp/hermes-webui-router-patch.py
COPY dockerfiles/hermes-webui-router.js /opt/hermes-webui/static/atlas-router.js
COPY dockerfiles/hermes-webui-brand-patch.py /tmp/hermes-webui-brand-patch.py
COPY dockerfiles/hermes-webui-manifest-patch.py /tmp/hermes-webui-manifest-patch.py
COPY dockerfiles/hermes-webui-release-patch.py /tmp/hermes-webui-release-patch.py
COPY dockerfiles/hermes-webui-smoke.py /tmp/hermes-webui-smoke.py
COPY dockerfiles/hermes-webui-brand.css /opt/hermes-webui/static/hermes-brand.css
COPY dockerfiles/hermes-webui-manifest.json /tmp/hermes-webui-manifest.json
@ -35,7 +38,9 @@ RUN /opt/hermes/.venv/bin/python /tmp/hermes-webui-base-patch.py \
&& /opt/hermes/.venv/bin/python /tmp/hermes-webui-telegram-project-patch.py \
&& /opt/hermes/.venv/bin/python /tmp/hermes-webui-router-patch.py \
&& /opt/hermes/.venv/bin/python /tmp/hermes-webui-brand-patch.py \
&& /opt/hermes/.venv/bin/python /tmp/hermes-webui-manifest-patch.py
&& /opt/hermes/.venv/bin/python /tmp/hermes-webui-manifest-patch.py \
&& HERMES_WEBUI_RELEASE_ID="${HERMES_WEBUI_RELEASE_ID}" \
/opt/hermes/.venv/bin/python /tmp/hermes-webui-release-patch.py
RUN /opt/hermes/.venv/bin/python -c 'import cryptography, yaml' \
&& grep -Fq 'VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh")' \

View File

@ -98,7 +98,7 @@ replace_between_exact(
' <h2 data-i18n="empty_title">',
' <div class="empty-logo">\n'
' <img class="hermes-agent-portrait" '
'src="static/hermes-agent-192.png" alt="Hermes Agent">\n'
'src="static/hermes-agent-512.png" alt="Hermes Agent">\n'
' </div>\n',
)

View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Stamp Atlas WebUI shell URLs with the immutable image release identity."""
from __future__ import annotations
import os
from pathlib import Path
import re
ROOT = Path(os.environ.get("HERMES_WEBUI_PATCH_ROOT", "/opt/hermes-webui"))
RELEASE_ID = os.environ.get("HERMES_WEBUI_RELEASE_ID", "").strip()
if not re.fullmatch(r"[a-z0-9][a-z0-9.-]{0,127}", RELEASE_ID):
raise SystemExit("HERMES_WEBUI_RELEASE_ID must be a safe immutable release token")
for relative in ("static/index.html", "static/sw.js"):
path = ROOT / relative
source = path.read_text(encoding="utf-8")
if "__WEBUI_VERSION__" not in source:
raise SystemExit(f"Hermes release patch context changed in {path}")
path.write_text(
source.replace("__WEBUI_VERSION__", f"__WEBUI_VERSION__-{RELEASE_ID}"),
encoding="utf-8",
)

View File

@ -7,6 +7,7 @@ import importlib.util
import json
import os
from pathlib import Path
import re
import shutil
import struct
import subprocess
@ -22,6 +23,7 @@ AGENT_FIXTURE = ROOT / "testing/fixtures/hermes-agent"
ATLAS_PATCHER = DOCKERFILES / "hermes-webui-atlas-patch.py"
BRAND_PATCHER = DOCKERFILES / "hermes-webui-brand-patch.py"
MANIFEST_PATCHER = DOCKERFILES / "hermes-webui-manifest-patch.py"
RELEASE_PATCHER = DOCKERFILES / "hermes-webui-release-patch.py"
SMOKE = DOCKERFILES / "hermes-webui-smoke.py"
ASSETS = DOCKERFILES / "hermes-webui-assets"
MANIFEST = DOCKERFILES / "hermes-webui-manifest.json"
@ -144,7 +146,7 @@ def test_production_patchers_apply_title_icons_theme_and_cache_contract(
assert '<img src="static/hermes-agent-192.png" alt="">' in index
assert (
'<img class="hermes-agent-portrait" '
'src="static/hermes-agent-192.png" alt="Hermes Agent">'
'src="static/hermes-agent-512.png" alt="Hermes Agent">'
) in index
assert 'aria-label="Hermes caduceus"' not in index
assert "favicon.svg" not in index
@ -321,10 +323,79 @@ def test_empty_state_uses_canonical_character_without_inline_staff(
empty_state = index.split('<div class="empty-state" id="emptyState">', 1)[1]
empty_state = empty_state.split('<div class="messages-inner"', 1)[0]
assert empty_state.count('class="hermes-agent-portrait"') == 1
assert empty_state.count('src="static/hermes-agent-192.png"') == 1
assert empty_state.count('src="static/hermes-agent-512.png"') == 1
assert "<svg" not in empty_state.split('<h2 data-i18n="empty_title">', 1)[0]
def test_release_patch_gives_every_shell_url_an_immutable_build_token(
tmp_path: Path,
) -> None:
"""Atlas image releases never share a browser cache key."""
target = _patched_fixture(tmp_path)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = "git-0123456789abcdef-build-11"
subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
check=True,
capture_output=True,
text=True,
)
index = (target / "static/index.html").read_text(encoding="utf-8")
worker = (target / "static/sw.js").read_text(encoding="utf-8")
token = "__WEBUI_VERSION__-git-0123456789abcdef-build-11"
assert token in index
assert token in worker
assert not re.search(r"__WEBUI_VERSION__(?!-git-0123456789abcdef-build-11)", index)
assert not re.search(r"__WEBUI_VERSION__(?!-git-0123456789abcdef-build-11)", worker)
@pytest.mark.parametrize("release_id", ["", "../escape", "UPPERCASE", "a" * 129])
def test_release_patch_rejects_missing_or_unsafe_tokens(
tmp_path: Path, release_id: str
) -> None:
"""A malformed build token cannot silently collapse browser cache keys."""
target = _patched_fixture(tmp_path)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = release_id
completed = subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
capture_output=True,
text=True,
)
assert completed.returncode != 0
assert "safe immutable release token" in completed.stderr
def test_release_patch_rejects_upstream_without_version_markers(
tmp_path: Path,
) -> None:
"""Pinned upstream drift fails the image build before publication."""
target = _patched_fixture(tmp_path)
index = target / "static/index.html"
index.write_text(
index.read_text(encoding="utf-8").replace("__WEBUI_VERSION__", "fixed"),
encoding="utf-8",
)
env = os.environ.copy()
env["HERMES_WEBUI_PATCH_ROOT"] = str(target)
env["HERMES_WEBUI_RELEASE_ID"] = "git-0123456789abcdef-build-11"
completed = subprocess.run(
[sys.executable, str(RELEASE_PATCHER)],
cwd=ROOT,
env=env,
capture_output=True,
text=True,
)
assert completed.returncode != 0
assert "release patch context changed" in completed.stderr
def test_dockerfile_copies_and_verifies_every_tracked_brand_asset() -> None:
"""The immutable image, not a runtime coordinator path, owns PWA assets."""
dockerfile = (DOCKERFILES / "Dockerfile.hermes-webui").read_text(encoding="utf-8")
@ -333,6 +404,8 @@ def test_dockerfile_copies_and_verifies_every_tracked_brand_asset() -> None:
assert "python /tmp/hermes-webui-brand-patch.py" in dockerfile
assert "COPY dockerfiles/hermes-webui-manifest-patch.py" in dockerfile
assert "python /tmp/hermes-webui-manifest-patch.py" in dockerfile
assert "COPY dockerfiles/hermes-webui-release-patch.py" in dockerfile
assert "python /tmp/hermes-webui-release-patch.py" in dockerfile
assert (
"COPY dockerfiles/hermes-webui-manifest.json /tmp/hermes-webui-manifest.json"
in dockerfile

View File

@ -139,10 +139,16 @@ def test_webui_dockerfile_is_kaniko_safe_and_uses_reviewed_repo_source() -> None
assert "hermes-webui-base-patch.py" in source
assert "hermes-webui-brand-patch.py" in source
assert "hermes-webui-manifest-patch.py" in source
assert "hermes-webui-release-patch.py" in source
assert "hermes-webui-smoke.py" in source
assert "hermes-webui-stt-patch.py" in source
assert "hermes-webui-atlas-voice.js" in source
assert "hermes-webui-manifest.json" in source
assert (
'--build-arg="HERMES_WEBUI_RELEASE_ID='
'git-${source_revision}-build-${BUILD_NUMBER}"'
in PIPELINE.read_text(encoding="utf-8")
)
assert source.count("RUN /opt/hermes/.venv/bin/python /tmp/hermes-webui-") == 1
assert "urljoin(manifest_url, source)" in (
ROOT / "dockerfiles/hermes-webui-smoke.py"