clowner: Menggandakan Aplikasi macOS Jadi Dua Salinan yang Berdiri Sendiri
Satu skrip Bash untuk menyalin sebuah .app menjadi salinan kedua dengan bundle identifier sendiri — dua Brave, dua Slack, dua apa pun, jalan berdampingan dengan data terpisah.
Masalahnya: saya butuh dua browser yang benar-benar terpisah — satu untuk akun pribadi, satu untuk kerja — tanpa saling berbagi sesi, dan tanpa harus berganti profil setiap kali.
macOS mengenali sebuah aplikasi dari CFBundleIdentifier di Info.plist-nya. Dua salinan dengan identifier yang sama dianggap satu aplikasi yang sama; ganti identifier-nya, salinan itu jadi aplikasi yang berbeda di mata sistem. Masalahnya, menyunting Info.plist merusak tanda tangan kode aslinya — jadi salinannya harus ditandatangani ulang.
clowner melakukan keduanya. Satu skrip Bash, memanggil perkakas yang memang sudah ada di setiap Mac.
curl -fsSL https://raw.githubusercontent.com/arisros/clowner/main/install.sh | sh
clowner # jalan tanpa argumen = wizard
Yang dikerjakan
- Salin bundle-nya dengan
cp -a. - Tulis ulang
CFBundleIdentifierdi setiapInfo.plistdi dalam bundle yang identifier-nya berawalan sama dengan aslinya — aplikasinya sendiri berikut helper-nya (…helper,…helper.renderer). Bundle bersarang yang tidak berhubungan dibiarkan. - Buang kunci auto-update (
KSProductID,SUFeedURL, …). - Kalau salinannya perlu jalan sebagai instance sendiri, ganti
Contents/MacOS/<exe>dengan pembungkus dua baris yang meng-execbinary aslinya dengan flag isolasi. - Bersihkan atribut quarantine, lalu tanda tangani ulang:
codesign --force --deep --sign -.
Tiga hal yang tidak terduga
Kunci auto-update harus dibuang. Kalau tidak, salinan dan aslinya sama-sama menunjuk ke feed Sparkle atau Keystone yang sama, lalu berebut memperbarui satu sama lain. Ada --keep-updater untuk yang memang menginginkannya, tapi defaultnya dibuang.
Nama aplikasi tidak cuma ada di Info.plist. Aplikasi yang melokalkan nama tampilannya — iWork dan banyak lainnya — menyimpannya di Contents/Resources/*.lproj/InfoPlist.strings, dan nilai di situ menang atas Info.plist. Sebelum ini ketahuan, menu bar dan Finder tetap menampilkan nama aslinya meski Info.plist sudah diubah.
Ikon juga punya jalur pintas sendiri. Aplikasi modern memuat ikonnya dari Assets.car yang sudah dikompilasi lewat CFBundleIconName, dan itu mengalahkan .icns biasa. Jadi ketika --icon dipakai, kunci CFBundleIconName ikut dihapus supaya .icns yang baru benar-benar terpakai. Penukaran ikonnya harus terjadi sebelum penandatanganan ulang, kalau tidak tanda tangannya langsung rusak lagi.
Bukan cuma browser
Intinya — salin, ganti identitas, tandatangani ulang — berlaku untuk .app apa pun. Browser cuma kasus yang butuh satu hal tambahan: flag supaya salinan kedua membuka jendelanya sendiri, bukan menyerahkan perintahnya ke instance yang sudah berjalan. Chromium dideteksi otomatis dan diberi --user-data-dir, Firefox diberi -no-remote --profile.
Aplikasi biasa tidak butuh pembungkus sama sekali dan bisa digandakan telanjang dengan --no-profile. Untuk aplikasi yang punya kunci single-instance sendiri, --args menerima flag apa pun:
clowner clone --src "/Applications/Foo.app" --name foo2 --args "--its-own-flag=$HOME/foo2"
Kenapa Bash
Tidak ada yang di-compile dan tidak ada runtime yang perlu dipasang. Skripnya jalan di /bin/bash bawaan macOS — versi 3.2, yang usianya sudah belasan tahun — jadi tidak ada langkah “install Bash dulu” sebelum bisa memakai alat yang tugasnya cuma menyalin satu folder. fzf opsional: kalau ada, pemilih aplikasinya bisa dicari; kalau tidak, jadi menu bernomor.
Setelah menggandakan, macOS kadang masih menyimpan nama dan ikon lama di cache. touch "/Applications/<clone>.app" lalu killall Dock Finder biasanya cukup.