diff --git a/hindsight-dev/hindsight_dev/generate_changelog.py b/hindsight-dev/hindsight_dev/generate_changelog.py index 40d2d4298..f5c5db5ea 100644 --- a/hindsight-dev/hindsight_dev/generate_changelog.py +++ b/hindsight-dev/hindsight_dev/generate_changelog.py @@ -42,6 +42,7 @@ class IntegrationMeta: "pydantic-ai": IntegrationMeta("hindsight-pydantic-ai", "Pydantic AI"), "crewai": IntegrationMeta("hindsight-crewai", "CrewAI"), "agent-framework": IntegrationMeta("hindsight-agent-framework", "Microsoft Agent Framework"), + "agno": IntegrationMeta("hindsight-agno", "Agno"), "ag2": IntegrationMeta("hindsight-ag2"), "ai-sdk": IntegrationMeta("@vectorize-io/hindsight-ai-sdk", "AI SDK"), "chat": IntegrationMeta("@vectorize-io/hindsight-chat", "Chat SDK"), @@ -76,6 +77,7 @@ class IntegrationMeta: "roo-code": IntegrationMeta("hindsight-roo-code", "Roo Code"), "omo": IntegrationMeta("hindsight-omo", "OMO"), "composio": IntegrationMeta("hindsight-composio", "Composio"), + "continue": IntegrationMeta("hindsight-continue", "Continue"), } VALID_INTEGRATIONS = list(INTEGRATIONS.keys()) diff --git a/hindsight-dev/tests/test_generate_changelog_registry.py b/hindsight-dev/tests/test_generate_changelog_registry.py new file mode 100644 index 000000000..5b0a6c549 --- /dev/null +++ b/hindsight-dev/tests/test_generate_changelog_registry.py @@ -0,0 +1,63 @@ +"""Regression tests for integration changelog registry coverage.""" + +import subprocess +import tomllib +from pathlib import Path + +from hindsight_dev.generate_changelog import INTEGRATIONS + +REPO_ROOT = Path(__file__).resolve().parents[2] +RELEASE_SCRIPT = REPO_ROOT / "scripts" / "release-integration.sh" +NEW_PYTHON_INTEGRATIONS = {"agno", "continue"} + + +def _release_script_integrations(): + result = subprocess.run( + ["bash", str(RELEASE_SCRIPT), "--list-integrations"], + check=True, + capture_output=True, + cwd=REPO_ROOT, + text=True, + ) + return result.stdout.splitlines() + + +def _validate_release_path(slug): + subprocess.run( + ["bash", str(RELEASE_SCRIPT), "--validate-only", slug], + check=True, + capture_output=True, + cwd=REPO_ROOT, + text=True, + ) + + +def test_release_script_integrations_match_changelog_metadata(): + release_integrations = _release_script_integrations() + + assert len(release_integrations) == len(set(release_integrations)) + assert set(release_integrations) == set(INTEGRATIONS) + assert NEW_PYTHON_INTEGRATIONS <= set(release_integrations) + + +def test_python_integration_metadata_matches_package_manifests(): + pyproject_slugs = {path.parent.name for path in (REPO_ROOT / "hindsight-integrations").glob("*/pyproject.toml")} + assert NEW_PYTHON_INTEGRATIONS <= pyproject_slugs + + for slug, meta in INTEGRATIONS.items(): + pyproject = REPO_ROOT / "hindsight-integrations" / slug / "pyproject.toml" + if not pyproject.exists(): + continue + manifest = tomllib.loads(pyproject.read_text()) + + assert meta.package_name == manifest["project"]["name"] + + +def test_new_integration_display_names_match_user_facing_brands(): + assert INTEGRATIONS["agno"].display_name == "Agno" + assert INTEGRATIONS["continue"].display_name == "Continue" + + +def test_new_integration_release_paths_validate_before_mutation(): + for slug in NEW_PYTHON_INTEGRATIONS: + _validate_release_path(slug) diff --git a/scripts/release-integration.sh b/scripts/release-integration.sh index b75ba5ca7..83a966685 100755 --- a/scripts/release-integration.sh +++ b/scripts/release-integration.sh @@ -28,12 +28,26 @@ usage() { exit 1 } -if [ -z "$1" ] || [ -z "$2" ]; then +if [ "${1:-}" = "--list-integrations" ] && [ "$#" -eq 1 ]; then + printf "%s\n" "${VALID_INTEGRATIONS[@]}" + exit 0 +fi + +VALIDATE_ONLY=false +if [ "${1:-}" = "--validate-only" ]; then + VALIDATE_ONLY=true + shift + if [ -z "${1:-}" ] || [ "$#" -ne 1 ]; then + usage + fi +fi + +if [ -z "$1" ] || { [ "$VALIDATE_ONLY" = false ] && [ -z "$2" ]; }; then usage fi INTEGRATION=$1 -VERSION_ARG=$2 +VERSION_ARG=${2:-} # Validate integration name VALID=false @@ -77,6 +91,54 @@ bump_version() { esac } +validate_manifest_version_field() { + local manifest=$1 + python3 - "$manifest" <<'PY' +import json +import sys +from pathlib import Path + +path = Path(sys.argv[1]) +if path.name == "pyproject.toml": + import tomllib + + data = tomllib.loads(path.read_text()) + version = data.get("project", {}).get("version") +else: + data = json.loads(path.read_text()) + version = data.get("version") + +if not isinstance(version, str) or not version: + raise SystemExit(f"Could not read a top-level release version from {path}") +PY +} + +INTEGRATION_DIR="hindsight-integrations/$INTEGRATION" + +if [ ! -d "$INTEGRATION_DIR" ]; then + print_error "Integration directory not found: $INTEGRATION_DIR" + exit 1 +fi + +if [ -f "$INTEGRATION_DIR/pyproject.toml" ]; then + MANIFEST_PATH="$INTEGRATION_DIR/pyproject.toml" +elif [ -f "$INTEGRATION_DIR/package.json" ]; then + MANIFEST_PATH="$INTEGRATION_DIR/package.json" +elif [ -f "$INTEGRATION_DIR/.claude-plugin/plugin.json" ]; then + MANIFEST_PATH="$INTEGRATION_DIR/.claude-plugin/plugin.json" +elif [ -f "$INTEGRATION_DIR/settings.json" ] && grep -q '"version"' "$INTEGRATION_DIR/settings.json"; then + MANIFEST_PATH="$INTEGRATION_DIR/settings.json" +else + print_error "No pyproject.toml, package.json, plugin.json, or versioned settings.json found in $INTEGRATION_DIR" + exit 1 +fi + +if [ "$VALIDATE_ONLY" = true ]; then + validate_manifest_version_field "$MANIFEST_PATH" + print_info "Validated $INTEGRATION release path via $MANIFEST_PATH" + exit 0 +fi + # Resolve version: either an explicit semver or a bump keyword if [[ "$VERSION_ARG" =~ ^(patch|minor|major)$ ]]; then CURRENT_VERSION=$(get_current_version) @@ -135,14 +197,6 @@ if [ -z "$OPENAI_API_KEY" ]; then exit 1 fi -# Determine integration type and update version -INTEGRATION_DIR="hindsight-integrations/$INTEGRATION" - -if [ ! -d "$INTEGRATION_DIR" ]; then - print_error "Integration directory not found: $INTEGRATION_DIR" - exit 1 -fi - if [ -f "$INTEGRATION_DIR/pyproject.toml" ]; then print_info "Updating version in $INTEGRATION_DIR/pyproject.toml" sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" "$INTEGRATION_DIR/pyproject.toml" @@ -159,9 +213,6 @@ elif [ -f "$INTEGRATION_DIR/settings.json" ] && grep -q '"version"' "$INTEGRATIO print_info "Updating version in $INTEGRATION_DIR/settings.json" sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" "$INTEGRATION_DIR/settings.json" rm "$INTEGRATION_DIR/settings.json.bak" -else - print_error "No pyproject.toml, package.json, plugin.json, or versioned settings.json found in $INTEGRATION_DIR" - exit 1 fi # Generate changelog entry using LLM