
Vložení vydání do CI, dvakrát
GeekBye v1.8.4 přesunul release buildy do CI pro macOS a Windows. Co changelog neříká, je to, že jsme to zkusili už jednou, o měsíc později to smazali kvůli nákladům na runnery a napodruhé to drželo pohromadě až proto, že tou dobou už release skript fungoval ručně.
Changelog v1.8.4 má řádek, který zní jako čistá údržba: „Release buildy teď pro macOS i Windows přicházejí z CI." Čte se to jako věc, která se stane jednou, čistě, a už na ni nikdy nepomyslíš. Nebylo tomu tak. Tým už jednou vydání do CI dal — a vytrhl je odtamtud. Toto vydání je druhý pokus a důvod, proč se udrželo, je ta skutečná inženýrská lekce, tak si to řekněme popořádku.
První pokus a proč zemřel
Ještě v říjnu 2025 existoval release.yml. Spouštěl se při pushnutí tagu — strčíš tag v*.*.* a macOS runner se rozjel, sestavil a publikoval. Vlastní nastavení podepisování dělal ručně: krok, který dekódoval podepisovací certifikát, vytvořil a odemkl dočasný keychain, naimportoval certifikát, aby ho codesign našel, a odpovídající úklidový krok na konci. Fungovalo to. Bylo to také jen pro macOS — vydání pro Windows v CI nebyla vůbec.
- listopadu bylo smazáno. Zpráva commitu je neobvykle upřímná ohledně důvodu: "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." (odstraň release workflow GitHub Actions kvůli šetření minut / macOS runnery stojí násobitel 10x — 100 účtovaných minut na 10minutové vydání. Vydání se budou dělat lokálně.) To je celý příběh prvního pokusu v jedné větě. GitHub účtuje čas macOS runnerů desetkrát dráž než Linux, a vydání spouštěné tagem znamená, že každý jediný tag — včetně těch na jedno použití, přetagování, těch typu „jejda, zapomněl jsem zvednout verzi" — tiše utratí sto účtovaných minut. Automatizace, kterou neovládáš, je automatizace, která utrácí peníze, zatímco spíš. Takže se vydání na dalších tři a půl měsíce vrátila na Mac jednoho vývojáře.
Kde se ty těžké problémy vlastně vyřešily
Tady je ta část, díky které druhý pokus funguje, a stala se úplně mimo CI. Během těch tří a půl měsíců lokálních vydání scripts/release.js — obyčejný Node skript, který vývojář spouští ručně — vstřebal každý problém s balením, který aplikace měla, jednu opravu po druhé:
- White-label obnovení. Tohle je white-label codebase, který staví GeekBye i Pavleur z jednoho zdroje, což znamená, že vydání vymění pole v
package.jsona ikonové assety za cílový produkt. Dvě opravy naučily skript všechno potom vrátit zpět, takže sestavení Pavleuru nenechalo tvůj git strom špinavý identitou Pavleuru. - Dual-arch Windows. Oprava, aby stavěl oba Windows instalátory, x64 i arm64, místo jednoho.
- Workaround pro nepodepsaný Windows. Novější electron-builder ignoroval config flag určený k vypnutí podepisování Windows, takže se skript naučil vynutit nepodepsaný build tím, že místo toho nastaví v prostředí
CSC_IDENTITY_AUTO_DISCOVERY=false— vráska, kterou najdeš jen tak, že do ní narazíš. - Automatická detekce notarizace. Skript se naučil dívat se na své prostředí a rozhodovat: jsou přítomné podepisovací přihlašovací údaje? Podepiš. Jsou přítomné i notarizační údaje Applu? Notarizuj. Žádné? Nepodepsaný build. Žádné flagy k zapamatování; přítomnost tajemství je konfigurace.
Nic z toho není okázalé. Všechno je ten druh věci, který, pokud tě překvapí uvnitř CI runneru, tě stojí hodinu push-čekej-selhání-čti-logy na pokus — při účtování 10x. Vyřešené na Macu, před kterým sedíš, každá stojí minutu.
Druhý pokus: šedesát šest řádků
Když se CI vrátilo ve v1.8.4, workflow mělo 66 řádků a jeho určující vlastností je, jak málo dělá. Commit to popisuje prostě: "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." (ruční spouštěč workflow_dispatch, který staví obě platformy paralelně a znovu využívá stávající skript release.js. macOS buildy jsou podepsané a notarizované, Windows buildy jsou nepodepsané.) Každé návrhové rozhodnutí v něm je jizva z prvního pokusu:
workflow_dispatch, ne spouštěné tagem. Vydání zahájíš kliknutím na „Run workflow". Každou placenou macOS minutu hlídá člověk. Problém s náklady, který zabil první verzi, se řeší prostě tím, že spouštěč neautomatizuješ — jediné místo, kde byla automatizace aktivně škodlivá.- Vstup
product. Dispatch bere rozbalovací nabídku —geekbyenebopavleur— takže totéž workflow dodá kteroukoli značku. White-label šev vede až ven, k tlačítku vydání. - Dva paralelní joby.
build-macnamacos-latest,build-winnawindows-latest, běžící současně. Windows v CI je tady opravdu nový; říjnové workflow ho nikdy nestavělo. - Žádná logika podepisování v YAML. V tom je celý smysl. Žádné žonglování s keychainem, žádný krok importu certifikátu, žádný úklid. Mac job spouští
node scripts/release.js <product> --publisha Windows job spouští totéž s--no-sign. Všechno, co první workflow dělalo ručně v YAML, teď žije ve skriptu, který už funguje. Workflow je orchestrátor, ne implementace.
Obě platformy chtějí opačné věci a joby to poctivě odrážejí. macOS staví pod hardened runtime, podepsaný a notarizovaný přes vestavěnou cestu electron-builderu (@electron/notarize), s přihlašovacími údaji dodanými z tajemství repozitáře a automaticky detekovanými skriptem. Windows staví nepodepsané NSIS instalátory pro x64 a arm64. Jedno workflow, dva joby, dvě zcela odlišné představy o „hotovo".
Důkaz je v tom, co se nestalo
Takhle poznáš, že pořadí bylo správné: po dvou úpravách z téhož dne, které pojmenovaly běhy a opravily název jednoho token tajemství, se release.yml čtyři měsíce nedotklo. Žádný hotfix selhání podepisování. Žádný spěch kolem odmítnuté notarizace. Žádná panika „nativní binár na runneru chybí". Na pipeline podepisování-a-notarizace — žánr CI nejproslulejší svým zmítáním — jsou čtyři měsíce ticha téměř neslýchané.
Bylo ticho, protože hluk už se odehrál někde levněji. Bitvy, které se obvykle odehrávají v záložce s CI logy, při účtování 10x, jeden force-push po druhém, se odehrály na Macích vývojářů během lokální éry. CI nemuselo být místem, kde se balení ladí, protože balení už bylo vyladěné. To je teze v jednom řádku: přesuň build ze svého Macu teprve poté, co tě tvůj Mac přestane překvapovat.
Jedna provázanost, která ještě může kousnout
Není to úplně bez slabého místa a stojí za to ho pojmenovat, protože je jemné. Swift bináry, na kterých macOS aplikace závisí — OCR, snímání obrazovky, transkriptory — jsou zakomitované v gitu. Ale vydání nedodává zakomitované kopie; build je na runneru překompilovává. A brána check-swift-version.js tvrdě shodí celé vydání, pokud kompilátor není zafixovaná verze. U tohoto vydání byla ta fixace Swift 6.2.x a nic ve workflow ji neinstaluje — job prostě věří, že výchozí Swift na macos-latest odpovídá. V den, kdy GitHub zvedne image svého runneru za tuto fixaci, se vydání zastaví, ne proto, že by na aplikaci bylo něco špatně, ale protože pipeline je tiše provázaná s image stroje, který neovládá. Je to jediný implementační detail, který tohle tenké workflow neposunulo dolů do něčeho, co samo vlastní.
Druhá polovina vydání: menší payloady
Vydání neslo jednu nesouvisející funkci hodnou zmínky, protože je to čistý příklad opravy latence odečtením. Akce Assist Me posílala backendu při každém požadavku víc kontextu, než potřebovala. Jeden commit osekal dvě věci: posílaná historie transkripce klesla z posledních 30 záznamů na posledních 15 a kontextové soubory profilu uživatele — vkládané do system promptu při každém volání — byly tvrdě omezeny na 4,000 znaků se značkou oříznutí. Míň k serializaci, míň k nahrání, míň ke čtení pro model, než začne odpovídat, což je metrika, kterou commit optimalizuje: time-to-first-token. V zájmu poctivosti: commit zrychlení tvrdí, ale neměří ho — v historii není žádné číslo před/po, takže to ber jako dobře zdůvodněné osekání, ne jako výhru změřenou benchmarkem.
Tři věci, které nás toto vydání naučilo
- CI je tenký obal nad skriptem, který už funguje. 66řádkové workflow nemá vlastní logiku podepisování; volá skript, který tři měsíce lokálních vydání už vyladily. Dej mechanismus do něčeho, co umíš spustit ručně, a nech CI jen rozhodovat, kdy ho spustit.
- Automatizuj práci, ne nutně spouštěč. První pokus zemřel, protože vydání spouštěná tagem automaticky utrácela runner minuty účtované s 10x. Ruční
workflow_dispatchzachovává automatizaci a odstraňuje část, která stála peníze — někdy je člověk ve smyčce ta funkce. - Laď tam, kde je iterace levná. Každá bitva o balení vyřešená na Macu vývojáře je bitva, která se nikdy neodehraje v záložce s CI logy za desetinásobnou cenu. Z Macu se odstěhuj poslední, ne první.
Předchozí kapitolu příběhu v1 najdete v dodání třiceti jazyků bez záchranné sítě (v1.8.3); a celý oblouk v anatomii dodávání softwaru k dokonalosti.