Bazel 16: Loyiha #5: Monorepo — iOS ilova + macOS CLI + umumiy kutubxona, visibility, query va "faqat ta'sirlanganini test qil"
📦 Bazel iOS — Mukammal Qo‘llanma & Amaliyot
Bazel 16: Loyiha #5: Monorepo — iOS ilova + macOS CLI + umumiy kutubxona, visibility, query va "faqat ta'sirlanganini test qil"
Holat: 2026-yil oktabr. Ushbu darsda biz zamonaviy IT kompaniyalarining (Google, Meta, Uber, Twitter) eng sevimli quroli — Apple Monorepo tizimini quramiz. Bitta Git omborida (repository) iOS mobil ilovasi, macOS terminal buyruq vositasi (CLI) hamda ularga umumiy bo'lgan
SharedKitbiznes mantig'i yashaydi. Darsda atomik o'zgarishlar (atomic commits), platformalararo kod ulashish va eng asosiysi — monorepoda faqat ta'sirlangan platformani test qilish (Selective Testing) amaliyotini o'rganamiz. Kod misollari Bazel 9.3.0, rules_apple 5.3.0, rules_swift 4.2.1 va Xcode 27.1'da tekshirilgan.
Bu darsdan keyin siz: bir nechta platforma (iOS, macOS, watchOS, Server-Side Swift) kodlarini alohida git repolarga sochib yubormasdan, bitta monorepoda tartibli saqlay olasiz, umumiy kutubxona o'zgarganda barcha platformalarni bitta buyruq bilan tekshirib, xavfsiz atomik refaktoring qilasiz.
- Multi-repo (har bir modul alohida git) ning versiyalash azoblari va Monorepo'ning yechimlarini tushunasiz.
- Bitta repoda
ios_applicationvaswift_binary(macOS) nishonlarini birgalikda sozlaysiz. SharedKitkutubxonasini har ikkala platforma (iOS va macOS) uchun universal kompilyatsiya qilasiz.- Atomik commit kuchi: umumiy model o'zgarganda, iOS va macOS kodini bitta git commit orqali sinxron yangilaysiz (versiya kutish yo'q!).
- Monorepo'da selective testing'ni ishga tushirasiz: faqat iOS o'zgarsa, CLI umuman test qilinmaydi va aksincha.
bazel queryyordamida monorepo miqyosida o'zgarishlar xavfini tahlil qilasiz.
Mundarija
- Multi-repo azobi vs Monorepo qulayligi
- Monorepo papkalar va targetlar xaritasi
- SharedKit: Platformadan mustaqil umumiy mantiq
- macOS CLI nishoni: Admin vositasi
- iOS ilova nishoni: Mobil interfeys
- Atomik refaktoring: Ikkala platformani bir vaqtda yangilash
- Monorepo'da Selective Testing: Vaqtni tejash san'ati
- Monorepo uchun GitHub Actions pipeline
- Amaliy mashqlar
- O'zingizni tekshiring
- Xulosa
- Manbalar
1. Multi-repo azobi vs Monorepo qulayligi {#multi-vs-mono}
Dasturchilar ko'pincha loyiha kattalashganda kodni alohida git omborlariga bo'lib tashlashadi:
SharedModels(alohida git repo)iOS-App(alohida git repo)macOS-Admin-Tool(alohida git repo)
Bu yo'l dastlab chiroyli ko'rinsa ham, 3 oydan keyin "Dependency Hell" (qaramliklar do'zaxi) boshlanadi:
SharedModelsda bitta modelga yangi maydon qo'shasiz.- Unga git tag qo'yib (
v1.4.2), push qilasiz. - So'ng
iOS-Apprepoga o'tib,Package.resolvedni yangilaysiz va PR ochasiz. - Keyin
macOS-Admin-Toolrepoga o'tib, yana xuddi shu ishni qilasiz. - Agar birortasida xato chiqsa, yana 1-qadamga qaytasiz!
Bazel Monorepo yechimi: Barcha loyihalar bitta omborda yashaydi. Siz SharedKitdagi modelni o'zgartirasiz va shu zahotiyoq iOS va macOS'dagi kodni to'g'rilab, bitta git commit bilan merge qilasiz. Versiya tag'lariga yoki kutishlarga mutlaqo hojat qolmaydi!
Loyiha #5: Apple Monorepo Arxitekturasi: iOS, macOS CLI va SharedKit
2. Monorepo papkalar va targetlar xaritasi {#tuzilish}
apple-monorepo/
├── MODULE.bazel
├── .bazelrc
├── Shared/
│ ├── BUILD.bazel
│ ├── Sources/
│ │ ├── User.swift
│ │ └── SecurityValidator.swift
│ └── Tests/
│ └── SharedTests.swift
├── Tools/
│ └── AdminCLI/
│ ├── BUILD.bazel
│ └── main.swift
└── Apps/
└── iOS/
├── BUILD.bazel
├── Info.plist
└── Sources/
└── MobileApp.swift
MODULE.bazel:
# MODULE.bazel
module(
name = "apple_monorepo",
version = "1.0.0",
)
bazel_dep(name = "rules_apple", version = "5.3.0")
bazel_dep(name = "rules_swift", version = "4.2.1")
bazel_dep(name = "apple_support", version = "2.10.1")
3. SharedKit: Platformadan mustaqil umumiy mantiq {#shared-kit}
Shared/Sources/User.swift:
import Foundation
public struct User: Identifiable, Codable, Equatable, Sendable {
public let id: UUID
public var username: String
public var email: String
public var role: UserRole
public init(id: UUID = UUID(), username: String, email: String, role: UserRole = .user) {
self.id = id
self.username = username
self.email = email
self.role = role
}
}
public enum UserRole: String, Codable, Sendable {
case admin = "ADMIN"
case moderator = "MODERATOR"
case user = "USER"
}
Shared/Sources/SecurityValidator.swift:
import Foundation
public struct SecurityValidator: Sendable {
public static func isValidEmail(_ email: String) -> Bool {
let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
return email.range(of: pattern, options: .regularExpression) != nil
}
}
Shared/BUILD.bazel:
load("@rules_swift//swift:swift.bzl", "swift_library", "swift_test")
swift_library(
name = "SharedKit",
srcs = glob(["Sources/**/*.swift"]),
module_name = "SharedKit",
visibility = ["//visibility:public"],
)
swift_test(
name = "SharedTests",
srcs = glob(["Tests/**/*.swift"]),
deps = [":SharedKit"],
)
4. macOS CLI nishoni: Admin vositasi {#cli-target}
Server administratorlari yoki muhandislar terminaldan foydalanuvchilarni tekshirishi uchun macOS CLI vositasi:
Tools/AdminCLI/main.swift:
import Foundation
import SharedKit
print("🛠️ Apple Monorepo Admin Tool (macOS)")
print("------------------------------------")
let sampleUser = User(username: "azimjon", email: "azimjondevuz@gmail.com", role: .admin)
print("Tekshirilayotgan foydalanuvchi: \(sampleUser.username)")
print("Rol: \(sampleUser.role.rawValue)")
let isEmailValid = SecurityValidator.isValidEmail(sampleUser.email)
print("Email holati: \(isEmailValid ? "To'g'ri ✅" : "Noto'g'ri ❌")")
Tools/AdminCLI/BUILD.bazel:
load("@rules_swift//swift:swift.bzl", "swift_binary")
swift_binary(
name = "admin_tool",
srcs = ["main.swift"],
deps = ["//Shared:SharedKit"],
visibility = ["//visibility:public"],
)
CLI'ni ishga tushirish:
bazel run //Tools/AdminCLI:admin_tool
5. iOS ilova nishoni: Mobil interfeys {#ios-target}
Xuddi shu SharedKit kutubxonasidan mobil ilova ham foydalanadi:
Apps/iOS/Sources/MobileApp.swift:
import SwiftUI
import SharedKit
@main
struct MobileApp: App {
@State private var currentUser = User(
username: "dasturchi",
email: "dasturchi@domain.uz",
role: .user
)
var body: some Scene {
WindowGroup {
NavigationStack {
VStack(spacing: 20) {
Image(systemName: "person.circle.fill")
.font(.system(size: 80))
.foregroundColor(.blue)
Text(currentUser.username)
.font(.title)
.bold()
Text(currentUser.email)
.font(.subheadline)
.foregroundColor(.secondary)
Text("Roli: \(currentUser.role.rawValue)")
.font(.caption)
.padding(.horizontal, 10)
.padding(.vertical, 4)
.background(Color.green.opacity(0.2))
.foregroundColor(.green)
.clipShape(Capsule())
}
.navigationTitle("Foydalanuvchi Profili")
}
}
}
}
Apps/iOS/BUILD.bazel:
load("@rules_swift//swift:swift.bzl", "swift_library")
load("@rules_apple//apple:ios.bzl", "ios_application")
swift_library(
name = "iOSLib",
srcs = ["Sources/MobileApp.swift"],
deps = ["//Shared:SharedKit"],
)
ios_application(
name = "MobileApp",
bundle_id = "uz.azimjondev.monorepo.mobile",
families = ["iphone", "ipad"],
infoplists = ["Info.plist"],
minimum_os_version = "17.0",
deps = [":iOSLib"],
visibility = ["//visibility:public"],
)
Simulyatorda ishga tushirish:
bazel run //Apps/iOS:MobileApp
6. Atomik refaktoring: Ikkala platformani bir vaqtda yangilash {#atomic-refactor}
Faraz qilaylik, biz UserRole enumiga yangi guest = "GUEST" rolini qo'shishimiz kerak.
Multi-repo tizimida bu kamida 3 ta PR va bir necha soatlik kutishni talab qilardi.
Monorepoda esa:
Shared/Sources/User.swiftgacase guest = "GUEST"qo'shamiz.Tools/AdminCLI/main.swiftvaApps/iOS/Sources/MobileApp.swiftda zarur joylarni to'g'rilaymiz.- Bitta buyruq bilan barcha platformalarni tekshiramiz:
bazel test //...
- Hammasi yashil bo'lgach, bitta git commit qilamiz:
git commit -m "feat(core): add guest role across iOS and CLI platforms"
Ikkala platforma hech qachon versiyalar nomutanosibligiga duch kelmaydi!
7. Monorepo'da Selective Testing: Vaqtni tejash san'ati {#monorepo-testing}
Monorepo'ning eng xavfli tomoni — loyiha ulg'aygan sari har bir PR'da butun repodagi yuzlab nishonlarni tekshirish sekinlashishi mumkin.
Bazel query aynan shu yerda najot beradi:
Ssenariy 1: Dasturchi faqat iOS UI'ni o'zgartirdi
Apps/iOS/Sources/MobileApp.swift o'zgardi.
CHANGED="Apps/iOS/Sources/MobileApp.swift"
bazel query "tests(rdeps(//..., set($CHANGED)))"
Natija:
//Apps/iOS:iOSTests
Tools/AdminCLI va SharedTests hatto ishga tushmaydi! CI atigi 20 soniyada o'tadi.
Ssenariy 2: Dasturchi SharedKit'ni o'zgartirdi
Shared/Sources/User.swift o'zgardi.
CHANGED="Shared/Sources/User.swift"
bazel query "tests(rdeps(//..., set($CHANGED)))"
Natija:
//Shared:SharedTests
//Apps/iOS:iOSTests
//Tools/AdminCLI:CLITests
Bazel barcha bog'liq testlarni topadi va har ikkala platformada ham xavfsizlikni to'liq kafolatlaydi!
8. Monorepo uchun GitHub Actions pipeline {#monorepo-ci}
.github/workflows/monorepo_ci.yml:
name: Monorepo CI
on:
pull_request:
branches: [ main ]
jobs:
selective-matrix:
runs-on: macos-15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Select Xcode 27.1
run: sudo xcode-select -s /Applications/Xcode_27.1.app/Contents/Developer
- name: Run Affected Tests Only
run: |
CHANGED=$(git diff --name-only origin/main...HEAD)
echo "O'zgargan fayllar: $CHANGED"
TESTS=$(bazel query "tests(rdeps(//..., set($CHANGED)))" 2>/dev/null || true)
if [ -n "$TESTS" ]; then
echo "Yugurtiriladigan testlar: $TESTS"
bazel test $TESTS
else
echo "Hech qanday test ta'sirlanmadi."
fi
Amaliy mashqlar {#mashqlar}
- Yangi platforma: WatchOS yoki Server (Vapor). Monorepoga
Apps/Watch/papkasi qo'shing vaSharedKitni watchOS targetiga ulab ko'ring. - **
rdepschuqurligi tahlili.**bazel query "rdeps(//..., //Shared:SharedKit, 1)"buyrug'i bilanSharedKitga bevosita bog'langan 1-darajali targetlar ro'yxatini chiqaring. - ⭐ Breaking change eksperimenti.
User.swiftdagiusernamemaydoni nominifullNamedeb o'zgartiring vabazel build //...xatolik bergan barcha fayllarni (iOS va CLI) bir vaqtda to'g'rilab ko'ring.
O'zingizni tekshiring {#checklist}
- [ ] Monorepo multi-repoga nisbatan versiyalash va dependency boshqaruvida qanday ustunlik berishini bilaman.
- [ ] Bitta
SharedKitmoduli bir vaqtning o'zida iOS va macOS targetlariga qanday ulanishini tushunaman. - [ ] Monorepoda
rdepsorqali faqat o'zgargan qatlamga bog'liq testlarni filtrlash formulasini qo'llay olaman. - [ ] Atomik commit nima ekanini va nima uchun u mobil jamoalarga xotirjamlik berishini tushuntira olaman.
Xulosa {#xulosa}
- Monorepo — bu zamonaviy gigant platformalarning (Google, Uber, Airbnb) tanlovi bo'lib, uning samaradorligini faqat Bazel kabi kuchli build tizimi kafolatlay oladi.
- Biz iOS va macOS vositalarini umumiy Swift kutubxonasi bilan birlashtirdik va ularni selective testing bilan qurollantirdik.
- Endi biz Bazel'ning ichki mexanizmlariga eng chuqur sho'ng'iymiz: O'z Starlark rule'larimizni yozish!
- Keyingi dars: Bazel 17: O'z rule'laringiz: genrule, rule(), provider'lar, depset'lar, aspect'lar va toolchain'lar
Manbalar {#manbalar}
- Dan Luu — Advantages of Monorepos — Monorepo arxitekturasi bo'yicha klassik tahlil.
- Uber Engineering — iOS Monorepo Architecture — Uber'ning keng ko'lamli mobil monorepo tajribasi.
- Bazel Documentation — Workspaces and Multiple Targets — Bir nechta targetlarni bitta omborda tashkil etish.