Sistem CI untuk pengembangan iOS: transformasi Intel ke ARM

Diterbitkan: 2024-02-14

Dalam lanskap teknologi yang terus berkembang, perusahaan harus beradaptasi terhadap angin perubahan agar tetap relevan dan kompetitif. Salah satu transformasi yang menggemparkan dunia teknologi adalah transisi dari arsitektur Intel x86_64 ke arsitektur iOS ARM, yang dicontohkan oleh chip Apple M1 yang inovatif. Dalam konteks ini, sistem CI untuk iOS telah menjadi pertimbangan penting bagi perusahaan dalam menghadapi perubahan ini, memastikan bahwa proses pengembangan dan pengujian perangkat lunak tetap efisien dan terkini dengan standar teknologi terkini.

Apple mengumumkan chip M1-nya hampir tiga tahun lalu, dan sejak itu, sudah jelas bahwa perusahaan tersebut akan menggunakan arsitektur ARM dan pada akhirnya menghentikan dukungan untuk perangkat lunak berbasis Intel. Untuk menjaga kompatibilitas antar arsitektur, Apple telah memperkenalkan versi baru Rosetta, kerangka terjemahan biner miliknya, yang telah terbukti andal di masa lalu selama transformasi arsitektur signifikan dari PowerPC ke Intel pada tahun 2006. Transformasi tersebut masih berlangsung, dan kita telah melihat Xcode kehilangan dukungan Rosetta di versi 14.3.

Di Miquido, kami menyadari perlunya migrasi dari Intel ke ARM beberapa tahun yang lalu. Kami memulai persiapannya pada pertengahan tahun 2021. Sebagai rumah perangkat lunak dengan banyak klien, aplikasi, dan proyek yang berjalan secara bersamaan, kami menghadapi beberapa tantangan yang harus kami atasi. Artikel ini dapat menjadi panduan Anda jika perusahaan Anda menghadapi situasi serupa. Situasi dan solusi yang dijelaskan digambarkan dari perspektif pengembangan iOS – namun Anda mungkin juga menemukan wawasan yang cocok untuk teknologi lain. Komponen penting dari strategi transisi kami adalah memastikan sistem CI untuk iOS kami sepenuhnya dioptimalkan untuk arsitektur baru, dengan menyoroti pentingnya Sistem CI untuk iOS dalam menjaga alur kerja yang efisien dan output berkualitas tinggi di tengah perubahan yang signifikan.

Masalahnya: Migrasi Arsitektur Intel Ke iOS ARM

Apple merilis chip M1 mereka pada tahun 2020

Proses migrasi dibagi menjadi dua cabang utama.

1. Mengganti komputer pengembang berbasis Intel yang sudah ada dengan Macbook M1 baru

Proses ini seharusnya relatif sederhana. Kami menetapkan kebijakan untuk mengganti semua Macbook Intel milik pengembang secara bertahap selama dua tahun. Saat ini, 95% karyawan kami menggunakan Macbook berbasis ARM.

Namun, kami menemui beberapa tantangan tak terduga selama proses ini. Pada pertengahan tahun 2021, kekurangan M1 Mac memperlambat proses penggantian kami. Pada akhir tahun 2021, kami hanya mampu mengganti segelintir Macbook dari hampir 200 Macbook yang menunggu. Kami memperkirakan diperlukan waktu sekitar dua tahun untuk sepenuhnya mengganti semua Mac Intel perusahaan dengan Macbook M1, termasuk teknisi non-iOS.

Untungnya, Apple merilis chip M1 Pro dan M2 baru mereka. Hasilnya, kami mengalihkan fokus dari mengganti Intel dengan M1 Mac menjadi menggantinya dengan chip M1 Pro dan M2.

Perangkat lunak yang belum siap untuk peralihan menyebabkan frustrasi pengembang

Para insinyur pertama yang menerima Macbook M1 baru mengalami kesulitan karena sebagian besar perangkat lunak belum siap untuk beralih ke arsitektur iOS ARM baru Apple. Alat pihak ketiga seperti Rubygems dan Cocoapods, yang merupakan alat manajemen ketergantungan yang mengandalkan banyak Rubygem lainnya, adalah yang paling terpengaruh. Beberapa alat ini tidak dikompilasi untuk arsitektur iOS ARM, sehingga sebagian besar perangkat lunak harus dijalankan menggunakan Rosetta, sehingga menyebabkan masalah kinerja dan frustrasi.

Namun, pembuat perangkat lunak berupaya menyelesaikan sebagian besar masalah yang muncul. Momen terobosan datang dengan dirilisnya Xcode 14.3, yang tidak lagi mendapat dukungan Rosetta. Ini adalah sinyal yang jelas bagi semua pengembang perangkat lunak bahwa Apple sedang mendorong migrasi arsitektur Intel ke iOS ARM. Hal ini memaksa sebagian besar pengembang perangkat lunak pihak ketiga yang sebelumnya mengandalkan Rosetta untuk memigrasikan perangkat lunak mereka ke ARM. Saat ini, 99% perangkat lunak pihak ketiga yang digunakan di Miquido setiap hari berjalan tanpa Rosetta.

2. Mengganti sistem CI Miquido untuk iOS

Mengganti sistem integrasi berkelanjutan iOS di Miquido terbukti menjadi tugas yang lebih rumit daripada sekadar menukar mesin. Pertama, silakan lihat infrastruktur kami saat itu:

Arsitektur CI di Miquido. Sistem CI untuk transformasi iOS.

