
Meletakkan Release ke dalam CI, Dua Kali
GeekBye v1.8.4 memindahkan build release ke CI untuk macOS dan Windows. Apa yang tidak diberitahu changelog ialah bahawa kami pernah mencuba ini sekali sebelum ini, memadamkannya sebulan kemudian kerana kos runner, dan hanya berjaya mengekalkannya pada percubaan kedua — kerana menjelang itu skrip release sudah berfungsi secara manual.
Changelog v1.8.4 mempunyai satu baris yang berbunyi seperti kerja rumah semata-mata: "Build release kini datang daripada CI untuk macOS mahupun Windows." Ia terbaca seperti jenis perkara yang berlaku sekali, kemas, dan tidak pernah difikirkan semula. Ternyata tidak. Pasukan sudah pernah meletakkan release ke CI sekali — dan mencabutnya keluar. Release ini ialah percubaan kedua, dan sebab ia kekal ialah pengajaran kejuruteraan yang sebenar, jadi mari kita ceritakan mengikut urutan.
Percubaan pertama, dan mengapa ia mati
Dahulu pada Oktober 2025 ada sebuah release.yml. Ia dicetus pada push tag — tolak sebuah tag v*.*.* dan sebuah runner macOS akan menyala, mem-build, dan menerbitkan. Ia melakukan persediaan penandatanganannya sendiri secara manual: satu langkah yang menyahkod sijil penandatanganan, mencipta dan membuka kunci sebuah keychain sementara, mengimport sijil itu supaya codesign boleh menjumpainya, dan satu langkah pembersihan yang sepadan di hujung. Ia berfungsi. Ia juga khusus-macOS — release Windows langsung tiada di CI.
Pada 4 November, ia dipadamkan. Mesej commit-nya luar biasa terus terang tentang sebabnya: "remove GitHub Actions release workflow to conserve minutes / macOS runners cost 10x multiplier (100 billed minutes per 10-minute release). Releases will be done locally." Itulah keseluruhan kisah percubaan pertama dalam satu ayat. GitHub mengecaj masa runner macOS pada sepuluh kali kadar Linux, dan sebuah release yang dicetus-tag bermakna setiap tag — termasuk yang sekali-guna, yang di-tag-semula, yang "alamak, terlupa naikkan versi" — secara senyap menghabiskan seratus minit dicaj. Automasi yang anda tidak kawal ialah automasi yang membelanjakan wang sementara anda tidur. Jadi release kembali ke Mac seorang pembangun selama tiga setengah bulan berikutnya.
Di mana masalah-masalah sukar sebenarnya diselesaikan
Inilah bahagian yang membuatkan percubaan kedua berjaya, dan ia berlaku sepenuhnya di luar CI. Sepanjang tiga setengah bulan release tempatan itu, scripts/release.js — skrip Node biasa yang dijalankan pembangun secara manual — menyerap setiap masalah packaging yang ada pada aplikasi, satu pembaikan pada satu masa:
- Restore white-label. Ini ialah codebase white-label yang mem-build kedua-dua GeekBye dan Pavleur daripada satu sumber, yang bermakna sebuah release menukar medan
package.jsondan aset ikon untuk produk sasaran. Dua pembaikan mengajar skrip untuk meletakkan semuanya semula selepas itu, jadi mem-build Pavleur tidak meninggalkan git tree anda kotor dengan identiti Pavleur. - Windows dual-arch. Satu pembaikan untuk mem-build kedua-dua installer Windows x64 dan arm64 dan bukannya satu.
- Helah Windows tak-bertandatangan. electron-builder yang lebih baru mengabaikan flag config yang sepatutnya melumpuhkan penandatanganan Windows, jadi skrip belajar memaksa build tak-bertandatangan dengan menetapkan
CSC_IDENTITY_AUTO_DISCOVERY=falsedi persekitaran sebaliknya — sebuah kedutan yang anda hanya temui dengan terlanggarnya. - Pengesanan-automatik notarisasi. Skrip belajar menjeling persekitarannya dan memutuskan: kelayakan penandatanganan ada? Tandatangani. Kelayakan notarisasi Apple juga ada? Notarisasikan. Tiada kedua-duanya? Build tak-bertandatangan. Tiada flag untuk diingat; kehadiran secret itulah konfigurasinya.
Tiada satu pun daripada itu yang glamor. Kesemuanya ialah jenis perkara yang, jika ia mengejutkan anda di dalam sebuah runner CI, mengenakan anda satu jam push-tunggu-gagal-baca-log setiap percubaan — pada caj 10x. Diselesaikan pada sebuah Mac yang ada di depan anda, setiap satunya mengenakan seminit.
Percubaan kedua: enam puluh enam baris
Apabila CI kembali di v1.8.4, workflow-nya 66 baris, dan kualiti yang mentakrifkannya ialah betapa sedikitnya ia melakukan. Commit-nya menggambarkannya dengan jelas: "Manual workflow_dispatch trigger that builds both platforms in parallel, reusing existing release.js script. macOS builds are signed and notarized, Windows builds are unsigned." Setiap keputusan reka bentuk di dalamnya ialah satu pengajaran yang ditinggalkan oleh kesakitan percubaan pertama:
workflow_dispatch, bukan dicetus-tag. Anda memulakan sebuah release dengan mengklik "Run workflow." Seorang manusia mengawal setiap minit macOS berbayar. Masalah kos yang membunuh versi pertama diselesaikan dengan sekadar tidak mengautomasikan pencetusnya — satu-satunya tempat automasi sememangnya memudaratkan.- Satu input
product. Dispatch mengambil sebuah dropdown —geekbyeataupavleur— jadi workflow yang sama mengeluarkan salah satu jenama. Jahitan white-label memanjang hingga ke butang release. - Dua job selari.
build-macpadamacos-latest,build-winpadawindows-latest, berjalan pada masa yang sama. Windows di CI benar-benar baharu di sini; workflow Oktober tidak pernah mem-build-nya. - Tiada logik penandatanganan di YAML. Inilah keseluruhan intinya. Tiada silap mata keychain, tiada langkah import sijil, tiada pembersihan. Job Mac menjalankan
node scripts/release.js <product> --publishdan job Windows menjalankan yang sama dengan--no-sign. Segala yang dilakukan workflow pertama secara manual di YAML kini tinggal di skrip yang sudah berfungsi. Workflow ialah orkestrator, bukan implementasi.
Kedua-dua platform mahukan perkara yang bertentangan, dan job-job itu mencerminkannya dengan jujur. macOS mem-build di bawah hardened runtime, ditandatangani dan dinotarisasi melalui laluan terbina electron-builder (@electron/notarize), dengan kelayakan dibekalkan daripada repository secret dan dikesan-automatik oleh skrip. Windows mem-build installer NSIS tak-bertandatangan untuk x64 dan arm64. Satu workflow, dua job, dua tanggapan "siap" yang sama sekali berbeza.
Buktinya ada pada apa yang tidak berlaku
Beginilah cara anda tahu urutannya betul: selepas dua ubahsuai pada hari yang sama yang menamakan run dan membetulkan nama sebuah token secret, release.yml tidak disentuh lagi selama empat bulan. Tiada hotfix kegagalan-penandatanganan. Tiada kelam-kabut penolakan-notarisasi. Tiada panik "native binary hilang di runner." Bagi sebuah pipeline penandatanganan-kod-dan-notarisasi — genre CI yang paling terkenal buruk kerana kekusutannya — empat bulan kesenyapan hampir tidak pernah kedengaran.
Ia senyap kerana bisingnya sudah berlaku di suatu tempat yang lebih murah. Pertarungan yang biasanya terungkai di sebuah tab log CI, pada caj 10x, satu force-push pada satu masa, telah terungkai di Mac para pembangun sepanjang era tempatan. CI tidak perlu menjadi tempat packaging dinyahpepijat, kerana packaging sudah dinyahpepijat. Itulah tesisnya dalam satu baris: alihkan build keluar dari Mac anda hanya selepas Mac anda berhenti mengejutkan anda.
Satu gandingan yang masih boleh menyusahkan
Ia tidak sepenuhnya tanpa titik lemah, dan ia berbaloi dinamakan kerana ia halus. Binari Swift yang aplikasi macOS bergantung padanya — OCR, tangkapan skrin, para transcriber — di-commit ke git. Tetapi release tidak menghantar salinan yang di-commit itu; build mengkompilnya semula di runner. Dan sebuah pintu check-swift-version.js menggagalkan-keras seluruh release jika pengkompil bukan versi yang dipaku. Pada release ini versi yang dipaku itu ialah Swift 6.2.x, dan tiada apa-apa dalam workflow yang memasangnya — job itu semata-mata mempercayai bahawa Swift lalai macos-latest sepadan. Pada hari GitHub menaikkan imej runner-nya melepasi versi yang dipaku, release berhenti, bukan kerana ada apa-apa yang salah dengan aplikasi, tetapi kerana pipeline secara senyap tergandeng pada sebuah imej mesin yang ia tidak kawal. Ia ialah satu-satunya butiran implementasi yang tidak ditolak turun oleh workflow nipis ini ke dalam sesuatu yang ia miliki sendiri.
Separuh lagi daripada release: payload yang lebih kecil
Release ini membawa satu ciri tidak berkaitan yang berbaloi disebut, kerana ia contoh bersih bagi satu pembaikan latensi melalui penolakan. Tindakan Assist Me menghantar lebih banyak konteks kepada backend daripada yang ia perlukan pada setiap request. Satu commit memangkas dua perkara: sejarah transkrip yang turut dihantar susut daripada 30 entri terakhir kepada 15 entri terakhir, dan fail konteks profil pengguna — yang disuntik ke dalam system prompt pada setiap panggilan — dihadkan-keras pada 4,000 aksara dengan satu penanda pemotongan. Kurang untuk disirikan, kurang untuk dimuat naik, kurang untuk model baca sebelum ia mula menjawab, yang merupakan metrik yang commit itu optimumkan: time-to-first-token. Demi kejujuran: commit itu mendakwa peningkatan kelajuan tetapi tidak mengukurnya — tiada nombor sebelum/selepas dalam sejarah, jadi anggap ia sebagai satu pemangkasan yang berasas ketimbang satu kemenangan yang diukur.
Tiga perkara yang diajarkan oleh release ini
- CI ialah pembalut nipis atas sebuah skrip yang sudah berfungsi. Workflow 66 baris tiada logik penandatanganan sendiri; ia memanggil sebuah skrip yang tiga bulan release tempatan sudah nyahpepijat. Letakkan mekanismenya dalam sesuatu yang boleh anda jalankan secara manual, dan biarkan CI hanya memutuskan bila menjalankannya.
- Automasikan kerjanya, tidak semestinya pencetusnya. Percubaan pertama mati kerana release yang dicetus-tag menghabiskan minit runner bercaj-10x secara automatik.
workflow_dispatchmanual mengekalkan automasi dan membuang bahagian yang membelanjakan wang — kadangkala manusia dalam gelung itulah cirinya. - Nyahpepijat di tempat lelaran murah. Setiap pertarungan packaging yang diselesaikan di Mac seorang pembangun ialah satu pertarungan yang tidak pernah berlaku di tab log CI pada sepuluh kali harga. Keluar dari Mac paling akhir, bukan paling awal.
Untuk bab sebelumnya dalam kisah v1, mengeluarkan tiga puluh bahasa tanpa jaring keselamatan (v1.8.3); dan untuk keseluruhan lengkungnya, anatomi mengeluarkan perisian hingga ke kesempurnaan.