electronでリリース用パッケージを作る

先日書いたとおり,electronでMastodonクライアントを作ったのだが,パッケージ化に関してかなり迷走したので,最近のelectron事情を書いておく.

electronに関しては,「ほら,こうしたら簡単に作れるでしょ?」といういわゆるHello World系の記事が非常に多く,細かい困りごとやリリースに必要な情報はあまり出てこない. また,2015年くらいの,electronが流行り始めた時期の記事は結構あるものの,最近はそこまで記事が多くないこともあり,最近のリリース事情がよくわからなかった.

アイコンを作る

アイコンは,どのようなビルド手段を使うにしてもおそらく同じセットが必要になる. これに関してはあまり変更されておらず,古い記事でもかなり役に立った.

とりあえず大本となるpng画像かなにかを手元に用意しておく.

Mac

icnsファイルを用意し,それをelectron-builderやelectron-packagerのオプションに渡すだけでアイコンが設定できる. プログラム本体でアイコン画像を参照したりする必要はない.

icnsを作るために,一応公式では

icon_16x16.png
icon_16x16@2x.png
icon_32x32.png
icon_32x32@2x.png
icon_128x128.png
icon_128x128@2x.png
icon_256x256.png
icon_256x256@2x.png
icon_512x512.png
icon_512x512@2x.png

が必要になると書かれている.このサイズをいちいち用意するのはめんどくさいのだが,なんかよさ気なリサイズサービスとかを使って,画像を用意しよう.

icnsへの変換自体は iconutilコマンドでできる. 詳しくはこのへんが参考になった.

hacknote.jp

Windows

Windowsicoファイルが必要になる. これをelectron-builderやelectron-packagerのオプションに渡すだけで良い.

プログラム内でアイコン画像を読み込む必要はない.

Linux

Linux用のアイコンというのは特別に用意する必要はないのだが(Mac用のicnsをそのまま流用できる),Linuxはちょっと特殊で,パッケージインストール時のアイコンは確かにelectron-builderやelectron-packagerのオプションとして渡す. しかし,アプリケーション起動した際に表示されるアイコンは,electronのプロセス内で指定する必要がある.

github.com

というわけで,アイコンを特別に用意する必要はないのだが,アイコン画像のpngMac用のicnsを用意しておこう.

electron-builderの設定は次章でやるとして,起動後のアプリアイコンの読み込みを書いてやる必要がある.

src/main/index.js あたりでwindowを作る処理がある.その付近で,

mainWindow = new BrowserWindow({
  titleBarStyle: 'hidden',
  width: 640,
  height: 480,
  useContentSize: true,
  icon: path.join(__dirname, '../../build/icons/256x256.png')
})

とかやってアイコン画像を読みだしてやる.ちなみに相対パスを解決させているが,これがビルド後にしっかり読み出せるのかどうかは,次章の方法で実際にビルドしてみて,どんなディレクトリ構成のものが出来上がるのかを観察してみるしかない. とりあえずWhalebirdの場合は,上記パスで解決できた.

ビルドする

ビルドツールとして,electron-packagerの情報が非常に多く出てくる. しかし,2018年4月現在,mac, windows, linux用のバイナリをつくるのにelectron-packagerを使うメリットは殆ど無い.

明らかにelectron-builderを使うほうが楽だ(masについてはelectron-builderでうまく行かなかったけど,これについては後日書こう).

というわけでelectron-builderを使ったビルド方法を説明する.

ちなみに公式が非常に参考になる.

Introduction · electron-builder

electron-builderのインストールは終わっている前提で,package.jsonにビルド設定を書いて,scriptsにビルドコマンドを記述していこう.

{
  "name": "Whalebird",
  "version": "0.4.0",
  ...
  "build": {
    "productName": "Whalebird for Mastodon",
    "appId": "org.whalebird.desktop",
    "directories": {
      "output": "build"
    },
    "files": [
      "dist/electron/**/*",
      "build/icons/*"
    ],
    "dmg": {
      "contents": [
        {
          "x": 410,
          "y": 150,
          "type": "link",
          "path": "/Applications"
        },
        {
          "x": 130,
          "y": 150,
          "type": "file"
        }
      ]
    },
    "mac": {
      "icon": "build/icons/icon.icns",
      "target": [
        "dmg"
      ],
      "category": "public.app-category.social-networking"
    },
    "win": {
      "icon": "build/icons/icon.ico",
      "target": "nsis"
    },
    "linux": {
      "icon": "build/icons",
      "target": [
        "deb",
        "rpm"
      ],
      "category": "Network"
    }
  }
}

