Go ile yazdığım bir komut satırı aracını paketlemek ve dağıtmak
Bir Go CLI aracını yazmak işin yarısı; onu sürümlemek, çapraz derlemek ve başkalarının kolayca kurabileceği biçimde dağıtmak diğer yarısı.
Yıllar önce Go’yu öğrenmek için küçük bir komut satırı aracı yazmıştım. O zaman ilgilendiğim şey aracın çalışmasıydı. Bu yıl aynı tür araçlardan birini ekip arkadaşlarımın da kullanmasını isteyince, daha önce hafife aldığım bir kısma odaklandım: bir aracı yazmak işin yarısı; onu insanların zahmetsizce kurabileceği hâle getirmek diğer yarısı.
Tek dosya: Go’nun sessiz avantajı
Go’nun bu iş için en büyük kozu, go build’in tek bir çalıştırılabilir dosya üretmesi. Çalışma zamanı yok, bağımlılık dizini yok. Bu dosyayı kopyalayan herkes aracı çalıştırabilir.
PHP ya da Node dünyasından gelince bunun ne kadar büyük bir kolaylık olduğu hemen anlaşılmıyor. Bir aracı paylaşmak demek, karşı tarafta doğru sürümün kurulu olmasını ummak demekti. Go’da bu soru hiç sorulmuyor.
Çapraz derleme
Tek bir komutla farklı işletim sistemleri için ayrı ikili dosyalar üretebiliyorsunuz:
GOOS=linux GOARCH=amd64 go build -o dist/arac-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -o dist/arac-darwin-arm64 .
GOOS=windows GOARCH=amd64 go build -o dist/arac-windows-amd64.exe .
Geliştirme makinem ne olursa olsun, kullanıcının makinesi için doğru ikiliyi üretebiliyorum. Bu adımı bir betiğe alıp her sürümde otomatik çalıştırıyorum.
Sürüm bilgisini ikiliye gömmek
Bir kullanıcı “hangi sürümü kullanıyorum?” diye sorabilmeli. Sürüm numarasını derleme anında, ldflags ile koda gömüyorum:
go build -ldflags "-X main.surum=1.4.0" -o arac .
package main
var surum = "dev"
func main() {
// --version bayrağı bu değeri yazdırır
}
Varsayılan dev; resmî bir sürüm derlendiğinde gerçek numara geçiyor. Bir hata raporu geldiğinde, kullanıcının elindeki sürümü tahmin etmek zorunda kalmıyorum.
Dağıtım ve kurulum
İkili dosyaları her sürümde bir depo sürümüne (release) ekliyorum. Kurulum böylece üç adıma iniyor: doğru dosyayı indir, çalıştırılabilir yap, PATH üzerindeki bir dizine taşı. Bunu da kısa bir kurulum betiğiyle tek komuta indirgemek mümkün.
go install: geliştiriciler için kısa yol
İkili dosya dağıtımı, Go kurulu olmayan kullanıcılar için doğru yol. Ama aracı kullanacak kişiler de geliştiriciyse, daha kısa bir yol var: go install.
go install github.com/kullanici/arac@latest
Bu komut, kaynaktan derleyip ikiliyi GOBIN dizinine ($GOPATH/bin, GOPATH tanımlı değilse $HOME/go/bin) koyuyor; bu dizinin kullanıcının PATH’inde olması gerekiyor. Tek satır, sürüm seçilebilir, ayrı bir indirme adımı yok. Ekip içi araçları çoğu zaman böyle dağıtıyorum; çünkü ekipte zaten herkeste Go kurulu.
Yani aslında iki ayrı kitleye iki ayrı dağıtım yolu sunuyorum: Go bilmeyenlere hazır ikili dosya, geliştiricilere go install. Bir aracı yaygınlaştırmanın sırrı, tek bir kurulum yöntemini herkese dayatmak değil; her kitleye onun için en az sürtünmeli yolu sunmaktır. Bu ayrımı yapmak birkaç dakikalık bir iş; ama aracın gerçekten kullanılıp kullanılmayacağını belirleyen şey çoğu zaman tam da bu.
Bir not daha: hangi yolu seçerseniz seçin, aracın dış bağımlılıklarını az tutun. Go’nun standart kütüphanesi şaşırtıcı derecede geniş; küçük bir araç için çoğu zaman tek bir dış pakete bile ihtiyaç olmuyor. Bağımlılık ne kadar azsa, derleme o kadar hızlı, ikili o kadar küçük ve aracın yıllar sonra hâlâ sorunsuz derlenme olasılığı o kadar yüksek.
Çıkardığım ders
Bir aracı kullanılır kılan, içindeki zekice kod değil; onu kurmanın ne kadar kolay olduğudur. Yıllar içinde teknik olarak iyi ama kurulumu zahmetli olduğu için kimsenin dokunmadığı çok araç gördüm. Go’yu bu tür işler için tercih etmemin sebebi sözdizimi değil; yazdığım şeyi başkasının eline “çalışır” hâlde ulaştırmayı bu kadar kolaylaştırması. Bir araç ancak kurulduğu kadar vardır.
Bu konuda sorulanlar
Go CLI aracını GoReleaser ve Homebrew ile çoklu platforma nasıl dağıtırım?
Tag push'unda tetiklenen tek bir workflow cross-compile edip Release'i yayımlasın, homebrew_casks: bloğu tap'i güncellesin; tap için ayrı bir PAT şart.
Bu konudaki deneyler
Uygulama ile sağlayıcı arasında duran, tek OpenAI uyumlu API sunan ve isteğe RAG bağlamını kendisi ekleyen self-hosted Go gateway.
Şu an ne yapıyor
Uygulamanın tek bir OpenAI uyumlu uca konuşmasını, isteğin hangi modele gideceğini ve — bir doküman havuzu bağlıysa — hangi dokümanlardan bağlam alacağını projenin ayarının belirlemesini sağlıyor. Sağlayıcı anahtarını uygulamalara dağıtmak istemeyen ve harcamayı proje başına sınırlamak isteyen herkes kendi sunucusunda çalıştırabilir.
Dile özgü serileştirme yerine dondurulmuş bir JSON zarfı; polyglot kuyruk standardı BabelQueue'ya dönüştü.
Şu an ne yapıyor
Dondurulmuş JSON zarfı dört dilde — PHP, Python, Go, Node.js — aynı baytları okutuyor; sidecar ya da broker eklentisi gerekmiyor. Polyglot kuyruk kuran ekipler spesifikasyonu ve SDK'ları bugün kullanabilir.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.