Steven
Steven8 λεπτά ανάγνωσης

Βάζοντας την έκδοση στο CI, δύο φορές

Το GeekBye v1.8.4 μετέφερε τα release builds στο CI για macOS και Windows. Αυτό που δεν λέει το changelog είναι ότι το δοκιμάσαμε ξανά μια φορά, το διαγράψαμε έναν μήνα αργότερα λόγω του κόστους των runners, και το κάναμε να κρατήσει μόνο τη δεύτερη φορά — επειδή ως τότε το release script δούλευε ήδη με το χέρι.

Μηχανική
CI/CD
Desktop
Εκδόσεις GeekBye
Βάζοντας την έκδοση στο CI, δύο φορές

Το changelog του v1.8.4 έχει μια γραμμή που ακούγεται σαν καθαρή αγγαρεία: «Τα release builds τώρα έρχονται από το CI και για macOS και για Windows.» Διαβάζεται σαν το είδος του πράγματος που συμβαίνει μία φορά, καθαρά, και δεν το ξανασκέφτεσαι ποτέ. Δεν ήταν έτσι. Η ομάδα είχε ήδη βάλει τις εκδόσεις στο CI μία φορά — και τις ξερίζωσε. Αυτή η έκδοση είναι η δεύτερη προσπάθεια, και ο λόγος που κράτησε είναι το πραγματικό μηχανικό μάθημα, οπότε ας το πούμε με τη σειρά.

Η πρώτη προσπάθεια, και γιατί πέθανε

Πίσω στον Οκτώβριο του 2025 υπήρχε ένα release.yml. Ενεργοποιούνταν σε push tag — σπρώχνεις ένα tag v*.*.* και ένας macOS runner ξεκινούσε, έχτιζε και δημοσίευε. Έκανε τη δική του ρύθμιση υπογραφής με το χέρι: ένα βήμα που αποκωδικοποιούσε το πιστοποιητικό υπογραφής, δημιουργούσε και ξεκλείδωνε ένα προσωρινό keychain, εισήγαγε το πιστοποιητικό ώστε να μπορεί να το βρει το codesign, και ένα αντίστοιχο βήμα καθαρισμού στο τέλος. Δούλευε. Ήταν επίσης μόνο για macOS — οι εκδόσεις Windows δεν ήταν καθόλου στο CI.

Στις 4 Νοεμβρίου, διαγράφηκε. Το μήνυμα του commit είναι ασυνήθιστα ειλικρινές για τον λόγο: "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." (αφαίρεσε το release workflow του GitHub Actions για εξοικονόμηση λεπτών / οι macOS runners κοστίζουν πολλαπλασιαστή 10x — 100 χρεωμένα λεπτά ανά 10λεπτη έκδοση. Οι εκδόσεις θα γίνονται τοπικά.) Αυτή είναι όλη η ιστορία της πρώτης προσπάθειας σε μία πρόταση. Το GitHub χρεώνει τον χρόνο των macOS runners δέκα φορές πιο ακριβά από τον Linux, και μια έκδοση που ενεργοποιείται από tag σημαίνει ότι κάθε tag — συμπεριλαμβανομένων των μιας χρήσης, των re-tags, εκείνων του τύπου «ωχ, ξέχασα να ανεβάσω την έκδοση» — ξοδεύει σιωπηλά εκατό χρεωμένα λεπτά. Ο αυτοματισμός που δεν ελέγχεις είναι αυτοματισμός που ξοδεύει χρήματα ενώ κοιμάσαι. Έτσι οι εκδόσεις γύρισαν στο Mac ενός developer για τους επόμενους τρεισήμισι μήνες.

Πού λύθηκαν στην πραγματικότητα τα δύσκολα προβλήματα

Να το κομμάτι που κάνει τη δεύτερη προσπάθεια να δουλεύει, και συνέβη εξ ολοκλήρου έξω από το CI. Κατά τη διάρκεια εκείνων των τρεισήμισι μηνών τοπικών εκδόσεων, το scripts/release.js — το απλό Node script που ένας developer τρέχει με το χέρι — απορρόφησε κάθε πρόβλημα packaging που είχε η εφαρμογή, ένα fix τη φορά:

  • White-label επαναφορά. Αυτό είναι ένα white-label codebase που χτίζει και το GeekBye και το Pavleur από μία πηγή, που σημαίνει ότι μια έκδοση αλλάζει πεδία στο package.json και icon assets για το προϊόν-στόχο. Δύο fixes έμαθαν το script να τα βάζει όλα πίσω μετά, ώστε το χτίσιμο του Pavleur να μην άφηνε το git δέντρο σου βρόμικο με την ταυτότητα του Pavleur.
  • Dual-arch Windows. Ένα fix για να χτίζει και τους δύο Windows installers, x64 και arm64, αντί για έναν.
  • Το workaround για ανυπόγραφα Windows. Ο νεότερος electron-builder αγνοούσε το config flag που προοριζόταν να απενεργοποιήσει την υπογραφή Windows, οπότε το script έμαθε να επιβάλλει ανυπόγραφο build ορίζοντας αντ' αυτού CSC_IDENTITY_AUTO_DISCOVERY=false στο περιβάλλον — μια πτυχή που τη βρίσκεις μόνο χτυπώντας πάνω της.
  • Αυτόματη ανίχνευση notarization. Το script έμαθε να κοιτάζει το περιβάλλον του και να αποφασίζει: υπάρχουν credentials υπογραφής; Υπόγραψε. Υπάρχουν και credentials notarization της Apple; Νοτάρισε. Κανένα; Ανυπόγραφο build. Κανένα flag για να θυμάσαι· η παρουσία των secrets είναι η διαμόρφωση.

Τίποτα από αυτά δεν είναι λαμπερό. Όλα είναι το είδος του πράγματος που, αν σε εκπλήξει μέσα σε έναν CI runner, σου κοστίζει μία ώρα push-περίμενε-απέτυχε-διάβασε-τα-logs ανά προσπάθεια — με χρέωση 10x. Λυμένα σε ένα Mac μπροστά στο οποίο κάθεσαι, το καθένα κοστίζει ένα λεπτό.

Η δεύτερη προσπάθεια: εξήντα έξι γραμμές

Όταν το CI επέστρεψε στο v1.8.4, το workflow ήταν 66 γραμμές, και η χαρακτηριστική του ιδιότητα είναι το πόσο λίγα κάνει. Το commit το περιγράφει απλά: "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." (χειροκίνητο workflow_dispatch trigger που χτίζει και τις δύο πλατφόρμες παράλληλα, επαναχρησιμοποιώντας το υπάρχον release.js script. Τα macOS builds είναι υπογεγραμμένα και notarized, τα Windows builds είναι ανυπόγραφα.) Κάθε σχεδιαστική απόφαση σε αυτό είναι μια ουλή από την πρώτη προσπάθεια:

  • workflow_dispatch, όχι ενεργοποιούμενο από tag. Ξεκινάς μια έκδοση πατώντας «Run workflow». Ένας άνθρωπος ελέγχει κάθε πληρωμένο macOS λεπτό. Το πρόβλημα κόστους που σκότωσε την πρώτη έκδοση λύνεται απλώς μη αυτοματοποιώντας τον trigger — το μόνο σημείο όπου ο αυτοματισμός ήταν ενεργά επιβλαβής.
  • Ένα input product. Το dispatch παίρνει ένα dropdown — geekbye ή pavleur — ώστε το ίδιο workflow να στέλνει όποιο brand. Η ραφή white-label φτάνει μέχρι έξω, ως το κουμπί έκδοσης.
  • Δύο παράλληλα jobs. build-mac σε macos-latest, build-win σε windows-latest, τρέχοντας ταυτόχρονα. Τα Windows στο CI είναι πραγματικά νέα εδώ· το workflow του Οκτωβρίου δεν τα έχτιζε ποτέ.
  • Καμία λογική υπογραφής στο YAML. Αυτό είναι όλο το νόημα. Δεν υπάρχει ζογκλερικό με keychain, κανένα βήμα import πιστοποιητικού, κανένας καθαρισμός. Το Mac job τρέχει node scripts/release.js <product> --publish και το Windows job τρέχει το ίδιο με --no-sign. Ό,τι έκανε το πρώτο workflow με το χέρι στο YAML ζει τώρα στο script που ήδη δουλεύει. Το workflow είναι ενορχηστρωτής, όχι υλοποίηση.

Οι δύο πλατφόρμες θέλουν αντίθετα πράγματα, και τα jobs το αντανακλούν τίμια. Το macOS χτίζει κάτω από hardened runtime, υπογεγραμμένο και notarized μέσα από την ενσωματωμένη διαδρομή του electron-builder (@electron/notarize), με τα credentials να παρέχονται από τα secrets του repository και να ανιχνεύονται αυτόματα από το script. Το Windows χτίζει ανυπόγραφους NSIS installers για x64 και arm64. Ένα workflow, δύο jobs, δύο εντελώς διαφορετικές έννοιες του «έτοιμο».

Η απόδειξη είναι σε αυτό που δεν συνέβη

Να πώς ξέρεις ότι η σειρά ήταν σωστή: μετά τις δύο ίδιας-ημέρας διορθώσεις που ονόμασαν τα runs και διόρθωσαν το όνομα ενός token secret, το release.yml δεν ξαναγγίχτηκε για τέσσερις μήνες. Κανένα hotfix για αποτυχία υπογραφής. Καμία φούρια απόρριψης notarization. Κανένας πανικός «το native binary λείπει από τον runner». Για ένα pipeline υπογραφής-και-notarization — το είδος CI με τη χειρότερη φήμη για ταλαιπωρία — τέσσερις μήνες σιωπής είναι σχεδόν ανήκουστο.

Ήταν ήσυχα επειδή ο θόρυβος είχε ήδη γίνει κάπου φθηνότερα. Οι μάχες που συνήθως παίζονται σε ένα tab με CI logs, με χρέωση 10x, ένα force-push τη φορά, είχαν παιχτεί στα Mac των developers κατά την τοπική εποχή. Το CI δεν χρειάστηκε να είναι εκεί όπου έγινε το debug του packaging, επειδή το packaging είχε ήδη γίνει debug. Αυτή είναι η θέση σε μία γραμμή: μετακίνησε το build από το Mac σου μόνο αφού το Mac σου σταματήσει να σε εκπλήσσει.

Η μία σύζευξη που μπορεί ακόμη να δαγκώσει

Δεν είναι εντελώς χωρίς αδύναμο σημείο, και αξίζει να το ονομάσουμε επειδή είναι λεπτό. Τα Swift binaries από τα οποία εξαρτάται η macOS εφαρμογή — OCR, screen capture, οι transcribers — είναι committed στο git. Αλλά η έκδοση δεν στέλνει τα committed αντίγραφα· το build τα ξαναχτίζει στον runner. Και μια πύλη check-swift-version.js αποτυγχάνει σκληρά ολόκληρη την έκδοση αν ο compiler δεν είναι η καρφωμένη έκδοση. Σε αυτή την έκδοση αυτό το κάρφωμα ήταν Swift 6.2.x, και τίποτα στο workflow δεν το εγκαθιστά — το job απλώς εμπιστεύεται ότι το προεπιλεγμένο Swift του macos-latest ταιριάζει. Την ημέρα που το GitHub ανεβάσει την εικόνα του runner του πέρα από το κάρφωμα, η έκδοση σταματά, όχι επειδή κάτι δεν πάει καλά με την εφαρμογή, αλλά επειδή το pipeline είναι σιωπηλά συζευγμένο με μια εικόνα μηχανήματος που δεν ελέγχει. Είναι η μία λεπτομέρεια υλοποίησης που αυτό το λεπτό workflow δεν έσπρωξε κάτω σε κάτι που κατέχει το ίδιο.

Το άλλο μισό της έκδοσης: μικρότερα payloads

Η έκδοση κουβάλησε μια άσχετη λειτουργία που αξίζει αναφορά, επειδή είναι ένα καθαρό παράδειγμα διόρθωσης latency με αφαίρεση. Η ενέργεια Assist Me έστελνε στο backend περισσότερο context από όσο χρειαζόταν σε κάθε αίτημα. Ένα commit έκοψε δύο πράγματα: το ιστορικό μεταγραφής που στελνόταν μαζί έπεσε από τις τελευταίες 30 εγγραφές στις τελευταίες 15, και τα αρχεία context του προφίλ του χρήστη — που εγχέονται στο system prompt σε κάθε κλήση — περιορίστηκαν σκληρά στους 4,000 χαρακτήρες με έναν δείκτη περικοπής. Λιγότερα για σειριοποίηση, λιγότερα για ανέβασμα, λιγότερα για να διαβάσει το μοντέλο πριν αρχίσει να απαντά, που είναι η μετρική που βελτιστοποιεί το commit: time-to-first-token. Χάριν ειλικρίνειας: το commit ισχυρίζεται την επιτάχυνση αλλά δεν τη μετράει — δεν υπάρχει νούμερο πριν/μετά στο ιστορικό, οπότε αντιμετώπισέ το ως ένα καλά αιτιολογημένο κόψιμο παρά ως μια μετρημένη με benchmark νίκη.

Τρία πράγματα που μας δίδαξε αυτή η έκδοση

  1. Το CI είναι ένα λεπτό περιτύλιγμα πάνω από ένα script που ήδη δουλεύει. Το workflow 66 γραμμών δεν έχει δική του λογική υπογραφής· καλεί ένα script που τρεις μήνες τοπικών εκδόσεων είχαν ήδη ξεψειρίσει. Βάλε τον μηχανισμό σε κάτι που μπορείς να τρέξεις με το χέρι, και άσε το CI απλώς να αποφασίζει πότε να το τρέξει.
  2. Αυτοματοποίησε τη δουλειά, όχι απαραίτητα τον trigger. Η πρώτη προσπάθεια πέθανε επειδή οι εκδόσεις που ενεργοποιούνταν από tag ξόδευαν αυτόματα λεπτά runner χρεωμένα με 10x. Ένα χειροκίνητο workflow_dispatch κρατά τον αυτοματισμό και αφαιρεί το κομμάτι που κόστιζε χρήματα — μερικές φορές ο άνθρωπος στον βρόχο είναι η λειτουργία.
  3. Κάνε debug εκεί όπου η επανάληψη είναι φθηνή. Κάθε μάχη packaging που λύθηκε στο Mac ενός developer είναι μια μάχη που δεν συμβαίνει ποτέ σε ένα tab με CI logs με δεκαπλάσια τιμή. Μετακινήσου από το Mac τελευταίος, όχι πρώτος.

Για το προηγούμενο κεφάλαιο της ιστορίας του v1, κυκλοφορώντας τριάντα γλώσσες χωρίς δίχτυ ασφαλείας (v1.8.3)· και για ολόκληρη την πορεία, η ανατομία της παράδοσης λογισμικού στην τελειότητα.

Σχετικά Άρθρα

Οι δύο τρόποι αστοχίας ενός click-through overlay
Steven
Steven9 λεπτά ανάγνωσης

Οι δύο τρόποι αστοχίας ενός click-through overlay

Το παράθυρο του GeekBye επιπλέει πάνω από τα πάντα και αφήνει τα κλικ σου να περνούν μέσα από αυτό — εκτός από εκεί όπου έχει κουμπιά. Αυτό είναι ένα αμφίπλευρο συμβόλαιο, και το v1.8.5 και το v1.8.14 δείχνουν πώς μοιάζει όταν σπάει η κάθε πλευρά: μια έκδοση όπου το overlay κατάπιε έναν διάλογο συστήματος, μία όπου έκλεψε τα πλήκτρα σου. Η νικήτρια διόρθωση για τη δεύτερη ήταν η διαγραφή κώδικα.

Μηχανική
Electron
Desktop
Κυκλοφορώντας τριάντα γλώσσες χωρίς δίχτυ ασφαλείας
Steven
Steven8 λεπτά ανάγνωσης

Κυκλοφορώντας τριάντα γλώσσες χωρίς δίχτυ ασφαλείας

Το GeekBye v1.8.3 συνέδεσε το react-i18next και μετέφρασε ολόκληρη την εφαρμογή σε 30 γλώσσες. Το κομμάτι της library ήταν ρουτίνα. Το ενδιαφέρον κομμάτι είναι αυτό που έλειπε: κανένας έλεγχος parity κλειδιών και κανένας extractor strings — έτσι hardcoded αγγλικά ξέφυγαν, ένα δανέζικο τυπογραφικό λάθος κυκλοφόρησε, και το changelog δεν μπορούσε να συμφωνήσει αν η εφαρμογή μιλούσε 28, 29 ή 30 γλώσσες.

Μηχανική
i18n
Desktop
Τέσσερις Εκδόσεις σε Είκοσι Έξι Ώρες: Σωληνώσεις Γλώσσας και ένα «Bridge Release»
Steven
Steven6 λεπτά ανάγνωσης

Τέσσερις Εκδόσεις σε Είκοσι Έξι Ώρες: Σωληνώσεις Γλώσσας και ένα «Bridge Release»

GeekBye από το v1.7.6 ως το v1.8.1: μια ρύθμιση γλώσσας που δεν είχε φύγει ποτέ από το laptop, ένα overlay στο Cmd+/ που δείχνει τα πραγματικά σου keybindings, μια έκδοση με μηδέν αλλαγές κώδικα — και το παράδοξο της κότας και του αυγού όταν μεταφέρεις το feed του auto-update.

Μηχανική
Release
Desktop