Ders 08 / 14
Çalışma Alanları
Birden çok paketin tek depoda tutulması, çalışma alanı bildirimi, yerel paketlerin bağla çözümlenmesi, topolojik derleme sırası ve sürümleme stratejileri.
İçindekiler
Önceki derslerde tüm paketler dışarıdan geldi: bildirimde bir aralıkla istendiler, bir dizine kuruldular, oradan çözümlendiler. Bir ekibin kendi yazdığı paketler için bu döngü ağırdır — kütüphanede yapılan her düzeltmenin, onu kullanan uygulamada denenebilmesi için yayımlanması gerekir.
Ölçüm kütüphanesi bu noktaya geldi. Biçimlendirme yardımcısı ayrı bir pakete çıkarıldı, ölçüm sonucunu sunan bir rapor aracı eklendi. Üçü de aynı ekibin ürünü ve birlikte değişiyorlar. Bu ders, üçünün tek bir depoda nasıl tutulacağını ele alır.
Çok Paketli Depo
Birden çok paketi tek bir sürüm denetimi deposunda tutan düzene çok paketli depo (monorepo) denir. Çözdüğü sorunlar somuttur:
Bölünmüş değişiklik. Kütüphaneye bir parametre eklemek ve onu kullanan araçları güncellemek, ayrı depolarda birkaç adımlı bir işlemdir: yayımla, bekle, aralığı güncelle, kur. Tek depoda bu, tek bir işlemedir (commit).
Sürüm gecikmesi. Ayrı depolarda kütüphanenin son hâli ile uygulamanın kullandığı hâl arasında her zaman bir fark bulunur. Tek depoda bu fark sıfırdır.
Yinelenen yapılandırma. Çözümleyici kuralları, biçimlendirme ayarları ve sınama düzeni tek yerde tutulur.
Bedeli de vardır. Depo büyür; her değişiklikte hangi paketlerin etkilendiğini hesaplamak gerekir; sınama süresi tüm paketlerin toplamı olmaya eğilimlidir; sürüm ve yayımlama kararları karmaşıklaşır. Bu maliyetler, paketler birlikte değişmiyorsa ödenmeye değmez. Ölçüt basittir: paketler aynı değişiklikte birlikte güncelleniyorsa tek depo, bağımsız gelişiyorsa ayrı depolar uygundur.
Çalışma Alanı Bildirimi
Kök dizindeki bildirim, hangi alt dizinlerin paket olduğunu söyler ve kendisinin yayımlanmayacağını işaretler:
{ "name": "olcum-deposu", "private": true, "workspaces": ["paketler/*"] }
private alanı bir güvenliktir: kök dizin bir kütüphane değildir ve kazayla yayımlanması
istenmez. workspaces alanı ise kurulum aracına, listelenen kalıplara uyan her dizini bir
çalışma alanı (workspace) olarak ele almasını söyler.
Depodaki üç paket şunlardır:
{ "name": "bicim-yardimcisi", "version": "1.4.0", "type": "module", "main": "index.mjs" }
{ "name": "metin-olcer", "version": "0.1.0", "type": "module", "exports": { ".": "./src/index.mjs" }, "dependencies": { "bicim-yardimcisi": "^1.4.0" } }
{ "name": "rapor-araci", "version": "0.2.0", "type": "module", "main": "index.mjs", "dependencies": { "metin-olcer": "^0.1.0" } }
Dikkat edilecek nokta, bağımlılıkların yerel olduklarını belirten özel bir yazım
kullanmamasıdır. metin-olcer paketi bicim-yardimcisi paketini, dışarıdan gelen herhangi
bir paket gibi çıplak adıyla ve bir aralıkla ister. Yerellik, bildirimin değil kurulumun
kararıdır: kurulum aracı, istenen adın depoda bir çalışma alanı olduğunu ve sürümünün
aralığa uyduğunu görürse kayda gitmez.
Bağların Kurulması
Yerel paket, ağaca kopyalanarak değil, sembolik bağ ile yerleştirilir:
$ for b in node_modules/*; do printf '%s -> %s\n' "$b" "$(readlink "$b")"; done node_modules/bicim-yardimcisi -> ../paketler/bicim node_modules/metin-olcer -> ../paketler/olcer node_modules/rapor-araci -> ../paketler/rapor
Sembolik bağ kavramı Linux’a Giriş kursundaki bağlantılar dersinde tanımlanmıştı: hedefe
işaret eden, hedefin kendisi olmayan bir girdi. Buradaki sonucu belirleyicidir. Çözümleme
kuralı node_modules dizininde adı arar, bulduğu girdinin bağ olup olmadığına bakmaz;
bulunan dizin kaynak ağacın kendisidir. Kaynakta yapılan bir değişiklik, yeniden kurulum
gerektirmeden tüketiciye görünür.
// dosya: paketler/bicim/index.mjs export function ondalik(sayi, basamak = 2) { return sayi.toFixed(basamak); }
// dosya: paketler/olcer/src/index.mjs — bolumleme ve istatistik modülleri aynı dizindedir import { ondalik } from 'bicim-yardimcisi'; import { cumlelereBol, sozcuklereBol } from './bolumleme.mjs'; import { enUzunSozcuk, ortalamaUzunluk } from './istatistik.mjs'; export function olc(metin) { const sozcukler = sozcuklereBol(metin); return { sozcukSayisi: sozcukler.length, cumleSayisi: cumlelereBol(metin).length, ortalamaUzunluk: ondalik(ortalamaUzunluk(sozcukler)), enUzun: enUzunSozcuk(sozcukler), }; }
// dosya: paketler/rapor/index.mjs import { olc } from 'metin-olcer'; export function rapor(metin) { const o = olc(metin); return `${o.sozcukSayisi} sozcuk / ${o.cumleSayisi} cumle / ort. ${o.ortalamaUzunluk}`; }
// dosya: dene.mjs — depo kökünde import { rapor } from 'rapor-araci'; console.log(rapor('Modul kendi kapsamini tasir. Betik genel kapsamda calisir.'));
$ node dene.mjs 8 sozcuk / 2 cumle / ort. 6.13
Üç paket, hiçbiri yayımlanmadan birbirini çıplak adıyla buldu. Bu düzenin sessiz bir tuzağı vardır: yerelde çalışan içe aktarma, yayımlandıktan sonra çalışmayabilir. Bağ üzerinden erişilen dizin kaynak ağacın tamamıdır; yayımlanan arşiv ise yalnızca bildirimde listelenen dosyaları taşır. Dışa aktarım haritası veya dosya listesi eksikse, hata yalnızca yayımdan sonra görünür. Bu yüzden çok paketli depolarda arşivin içeriğini yayımlamadan önce denetlemek bir alışkanlık olmalıdır.
Derleme Sırası
Paketler birbirine bağlı olduğuna göre, üzerlerinde yapılan toplu işlemler — derleme,
sınama, yayımlama — rastgele sırada yürütülemez. rapor-araci paketini sınamak,
metin-olcer paketinin hazır olmasını gerektirir.
Doğru sıra, bağımlılık çizgesinin topolojik sıralamasıdır. Veri Yapıları kursunda tanıtılan bu sıralama, burada doğrudan uygulanır:
// dosya: sira.mjs — depo kökünde, paketler/ dizinini okur import { readdir, readFile } from 'node:fs/promises'; import { join } from 'node:path'; const kok = 'paketler'; const paketler = new Map(); for (const dizin of (await readdir(kok)).sort()) { const bildirim = JSON.parse(await readFile(join(kok, dizin, 'package.json'), 'utf8')); paketler.set(bildirim.name, Object.keys(bildirim.dependencies ?? {})); } // Derine arama ile topolojik sıralama: bağımlılıklar önce yayımlanır. const sonuc = []; const durum = new Map(); function ziyaret(ad) { if (durum.get(ad) === 'bitti') return; if (durum.get(ad) === 'sürüyor') throw new Error(`döngü: ${ad}`); durum.set(ad, 'sürüyor'); for (const bagimlilik of paketler.get(ad) ?? []) { if (paketler.has(bagimlilik)) ziyaret(bagimlilik); } durum.set(ad, 'bitti'); sonuc.push(ad); } for (const ad of paketler.keys()) ziyaret(ad); console.log(sonuc.join(' -> '));
$ node sira.mjs bicim-yardimcisi -> metin-olcer -> rapor-araci
Betiğin iki ayrıntısı önemlidir. Depoda bulunmayan bağımlılıklar atlanır: dış paketlerin sırası bizi ilgilendirmez, onlar zaten kuruludur. İkincisi, ziyaret durumu üç değerlidir; “sürüyor” durumundaki bir düğüme yeniden gelinmesi döngü demektir ve hata verilir. Paketler arası dairesel bağımlılık, modüller arasındakinin aksine hiçbir biçimde çözülemez.
Aynı çizge ikinci bir soruya da yanıt verir: bir pakette yapılan değişiklikten hangi paketler etkilenir? Yanıt, çizgenin ters yönde dolaşılmasıyla bulunur ve yalnızca etkilenen paketlerin sınanmasını sağlar. Büyük depolarda sınama süresini yönetilebilir tutan yöntem budur.
Sürümleme Stratejisi
Depodaki paketlerin sürümleri iki biçimde yönetilir.
Ortak sürüm. Tüm paketler aynı numarayı taşır ve birlikte yayımlanır. Yayımlama kararı tektir, izlenmesi kolaydır; karşılığında hiç değişmemiş bir paket de yeni sürüm alır ve tüketicileri gereksiz yükseltmeye zorlanır.
Bağımsız sürüm. Her paket kendi numarasını taşır ve yalnızca değiştiğinde yayımlanır. Tüketici açısından dürüst olan yaklaşımdır; karşılığında hangi paketin hangi sürümde olduğunun izlenmesi ve depo içi aralıkların güncel tutulması gerekir.
Seçim, tüketicinin kim olduğuna bağlıdır. Paketler yalnızca depo içinde kullanılıyorsa ortak sürüm işi kolaylaştırır. Dışarıya yayımlanıyorsa bağımsız sürüm, önceki derste kurulan sözleşmeye sadık kalan tek seçenektir: sürüm numarası ancak gerçek bir değişikliği bildirdiğinde anlam taşır.
Özet
- Çok paketli depo, birlikte değişen paketleri tek işlemede güncellemeyi ve tek yapılandırmayı paylaşmayı sağlar; paketler bağımsız gelişiyorsa maliyeti karşılıksız kalır.
- Kök bildirimindeki çalışma alanı listesi hangi dizinlerin paket olduğunu belirtir; bağımlılıklar yerel olduklarını belirten özel bir yazım kullanmaz.
- Yerel paketler ağaca sembolik bağla yerleştirilir; kaynakta yapılan değişiklik yeniden kurulum gerektirmeden görünür.
- Bağ üzerinden tüm kaynak ağacı erişilebilir olduğu için, yayımlanan arşivde eksik kalan dosyalar yalnızca yayımdan sonra fark edilir.
- Paketler üzerindeki toplu işlemler bağımlılık çizgesinin topolojik sırasına göre yürütülür; ters yönde dolaşma, bir değişiklikten etkilenen paketleri verir.
Sonraki Adım
Ağaçtaki paketlerin çoğu doğrudan seçilmedi; başka paketlerin bağımlılığı oldukları için oradalar. Bu paketler de kod çalıştırır ve kurulum sırasında betik işletebilirler. Sonraki ders, bu geçişli kütlenin büyüklüğünü ölçer ve bağımlılık seçiminin, kilitlemenin ve doğrulamanın savunma değeri olan yanlarını ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.