SuperAPI AI Directory

hermes-hookdeck-release

General & other

View repository

Updated 10-15 22:14

About this skill

hermes-hookdeck — release workflow

Canonical documentation

Follow README.md § Releasing for the human steps (GitHub UI). This skill adds how the automation works, the gates that must pass first, and a research loop for drafting notes.

The tag is the version

Nothing in the repo declares a version. setuptools-scm derives it from the git tag at build time and bakes it into hookdeck/_version.py inside the wheel, so hookdeck.__version__ and the PyPI version are the same string by construction and cannot drift.

The practical consequence: you choose the number when you create the release, not in a commit beforehand. There is nothing to bump, and no pre-release step that can be forgotten.

PyPI is append-only. A version number that has been published can never be reused or replaced, even after a yank. That is why the number is worth getting right before you publish — it is the one part of this that cannot be undone.

Between releases a source checkout reports a dev version derived from the last tag (0.1.2.dev4+g1a2b3c4). That is expected, not a bug: it says "four commits past v0.1.1".

Agent checklist (end-to-end)

Follow in order. Items marked gate are blocking unless the maintainer explicitly overrides.

  • Everything intended for the release is merged to main. Nothing is released from a branch.
  • PREV_TAG / NEW_TAG confirmed — git tag --sort=-v:refname | head -1 for the current release; propose NEW_TAG and agree it.
  • Change set reviewed: git log PREV_TAG..origin/main — read the full messages, not just subjects. Group for user-facing notes.
  • gate — SemVer: NEW_TAG matches the minimum bump for the delta (see SemVer below). Stop and realign if under-bumped. Nothing else validates this — the tag is the version, so a wrong tag is a wrong release.
  • gate — CI green on main: the tip of main has passing checks.
  • Release notes drafted — see Drafting release notes and references/release-notes-template.md.
  • gate — maintainer approval of tag name and notes. No surprise releases; publishing is irreversible on PyPI.
  • Publish with gh release create (below), targeting main.
  • Confirm the Release workflow succeeded and the version is on PyPI.

What triggers a release

Publishing a GitHub release — .github/workflows/release.yml runs on release: [published]. There is no separate approval step: publishing the release is the decision to ship.

Pushing a bare tag does nothing. The tag is created by the release.

What the workflow does

  1. build — checks out the release's tag with full history (setuptools-scm needs the tag present to resolve the version), runs ruff and the full test suite, builds the wheel and sdist, and runs twine check.
  2. publish — uploads to PyPI via Trusted Publishing (OIDC, no stored token), in the pypi environment.
  3. attach-artifacts — attaches the wheel and sdist to the GitHub release you created, so it carries the artifacts that actually went to PyPI.

A failure in build means nothing was published; fix main and publish a new release. A failure in publish can be re-run from the Actions tab (gh run rerun <id> --failed) — do not create a second release for the same version.

Because the version comes from the tag, a mistyped tag produces a real release at the wrong number rather than an error. Check the tag before publishing; that is the check the workflow can no longer do for you.

SemVer: validate the proposed version

Check any proposed tag against what actually changed since PREV_TAG.

The contract this package offers is its configuration, its environment variables, its CLI, its agent tools, and the Hermes surfaces it registers.

Change since PREV_TAG Bump Examples
Breaking — an existing install stops working, or behaves differently without the operator changing anything MAJOR Renamed or removed config key or HOOKDECK_EG_* variable; a default that changes delivery behaviour; a removed hermes hookdeck subcommand; a renamed or removed hookdeck_* agent tool; a ledger schema change that an older row cannot satisfy
New capability, backward compatible MINOR New config option with a safe default; new subcommand or agent tool; a new route feature; support for a Hermes version that was not supported before
Fixes and corrections, no new capability, nothing breaks PATCH Bug fixes; corrections to the bundled skill or plugin.yaml; dependency bumps with no behaviour change; packaging metadata

A change under hookdeck/ ships even when it is only prose. The bundled skill (hookdeck/skills/), plugin.yaml and the dashboard bundle are inside the wheel, so correcting them is a patch release, not a docs-only change.

A change outside hookdeck/ usually does not ship. docs/, README.md, AGENTS.md, tests/, skills/ at the repo root and .github/ are not packaged. A release containing only those has nothing for a user to install — say so rather than cutting one.

