- Karmaşık bir CI'ı GitHub Actions'a geri taşırken merge queue, birden fazla runner, Rust derlemeleri, Docker imajları ve entegrasyon testleri iç içe geçti; yapılandırmadan çok hata ayıklama maliyeti öne çıktı
main dalına giren tüm değişikliklerin testleri geçmesi gerekiyor; biçimlendirme, kullanılmayan bağımlılıklar ve lint gibi küçük hatalar otomatik düzeltiliyor ve CI çıktılarının sürüm çıktılarıyla aynı olması hedefleniyor
- Merge queue öncesi ve sonrası doğrulamayı zorunlu kılmak için iki aşamadaki iş adlarının aynı olması gerekti; aksi halde kontroller durabiliyor ya da başarısız değişiklikler birleştirilebiliyor
GITHUB_TOKEN, workflow permissions, özel token'lar ve fork/self-hosted runner istisnaları iç içe geçince güvenlik modelini anlamak zorlaşıyor ve hata yapma payı büyüyor
- Docker konteyner çalıştırma, YAML workflow'ları ve sınırlı yerel test imkânı geliştirme hızını yavaşlatsa da yeni CI betikleri birleştirme süresini ciddi biçimde kısalttı
GitHub Actions'a Dönen Karmaşık CI
- Son iki haftadır CI betikleri GitHub Actions üzerinde yeniden yazıldı
- Bu, CI yapılandırmasının üçüncü büyük değişikliği oldu
- İlki GitHub Actions'tı
- Sonra Earthly'ye geçildi
- Earthly durdurulunca yeniden GitHub Actions'a dönüldü
- Mevcut CI, merge queue, birden fazla runner, Rust derlemeleri, Docker imajları ve ağır entegrasyon testlerini birlikte ele alıyor
- Runner olarak self-hosted, blacksmith.sh ve GitHub-hosted birlikte kullanılıyor
- Tek bir PR'ı birleştirmek için paralel runner'larda toplam yaklaşık 1 saatlik CI süresi harcanıyor
CI'ın Karşılaması Gereken Koşullar
main dalına giren tüm değişiklikler tüm testleri geçmek zorunda
- Biçimlendirme, kullanılmayan bağımlılıklar ve lint sorunları gibi küçük hatalar başarısızlıkla sonuçlanmak yerine otomatik düzeltilmeli
- CI'da test edilen çıktılar, gerçek sürüm çıktılarıyla aynı olmalı
- Geliştirici deneyimi için CI hızlı tamamlanmalı
- GitHub Actions bu koşulları teknik olarak destekliyor, ancak kurulum sürecine gizli tuzaklar, tutarsız davranışlar ve zor hata ayıklama eşlik ediyor
Merge queue ve durum kontrolleri
- Temiz bir
main dalı korumanın anahtarı GitHub'ın merge queue özelliği
- Merge queue, CI çalışmadan önce PR'ı
main üzerine rebase ediyor
- Gerekli CI çalıştırmaları iki aşamaya ayrılıyor
- Kuyruğa girmeden önce CI çalışıyor ve küçük sorunları otomatik düzeltiyor
- Kuyruk içinde CI tekrar çalışıyor ve son birleştirme durumunu doğruluyor
- GitHub Actions'ta bu iki çalıştırmayı da zorunlu yapmak için iki aşamadaki iş adlarının aynı olması gerekiyor
- GitHub bu iki çalıştırmayı aynı kontrol olarak görüyor ve birleştirme için ikisinin de başarılı olmasını istiyor
- Bu yöntem, saatler süren hata ayıklamadan sonra bir Stack Overflow yanıtı sayesinde bulundu
- Diğer yaklaşımlar, durum kontrollerinin kuyruğa girişten önce beklemede kalmasına ve işlerin hiç başlamamasına ya da merge queue içinde başarısız olması gereken işlerin başarısız olsa bile birleştirilmesine yol açabiliyor
Anlaması Zor GitHub Actions Güvenlik Modeli
- Yakın zamanda popüler bir GitHub Action'ın ele geçirilmesinin ardından “bağımlılıkları hash ile sabitleyin” önerisi öne çıktı, ancak yorumlarda neredeyse kimsenin bunu yapmadığı görüldü
- GitHub Actions'ın varsayılan token'ı
GITHUB_TOKEN
- Bu token varsayılan izinlerle başlatılıyor
- Varsayılan izinler, depo ayarlarındaki Actions → General → Workflow Permissions bölümünden belirlenebiliyor
GITHUB_TOKEN varsayılan izinleri kısıtlıysa gerekli action'ları ve komutları çalıştırmak için izinleri yükseltmek gerekiyor; varsayılanlar genişse workflow dosyasından bazı izinler çıkarılabiliyor
- Daha iyi varsayılan yaklaşım, hiç izin olmadan başlayıp kullanıcının yalnızca gereken izinleri eklemesi olurdu
- İzin türleri çok fazla ve GitHub uzmanı olmayan biri için her birinin neyi koruduğunu anlamak zor
Token ve izin istisnaları
softprops/action-gh-release ile GitHub release'i otomatik oluştururken özel CI_RELEASE token'ı kullanılıyor
- name: Release on GitHub
if: env.version_exists == 'false'
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ env.CURRENT_VERSION }}
generate_release_notes: true
make_latest: true
token: ${{ secrets.CI_RELEASE }}
- Varsayılan token ile release'in kendisi tamamlanıyor, ancak release sonrasındaki workflow tetiklenmiyor
- Bunun için ayrı bir uyarı da olmadığı için, nedenini anlamak ancak aynı sorunu yaşamış birinin bıraktığı issue bulununca mümkün oluyor
- Workflow YAML içinde izinleri yükseltmek de mümkün
- Korunması istenen kodun içinden izin yükseltme fikri alışılmadık geliyor
- GitHub belgelerine göre
permissions anahtarıyla fork depolar için okuma izinleri eklenip çıkarılabiliyor, ancak normalde yazma izni verilemiyor
- İstisna, yöneticinin GitHub Actions ayarlarında Send write tokens to workflows from pull requests seçeneğini açması
- Çok sayıda istisna ve tuzak yüzünden GitHub Actions güvenlik modeli güçlü olsa da saldırı yüzeyini ve hata yapma ihtimalini birlikte artırıyor
Self-hosted runner belirsizliği
- GitHub belgeleri, herkese açık depolarda self-hosted runner kullanımını önermiyor
- Çünkü herkese açık depoların fork'ları PR üzerinden self-hosted runner makinesinde tehlikeli kod çalıştırabiliyor
- GitHub'da dış katkıcıların PR'larını çalıştırmadan önce onay isteme seçeneği de var
- Bu ayarla self-hosted runner birlikte kullanıldığında bunun gerçekten güvenli olup olmadığına dair belgelerde net bir yanıt yok, internette de ortak bir görüş bulunmuyor
- Karmaşıklık yüksek olduğu için konu tam güven verecek şekilde çözülememiş kalıyor
Docker ve GitHub Actions çatışması
- GitHub Actions işleri konteyner içinde çalıştırabiliyor
- Böylece bağımlılıkları her seferinde kurmak yerine dev container içinde önceden paketlemek mümkün oluyor
- Gerçek kullanımda dosya izinleri sorunu tekrar tekrar ortaya çıkıyor
- Konteyner dosyaları bir kullanıcıyla derlerken GitHub runner farklı bir uid/gid ile çalışabiliyor
- Sonuç olarak konteyner içindeki dosyalara, GitHub workspace'ine ya da geçici host dizinlerine erişim kaybolabiliyor
$HOME dizini de farklılaşabiliyor
- Dev container araçları
/home/ubuntu altına kurabiliyor
- GitHub Actions içinde ise
$HOME değeri /github/home olabiliyor
$HOME altındaki dosyalara bağımlı araçlar gerekli dosyaları bulamayabiliyor
- Host sistemle etkileşen action'lar da bozulabiliyor
- GitHub cache 10GB ile sınırlı olduğu için blacksmith'in sticky disk action'ı önbellek için NVMe sürücüsü bağlıyor
- Bu, konteyner içinde çalışmadı ve ancak blacksmith.sh tarafındaki bir düzeltmeden sonra çözüldü
container alanının kendisinde de kısıtlar var
- Entrypoint override edilemiyor
- Bazı step'leri konteyner içinde, diğerlerini dışında çalıştırmak da mümkün değil
YAML workflow geliştirme ve hata ayıklama
- GitHub Actions mantığı YAML ile yazılıyor ve karmaşıklık arttıkça hata yapma olasılığı yükseliyor
- RustRover'ın GitHub YAML linter denetimi yardımcı oldu, ancak daha iyi statik analiz gerekiyor
- Yerelde gerçek CI davranışını yeterince denemek zor
- act bilinen bir araç, ancak CI'da yapılmak istenenlerin yalnızca küçük bir kısmını destekliyor
- En iyi hata ayıklama yöntemi, aynı deponun bir kopyasını daha oluşturup CI beklenen gibi çalışana kadar
git commit -a -m "wip" && git push test-ci branch döngüsünü tekrarlamak oldu
Workflow ayrımı ve çıktıların yeniden kullanımı
- Her seferinde tüm CI hattını çalıştırmamak için tek tek workflow'lar küçük tutuldu
- Her workflow sonunda çıktılar yükleniyor, sonraki workflow'lar bunları indirerek her şeyi baştan derlemek zorunda kalmıyor
- Önceki çalıştırmalardan çıkan çıktılar indirilerek workflow'lar izole biçimde test edilebiliyor
- Ancak önceki çalıştırmadan indirme yaparken
download-artifact action'ına token vermek gerekiyor
- Bu token varsayılan token olabiliyor, ama neden yine de açıkça belirtilmesi gerektiği cevaplanmamış bir soru olarak kalıyor
- Ana workflow dosyası, diğer YAML dosyalarını çağıran bir zincire dönüşüyor
jobs:
invoke-build-rust:
name: Build Rust
uses: ./.github/workflows/build-rust.yml
invoke-build-java:
name: Build Java
uses: ./.github/workflows/build-java.yml
invoke-tests-unit:
name: Unit Tests
needs: [invoke-build-rust, invoke-build-java]
uses: ./.github/workflows/test-unit.yml
invoke-tests-adapter:
name: Adapter Tests
needs: [invoke-build-rust]
uses: ./.github/workflows/test-adapters.yml
secrets: inherit
invoke-build-docker:
name: Build Docker
needs: [invoke-build-rust, invoke-build-java]
uses: ./.github/workflows/build-docker.yml
invoke-tests-integration:
name: Integration Tests
needs: [invoke-build-docker]
uses: ./.github/workflows/test-integration.yml
invoke-tests-java:
name: Java Tests
needs: [invoke-build-java]
uses: ./.github/workflows/test-java.yml
- Bazı işlerde
secrets: inherit gerekiyor
- Bir workflow başka bir workflow çağırdığında secret'lar varsayılan olarak paylaşılmıyor
- Tüm CI hattında başarısız olup tek tek step çalıştırmada başarılı olmasının nedeni buydu
Daha hızlı birleştirme, hâlâ pahalı hata ayıklama
- Yeni CI betikleri birleştirme süresini ciddi biçimde kısalttı ve sonuçtan memnun kalındı
- Ancak o noktaya gelmek çok fazla zaman aldı; sorun çıktığında hata ayıklamanın hâlâ daha kolay olması gerekiyor
Henüz yorum yok.