Kami memiliki instance cloud Gitlab dan 9 Mac Mini berbasis Intel yang terhubung dengannya. Mesin-mesin ini berfungsi sebagai pelari pekerjaan dan Gitlab bertanggung jawab atas orkestrasi. Setiap kali tugas CI dimasukkan dalam antrean, Gitlab menugaskannya ke runner pertama yang tersedia dan memenuhi persyaratan proyek yang ditentukan dalam file gitlab-ci.yml. Gitlab akan membuat skrip pekerjaan yang berisi semua perintah build, variabel, jalur, dll. Skrip ini kemudian dipindahkan ke runner dan dieksekusi di mesin tersebut.

Meskipun pengaturan ini mungkin tampak kuat, kami menghadapi masalah dengan virtualisasi karena buruknya dukungan prosesor Intel. Akibatnya, kami memutuskan untuk tidak menggunakan virtualisasi seperti Docker dan menjalankan pekerjaan pada mesin fisik itu sendiri. Kami mencoba menyiapkan solusi yang efisien dan andal berdasarkan Docker, namun keterbatasan virtualisasi seperti kurangnya akselerasi GPU mengakibatkan pekerjaan memerlukan waktu eksekusi dua kali lebih lama dibandingkan pada mesin fisik. Hal ini menyebabkan lebih banyak overhead dan pengisian antrian yang cepat.

Karena macOS SLA, kami hanya dapat menyiapkan dua VM secara bersamaan. Oleh karena itu, kami memutuskan untuk memperluas kumpulan pelari fisik dan menyiapkan mereka untuk menjalankan tugas Gitlab langsung di sistem operasi mereka. Namun pendekatan ini juga mempunyai beberapa kelemahan.

Tantangan dalam Proses Pembangunan dan Manajemen Pelari

  1. Tidak ada isolasi build di luar sandbox direktori build.

Pelari mengeksekusi setiap build pada mesin fisik, yang berarti build tidak diisolasi dari sandbox direktori build. Hal ini mempunyai kelebihan dan kekurangan. Di satu sisi, kita dapat menggunakan cache sistem untuk mempercepat pembangunan karena sebagian besar proyek menggunakan rangkaian dependensi pihak ketiga yang sama.

Di sisi lain, cache menjadi tidak dapat dipertahankan karena sisa dari satu proyek dapat mempengaruhi proyek lainnya. Hal ini sangat penting untuk cache seluruh sistem, karena runner yang sama digunakan untuk pengembangan Flutter dan React Native. React Native, khususnya, memerlukan banyak dependensi yang di-cache melalui NPM.

  1. Potensi kekacauan alat sistem.

Meskipun tidak ada pekerjaan yang dijalankan dengan hak istimewa sudo, mereka masih dapat mengakses beberapa sistem atau alat pengguna, seperti Ruby. Hal ini menimbulkan potensi ancaman kerusakan pada beberapa alat ini, terutama karena macOS menggunakan Ruby untuk beberapa perangkat lunak lawasnya, termasuk beberapa fitur Xcode lawas. Versi sistem Ruby bukanlah sesuatu yang ingin Anda ganggu.

Namun, memperkenalkan rbenv menciptakan lapisan kompleksitas lain yang harus dihadapi. Penting untuk dicatat bahwa Rubygem diinstal per versi Ruby, dan beberapa permata ini memerlukan versi Ruby tertentu. Hampir semua alat pihak ketiga yang kami gunakan bergantung pada Ruby, dengan Cocoapods dan Fastlane sebagai aktor utamanya.

  1. Mengelola identitas penandatanganan.

Mengelola beberapa identitas penandatanganan dari berbagai akun pengembangan klien dapat menjadi tantangan ketika berhubungan dengan gantungan kunci sistem pada runner. Identitas penandatanganan adalah bagian data yang sangat sensitif karena memungkinkan kami merancang kode aplikasi, sehingga rentan terhadap potensi ancaman.

Untuk memastikan keamanan, identitas harus dimasukkan ke dalam kotak pasir di seluruh proyek dan dilindungi. Namun, proses ini bisa menjadi mimpi buruk mengingat kompleksitas tambahan yang diperkenalkan oleh macOS dalam implementasi gantungan kunci mereka.

  1. Tantangan dalam lingkungan multi-proyek.

Tidak semua proyek dibuat menggunakan alat yang sama, khususnya Xcode. Beberapa proyek, terutama yang berada dalam tahap dukungan, dikelola menggunakan versi terakhir Xcode yang digunakan untuk mengembangkan proyek tersebut. Artinya, jika ada pekerjaan yang diperlukan pada proyek tersebut, CI harus mampu membangunnya. Akibatnya, pelari harus mendukung beberapa versi Xcode secara bersamaan, yang secara efektif mempersempit jumlah pelari yang tersedia untuk pekerjaan tertentu.

5. Diperlukan usaha ekstra.

Setiap perubahan yang dilakukan pada seluruh runner, seperti instalasi perangkat lunak, harus dilakukan pada semua runner secara bersamaan. Meskipun kami memiliki alat otomatisasi untuk ini, diperlukan upaya ekstra untuk memelihara skrip otomatisasi.

Solusi infrastruktur yang disesuaikan untuk beragam kebutuhan klien

Miquido adalah rumah perangkat lunak yang bekerja dengan banyak klien dengan kebutuhan berbeda. Kami menyesuaikan layanan kami untuk memenuhi kebutuhan spesifik setiap klien. Kami sering meng-host basis kode dan infrastruktur yang diperlukan untuk usaha kecil atau start-up karena mereka mungkin kekurangan sumber daya atau pengetahuan untuk memeliharanya.

Klien perusahaan biasanya memiliki infrastruktur sendiri untuk menampung proyek mereka. Namun, ada pula yang tidak mempunyai kapasitas untuk melakukan hal tersebut atau diwajibkan oleh peraturan industri untuk menggunakan infrastruktur mereka. Mereka juga memilih untuk tidak menggunakan layanan SaaS pihak ketiga seperti Xcode Cloud atau Codemagic. Sebaliknya, mereka menginginkan solusi yang sesuai dengan arsitektur yang ada.

Untuk mengakomodasi klien-klien ini, kami sering menghosting proyek di infrastruktur kami atau menyiapkan konfigurasi iOS integrasi berkelanjutan yang sama di infrastruktur mereka. Namun, kami sangat berhati-hati saat menangani informasi dan file sensitif, seperti identitas penandatanganan.

Memanfaatkan Fastlane untuk manajemen pembangunan yang efisien

Di sini Fastlane hadir sebagai alat yang berguna. Ini terdiri dari berbagai modul yang disebut tindakan yang membantu menyederhanakan proses dan memisahkannya antara klien yang berbeda. Salah satu tindakan ini, yang disebut pencocokan, membantu mempertahankan identitas penandatanganan pengembangan dan produksi, serta profil penyediaan. Ia juga berfungsi di tingkat OS untuk memisahkan identitas tersebut di gantungan kunci terpisah selama waktu build dan melakukan pembersihan setelah build, yang sangat membantu karena kami menjalankan semua build di mesin fisik.

Fastlane: alat otomatisasi pengembangan
Kredit Gambar: Jalur Cepat

Kami awalnya beralih ke Fastlane karena alasan tertentu, namun ternyata Fastlane memiliki fitur tambahan yang mungkin berguna bagi kami.

  1. Bangun Unggah ke Testflight

Sebelumnya, AppStoreConnect API tidak tersedia untuk umum bagi pengembang. Artinya, satu-satunya cara untuk mengunggah build ke Testflight adalah melalui Xcode atau menggunakan Fastlane. Fastlane adalah alat yang pada dasarnya menghapus ASC API dan mengubahnya menjadi tindakan yang disebut pilot . Namun, metode ini sering kali rusak pada pembaruan Xcode berikutnya. Jika pengembang ingin mengunggah build mereka ke Testflight menggunakan baris perintah, Fastlane adalah pilihan terbaik yang tersedia.

  1. Peralihan Mudah Antar Versi Xcode

Memiliki lebih dari satu instance Xcode pada satu mesin, penting untuk memilih Xcode mana yang akan digunakan untuk build. Sayangnya, Apple mempersulit peralihan antar versi Xcode – Anda harus menggunakan 'xcode-select' untuk melakukannya, yang juga memerlukan hak sudo. Fastlane juga mencakup hal itu.

  1. Utilitas Tambahan untuk Pengembang

Fastlane menyediakan banyak utilitas berguna lainnya termasuk pembuatan versi dan kemampuan untuk mengirimkan hasil pembangunan ke webhook.

Kekurangan Fastlane

Mengadaptasi Fastlane ke proyek kami merupakan hal yang baik dan solid, jadi kami menuju ke arah itu. Kami berhasil menggunakannya selama beberapa tahun. Namun, selama beberapa tahun ini, kami mengidentifikasi beberapa masalah:

  1. Fastlane membutuhkan pengetahuan Ruby.

Fastlane adalah alat yang ditulis dalam Ruby, dan memerlukan pengetahuan yang baik tentang Ruby untuk menggunakannya secara efektif. Ketika ada bug di konfigurasi Fastlane Anda atau di alat itu sendiri, melakukan debug menggunakan irb atau pry bisa jadi cukup menantang.

  1. Ketergantungan pada banyak permata.

Fastlane sendiri mengandalkan kurang lebih 70 gems. Untuk mengurangi risiko rusaknya sistem Ruby, proyek menggunakan permata bundler lokal. Mengambil semua permata ini menciptakan banyak waktu tambahan.

  1. Masalah sistem Ruby dan rubygems.

Akibatnya, semua masalah dengan sistem Ruby dan rubygems yang disebutkan sebelumnya juga berlaku di sini.

  1. Redundansi untuk proyek Flutter.

Proyek Flutter juga terpaksa menggunakan pencocokan fastlane hanya untuk menjaga kompatibilitas dengan proyek iOS dan melindungi gantungan kunci pelari. Hal ini jelas tidak diperlukan, karena Flutter memiliki sistem build sendiri, dan overhead yang disebutkan sebelumnya diperkenalkan hanya untuk mengelola identitas penandatanganan dan profil penyediaan.

Sebagian besar masalah ini telah diperbaiki, namun kami membutuhkan solusi yang lebih kuat dan andal.

Idenya: Mengadaptasi alat integrasi berkelanjutan yang baru dan lebih kuat untuk iOS

Kabar baiknya adalah Apple telah memperoleh kendali penuh atas arsitektur chipnya dan telah mengembangkan kerangka virtualisasi baru untuk macOS. Kerangka kerja ini memungkinkan pengguna untuk membuat, mengonfigurasi, dan menjalankan mesin virtual Linux atau macOS yang memulai dengan cepat dan memiliki karakteristik kinerja seperti asli – dan yang saya maksud adalah seperti asli.

Hal ini tampak menjanjikan dan dapat menjadi landasan bagi alat integrasi berkelanjutan kami yang baru untuk iOS. Namun, itu hanyalah sebagian dari solusi lengkap. Memiliki alat manajemen VM, kami juga memerlukan sesuatu yang dapat menggunakan kerangka kerja tersebut dalam koordinasi dengan runner Gitlab kami.

Karena itu, sebagian besar masalah kita mengenai kinerja virtualisasi yang buruk akan menjadi usang. Ini juga memungkinkan kami menyelesaikan sebagian besar masalah yang ingin kami selesaikan dengan Fastlane secara otomatis.

Mengembangkan solusi yang disesuaikan untuk manajemen identitas penandatanganan berdasarkan permintaan

Kami memiliki satu masalah terakhir yang harus diselesaikan – manajemen identitas penandatanganan. Kami tidak ingin menggunakan Fastlane untuk ini karena terkesan berlebihan untuk kebutuhan kami. Sebaliknya, kami mencari solusi yang lebih disesuaikan dengan kebutuhan kami. Kebutuhan kami sangat jelas: proses manajemen identitas harus dilakukan sesuai permintaan, khusus untuk waktu pembuatan, tanpa identitas yang sudah diinstal sebelumnya pada gantungan kunci, dan kompatibel dengan mesin apa pun yang menjalankannya.

Masalah distribusi dan kurangnya API AppstoreConnect yang stabil menjadi usang ketika Apple merilis `altool` mereka, yang memungkinkan komunikasi antara pengguna dan ASC.

Jadi kami punya ide dan harus menemukan cara untuk menghubungkan ketiga aspek tersebut:

  1. Menemukan cara untuk memanfaatkan kerangka Virtualisasi Apple.
  2. Membuatnya berfungsi dengan pelari Gitlab.
  3. Menemukan solusi untuk menandatangani manajemen identitas di berbagai proyek dan runner.

Solusinya: Sekilas tentang pendekatan kami (termasuk alat)

Kami mulai mencari solusi untuk mengatasi semua masalah yang disebutkan sebelumnya.

  1. Memanfaatkan kerangka Virtualisasi Apple.

Untuk kendala pertama, kami menemukan solusi dengan cukup cepat: kami menemukan alat tart Cirrus Labs. Sejak saat pertama, kami tahu ini akan menjadi pilihan kami.

Keuntungan paling signifikan menggunakan alat tart yang ditawarkan oleh Cirrus Lab adalah:

  • Kemungkinan membuat vms dari gambar .ipsw mentah.
  • Kemungkinan membuat vms menggunakan templat yang sudah dikemas sebelumnya (dengan beberapa alat utilitas terpasang, seperti brew atau Xcode), tersedia di halaman GitHub Cirrus Labs.
  • Alat Tart menggunakan pengemas untuk dukungan pembuatan gambar dinamis.
  • Alat tart mendukung gambar Linux & MacOS.
  • Alat ini memanfaatkan fitur luar biasa dari sistem file APFS yang memungkinkan duplikasi file tanpa benar-benar menyisakan ruang disk untuk file tersebut. Dengan cara ini, Anda tidak perlu mengalokasikan ruang disk sebanyak 3 kali ukuran gambar aslinya. Anda hanya memerlukan ruang disk yang cukup untuk gambar asli, sedangkan klon hanya menempati ruang yang merupakan perbedaan antara gambar asli dan gambar asli. Ini sangat membantu, terutama karena gambar macOS cenderung berukuran cukup besar.

Misalnya, image operasional macOS Ventura dengan Xcode dan utilitas lain yang diinstal memerlukan ruang disk minimal 60 GB. Dalam keadaan normal, sebuah gambar dan dua klonnya akan memakan ruang disk hingga 180 GB, yang merupakan jumlah yang signifikan. Dan ini hanyalah permulaan, karena Anda mungkin ingin memiliki lebih dari satu image asli atau menginstal beberapa versi Xcode pada satu VM, yang selanjutnya akan meningkatkan ukurannya.

  • Alat ini memungkinkan pengelolaan alamat IP untuk VM asli dan kloning, sehingga memungkinkan akses SSH ke VM.
  • Kemampuan untuk melakukan cross-mount direktori antara mesin host dan VM.
  • Alat ini mudah digunakan dan memiliki CLI yang sangat sederhana.

Hampir tidak ada kekurangan dari alat ini dalam hal penggunaannya untuk manajemen VM. Hampir tidak ada apa-apa, kecuali satu hal: meskipun menjanjikan, plugin pengemas untuk membuat gambar dengan cepat sangat memakan waktu, jadi kami memutuskan untuk tidak menggunakannya.

Kami mencoba kue tart, dan hasilnya luar biasa. Kinerjanya seperti aslinya, dan pengelolaannya mudah.

Setelah berhasil mengintegrasikan kue tart dengan hasil yang mengesankan, selanjutnya kami fokus untuk mengatasi tantangan lainnya.

  1. Menemukan cara untuk menggabungkan kue tar dengan pelari Gitlab.

Setelah menyelesaikan masalah pertama, kami dihadapkan pada pertanyaan tentang bagaimana menggabungkan tart dengan runner Gitlab.

Mari kita mulai dengan menjelaskan apa yang sebenarnya dilakukan oleh pelari Gitlab:

Diagram delegasi pekerjaan Gitlab yang disederhanakan. Sistem CI untuk iOS

Kami perlu menyertakan teka-teki tambahan pada diagram, yang melibatkan pengalokasian tugas dari host pelari ke VM. Pekerjaan GitLab adalah skrip shell yang menyimpan variabel penting, entri PATH, dan perintah.

Tujuan kami adalah mentransfer skrip ini ke VM dan menjalankannya.

Namun, tugas ini ternyata lebih menantang daripada yang kami duga sebelumnya.

Pelari

Eksekutor runner Gitlab standar seperti Docker atau SSH mudah diatur dan memerlukan sedikit atau tanpa konfigurasi. Namun, kami memerlukan kontrol yang lebih besar terhadap konfigurasi, sehingga kami perlu menjelajahi eksekutor khusus yang disediakan oleh GitLab.

Pelaksana khusus adalah pilihan bagus untuk konfigurasi non-standar karena setiap langkah pelari (persiapan, eksekusi, pembersihan) dijelaskan dalam bentuk skrip shell. Satu-satunya hal yang hilang adalah alat baris perintah yang dapat melakukan tugas yang kami perlukan dan dieksekusi dalam skrip konfigurasi runner.

Saat ini, ada beberapa alat yang tersedia yang dapat melakukan hal tersebut – misalnya, eksekutor tart CirrusLabs Gitlab. Alat inilah yang kami cari saat itu. Namun belum ada, dan setelah melakukan penelitian, kami tidak menemukan alat apa pun yang dapat membantu kami menyelesaikan tugas kami.

Menulis solusi sendiri

Karena kami tidak dapat menemukan solusi yang tepat, kami menulisnya sendiri. Bagaimanapun, kami adalah insinyur! Idenya tampak solid, dan kami memiliki semua alat yang diperlukan, jadi kami melanjutkan pengembangan.

Kami telah memilih untuk menggunakan Swift dan beberapa perpustakaan sumber terbuka yang disediakan oleh Apple: Swift Argument Parser untuk menangani eksekusi baris perintah dan Swift NIO untuk menangani koneksi SSH dengan VM. Kami memulai pengembangan, dan dalam beberapa hari, kami memperoleh prototipe alat kerja pertama yang akhirnya berkembang menjadi MQVMRunner.

Infrastruktur iOS CI: MQVMRunner

Pada tingkat tinggi, alat ini bekerja sebagai berikut:

  1. (Langkah persiapan)
    1. Baca variabel yang disediakan di gitlab-ci.yml (nama gambar dan variabel tambahan).
    2. Pilih basis VM yang diminta
    3. Kloning basis VM yang diminta.
    4. Siapkan direktori cross-mount dan salin skrip pekerjaan Gitlab ke dalamnya, atur izin yang diperlukan untuknya.
    5. Jalankan kloning dan periksa koneksi SSH.
    6. Siapkan semua dependensi yang diperlukan (seperti versi Xcode), jika diperlukan.
  2. (Jalankan langkah)
    1. Jalankan pekerjaan Gitlab dengan mengeksekusi skrip dari direktori yang dipasang silang pada klon VM yang telah disiapkan melalui SSH.
  3. (Langkah pembersihan)
    1. Hapus gambar yang dikloning.

Tantangan dalam pembangunan

Selama pengembangan, kami menemui beberapa kendala yang menyebabkan tidak berjalan semulus yang kami inginkan.

  1. manajemen alamat IP.

Mengelola alamat IP adalah tugas penting yang harus ditangani dengan hati-hati. Dalam prototipe, penanganan SSH diimplementasikan menggunakan perintah shell SSH langsung dan hardcoded. Namun, dalam kasus shell non-interaktif, otentikasi kunci disarankan. Selain itu, disarankan untuk menambahkan host ke file unknown_hosts untuk menghindari gangguan. Meskipun demikian, karena manajemen alamat IP mesin virtual yang dinamis, ada kemungkinan penggandaan entri untuk IP tertentu, yang menyebabkan kesalahan. Oleh karena itu, kita perlu menetapkan unknown_hosts secara dinamis untuk pekerjaan tertentu guna mencegah masalah tersebut.

  1. Solusi Swift murni.

Mempertimbangkan hal itu, dan fakta bahwa perintah shell yang di-hardcode dalam kode Swift tidak terlalu elegan, kami pikir akan lebih baik jika menggunakan perpustakaan Swift khusus dan memutuskan untuk menggunakan Swift NIO. Kami menyelesaikan beberapa masalah tetapi pada saat yang sama, memperkenalkan beberapa masalah baru seperti – misalnya – terkadang log yang ditempatkan di stdout ditransfer *setelah* saluran SSH dihentikan karena perintah selesai dieksekusi – dan, saat kami mendasarkan pada keluaran itu dalam pekerjaan selanjutnya, eksekusinya gagal secara acak.

  1. Pemilihan versi Xcode.

Karena plugin Packer bukan pilihan untuk pembuatan image dinamis karena konsumsi waktu, kami memutuskan untuk menggunakan satu basis VM dengan beberapa versi Xcode yang sudah diinstal sebelumnya. Kami harus menemukan cara bagi pengembang untuk menentukan versi Xcode yang mereka perlukan di gitlab-ci.yml – dan kami telah menghadirkan variabel khusus yang tersedia untuk digunakan dalam proyek apa pun. MQVMRunner kemudian akan menjalankan `xcode-select` pada VM yang dikloning untuk menyiapkan versi Xcode yang sesuai.

Dan masih banyak lagi

Menyederhanakan Migrasi Proyek dan integrasi berkelanjutan untuk Alur Kerja iOS dengan Mac Studios

Kami telah menyiapkannya di dua Mac Studios baru dan mulai memigrasi proyek. Kami ingin membuat proses migrasi untuk pengembang kami setransparan mungkin. Kami tidak dapat membuatnya sepenuhnya mulus, namun pada akhirnya, kami sampai pada titik di mana mereka hanya perlu melakukan beberapa hal di gitlab-ci.yml:

  • Tag pelari: menggunakan Mac Studios, bukan Intel.
  • Nama gambar: parameter opsional, diperkenalkan untuk kompatibilitas di masa mendatang jika kita memerlukan lebih dari satu VM dasar. Saat ini, defaultnya selalu pada VM dasar tunggal yang kami miliki.
  • Versi Xcode: parameter opsional; jika tidak disediakan, versi terbaru yang tersedia akan digunakan.

Alat ini mendapat tanggapan awal yang sangat baik, jadi kami memutuskan untuk menjadikannya sumber terbuka. Kami telah menambahkan skrip instalasi untuk menyiapkan Gitlab Custom Runner dan semua tindakan serta variabel yang diperlukan. Dengan menggunakan alat kami, Anda dapat menyiapkan runner GitLab Anda sendiri dalam hitungan menit – satu-satunya hal yang Anda perlukan hanyalah VM tart dan dasar tempat pekerjaan akan dieksekusi.

Integrasi berkelanjutan terakhir untuk struktur iOS terlihat sebagai berikut:

Infrastruktur CI Terakhir: MQVMRunner

3. Solusi untuk manajemen identitas yang efisien

Kami telah berjuang untuk menemukan solusi efisien untuk mengelola identitas penandatanganan klien kami. Hal ini sangat menantang, karena identitas penandatanganan adalah data yang sangat rahasia dan tidak boleh disimpan di tempat yang tidak aman lebih lama dari yang diperlukan.

Selain itu, kami ingin memuat identitas ini hanya selama waktu pembangunan, tanpa solusi lintas proyek. Artinya, identitas tidak boleh dapat diakses di luar sandbox aplikasi (atau build). Kami telah mengatasi masalah terakhir dengan melakukan transisi ke VM. Namun, kami masih perlu menemukan cara untuk menyimpan dan memuat identitas penandatanganan ke dalam VM hanya untuk waktu pembuatan.

Masalah dengan Pertandingan Fastlane

Pada saat itu, kami masih menggunakan pencocokan Fastlane, yang menyimpan identitas dan ketentuan terenkripsi dalam repositori terpisah, memuatnya selama proses build ke dalam instance gantungan kunci terpisah, dan menghapus instance tersebut setelah build.

Pendekatan ini tampaknya nyaman, namun memiliki beberapa masalah:

  • Membutuhkan seluruh pengaturan Fastlane agar berfungsi.

Fastlane adalah rubygem, dan semua masalah yang tercantum di bab pertama berlaku di sini.

  • Pembayaran repositori pada waktu pembuatan.

Kami menyimpan identitas kami di repositori terpisah yang diperiksa selama proses pembangunan, bukan proses penyiapan. Ini berarti kami harus membuat akses terpisah ke repositori identitas, tidak hanya untuk Gitlab, tetapi untuk runner tertentu, serupa dengan cara kami menangani dependensi pribadi pihak ketiga.

  • Sulit untuk dikelola di luar Pertandingan.

Jika Anda menggunakan Match untuk mengelola identitas atau penyediaan, intervensi manual tidak diperlukan. Mengedit, mendekripsi, dan mengenkripsi profil secara manual sehingga kecocokan masih dapat berfungsi di kemudian hari adalah hal yang membosankan dan memakan waktu. Menggunakan Fastlane untuk melakukan proses ini biasanya mengakibatkan penghapusan total pengaturan penyediaan aplikasi dan pembuatan yang baru.

  • Agak sulit untuk di-debug.

Jika terjadi masalah penandatanganan kode, Anda mungkin kesulitan menentukan kecocokan identitas dan penyediaan yang baru saja diinstal, karena Anda harus mendekodekannya terlebih dahulu.

  • Perhatian pada keamanan.

Cocokkan akun pengembang yang diakses menggunakan kredensial yang diberikan untuk membuat perubahan atas nama mereka. Meskipun Fastlane bersifat open source, beberapa klien menolaknya karena masalah keamanan.

  • Yang terakhir, menyingkirkan Match akan menghilangkan hambatan terbesar dalam perjalanan kami untuk menyingkirkan Fastlane sepenuhnya.

Persyaratan awal kami adalah sebagai berikut:

  • Pemuatan memerlukan penandatanganan identitas dari tempat yang aman, sebaiknya dalam bentuk non-teks biasa, dan menempatkannya di gantungan kunci.
  • Identitas itu harus dapat diakses oleh Xcode.
  • Sebaiknya, variabel kata sandi identitas, nama rantai kunci, dan kata sandi rantai kunci dapat diatur untuk tujuan debugging.

Match memiliki semua yang kami butuhkan, namun mengimplementasikan Fastlane hanya untuk menggunakan Match sepertinya berlebihan, terutama untuk solusi lintas platform dengan sistem build mereka sendiri. Kami menginginkan sesuatu yang mirip dengan Match, tetapi tanpa beban berat yang ditanggung Ruby.

Menciptakan solusi sendiri

Jadi kami berpikir – ayo tulis sendiri! Kami melakukannya dengan MQVMRunner, jadi kami juga bisa melakukannya di sini. Kami juga memilih Swift untuk melakukannya, terutama karena kami bisa mendapatkan banyak API yang diperlukan secara gratis menggunakan kerangka Keamanan Apple.

Tentu saja juga tidak berjalan semulus yang diharapkan.

  • Kerangka keamanan sudah ada.

Strategi termudah adalah memanggil perintah bash seperti yang dilakukan Fastlane. Namun, dengan tersedianya kerangka Keamanan, kami pikir akan lebih elegan jika digunakan untuk pengembangan.

  • Kurang pengalaman.

Kami tidak terlalu berpengalaman dengan kerangka Keamanan untuk macOS, dan ternyata kerangka tersebut sangat berbeda dari yang biasa kami gunakan di iOS. Hal ini menjadi bumerang bagi kami dalam banyak kasus ketika kami tidak menyadari keterbatasan macOS atau berasumsi bahwa cara kerjanya sama seperti di iOS – sebagian besar asumsi tersebut salah.

  • Dokumentasi yang buruk.

Dokumentasi kerangka Keamanan Apple, secara sederhana, sederhana. Ini adalah API yang sangat lama sejak versi pertama OSX, dan terkadang, kami mendapat kesan bahwa API tersebut belum diperbarui sejak saat itu. Sebagian besar kode tidak didokumentasikan, namun kami mengantisipasi cara kerjanya dengan membaca kode sumber. Untungnya bagi kami, ini open-source.

  • Penghentian tanpa penggantian.

Sebagian besar kerangka kerja ini sudah tidak digunakan lagi; Apple mencoba untuk beralih dari gantungan kunci “gaya macOS” yang khas (beberapa gantungan kunci dapat diakses dengan kata sandi) dan menerapkan gantungan kunci “gaya iOS” (gantungan kunci tunggal, disinkronkan melalui iCloud). Jadi mereka tidak lagi menggunakannya di macOS Yosemite pada tahun 2014, tetapi tidak menemukan penggantinya dalam sembilan tahun terakhir – jadi, satu-satunya API yang tersedia bagi kami, untuk saat ini, tidak digunakan lagi karena belum ada yang baru.

Kami berasumsi bahwa identitas penandatanganan dapat disimpan sebagai string yang dikodekan base64 dalam variabel Gitlab per proyek. Ini aman, berbasis per proyek, dan jika ditetapkan sebagai variabel bertopeng, variabel ini dapat dibaca dan ditampilkan di log build sebagai non-teks biasa.

Jadi, kami memiliki data identitas. Kami hanya perlu memasukkannya ke dalam gantungan kunci. Menggunakan API Keamanan Setelah beberapa kali mencoba dan melalui dokumentasi kerangka Keamanan, kami menyiapkan prototipe dari sesuatu yang kemudian menjadi MQSwiftSign.

Mempelajari sistem Keamanan macOS, tetapi dengan cara yang sulit

Kami harus mendapatkan pemahaman mendalam tentang cara kerja gantungan kunci macOS untuk mengembangkan alat kami. Hal ini melibatkan penelitian bagaimana rantai kunci mengelola item, akses dan izinnya, serta struktur data rantai kunci. Misalnya, kami menemukan bahwa gantungan kunci adalah satu-satunya file macOS yang sistem operasinya mengabaikan kumpulan ACL. Selain itu, kami mengetahui bahwa ACL pada item gantungan kunci tertentu adalah daftar teks biasa yang disimpan dalam file gantungan kunci. Kami menghadapi beberapa tantangan selama perjalanan, namun kami juga belajar banyak.

Salah satu tantangan signifikan yang kami temui adalah petunjuknya. Alat kami terutama dirancang untuk berjalan pada sistem CI iOS, yang berarti alat tersebut harus non-interaktif. Kami tidak dapat meminta pengguna untuk mengonfirmasi kata sandi pada CI.

Namun, sistem keamanan macOS dirancang dengan baik, sehingga tidak mungkin mengedit atau membaca informasi rahasia, termasuk identitas penandatanganan, tanpa izin pengguna yang jelas. Untuk mengakses sumber daya tanpa konfirmasi, program pengakses harus disertakan dalam Daftar Kontrol Akses sumber daya. Ini adalah persyaratan ketat yang tidak dapat dilanggar oleh program apa pun, bahkan program Apple yang disertakan dengan sistem. Jika ada program yang perlu membaca atau mengedit entri rantai kunci, pengguna harus memberikan kata sandi rantai kunci untuk membuka kuncinya dan, secara opsional, menambahkannya ke ACL entri.

Mengatasi Tantangan Izin Pengguna

Jadi, kami harus menemukan cara agar Xcode dapat mengakses identitas yang diatur oleh gantungan kunci kami tanpa meminta izin pengguna menggunakan perintah kata sandi. Untuk melakukannya, kita dapat mengubah daftar kontrol akses suatu item, namun hal ini juga memerlukan izin pengguna – dan, tentu saja, diperlukan. Jika tidak, hal itu akan merusak keseluruhan tujuan memiliki ACL. Kami telah mencoba untuk melewati perlindungan tersebut – kami mencoba untuk mencapai efek yang sama seperti dengan perintah `security set-key-partition-list`.

Setelah mendalami dokumentasi kerangka kerja, kami belum menemukan API apa pun yang memungkinkan pengeditan ACL tanpa meminta pengguna memberikan kata sandi. Hal terdekat yang kami temukan adalah `SecKeychainItemSetAccess`, yang memicu prompt UI setiap saat. Kemudian kami menyelam lagi, tapi kali ini, ke dalam dokumentasi terbaik, yaitu kode sumber itu sendiri. Bagaimana Apple menerapkannya?

Ternyata – seperti yang diharapkan – mereka menggunakan API pribadi. Metode yang disebut `SecKeychainItemSetAccessWithPassword` pada dasarnya melakukan hal yang sama seperti `SecKeychainItemSetAccess`, namun alih-alih meminta kata sandi kepada pengguna, kata sandi tersebut diberikan sebagai argumen ke suatu fungsi. Tentu saja – sebagai API pribadi, ini tidak tercantum dalam dokumentasi, namun Apple tidak memiliki dokumentasi untuk API tersebut seolah-olah mereka tidak dapat memikirkan untuk membuat aplikasi untuk penggunaan pribadi atau perusahaan. Karena alat ini dimaksudkan hanya untuk penggunaan internal, kami tidak ragu untuk menggunakan API pribadi. Satu-satunya hal yang harus dilakukan adalah menjembatani metode C ke Swift.

Mengatasi Tantangan Izin Pengguna

Jadi, alur kerja akhir dari prototipe adalah sebagai berikut:

  1. Buat gantungan kunci sementara yang tidak terkunci dengan kunci otomatis dimatikan.
  2. Dapatkan dan dekode data identitas penandatanganan yang dikodekan base64 dari variabel lingkungan (diteruskan oleh Gitlab).
  3. Impor identitas ke dalam gantungan kunci yang dibuat.
  4. Tetapkan opsi akses yang tepat untuk identitas yang diimpor sehingga Xcode dan alat lain dapat membacanya untuk desain kode.

Peningkatan lebih lanjut

Prototipe ini berfungsi dengan baik, jadi kami mengidentifikasi beberapa fitur tambahan yang ingin kami tambahkan ke alat ini. Tujuan kami pada akhirnya adalah menggantikan fastlane; kami telah menerapkan tindakan `pertandingan`. Namun, fastlane masih menawarkan dua fitur berharga yang belum kami miliki – penyediaan instalasi profil dan pembuatan ekspor.plist.

Instalasi Profil Penyediaan

Instalasi profil penyediaan cukup mudah – cukup mengekstrak UUID profil dan menyalin file ke `~/Library/MobileDevice/Provisioning Profiles/` dengan UUID sebagai nama file – dan itu cukup bagi Xcode untuk melihatnya dengan benar. Bukanlah ilmu yang luar biasa untuk menambahkan plugin sederhana ke alat kami untuk mengulang direktori yang disediakan dan melakukan itu untuk setiap file .mobileprovision yang ditemukan di dalamnya.

Ekspor. Pembuatan plist

Namun, pembuatan ekspor.plist sedikit lebih rumit. Untuk menghasilkan file IPA yang tepat, Xcode mengharuskan pengguna untuk menyediakan file plist dengan informasi spesifik yang dikumpulkan dari berbagai sumber – file proyek, plist hak, pengaturan ruang kerja, dll. Alasan mengapa Xcode hanya dapat mengumpulkan data tersebut melalui wizard distribusi tetapi tidak melalui CLI tidak saya ketahui. Namun, kami mengumpulkannya menggunakan API Swift, hanya memiliki referensi proyek/ruang kerja dan sedikit pengetahuan tentang bagaimana file proyek Xcode dibuat.

Hasilnya lebih baik dari yang diharapkan, jadi kami memutuskan untuk menambahkannya sebagai plugin lain ke alat kami. Kami juga merilisnya sebagai proyek sumber terbuka untuk khalayak yang lebih luas. Saat ini, MQSwiftSign adalah alat serbaguna yang berhasil digunakan sebagai pengganti tindakan fastlane dasar yang diperlukan untuk membangun dan mendistribusikan aplikasi iOS Anda dan kami menggunakannya di setiap proyek kami di Miquido.

Pikiran Terakhir: Kesuksesan

Beralih dari arsitektur Intel ke iOS ARM adalah tugas yang menantang. Kami menghadapi banyak kendala dan menghabiskan banyak waktu untuk mengembangkan alat karena kurangnya dokumentasi. Namun, kami pada akhirnya membangun sistem yang kuat:

  • Dua pelari yang harus dikelola, bukan sembilan;
  • Menjalankan perangkat lunak yang sepenuhnya berada di bawah kendali kami, tanpa banyak overhead dalam bentuk rubygem – kami dapat menyingkirkan fastlane atau perangkat lunak pihak ketiga apa pun dalam konfigurasi build kami;
  • BANYAK pengetahuan dan pemahaman tentang hal-hal yang biasanya tidak kita perhatikan – seperti keamanan sistem macOS dan kerangka Keamanan itu sendiri, struktur proyek Xcode sebenarnya, dan masih banyak lagi.

Saya dengan senang hati akan mendorong Anda – Jika Anda kesulitan menyiapkan runner GitLab untuk versi iOS, cobalah MQVMRunner kami. Jika Anda memerlukan bantuan dalam membangun dan mendistribusikan aplikasi menggunakan satu alat dan tidak ingin bergantung pada rubygem, cobalah MQSwiftSign. Cocok untuk saya, mungkin juga cocok untuk Anda!