-
Notifications
You must be signed in to change notification settings - Fork 8
ci(hermes): enforce a version contract for the TinyFish plugin pin (PF-3818) #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| --- | ||
| name: Hermes Version Gate | ||
|
|
||
| on: # yamllint disable-line rule:truthy | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, labeled, unlabeled] | ||
| branches: [main] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| # Required check; unfiltered and unconditional so it reports on every PR. | ||
| version-bumped: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: hermes/pyproject.toml version must move | ||
| env: | ||
| BASE_REF: ${{ github.base_ref }} | ||
| NO_PLUGIN_RELEASE: ${{ contains(github.event.pull_request.labels.*.name, 'no-plugin-release') }} | ||
| run: | | ||
| set -euo pipefail | ||
| if [ "$NO_PLUGIN_RELEASE" = "true" ]; then | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fork PRs cant self-label, so any outside hermes/ change sits red until a maintainer labels it or the author bumps the version. fine if intended, but should hermes/README.md say so? |
||
| echo "no-plugin-release label present; version bump not required" | ||
| exit 0 | ||
| fi | ||
| version() { sed -n 's/^version = "\(.*\)"$/\1/p' "$1" | head -1; } | ||
| base=$(git merge-base "origin/${BASE_REF}" HEAD) | ||
| if git diff --quiet "$base" HEAD -- hermes/; then | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this counts README, tests, Makefile, requirements-dev.txt, none of which install, and each bump becomes a #39 tag and an internal PRs Renovate PR that needs the manual connect QA. should the diff be limited to tinyfish_hermes, plugin.yaml, pyproject.toml, init.py? |
||
| echo "no hermes/ changes in this PR; version bump not required" | ||
| exit 0 | ||
| fi | ||
| head_version=$(version hermes/pyproject.toml) | ||
| git show "${base}:hermes/pyproject.toml" > "$RUNNER_TEMP/base-pyproject.toml" | ||
| base_version=$(version "$RUNNER_TEMP/base-pyproject.toml") | ||
| echo "base=${base_version} head=${head_version}" | ||
| [ -n "$head_version" ] || { echo "::error file=hermes/pyproject.toml::no version found"; exit 1; } | ||
| if [ "$head_version" = "$base_version" ]; then | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Reject version downgrades. This condition accepts every version that differs from the base version. For example, a change from 🤖 Prompt for AI Agents
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ruleset isnt strict, so two PRs both bumping to 0.1.1 stay green, first merge tags hermes-v0.1.1, second merges hermes/ changes under an existing tag (the freeze this gate exists for). should #39 fail when the tag exists but hermes/ differs from its sha? |
||
| echo "::error file=hermes/pyproject.toml::this PR changes hermes/ but leaves version at ${base_version}; bump it, or label the PR no-plugin-release" | ||
| exit 1 | ||
| fi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| name: tinyfish | ||
| version: 0.1.0 | ||
| version: 0.1.1 | ||
| description: "First-party TinyFish provider plugin for Hermes Agent: Search and Fetch over the TinyFish REST APIs with API-key auth, plus credit-gated Browser sessions." | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why doesnt the desc describe agent? |
||
| author: TinyFish | ||
| kind: backend | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build" | |
|
|
||
| [project] | ||
| name = "tinyfish-hermes" | ||
| version = "0.1.0" | ||
| version = "0.1.1" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this PR changes only tests, so after merge Renovate bumps the internal pin to a sha with zero plugin change and someone runs connect QA for nothing. drop the bump and label no-plugin-release instead? (also lets the gate go red then green here) |
||
| description = "TinyFish web provider plugin for Hermes Agent - Search and Fetch over the TinyFish REST APIs" | ||
| readme = "README.md" | ||
| license = { text = "MIT" } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| import argparse | ||
| import json | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
| import pytest | ||
|
|
@@ -628,3 +629,50 @@ def test_in_session_status_command(env: dict[str, Any]) -> None: | |
| assert cli.tinyfish_status_command("bogus", provider=provider) == ( | ||
| "Usage: /tinyfish-status [live]" | ||
| ) | ||
|
|
||
|
|
||
| # Mirrors hermesPluginStatusSchema in ux-labs sdk/cli/src/lib/hermes-plugin.ts. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this names a file in a private repo nobody reading this one can open, and two hand-copies of a 6-key contract drift silently (the zod side already tolerates extras and nullish). could the comment just say 'keys tinyfish doctor parses'? |
||
| _TINYFISH_CLI_STATUS_KEYS = frozenset( | ||
| { | ||
| "ok", | ||
| "api_key_configured", | ||
| "api_key_env_var", | ||
| "plugin_version", | ||
| "provider_available", | ||
| "web_backend_configured", | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| def test_status_json_carries_every_key_the_tinyfish_cli_parses( | ||
| env: dict[str, Any], capsys: pytest.CaptureFixture[str] | ||
| ) -> None: | ||
| """A missing key degrades `tinyfish doctor` to 'unparseable output'.""" | ||
| args = _parser().parse_args(["status", "--json"]) | ||
|
|
||
| assert cli.dispatch_tinyfish_cli(args) == 0 | ||
|
|
||
| payload = json.loads(capsys.readouterr().out) | ||
| assert _TINYFISH_CLI_STATUS_KEYS <= payload.keys() | ||
|
|
||
|
|
||
| def test_status_json_exits_zero_even_when_unhealthy( | ||
| env: dict[str, Any], capsys: pytest.CaptureFixture[str] | ||
| ) -> None: | ||
| """A non-zero exit reads as 'plugin not installed' in `tinyfish doctor`.""" | ||
| env["config"] = {} | ||
|
|
||
| assert cli.dispatch_tinyfish_cli(_parser().parse_args(["status", "--json"])) == 0 | ||
| assert json.loads(capsys.readouterr().out)["ok"] is False | ||
|
|
||
|
|
||
| def test_plugin_manifest_name_is_what_uninstall_keys_on() -> None: | ||
| """`tinyfish connect --uninstall` runs `hermes plugins uninstall tinyfish`.""" | ||
| manifest = Path(__file__).resolve().parents[1] / "plugin.yaml" | ||
| names = [ | ||
| line.partition(":")[2].strip().strip("'\"") | ||
| for line in manifest.read_text(encoding="utf-8").splitlines() | ||
| if line.startswith("name:") | ||
| ] | ||
|
|
||
| assert names == ["tinyfish"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| import tomllib | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C2 'requires-python|python-version|setup-python|tox|target-version' \
hermes/pyproject.toml .githubRepository: tinyfish-io/tinyfish-web-agent-integrations Length of output: 2903 Use a Python 3.10-compatible TOML parser.
🤖 Prompt for AI Agents |
||
|
|
||
| import tinyfish_hermes as plugin | ||
|
|
||
|
|
@@ -53,3 +54,13 @@ def test_public_version_is_exported() -> None: | |
| assert isinstance(plugin.__version__, str) | ||
| assert plugin.__version__ | ||
| assert "__version__" in plugin.__all__ | ||
|
|
||
|
|
||
| def test_plugin_manifest_version_matches_the_distribution_version() -> None: | ||
| """Both feed `plugin_version`; drift makes one install report two versions.""" | ||
| hermes_root = Path(plugin.__file__).resolve().parents[1] | ||
| pyproject = tomllib.loads( | ||
| (hermes_root / "pyproject.toml").read_text(encoding="utf-8") | ||
| ) | ||
|
|
||
| assert plugin._version_from_plugin_manifest() == pyproject["project"]["version"] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkout here is refs/pull/N/merge, so HEAD^1 is already the base tip. could this be fetch-depth 2 and base=$(git rev-parse HEAD^1), dropping BASE_REF and the merge-base?