Skip to content

Land the work from #11 and #12, which merged into their base branches… #32

Land the work from #11 and #12, which merged into their base branches…

Land the work from #11 and #12, which merged into their base branches… #32

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -e '.[dev]'
- run: ruff check --output-format=github .
dashboard:
# The dashboard bundle ships in the wheel and its two action buttons
# mutate a live Hookdeck project, so it needs a check of its own — the
# Python suite cannot see it. No npm install: the bundle takes everything
# from the injected SDK, so a fake one under node:vm exercises the real
# file, and node's own test runner runs it.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- run: node --test "tests/dashboard/*.test.mjs"
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# The floor is `requires-python`; the ceiling is whatever is current.
# Hermes decides which of these a real install runs on, so the plugin
# should not be the thing that narrows it.
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -e '.[dev]'
- run: python -m pytest -q
package:
# A plugin that installs without its manifest, dashboard bundle or skill
# registers nothing, and the failure is silent — the platform simply never
# appears. Building the artifact and looking inside it is the only way that
# gets caught before a release.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# setuptools-scm reads the version from the tags. Without them this
# still builds, but at a meaningless version — and the point of this
# job is to inspect an artifact that resembles a released one.
fetch-depth: 0
fetch-tags: true
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install build twine
- run: python -m build
- run: twine check dist/*
- name: The wheel must carry the non-Python half of the plugin
run: |
set -euo pipefail
wheel=$(ls dist/*.whl)
echo "inspecting $wheel"
contents=$(python -m zipfile --list "$wheel")
for required in \
hookdeck/plugin.yaml \
hookdeck/dashboard/manifest.json \
hookdeck/dashboard/dist/index.js \
hookdeck/skills/triage-webhook-failures/SKILL.md
do
if ! grep -qF "$required" <<<"$contents"; then
echo "::error::$required is missing from the wheel"
exit 1
fi
echo " ✓ $required"
done
- name: The dashboard manifest must carry the version that was built
# Hermes reads this off disk and shows it. It used to be a literal, and
# it silently drifted: 0.1.1 shipped with a manifest saying 0.1.0. It is
# generated now, so this asserts the generator ran rather than trusting
# that it did.
run: |
set -euo pipefail
python - <<'PY'
import glob, json, re, sys, zipfile
wheel = glob.glob("dist/*.whl")[0]
built = re.match(r"[^-]+-([^-]+)-", wheel.split("/")[-1]).group(1)
manifest = json.loads(
zipfile.ZipFile(wheel).read("hookdeck/dashboard/manifest.json")
)
found = manifest.get("version")
if found != built:
print(f"::error::wheel is {built} but its dashboard manifest says {found}")
sys.exit(1)
print(f" ✓ dashboard manifest reports {found}")
PY
- name: The entry point Hermes discovers the plugin by must be declared
run: |
set -euo pipefail
pip install dist/*.whl
python - <<'PY'
from importlib.metadata import entry_points
found = entry_points(group="hermes_agent.plugins")
names = {e.name: e.value for e in found}
assert names.get("hookdeck") == "hookdeck", names
print("✓ hermes_agent.plugins entry point:", names)
PY
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/