Land the work from #11 and #12, which merged into their base branches… #32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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/ |