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.

Ir para a instalação

Informações da origem

Repositório
hookdeck/hermes-hookdeck
Última atividade na origem
12 de agosto de 2026 às 10:45
Idioma detectado do SKILL.md
inglês
Estrelas
0
Forks
2

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
2 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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) |
Ver no GitHub