Mundarija (42)
- Bu darsda
- 1. Nega bu kerak?
- 2. Monorepo va polyrepo
- 3. Submodule — repo ichida repo
- 3.1 Ulash
- 3.2 Git ichida nima saqlanadi
- 3.3 1-tuzoq: bo'sh papka
- 3.4 2-tuzoq: yangilanish o'zi kelmaydi
- 3.5 3-tuzoq: hamkasbning eskirgan papkasi
- 4. Subtree — fayllar o'zingizda
- 4.1 Qo'shish
- 4.2 Yangilash va klon
- 4.3 Narxi
- 4.4 Qaysi birini tanlash
- 5. CODEOWNERS monorepo'da
- 5.1 Qisqa takror
- 5.2 Faqat bitta CODEOWNERS
- 5.3 PR #12 da nima bo'ldi
- 5.4 Natijani halol o'qish: 404
- 5.5 GitHub 500 va 502
- 6. Path filtrlar — CI faqat kerak bo'lganda
- 6.1 paths va paths-ignore
- 6.2 Git'ning o'zida: nima o'zgardi
- 6.3 Tuzoq: majburiy tekshiruv "Pending"
- 7. Loyiha qadami: CODEOWNERS va CI path filtri
- 7.1 CODEOWNERS
- 7.2 CI: path filtr va @v7
- 7.3 Tekshiruv va commit
- 8. Ko'p uchraydigan xatolar
- 8.1 Klondan keyin "Cannot find module './umumiy/...'"
- 8.2 Hamkasbning yangilanishi "yo'qoldi"
- 8.3 fatal: transport 'file' not allowed
- 8.4 CODEOWNERS ishlamayapti
- 8.5 Hujjat PR'i merge bo'lmayapti
- 8.6 paths va paths-ignore birga
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): CODEOWNERS "oxirgi moslik"
- 3-mashq (qiyin): qaysi PR'da CI ishlaydi
- 10. Real ishda
- Xulosa
- Manbalar
Git va monorepo: submodule, subtree, CODEOWNERS va path filtrlar
Qisqacha: Bir nechta loyihani saqlashning ikki yo'li bor: har biri alohida repo (polyrepo) yoki hammasi bitta repo'da (monorepo). Umumiy kodni boshqa repo'dan ulash uchun Git ikki vosita beradi. Submodule — repo ichida boshqa repo'ga havola: Git fayllarni emas, faqat commit hash'ini saqlaydi, shuning uchun oddiy
git clonedan keyin papka bo'sh qoladi. Subtree — boshqa repo'ning fayllari to'g'ridan ilova tarixiga ko'chiriladi, klon oddiy. Monorepo'da ikki narsa muhim bo'ladi: kim qaysi papkaga javobgar (CODEOWNERS) va CI faqat o'zgargan qism uchun ishlashi (path filtrlar). Bugunvazifalar-apiga ikkalasini qo'shamiz.
Bu darsda
- Monorepo va polyrepo farqini va har birining narxini bilasiz.
git submodulebilan kutubxonani ulaysiz va uning uch tuzog'ini o'z ko'zingiz bilan ko'rasiz.git subtreebilan xuddi shu ishni qilib, submodule bilan solishtirasiz.- Monorepo'da
CODEOWNERSqanday ishlashini va GitHub uni qayerdan o'qishini bilasiz. - GitHub Actions'da
paths/paths-ignorebilan CI'ni faqat kerakli o'zgarishga cheklaysiz.
Oldin bilishingiz kerak: Workspaces va monorepo asoslari, GitHub'da code review amaliyoti, Branch himoyasi va rulesets, GitHub Actions: birinchi CI workflow, Katta repolar.
1. Nega bu kerak?
Workspaces va monorepo asoslari darsida npm workspaces bilan bitta repo'da bir nechta paketni boshqardik. U yerda savol paket darajasida edi: bog'liqliklar, node_modules, buyruqlar. Bugungi savol — Git darajasida:
- Ikki repo bitta umumiy kodni ishlatadi. Uni qanday ulash kerak — nusxa ko'chirmasdan?
- Bitta repo'da backend, sayt va hujjatlar. Backend PR'iga kim review beradi?
- Hujjatdagi bitta harf tuzatilsa, 20 daqiqalik CI nega ishga tushishi kerak?
Bizda jonli misol bor. Kursning sinov reposi (github.com/Sarvarbek0704/ilmhamroh-git-sinov) — monorepo: sayt/ papkasida 07-qismdagi portfolio, vazifalar-api/ papkasida 25–32-qism backend'i. Repo 07-qismdan qolgan: yangi repo yaratilmadi, 07-qism tarixi saqlanib, vazifalar-api unga papka bo'lib qo'shildi.
Hayotiy o'xshatish: oilada uch farzand. Polyrepo — har biri alohida kvartirada: mustaqil, lekin bayramda hamma bir joyga yig'ilishi qiyin. Monorepo — katta hovli: hamma bir joyda, umumiy oshxona, lekin "kim idish yuvadi" degan qoida (CODEOWNERS) va "kim nima qilganini" ko'rib turish (path filtrlar) kerak.
2. Monorepo va polyrepo
| Polyrepo | Monorepo | |
|---|---|---|
| Tuzilish | har loyiha — alohida repo | hammasi bitta repo'da, papkalarda |
| Umumiy kodni o'zgartirish | kutubxona relizi → har repo'da yangilash | bitta PR hamma joyni o'zgartiradi |
| Huquqlar | repo darajasida | CODEOWNERS va papka qoidalari |
| CI | har repo o'zinikini | path filtr yoki "o'zgargan paketlar" |
| Klon hajmi | kichik | katta (Katta repolar usullari) |
| Misollar | ko'p ochiq kutubxonalar | Google, Meta; Next.js, Babel, React repo'lari |
Monorepo'ning eng katta foydasi — atomar o'zgarish: API va uni ishlatadigan sayt bitta commitda o'zgaradi. Polyrepo'da bu ikki PR, ikki reliz va "qaysi versiya qaysi bilan mos" degan savol. Monorepo'ning narxi — vositalar: CI, review, klon hajmi uchun maxsus sozlash kerak.
Polyrepo'da umumiy kod odatda paket sifatida ulashiladi (npm'ga chiqariladi). Lekin ba'zan kodni paket qilish ortiqcha: konfiguratsiya fayllari, umumiy shablonlar, hali barqaror bo'lmagan kutubxona. Shunda Git'ning o'z vositalari ishlatiladi.
3. Submodule — repo ichida repo
3.1 Ulash
«Bahor» jamoasida kutubxona repo'si bor — sana yordamchilari. bot repo'si uni umumiy/ papkasi sifatida ulaydi. Biz hammasini lokal repo'larda sinadik:
git submodule add ../kutubxona umumiy
git commit -m "chore: umumiy kutubxona submodule sifatida"
cat .gitmodulesTerminalda:
[submodule "umumiy"]
path = umumiy
url = ../kutubxona.gitmodules — oddiy matn fayli: qaysi papka, qaysi repo'dan. U commit qilinadi va hamma klonlarga boradi. Haqiqiy loyihada url — GitHub manzili.
Diqqat: Lokal yo'l yoki
file://bilan submodule qo'shish sukut bo'yicha taqiqlangan — 2022-yil oktabrdagi xavfsizlik tuzatishidan beri (Git 2.38.1). Biz sinab ko'rdik:fatal: transport 'file' not allowed. Bizning sinovdaprotocol.file.allow=alwayssozlamasini faqat vaqtinchalik sozlama faylida yoqdik. GitHub manzili bilan bu kerak emas.
3.2 Git ichida nima saqlanadi
git ls-tree HEAD umumiy
git submodule status160000 commit 03c875f8397acc242fa8427a17ba7ffdbe870653 umumiy
03c875f8397acc242fa8427a17ba7ffdbe870653 umumiy (v1.0.0)Git ichkaridan darsida daraxtdagi yozuvlar 100644 blob (fayl) yoki 040000 tree (papka) edi. Bu yerda yangi tur: 160000 commit — gitlink. bot repo'si umumiy/ ichidagi fayllarni umuman saqlamaydi. U faqat bitta narsani biladi: "kutubxona ning 03c875f commiti". Submodule — fayllar emas, aniq commitga ko'rsatkich.
flowchart LR
subgraph bot["bot repo'si"]
T["daraxt"] --> R["README.md<br/>100644 blob"]
T --> G[".gitmodules<br/>100644 blob"]
T --> U["umumiy<br/>160000 commit 03c875f"]
end
subgraph kut["kutubxona repo'si"]
C1["03c875f<br/>v1.0.0"] --> C2["c8b5093<br/>feat: vaqt"]
end
U -. "aniq commit,<br/>branch emas" .-> C1Bu dizayn ham kuch, ham manba muammolar. Kuch: bot doim kutubxonaning sinalgan versiyasi bilan ishlaydi, kutubxonadagi yangi xato uni o'zi buzmaydi. Muammo — keyingi uch tuzoq.
3.3 1-tuzoq: bo'sh papka
Hamkasb bot ni odatdagidek klonladi:
git clone bot klon-oddiy
ls -A klon-oddiy/umumiy | wc -l
git -C klon-oddiy submodule statusoddiy klon: umumiy/ da 0 fayl
-03c875f8397acc242fa8427a17ba7ffdbe870653 umumiyPapka bor, lekin bo'sh. - belgisi — "submodule ishga tushirilmagan". Ilova import { sana } from "./umumiy/sana.js" da yiqiladi va xato xabari submodule haqida hech narsa demaydi. Ikki davo:
git submodule update --init
git clone --recurse-submodules bot klon-rekursivIkkalasidan keyin umumiy/ da sana.js paydo bo'ldi. Birinchisi — mavjud klonni tuzatadi, ikkinchisi — klon paytidayoq hammasini oladi. CI'da ham buni unutmang: actions/checkout da submodules: true sozlamasi bor (docs, sinalmagan, 2026-11).
3.4 2-tuzoq: yangilanish o'zi kelmaydi
Kutubxonaga yangi funksiya qo'shildi (feat: vaqt yordamchisi). bot da:
git submodule status 03c875f8397acc242fa8427a17ba7ffdbe870653 umumiy (v1.0.0)Hech narsa o'zgarmadi — bot hali ham 03c875f ga qarab turibdi. Bu ataylab: gitlink aniq commit. Yangilash — qo'lda va alohida commit bilan:
git submodule update --remote umumiy
git submodule status
git status --short
git diff --submodule=logBiz ishga tushirdik (From qatoridagi yo'l qisqartirildi):
From …/kutubxona
03c875f..c8b5093 main -> origin/main
+c8b50930e2fdff7b0e3efb7729bbd242ff866ff7 umumiy (v1.0.0-1-gc8b5093)
M umumiy
Submodule umumiy 03c875f..c8b5093:
> feat: vaqt yordamchisi--remote — "kutubxonaning main idagi oxirgi commitini ol". + belgisi — submodule'da checkout qilingan commit bot yozib qo'ygan commitdan farq qiladi. git status uni oddiy o'zgarish (M umumiy) deb ko'rsatadi. --submodule=log — diff o'rniga kutubxonaga qaysi commitlar qo'shilganini chiqaradi. Endi git commit -am "chore: umumiy kutubxonani yangilash" — va gitlink c8b5093 ga ko'chdi.
3.5 3-tuzoq: hamkasbning eskirgan papkasi
Hamkasb (klon-rekursiv) bu commitni git pull qildi:
git pull
git submodule status
git status --short+03c875f8397acc242fa8427a17ba7ffdbe870653 umumiy (v1.0.0)
M umumiypull gitlink'ni yangiladi, lekin umumiy/ papkasining o'zi eski commitda qoldi. Endi u M umumiy ni ko'radi — go'yo o'zi nimanidir o'zgartirgan. Agar u o'ylamay git commit -a qilsa, gitlink'ni orqaga — 03c875f ga — qaytaradi va hamkasbining yangilanishini bekor qiladi. Bu submodule'li jamoalarda eng ko'p uchraydigan xato.
git submodule update
git submodule status c8b50930e2fdff7b0e3efb7729bbd242ff866ff7 umumiy (v1.0.0-1-gc8b5093)Endi bo'sh joy — mos. Har pull dan keyin git submodule update ni unutmaslik uchun git config submodule.recurse true sozlamasi bor: shunda pull, switch va boshqa buyruqlar submodule'larni ham yangilaydi (Git hujjati, sinalmagan, 2026-11).
Tekshirib ko'ring:
git submodule statuschiqishidagi uch belgi —-,+va bo'sh joy — nimani bildiradi?
Javob
- — submodule ishga tushirilmagan (papka bo'sh, update --init kerak). + — papkadagi commit loyiha yozib qo'ygan gitlink'dan farq qiladi (yo siz yangiladingiz va hali commit qilmadingiz, yo hamkasb yangilagan va siz submodule update qilmadingiz). Bo'sh joy — hammasi mos.
4. Subtree — fayllar o'zingizda
4.1 Qo'shish
git subtree boshqacha ishlaydi: kutubxonaning fayllarini ilova tarixiga ko'chiradi. Xuddi shu kutubxona ni sayt repo'siga qo'shamiz:
git subtree add --prefix=umumiy ../kutubxona v1.0.0 --squash
git log --oneline --graph
git ls-tree HEAD umumiy/Terminalda:
* ed54947 (HEAD -> main) Merge commit 'f03aa2f4d2727bb7f8bb757cf6cc4627cf39f253' as 'umumiy'
|\
| * f03aa2f Squashed 'umumiy/' content from commit 03c875f
* d6b0dfe feat: sayt
100644 blob 304e2f8c21fcd4ce53e480db14c9c3e6b848f943 umumiy/sana.js--squash — kutubxonaning butun tarixi emas, bitta "siqilgan" commit. Keyin u merge commit bilan ilovaga qo'shiladi. Eng muhimi oxirgi qatorda: umumiy/sana.js — oddiy 100644 blob, ya'ni oddiy fayl. Hech qanday gitlink yo'q.
4.2 Yangilash va klon
git subtree pull --prefix=umumiy ../kutubxona main --squash \
-m "chore: umumiy kutubxonani yangilash (subtree)"Merge made by the 'ort' strategy.
umumiy/sana.js | 1 +
1 file changed, 1 insertion(+)Yangilanish — oddiy merge. Endi hamkasb oddiy git clone sayt qiladi va umumiy/sana.js darhol joyida — hech qanday --recurse-submodules, update --init kerak emas. Kanon tajribasida (IlmHamroh, 2026-11) xuddi shunday ikki ilovaning .git hajmi solishtirildi: submodule'li — 166 KB, subtree'li — 87 KB. Submodule ichki repo'ni .git/modules/ da to'liq saqlaydi.
4.3 Narxi
Subtree'ning narxi — qaytarish qiyin. Siz umumiy/ dagi faylni ilova ichida tuzatdingiz. Endi bu tuzatishni kutubxonaga qaytarish kerak: git subtree push yoki split bilan (Git hujjati, sinalmagan, 2026-11). Bu sekin va xatoga moyil, chunki Git butun ilova tarixidan umumiy/ ga tegishli commitlarni ajratadi. Shuning uchun subtree odatda "faqat o'qish" uchun yaxshi: kutubxonani olasiz, lekin unda o'zgartirmaysiz.
4.4 Qaysi birini tanlash
| Submodule | Subtree | Monorepo papkasi | |
|---|---|---|---|
| Git'da nima | gitlink (commit hash) | oddiy fayllar | oddiy fayllar |
git clone |
papka bo'sh | to'liq | to'liq |
| Kutubxona tarixi | alohida repo'da | siqilgan yoki to'liq | umumiy tarix |
| Yangilash | update --remote + commit |
subtree pull (merge) |
oddiy commit |
| O'zgarishni qaytarish | submodule ichida commit, push | subtree push — qiyin |
shart emas |
| Mos | aniq versiyaga bog'lanish, katta tashqi repo | kam o'zgaradigan begona kod | bitta jamoa kodi |
Kanon xulosasi: bitta jamoaning kodi uchun — monorepo papkasi (sinov reposida sayt/ va vazifalar-api/ shunday). Boshqa jamoaning kodi aniq versiyada kerak bo'lsa — submodule, uch tuzog'ini bilgan holda. Begona, kam o'zgaradigan kod — subtree yoki oddiy npm paket.
5. CODEOWNERS monorepo'da
5.1 Qisqa takror
GitHub'da code review amaliyoti darsida CODEOWNERS faylini yozgan edik: har qatorda shablon va egalar, oxirgi mos qator ustun, fayl .github/, ildiz yoki docs/ da turadi. Ega yozish huquqiga ega bo'lishi kerak, fayl PR'ning base branch'ida bo'lishi kerak. Bugun yangi savol: monorepo'da u qanday tuziladi.
5.2 Faqat bitta CODEOWNERS
GitHub repo'ning ildizidagi joylardan bittasini o'qiydi: .github/CODEOWNERS, CODEOWNERS yoki docs/CODEOWNERS. Hujjatga ko'ra, u shu tartibda qidiradi va birinchi topgan faylini ishlatadi (2026-11 holatiga). Boshqa joy hujjatda sanalmagan: vazifalar-api/.github/CODEOWNERS — ichki papkadagi fayl — monorepo'da o'qilmaydi.
vazifalar-api alohida repo bo'lganida uning .github/CODEOWNERS i ildizda turadi va ishlaydi. Sinov reposida esa u vazifalar-api/ ichiga tushdi. Shuning uchun kanon ikki fayl yozdi: loyihaning o'z fayli (alohida repo uchun) va sinov reposining ildizidagi fayl, /vazifalar-api/... yo'llari bilan. Ildiz fayli (real login @login ga almashtirildi):
* @login
/sayt/ @login
/vazifalar-api/src/auth/ @login
/vazifalar-api/src/ai/ @login
/vazifalar-api/prisma/migrations/ @login
/.github/ @loginHar qator — bitta "mas'uliyat hududi". Kanonda egasi bitta (bitta odamli sinov repo). Jamoada har qator boshqa odam yoki jamoa bo'ladi: @tashkilot/xavfsizlik — auth/ ga, @tashkilot/ai — ai/ ga.
5.3 PR #12 da nima bo'ldi
Kanon CODEOWNERS'ni PR #12 bilan qo'shdi (kursning sinov reposi, PR #12 ni ko'ring). PR fayllari va review so'rovlari — gh bilan o'qildi:
gh pr view 12 -R login/vazifalar-api \
--json reviewRequests,files \
--jq '{so_rovlar: .reviewRequests, fayllar: [.files[].path]}'Kanon chiqishi (repo nomi login/vazifalar-api ga almashtirildi):
{"fayllar":[".github/CODEOWNERS","vazifalar-api/.github/CODEOWNERS","vazifalar-api/.github/workflows/ci.yml"],"so_rovlar":[]}Review so'rovlari bo'sh. Ikki sabab: birinchisi — CODEOWNERS shu PR'ning o'zida qo'shilgan, u hali base branch'da yo'q. Ikkinchisi — muallif o'zi ega, GitHub muallifni o'z PR'iga reviewer qilib chaqirmaydi. Bu GitHub'da code review amaliyoti darsida ko'rgan xulq.
Kuchi esa ruleset bilan ochiladi: Branch himoyasi darsidagi Require review from Code Owners yoqilsa, src/auth/ ga tegadigan PR xavfsizlik jamoasi tasdiqlamaguncha merge bo'lmaydi. Kanon ruleset'ida tasdiqlar soni 0 — bu bitta odamli sinov repo cheklovi: o'z PR'ingizni tasdiqlay olmaysiz (kanon buni "Can not approve your own pull request" xatosi bilan ko'rdi). Jamoada — kamida 1 tasdiq va CODEOWNERS review.
5.4 Natijani halol o'qish: 404
Kanon CODEOWNERS sintaksisini GitHub'ning maxsus endpoint'i bilan tekshirmoqchi bo'ldi. PR branch'ida, merge'dan keyin main da va bir daqiqadan keyin qayta — har safar:
{
"message": "Not Found",
"documentation_url": "https://docs.github.com/rest",
"status": "404"
}gh: Not Found (HTTP 404)Kanon yozuvida "sabab aniqlanmadi" deb qoldi. Biz yozuvdagi buyruqning o'zini o'qidik:
gh api 'repos/$R/codeowners/errors'Bittalik tirnoq ichida $R o'zgaruvchisi ochilmaydi — bash uni so'zma-so'z qoldiradi. So'rov repos/$R/codeowners/errors manziliga ketdi, bunday repo yo'q — 404. Xuddi shu endpoint to'g'ri manzil bilan (faqat o'qish, 2026-11-04; repo nomi login/vazifalar-api ga almashtirildi):
gh api repos/login/vazifalar-api/codeowners/errors{"errors":[]}Xatolar ro'yxati bo'sh — sintaksis GitHub tomonidan tasdiqlandi. 07-qismda xuddi shu endpoint "Unknown owner" xatosini qaytargan edi, ya'ni u ishlaydi. Saboq ikki xil. Birinchisi — bash: o'zgaruvchi bo'lsa — qo'shtirnoq ("repos/$R/..."). Ikkinchisi — kattaroq: 404 "endpoint ishlamaydi" degani emas, ko'pincha "manzil noto'g'ri" degani. Natija kutilmagan bo'lsa, avval o'z buyrug'ingizni tekshiring.
5.5 GitHub 500 va 502
Shu PR paytida GitHub uch marta xato qaytardi. Birinchi push:
! [remote rejected] chore/codeowners -> chore/codeowners (Internal Server Error)Qayta push — o'tdi. gh pr create — HTTP 502: 502 Bad Gateway, qayta urinish — PR #12 ochildi. gh pr merge esa 502 qaytardi, lekin keyingi tekshiruv:
#12 MERGED 8ec1500PR merge bo'lgan edi. Server javob berishga ulgurmadi, lekin amal bajarildi. Qoida: 5xx xatodan keyin ko'r-ko'rona qayta urinmang — avval holatni tekshiring (gh pr view). Ikki marta merge xavfli emas (GitHub rad etadi), lekin ikki marta yaratish yoki ikki marta yuborish — ehtimol. Bu Idempotentlik g'oyasining o'zi, faqat mijoz tomonidan.
6. Path filtrlar — CI faqat kerak bo'lganda
6.1 paths va paths-ignore
GitHub Actions'da push va pull_request hodisalariga fayl yo'li bo'yicha filtr qo'yiladi. GitHub hujjatidan (2026-11):
paths— "kamida bitta yo'l naqshga mos kelsa, workflow ishlaydi".paths-ignore— "hamma yo'l naqshlarga mos kelsa, workflow ishlamaydi".- Bitta hodisada ikkalasini birga ishlatib bo'lmaydi. Istisno kerak bo'lsa —
pathsichida!bilan:!naqshi undan oldingi mosni bekor qiladi, tartib muhim.
Monorepo uchun tipik:
on:
pull_request:
paths:
- "vazifalar-api/**"
- "!**/*.md""vazifalar-api/ ga tegsa — ishla, lekin faqat Markdown o'zgargan bo'lsa — yo'q."
6.2 Git'ning o'zida: nima o'zgardi
GitHub filtri ichkarida aslida shu savolga javob beradi: PR'ning base'dan keyin qaysi fayllari o'zgardi. Lokal'da buni git diff bilan ko'ramiz. Monorepo'da feat/port branch'ida API porti va hujjat o'zgardi:
git diff --name-only main...HEAD
git diff --name-only main...HEAD | cut -d/ -f1 | sort -uTerminalda:
docs/README.md
vazifalar-api/src/main.ts
docs
vazifalar-apiUch nuqta (main...HEAD) — Ajralgan tarix darsidagi kabi "umumiy ajdoddan beri shu branch o'zgartirgan narsa". main keyinroq oldinga ketgan bo'lsa ham, boshqalarning o'zgarishi aralashmaydi. Turborepo va Nx kabi vositalar "o'zgargan paketlar" (affected) ni aynan shunday hisoblaydi, keyin faqat ularning testini ishga tushiradi (Workspaces va monorepo asoslari).
6.3 Tuzoq: majburiy tekshiruv "Pending"
Hujjatdagi muhim ogohlantirish: workflow path filtr tufayli o'tkazib yuborilsa, unga bog'liq tekshiruvlar "Pending" holatda qoladi. Ruleset'da bu tekshiruv majburiy (required status check) bo'lsa — PR merge bo'lmaydi. Hujjat PR'ini ochdingiz, CI ishlamadi, va PR abadiy kutadi.
Yechimlar (sinalmagan, 2026-11): majburiy tekshiruvni path filtrsiz, lekin ichida "o'zgarish bormi" deb tekshiradigan job'ga qo'yish; yoki filtrni workflow emas, job darajasida qilish. Kanon sinov reposida Actions o'chirilgan va required status checks sinalmagan — buni o'zingiz birinchi bo'lib ko'rasiz.
Yana bir chegara (GitHub hujjati, 2026-11): diff 3 000 fayldan katta bo'lsa va mos fayllar birinchi 3 000 tasiga tushmasa, workflow ishlamaydi.
7. Loyiha qadami: CODEOWNERS va CI path filtri
7.1 CODEOWNERS
vazifalar-api/.github/CODEOWNERS (alohida repo uchun; real login @login ga almashtirildi):
# CODEOWNERS: qaysi papkaga kim javobgar. PR shu fayllarga
# tegsa, egasi avtomatik review'ga chaqiriladi.
# Oxirgi mos qator ustun. Kurs reposida egasi bitta — jamoada
# har qator boshqa odam yoki @tashkilot/jamoa bo'ladi.
* @login
# xavfsizlik va ma'lumotlar — eng ehtiyotkor review
/src/auth/ @login
/prisma/migrations/ @login
# AI qatlami (jamoada: @tashkilot/ai)
/src/ai/ @login
# CI va repo sozlamalari — butun jamoaga ta'sir qiladi
/.github/ @loginIzohlar telefon uchun qisqartirildi, qoidalar — kanondagidek. Nega aynan shu papkalar? auth/ — xato bo'lsa, butun tizim ochiladi. prisma/migrations/ — migratsiyani qaytarish qiyin, prodakshn ma'lumotiga tegadi. ai/ — kvota va pul. .github/ — CI o'zgarishi hammaga ta'sir qiladi. Qolgan hamma narsa — *.
7.2 CI: path filtr va @v7
.github/workflows/ci.yml da ikki o'zgarish:
on:
workflow_dispatch:
+ # PR'da yoqilsa — faqat KODga tegishli o'zgarishda ishlasin.
+ # Monorepo'da: paths: ["vazifalar-api/**"]
+ # pull_request:
+ # paths-ignore: ["**/*.md", "docs/**",
+ # ".github/CODEOWNERS", ".github/ISSUE_TEMPLATE/**"]
@@
- - uses: actions/checkout@v5
- - uses: actions/setup-node@v5
+ - uses: actions/checkout@v7
+ - uses: actions/setup-node@v7Birinchisi — path filtr, lekin izohda. Kurs qarori: CI faqat qo'lda (workflow_dispatch), tekshiruv lokal npm run check bilan — Actions daqiqalari pulli. O'quvchi pull_request ni yoqsa, filtr tayyor: hujjat, CODEOWNERS va issue shablonlari o'zgargan PR'da 20 daqiqalik test ishga tushmaydi. Izohdagi qator telefon uchun bo'lindi; faylda u bitta qator.
Ikkinchisi — actions versiyalari. actions/checkout ning oxirgi reliz liniyasi — v7 (v7.0.1), actions/setup-node niki ham v7 (v7.1.0) — ikkalasining releases sahifasi, 2026-11-04.
YAML'ni tekshirdik: yaml kutubxonasi bilan ikkala variant — izohli va pull_request yoqilgan — xatosiz o'qildi, yoqilgan holda on ichida paths-ignore ning 4 ta naqshi bor. action-validator bilan tekshiruv bu safar ishga tushirilmadi.
7.3 Tekshiruv va commit
Loyiha kodi o'zgarmadi, lekin har commitdan oldin to'liq tekshiruv — qoida:
npm run checkKanon natijasi: tip 0, lint 0, build 0; Vitest:
Test Files 69 passed | 1 skipped (70)
Tests 346 passed | 1 skipped (347)Bisect darsidagi 346 + 1 skip — o'zgarishsiz. Commit — yangi branch'da, yangilangan main dan. Jamoa workflow'lari darsida docs/hissa-qoshish branch'ini ochgan bo'lsangiz, hozir unda turgan bo'lishingiz mumkin — shuning uchun avval main ga qayting:
git switch main
git pull
git switch -c chore/codeowners
git add .github/CODEOWNERS .github/workflows/ci.yml
git commit -m "chore(repo): CODEOWNERS va CI path filtri; actions @v7"chore(repo) — kodga emas, repo infratuzilmasiga tegadi (Conventional Commits). PR va squash — Jamoa workflow'lari darsidagi tartibda.
Bitta izoh monorepo haqida. CHANGELOG va reliz darsida siz v4.0.1 tag'ini qo'ydingiz. Kursning sinov reposida esa tag vazifalar-api-v4.0.1 deb nomlangan: u monorepo, unda 07-qismdan v1.0.0 tag'i (sayt relizi) allaqachon bor. Monorepo'da tag'ga paket nomi qo'shiladi — aks holda ikki loyihaning v1.0.0 lari to'qnashadi. Reliz vositalari ham monorepo'da tag'ni paket nomi bilan yasaydi (masalan, changesets — nom@1.2.3 shaklida; sinalmagan, 2026-11).
8. Ko'p uchraydigan xatolar
8.1 Klondan keyin "Cannot find module './umumiy/...'"
Submodule papkasi bo'sh. Davosi: git submodule update --init (yoki keyingi safar git clone --recurse-submodules).
8.2 Hamkasbning yangilanishi "yo'qoldi"
Kimdir pull dan keyin submodule update qilmay commit -a qildi va gitlink orqaga qaytdi. Davosi: review'da umumiy gitlink o'zgarishiga e'tibor; git config submodule.recurse true.
8.3 fatal: transport 'file' not allowed
Lokal yo'l bilan submodule — Git 2.38.1 dan beri sukut bo'yicha taqiqlangan. Faqat sinovda va faqat o'z sozlamangizda: git -c protocol.file.allow=always submodule add ....
8.4 CODEOWNERS ishlamayapti
Tekshiring: fayl ildiz, .github/ yoki docs/ dami (monorepo ichki papkasida emas)? Base branch'da bormi? Egalarda yozish huquqi bormi? gh api "repos/<egasi>/<repo>/codeowners/errors" — qo'shtirnoq bilan.
8.5 Hujjat PR'i merge bo'lmayapti
Path filtr workflow'ni o'tkazib yubordi, majburiy tekshiruv "Pending" da qoldi. «Tuzoq: majburiy tekshiruv "Pending"» bo'limiga qarang.
8.6 paths va paths-ignore birga
GitHub bitta hodisada ikkalasini qabul qilmaydi. paths + ! naqshlari bilan yozing.
9. Mashqlar
Mashqlar kurs/mashqlar/32/07-monorepo/ da. 2-mashq yechimi Node 24.21.0 da, 3-mashq yechimi Git 2.54.0 da tekshirilgan (biz ishga tushirdik).
1-mashq (oson): bo'shliqlarni to'ldiring
- Submodule Git daraxtida qaysi tur bilan saqlanadi:
160000 - Klon paytida submodule'larni ham olish:
git clone git submodule statusda "ishga tushirilmagan" belgisi —- Kutubxona fayllarini ilova tarixiga ko'chiradigan buyruq —
git - "Hamma o'zgargan fayl mos kelsa — ishlama" degan filtr —
Yechim
- commit — gitlink, fayllar emas.
- --recurse-submodules — mavjud klonda
submodule update --init. - - —
+— farq bor, bo'sh joy — mos. - subtree —
add --prefix=... --squash. - paths-ignore —
pathsesa "bittasi mos kelsa — ishla".
2-mashq (o'rta): CODEOWNERS "oxirgi moslik"
egalar(codeowners, fayl) funksiyasini yozing: CODEOWNERS matni va fayl yo'li bo'yicha egalar ro'yxatini qaytarsin. Qoidalar: # va bo'sh qatorlar tashlanadi; * — hamma fayl; / bilan boshlangan naqsh — ildizdan; / bilan tugagan — papka va uning ichidagi hamma narsa; naqshdagi * — / dan boshqa istalgan belgilar. Oxirgi mos qator yutadi. Monorepo ildiz fayli bilan sinang — «Faqat bitta CODEOWNERS» bo'limidagi fayl, jamoa egalari qo'shilgan. Ishora: har naqshni RegExp ga o'giring; maxsus belgilarni (., + va h.k.) \ bilan himoyalang (RegExp asoslari va metodlari).
Yechim
function naqshdanRegex(naqsh) {
const ildizdan = naqsh.startsWith("/");
const papka = naqsh.endsWith("/");
let ichki = naqsh.replace(/^\//, "").replace(/\/$/, "");
ichki = ichki
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*\*/g, "\u0000")
.replace(/\*/g, "[^/]*")
.replace(/\u0000/g, ".*");
const bosh = ildizdan || ichki.includes("/") ? "^" : "(^|/)";
const oxir = papka ? "/" : "(/|$)";
return new RegExp(bosh + ichki + oxir);
}
function egalar(codeowners, fayl) {
let natija = [];
for (const qator of codeowners.split("\n")) {
const q = qator.trim();
if (q === "" || q.startsWith("#")) continue;
const [naqsh, ...kimlar] = q.split(/\s+/);
const mos = naqsh === "*" || naqshdanRegex(naqsh).test(fayl);
if (mos) natija = kimlar;
}
return natija;
}
const CODEOWNERS = `
* @login
/sayt/ @login
/vazifalar-api/src/auth/ @login @xavfsizlik
/vazifalar-api/src/ai/ @login @ai-jamoa
/vazifalar-api/prisma/migrations/ @login @dba
/.github/ @login @devops
`;
for (const f of [
"vazifalar-api/src/ai/chidamli.ts",
"vazifalar-api/prisma/migrations/0001/migration.sql",
".github/CODEOWNERS",
"vazifalar-api/.github/workflows/ci.yml",
"README.md",
]) {
console.log(f, "→", egalar(CODEOWNERS, f).join(" "));
}Konsolda:
vazifalar-api/src/ai/chidamli.ts → @login @ai-jamoa
vazifalar-api/prisma/migrations/0001/migration.sql → @login @dba
.github/CODEOWNERS → @login @devops
vazifalar-api/.github/workflows/ci.yml → @login
README.md → @loginTo'rtinchi qatorga qarang: vazifalar-api/.github/workflows/ci.yml — CI fayli, lekin DevOps jamoasi chaqirilmaydi. /.github/ ildizdan boshlanadi va faqat .github/ papkasini tutadi. Monorepo'da ichki loyihaning .github/ i uchun alohida qator kerak: /vazifalar-api/.github/ @login @devops. Kanon PR #12 ning uchinchi fayli aynan shu edi. ** ni vaqtincha \u0000 belgisiga almashtirish — keyingi * almashtirishi uni buzmasligi uchun. Haqiqiy GitHub qoidalari bundan murakkabroq (masalan, docs/* faqat bir daraja) — bu soddalashtirilgan model.
3-mashq (qiyin): qaysi PR'da CI ishlaydi
mono-yarat.sh monorepo yaratadi (sayt/, vazifalar-api/, docs/) va uchta branch: docs/izoh, feat/port, fix/sayt. Har branch uchun quyidagi filtr bo'yicha "api CI ishlaydimi" degan qarorni chiqaring:
paths:
- "vazifalar-api/**"
- "!**/*.md"Yana o'zgargan yuqori papkalarni ham chiqaring. Ishora: git diff --name-only main...<branch>; filtr tartibini bash case bilan takrorlang — case birinchi mos tarmoqda to'xtaydi, shuning uchun istisnoni (*.md) birinchi yozing.
Yechim
bash mono-yarat.sh mono && cd mono
# paths: ["vazifalar-api/**", "!**/*.md"] — tartib muhim
kerakmi() {
while read -r f; do
case "$f" in
*.md) ;;
vazifalar-api/*) return 0 ;;
esac
done
return 1
}
for b in docs/izoh feat/port fix/sayt; do
fayllar=$(git diff --name-only main..."$b")
if echo "$fayllar" | kerakmi; then qaror="ishlaydi"
else qaror="o'tkaziladi"; fi
papkalar=$(echo "$fayllar" | cut -d/ -f1 | sort -u | paste -sd' ')
echo "$b: api CI $qaror (o'zgargan: $papkalar)"
doneBiz ishga tushirdik:
docs/izoh: api CI o'tkaziladi (o'zgargan: docs vazifalar-api)
feat/port: api CI ishlaydi (o'zgargan: docs vazifalar-api)
fix/sayt: api CI o'tkaziladi (o'zgargan: sayt)docs/izoh vazifalar-api/README.md ga tegdi — lekin u Markdown, istisno ishladi. feat/port — .ts fayl, CI kerak. fix/sayt — boshqa loyiha. Agar case da tartibni almashtirsangiz (vazifalar-api/* birinchi), docs/izoh ham "ishlaydi" bo'ladi — GitHub'dagi ! naqshining tartibi xuddi shunday muhim. Va eslang: ruleset'da majburiy tekshiruv bo'lsa, docs/izoh PR'i "Pending" da qoladi.
10. Real ishda
- Qayerda uchraydi: katta frontend kompaniyalari va ochiq loyihalar (React, Next.js, Babel) — monorepo; ko'p mikroxizmatli tizimlar — ko'pincha polyrepo. Submodule — firmware, o'yin dvigatellari, tashqi C kutubxonalari; JavaScript'da kamroq, chunki npm paket qulayroq.
- Monorepo vositalari: Turborepo, Nx — o'zgargan paketlar, kesh; ular Git tarixiga tayanadi (Workspaces va monorepo asoslari).
- Intervyu savollari: "Monorepo va polyrepo — afzallik va kamchiliklar?", "Submodule'ning tuzoqlari?", "CODEOWNERS qanday ishlaydi, oxirgi moslik nima?", "Hujjat PR'ida CI'ni qanday o'tkazib yuborasiz va qanday tuzoq bor?"
Xulosa
- Polyrepo — har loyiha alohida; monorepo — bitta repo, atomar o'zgarish, lekin maxsus vositalar.
- Submodule — gitlink (
160000 commit): aniq commitga ko'rsatkich, fayllar emas. - Submodule tuzoqlari: oddiy klonda bo'sh papka, yangilanish o'zi kelmaydi,
pulldan keyin eskirgan papka (+). - Subtree — fayllar ilova tarixida, klon oddiy; o'zgarishni qaytarish qiyin.
- GitHub faqat ildiz,
.github/yokidocs/dagi bitta CODEOWNERS'ni o'qiydi; monorepo'da yo'llar ildizdan. paths— bittasi mos kelsa ishlaydi;paths-ignore— hammasi mos kelsa ishlamaydi; o'tkazilgan majburiy tekshiruv "Pending".- 404 va 502 — avval o'z buyrug'ingizni va haqiqiy holatni tekshiring.
Keyingi dars: Git konfiguratsiyasi va unumdorlik sozlamalari — 40 ming faylli repo'da git status ni o'lchaymiz: untrackedCache, fsmonitor, commit-graph, git maintenance va kundalik foydali sozlamalar.
Manbalar
- Git hujjati — git-submodule: https://git-scm.com/docs/git-submodule
- Pro Git kitobi — Git Tools: Submodules: https://git-scm.com/book/en/v2/Git-Tools-Submodules (sinalmagan, 2026-11)
- Git hujjati — git-subtree: https://git-scm.com/docs/git-subtree (sinalmagan, 2026-11)
- Git hujjati — git-config (
protocol.allow,submodule.recurse): https://git-scm.com/docs/git-config (sinalmagan, 2026-11) - GitHub Docs — About code owners: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners (sinalmagan, 2026-11)
- GitHub Docs — REST API: List CODEOWNERS errors: https://docs.github.com/en/rest/repos/repos#list-codeowners-errors (sinalmagan, 2026-11)
- GitHub Docs — Workflow syntax (
paths,paths-ignore): https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax - actions/checkout relizlari: https://github.com/actions/checkout/releases
- Kanon tajribasi (IlmHamroh, 2026-11-04): Git 2.54.0, gh 2.92.0, Windows 11. Submodule va subtree («Bahor» kutubxona, bot, sayt), monorepo diff,
codeowners/errorsqayta tekshiruvi va mashq yechimlari — biz ishga tushirdik (2026-11-04). PR #12, 500/502 va 404 chiqishlari, Vitest 346 + 1 skip,.git166/87 KB — kanon tajribasi.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!