Azimjon Abdurasulov

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 SharedKit biznes 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_application va swift_binary (macOS) nishonlarini birgalikda sozlaysiz.
  • SharedKit kutubxonasini 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 query yordamida monorepo miqyosida o'zgarishlar xavfini tahlil qilasiz.

Mundarija

  1. Multi-repo azobi vs Monorepo qulayligi
  2. Monorepo papkalar va targetlar xaritasi
  3. SharedKit: Platformadan mustaqil umumiy mantiq
  4. macOS CLI nishoni: Admin vositasi
  5. iOS ilova nishoni: Mobil interfeys
  6. Atomik refaktoring: Ikkala platformani bir vaqtda yangilash
  7. Monorepo'da Selective Testing: Vaqtni tejash san'ati
  8. Monorepo uchun GitHub Actions pipeline
  9. Amaliy mashqlar
  10. O'zingizni tekshiring
  11. Xulosa
  12. 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:

  1. SharedModelsda bitta modelga yangi maydon qo'shasiz.
  2. Unga git tag qo'yib (v1.4.2), push qilasiz.
  3. So'ng iOS-App repoga o'tib, Package.resolvedni yangilaysiz va PR ochasiz.
  4. Keyin macOS-Admin-Tool repoga o'tib, yana xuddi shu ishni qilasiz.
  5. 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:

  1. Shared/Sources/User.swiftga case guest = "GUEST" qo'shamiz.
  2. Tools/AdminCLI/main.swift va Apps/iOS/Sources/MobileApp.swiftda zarur joylarni to'g'rilaymiz.
  3. Bitta buyruq bilan barcha platformalarni tekshiramiz:
bazel test //...
  1. 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}

  1. Yangi platforma: WatchOS yoki Server (Vapor). Monorepoga Apps/Watch/ papkasi qo'shing va SharedKitni watchOS targetiga ulab ko'ring.
  2. **rdeps chuqurligi tahlili.** bazel query "rdeps(//..., //Shared:SharedKit, 1)" buyrug'i bilan SharedKitga bevosita bog'langan 1-darajali targetlar ro'yxatini chiqaring.
  3. ⭐ Breaking change eksperimenti. User.swiftdagi username maydoni nomini fullName deb o'zgartiring va bazel 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 SharedKit moduli bir vaqtning o'zida iOS va macOS targetlariga qanday ulanishini tushunaman.
  • [ ] Monorepoda rdeps orqali 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}

  1. Dan Luu — Advantages of Monorepos — Monorepo arxitekturasi bo'yicha klassik tahlil.
  2. Uber Engineering — iOS Monorepo Architecture — Uber'ning keng ko'lamli mobil monorepo tajribasi.
  3. Bazel Documentation — Workspaces and Multiple Targets — Bir nechta targetlarni bitta omborda tashkil etish.

Markdown manba

iOS Lessons