Releasing node-telegram-bot-api
End-to-end release flow. Replace X.Y.Z with the target version (example below uses 2.0.0).
Releases ship from master; work happens on a short-lived version/X.Y.Z branch.
v2 notes. The lockfile is
bun.lock(the v1 redesign droppedpackage-lock.json).bun.lockpins dependencies only, not the root package version, so a version bump changespackage.jsonand nothing else - there is no lockfile to regenerate or commit. A prerelease like-alpha.1/-rc.0publishes under a non-latesttag (see section 8).
0. Orient first (don't assume)
git status # must be clean
node -p "require('./package.json').version" # current version
npm view node-telegram-bot-api dist-tags # what 'latest' / 'next' point at
Decide the new version and whether it should become npm latest (a stable release does;
a prerelease like -rc.0 should publish under a different tag with --tag next).
1. Branch
git checkout -b version/X.Y.Z
2. README + CHANGELOG
README rarely needs changes (the version comes from an npm badge, not hard-coded). Only touch it if a documented fact changed.
CHANGELOG.md must have an entry that matches the existing convention exactly. Every released version uses:
## [X.Y.Z][X.Y.Z] - YYYY-MM-DDwith a matching link-def at the very bottom of the file:
[X.Y.Z]:https://github.com/yagop/node-telegram-bot-api/releases/tag/vX.Y.Zand the
[Unreleased]compare link bumped to the new tag:[Unreleased]:https://github.com/yagop/node-telegram-bot-api/compare/vX.Y.Z...masterUse the actual release date (today),
-(ASCII hyphen, not an em dash), and keep an empty## [Unreleased][Unreleased]section at the top.
doc/api.mdis the generated API reference and is tracked in git. If the release changes public declarations or their documentation, runnpm run generate:docsand include the updated reference. See the generate-docs skill.
3. Bump version (package.json only, no git tag yet)
npm version X.Y.Z --no-git-tag-version
This updates package.json only. Verify bun.lock is untouched:
node -p "require('./package.json').version" # == X.Y.Z
git diff --exit-code bun.lock # exits 0 = no changes (expected)
4. Sanity gate (must pass before publishing)
npm run check # full gate: typecheck (src + test + examples) + lint:core + check:edge + unit (bun)
npm run build # this is what `prepublishOnly` runs on publish - must succeed
dist/ is gitignored (built on publish via prepublishOnly), so it is NOT committed.
CI additionally runs the unit suite on the Node 22/24/26 matrix - see the run-tests skill.
5. Commit + push
Only two files change: CHANGELOG.md and package.json. The repo's release commits use the
bare version as the message:
git add CHANGELOG.md package.json
git commit -m "X.Y.Z"
git push -u origin version/X.Y.Z
6. PR to master, wait for green CI, merge
gh may not be available here, and the GitHub MCP toolset for this environment is read/list-only. Create/merge the PR via the GitHub web UI (or the GitHub REST API if you have a GITHUB_TOKEN).
- Open a PR head=
version/X.Y.Zbase=master, titleX.Y.Z. - Wait for all checks to pass. Expect: Typecheck (src + test + examples), Unit tests (Node 22/24/26 + Bun), Build (dist); AppVeyor runs the same unit suite on Windows (Node 20/22); Integration is skipped on PRs (no secrets).
- Merge the PR (merge commit).
7. Pull master
git checkout master && git pull origin master
node -p "require('./package.json').version" # == X.Y.Z
8. npm publish
Auth from NPM_TOKEN (env). Write it without printing the value, dry-run first, then publish:
printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > "$HOME/.npmrc"
npm whoami # should print: yagop
npm publish --dry-run # inspect the tarball (expect README, LICENSE, dist/, package.json)
npm publish # stable -> latest; for a prerelease add: --tag next
npm view node-telegram-bot-api version # confirm == X.Y.Z
⚠️ Read-only-token gotcha.
npm whoamisucceeding does NOT mean the token can publish. Ifnpm publishreturnsE403 ... You may not perform that action with these credentials(andnpm profile getalso 403s), the token is read-only / too narrowly scoped. A 403 is a clean failure - nothing was uploaded. Fix: use an npm Automation token, or a Granular token with Read and write that includes this package. If you can't get one, the maintainer publishes manually with their OTP:! cd /workspace && npm publish.
9. Tag the release
The CHANGELOG link-def points at releases/tag/vX.Y.Z, and every prior release has a matching
annotated tag. Tag the released master HEAD and push:
git tag -a vX.Y.Z -m "X.Y.Z" $(git rev-parse master)
git push origin vX.Y.Z
10. GitHub Release
There is no create-release path via local tooling: gh isn't installed, and the GitHub MCP
server only exposes read-only release tools (get_latest_release, get_release_by_tag,
list_releases) - no create/update. Two options:
- REST API (needs a token). If
GITHUB_TOKENis in the session env (a PAT withrepo, or fine-grained with Contents: write), POST to the releases endpoint. Build the JSON body from a notes file so newlines/quotes are escaped safely (see snippet below). Note: setting the var in a separate terminal won't reach the session - it must be injected the same wayNPM_TOKENis. Body = the CHANGELOGX.Y.Zsection verbatim (it's self-contained, no reference links), with acompare/<prev>...vX.Y.Zline appended. Usemake_latest: "true",prerelease: false. - Web UI (no token).
https://github.com/yagop/node-telegram-bot-api/releases/new?tag=vX.Y.Z-> titleX.Y.Z, paste the notes, check Set as the latest release.
REST snippet (run with node, token from env, never printed):
// create-release.mjs
import { readFile } from "node:fs/promises";
const token = process.env.GITHUB_TOKEN;
if (!token) { console.error("GITHUB_TOKEN not set"); process.exit(2); }
const body = await readFile(new URL("./RELEASE_NOTES.md", import.meta.url), "utf8");
const res = await fetch("https://api.github.com/repos/yagop/node-telegram-bot-api/releases", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "ntba-release-script",
},
body: JSON.stringify({ tag_name: "vX.Y.Z", name: "X.Y.Z", body, make_latest: "true", prerelease: false }),
});
console.log(res.status, (await res.json()).html_url ?? "");
11. (Optional) Announce on Telegram
Draft a short channel post (emojis, Telegram markdown *bold* / inline `code`) with the
few most relevant changes + the migration link + npm i node-telegram-bot-api. Keep it short.
Done-checklist
-
npm view node-telegram-bot-api version== X.Y.Z (correct dist-tag) -
masterat the release commit, version X.Y.Z - annotated tag
vX.Y.Zpushed (CHANGELOG link resolves) - GitHub Release published, marked latest