İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat