Steven
Steven5 minit bacaan

Satu Codebase, Dua Aplikasi: Cara White-Label Tanpa Membuat Fork

GeekBye dan Pavleur ialah dua aplikasi desktop berjenama berbeza yang dibina daripada satu repositori — tiada fork, tiada codebase berganda. Inilah jentera masa build yang membolehkan satu codebase dikompil menjadi dua produk, dan pepijat satu baris yang membuatkan aplikasi kedua kami memperkenalkan dirinya dengan nama yang salah.

Kejuruteraan
Seni Bina
Binaan
GeekBye Releases
Satu Codebase, Dua Aplikasi: Cara White-Label Tanpa Membuat Fork

GeekBye ialah aplikasi pencatat mesyuarat. Pavleur pula ialah aplikasi latihan temu duga berkuasa AI. Kedua-duanya kelihatan seperti dua produk berbeza kerana ia memang dua produk berbeza — tetapi ia dibina daripada satu codebase, tanpa fork dan tanpa cabang yang makin lama makin terpesong. Inilah sisi kejuruteraan white-labeling: bukan "mengapa menjual aplikasi berjenama" (itu tulisan lain), sebaliknya bagaimana anda benar-benar mengompil satu repositori menjadi dua aplikasi — dan pepijat tidak glamor yang bersembunyi dalam setiap sistem white-label sehinggalah anda pergi mencarinya.

Jana, jangan buat cabang

Cara yang salah untuk menjalankan dua produk daripada satu codebase ialah menabur if (product === 'pavleur') di seluruh aplikasi, atau membuat fork dan berdoa agar kedua-dua salinan tidak akan menyimpang. Kedua-duanya cepat mereput.

Pendekatan GeekBye ialah penjanaan kod pada masa build. Setiap produk mempunyai fail config — configs/geekbye/config.ts, configs/pavleur/config.ts — yang menyimpan identitinya: nama, bundle id, protokol URL, repositori releases untuk kemas kini automatik, dan seterusnya. Sebelum build, satu skrip dijalankan:

node scripts/build-product.js pavleur

Skrip itu membaca config produk yang dipilih dan melakukan tiga perkara konkrit:

  1. Menjana dua modul config — satu untuk renderer (src/config/product.ts), satu untuk main process (electron/config/product.ts) — setiap satunya dicap dengan pengepala "DO NOT EDIT MANUALLY — changes will be overwritten". Baki aplikasi hanya meng-import productName, applicationId, dan protocol daripada config dan tidak pernah memikirkan jenama mana dirinya.
  2. Menyalin hanya aset jenama itu — ikon, logo kecil, ikon pemberitahuan toast. Build GeekBye secara fizikal mengandungi sifar grafik Pavleur dan begitu juga sebaliknya. Tiada apa-apa yang boleh bocor.
  3. Menulis semula package.json (pada build keluaran) — app id, nama produk, protokol URL, repo GitHub releases untuk penerbitan, dan, dengan kemasnya, teks kebenaran mikrofon berjenama supaya macOS bertanya "Pavleur ingin mengakses mikrofon," bukan jenama yang salah.

Keseluruhan aplikasi bersifat bebas jenama; jenama disuntik pada masa build. Tiada suis produk masa jalanan yang boleh tersilap, kerana pada masa jalanan hanya pernah ada satu produk yang tertanam.

Satu backend, dua jenama

Kedua-dua aplikasi bercakap dengan backend yang sama. Jadi bagaimana pelayan tahu sesuatu permintaan itu milik produk yang mana — dan penetapan harga, prompt, serta had yang mana?

Dua isyarat, kedua-duanya ditetapkan pada masa build. Pertama, satu application id (geekbye atau pavleur) yang tertanam dalam config dan dihantar bersama permintaan, supaya backend boleh menghalakan kepada tingkah laku produk yang betul. Kedua, satu User-Agent yang dilampirkan client pada setiap panggilan backend, berbentuk Product/version (platform) — cth. Pavleur/1.2.5 (win32) atau GeekBye/1.7.3 (darwin). Header tunggal itu memberi backend atribusi setiap produk dan setiap OS secara percuma, tanpa medan analitik berasingan. Sementara itu, kemas kini automatik kekal terasing: setiap produk menerbitkan ke repositori releases-nya sendiri, jadi kemas kini GeekBye tidak akan sekali-kali disajikan kepada pemasangan Pavleur.

