İçeriğe geç
academia.sh

Ders 14 / 14

Görev Otomasyonu

Betik tanımlarının bildirim dosyasında saklanması, betik koşucusunun yaptığı işler, yaşam döngüsü kancaları, çıkış kodu sözleşmesi ve yeniden üretilebilir komut kümesi.

İçindekiler

Kurs boyunca kurulan araçların hepsi komut satırından çağrılan programlardır: paketleyici, çözümleyici, biçimlendirici, sınama koşucusu. Her birinin kendi seçenekleri, kendi girdi yolları vardır.

Bu komutların geliştiricinin belleğinde durması iki sorun çıkarır. Komutlar kişiden kişiye farklılaşır ve yapı sunucusunda çalışan komut, geliştiricinin çalıştırdığından başka olur. Bu ders, komutları projenin kendisine yazmayı ve çalıştırılmalarını tek bir sözleşmeye bağlamayı ele alır.

Betik Tanımları

Bildirim dosyasındaki betik alanı, projeye ait komutları adlarıyla saklar:

{
  "name": "metin-olcer",
  "version": "0.1.0",
  "type": "module",
  "exports": { ".": "./src/index.mjs" },
  "scripts": {
    "presinama": "node --check src/index.mjs",
    "sinama": "node --test",
    "olcum": "node araclar/olc.mjs",
    "bozuk": "node --check src/bozuk.mjs"
  }
}

Tanımların bildirimde durmasının üç sonucu vardır. Komutlar sürüm denetimine girer ve değişiklikleri incelenebilir. Depoyu klonlayan bir geliştirici, hangi komutların bulunduğunu okuyarak öğrenir. Aynı adlar hem yerelde hem yapı hattında çağrılır; iki ortam arasındaki fark ortadan kalkar.

Adlandırma sözleşmesi de önemlidir. Ad, aracın adını değil yapılan işi anlatmalıdır. Sınama komutu sinama adını taşır; hangi sınama koşucusunun kullanıldığı tanımın sağ tarafındadır. Araç değiştiğinde çağıranlar değişmez — komut adları projenin arayüzüdür, araç seçimi ise gerçekleştirimi.

Betik Koşucusunun Yaptığı

Betikleri çalıştıran araca betik koşucusu denir ve yaptığı iş dört maddeye sığar. Kendi gerçekleştirimi, dördünü de görünür kılar:

// dosya: kos.mjs
import { spawnSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { delimiter, resolve } from 'node:path';

const bildirim = JSON.parse(readFileSync('package.json', 'utf8'));
const betikler = bildirim.scripts ?? {};
const [istenen, ...ekArgumanlar] = process.argv.slice(2);

function calistir(ad, argumanlar = []) {
  const komut = [betikler[ad], ...argumanlar].join(' ');
  console.log(`> ${ad}: ${komut}`);
  const sonuc = spawnSync(komut, {
    shell: true,
    stdio: 'inherit',
    env: { ...process.env, PATH: resolve('node_modules/.bin') + delimiter + process.env.PATH },
  });
  return sonuc.status ?? 1;
}

if (betikler[istenen] === undefined) {
  console.error(`betik yok: ${istenen}`);
  process.exit(1);
}

for (const ad of [`pre${istenen}`, istenen, `post${istenen}`]) {
  if (betikler[ad] === undefined) continue;
  const kod = calistir(ad, ad === istenen ? ekArgumanlar : []);
  if (kod !== 0) {
    console.error(`durdu: ${ad} betiği ${kod} kodu ile çıktı`);
    process.exit(kod);
  }
}

Komutu bulur. Ad bildirimdeki tablodan okunur; yoksa hata verilir.

Ortamı hazırlar. Kurulu paketlerin çalıştırılabilir dosyaları node_modules/.bin dizinine yerleştirilir ve bu dizin arama yolunun başına eklenir. Sonucu şudur: bir aracı komut adıyla çağırmak için sistem geneline kurmak gerekmez; projenin kendi kopyası bulunur. Linux’a Giriş kursundaki komut yolu dersinde tanıtılan arama sırası burada bilinçli bir araç hâline gelir.

Kancaları çağırır. İstenen betikten önce pre önekli, sonra post önekli aynı adlı betik varsa çalıştırılır.

Çıkış kodunu taşır. Alt sürecin kodu koşucunun kodu olur.

Sınama betiği çalıştırıldığında dördünün üçü tek çıktıda görünür:

$ node ../kos.mjs sinama 2>&1 | grep -vE 'duration_ms|ms\)|^$'
> presinama: node --check src/index.mjs
> sinama: node --test
ℹ tests 2
ℹ suites 0
ℹ pass 2
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0

Kanca kendiliğinden çalıştı, ardından asıl betik çalıştı. Süzgeç, çıktıdaki süre ölçümlerini eler; süreler her çalıştırmada değişir. Sınama dosyası, çalışma zamanının yerleşik sınama arayüzünü kullanır:

// dosya: test/olcum.test.mjs
import assert from 'node:assert/strict';
import test from 'node:test';
import { olc } from '../src/index.mjs';

test('sözcük ve cümle sayısı', () => {
  const olcum = olc('Bir cumle. Iki cumle.');
  assert.equal(olcum.sozcukSayisi, 4);
  assert.equal(olcum.cumleSayisi, 2);
});

test('boş metin sıfır döndürür', () => {
  assert.equal(olc('').sozcukSayisi, 0);
});

Argüman Aktarımı

Bazı betikler değişken girdi alır. Koşucu, betik adından sonra verilen argümanları komutun sonuna ekler:

// dosya: araclar/olc.mjs
import { olc } from '../src/index.mjs';

const metin = process.argv.slice(2).join(' ');
if (metin === '') {
  console.error('kullanım: node araclar/olc.mjs <metin>');
  process.exit(2);
}
const olcum = olc(metin);
console.log(`${olcum.sozcukSayisi} sözcük, ${olcum.cumleSayisi} cümle, en uzun: ${olcum.enUzun}`);
$ node ../kos.mjs olcum "Modul kendi kapsamini tasir. Betik genel kapsamda calisir."
> olcum: node araclar/olc.mjs Modul kendi kapsamini tasir. Betik genel kapsamda calisir.
8 sözcük, 2 cümle, en uzun: kapsamini

Argümanların kabuk tarafından yeniden yorumlandığına dikkat edin: komut bir kabuk içinde çalıştığı için boşluk ve özel karakterler kabuk kurallarına tabidir. Kabuk Programlama kursundaki alıntılama kuralları burada geçerlidir; değişken içerik taşıyan argümanlar alıntılanmalıdır.

Başarısızlık Sözleşmesi

Otomasyonun tümü tek bir sözleşmeye dayanır: bir komut başarısız olduğunda sıfırdan farklı bir çıkış kodu döndürür. Sözleşme tutulduğunda zincir kendiliğinden durur:

// dosya: src/bozuk.mjs
export function eksik(metin) {
  return metin.split(/\s+/.length;
}
$ node ../kos.mjs bozuk 2>&1 | grep -E '^>|^durdu'
> bozuk: node --check src/bozuk.mjs
durdu: bozuk betiği 1 kodu ile çıktı
$ node ../kos.mjs bozuk > /dev/null 2>&1; echo "cikis kodu: $?"
cikis kodu: 1

Sözdizim denetimi başarısız oldu, koşucu durdu ve kendi çıkış kodunu 1 yaptı. Bu kod, onu çağıran bir üst katmanın — yapı hattının — durmasını sağlar.

Sözleşmeyi bozan iki yaygın hata vardır. Birincisi, hata durumunda sıfır döndüren betiklerdir; boru hattının son komutu başarılıysa kabuk sıfır döndürür ve gerçek hata gizlenir. İkincisi, yalnızca ekrana hata yazıp çıkış kodunu ayarlamayan betiklerdir; otomasyon ekranı okumaz, yalnızca kodu okur.

Yeniden Üretilebilirlik

Aynı komutun aynı sonucu vermesi dört koşula bağlıdır.

Bağımlılıklar kilitten kurulur. Kilit dosyası varsa kurulum bir arama değil yeniden üretimdir. Yapı hattında, kilidi güncelleyen değil kilide uyan kurulum biçimi kullanılmalıdır; kilitle bildirim çelişirse işlem durmalıdır.

Araçlar projeye kuruludur. Çözümleyici, biçimlendirici ve derleme araçları geliştirme bağımlılığı olarak bildirilir. Sistem geneline kurulmuş bir araca güvenmek, sürümünü denetimsiz bırakmaktır.

Komutlar tek yerde tanımlıdır. Yapı hattı yapılandırması komutları yeniden yazmaz; betik adlarını çağırır. Böylece komutun içeriği değiştiğinde iki yer güncellenmez.

Çıktılar girdilerden belirlenir. Derleme çıktısı, kaynak ve yapılandırmadan başka bir şeye — zamana, makine adına, ağ erişimine — bağlıysa yeniden üretilemez.

Bu dört koşul sağlandığında, yapı hattı yalnızca geliştiricinin yerelde çalıştırdığı komutları çalıştıran bir ortama dönüşür. Bir hata yapı hattında çıkıp yerelde çıkmıyorsa, koşullardan biri sağlanmıyor demektir; hatanın kendisini aramadan önce hangi koşulun bozulduğu aranmalıdır.

Özet

  • Betik tanımları bildirimde saklanır; sürüm denetimine girer, belgeleme işlevi görür ve yerel ile yapı hattı arasındaki komut farkını ortadan kaldırır.
  • Betik adları yapılan işi anlatır, aracı değil; araç değiştiğinde çağıranlar değişmez.
  • Koşucu komutu bulur, kurulu araçların dizinini arama yolunun başına ekler, pre ve post kancalarını çağırır ve çıkış kodunu taşır.
  • Otomasyon çıkış kodu sözleşmesine dayanır: başarısızlık sıfırdan farklı bir kodla bildirilmezse zincir yanlış biçimde sürer.
  • Yeniden üretilebilirlik dört koşula bağlıdır: kilitten kurulum, projeye kurulu araçlar, tek yerde tanımlı komutlar ve girdilerden belirlenen çıktılar.

Kurs Kapanışı

Kurs, tek dosyalık bir ölçüm betiğiyle başladı ve o betiği bir pakete dönüştürerek ilerledi. Yol boyunca dört soru yanıtlandı.

Kod nasıl bölünür? Modül kapsamı, adların dışarı sızmasını ayrıştırma düzeyinde engeller. Dışa aktarım yüzeyi bir tasarım kararıdır: statik sözdizim bağlamayı değerlendirmeden önceye alır, çağrı tabanlı sistem esneklik karşılığında bu güvenceden vazgeçer, dinamik içe aktarım ikisinin arasında bir yol açar.

Bağımlılıklar nasıl yönetilir? Çıplak ad, dizin ağacında belirlenimci bir kuralla aranır. Sürüm aralıkları bir kolaylıktır, kilit dosyası bir güvencedir. Ağaçtaki yinelenen kopyalar sessiz arızalar üretir; geçişli kapanış, güvenilen kod kütlesinin ölçüsüdür.

Kaynak hedefe nasıl ulaşır? Paketleyici modül çizgesini tek çıktıya indirger, aktarım sözdizimi ve yetenekleri hedefe uyarlar, kaynak haritası üretilmiş konumdan kaynağa geri dönüş sağlar. Her adımın ölçülebilir bir bedeli vardır.

Kalite nasıl korunur? Statik çözümleme kusur örüntülerini kod çalıştırmadan yakalar, biçimlendirme tartışmayı ortadan kaldırır, görev otomasyonu bu araçların herkeste aynı biçimde çalışmasını sağlar.

Kursun boydan boya taşıdığı ilke, dördünde de aynıdır: bilgiyi kodun içine yazmak. Bağımlılık bildirimde, sürüm sözleşmesi numarada, kurulu ağaç kilitte, kaynak eşlemesi haritada, komutlar betik tablosunda durur. Belleğe ya da alışkanlığa bırakılan her bilgi, bir başkasının makinesinde kaybolur.

Bir bilgi türü hâlâ kodun dışındadır: bir işlevin hangi tipte değer beklediği ve ne döndürdüğü. Ölçüm kütüphanesinin işlevleri metin bekler, ama bu beklenti yalnızca çalışma anında sınanır — sayıyla çağrılan bir işlev hatayı ancak kendisine gelindiğinde verir. Sonraki kurs, bu bilgiyi kaynağa yazmayı ve derleme anında denetlemeyi ele alıyor: tip çıkarımı, daraltma, genelleştirilmiş tipler ve derleyicinin katılık düzeyinin bilinçli seçimi.

İ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