build節にビルド設定を書く.

大事なポイント: files

これはビルド対象となるファイルを指定する. npm run buildの結果が格納されるdistディレクトリはもちろんのこと,忘れてはいけないのはアイコンが入っている build/iconsだ.

前述の通り,linuxではプロセス起動時にアイコンファイルの読み込みを行う.そのため,ビルドの成果物にアイコンファイルが含まれている必要がある. そのために,filesにアイコンが格納されているディレクトリを記述しておく必要がある.

ビルドコマンド

package.jsonのscriptsで,

{
  "name": "Whalebird",
  "version": "0.4.0",
  "scripts": {
    "build:mac": "node .electron-vue/build.js && electron-builder --mac --x64",
    "build:windows": "node .electron-vue/build.js && electron-builder --win --x64",
    "build:linux": "node .electron-vue/build.js && electron-builder --linux --x64"
  }
}

とか定義しておくと,このスクリプトを動かすだけでビルドしてくれる.

証明書とかどうするの?

WindowsLinuxは適当にバイナリを作っても動くんだけど,Macだけは証明書をちゃんと設定しておかないと,AppStore配布ではないMacアプリとしても動かない.

というわけで証明書を用意する. Macのアプリを作ったことがある人ならわかるだろうけれど,証明書を作ったりするのは結構めんどくさい.

※以降の操作をするには,Appleデベロッパープログラム(有料)に登録している必要がある

必要になる証明書は,

  • Developer ID Application
  • Developer ID Installer

の2つだ.

キーチェーンアクセス -> 証明書アシスタント -> 証明局に証明書を要求

として,CertificateSigningRequestを作成しよう.

そしたら,developer.apple.comのCertificates, Identifiers & Profilesに行く. で,macOSのCertificatesとして,Developer ID ApplciationとDeveloper ID Installerを作成し,これをダウンロードしておこう

ダウンロードした2ファイルをダブルクリック(もしくはドラッグアンドドロップ)してキーチェーンに登録する.

この,キーチェーンに2つの鍵が登録された状態で,npm run build:mac すると,electron-builderが登録されている鍵を使ってビルドしてくれる.

音声ファイル等を埋め込みたい

効果音のファイルだったり,画像等のリソースを使いたい場合がある.

しかし,electron-builderは基本的にasarという形式にバイナリを圧縮してしまう. すると,メインプロセス内でファイルパスを指定して開こうとしても,asar内のファイルであるために開けないことがよくある(asar圧縮しなければ開ける,もちろん開発時もasarになっていないのでちゃんと動く).

そういう場合には,asarを諦めてもいいんだけど,リソースファイルだけasarしないという手段が選べる.

