Skip to main content

hookdeck-cli-release

Guides maintainers through Hookdeck CLI releases (stable GA, beta from main, beta from feature branches) and user-centric GitHub release notes. Validates proposed versions against SemVer from the actual change set (e.g. breaking changes require a major bump). Use when cutting a release, drafting release notes, choosing vMAJOR.MINOR.PATCH, GoReleaser, npm publish, pre-releases, following the release checklist, or `gh release create`.

Source facts

Repository
hookdeck/hookdeck-cli
Last source activity
September 24, 2026 at 17:58
Detected SKILL.md language
English
Stars
365
Forks
22

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
hookdeck-cli-release
description
Guides maintainers through Hookdeck CLI releases (stable GA, beta from main, beta from feature branches) and user-centric GitHub release notes. Validates proposed versions against SemVer from the actual change set (e.g. breaking changes require a major bump). Use when cutting a release, drafting release notes, choosing vMAJOR.MINOR.PATCH, GoReleaser, npm publish, pre-releases, following the release checklist, or `gh release create`.
# Hookdeck CLI — release workflow ## Canonical documentation Follow **[README.md](../../../README.md) § Releasing** for human-oriented steps (GitHub UI alternative, install commands for beta/stable). **Agents:** perform the **publish** step with the **GitHub CLI** (`gh`) — see **Publish with GitHub CLI (`gh`)** below (temp notes file → `gh release create` → remove temp file). This skill adds **how automation works**, **release note expectations**, and a **research loop** for drafting notes. ## Agent checklist (end-to-end) Follow **in order**. Treat items with **gate** as blocking unless the maintainer explicitly overrides. - [ ] **Release shape:** GA from **`main`** vs beta from **`main`** vs beta from **feature branch** — matches [README § Releasing](../../../README.md) and maintainer intent. - [ ] **`PREV_TAG` / `NEW_TAG`:** Confirmed (or proposed and agreed); baseline tag is correct for the line of development (e.g. last GA vs beta series). - [ ] **Change set:** Reviewed `git log PREV_TAG..HEAD` (and diff if needed); changes grouped for **user-facing** release notes (see **Research loop**). - [ ] **SemVer gate:** Proposed `NEW_TAG` matches **minimum** MAJOR/MINOR/PATCH for the delta (see **SemVer: validate the proposed version**). Stop and realign if under-bumped. - [ ] **Release notes:** Draft complete (see **Drafting release notes** and [references/release-notes-template.md](references/release-notes-template.md)); includes **Full Changelog** compare link; **contributor shout-outs only when warranted** (see that section). - [ ] **CI gate:** Latest commit on the **target branch** has **green** GitHub checks (mandatory for GA on `main`; required for betas on the branch being tagged). - [ ] **Approval:** Maintainer signed off on tag name, notes, and branch — no unilateral surprise tags. - [ ] **Acceptance group is idle (gate):** no acceptance run in progress or queued — `gh run list --workflow=test-acceptance.yml --status in_progress` and `--status queued` both empty. The release gate shares the `acceptance-suite` concurrency group, which keeps only one *pending* run: if the release's acceptance run is left pending, a pull request pushed behind it cancels it, and the builds and npm publish that depend on it never run. Don't open or push PRs while a release is building. - [ ] **Publish:** Write notes to a **temporary file**, run **`gh release create`** (see **Publish with GitHub CLI (`gh`)**), then **`rm`** the temp file. Use `--prerelease` for betas. (Humans may still use the GitHub UI per README.) - [ ] **Post-publish (optional):** Confirm the **`release`** workflow in Actions completed successfully for the new tag. For commit-level detail while working through the checklist, use the **Research loop** below. ## What triggers a release? - **[.github/workflows/release.yml](../../../.github/workflows/release.yml)** runs on **`push` of tags** matching `v*` (not on ordinary branch pushes). - **We release by creating a GitHub Release**, which creates the tag as a side effect and starts the workflow. `gh release create` (below) or the GitHub UI both do this. - Pushing a bare `git tag` *also* starts the workflow, and that is exactly why you must not do it. It publishes to npm, Homebrew, Scoop and Docker off a release whose notes are empty, and npm will not let you republish the version. **Never push a `v*` tag by hand — not to cut a release, and never as a bookmark.** ## What the workflow does (high level) 1. **GoReleaser** (macOS, Linux, Windows jobs): builds binaries/archives, updates distribution channels per [.goreleaser/*.yml](../../../.goreleaser/mac.yml) (Homebrew, Scoop, Docker, GitHub release artifacts). Config uses `release.mode: append` and `changelog.disable: true` — **GoReleaser does not write the release note body from git**; maintainers supply or edit the GitHub release description. 2. **`publish-npm` job**: Determines which branch contains the tag (prefers `main` / `master`, else first matching remote branch), checks out that branch, sets `package.json` version from the tag, builds npm binaries via GoReleaser, runs **`npm publish`** with `latest` for stable semver or a derived tag (e.g. `beta`) for pre-releases (see workflow `npm_tag` step). ## Stable (GA) release - **Humans (README):** GitHub Releases → Draft → new tag `vM.m.p` → target **`main`** → notes → Publish. - **Agents:** After gates pass, use **`gh release create`** with `--target main` and `--notes-file` (see **Publish with GitHub CLI (`gh`)**). - **Do not publish a GA release until CI is green for `main`:** the **latest commit on `main`** must show successful checks in GitHub (same bar as README: ensure tests pass on `main`). Verify on the **Actions** tab (filter branch `main`, confirm the run for the tip of `main` succeeded) or via the commit’s status on github.com. - **CLI check:** after `git fetch origin main`, confirm the status check rollup is `SUCCESS` for `origin/main` (replace owner/repo if forked): ```bash SHA=$(git rev-parse origin/main) gh api graphql -f query=' query($owner:String!,$repo:String!,$sha:GitObjectID!){ repository(owner:$owner,name:$repo){ object(oid:$sha){ ... on Commit { statusCheckRollup { state } } } } }' -F owner=hookdeck -F repo=hookdeck-cli -F sha="$SHA" \ --jq '.data.repository.object.statusCheckRollup.state' ``` Returns **uppercase** `SUCCESS` / `FAILURE` / `PENDING` / `ERROR` / `EXPECTED`. Do **not** tag or publish GA unless this is `SUCCESS`. > **Do not use `gh api "repos/OWNER/REPO/commits/${SHA}/status"`.** That endpoint reports only *legacy commit statuses*, which GitHub Actions never writes — it writes *check runs*. In a repo whose CI is Actions it returns `pending` off `total_count: 0` no matter how green CI is, so the gate blocks every release. `statusCheckRollup` folds check runs **and** legacy statuses into one verdict, so it stays correct if a status-writing integration is ever added. > > `gh pr checks` and `gh api ".../commits/${SHA}/check-runs"` are valid alternatives, but neither covers legacy statuses on its own. - Stable tags drive **`latest`** on npm, stable Homebrew/Scoop formulas, Docker `latest`. ## Pre-release (beta) - **From `main`:** Tag like `v1.3.0-beta.1`, target `main`, mark **pre-release** (`gh release create ... --prerelease`). Good for broad beta testing. **Still verify `main` is green** (same CI check as GA) before tagging. - **From a feature branch:** Same tag pattern; **`--target <feature-branch>`** so the workflow builds that tip. **Verify CI is green for that branch’s latest commit** before tagging. Add notes on **what to test** (betas often ship with minimal notes; still document intent). - Install paths for beta: see README (npm `@beta`, `hookdeck-beta` brew/scoop, Docker image tag — **not** `latest` for beta). ## Publish with GitHub CLI (`gh`) **Create the release with `gh` — do not push a tag.** `gh release create` publishes the GitHub Release *with its notes* and creates the tag in one step, in that order, so the workflow never runs against an empty release. This is the team's process, not a convenience. 1. **Create a temp file for notes** (never commit it). Register cleanup so the file is removed even if `gh` fails: ```bash NOTES_FILE="$(mktemp "${TMPDIR:-/tmp}/hookdeck-cli-release-notes.XXXXXX.md")" trap 'rm -f "$NOTES_FILE"' EXIT ``` 2. **Write** the final markdown body to `"$NOTES_FILE"` (same content you would paste in the GitHub UI). 3. **Create the release** (run from a clone of `hookdeck/hookdeck-cli`, or use `--repo` as below): **Stable GA from `main`:** ```bash gh release create "vM.m.p" \ --repo hookdeck/hookdeck-cli \ --target main \ --title "vM.m.p" \ --notes-file "$NOTES_FILE" ``` **Pre-release (beta):** add `--prerelease`. **Feature branch:** set `--target <branch>` instead of `main`. 4. **Cleanup:** With `trap` above, the temp file is deleted on shell exit. If you did not use `trap`, run `rm -f "$NOTES_FILE"` after `gh` succeeds. **Requirements:** `gh` installed and authenticated (`gh auth login`). Do not put secrets in the notes file. ### Fallback: the GitHub UI If `gh` is unavailable, use the **README** flow: GitHub Releases → Draft a new release → create the tag from the target branch → paste the notes → Publish. Same outcome, same order. There is no `git tag` fallback. A hand-pushed tag starts the same publish pipeline from a release that has no notes, and the npm version it burns cannot be reused. ## SemVer: validate the proposed version The user may suggest a tag (e.g. `v2.0.1`). **Always sanity-check it** against what actually changed since **`PREV_TAG`** (usually the last **GA** tag on that line of development—confirm with the maintainer for long beta series). **Interpret SemVer for this CLI (user-facing contract):** | Change since `PREV_TAG` | Bump | Examples | |-------------------------|------|----------| | **Breaking** — requires users to change scripts, configs, or habits | **MAJOR** | Removed or renamed commands/flags; different defaults that break automation; dropped or incompatible config file fields; incompatible change to documented machine-readable output | | **New capability**, backward compatible | **MINOR** | New commands or flags; new subcommands; additive behavior; deprecations **announced** but old path still works | | **Fixes / internal / docs-only** (no new user-facing capability, no break) | **PATCH** | Bug fixes; telemetry/CI; dependency bumps with no CLI contract change; help text clarifications | **Signals (hints only):** Conventional commits with `BREAKING CHANGE:` / `feat!:` / `fix!:` suggest severity—still **read the diff and release notes**; commits can be mis-tagged. **Pre-releases** (`v2.1.0-beta.1`): the **base version** (`2.1.0`) must still follow the table above relative to the last GA. A beta for a **major** rewrite should be `v3.0.0-beta.1`, not `v2.5.0-beta.1`, if the delta includes breaking changes vs `v2.x` GA. **Agent behavior:** 1. After categorizing changes for release notes, state the **minimum** SemVer bump required. 2. Compare to the user’s proposed `NEW_TAG`. If they conflict (e.g. patch tag but breaking changes), **do not treat the user’s version as authoritative**—explain the mismatch and recommend the correct `vMAJOR.MINOR.PATCH` (and pre-release suffix if applicable). 3. If ambiguous (unclear whether a change breaks callers), **ask the maintainer** before tagging. ## Drafting release notes (user-centric) Use **[references/release-notes-template.md](references/release-notes-template.md)** for the skeleton and **[references/release-notes-voice.md](references/release-notes-voice.md)** for how to write the prose — second person, lead with the fix then explain the defect, state the consequence concretely, no tables, issues linked in full. Read one recent GA release in full (v2.5.0 or v2.6.0) before drafting; they are the specification the voice file summarises. **Sections:** Include only headings that have real content — **omit** empty sections (e.g. do not add “Breaking changes” with “None”). **Patterns observed in this repo:** - **Large GA (e.g. v2.0.0):** `Summary`, then as needed: `Breaking changes / migration`, `New features` (subsections per area), `Improvements / behavior changes`, `Internal` — skip any block with nothing to say. - **Feature release (e.g. v1.9.0):** `## Features` with detailed bullets + **Full Changelog** compare link. - **Patch (e.g. v1.9.1):** `## Fixes`, `## Updates`, PR links with authors + **Full Changelog** — omit unused sections. Always include a **Full Changelog** line: `https://github.com/hookdeck/hookdeck-cli/compare/<prev_tag>...<new_tag>` **Contributors / shout-outs:** Do **not** add a generic “thanks to all contributors” block every release. **Regular maintainers and repeat contributors do not need a call-out.** Only include a contributor section when: - There are **new contributors** first shipping in this release (welcome them by name/GitHub handle), and/or - Someone made an **exceptionally large** contribution worth highlighting for this specific release. Otherwise omit the **Contributors** section entirely. ## Research loop (agent or maintainer) 1. **Tags:** Confirm `PREV_TAG` and `NEW_TAG` with the user (or `git describe --tags --abbrev=0` on the release branch). For beta series, baseline may be last **GA** tag. 2. **Commits:** `git log PREV_TAG..HEAD --oneline` and read full messages. Treat **Conventional Commits** (`feat:`, `fix:`, `BREAKING CHANGE:`) as hints only — rewrite for **user impact** (commands, flags, migrations). 3. **Group:** Merge related commits; call out breaking changes and required user actions explicitly. 4. **SemVer check:** Using **SemVer: validate the proposed version**, classify the delta since `PREV_TAG` and verify the proposed `NEW_TAG` matches the required **MAJOR / MINOR / PATCH** bump. Flag mismatches before any tag or release. 5. **PRs / links:** Map commits to PRs (`gh pr list`, GitHub compare UI) for **PR links in the notes**. Use **Contributors** shout-outs only per **Drafting release notes** (new contributors or exceptional contribution—not every author every time). 6. **Sanity:** Skim diff or `REFERENCE.md` / user-facing help if commits are unclear.
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub