Skip to main content

hermes-hookdeck-release

Guides maintainers through releasing the hermes-hookdeck plugin to PyPI. Publishing a GitHub release is what triggers the publish, and the tag is the version — nothing in the repo is bumped. Validates the proposed version against SemVer from the actual change set, including the rule that a change to the bundled skill or plugin.yaml is a shipped change. Use when cutting a release, publishing to PyPI, drafting release notes, choosing vMAJOR.MINOR.PATCH, `gh release create`, or following the release checklist.

Quellinformationen

Repository
hookdeck/hermes-hookdeck
Letzte Quellaktivität
12. August 2026 um 10:45
Erkannte Sprache von SKILL.md
Englisch
Sterne
1
Forks
2

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
hermes-hookdeck-release
description
Guides maintainers through releasing the hermes-hookdeck plugin to PyPI. Publishing a GitHub release is what triggers the publish, and the tag is the version — nothing in the repo is bumped. Validates the proposed version against SemVer from the actual change set, including the rule that a change to the bundled skill or plugin.yaml is a shipped change. Use when cutting a release, publishing to PyPI, drafting release notes, choosing vMAJOR.MINOR.PATCH, `gh release create`, or following the release checklist.
# hermes-hookdeck — release workflow ## Canonical documentation Follow **[README.md](../../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](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](../../.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: ```bash 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: ```bash 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`: ```bash 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 ```bash 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](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:** ```bash 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](../../AGENTS.md). ## Related files | Topic | Location | |---|---| | Human release steps | [README.md § Releasing](../../README.md) | | CI entrypoint | [.github/workflows/release.yml](../../.github/workflows/release.yml) | | Version derivation | [pyproject.toml](../../pyproject.toml) `[tool.setuptools_scm]` | | What is packaged | [pyproject.toml](../../pyproject.toml) `[tool.setuptools.package-data]` | | Repo conventions | [AGENTS.md](../../AGENTS.md) |
Auf GitHub ansehen