IlmHamroh
JavaScript Full-stack/32-qism. Professional Git va jamoada ishlash2/19-dars22 daqiqa
Mundarija (36)

CHANGELOG va reliz avtomatikasi: release-please, semantic-release, changesets

Qisqacha: Reliz — foydalanuvchiga chiqarilgan, raqamlangan versiya. Uning ikki hujjati bor: versiya raqami (SemVer: fix → patch, feat → minor, buzuvchi o'zgarish → major) va CHANGELOG — "nima o'zgardi" ro'yxati. Ikkalasini qo'lda yuritish unutiladi va eskiradi. Shuning uchun ular Conventional Commits xabarlaridan avtomatik hosil qilinadi: semantic-release (CI'da to'liq avtomat), release-please (reliz PR'i orqali), changesets (har PR bilan alohida o'zgarish fayli). Kanon tajribasida uchala vosita vazifalar-api uchun bir xil javob berdi — 4.0.1.

Bu darsda

  • SemVer qoidalarini, 0.x va pre-release (-beta.1) versiyalarini qo'llaysiz.
  • Keep a Changelog formatidagi CHANGELOG'ni o'qiysiz va yozasiz.
  • Commit xabarlaridan keyingi versiyani o'zingiz hisoblaydigan funksiya yozasiz.
  • conventional-changelog, semantic-release va changesets'ni lokal ishga tushirasiz, release-please qanday ishlashini bilasiz.
  • vazifalar-api ning 4.0.1 relizini chiqarasiz: CHANGELOG, versiya, tag.

Oldin bilishingiz kerak: Tag va relizlar, Semver va versiya diapazonlari, Conventional Commits va commitlint, Bisect.

1. Nega bu kerak?

Bisect darsida breaker nuqsonini tuzatdik va fix(ai): … commitini qildik. Endi bu tuzatish foydalanuvchiga yetib borishi kerak. Ikki savol tug'iladi.

Birinchisi: qaysi versiya? package.json da 4.0.0 turibdi. Keyingisi 4.0.1 mi, 4.1.0 mi, 5.0.0 mi? Javob noto'g'ri bo'lsa, API'dan foydalanayotganlar adashadi. Masalan, ^4.0.0 deb bog'langan loyiha buzuvchi o'zgarishni "xavfsiz yangilanish" deb oladi.

Ikkinchisi: nima o'zgardi? Foydalanuvchi har commitni o'qimaydi. Unga qisqa ro'yxat kerak: "4.0.1 da — AI so'rovlari endi 25 soniyada tugaydi, zaxira model to'silmaydi". Bu ro'yxat CHANGELOG (o'zgarishlar tarixi) faylida turadi.

Kanon reliz paytida yana bir haqiqiy xatoni topdi. MCP server o'z versiyasini mijozga aytadi — va u mcp-server.ts ichida "4.0.0" deb qotirib yozilgan edi. package.json 4.0.1 ga o'tdi, MCP esa "men 4.0.0" deyishda davom etardi. Bir narsa ikki joyda yozilsa, biri albatta eskiradi. Versiya va CHANGELOG ham shunday: ularni qo'lda yuritsangiz, bir kun unutasiz.

Hayotiy o'xshatish: choyxonadagi menyu. Oshpaz retseptni o'zgartirdi, lekin devordagi menyu va kassadagi narx ro'yxati eski qoldi. Mehmon bir narsani ko'radi, boshqasini oladi. Yechim — bitta manbadan (oshxona daftari) hammasini avtomatik chop etish. Bizda bu manba — commit xabarlari.

2. SemVer: qisqa takror va yangi detallar

2.1 Uch raqam

Tag va relizlar va Semver darslaridan bilasiz: versiya MAJOR.MINOR.PATCH.

O'zgarish Commit turi Misol
xato tuzatildi, hech narsa buzilmadi fix: 4.0.0 → 4.0.1
yangi imkoniyat, eskisi ishlaydi feat: 4.0.1 → 4.1.0
eski kod buziladi feat!: yoki BREAKING CHANGE: 4.1.0 → 5.0.0
hujjat, sozlama, test docs:, chore:, test: reliz yo'q

Conventional Commits darsidagi xabar formati shu jadval uchun yaratilgan. ! sarlavhada yoki tanadagi BREAKING CHANGE: qatori — "bu o'zgarish eski foydalanuvchilarni buzadi" degan belgi. Bir nechta commit bo'lsa, eng kattasi yutadi: bitta feat va o'nta fix — minor.

2.2 0.x va pre-release

Ikki holat alohida qoidaga ega (semver.org, 2.0.0):

  • 0.y.z — boshlang'ich ishlab chiqish. "Hamma narsa istalgan payt o'zgarishi mumkin, ommaviy API'ni barqaror deb hisoblamang." Loyiha hali shakllanayotgan bo'lsa — 0.x. Birinchi barqaror reliz — 1.0.0.
  • Pre-release (oldindan chiqariladigan versiya) — chiziqcha va belgi: 4.1.0-beta.1, 5.0.0-rc.2. Bu "tayyor emas, sinab ko'ring" degani. Pre-release asosiy versiyadan oldin turadi: 4.1.0-beta.1 < 4.1.0.

Pre-release'lar o'zaro qanday tartiblanadi? Nuqta bilan ajratilgan qismlar chapdan solishtiriladi. Raqamlar — son sifatida (beta.2 < beta.11), so'zlar — alifbo bo'yicha (alpha < beta < rc). Buni 2-mashqda funksiya qilib yozasiz.

^ va ~ belgilarini eslang: ^4.0.0 — 4.x.x ning istalgani (major o'zgarmasa), ~4.0.0 — faqat 4.0.x. Ikkalasi ham pre-release'larni sukut bo'yicha olmaydi (Semver darsi). Demak, 5.0.0-beta.1 chiqarib, foydalanuvchilarni buzib qo'yish xavfi yo'q — beta faqat aniq so'raganlarga yetadi.

2.3 Keyingi versiyani hisoblaymiz

Reliz vositalari ichida xuddi shu mantiq ishlaydi. Uni o'zimiz yozib ko'ramiz:

js
const SARLAVHA = /^(\w+)(?:\(([^)]*)\))?(!)?: (.+)$/;
const DARAJA = { patch: 1, minor: 2, major: 3 };

function ozgarishTuri(xabar) {
  const [sarlavha, ...tana] = xabar.split("\n");
  const m = sarlavha.match(SARLAVHA);
  if (!m) return null;
  const [, tur, , undov] = m;
  const buzuvchi = tana.some((q) => q.startsWith("BREAKING CHANGE:"));
  if (undov || buzuvchi) return "major";
  if (tur === "feat") return "minor";
  if (tur === "fix") return "patch";
  return null;
}

function keyingiVersiya(joriy, xabarlar) {
  let eng = null;
  for (const x of xabarlar) {
    const t = ozgarishTuri(x);
    if (t && (!eng || DARAJA[t] > DARAJA[eng])) eng = t;
  }
  if (!eng) return null;
  const [ma, mi, pa] = joriy.split(".").map(Number);
  if (eng === "major") return `${ma + 1}.0.0`;
  if (eng === "minor") return `${ma}.${mi + 1}.0`;
  return `${ma}.${mi}.${pa + 1}`;
}

console.log(keyingiVersiya("4.0.0", ["fix(ai): alohida breaker"]));
console.log(keyingiVersiya("1.2.0", [
  "feat(cli): --baza bayrog'i",
  "fix: yaxlitlash 2 xonagacha",
  "feat(cli)!: --json chiqish formati sukut bo'yicha",
]));
console.log(keyingiVersiya("1.2.0", ["docs: README", "chore: kesh"]));

Konsolda:

text
4.0.1
2.0.0
null

Birinchi qator — vazifalar-api: bitta fix → patch. Ikkinchisi — uchta commit, eng kattasi ! → major. Uchinchisi — faqat hujjat va sozlama: reliz kerak emas, null.

Muntazam ifoda (regex) sarlavhani to'rt qismga ajratadi: tur (fix), qavs ichidagi soha (ai), ixtiyoriy ! va tavsif. Uni Conventional Commits darsidagi commitlint qoidasi bilan solishtiring — ular bir formatni o'qiydi.

Tekshirib ko'ring: Joriy versiya 2.3.1. Oxirgi relizdan beri uchta commit: fix: sana formati, refactor: nomlash, perf: ro'yxat tezroq. Bizning keyingiVersiya nima qaytaradi?

Javob

2.3.2. fix — patch; refactor va perf bizning funksiyada reliz bermaydi. Haqiqiy vositalarda perf qanday hisoblanishi presetga (vosita ishlatadigan tayyor qoidalar to'plamiga) bog'liq — sozlamasini hujjatdan tekshiring (biz sinamadik). Qoida: versiyaga ta'sir qiladigan turlar ro'yxatini jamoa kelishib, sozlamaga yozib qo'yadi.

3. CHANGELOG: kim uchun va qanday

3.1 Keep a Changelog

CHANGELOG — odamlar uchun yozilgan o'zgarishlar ro'yxati. Eng mashhur kelishuv — Keep a Changelog (keepachangelog.com, 1.1.0). Uning asosiy qoidalari (rasmiy sahifadan, 2026-11-04 da tekshirildi):

  • eng yangi versiya — tepada;
  • har versiyada sana (2026-11-04 shaklida);
  • o'zgarishlar turlarga bo'linadi: Added, Changed, Deprecated, Removed, Fixed, Security;
  • tepada Unreleased bo'limi — hali relizga chiqmagan o'zgarishlar shu yerda yig'iladi.

Muhim fikr: CHANGELOG — git log emas. Unda "lint tuzatildi", "test qo'shildi" kabi ichki ishlar bo'lmaydi. Foydalanuvchiga ta'sir qiladigan narsalargina yoziladi.

3.2 Avtomatik CHANGELOG

Commit xabarlari to'g'ri yozilsa, CHANGELOG'ni ulardan hosil qilish mumkin: feat → "Features", fix → "Bug Fixes", ! → "BREAKING CHANGES". Turlar Keep a Changelog'dagidan biroz boshqacha nomlanadi, lekin g'oya bir xil. docs, chore, test kirmaydi — ular foydalanuvchi uchun emas.

Narxi: sifat commit xabarlariga bog'liq. "fix: tuzatdim" degan xabar CHANGELOG'da ham "tuzatdim" bo'lib chiqadi. Avtomatika yaxshi xabar yozish odatini majburiy qiladi — va bu yaxshi.

4. Reliz quvuri

Reliz vositalari ichida bir xil qadamlar turadi:

flowchart LR
  C["commitlar: fix, feat, !"] --> A["tahlil: patch / minor / major"]
  A --> V["versiya: package.json"]
  A --> L["CHANGELOG.md"]
  V --> T["git tag v4.0.1"]
  L --> T
  T --> R["GitHub Release, npm"]

Chapdan o'qing. Oxirgi tagdan beri commitlar tahlil qilinadi va eng katta o'zgarish versiyani belgilaydi. Keyin package.json va CHANGELOG yangilanadi, tag qo'yiladi, oxirida reliz e'lon qilinadi. Vositalar shu zanjirni kim va qachon boshlashida farq qiladi.

5. Uch vosita

Uchala vosita ham vazifalar-api tarixida sinaldi: v4.0.0 tagi va undan keyin bitta fix(ai): … commiti. Hammasi lokal, GitHub'ga hech narsa yuborilmadi. Versiyalar 2026-10-27 dan oldin chiqqan, ya'ni kamida bir hafta sinovdan o'tgan.

Vosita Qanday ishlaydi Kanon natijasi
conventional-changelog-cli 5.0.0 commitlardan Markdown ## 4.0.1 → "Bug Fixes"
semantic-release 25.0.9 CI'da: tahlil → versiya → tag → reliz "The next release version is 4.0.1"
@changesets/cli 3.0.3 PR bilan .changeset/*.md → changeset version package.json 4.0.1
release-please 17.11.2 GitHub'da reliz PR'i sinalmagan

5.1 conventional-changelog — eng sodda

Faqat CHANGELOG yozadi. Versiyani o'zi tanlamaydi — uni package.json dan oladi. Kanon CHANGELOG'i aynan shu vosita bilan qurildi.

Kichik namuna repo'da sinab ko'ramiz: valyuta CLI paketi, v1.2.0 tagidan keyin 5 ta commit, oxirgisi buzuvchi:

text
94b9c07 (HEAD -> main) feat(cli)!: --json chiqish formati sukut bo'yicha
3b14fc0 chore: kesh papkasi .gitignore ga
31ff4f4 docs: README misollari
b5293bf fix: yaxlitlash 2 xonagacha
455a92e feat(cli): --baza bayrog'i
1423afd (tag: v1.2.0) feat: kurslarni CBU API'dan olish

Oxirgi commit tanasida: BREAKING CHANGE: jadval endi --jadval bayrog'i bilan; skriptlar --jadval qo'shsin.

bash
npm version 2.0.0 --no-git-tag-version
npx --yes conventional-changelog-cli@5.0.0 -p conventionalcommits

Biz ishga tushirdik (vosita kanon o'rnatgan nusxadan, Node 24.21.0; sarlavhadagi havola manzili (…) bilan qisqartirildi — u pastda alohida):

text
## [2.0.0](…) (2026-11-04)

### ⚠ BREAKING CHANGES

* **cli:** jadval endi --jadval bayrog'i bilan; skriptlar --jadval qo'shsin.

### Features

* **cli:** --baza bayrog'i 455a92e
* **cli:** --json chiqish formati sukut bo'yicha 94b9c07

### Bug Fixes

* yaxlitlash 2 xonagacha b5293bf

Uch narsaga e'tibor bering:

  1. docs va chore yo'q — foydalanuvchi uchun emas.
  2. BREAKING CHANGES tepada, alohida — yangilanishdan oldin o'qilishi shart.
  3. Sarlavhadagi havola buzuq: uning manzili — ///compare/v1.2.0...v2.0.0. Repo'da origin remote yo'q edi, vosita GitHub manzilini bilmadi. Kanon tajribasida esa remote lokal papka edi va havolalarga kompyuter yo'li tushdi. Shuning uchun kanon CHANGELOG'idan havolalar olib tashlangan. GitHub'dagi repo'da havolalar to'g'ri chiqadi.

Yana bir tuzoq: yangi bo'lim sarlavhasi package.json versiyasidan olinadi. Avval npm version qilmasangiz, vosita yangi versiyani bilmaydi. Tartib: avval versiya, keyin CHANGELOG.

Diqqat: npm'da conventional-changelog-cli paketi "This package is no longer maintained" — "endi qo'llab-quvvatlanmaydi" deb belgilangan (2026-11-04 holati). Vorisi — conventional-changelog paketining o'z CLI'si. Biz uning 8.1.3 versiyasini xuddi shu valyuta repo'sida sinadik: natija yuqoridagi bilan bir xil chiqdi. Ikki farqi bor. Preset (tayyor qoidalar to'plami) — alohida paket: conventional-changelog-conventionalcommits. Sukut bo'yicha natija ekranga emas, CHANGELOG.md fayliga yoziladi; ekranda ko'rish uchun --stdout bayrog'i kerak. Kanon CHANGELOG'i 5.0.0 bilan qurilgan, shuning uchun darsdagi buyruqlar shu versiyada qoldi.

5.2 semantic-release — to'liq avtomat

semantic-release hamma qadamni o'zi bajaradi: tahlil, versiya, CHANGELOG (plaginlar bilan), tag, GitHub Release, npm'ga chiqarish. U CI'da ishlash uchun yaratilgan: hujjatiga ko'ra, reliz branch'idagi (odatda main) har muvaffaqiyatli build'dan keyin ishga tushadi. Odam versiya raqamini umuman tanlamaydi.

Lokal sinash uchun --dry-run (hech narsani o'zgartirmay, faqat ko'rsatish) va --no-ci bayroqlari bor. Xuddi shu valyuta repo'sida biz ishga tushirdik (remote — lokal "bare" repo; vaqt belgilari olib tashlandi, faqat asosiy qatorlar). Chiqishda uch narsani qidiring: oxirgi tag, commitlar soni va "next release version":

text
[semantic-release] » ℹ  Running semantic-release version 25.0.9
[semantic-release] » ✔  Allowed to push to the Git repository
[semantic-release] » ℹ  Found git tag v1.2.0 associated with version 1.2.0 on branch main
[semantic-release] » ℹ  Found 5 commits since last release
[semantic-release] [@semantic-release/commit-analyzer] » ℹ  The release type for the commit is major
[semantic-release] [@semantic-release/commit-analyzer] » ℹ  Analysis of 5 commits complete: major release
[semantic-release] » ℹ  The next release version is 2.0.0
[semantic-release] » ⚠  Skip v2.0.0 tag creation in dry-run mode

"5 commit tahlil qilindi: major reliz. Keyingi versiya — 2.0.0. Dry-run'da tag yaratilmadi." Bizning keyingiVersiya funksiyasi bilan aynan bir xil javob.

Allowed to push qatoriga qarang. semantic-release dry-run'da ham remote'ga yozish huquqini tekshiradi — u tag va relizni o'zi yuboradi. Shuning uchun lokal sinov uchun remote kerak bo'ldi.

Diqqat: Lokal sinovdagi tuzoq. git init --bare yangi repo'ning sukut branch'ini Git sozlamasidan oladi. U master bo'lsa, siz esa main yuborsangiz, bare repo'ning HEAD i bo'sh branch'ga qaraydi. Biz shu holatni qayta yaratdik:

fatal: couldn't find remote ref HEAD

"Remote'da HEAD topilmadi" — semantic-release ichidagi git fetch 128 kod bilan yiqildi. Davosi: git init --bare -b main.

5.3 changesets — har PR bilan o'zgarish fayli

changesets boshqacha yondashadi: versiyani commit xabaridan emas, odam yozgan fayldan oladi. Har PR bilan birga kichik fayl qo'shiladi:

text
---
"valyuta": minor
---

--baza bayrog'i: istalgan valyutaga nisbatan kurs.

Tepada — qaysi paket va qanday o'zgarish (patch, minor, major). Pastda — CHANGELOG'ga tushadigan matn. Bu fayl PR review'dan o'tadi: "minor deb belgilabsiz, lekin bu buzuvchi o'zgarish-ku" degan izoh aynan shu yerda yoziladi.

Reliz paytida bitta buyruq hamma fayllarni yig'adi:

bash
npx changeset status
npx changeset version

valyuta repo'sida ikki fayl (minor va patch) bilan biz ishga tushirdik:

text
🦋 changeset v3.0.3

Packages to be bumped:
- minor
  - valyuta
🦋 changeset v3.0.3

All files have been updated. Review them and commit at your leisure

Natija: package.json — 1.3.0, .changeset/ papkasida faqat config.json qoldi, CHANGELOG.md:

text
# valyuta

## 1.3.0

### Minor Changes

- --baza bayrog'i: istalgan valyutaga nisbatan kurs.

### Patch Changes

- Kurslar 2 xonagacha yaxlitlanadi.

Ikki o'zgarish bitta relizga birlashdi, eng kattasi (minor) yutdi. changesets ayniqsa monorepo'da kuchli: bitta repo'da bir nechta paket bo'lsa, har PR qaysi paketga tegishini aniq aytadi. Monorepo'ni Git va monorepo darsida ko'ramiz.

Kanon topgan ikki tuzoq, ikkalasini biz ham qayta ko'rdik:

  • changeset init interaktiv. 3.x versiyada u savol beradi: "Should the GitHub integration be used for changelogs?". Kiritish bo'lmasa (CI, skript) jarayon exit 13 bilan tugaydi. Yechim — .changeset/config.json ni qo'lda yozish.
  • "private": true paket. vazifalar-api npm'ga chiqmaydi, shuning uchun package.json da private: true. changesets bunday paketni sukut bo'yicha versiyalamaydi: changeset status "Packages to be bumped:" deydi va ro'yxat bo'sh. Config'ga "privatePackages": { "version": true, "tag": false } qo'shilgach — ishladi.

5.4 release-please — reliz PR'i

release-please (Google) GitHub bilan ishlaydi. U Actions'da yuradi va har main ga merge'dan keyin reliz PR'ini ochadi yoki yangilaydi. PR ichida — yangi versiya va CHANGELOG. Jamoa relizga tayyor bo'lganda shu PR'ni merge qiladi. Shunda release-please tag va GitHub Release yaratadi (github.com/googleapis/release-please, sinalmagan, 2026-11).

Kanonda u sinalmagan: lokal repoda ishlamaydi (GitHub API kerak), sinov reposida esa Actions o'chiq edi. Bu darsdagi tavsif — rasmiy hujjatdan.

5.5 Qaysi birini tanlash

Holat Mos vosita
CHANGELOG kerak, versiyani o'zingiz tanlaysiz conventional-changelog
npm kutubxona, har merge — reliz semantic-release
reliz vaqtini jamoa tanlaydi, GitHub'da release-please
monorepo, ko'p paket changesets

Kanon vazifalar-api uchun conventional-changelog ni tanladi: lokal ishlaydi, token va CI talab qilmaydi (loyihaning CI fayli hozircha qo'lda ishga tushiriladi). CI to'liq ishlaganda (GitHub Actions chuqur darsi) release-please yoki semantic-release'ga o'tish oson: commit xabarlari allaqachon tayyor.

Tekshirib ko'ring: Jamoangizda commit xabarlari tartibsiz ("tuzatdim", "update"), lekin har PR'ga tavsif yoziladi. Qaysi vosita mosroq va nega?

Javob

changesets. U versiya va CHANGELOG matnini commit xabaridan emas, PR bilan qo'shilgan .changeset/*.md fayldan oladi. semantic-release va release-please xabarlarni tahlil qiladi — tartibsiz xabarlarda ular "reliz yo'q" deydi yoki noto'g'ri versiya beradi. Uzoq muddatda baribir Conventional Commits'ga o'tish yaxshi, lekin changesets'ni hozir boshlash mumkin.

6. GitHub Release va tag

Tag — Git'ning o'zida, GitHub Release esa GitHub'dagi sahifa: tag + sarlavha + izoh + fayllar. Tag va relizlar darsida uni brauzerda yaratish yo'lini ko'rgansiz. Avtomatik yo'l — gh buyrug'i (cli.github.com/manual/gh_release_create, sinalmagan, 2026-11):

bash
gh release create v4.0.1 --title "4.0.1" --notes-file eslatma.md

Kanonda GitHub Release yaratilmadi — faqat annotated tag yuborildi. Tag uchun bitta foydali odat: git push --follow-tags — joriy branch bilan birga unga tegishli annotated taglarni ham yuboradi. Oddiy git push taglarni yubormaydi (07-qismda ko'rgansiz).

7. Loyiha qadami: 4.0.1 relizi

7.1 Kanon tajribasi: uch vosita, bitta javob

Kanon uchala vositani vazifalar-api tarixida ishga tushirdi (v4.0.0 + fix(ai): …). Natija:

Vosita Javob Vaqt
conventional-changelog ## 4.0.1 → Bug Fixes 16.6 s
semantic-release --dry-run "Analysis of 1 commits complete: patch release" 31.4 s
changesets package.json 4.0.1, "Patch Changes" —

Uchalasi ham 4.0.1. Vaqtlar yuklangan mashinada o'lchangan — taqqoslash uchun emas.

7.2 Versiya va CHANGELOG

O'z repo'ngizda tag bo'lmasa, avval qo'ying: 30-qism oxiridagi commitga v3.0.0, 31-qism oxiridagisiga v4.0.0 (Tag va relizlar darsidagi git tag -a). Keyin:

bash
npm version 4.0.1 --no-git-tag-version
npx --yes conventional-changelog-cli@5.0.0 \
  -p conventionalcommits -r 0 > CHANGELOG.md

npm version package.json va package-lock.json dagi versiyani o'zgartiradi. --no-git-tag-version — commit va tag qilmasin, ularni o'zimiz qilamiz. -r 0 — CHANGELOG'ni butun tarixdan qayta quradi. Kanon faylning boshiga qisqa izoh qo'shdi va commit havolalarini olib tashladi:

text
# O'zgarishlar tarixi

Format — [Conventional Commits](https://www.conventionalcommits.org/) dan `conventional-changelog -p conventionalcommits` bilan hosil qilinadi (32-qism #02);
versiyalar — [SemVer](https://semver.org/): `fix` → patch, `feat` → minor, `!`/`BREAKING CHANGE` → major. Qo'lda tahrirlanmaydi — commit xabarlari to'g'ri yozilsin.

## 4.0.1 (2026-11-04)

### Bug Fixes

* **ai:** har modelga alohida circuit breaker va umumiy deadline; 31-qism nuqsonlari (b3157bc)

## 4.0.0 (2026-11-02)

### Features

* **ai:** kunlik kvota va javob keshi (foydalanuvchi bo'yicha), AI hodisalari; MCP server (stdio, API kalit bilan) (ddeead9)
...

Kanon faylidan boshi; 4.0.0 ostida yana 6 ta feat va oxirida 3.0.0 bor. Sizda SHA'lar va sanalar boshqacha, GitHub remote bo'lsa — commitlar havola bo'lib chiqadi, ularni qoldiring.

API.md dagi qo'lda yozilgan "O'zgarishlar tarixi" bo'limi o'chirilmadi: tepasiga "4.0.1 dan boshlab — CHANGELOG.md" havolasi qo'shildi, eski qaydlar tarix uchun qoldi.

7.3 Reliz paytida topilgan xato

MCP server versiyasi ikki joyda edi. Endi src/mcp.ts uni package.json dan o'qiydi (kanon kodidan parcha, telefon uchun bo'lingan):

ts
import { readFileSync } from "node:fs";
// ...
const { version } = JSON.parse(
  readFileSync(new URL("../package.json", import.meta.url), "utf8"),
) as { version: string };
const server = mcpServerYarat(apiMijoz(url, kalit), version);

new URL("../package.json", import.meta.url) — fayl yo'li joriy modulga nisbatan, qayerdan ishga tushirilishidan qat'i nazar (ESM import darsidagi import.meta). Bo'lingan nusxani biz loyihaning shu bosqichi nusxasida tsc --noEmit va ESLint bilan tekshirdik.

Kanon tekshiruvi: npm run check yashil (Vitest 346 + 1 skip), node dist/mcp.js ishga tushdi.

7.4 Commit va tag

bash
git add -A
M="chore(release): 4.0.1 — CHANGELOG.md;"
M+=" fix(mcp): server versiyasi package.json dan"
git commit -m "$M"
git tag -a v4.0.1 -m "vazifalar-api 4.0.1"
git push --follow-tags

Kanon xabarini aynan oldik. Lekin uning narxi bor. Vositalar faqat sarlavha boshidagi turni o'qiydi — bu yerda chore. Ichidagi fix(mcp) ni hech qaysi vosita ko'rmaydi va u keyingi CHANGELOG'ga tushmaydi. To'g'ri yo'l — ikki commit: avval fix(mcp): …, keyin chore(release): 4.0.1. "Bitta commit — bitta tur" qoidasi aynan shuning uchun.

Kanon sinov reposida (u monorepo: 07-qism portfoliosi va vazifalar-api yonma-yon) tag nomi vazifalar-api-v4.0.1 bo'ldi — portfolioning v1.0.0 tagi bilan to'qnashmasin. PR #11: github.com/Sarvarbek0704/ilmhamroh-git-sinov/pull/11. Sizning repo'ngizda bitta paket — oddiy v4.0.1 yetadi.

8. Ko'p uchraydigan xatolar

8.1 Versiya ikki joyda

package.json 4.0.1, ilova esa "4.0.0" deydi. Belgisi: kodda versiya satri qotirilgan. Davosi: bitta manba — package.json; qolganlari undan o'qisin. Bunday joylarni topish oson: git grep '"4.0.0"'.

8.2 CHANGELOG'da yangi bo'lim yo'q

conventional-changelog eski versiya bo'yicha yozdi yoki hech narsa qo'shmadi. Sabab: npm version qilinmagan, package.json hali eski versiyada. Davosi: avval versiya, keyin CHANGELOG.

8.3 Buzuq havolalar

CHANGELOG sarlavhasidagi havola ///compare/v1.2.0...v2.0.0 ga yoki kompyuteringizdagi papkaga qaraydi. Sabab: repo'da remote yo'q yoki u lokal papka. Davosi: git remote -v da GitHub manzili bo'lsin, yoki havolalarni olib tashlang.

8.4 changeset init osilib qoldi yoki exit 13

Biz stdin'siz ishga tushirganimizda vosita savol berdi va to'xtadi (terminal boshqaruv belgilari olib tashlandi):

text
◆  Should the GitHub integration be used for changelogs?
○ Yes / ● No

Keyin Warning: Detected unsettled top-level await … va exit 13. Bu — "javob kutyapman, lekin kiritish yo'q" degani. Terminalda o'zingiz javob bering yoki config.json ni qo'lda yozing.

8.5 "Packages to be bumped:" bo'sh

private: true paket. Config'ga privatePackages.version: true qo'shing.

8.6 Tag yuborilmadi

GitHub'da reliz yo'q, CHANGELOG'dagi taqqoslash havolasi 404. Sabab: git push taglarni yubormaydi. Davosi: git push origin v4.0.1 yoki git push --follow-tags.

9. Mashqlar

Mashqlar kurs/mashqlar/32/02-reliz/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).

1-mashq (oson): bo'shliqlarni to'ldiring

  • Joriy 3.4.2, yagona commit feat: eksport — keyingi versiya
  • Joriy 3.4.2, commit fix!: sana formati o'zgardi — keyingi versiya
  • Pre-release 2.0.0-rc.1 va 2.0.0 dan qaysi biri katta? —
  • changesets'da hamma o'zgarish fayllarini yig'ib, versiya va CHANGELOG'ni yangilaydigan buyruq — npx changeset
  • Keep a Changelog'da hali chiqmagan o'zgarishlar bo'limi —
Yechim
  • 3.5.0 — feat → minor, patch nolga tushadi.
  • 4.0.0 — ! turidan qat'i nazar major qiladi.
  • 2.0.0 — pre-release asosiy versiyadan oldin turadi.
  • version — changeset status faqat ko'rsatadi, version o'zgartiradi.
  • Unreleased — reliz paytida u yangi versiya sarlavhasiga aylanadi.

2-mashq (o'rta): pre-release'larni tartiblash

versiyaSolishtir(a, b) funksiyasini yozing: a < b bo'lsa manfiy, teng bo'lsa 0, katta bo'lsa musbat son qaytarsin. Qoidalar (semver.org, 11-band):

  1. Avval MAJOR.MINOR.PATCH sonlar sifatida solishtiriladi.
  2. Ular teng bo'lsa: pre-release'siz versiya katta.
  3. Ikkalasida pre-release bo'lsa — nuqta bilan bo'lingan qismlar chapdan: ikkalasi son bo'lsa — son sifatida; son har doim so'zdan kichik; so'zlar — alifbo bo'yicha; qismlari kam bo'lgani kichik.

Ishora: split("-") bilan asosiy qism va pre-release'ni ajrating (bu mashqda pre-release ichida chiziqcha yo'q deb hisoblang); /^\d+$/.test(q) — qism sonmi. Massivni sort(versiyaSolishtir) bilan tartiblang (sort va taqqoslash funksiyasi).

Yechim
js
function qismSolishtir(x, y) {
  const xSon = /^\d+$/.test(x);
  const ySon = /^\d+$/.test(y);
  if (xSon && ySon) return Number(x) - Number(y);
  if (xSon) return -1;
  if (ySon) return 1;
  return x < y ? -1 : x > y ? 1 : 0;
}

function versiyaSolishtir(a, b) {
  const [aAsos, aOld] = a.split("-");
  const [bAsos, bOld] = b.split("-");
  const aS = aAsos.split(".").map(Number);
  const bS = bAsos.split(".").map(Number);
  for (let i = 0; i < 3; i++) {
    if (aS[i] !== bS[i]) return aS[i] - bS[i];
  }
  if (!aOld && !bOld) return 0;
  if (!aOld) return 1;
  if (!bOld) return -1;
  const aQ = aOld.split(".");
  const bQ = bOld.split(".");
  for (let i = 0; i < Math.min(aQ.length, bQ.length); i++) {
    const f = qismSolishtir(aQ[i], bQ[i]);
    if (f !== 0) return f;
  }
  return aQ.length - bQ.length;
}

const versiyalar = [
  "1.0.0", "1.0.0-rc.1", "1.0.0-beta.11", "1.0.0-beta.2",
  "1.0.0-beta", "1.0.0-alpha.1", "1.0.0-alpha", "0.9.9",
];
console.log(versiyalar.sort(versiyaSolishtir));

Konsolda:

text
[
  '0.9.9',
  '1.0.0-alpha',
  '1.0.0-alpha.1',
  '1.0.0-beta',
  '1.0.0-beta.2',
  '1.0.0-beta.11',
  '1.0.0-rc.1',
  '1.0.0'
]

Bu tartib semver.org'dagi namunaga mos. Ikki nozik joy: beta.2 < beta.11 (son sifatida, matn bo'lsa "11" < "2" bo'lardi) va alpha < alpha.1 (qismi kami kichik). Haqiqiy loyihada bu ishni semver npm paketi qiladi — u bilan Semver darsida tanishgansiz.

3-mashq (qiyin): o'z CHANGELOG generatoringiz

changelogYasa(versiya, sana, xabarlar) funksiyasini yozing. U conventional-changelog'ning valyuta repo'si uchun bergan natijasini («conventional-changelog — eng sodda» bo'limi) SHA'larsiz va havolalarsiz qaytarsin:

  • sarlavha: ## 2.0.0 (2026-11-04);
  • ### ⚠ BREAKING CHANGES — tanasida BREAKING CHANGE: bo'lgan commitlar shu matn bilan; ! bor, lekin tana yo'q bo'lsa — tavsifi bilan;
  • ### Features (feat) va ### Bug Fixes (fix); boshqa turlar kirmaydi;
  • soha bo'lsa — * **soha:** tavsif, bo'lmasa — * tavsif;
  • bo'lim ichida tartib: avval soha (yo'q bo'lsa tavsif), keyin tavsif bo'yicha;
  • bo'sh bo'lim chiqmaydi, bo'limlar orasida bitta bo'sh qator.

Ishora: «Keyingi versiyani hisoblaymiz» bo'limidagi SARLAVHA regex'i; qatorlarni massivga yig'ib, oxirida join("\n").

Yechim
js
const SARLAVHA = /^(\w+)(?:\(([^)]*)\))?(!)?: (.+)$/;
const BOLIMLAR = [["feat", "Features"], ["fix", "Bug Fixes"]];

const qator = (c) =>
  c.soha ? `* **${c.soha}:** ${c.tavsif}` : `* ${c.tavsif}`;
const kalit = (c) => [c.soha ?? c.tavsif, c.tavsif];

function tartibla(royxat) {
  return royxat.sort((a, b) => {
    const [a1, a2] = kalit(a);
    const [b1, b2] = kalit(b);
    if (a1 !== b1) return a1 < b1 ? -1 : 1;
    return a2 < b2 ? -1 : a2 > b2 ? 1 : 0;
  });
}

function changelogYasa(versiya, sana, xabarlar) {
  const commitlar = [];
  const buzuvchi = [];
  for (const x of xabarlar) {
    const [sarlavha, ...tana] = x.split("\n");
    const m = sarlavha.match(SARLAVHA);
    if (!m) continue;
    const [, tur, soha, undov, tavsif] = m;
    const izoh = tana.find((q) => q.startsWith("BREAKING CHANGE: "));
    if (izoh) {
      buzuvchi.push({ soha, tavsif: izoh.slice(17) });
    } else if (undov) {
      buzuvchi.push({ soha, tavsif });
    }
    commitlar.push({ tur, soha, tavsif });
  }
  const chiqish = [`## ${versiya} (${sana})`];
  if (buzuvchi.length) {
    chiqish.push("", "### ⚠ BREAKING CHANGES", "");
    chiqish.push(...tartibla(buzuvchi).map(qator));
  }
  for (const [tur, nom] of BOLIMLAR) {
    const ular = commitlar.filter((c) => c.tur === tur);
    if (!ular.length) continue;
    chiqish.push("", `### ${nom}`, "");
    chiqish.push(...tartibla(ular).map(qator));
  }
  return chiqish.join("\n");
}

console.log(changelogYasa("2.0.0", "2026-11-04", [
  "feat(cli)!: --json chiqish formati sukut bo'yicha\n\n" +
    "BREAKING CHANGE: jadval endi --jadval bayrog'i bilan; " +
    "skriptlar --jadval qo'shsin.",
  "chore: kesh papkasi .gitignore ga",
  "docs: README misollari",
  "fix: yaxlitlash 2 xonagacha",
  "feat(cli): --baza bayrog'i",
]));

Konsolda:

text
## 2.0.0 (2026-11-04)

### ⚠ BREAKING CHANGES

* **cli:** jadval endi --jadval bayrog'i bilan; skriptlar --jadval qo'shsin.

### Features

* **cli:** --baza bayrog'i
* **cli:** --json chiqish formati sukut bo'yicha

### Bug Fixes

* yaxlitlash 2 xonagacha

Vosita chiqishi bilan solishtiring: SHA'lar va sarlavha havolasidan boshqa hamma narsa bir xil. Commitlar git log tartibida (yangisi tepada) berilgan, lekin bo'lim ichida tartib o'zgardi — biz ham vosita kabi soha va tavsif bo'yicha saraladik. Haqiqiy vosita bundan ko'p ish qiladi: tag'lar oralig'ini topadi, Refs #12 kabi footer'larni issue havolasiga aylantiradi, revert commitlarini hisobga oladi.

10. Real ishda

  • Qayerda uchraydi: har npm paketi va ochiq loyihada — CHANGELOG.md va GitHub Releases. Ko'p katta kutubxonalar o'z relizlarini shu vositalar bilan chiqaradi; qaysi biri ishlatilganini repo'dagi config fayldan bilasiz: .releaserc, release-please-config.json, .changeset/.
  • Ilova va kutubxona farqi: kutubxonada semver — foydalanuvchilar bilan shartnoma. API ilovada esa mijozlar ^ bilan bog'lanmaydi, lekin CHANGELOG va versiya baribir kerak: xato haqida xabar "qaysi versiyada?" deb boshlanadi.
  • Intervyu savollari: "Semver nima, major qachon oshadi?", "CHANGELOG'ni qanday yuritasiz?", "semantic-release va changesets farqi?", "^1.2.3 qaysi versiyalarni oladi?"

Xulosa

  • Versiya: fix → patch, feat → minor, ! yoki BREAKING CHANGE → major; docs, chore — reliz yo'q.
  • 0.x — barqaror emas; pre-release (-beta.1) asosiy versiyadan oldin turadi va ^ uni sukut bo'yicha olmaydi.
  • CHANGELOG — odamlar uchun: eng yangisi tepada, turlarga bo'lingan, ichki ishlarsiz.
  • conventional-changelog — faqat CHANGELOG; semantic-release — CI'da hammasi; release-please — reliz PR'i; changesets — PR bilan o'zgarish fayli.
  • Bir narsa ikki joyda yozilsa, biri eskiradi: versiyaning yagona manbai — package.json.
  • vazifalar-api 4.0.1 — uchala vosita bir xil javob berdi; tag git push --follow-tags bilan yuboriladi.

Keyingi dars: Jamoa workflow'lari: GitHub flow, trunk-based, git-flow — bitta o'zgarish uch xil branch strategiyasida qanday tarix qoldirishini solishtiramiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
CHANGELOG va reliz avtomatikasi: release-please, semantic-release, changesets — IlmHamroh