Steven
Steven8 perc olvasás

A kiadás CI-be helyezése, kétszer

A GeekBye v1.8.4 áthelyezte a kiadási buildeket a CI-be macOS-re és Windowsra. Amit a changelog nem mond el, az az, hogy ezt egyszer már megpróbáltuk, egy hónappal később töröltük a runner-költségek miatt, és csak másodszorra sikerült megtartani — mert addigra a kiadási szkript már kézzel is működött.

Mérnöki munka
CI/CD
Desktop
GeekBye kiadások
A kiadás CI-be helyezése, kétszer

A v1.8.4 changelogjában van egy sor, amely tiszta házimunkának hangzik: „A kiadási buildek mostantól a CI-ből jönnek macOS-re és Windowsra egyaránt." Úgy olvasható, mint az a fajta dolog, amely egyszer megtörténik, tisztán, és soha többé nem gondolsz rá. Nem így volt. A csapat már egyszer betette a kiadásokat a CI-be — és kitépte onnan. Ez a kiadás a második próbálkozás, és az ok, amiért megmaradt, a tényleges mérnöki tanulság, úgyhogy mondjuk el sorrendben.

Az első próbálkozás, és miért halt meg

Még 2025 októberében volt egy release.yml. Tag pusholásra indult — belöksz egy v*.*.* taget, és egy macOS runner elindult, buildelt és publikált. A saját aláírási beállítását kézzel végezte: egy lépés, amely dekódolta az aláíró tanúsítványt, létrehozott és feloldott egy ideiglenes keychaint, importálta a tanúsítványt, hogy a codesign megtalálja, és egy hozzá illő takarítási lépés a végén. Működött. Ráadásul csak macOS-es volt — a Windows-kiadások egyáltalán nem voltak a CI-ben.

November 4-én törölték. A commit üzenete szokatlanul őszinte az okról: "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." (távolítsd el a GitHub Actions kiadási workflow-t a percek megőrzése érdekében / a macOS runnerek 10x szorzóba kerülnek — 100 számlázott perc egy 10 perces kiadásonként. A kiadások helyben készülnek.) Ez az első próbálkozás teljes története egyetlen mondatban. A GitHub a macOS runner idejét a Linuxénak tízszeres díján számlázza, és egy tag által kiváltott kiadás azt jelenti, hogy minden egyes tag — beleértve az eldobhatókat, az újratageléseket, az „ajjaj, elfelejtettem verziót emelni" fajtát — csendben elkölt száz számlázott percet. Az automatizálás, amelyet nem irányítasz, olyan automatizálás, amely pénzt költ, miközben alszol. Így a kiadások visszakerültek egy fejlesztő Macjére a következő három és fél hónapra.

Ahol valójában megoldódtak a nehéz problémák

Íme a rész, amely a második próbálkozást működővé teszi, és teljes egészében a CI-n kívül történt. A helyi kiadások e három és fél hónapja alatt a scripts/release.js — az egyszerű Node szkript, amelyet egy fejlesztő kézzel futtat — magába szívta az alkalmazás minden csomagolási problémáját, egyszerre egy javítást:

  • White-label visszaállítás. Ez egy white-label kódbázis, amely a GeekBye-t és a Pavleurt is egyetlen forrásból buildeli, ami azt jelenti, hogy egy kiadás lecseréli a package.json mezőit és az ikon-asset-eket a cél-termékre. Két javítás megtanította a szkriptet, hogy utána mindent tegyen vissza, így a Pavleur buildelése nem hagyta a git fádat piszkosan Pavleur identitásával.
  • Dual-arch Windows. Egy javítás, hogy mindkét Windows telepítőt buildelje, x64-et és arm64-et, egy helyett.
  • Az aláíratlan Windows workaround. Az újabb electron-builder figyelmen kívül hagyta a config flaget, amely a Windows-aláírás kikapcsolására szolgált, így a szkript megtanulta, hogy inkább aláíratlan buildet kényszerítsen ki azzal, hogy CSC_IDENTITY_AUTO_DISCOVERY=false-t állít a környezetben — egy ránc, amelyet csak úgy találsz meg, ha beleütközöl.
  • A notarizáció automatikus felismerése. A szkript megtanulta megnézni a környezetét és dönteni: van aláírási hitelesítő adat? Írd alá. Apple notarizációs hitelesítő adat is van? Notarizáld. Egyik sem? Aláíratlan build. Nincs megjegyzendő flag; a titkok jelenléte maga a konfiguráció.

Egyik sem csillogó. Mind az a fajta dolog, amely, ha egy CI runneren belül lep meg, egy óra push-várj-bukj-olvasd-a-logokat kerül próbálkozásonként — 10x számlázás mellett. Egy Macen megoldva, amely előtt ülsz, mindegyik egy percbe kerül.

A második próbálkozás: hatvanhat sor

Amikor a CI visszatért a v1.8.4-ben, a workflow 66 soros volt, és meghatározó tulajdonsága az, hogy milyen keveset csinál. A commit egyszerűen írja le: "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." (kézi workflow_dispatch trigger, amely mindkét platformot párhuzamosan buildeli, újrahasználva a meglévő release.js szkriptet. A macOS buildek aláírtak és notarizáltak, a Windows buildek aláíratlanok.) Minden benne lévő tervezési döntés heg az első próbálkozásból:

  • workflow_dispatch, nem tag által kiváltott. Egy kiadást a „Run workflow" gombra kattintva indítasz. Ember őrzi minden fizetett macOS percet. A költségprobléma, amely megölte az első verziót, egyszerűen úgy oldódik meg, hogy nem automatizálod a triggert — az egyetlen hely, ahol az automatizálás aktívan ártalmas volt.
  • Egy product input. A dispatch egy legördülőt kap — geekbye vagy pavleur — így ugyanaz a workflow bármelyik márkát szállítja. A white-label varrat egészen a kiadás gombig kifut.
  • Két párhuzamos job. build-mac a macos-latest-en, build-win a windows-latest-en, egyszerre futva. A Windows a CI-ben itt igazán új; az októberi workflow soha nem buildelte.
  • Semmi aláírási logika a YAML-ban. Ez az egész lényeg. Nincs keychain-zsonglőrködés, nincs tanúsítvány-import lépés, nincs takarítás. A Mac-job a node scripts/release.js <product> --publish-t futtatja, a Windows-job pedig ugyanazt --no-sign-nal. Minden, amit az első workflow kézzel csinált a YAML-ban, most a szkriptben él, amely már működik. A workflow vezénylő, nem implementáció.

A két platform ellentétes dolgokat akar, és a jobok ezt őszintén tükrözik. A macOS hardened runtime alatt buildel, aláírva és notarizálva az electron-builder beépített útján keresztül (@electron/notarize), a repository titkaiból szolgáltatott és a szkript által automatikusan felismert hitelesítő adatokkal. A Windows aláíratlan NSIS telepítőket buildel x64-re és arm64-re. Egy workflow, két job, a „kész" két teljesen eltérő fogalma.

A bizonyíték abban van, ami nem történt meg

Íme, honnan tudod, hogy a sorrend helyes volt: a két aznapi finomítás után, amely elnevezte a futtatásokat és kijavította egy token-titok nevét, a release.yml-t négy hónapig nem érintették újra. Semmi aláírási hiba hotfix. Semmi notarizáció-elutasítási kapkodás. Semmi „a natív bináris hiányzik a runnerről" pánik. Egy aláírási-és-notarizációs pipeline esetében — a CI azon műfaja, amely a leghírhedtebb a vergődéséről — négy hónap csend szinte hallatlan.

Azért volt csend, mert a zaj már megtörtént valahol olcsóbban. A harcok, amelyek általában egy CI-log fülön játszódnak le, 10x számlázás mellett, egyszerre egy force-push, a fejlesztők Macjein játszódtak le a helyi korszakban. A CI-nek nem kellett annak a helynek lennie, ahol a csomagolást kidebugolják, mert a csomagolás már ki volt debugolva. Ez a tézis egyetlen sorban: a buildet csak azután helyezd át a Macedről, hogy a Maced már nem lep meg.

Az egyetlen csatolás, amely még haraphat

Nincs teljesen gyenge pont nélkül, és érdemes megnevezni, mert finom. A Swift binárisok, amelyektől a macOS alkalmazás függ — OCR, képernyőrögzítés, a transzkriptorok — be vannak commitolva a gitbe. De a kiadás nem a commitolt másolatokat szállítja; a build újrafordítja őket a runneren. És egy check-swift-version.js kapu keményen elbuktatja az egész kiadást, ha a fordító nem a rögzített verzió. Ennél a kiadásnál ez a rögzítés Swift 6.2.x volt, és semmi a workflow-ban nem telepíti — a job egyszerűen megbízik abban, hogy a macos-latest alapértelmezett Swiftje illeszkedik. Azon a napon, amikor a GitHub a runner-képét a rögzítésen túlra emeli, a kiadás leáll, nem azért, mert bármi baj lenne az alkalmazással, hanem mert a pipeline csendben egy olyan gépképhez van csatolva, amelyet nem irányít. Ez az egyetlen implementációs részlet, amelyet ez a vékony workflow nem tolt le valamibe, amelyet birtokol.

A kiadás másik fele: kisebb payloadok

A kiadás egy nem kapcsolódó funkciót is hozott, amely említést érdemel, mert tiszta példa a latencia kivonással történő javítására. Az Assist Me művelet több kontextust küldött a backendnek, mint amennyire szüksége volt minden kérésnél. Egy commit két dolgot nyesett meg: a küldött transzkripciós előzmény az utolsó 30 bejegyzésről az utolsó 15-re csökkent, a felhasználó profil-kontextusfájljai pedig — amelyek minden híváskor a system promptba kerülnek — keményen 4,000 karakterre lettek korlátozva egy csonkolási jelölővel. Kevesebb szerializálni, kevesebb feltölteni, kevesebb a modellnek elolvasni, mielőtt válaszolni kezd, ami a metrika, amelyre a commit optimalizál: time-to-first-token. A becsületesség kedvéért: a commit állítja a gyorsulást, de nem méri — nincs előtte/utána szám az előzményben, úgyhogy kezeld jól megindokolt nyesésként, nem benchmarkkal mért nyereségként.

Három dolog, amit ez a kiadás tanított nekünk

  1. A CI vékony burkolat egy szkript felett, amely már működik. A 66 soros workflow-nak nincs saját aláírási logikája; egy szkriptet hív, amelyet három hónapnyi helyi kiadás már kidebugolt. Tedd a mechanizmust valamibe, amelyet kézzel is le tudsz futtatni, és hagyd, hogy a CI csak azt döntse el, mikor futtassa.
  2. A munkát automatizáld, nem feltétlenül a triggert. Az első próbálkozás azért halt meg, mert a tag által kiváltott kiadások automatikusan költöttek 10x-számlázott runner-perceket. Egy kézi workflow_dispatch megtartja az automatizálást, és eltávolítja a részt, amely pénzbe került — néha az ember a hurokban a funkció.
  3. Ott debugolj, ahol az iteráció olcsó. Minden csomagolási harc, amely egy fejlesztő Macjén oldódik meg, olyan harc, amely soha nem történik meg egy CI-log fülön tízszeres áron. A Macről utoljára költözz le, ne először.

A v1-történet előző fejezetéért lásd harminc nyelv szállítása biztonsági háló nélkül (v1.8.3); a teljes ívért pedig a szoftver tökéletesre szállításának anatómiája.