| 1 | --- |
| 2 | name: release |
| 3 | description: Use whenever cutting, preparing, tagging, or publishing an onde-cli release — version bumps, release branch, merges, tagging, pushing to trigger the publish pipeline, and verifying it landed. Also use when a release went wrong (wrong tag target, failed crates.io verify, development left behind main, unsigned release commits) and the repo state needs repair. Trigger even if the user only says "release vX.Y.Z", "bump the version", "tag a release", or "publish the new version". |
| 4 | user-invocable: true |
| 5 | --- |
| 6 | |
| 7 | # Release skill |
| 8 | |
| 9 | End-to-end release procedure for `onde-cli`. Complements the git skill (merge rules) |
| 10 | and the distribution skill (channel details) — this skill is the concrete runbook. |
| 11 | |
| 12 | ## Decide the version |
| 13 | |
| 14 | - Look at `git tag -l 'v*' --sort=-v:refname | head -1` for the latest release. |
| 15 | - Look at the commits since (`git log v<latest>..development --oneline`). |
| 16 | Fixes and chores only → patch bump. New user-facing features → minor bump. |
| 17 | - Check for an abandoned `release/vX.Y.Z` branch — its version is usually the |
| 18 | intended next one. If it exists, inspect it (`git log development..release/...`) |
| 19 | before deciding to reuse or delete it; do not blindly `branch -D` without looking, |
| 20 | and never reuse a branch whose commits were made unsigned or by the wrong identity. |
| 21 | |
| 22 | ## Version bump surfaces |
| 23 | |
| 24 | These files pin the crate version and must all move together: |
| 25 | |
| 26 | - `Cargo.toml` — `version = "X.Y.Z"` in `[package]` |
| 27 | - `pub/onde_cli/pubspec.yaml` — `version: X.Y.Z` |
| 28 | - `server.json` — three occurrences: top-level `version`, and the npm and NuGet |
| 29 | `packages[].version` entries (search for the old version string to catch all) |
| 30 | |
| 31 | `docs/mcp-registry.md` and `.github/workflows/release-mcp-registry.yml` mention |
| 32 | past versions only as historical examples — do not bump those. |
| 33 | |
| 34 | The npm wrapper manifests are NOT bump targets: the committed `package.json` files |
| 35 | are rewritten at release time by `npm/scripts/render-*.cjs` from the tag. |
| 36 | |
| 37 | After editing, run `cargo check --locked` (or let the build update `Cargo.lock`) |
| 38 | so the lockfile carries the new version, and commit it in the same bump commit. |
| 39 | |
| 40 | ## Sequence |
| 41 | |
| 42 | Follow the git skill's branch flow — `release/vX.Y.Z → development → main`, |
| 43 | `--no-ff` at every hop, tag on the final `main` merge commit. Concretely: |
| 44 | |
| 45 | ```bash |
| 46 | git checkout development |
| 47 | git pull <remote> development # start from the pushed tip |
| 48 | git checkout -b release/vX.Y.Z |
| 49 | # ... edit the bump surfaces ... |
| 50 | git commit -am "Prepare vX.Y.Z" # signed, correct identity (see below) |
| 51 | git checkout development |
| 52 | git merge --no-ff release/vX.Y.Z -m "Merge release prep for vX.Y.Z" |
| 53 | git checkout main |
| 54 | git pull <remote> main |
| 55 | git merge --no-ff development -m "Merge development for vX.Y.Z" |
| 56 | git tag vX.Y.Z |
| 57 | git push <remote> development main vX.Y.Z |
| 58 | git branch -d release/vX.Y.Z # only after the merge lands |
| 59 | ``` |
| 60 | |
| 61 | The remote here is the one whose URL is the sigit.si or GitHub org repo — |
| 62 | check `git remote -v`; this repo is commonly cloned with per-account remotes |
| 63 | (`gh-setoelkahfi`, `gh-paydii`, `gh-keypair34`) rather than `origin`. |
| 64 | |
| 65 | ### What a good result looks like |
| 66 | |
| 67 | - The tag sits on the step-3 merge commit on `main` (`git log --graph main` |
| 68 | shows a merge commit directly above the previous release). |
| 69 | - `git log development..main` and `git log main..development` are both empty |
| 70 | after the merges — the two branches carry identical trees. |
| 71 | - Never tag a release-branch tip or a bare bump commit on `main`; if the work |
| 72 | somehow already landed on `main` linearly, follow the git skill's fallback |
| 73 | (normal commit, tag it) rather than rewriting history. |
| 74 | |
| 75 | ### Post-push back-merge check |
| 76 | |
| 77 | If anything reached `main` without passing through `development` first (hotfix, |
| 78 | manual push, a fast-forwarded release), bring it back immediately: |
| 79 | |
| 80 | ```bash |
| 81 | git checkout development |
| 82 | git merge --no-ff main -m "Merge main for vX.Y.Z" |
| 83 | git push <remote> development |
| 84 | ``` |
| 85 | |
| 86 | A release is not done until the two branches contain the same tree. |
| 87 | |
| 88 | ## Commits and signatures — read the git-commit-signing skill first |
| 89 | |
| 90 | Release commits must be GPG-signed by the maintainer identity that GitHub can |
| 91 | verify (this repo: `Seto Elkahfi <1797197+setoelkahfi@users.noreply.github.com>`, |
| 92 | EDDSA key `2B76DF0F04014E36`). The `gh` CLI's active account is independent of |
| 93 | git config — set both, and verify before pushing: |
| 94 | |
| 95 | ```bash |
| 96 | git log -1 --format='%an <%ae> sig:%G? %GK' # want sig:G and the right key |
| 97 | gh api repos/<owner>/<repo>/commits/<sha> --jq .commit.verification.verified |
| 98 | ``` |
| 99 | |
| 100 | Every `git commit` and `git merge` in this flow creates a commit — configure |
| 101 | signing so merges are signed too, or amend unsigned ones before pushing |
| 102 | (`git commit --amend --no-edit -S`, only on unpushed commits). |
| 103 | |
| 104 | Commit trailer: end release-related commit messages with |
| 105 | |
| 106 | ``` |
| 107 | Co-Authored-By: siGit Code v1.6.7-acp <noreply@sigit.si> |
| 108 | ``` |
| 109 | |
| 110 | ## The tag push is the release |
| 111 | |
| 112 | Pushing `vX.Y.Z` triggers `release-crates.yml`, which verifies the tag against |
| 113 | `Cargo.toml` and `pubspec.yaml`, publishes the crate, then dispatches the |
| 114 | wrapper workflows (GitHub Releases, npm, NuGet, PyPI, then Homebrew and pub.dev |
| 115 | after the GitHub Release assets exist). There is no separate "publish" step. |
| 116 | |
| 117 | Watch it land: |
| 118 | |
| 119 | ```bash |
| 120 | gh run list --branch vX.Y.Z |
| 121 | gh run watch <run-id> --exit-status |
| 122 | ``` |
| 123 | |
| 124 | `CI` also runs on the tag push; both should be green. If `release-crates` fails, |
| 125 | no wrapper publishes — fix the cause, move the tag only if it has not published |
| 126 | anything yet (crates.io versions are immutable; a failed verify is safe to retry, |
| 127 | a published crate is not). |
| 128 | |
| 129 | ## Verify |
| 130 | |
| 131 | After the pipeline finishes, spot-check the fan-out (all idempotent, safe to re-check): |
| 132 | |
| 133 | - crates.io: `cargo search onde-cli --limit 1` |
| 134 | - GitHub Release: `gh release view vX.Y.Z` |
| 135 | - npm: `npm view onde-cli version` |
| 136 | - PyPI: `https://pypi.org/pypi/onde-cli/json` → `info.version` |
| 137 | - NuGet: flat container index for `onde-cli` |
| 138 | - Homebrew/pub.dev trail the GitHub Release; they may take a few extra minutes |
| 139 | |
| 140 | ## Failure modes seen before |
| 141 | |
| 142 | - Fast-forwarding `main` to the bump commit and tagging it — leaves no merge |
| 143 | commit, and leaves `development` behind. Back-merge `main` into `development` |
| 144 | right away and prefer merge commits next time. |
| 145 | - Deleting a leftover `release/` branch that contained unsigned bump commits and |
| 146 | redoing the bump fresh — fine, but check it first. |
| 147 | - Forgetting `Cargo.lock` in the bump commit. |
| 148 | - Pushing with the wrong `gh` account active — the push works, but PR/issue |
| 149 | actions afterwards get attributed to the wrong identity. |