mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
chore(release): bump ACP Registry assets in lockstep with pyproject
Some checks failed
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
Docker Build and Publish / build-amd64 (push) Waiting to run
Docker Build and Publish / build-arm64 (push) Waiting to run
Docker Build and Publish / merge (push) Blocked by required conditions
Docker Build and Publish / move-main (push) Blocked by required conditions
Docker Build and Publish / move-latest (push) Blocked by required conditions
Lint (ruff + ty) / ruff + ty diff (push) Waiting to run
Lint (ruff + ty) / ruff enforcement (blocking) (push) Waiting to run
Lint (ruff + ty) / Windows footguns (blocking) (push) Waiting to run
Nix / nix (macos-latest) (push) Waiting to run
Nix / nix (ubuntu-latest) (push) Waiting to run
OSV-Scanner / Scan lockfiles (push) Waiting to run
Tests / test (push) Waiting to run
Tests / e2e (push) Waiting to run
uv.lock check / uv lock --check (push) Waiting to run
Nix Lockfile Fix / auto-fix-main (push) Has been cancelled
Nix Lockfile Fix / fix (push) Has been cancelled
Some checks failed
Deploy Site / deploy-vercel (push) Waiting to run
Deploy Site / deploy-docs (push) Waiting to run
Docker Build and Publish / build-amd64 (push) Waiting to run
Docker Build and Publish / build-arm64 (push) Waiting to run
Docker Build and Publish / merge (push) Blocked by required conditions
Docker Build and Publish / move-main (push) Blocked by required conditions
Docker Build and Publish / move-latest (push) Blocked by required conditions
Lint (ruff + ty) / ruff + ty diff (push) Waiting to run
Lint (ruff + ty) / ruff enforcement (blocking) (push) Waiting to run
Lint (ruff + ty) / Windows footguns (blocking) (push) Waiting to run
Nix / nix (macos-latest) (push) Waiting to run
Nix / nix (ubuntu-latest) (push) Waiting to run
OSV-Scanner / Scan lockfiles (push) Waiting to run
Tests / test (push) Waiting to run
Tests / e2e (push) Waiting to run
uv.lock check / uv lock --check (push) Waiting to run
Nix Lockfile Fix / auto-fix-main (push) Has been cancelled
Nix Lockfile Fix / fix (push) Has been cancelled
The ACP Registry manifest (acp_registry/agent.json), the npm launcher package.json, and the launcher's HERMES_AGENT_VERSION constant must all match pyproject.toml exactly — tests/acp/test_registry_manifest.py enforces this lockstep. Without a release-script hook, the next weekly version bump fails that test until someone hand-edits four files. Extend update_version_files() to drive the ACP bump alongside __init__.py and pyproject.toml, and add tests covering the lockstep and the missing-files no-op path. Also map adam.manning@gmail.com -> am423 for the salvage commit.
This commit is contained in:
parent
4c94396206
commit
d364132114
2 changed files with 206 additions and 0 deletions
|
|
@ -21,6 +21,7 @@ Usage:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
@ -33,6 +34,13 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
VERSION_FILE = REPO_ROOT / "hermes_cli" / "__init__.py"
|
VERSION_FILE = REPO_ROOT / "hermes_cli" / "__init__.py"
|
||||||
PYPROJECT_FILE = REPO_ROOT / "pyproject.toml"
|
PYPROJECT_FILE = REPO_ROOT / "pyproject.toml"
|
||||||
|
|
||||||
|
# ACP Registry assets that must stay version-locked with pyproject.toml.
|
||||||
|
# tests/acp/test_registry_manifest.py enforces this lockstep, so the release
|
||||||
|
# bump touches all four files atomically.
|
||||||
|
ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
|
||||||
|
ACP_NPM_PACKAGE_JSON = REPO_ROOT / "packages" / "hermes-agent-acp" / "package.json"
|
||||||
|
ACP_NPM_LAUNCHER = REPO_ROOT / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js"
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────
|
||||||
# Git email → GitHub username mapping
|
# Git email → GitHub username mapping
|
||||||
# ──────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -56,6 +64,7 @@ AUTHOR_MAP = {
|
||||||
"jeremy@geocaching.com": "outdoorsea",
|
"jeremy@geocaching.com": "outdoorsea",
|
||||||
"leone.parise@gmail.com": "leoneparise",
|
"leone.parise@gmail.com": "leoneparise",
|
||||||
"mr@shu.io": "mrshu",
|
"mr@shu.io": "mrshu",
|
||||||
|
"adam.manning@gmail.com": "am423",
|
||||||
"buraysandro9@gmail.com": "ygd58",
|
"buraysandro9@gmail.com": "ygd58",
|
||||||
"yanglongwei06@gmail.com": "Alex-yang00",
|
"yanglongwei06@gmail.com": "Alex-yang00",
|
||||||
"teknium@nousresearch.com": "teknium1",
|
"teknium@nousresearch.com": "teknium1",
|
||||||
|
|
@ -1153,6 +1162,44 @@ def update_version_files(semver: str, calver_date: str):
|
||||||
)
|
)
|
||||||
PYPROJECT_FILE.write_text(pyproject)
|
PYPROJECT_FILE.write_text(pyproject)
|
||||||
|
|
||||||
|
# Update ACP Registry manifest + npm launcher (must stay version-locked
|
||||||
|
# with pyproject — enforced by tests/acp/test_registry_manifest.py).
|
||||||
|
_update_acp_registry_versions(semver)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_acp_registry_versions(semver: str) -> None:
|
||||||
|
"""Bump the ACP Registry manifest, npm package, and launcher in lockstep.
|
||||||
|
|
||||||
|
Skips silently if any of the files are missing — the ACP Registry assets
|
||||||
|
landed mid-cycle and older release branches may not have them.
|
||||||
|
"""
|
||||||
|
if ACP_REGISTRY_MANIFEST.exists():
|
||||||
|
manifest = json.loads(ACP_REGISTRY_MANIFEST.read_text(encoding="utf-8"))
|
||||||
|
manifest["version"] = semver
|
||||||
|
npx = manifest.get("distribution", {}).get("npx", {})
|
||||||
|
if "package" in npx:
|
||||||
|
npx["package"] = f"@nousresearch/hermes-agent-acp@{semver}"
|
||||||
|
# Preserve trailing newline + 2-space indent the file already uses.
|
||||||
|
ACP_REGISTRY_MANIFEST.write_text(
|
||||||
|
json.dumps(manifest, indent=2) + "\n", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ACP_NPM_PACKAGE_JSON.exists():
|
||||||
|
package = json.loads(ACP_NPM_PACKAGE_JSON.read_text(encoding="utf-8"))
|
||||||
|
package["version"] = semver
|
||||||
|
ACP_NPM_PACKAGE_JSON.write_text(
|
||||||
|
json.dumps(package, indent=2) + "\n", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ACP_NPM_LAUNCHER.exists():
|
||||||
|
launcher = ACP_NPM_LAUNCHER.read_text(encoding="utf-8")
|
||||||
|
launcher = re.sub(
|
||||||
|
r"const HERMES_AGENT_VERSION\s*=\s*'[^']+';",
|
||||||
|
f"const HERMES_AGENT_VERSION = '{semver}';",
|
||||||
|
launcher,
|
||||||
|
)
|
||||||
|
ACP_NPM_LAUNCHER.write_text(launcher, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
def build_release_artifacts(semver: str) -> list[Path]:
|
def build_release_artifacts(semver: str) -> list[Path]:
|
||||||
"""Build sdist/wheel artifacts for the current release.
|
"""Build sdist/wheel artifacts for the current release.
|
||||||
|
|
|
||||||
159
tests/scripts/test_release_acp_registry.py
Normal file
159
tests/scripts/test_release_acp_registry.py
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
"""Tests for the ACP Registry version-lockstep bump in scripts/release.py.
|
||||||
|
|
||||||
|
The official ACP Registry manifest, the @nousresearch/hermes-agent-acp npm
|
||||||
|
package, and the npm launcher's HERMES_AGENT_VERSION constant must all match
|
||||||
|
``pyproject.toml`` exactly — ``tests/acp/test_registry_manifest.py`` enforces
|
||||||
|
this at lint time. The release script is the single place that bumps them in
|
||||||
|
lockstep with pyproject; if that bump ever silently breaks, weekly releases
|
||||||
|
fail the manifest test until someone hand-edits four files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def _load_release_module(monkeypatch, tmp_root: Path):
|
||||||
|
"""Import scripts/release.py with REPO_ROOT pinned to a temp tree."""
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"_release_under_test",
|
||||||
|
Path(__file__).resolve().parents[2] / "scripts" / "release.py",
|
||||||
|
)
|
||||||
|
assert spec and spec.loader
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
|
# Repoint every REPO_ROOT-derived path at our temp tree.
|
||||||
|
monkeypatch.setattr(module, "REPO_ROOT", tmp_root)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module, "ACP_REGISTRY_MANIFEST", tmp_root / "acp_registry" / "agent.json"
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module,
|
||||||
|
"ACP_NPM_PACKAGE_JSON",
|
||||||
|
tmp_root / "packages" / "hermes-agent-acp" / "package.json",
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
module,
|
||||||
|
"ACP_NPM_LAUNCHER",
|
||||||
|
tmp_root / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js",
|
||||||
|
)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def _write_fixture(root: Path, version: str) -> None:
|
||||||
|
"""Write the three ACP-registry files we expect release.py to bump."""
|
||||||
|
manifest_dir = root / "acp_registry"
|
||||||
|
manifest_dir.mkdir(parents=True)
|
||||||
|
(manifest_dir / "agent.json").write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"id": "hermes-agent",
|
||||||
|
"name": "Hermes Agent",
|
||||||
|
"version": version,
|
||||||
|
"description": "test",
|
||||||
|
"distribution": {
|
||||||
|
"npx": {"package": f"@nousresearch/hermes-agent-acp@{version}"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
+ "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
package_dir = root / "packages" / "hermes-agent-acp"
|
||||||
|
(package_dir / "bin").mkdir(parents=True)
|
||||||
|
(package_dir / "package.json").write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"name": "@nousresearch/hermes-agent-acp",
|
||||||
|
"version": version,
|
||||||
|
"bin": {"hermes-agent-acp": "bin/hermes-agent-acp.js"},
|
||||||
|
},
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
+ "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(package_dir / "bin" / "hermes-agent-acp.js").write_text(
|
||||||
|
f"const HERMES_AGENT_VERSION = '{version}';\n"
|
||||||
|
f"const HERMES_SPEC = `hermes-agent[acp]==${{HERMES_AGENT_VERSION}}`;\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_acp_registry_versions_bumps_all_three_files(monkeypatch, tmp_path):
|
||||||
|
_write_fixture(tmp_path, "0.13.0")
|
||||||
|
module = _load_release_module(monkeypatch, tmp_path)
|
||||||
|
|
||||||
|
module._update_acp_registry_versions("0.14.0")
|
||||||
|
|
||||||
|
manifest = json.loads(
|
||||||
|
(tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
assert manifest["version"] == "0.14.0"
|
||||||
|
assert (
|
||||||
|
manifest["distribution"]["npx"]["package"]
|
||||||
|
== "@nousresearch/hermes-agent-acp@0.14.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
package = json.loads(
|
||||||
|
(
|
||||||
|
tmp_path / "packages" / "hermes-agent-acp" / "package.json"
|
||||||
|
).read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
assert package["version"] == "0.14.0"
|
||||||
|
|
||||||
|
launcher = (
|
||||||
|
tmp_path / "packages" / "hermes-agent-acp" / "bin" / "hermes-agent-acp.js"
|
||||||
|
).read_text(encoding="utf-8")
|
||||||
|
assert "const HERMES_AGENT_VERSION = '0.14.0';" in launcher
|
||||||
|
assert "0.13.0" not in launcher
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_acp_registry_versions_is_silent_when_files_missing(
|
||||||
|
monkeypatch, tmp_path
|
||||||
|
):
|
||||||
|
"""Older release branches predate the ACP Registry assets — must no-op."""
|
||||||
|
module = _load_release_module(monkeypatch, tmp_path)
|
||||||
|
|
||||||
|
# No fixture written; function should not raise.
|
||||||
|
module._update_acp_registry_versions("0.14.0")
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_version_files_bumps_acp_assets_alongside_pyproject(
|
||||||
|
monkeypatch, tmp_path
|
||||||
|
):
|
||||||
|
"""End-to-end: update_version_files() is the function release.py actually
|
||||||
|
calls, so it must drive the ACP bump too."""
|
||||||
|
_write_fixture(tmp_path, "0.13.0")
|
||||||
|
(tmp_path / "pyproject.toml").write_text(
|
||||||
|
'[project]\nname = "hermes-agent"\nversion = "0.13.0"\n', encoding="utf-8"
|
||||||
|
)
|
||||||
|
version_dir = tmp_path / "hermes_cli"
|
||||||
|
version_dir.mkdir()
|
||||||
|
(version_dir / "__init__.py").write_text(
|
||||||
|
'__version__ = "0.13.0"\n__release_date__ = "2026-05-14"\n',
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
module = _load_release_module(monkeypatch, tmp_path)
|
||||||
|
monkeypatch.setattr(module, "VERSION_FILE", version_dir / "__init__.py")
|
||||||
|
monkeypatch.setattr(module, "PYPROJECT_FILE", tmp_path / "pyproject.toml")
|
||||||
|
|
||||||
|
module.update_version_files("0.14.0", "2026-05-21")
|
||||||
|
|
||||||
|
pyproject_text = (tmp_path / "pyproject.toml").read_text(encoding="utf-8")
|
||||||
|
assert 'version = "0.14.0"' in pyproject_text
|
||||||
|
|
||||||
|
manifest = json.loads(
|
||||||
|
(tmp_path / "acp_registry" / "agent.json").read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
assert manifest["version"] == "0.14.0"
|
||||||
|
assert (
|
||||||
|
manifest["distribution"]["npx"]["package"]
|
||||||
|
== "@nousresearch/hermes-agent-acp@0.14.0"
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue