iOS geliştirme için CI sistemleri: Intel'den ARM'ye dönüşüm

Yayınlanan: 2024-02-14

Sürekli gelişen teknoloji ortamında şirketlerin güncel ve rekabetçi kalabilmek için değişim rüzgarlarına uyum sağlaması gerekiyor. Teknoloji dünyasını kasıp kavuran dönüşümlerden biri de Intel x86_64 mimarisinden iOS ARM mimarisine geçiş oldu; Apple'ın çığır açan Apple M1 çipi bunun bir örneğini oluşturuyor. Bu bağlamda iOS için CI sistemleri , yazılım geliştirme ve test süreçlerinin verimli ve en son teknolojik standartlarla güncel kalmasını sağlayarak, bu değişime yön veren şirketler için çok önemli bir husus haline geldi.

Apple, M1 yongalarını neredeyse üç yıl önce duyurdu ve o zamandan bu yana şirketin ARM mimarisini benimseyip sonunda Intel tabanlı yazılım desteğini bırakacağı açıktı. Mimariler arasındaki uyumluluğu sürdürmek için Apple, 2006 yılında PowerPC'den Intel'e yapılan önemli mimari dönüşümü sırasında geçmişte güvenilirliği kanıtlanmış olan, tescilli ikili çeviri çerçevesi Rosetta'nın yeni bir sürümünü tanıttı. Dönüşüm hala devam ediyor ve gördük Xcode, 14.3 sürümünde Rosetta desteğini kaybetti.

Miquido olarak birkaç yıl önce Intel'den ARM'ye geçme ihtiyacını fark ettik. 2021 yılı ortalarında hazırlıklara başladık. Birden fazla istemcinin, uygulamanın ve projenin eş zamanlı devam ettiği bir yazılım evi olarak, üstesinden gelmemiz gereken birkaç zorlukla karşılaştık. Şirketinizin benzer durumlarla karşı karşıya kalması durumunda bu makale nasıl yapılır rehberiniz olabilir. Açıklanan durumlar ve çözümler iOS geliştirme perspektifinden gösterilmektedir; ancak diğer teknolojiler için de uygun bilgiler bulabilirsiniz. Geçiş stratejimizin kritik bir bileşeni, iOS için CI sistemimizin yeni mimari için tamamen optimize edilmesini sağlamaktı; Böyle önemli bir değişikliğin ortasında verimli iş akışlarını ve yüksek kaliteli çıktıları sürdürmek için iOS için CI sistemleri .

Sorun: Intel'den iOS ARM Mimarisine Geçiş

Apple, M1 çipini 2020'de piyasaya sürdü

Göç süreci iki ana kola ayrıldı.

1. Mevcut Intel tabanlı geliştiricilerin bilgisayarlarının yeni M1 Macbook'larla değiştirilmesi

Bu sürecin nispeten basit olması gerekiyordu. Geliştiricinin tüm Intel Macbook'larını iki yıl içinde kademeli olarak değiştirmeye yönelik bir politika oluşturduk. Şu anda çalışanlarımızın %95'i ARM tabanlı Macbook kullanıyor.

Ancak bu süreçte beklenmedik bazı zorluklarla karşılaştık. 2021'in ortasında M1 Mac sıkıntısı, değiştirme sürecimizi yavaşlattı. 2021'in sonunda, neredeyse 200 bekleyen Macbook'tan yalnızca bir avuç dolusu Macbook'u değiştirebildik. Şirketin tüm Intel Mac'lerini, iOS dışı mühendisler de dahil olmak üzere M1 Macbook'larla tamamen değiştirmenin yaklaşık iki yıl süreceğini tahmin ettik.

Neyse ki Apple yeni M1 Pro ve M2 çiplerini piyasaya sürdü. Sonuç olarak odak noktamızı Intel'leri M1 Mac'lerle değiştirmek yerine M1 Pro ve M2 çipleriyle değiştirmeye kaydırdık.

Yazılımın geçişe hazır olmaması geliştiricilerin hayal kırıklığına uğramasına neden oldu

Yeni M1 Macbook'ları alan ilk mühendisler, yazılımların çoğunun Apple'ın yeni iOS ARM mimarisine geçmeye hazır olmaması nedeniyle zor zamanlar geçirdi. Diğer birçok Rubygem'e dayanan bağımlılık yönetimi araçları olan Rubygems ve Cocoapods gibi üçüncü taraf araçlar en çok etkilenenler oldu. O zamanlar bu araçlardan bazıları iOS ARM mimarisi için derlenmemişti, dolayısıyla yazılımların çoğunun Rosetta kullanılarak çalıştırılması gerekiyordu, bu da performans sorunlarına ve hayal kırıklığına neden oluyordu.

Ancak yazılım yaratıcıları bu sorunların çoğunu ortaya çıktıkça çözmeye çalıştılar. Çığır açan an, artık Rosetta desteğine sahip olmayan Xcode 14.3'ün piyasaya sürülmesiyle geldi. Bu, tüm yazılım geliştiricilere Apple'ın Intel'den iOS ARM mimarisine geçiş için baskı yaptığının açık bir sinyaliydi. Bu, daha önce Rosetta'ya güvenen çoğu 3. taraf yazılım geliştiricisini, yazılımlarını ARM'e taşımaya zorladı. Günümüzde Miquido'da günlük olarak kullanılan 3. parti yazılımların %99'u Rosetta olmadan çalışmaktadır.

2. Miquido'nun CI sisteminin iOS için değiştirilmesi

Miquido'da sürekli entegrasyonlu iOS sistemini değiştirmenin, makineleri değiştirmekten daha karmaşık bir iş olduğu ortaya çıktı. Öncelikle, lütfen o zamanki altyapımıza bir göz atın:

Miquido'daki CI mimarisi. iOS dönüşümü için CI sistemi.

Bir Gitlab bulut örneğimiz ve ona bağlı 9 Intel tabanlı Mac Mini'miz vardı. Bu makineler iş yürütücü olarak hizmet veriyordu ve orkestrasyondan Gitlab sorumluydu. Bir CI işi kuyruğa alındığında Gitlab, onu gitlab-ci.yml dosyasında belirtilen proje gereksinimlerini karşılayan ilk kullanılabilir çalıştırıcıya atadı. Gitlab, tüm derleme komutlarını, değişkenleri, yolları vb. içeren bir iş komut dosyası oluşturacaktı. Bu komut dosyası daha sonra çalıştırıcıya taşındı ve o makinede yürütüldü.

Bu kurulum sağlam görünse de Intel işlemcilerin zayıf desteği nedeniyle sanallaştırmayla ilgili sorunlarla karşılaştık. Sonuç olarak Docker gibi sanallaştırmayı kullanmamaya ve işleri fiziksel makinelerde yürütmeye karar verdik. Docker'ı temel alan verimli ve güvenilir bir çözüm oluşturmaya çalıştık, ancak GPU hızlandırma eksikliği gibi sanallaştırma sınırlamaları, işlerin yürütülmesinin fiziksel makinelere göre iki kat daha uzun sürmesine neden oldu. Bu, daha fazla yüke ve kuyrukların hızla dolmasına yol açtı.

MacOS SLA'sı nedeniyle aynı anda yalnızca iki VM kurabildik. Bu nedenle, fiziksel çalıştırıcı havuzunu genişletmeye ve onları Gitlab işlerini doğrudan işletim sistemlerinde yürütecek şekilde ayarlamaya karar verdik. Ancak bu yaklaşımın bazı dezavantajları da vardı.

Oluşturma Süreci ve Çalıştırıcı Yönetimindeki Zorluklar

  1. Yapı dizini sanal alanının dışındaki yapıların izolasyonu yoktur.

Çalıştırıcı her yapıyı fiziksel bir makinede yürütür; bu, yapıların yapı dizini sanal alanından yalıtılmadığı anlamına gelir. Bunun avantajları ve dezavantajları var. Bir yandan, çoğu proje aynı üçüncü taraf bağımlılık kümesini kullandığından, derlemeleri hızlandırmak için sistem önbelleklerini kullanabiliriz.

Öte yandan, bir projeden kalanlar diğer tüm projeleri etkileyebileceğinden önbellek sürdürülemez hale gelir. Hem Flutter hem de React Native geliştirme için aynı çalıştırıcılar kullanıldığından, bu özellikle sistem çapındaki önbellekler için önemlidir. Özellikle React Native, NPM aracılığıyla önbelleğe alınan birçok bağımlılığı gerektirir.

  1. Potansiyel sistem aracı karmaşası.

Her iki iş de sudo ayrıcalıklarıyla yürütülmemiş olmasına rağmen, Ruby gibi bazı sistem veya kullanıcı araçlarına erişmeleri hâlâ mümkündü. Bu, özellikle macOS'un, bazı eski Xcode özellikleri de dahil olmak üzere bazı eski yazılımları için Ruby'yi kullanması nedeniyle, bu araçlardan bazılarının kırılmasına yönelik potansiyel bir tehdit oluşturuyordu. Ruby'nin sistem sürümüyle uğraşmak isteyeceğiniz bir şey değil.

Ancak rbenv'in tanıtılması, başa çıkılması gereken başka bir karmaşıklık katmanı yaratır. Rubygem'lerin Ruby sürümüne göre kurulduğunu ve bu gemlerden bazılarının Ruby'nin belirli sürümlerini gerektirdiğini unutmamak önemlidir. Kullandığımız üçüncü taraf araçların neredeyse tamamı Ruby'ye bağlıydı; ana aktörler Cocoapod'lar ve Fastlane'di.

  1. İmza kimliklerini yönetme.

Koşuculardaki sistem anahtarlıkları söz konusu olduğunda, çeşitli müşteri geliştirme hesaplarından birden fazla imzalama kimliğini yönetmek zor olabilir. İmza kimliği, uygulamayı birlikte tasarlamamıza olanak tanıdığı ve uygulamayı potansiyel tehditlere karşı savunmasız hale getirdiği için son derece hassas bir veri parçasıdır.

Güvenliği sağlamak için kimlikler projeler genelinde korumalı alana alınmalı ve korunmalıdır. Ancak macOS'un anahtarlık uygulamasında getirdiği ek karmaşıklık göz önüne alındığında bu süreç bir kabusa dönüşebilir.

  1. Çok projeli ortamlardaki zorluklar.

Tüm projeler aynı araçlar, özellikle de Xcode kullanılarak oluşturulmamıştır. Özellikle destek aşamasındaki bazı projeler, projenin geliştirildiği Xcode'un son sürümü kullanılarak sürdürüldü. Bu, eğer bu projelerde herhangi bir çalışma yapılması gerekiyorsa, CI'nin bunu inşa edebilmesi gerektiği anlamına geliyor. Sonuç olarak koşucular aynı anda birden fazla Xcode sürümünü desteklemek zorunda kaldı ve bu da belirli bir iş için mevcut koşucu sayısını etkili bir şekilde daralttı.

5. Ekstra çaba gerekiyor.

Yazılım kurulumu gibi koşucular arasında yapılan herhangi bir değişiklik, tüm koşucularda aynı anda gerçekleştirilmelidir. Bunun için bir otomasyon aracımız olmasına rağmen otomasyon komut dosyalarının bakımı ekstra çaba gerektiriyordu.

Farklı müşteri ihtiyaçlarına yönelik özelleştirilmiş altyapı çözümleri

Miquido, farklı ihtiyaçları olan birden fazla istemciyle çalışan bir yazılım evidir. Hizmetlerimizi her müşterinin özel gereksinimlerini karşılayacak şekilde özelleştiriyoruz. Küçük işletmeler veya start-up'lar için kod tabanını ve gerekli altyapıyı çoğunlukla barındırıyoruz, çünkü bu işletmelerin bunu sürdürmek için gereken kaynaklara veya bilgiye sahip olmamaları muhtemeldir.

Kurumsal müşterilerin genellikle projelerini barındırmak için kendi altyapıları vardır. Ancak bazılarının bunu yapma kapasitesi yoktur veya sektör düzenlemeleri nedeniyle altyapılarını kullanmakla yükümlüdürler. Ayrıca Xcode Cloud veya Codemagic gibi 3. taraf SaaS hizmetlerini kullanmamayı da tercih ediyorlar. Bunun yerine mevcut mimarilerine uygun bir çözüm istiyorlar.

Bu müşterilere uyum sağlamak için projeleri genellikle altyapımızda barındırıyoruz veya altyapılarında aynı sürekli entegrasyonlu iOS yapılandırmasını kuruyoruz. Ancak imza kimlikleri gibi hassas bilgi ve dosyalarla uğraşırken ekstra özen gösteriyoruz.

Verimli yapı yönetimi için Fastlane'den yararlanma

Burada Fastlane kullanışlı bir araç olarak geliyor. Süreci kolaylaştırmaya ve farklı istemciler arasında ayırmaya yardımcı olan, eylemler adı verilen çeşitli modüllerden oluşur. Eşleştirme adı verilen bu eylemlerden biri, geliştirme ve üretim imzalama kimliklerinin yanı sıra temel hazırlık profillerinin korunmasına yardımcı olur. Ayrıca, bu kimlikleri derleme süresi boyunca ayrı anahtarlıklarda ayırmak için işletim sistemi düzeyinde de çalışır ve derlemeden sonra bir temizleme gerçekleştirir; bu, tüm derlemelerimizi fiziksel makinelerde çalıştırdığımız için ekstra faydalıdır.

Fastlane: geliştirme otomasyon aracı
Resim Kredisi: Fastlane

Başlangıçta belirli bir nedenden dolayı Fastlane'e başvurduk ancak bizim için yararlı olabilecek ek özelliklere sahip olduğunu keşfettik.

  1. Testflight'a Yükleme Oluşturun

Geçmişte AppStoreConnect API, geliştiricilerin kullanımına açık değildi. Bu, Testflight'a bir yapı yüklemenin tek yolunun Xcode veya Fastlane kullanmak olduğu anlamına geliyordu. Fastlane, esasen ASC API'sini sıyıran ve onu pilot adı verilen bir eyleme dönüştüren bir araçtı. Ancak bu yöntem genellikle bir sonraki Xcode güncellemesinde bozuldu. Bir geliştirici, yapılarını komut satırını kullanarak Testflight'a yüklemek isterse Fastlane mevcut en iyi seçenekti.

  1. Xcode Sürümleri Arasında Kolay Geçiş

Tek bir makinede birden fazla Xcode örneğinin olması nedeniyle derleme için hangi Xcode'un kullanılacağını seçmek gerekiyordu. Ne yazık ki Apple, Xcode sürümleri arasında geçiş yapmayı zahmetli hale getirdi; bunu yapmak için 'xcode-select'i kullanmanız gerekir, bu da ayrıca sudo ayrıcalıkları gerektirir. Fastlane bunu da kapsıyor.

  1. Geliştiriciler için Ek Yardımcı Programlar

Fastlane, sürüm oluşturma ve derleme sonuçlarını web kancalarına gönderme yeteneği de dahil olmak üzere birçok başka yararlı yardımcı program sağlar.

Fastlane'in dezavantajları

Fastlane'i projelerimize uyarlamak sağlam ve sağlamdı, biz de o yöne gittik. Birkaç yıl başarıyla kullandık. Ancak bu yıllar içinde birkaç sorun tespit ettik:

  1. Fastlane Ruby bilgisi gerektirir.

Fastlane, Ruby ile yazılmış bir araçtır ve onu etkili bir şekilde kullanmak için iyi bir Ruby bilgisi gerektirir. Fastlane yapılandırmanızda veya aracın kendisinde hatalar olduğunda, bunları irb veya pry kullanarak hata ayıklamak oldukça zor olabilir.

  1. Çok sayıda mücevhere bağımlılık.

Fastlane'in kendisi yaklaşık 70 mücevhere dayanıyor. Ruby sisteminin bozulması riskini azaltmak için projelerde yerel paketleyici mücevherleri kullanılıyordu. Tüm bu mücevherleri getirmek çok fazla zaman kaybı yarattı.

  1. Sistem Ruby ve rubygem sorunları.

Sonuç olarak, Ruby sistemi ve rubygem'lerle ilgili daha önce bahsedilen tüm sorunlar burada da geçerlidir.

  1. Flutter projeleri için yedeklilik.

Flutter projeleri ayrıca iOS projeleriyle uyumluluğu korumak ve koşucunun anahtarlıklarını korumak için fastlane eşleşmesini kullanmak zorunda kaldı. Flutter'ın kendi inşa sistemi yerleşik olduğundan ve daha önce bahsedilen ek yük yalnızca imzalama kimliklerini ve ön hazırlık profillerini yönetmek için getirildiğinden, bu son derece gereksizdi.

Bu sorunların çoğu süreç içinde düzeltildi ancak daha sağlam ve güvenilir bir çözüme ihtiyacımız vardı.

Fikir: iOS için yeni, daha sağlam bir sürekli entegrasyon aracının uyarlanması

İyi haber şu ki Apple, çip mimarisi üzerinde tam kontrol sahibi oldu ve macOS için yeni bir sanallaştırma çerçevesi geliştirdi. Bu çerçeve, kullanıcıların hızlı bir şekilde başlayan ve yerel benzeri bir performansla karakterize edilen Linux veya macOS sanal makinelerini oluşturmasına, yapılandırmasına ve çalıştırmasına olanak tanır - ve gerçekten yerel benzeri demek istiyorum.

Bu umut verici görünüyordu ve iOS'a yönelik yeni sürekli entegrasyon araçlarımız için bir temel taşı olabilirdi. Ancak bu, tam çözümün yalnızca bir kısmıydı. Bir VM yönetim aracına sahip olduğumuzdan, bu çerçeveyi Gitlab çalıştırıcılarımızla koordineli olarak kullanabilecek bir şeye de ihtiyacımız vardı.

Bu durumda, zayıf sanallaştırma performansıyla ilgili sorunlarımızın çoğu geçerliliğini yitirecektir. Ayrıca Fastlane ile çözmeyi amaçladığımız sorunların çoğunu otomatik olarak çözmemize de olanak tanıyacak.

İsteğe bağlı imzalama kimliği yönetimi için özel bir çözüm geliştirme

Çözmemiz gereken son bir sorunumuz vardı: Kimlik yönetimini imzalamak. İhtiyaçlarımıza göre aşırı göründüğü için Fastlane'i bunun için kullanmak istemedik. Bunun yerine gereksinimlerimize daha uygun bir çözüm arıyorduk. İhtiyaçlarımız basitti: Kimlik yönetimi sürecinin, anahtarlığa önceden yüklenmiş herhangi bir kimlik olmadan, yalnızca oluşturma süresi için talep üzerine yapılması ve üzerinde çalışacağı herhangi bir makineyle uyumlu olması gerekiyordu.

Dağıtım sorunu ve kararlı AppstoreConnect API eksikliği, Apple'ın kullanıcılar ile ASC arasında iletişime izin veren "altool"unu piyasaya sürmesiyle geçerliliğini yitirdi.

Bu yüzden bir fikrimiz vardı ve bu üç hususu birbirine bağlamanın bir yolunu bulmamız gerekiyordu:

  1. Apple'ın Sanallaştırma çerçevesini kullanmanın bir yolunu bulmak.
  2. Gitlab koşucularıyla çalışmasını sağlamak.
  3. Birden fazla proje ve çalıştırıcıda kimlik yönetimi imzalamaya yönelik bir çözüm bulma.

Çözüm: Yaklaşımımıza kısa bir bakış (araçlar dahil)

Daha önce bahsettiğimiz tüm sorunlara çözüm aramaya başladık.

  1. Apple'ın Sanallaştırma çerçevesini kullanma.

İlk engele oldukça hızlı bir şekilde çözüm bulduk: Cirrus Labs'ın tart aracına rastladık. İlk andan itibaren bunun bizim seçimimiz olacağını biliyorduk.

Cirrus Lab tarafından sunulan tart aracını kullanmanın en önemli avantajları şunlardır:

  • Ham .ipsw görüntülerinden vms oluşturma imkanı.
  • Cirrus Labs GitHub sayfasında bulunan, önceden paketlenmiş şablonları (brew veya Xcode gibi bazı yardımcı araçlar yüklü olarak) kullanarak sanal makineler oluşturma olanağı.
  • Tart aracı, dinamik görüntü oluşturma desteği için paketleyiciyi kullanır.
  • Tart aracı hem Linux hem de MacOS görüntülerini destekler.
  • Araç, APFS dosya sisteminin, aslında disk alanı ayırmadan dosyaların çoğaltılmasını sağlayan olağanüstü bir özelliğini kullanır. Bu sayede orijinal imaj boyutunun 3 katı kadar disk alanı ayırmanıza gerek kalmaz. Yalnızca orijinal görüntü için yeterli disk alanına ihtiyacınız vardır; klon ise yalnızca kendisiyle orijinal görüntü arasındaki fark kadar olan alanı kaplar. Bu son derece faydalıdır, özellikle de macOS görüntüleri oldukça büyük olma eğiliminde olduğundan.

Örneğin, Xcode ve diğer yardımcı programların yüklü olduğu çalışır durumdaki bir macOS Ventura görüntüsü, minimum 60 GB disk alanı gerektirir. Normal şartlarda, bir görüntü ve onun iki klonu 180 GB'a kadar disk alanı kaplar ve bu önemli bir miktardır. Birden fazla orijinal görüntüye sahip olmak veya tek bir VM'ye birden fazla Xcode sürümü yüklemek isteyebilirsiniz, bu da boyutu daha da artıracaktır.

  • Araç, orijinal ve klonlanmış VM'ler için IP adresi yönetimine olanak tanıyarak VM'lere SSH erişimine olanak tanır.
  • Ana makine ile VM'ler arasında dizinleri çapraz bağlama yeteneği.
  • Araç kullanıcı dostudur ve çok basit bir CLI'ye sahiptir.

Bu aracın VM yönetimi için kullanılması açısından eksik olduğu neredeyse hiçbir şey yok. Tek bir şey dışında pek bir şey olmadı: Her ne kadar umut verici olsa da, anında görüntü oluşturmaya yönelik paketleyici eklentisi aşırı derecede zaman alıyordu, bu yüzden onu kullanmamaya karar verdik.

Tart denedik ve fevkalade işe yaradı. Performansı yerel gibiydi ve yönetimi kolaydı.

Etkileyici sonuçlarla başarılı bir şekilde tartı entegre ettikten sonra diğer zorlukların üstesinden gelmeye odaklandık.

  1. Turtayı Gitlab koşucularıyla birleştirmenin bir yolunu bulmak.

İlk sayıyı çözdükten sonra turtayı Gitlab koşucularıyla nasıl birleştireceğimiz sorusuyla karşılaştık.

Gitlab koşucularının gerçekte ne yaptığını açıklayarak başlayalım:

Gitlab iş delegasyonunun basitleştirilmiş diyagramı. iOS için CI sistemi

Görevlerin koşucu ana bilgisayardan VM'ye tahsis edilmesini içeren fazladan bir bulmacayı diyagrama eklememiz gerekiyordu. GitLab işi, önemli değişkenleri, PATH girişlerini ve komutları tutan bir kabuk betiğidir.

Amacımız bu betiği sanal makineye aktarıp çalıştırmaktı.

Ancak bu görevin başlangıçta düşündüğümüzden daha zorlu olduğu ortaya çıktı.

Koşucu

Docker veya SSH gibi standart Gitlab çalıştırıcı yürütücülerinin kurulumu basittir ve çok az yapılandırma gerektirir veya hiç yapılandırma gerektirmez. Ancak yapılandırma üzerinde daha fazla kontrole ihtiyacımız vardı ve bu da bizi GitLab tarafından sağlanan özel yürütücüleri keşfetmeye yöneltti.

Özel yürütücüler, her bir çalıştırıcı adımının (hazırlama, yürütme, temizleme) bir kabuk komut dosyası biçiminde açıklanması nedeniyle standart olmayan yapılandırmalar için mükemmel bir seçenektir. Eksik olan tek şey, ihtiyacımız olan görevleri gerçekleştirebilecek ve çalıştırıcı yapılandırma komut dosyalarında yürütülebilecek bir komut satırı aracıydı.

Şu anda tam olarak bunu yapan birkaç araç mevcut; örneğin CirrusLabs Gitlab tart yürütücüsü. Bu araç o zamanlar tam da aradığımız şeydi. Ancak henüz mevcut değildi ve araştırma yaptıktan sonra görevimizi gerçekleştirmemize yardımcı olabilecek herhangi bir araç bulamadık.

Kendi çözümünü yazmak

Mükemmel bir çözüm bulamadığımız için kendimiz yazdık. Sonuçta biz mühendisiz! Fikir sağlam görünüyordu ve gerekli tüm araçlara sahiptik, bu yüzden geliştirmeye devam ettik.

Swift'i ve Apple tarafından sağlanan birkaç açık kaynak kitaplığı kullanmayı seçtik: Komut satırı yürütmeyi yönetmek için Swift Argument Ayrıştırıcı ve VM'lerle SSH bağlantısını yönetmek için Swift NIO. Geliştirmeye başladık ve birkaç gün içinde, sonunda MQVMRunner'a dönüşecek olan bir aracın ilk çalışan prototipini elde ettik.

iOS CI altyapısı: MQVMRunner

Araç yüksek düzeyde şu şekilde çalışır:

  1. (Adım hazırlayın)
    1. gitlab-ci.yml dosyasında sağlanan değişkenleri okuyun (görüntü adı ve ek değişkenler).
    2. İstenen VM tabanını seçin
    3. İstenen VM tabanını klonlayın.
    4. Çapraz bağlı bir dizin oluşturun ve Gitlab iş komut dosyasını bunun için gerekli izinleri ayarlayarak kopyalayın.
    5. Klonu çalıştırın ve SSH bağlantısını kontrol edin.
    6. Gerekirse gerekli bağımlılıkları (Xcode sürümü gibi) ayarlayın.
  2. (Adımını yürütün)
    1. Gitlab işini, SSH aracılığıyla hazırlanmış bir VM klonunda çapraz bağlı bir dizinden bir komut dosyası yürüterek çalıştırın.
  3. (Temizleme adımı)
    1. Klonlanmış görüntüyü silin.

Geliştirmedeki zorluklar

Geliştirme sırasında çeşitli sorunlarla karşılaştık ve bu da sürecin istediğimiz kadar sorunsuz ilerlememesine neden oldu.

  1. IP adresi yönetimi.

IP adreslerini yönetmek, dikkatle ele alınması gereken çok önemli bir iştir. Prototipte SSH işleme, doğrudan ve sabit kodlu SSH kabuk komutları kullanılarak uygulandı. Ancak etkileşimli olmayan kabuklar durumunda anahtar kimlik doğrulaması önerilir. Ek olarak, kesintileri önlemek için ana bilgisayarı bilinen_hosts dosyasına eklemeniz önerilir. Bununla birlikte, sanal makinelerin IP adreslerinin dinamik yönetimi nedeniyle, belirli bir IP için girişi iki katına çıkararak hatalara yol açma olasılığı vardır. Bu nedenle bu tür sorunları önlemek için bilinen_ana bilgisayarları belirli bir iş için dinamik olarak atamamız gerekir.

  1. Saf Swift çözümü.

Bunu ve Swift kodundaki sabit kodlanmış kabuk komutlarının pek de zarif olmadığı gerçeğini göz önünde bulundurarak, özel bir Swift kütüphanesi kullanmanın güzel olacağını düşündük ve Swift NIO'yu kullanmaya karar verdik. Bazı sorunları çözdük, ancak aynı zamanda birkaç yeni sorun da ekledik; örneğin, bazen stdout'a yerleştirilen günlükler, komutun yürütülmesinin bitmesi nedeniyle SSH kanalı sonlandırıldıktan * sonra* aktarılıyor - ve bizim temel aldığımız gibi Daha sonraki çalışmalarda bu çıktı, yürütmenin rastgele başarısız olmasına neden oldu.

  1. Xcode sürüm seçimi.

Packer eklentisi, zaman tüketimi nedeniyle dinamik görüntü oluşturma için bir seçenek olmadığından, birden çok Xcode sürümünün önceden yüklendiği tek bir VM tabanını kullanmaya karar verdik. Geliştiricilerin ihtiyaç duydukları Xcode sürümünü gitlab-ci.yml dosyasında belirtmelerinin bir yolunu bulmamız gerekiyordu ve herhangi bir projede kullanılabilecek özel değişkenler bulduk. MQVMRunner daha sonra ilgili Xcode sürümünü ayarlamak için klonlanmış bir VM üzerinde "xcode-select" komutunu çalıştıracaktır.

Ve çok, çok daha fazlası

Proje Geçişini Kolaylaştırma ve iOS İş Akışı için Mac Studios ile sürekli entegrasyon

Bunu iki yeni Mac Studio'da kurduk ve projeleri taşımaya başladık. Geliştiricilerimiz için geçiş sürecini mümkün olduğunca şeffaf hale getirmek istedik. Tamamen kusursuz hale getiremedik ama sonunda gitlab-ci.yml'de yalnızca birkaç şey yapmaları gereken noktaya geldik:

  • Koşucuların etiketleri: Intel yerine Mac Studios kullanmak.
  • Görüntünün adı: birden fazla temel VM'ye ihtiyaç duymamız durumunda gelecekteki uyumluluk için sunulan isteğe bağlı parametre. Şu anda her zaman varsayılan olarak sahip olduğumuz tek tabanlı VM'dir.
  • Xcode sürümü: isteğe bağlı parametre; sağlanmadığı takdirde mevcut en yeni sürüm kullanılacaktır.

Araç ilk başta çok iyi geri bildirimler aldı, bu yüzden onu açık kaynak yapmaya karar verdik. Gitlab Custom Runner'ı ve gerekli tüm eylemleri ve değişkenleri ayarlamak için bir kurulum komut dosyası ekledik. Aracımızı kullanarak, kendi GitLab çalıştırıcınızı birkaç dakika içinde kurabilirsiniz; ihtiyacınız olan tek şey, işlerin yürütüleceği tart ve temel VM'dir.

İOS yapısı için son sürekli entegrasyon aşağıdaki gibi görünür:

Nihai CI altyapısı: MQVMRunner

3. Etkin kimlik yönetimi için çözüm

Müşterilerimizin imza kimliklerini yönetmek için etkili bir çözüm bulmakta zorlanıyoruz. İmzalayan kimlik son derece gizli veriler olduğundan ve güvenli olmayan bir yerde gerekenden daha uzun süre saklanmaması gerektiğinden, bu özellikle zorlayıcıydı.

Ek olarak, bu kimlikleri projeler arası çözümler olmadan yalnızca derleme süresi boyunca yüklemek istedik. Bu, kimliğe uygulama (veya derleme) sanal alanının dışında erişilmemesi gerektiği anlamına geliyordu. VM'lere geçerek ikinci sorunu zaten ele aldık. Ancak yine de imzalama kimliğini yalnızca derleme süresi boyunca sanal makineye depolamanın ve yüklemenin bir yolunu bulmamız gerekiyordu.

Fastlane Match ile ilgili sorunlar

O zamanlar şifrelenmiş kimlikleri ve provizyonları ayrı bir depoda saklayan, bunları derleme süreci sırasında ayrı bir anahtarlık örneğine yükleyen ve derlemeden sonra bu örneği kaldıran Fastlane eşleşmesini hâlâ kullanıyorduk.

Bu yaklaşım uygun gibi görünse de bazı sorunları var:

  • Tüm Fastlane kurulumunun çalışması gerekir.

Fastlane yakuttur ve ilk bölümde listelenen tüm konular burada da geçerlidir.

  • Derleme sırasında deponun kullanıma alınması.

Kimliklerimizi kurulum süreci yerine derleme süreci sırasında kontrol edilen ayrı bir depoda tuttuk. Bu, özel üçüncü taraf bağımlılıklarını nasıl ele aldığımıza benzer şekilde, kimlik deposuna yalnızca Gitlab için değil, belirli koşucular için ayrı erişim kurmamız gerektiği anlamına geliyordu.

  • Match dışında yönetmek zor.

Match'i kimlikleri yönetmek veya temel hazırlık yapmak için kullanıyorsanız, manuel müdahaleye çok az ihtiyaç vardır veya hiç gerek yoktur. Profilleri manuel olarak düzenlemek, şifresini çözmek ve şifrelemek, böylece eşleşmelerin daha sonra onlarla çalışmaya devam edebilmesi sıkıcı ve zaman alıcıdır. Bu işlemi gerçekleştirmek için Fastlane'i kullanmak genellikle uygulama sağlama kurulumunun tamamen silinmesi ve yeni bir tane oluşturulmasıyla sonuçlanır.

  • Hata ayıklamak biraz zor.

Herhangi bir kod imzalama sorunu olması durumunda, ilk önce bunların kodunu çözmeniz gerekeceğinden yeni yüklenen kimlik ve temel hazırlık eşleşmesini belirlemekte zorlanabilirsiniz.

  • Güvenlik endişeleri.

Erişilen geliştirici hesaplarını, onlar adına değişiklik yapmak için sağlanan kimlik bilgilerini kullanarak eşleştirin. Fastlane açık kaynak olmasına rağmen bazı müşteriler güvenlik endişeleri nedeniyle bunu reddetti.

  • Son olarak Match'ten kurtulmak, Fastlane'den tamamen kurtulmamızın önündeki en büyük engeli de ortadan kaldıracaktır.

Başlangıçtaki gereksinimlerimiz şunlardı:

  • Yükleme, kimliğin güvenli bir yerden, tercihen düz metin olmayan bir biçimde imzalanmasını ve anahtarlığa yerleştirilmesini gerektirir.
  • Bu kimliğe Xcode tarafından erişilebilir olmalıdır.
  • Tercihen kimlik parolası, anahtarlık adı ve anahtarlık parolası değişkenleri hata ayıklama amacıyla ayarlanabilir olmalıdır.

Match ihtiyacımız olan her şeye sahipti, ancak Fastlane'i sadece Match'i kullanmak için uygulamak, özellikle kendi derleme sistemlerine sahip platformlar arası çözümler için aşırıya kaçma gibi görünüyordu. Match'e benzer bir şey istedik ama ağır Ruby yükü olmadan taşıyordu.

Kendi çözümünü yaratmak

Biz de düşündük – hadi bunu kendimiz yazalım! Bunu MQVMRunner ile yaptık, burada da yapabiliriz. Ayrıca bunu yapmak için Swift'i seçtik, çünkü esas olarak Apple Güvenlik çerçevesini kullanarak gerekli birçok API'yi ücretsiz olarak alabiliyorduk.

Tabii her şey beklendiği kadar sorunsuz gitmedi.

  • Güvenlik çerçevesi mevcut.

En kolay strateji, Fastlane'in yaptığı gibi bash komutlarını çağırmaktı. Ancak Güvenlik çerçevesinin mevcut olması nedeniyle geliştirme için kullanılmasının daha şık olacağını düşündük.

  • Tecrübe eksikliği.

MacOS için Güvenlik çerçevesi konusunda pek tecrübeli değildik ve bunun iOS'ta alışık olduğumuzdan önemli ölçüde farklı olduğu ortaya çıktı. Bu, macOS sınırlamalarının farkında olmadığımız veya iOS'takiyle aynı şekilde çalıştığını varsaydığımız birçok durumda bize geri tepti; bu varsayımların çoğu yanlıştı.

  • Korkunç belgeler.

Apple Güvenlik çerçevesinin belgeleri, en hafif deyimle mütevazıdır. Bu, OSX'in ilk sürümlerine kadar uzanan çok eski bir API ve bazen o zamandan beri güncellenmediği izlenimine kapıldık. Kodun büyük bir kısmı belgelenmemiştir ancak kaynak kodunu okuyarak nasıl çalıştığını tahmin ettik. Neyse ki bizim için açık kaynaklı.

  • Değiştirmeler olmadan kullanımdan kaldırmalar.

Bu çerçevenin büyük bir kısmı kullanımdan kaldırıldı; Apple, tipik "macOS tarzı" anahtarlıktan (şifreyle erişilebilen birden fazla anahtarlık) uzaklaşmaya ve "iOS tarzı" anahtarlığı (iCloud aracılığıyla senkronize edilen tek anahtarlık) uygulamaya çalışıyor. Bu nedenle, 2014 yılında macOS Yosemite'de bu özelliği kullanımdan kaldırdılar ancak son dokuz yılda bunun yerine geçecek herhangi bir şey bulamadılar; bu nedenle, elimizdeki tek API şimdilik kullanımdan kaldırıldı çünkü henüz yeni bir API yok.

İmzalama kimliklerinin proje başına Gitlab değişkenlerinde base64 kodlu dizeler olarak saklanabileceğini varsaydık. Güvenlidir, proje bazındadır ve maskelenmiş bir değişken olarak ayarlanırsa, derleme günlüklerinde düz metin olmayan bir şekilde okunabilir ve görüntülenebilir.

Yani kimlik verimiz vardı. Sadece anahtarlığa koymamız gerekiyordu. Güvenlik API'sini Kullanma Birkaç denemeden ve Güvenlik çerçevesi belgelerini inceledikten sonra, daha sonra MQSwiftSign olacak bir şeyin prototipini hazırladık.

MacOS Güvenlik sistemini öğrenmek ama zor yoldan

Aracımızı geliştirmek için macOS anahtar zincirinin nasıl çalıştığına dair derinlemesine bir anlayış kazanmamız gerekiyordu. Bu, anahtarlığın öğeleri nasıl yönettiğini, bunların erişimlerini ve izinlerini ve anahtarlık verilerinin yapısını araştırmayı içeriyordu. Örneğin, anahtarlığın, işletim sisteminin ACL kümesini yok saydığı tek macOS dosyası olduğunu keşfettik. Ek olarak, belirli anahtarlık öğelerindeki ACL'nin, bir anahtarlık dosyasına kaydedilen düz metin plist olduğunu öğrendik. Yol boyunca birçok zorlukla karşılaştık ama aynı zamanda çok şey öğrendik.

Karşılaştığımız önemli zorluklardan biri yönlendirmelerdi. Aracımız öncelikle CI iOS sistemlerinde çalışacak şekilde tasarlandı; bu da etkileşimli olmaması gerektiği anlamına geliyordu. Kullanıcılardan CI'da bir şifreyi onaylamalarını isteyemedik.

Ancak macOS güvenlik sistemi iyi tasarlanmış olup, imza kimliği de dahil olmak üzere gizli bilgilerin kullanıcının açık izni olmadan düzenlenmesini veya okunmasını imkansız hale getirir. Bir kaynağa onay olmadan erişmek için, erişen programın kaynağın Erişim Kontrol Listesine dahil edilmesi gerekir. Bu, sistemle birlikte gelen Apple programları dahil hiçbir programın bozamayacağı katı bir gerekliliktir. Herhangi bir programın bir anahtarlık girişini okuması veya düzenlemesi gerekiyorsa, kullanıcının kilidi açmak için bir anahtarlık şifresi sağlaması ve isteğe bağlı olarak bunu girişin ACL'sine eklemesi gerekir.

Kullanıcı İzni Zorluklarının Aşılması

Bu nedenle, Xcode'un, anahtar zincirimiz tarafından oluşturulan bir kimliğe, şifre istemini kullanarak kullanıcıdan izin istemeden erişmesinin bir yolunu bulmamız gerekiyordu. Bunu yapmak için bir öğenin erişim kontrol listesini değiştirebiliriz ancak bu aynı zamanda kullanıcının iznini de gerektirir ve elbette gerektirir. Aksi takdirde, ACL'ye sahip olmanın tüm amacını baltalayacaktır. Biz bu güvenlik önlemini atlamaya çalışıyoruz; 'güvenlik seti-anahtar-bölüm-listesi' komutuyla aynı etkiyi elde etmeye çalıştık.