Onboarding juga berbeza mengikut produk — wizard GeekBye berkisar tentang transkripsi mesyuarat, wizard Pavleur berkisar tentang latihan temu duga — dan yang betul dipilih pada masa jalanan daripada satu application id yang tertanam itu. Jentera yang sama, dua pintu hadapan.

Pepijat itu: apabila Pavleur memanggil dirinya GeekBye

Inilah kisah perangnya, dan ia kisah yang versinya dimiliki oleh setiap sistem white-label.

Penjanaan config berfungsi. Ikon bertukar. Gesaan mikrofon berjenama. Protokol dan bundle id betul. Mengikut setiap ukuran yang kelihatan, build Pavleur ialah Pavleur. Namun, untuk seketika, setiap build Pavleur menghubungi backend sambil memanggil dirinya GeekBye.

Puncanya ialah satu string yang di-hardcode, dan ia bersembunyi di tempat paling tidak glamor yang boleh dibayangkan: pembina User-Agent, terselit di dalam helper retry rangkaian. Segala-galanya yang menghadap pengguna sudah dialirkan melalui config, tetapi utiliti paip-dalam yang satu ini masih mempunyai literal `GeekBye/${version}` di dalamnya. Tiada sesiapa menganggap utiliti retry sebagai "penjenamaan", jadi tiada sesiapa memeriksanya. Pembetulannya empat baris — import productName dinamik daripada config dan interpolasikannya menggantikan jenama yang di-hardcode — tetapi pengajarannya ialah inti keseluruhannya:

White-labeling bukan "adakah logo bertukar." Ia ialah "adakah setiap string identiti melalui config." Dan saki-baki terakhir tidak pernah tinggal di UI, tempat anda akan mencari. Ia tinggal dalam User-Agent, pengendali protokol deep-link, teks gesaan kebenaran, mesej ralat — paip yang tiada sesiapa failkan di bawah "jenama". Satu-satunya pertahanan yang boleh dipercayai ialah melayan literal jenama sebagai sesuatu yang anda buru secara aktif: grep codebase untuk nama yang di-hardcode dan gagalkan build jika ia muncul di mana-mana di luar config yang sepatutnya mentakrifkannya.

Tiga perkara yang white-labeling ajarkan kepada kami

  1. Jana, jangan buat cabang. Codegen masa build bagi dua modul config kecil serta penyalinan aset terpilih mengalahkan kedua-dua fork dan belukar semakan produk masa jalanan. Aplikasi kekal bebas jenama; jenama ialah input build.
  2. String identiti bocor ke dalam paip. UI ialah 90% yang mudah. 10% yang sukar ialah User-Agent, protokol URL, string kebenaran mikrofon, teks ralat. Alirkan semuanya melalui satu export productName / applicationId — dan grep nama jenama mentah sebagai pintu CI supaya literal yang tersasar tidak akan sekali-kali dikeluarkan.
  3. Satu backend, banyak jenama, bezakan melalui id dan header. Application id masa build serta User-Agent Product/version (platform) memberi atribusi setiap produk dan setiap platform dengan sifar infrastruktur berganda — mengasingkan dengan bersih identiti masa build yang tidak berubah daripada tingkah laku masa jalanan.

Untuk sisi produk-dan-privasi menjalankan pencatat nota white-label di bawah jenama anda sendiri, lihat pencatat nota AI white-label. Ini ialah bab keenam kisah v1 yang menjadi GeekBye v2 — untuk bab sebelumnya, apa sebenarnya keluaran 127 commit itu (v1.7.0); dan untuk keseluruhan lengkoknya, anatomi mengeluarkan perisian hingga ke kesempurnaan.