package.jsonのbuildに,extraResourcesという項目を追加する.

  "build": {
    "productName": "Whalebird for Mastodon",
    "appId": "org.whalebird.desktop",
    "directories": {
      "output": "build"
    },
    "extraResources": [
      "build/sounds/*"
    ],

ここに,指定したファイルはasarに圧縮されることはないが,ちゃんとビルド後のパッケージに含まれる.

場所はこちらに記載されている通り.

Application Contents · electron-builder

Twitterクライアントの開発を辞める

3年くらい運用してきたTwitterクライアントがあります.

2018年6月20日に予定されているTwitterAPIのUserStream廃止に伴い,このクライアントの開発を辞めます.

理由は言うまでもなくUserStreamです. 今後はMastodonクライアントとして生まれ変わったので,そちらをよろしくお願いします.

h3poteto.hatenablog.com

今後の予定

2018-04-07

終了告知アラートを載せたアプリをAppStoreに公開します(レビュー次第なので公開の日程については前後する可能性があります). また,このタイミングで終了する旨をAppStoreの説明に記述します.

2018-06-20

AppStoreからWhalebirdを削除.予定通りにいけば,この日にTwitterAPIからUserStreamが削除されるため,UserStreamに依存した処理が全て動かなくなります. 当アプリでの影響範囲は以下のとおりです.

  1. アプリ内でのUserStream利用が不可能になる
  2. プッシュ通知が一切届かなくなる

1に関してはどう考えても無理ですね.

2に関して言うと,現状WhalebirdはRailsのサーバをバックエンドに据えて,アプリからはそこにOAuth認証かけて,RailsサーバにAPIリクエストを送っています.そして,RailsサーバはAPIリクエストをリレーしてTwitterAPI を呼んでいます.

で,そのRailsサーバでプッシュ通知用に全員分のUserStreamをwatchさせておき,通知があったらAPNsに通知を送る仕組みを作っています. そのため,UserStreamが死ぬと,この通知を監視する仕組みがすべて使えなくなります.

公開を終了しても,しばらくはそのままRailsサーバを生かしておくので,既にインストール済みの人は上記機能以外であれば今までどおり使える状態を維持します.

2018-07-20

Railsサーバを停止し,全ての機能が利用できなくなります.

理由

「なにも削除しなくとも,通知が使えなくても残しておけばいいのでは?」とも思って,だいぶ悩んだのですが…….

通知なくなったら自分で自分のアプリを使わなくなると思う

Twitterクライアントというのは,できれば1つで全てを完結してほしい. 通知はAというクライアントで受けて,リストを見るのはBで,つぶやきはCでやる,みたいな器用な使い方はしないわけです.デスクトップならまだしもiOSで. 通知がなくなった時点で,別のアプリケーションで通知を受けるように設定するだろうから,そうなったらもうWhalebirdは使わないわけです(俺が).

で,俺が使わないクライアントを俺が開発・メンテしていくのは無理です

コストも多少なりともかかっているわけでして.

コストについて

iOS版Whalebirdは有料アプリでした. これには理由があります.

  • TwitterのOAuth上限数が制定されたため
  • 人数増加に伴ってサーバ費用が増えることが見込まれたため

このクライアントを作り始めたタイミングで,TwitterサードパーティアプリケーションがOAuth認証できるユーザの上限を10万に制限しました. このため当時は,「Twitterクライアントはビジネスとしてはもう成立しない」と言われました. The Worldなんかは,この問題を,サーバを複数用意し(ClientIDも複数用意)ユーザにどのサーバでOAuthするかを選択させることで,回避してましたね.

まだ,前述のような構成のため,プッシュ通知を必要とするユーザが増えるほど,UserStreamの起動数を増やす必要があります.これは普通のウェブサービスよりよっぽどメモリを食いそうな予感がしたため,「あんまり流行ると困るな」という感覚はありました.

そういうわけで$2程度の有料アプリとして配布してきました.

実際3年運用していて,リソース上限に達するようなこともなく,それほど流行ることもなかったため,特にコストが増えるようなことはありませんでした. が,普通のウェブサービスよりは多少カネをかけました.

EC2 × 2台 + ElastiCache(Redis) × 1台 + RDS(他サービスと共用)

それでもt2.microとかを使っているんで,$40/monthくらいですかね.

使ってくれた方には感謝

開発終了を決めたタイミングでアプリを無料化しました(その時点から売上が立つのはどうも申し訳なかったので).

そのおかげでかなりダウンロードされたようです. 最終的にTwitterアカウント連携してくれた方が187人(俺のアカウントやテスト用のアカウントも含む). UserStream起動が77アカウント分です.

有料時代はだいたいUserStream常時40アカウント分くらい起動していたので,結構増えましたね.

2018年3月31日現在で,ユニット数499,売上$131でした. 運用にかかってる費用を考えると全然赤字ですが,もともとそのつもりだったので.

今までご利用いただきありがとうございました. みなさんのTwitter生活が幸せであったことを祈ります.

最高のmastodonクライアントを探した結果,自作した

デスクトップ向けのmastodonクライアント,whalebirdを作りました. Electron製なのでLinuxでもMacでもWindowsでも動きます(たぶん).

f:id:h3poteto:20180323234814p:plain

続きを読む

匿名ダイアリーの新着エントリーを延々読みたいだけのサービスを作った

みなさんさようなら.

新しいサービスを作ったよ.

https://masuda-stream.net/

また世の中で俺しか使わないようなサービスを作ってしまった.

続きを読む

django-allauthのはてなログイン用providerを作った

みなさんさようなら. またいらんものを作りましたぞ.

続きを読む