Çerçeve belgelerini derinlemesine inceledikten sonra, kullanıcıdan bir parola girmesini istemeden ACL'yi düzenlemeye izin veren herhangi bir API bulamadık. Bulduğumuz en yakın şey, her seferinde bir kullanıcı arayüzü istemini tetikleyen 'SecKeychainItemSetAccess'. Sonra bir kez daha daldık ama bu sefer en iyi belgelere, yani kaynak kodun kendisine. Apple bunu nasıl uyguladı?

Beklenebileceği gibi özel bir API kullandıkları ortaya çıktı. 'SecKeychainItemSetAccessWithPassword' adı verilen bir yöntem, temelde 'SecKeychainItemSetAccess' ile aynı şeyi yapar, ancak kullanıcıdan parola istemek yerine parola, bir işleve argüman olarak sağlanır. Elbette - özel bir API olarak belgelerde listelenmiyor, ancak Apple sanki kişisel veya kurumsal kullanım için bir uygulama oluşturmayı düşünemiyormuş gibi bu tür API'lere ilişkin belgelerden yoksun. Aracın yalnızca dahili kullanıma yönelik olması gerektiğinden, özel API'yi kullanmaktan çekinmedik. Yapılması gereken tek şey C yöntemini Swift'e bağlamaktı.

Kullanıcı İzni Zorluklarının Aşılması

Yani prototipin son iş akışı şu şekildeydi:

  1. Otomatik kilit kapalıyken geçici kilitsiz anahtar zincirini oluşturun.
  2. Base64 kodlu imzalama kimliği verilerini çevresel değişkenlerden (Gitlab tarafından aktarılan) alın ve kodunu çözün.
  3. Kimliği oluşturulan anahtarlığa aktarın.
  4. Xcode ve diğer araçların ortak tasarım amacıyla okuyabilmesi için içe aktarılan kimlik için uygun erişim seçeneklerini ayarlayın.

Daha fazla yükseltme

Prototip iyi çalışıyordu, bu nedenle araca eklemek istediğimiz birkaç ek özellik belirledik. Amacımız eninde sonunda fastlane'i değiştirmekti; 'eşleştirme' eylemini zaten uyguladık. Ancak fastlane, henüz sahip olmadığımız iki değerli özelliği sunmaya devam ediyordu: ön hazırlık profili kurulumu ve Export.plist oluşturma.

Profil Kurulumu Sağlama

Ön hazırlık profili kurulumu oldukça basittir - profil UUID'sinin çıkarılması ve dosyayı dosya adı olarak UUID ile `~/Library/MobileDevice/Provisioning Profiles/` konumuna kopyalamaktan ibarettir - ve bu, Xcode'un onu düzgün bir şekilde görmesi için yeterlidir. Sağlanan dizin üzerinde döngü yapmak ve bunu içinde bulduğu her .mobileprovision dosyası için yapmak için aracımıza basit bir eklenti eklemek roket bilimi değildir.

Export.plist Oluşturma

Ancak, Export.plist'in oluşturulması biraz daha zordur. Uygun bir IPA dosyası oluşturmak için Xcode, kullanıcıların çeşitli kaynaklardan (proje dosyası, yetkilendirme listesi, çalışma alanı ayarları vb.) toplanan belirli bilgileri içeren bir plist dosyası sağlamasını gerektirir. Xcode'un bu verileri yalnızca dağıtım sihirbazı aracılığıyla toplayabilmesinin ancak toplayamamasının nedeni CLI aracılığıyla benim için bilinmiyor. Ancak bunları Swift API'lerini kullanarak, yalnızca proje/çalışma alanı referanslarına ve Xcode proje dosyasının nasıl oluşturulduğuna dair küçük bir bilgi birikimine sahip olarak toplayacaktık.

Sonuç beklenenden daha iyiydi, bu yüzden onu aracımıza başka bir eklenti olarak eklemeye karar verdik. Ayrıca bunu daha geniş bir kitleye açık kaynaklı bir proje olarak yayınladık. Şu anda MQSwiftSign, iOS uygulamanızı oluşturmak ve dağıtmak için gereken temel fastlane eylemlerinin yerine başarılı bir şekilde kullanılabilecek çok amaçlı bir araçtır ve bunu Miquido'daki her projemizde kullanıyoruz.

Son Düşünceler: Başarı

Intel'den iOS ARM mimarisine geçiş zorlu bir işti. Belge eksikliği nedeniyle çok sayıda engelle karşılaştık ve araçları geliştirmek için önemli miktarda zaman harcadık. Ancak sonuçta sağlam bir sistem kurduk:

  • Yönetilecek dokuz yerine iki koşucu;
  • Tamamen bizim kontrolümüz altında olan yazılımı, yakut taşları şeklinde bir ton ek yük olmadan çalıştırarak, yapı konfigürasyonlarımızda fastlane veya herhangi bir 3. parti yazılımdan kurtulmayı başardık;
  • MacOS sistem güvenliği ve Güvenlik çerçevesinin kendisi, gerçek bir Xcode proje yapısı ve çok daha fazlası gibi, genellikle dikkat etmediğimiz şeylere ilişkin BİRÇOK bilgi ve anlayış.

Sizi memnuniyetle tavsiye ederim – iOS yapıları için GitLab çalıştırıcınızı ayarlamakta zorlanıyorsanız MQVMRunner'ımızı deneyin. Uygulamanızı tek bir araç kullanarak oluşturma ve dağıtma konusunda yardıma ihtiyacınız varsa ve rubygem'lere güvenmek istemiyorsanız MQSwiftSign'ı deneyin. Benim için çalışıyor, sizin için de işe yarayabilir!