Verify which of the two you are in rather than assuming:

git diff --stat PREV_TAG..origin/main -- hookdeck/

Empty output means nothing shipped.

Agent behaviour: state the minimum bump the change set requires, then compare it to what was proposed. If they conflict, do not treat the proposal as authoritative — explain the mismatch and recommend the correct version. If it is genuinely ambiguous whether something breaks an existing install, ask.

Publish with GitHub CLI (gh)

Create the release with gh rather than pushing a tag — a bare tag does not trigger anything.

  1. Write the notes to a temp file and register cleanup, so a failure does not leave it behind:

    NOTES_FILE="$(mktemp "${TMPDIR:-/tmp}/hermes-hookdeck-release-notes.XXXXXX.md")"
    trap 'rm -f "$NOTES_FILE"' EXIT
    
  2. Write the final markdown body to "$NOTES_FILE".

  3. Create the release, targeting main:

    gh release create "vM.m.p" \
      --repo hookdeck/hermes-hookdeck \
      --target main \
      --title "vM.m.p" \
      --notes-file "$NOTES_FILE"
    

    Add --prerelease for rc, a or b versions, so the repository's "latest release" does not point at a release candidate.

  4. Watch it: gh run watch — or gh run list --workflow=release.yml --limit 1.

Requirements: gh authenticated. Nothing needs to be bumped first — the tag you pass here becomes the version. Do not put secrets in the notes file.

Verify afterwards

pip download --no-deps -d /tmp/verify hermes-hookdeck==M.m.p

For a release that changed anything under hookdeck/, confirm the change is really in the artifact rather than only in the repo — install it into a clean venv and check the file, not the working tree.

Drafting release notes

Use references/release-notes-template.md as the skeleton. Include only headings with real content — omit a section rather than writing "None".

Write for someone who runs a gateway, not for someone who reads this repo. The useful question is "what changes for me, and do I have to do anything?" — not "which files moved".

  • Lead with anything that requires action. Config changes, renamed variables, anything that alters delivery behaviour. If nothing does, say the upgrade is a drop-in.
  • A fix is only worth a bullet if the reader could have hit it. Say what went wrong from the outside ("the bundled skill never loaded"), not what the code did.
  • Always end with the compare link: https://github.com/hookdeck/hermes-hookdeck/compare/<prev_tag>...<new_tag>

Contributors: do not add a generic thanks block every release. Include one only for a first-time contributor or an exceptionally large contribution.

Research loop

  1. Tags: git tag --sort=-v:refname | head -5. Confirm PREV_TAG.

  2. Commits: git log PREV_TAG..origin/main — full messages. This repo writes the reasoning into commit bodies; that is the changelog source.

  3. Shipped or not: git diff --stat PREV_TAG..origin/main -- hookdeck/.

  4. Group by user impact, not by file.

  5. SemVer check against the table above.

  6. CI:

    gh api "repos/hookdeck/hermes-hookdeck/commits/$(git rev-parse origin/main)/check-runs" \
      --jq '.check_runs[] | "\(.name): \(.status)/\(.conclusion // "-")"'
    

    Every run must be completed/success. Do not release otherwise.

    Ask for check-runs, not /status. The latter reports the legacy commit Status API, which this repo does not use — with no statuses recorded it answers pending forever, so it reads as a red gate on a green main.

Safety and governance

  • PyPI is append-only. A published version cannot be reused, replaced, or truly deleted. Yanking hides it from resolvers; it does not free the number.
  • Do not release a red main. The workflow runs the suite itself and will fail the build, but finding out during a release is the wrong time.
  • Do not under-bump. Resolve a SemVer disagreement with the maintainer before publishing, not after.
  • The tag is unchecked. Nothing compares it to the change set, so a typo (v0.2.0 for a patch) ships at that number and burns it permanently.
  • Green tests are not proof of integration. The suite runs against a stub this repo also owns. Both defects found after 200 green tests came from a real hermes gateway run. For a release touching the delivery path, say which level you actually exercised — see AGENTS.md.

Related files

Topic Location
Human release steps README.md § Releasing
CI entrypoint .github/workflows/release.yml
Version derivation pyproject.toml [tool.setuptools_scm]
What is packaged pyproject.toml [tool.setuptools.package-data]
Repo conventions AGENTS.md