🐙

Electronで開発したデスクトップアプリをSnap Storeに公開する

2023/04/26に公開

本記事について

本記事ではElectronにて開発したデスクトップアプリをSnap Storeに公開する方法を紹介します。厳密には、Electronアプリをパッケージ化できるElectron Forgeを用いてアプリの開発とパッケージ化を行い、electron-builderにて.snapファイルを生成し、それをSnap Storeに公開する手順となります。Snap Storeで公開すると、全世界のユーザーがsnapコマンドやUbuntu Software経由でパッケージをインストールできるようになります。

筆者は、このElectronで開発したフローチャートが作成できるアプリflowchartorをSnap Storeに公開しています。もし良ければ、以下のコマンドでインストール可能なので、使ってみてください。

sudo snap install flowchartor

どういったアプリなのか簡単に紹介した動画も作ってみました。この動画では、UbuntuにApacheをインストールするフローチャートを、このアプリで作成しています。
https://youtu.be/ERNmbG2bA8Y

Electronをまったく知らない方は、日本語の公式サイトが分かりやすく纏めてくれているので、まずは上記のリンクから先にチュートリアルだけでもざっと見てもらうことをお勧めします。snapについてはこちらの記事がおすすめです。

環境

本記事で紹介する内容は以下の環境で動作確認しています。

  • OS: Ubuntu22.04
  • Node.js: v18.15.0

Electron Forgeによる開発環境構築

プロジェクトの作成

ここではこちらで紹介されている、WebpackとTypescriptが同梱されたElectron Forgeの開発環境を構築します。以下のコマンドを任意のディレクトリで実行してください。my-new-appの箇所は好きなプロジェクト名が指定可能です。

npm init electron-app@latest my-new-app -- --template=webpack-typescript

これでmy-new-appディレクトリが作成されたので、cdコマンドで移動してください。まずはプロジェクトを起動してみましょう。以下のコマンドを実行します。

npm start

次のようなウインドウが開かれれば成功です。

各ファイルについて

ではここで、プロジェクトのファイル構成を見てみましょう。隠しファイルも含めると以下のようになっていると思います。※表示の都合上、node_modulesディレクトリは除外しています。

.
├── .eslintrc.json
├── .gitignore
├── .webpack
│   ├── main
│   └── renderer
├── forge.config.ts
├── package.json
├── src
│   ├── index.css
│   ├── index.html
│   ├── index.ts
│   ├── preload.ts
│   └── renderer.ts
├── tsconfig.json
├── webpack.main.config.ts
├── webpack.plugins.ts
├── webpack.renderer.config.ts
├── webpack.rules.ts
└── yarn.lock

Electronのチュートリアルを読まれた方なら、ファイル名から、そのファイルの役割は何となく想像できると思いますが、今回使用したForge特有のファイルもあるので、筆者の理解の範囲内で、開発に必要な知識を少しだけ解説します。まずは、forge.config.tsを開いてください。以下の記述があると思います。

forge.config.ts
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],

ここでは、最初にインポートした各パッケージの形式に対応したMaker用のクラスをインスタンス化して配列に格納し、それをmakersに設定しています。これにより、後述するパッケージ化のためのmakeコマンドを実行すると、アプリをパッケージ化した.zip、.rpm、.debファイル等が作成されます。但し、実際に作成されるパッケージファイルは実行しているOSに依存するようです。例えば、macOSでmakeすると、.zipファイルは作成されますが、.rpmや.debファイルは作成されません。逆に、Ubuntuでmakeすると.zipファイルは作成されず.debファイルが作成されます。注意点として、Ubuntuでmakeを試したところ、上記のようにnew MakerRPM({})を指定していると.rpmファイルも作成しようとします。但し、基本的にUbuntuではrpm-buildは使用できないのでエラーが発生します。ですので、Ubuntuにてmakeする場合は、new MakerRPM({})を削除してください。

次に、以下の箇所を見てみましょう。

forge.config.ts
entryPoints: [
  {
    html: './src/index.html',
    js: './src/renderer.ts',
    name: 'main_window',
    preload: {
      js: './src/preload.ts',
    },
  },
],

その名の通り、ここでは各エントリーポイントとなるファイルを指定しています。
html:で指定されているindex.htmlが最初にウインドウに読み込まれます。このファイルを確認すると、先ほどnpm startにてアプリを起動した際に表示されたHTMLが確認できます。

js:で指定されているrenderer.tsは、index.htmlで読み込まれるスクリプトです。アプリを起動して開発者ツールのコンソールタブを開くと、このファイルで出力している文字列が確認できます。

name:で指定している文字列は、このentryPointsに対して付与したい名前のようなものかと。これにより、別ファイル(例えばindex.ts)からentryPointsの内容を参照したいとき、付与した名前を接頭辞にしたグローバル変数を使えるようになるみたいです。詳細はこちらを参照してください。

preload:については、そのままpreloadファイルを指定しているだけです。preloadの詳細については公式チュートリアルを参照してください。

今度は、package.jsonを開いてください。以下の記述があると思います。

package.json
"main": ".webpack/main",

このように、このプロジェクトでは、.webpack/main配下にwebpackによりバンドルされたファイルやコンパイルされたファイルが出力されるようになっており、アプリが起動すると、ここのスクリプトを読み込もうとするようです。そのため、例えば何らかのファイルから__dirname(現在のディレクトリのパスが格納された変数)を出力すると/.webpack/mainが戻り値となります。開発する上で知っておいた方が良いと思うので、覚えておいてください。

Reactのセットアップ

最後に、先ほど紹介したflowchartorではReactを使用したため、この環境でReactを使用する方法も紹介したいと思います。まずは必要なパッケージをインストールします。

npm i react react-dom && npm i -D @types/react @types/react-dom

次にindex.htmlを以下に変更して保存します。

./src/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>My Electron App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

次に、renderer.tsの拡張子を.tsxに変更し、以下の内容で保存します。

./src/renderer.tsx
import './index.css';
import React from 'react';
import { createRoot } from "react-dom/client"
import App from './component/App';

const root = createRoot(document.getElementById('root'));

root.render(
  <App />
)

続いて、./src/componentディレクトリを作成し、そこにApp.tsxを以下の内容で作成します。

./src/component/App.tsx
import React from "react";

const App: React.FC = () => {
  return (
    <p>This is App Component.</p>
  )
}

export default App;

続いて、tsconfig.jsonに以下を追記して保存します。

./tsconfig.json
"jsx": "react",

最後に、forge.config.jsのエントリーポイントを修正して保存します。

./forge.config.js
- js: './src/renderer.ts',
+ js: './src/renderer.tsx',

この状態で、以下のコマンドを実行してアプリを起動すると、ウインドウの表示が変更されていると思います。

npm start

プロジェクトのパッケージ化

では、makeによるパッケージ化を試してみましょう。以下のコマンドを実行します。

npm run make

しばらくすると新たにoutディレクトリが作成され、そこにmakeされた実行可能ファイルやパッケージファイルが格納されています。例えばUbuntuの場合、実行可能ファイルとしてout/my-new-app-linux-x64/my-new-appが生成され、これを実行するとアプリが起動します。パッケージファイルはout/make/deb/x64/my-new-app_1.0.0_amd64.debとして生成されます。macOSの場合だと、実行可能ファイルとしてout/my-new-app-darwin-x64/my-new-app.appが生成され、このファイルを起動するとアプリが立ち上がります。パッケージファイルはout/make/zip/darwin/x64/my-new-app-darwin-x64-1.0.0.zipとして生成されます。

electron-builderによる.snapファイルの作成

それでは、今作成したElectronアプリをSnap Storeに公開するために、まずは.snapファイルを生成したいと思います。この記事では最初に説明したように、electron-builderによりこれを行います。

基本的には、公式サイトの手順通りに実施するだけです。サイトではnpmではなくyarnを強く推奨しているので、yarnがインストールされていない場合は、以下によりインストールします。

npm install --global yarn
yarn --version

yarnのバージョンが出力されればOKです。

続いて、electron-builder関連のパッケージをインストールします。

yarn add electron-builder --dev

続いて、package.jsonに以下を追記します。

./package.json
"scripts": {
+  "app:dir": "electron-builder --dir",
+  "app:dist": "electron-builder"
},
//...
+ "build": {
+   "appId": "your.id",
+   "mac": {
+     "category": "your.app.category.type"
+   }
+ },

これで準備完了です。それでは、以下のコマンドにより.snapファイルを作成します。

yarn app:dist

実行が完了すると、distディレクトリが作成され、そこにmy-new-app_1.0.0_amd64.snapが格納されていると思います。これを以下のコマンドによりインストールします。※今作成した.snapファイルは未署名のため、--dangerousオプションが必要となります。

sudo snap install dist/my-new-app_1.0.0_amd64.snap --dangerous

インストール後、以下のコマンドを実行することでアプリが起動できます。

/snap/bin/my-new-app

Snap Storeへの公開

アカウントの作成

では、先ほど作成した.snapファイルをSnap Storeに公開したいと思います。それにはアカウント登録が必要なので、Snap Storeのサイトでアカウントを作成します。

アカウントが作成できたらsnapcraftコマンドをインストールして、ログインにより認証情報を取得します。

sudo snap install snapcraft --classic
snapcraft login

それでは、アップロードする前にいくつか必要な準備をしましょう。

パッケージのセルフレビュー

必須ではないと思いますが、パッケージのセルフレビューをしておきましょう。Snap Storeにアップロードされたパッケージは自動的なレビューと、パッケージの内容によっては人力によるマニュアルレビューも経た上で公開されるようです。このうち自動的なレビューについては、ツールを使えば自分でも実行可能なので、実際にアップロードする前にレビューしてみましょう。passと出力されればOKです。

sudo snap install review-tools
snap-review dist/my-new-app_1.0.0_amd64.snap

パッケージ名の登録

アップロード前にパッケージの名前を事前にSnap Storeに登録する必要があるみたいです。Snap Storeではパッケージ名の重複は不可なので、付けたいパッケージ名をサイトで検索して、使用されていないことを確認しておきましょう。名前が決まったら以下により登録します。※ここではmy-new-appで登録すると仮定します。

snapcraft register my-new-app

アップロード先のチャンネルを決める

チャンネルとは、開発者がパッケージのリビジョンを管理しやすくするための仕組みで、以下の4つが用意されています。

  • stable
  • candidate
  • beta
  • edge

特に拘りがなければ、stableを使用すれば良いと思います。

Snap Storeへ公開する

それではいよいよSnap Storeに公開します。

snapcraft upload --release=stable dist/my-new-app_1.0.0_amd64.snap

問題なく完了すれば、今公開したパッケージがSnap Storeで検索可能になり、そこからインストールすることができます。ローカルにインストール済みのsnapファイルを一度アンインストールして試してみましょう。

sudo snap remove --purge my-new-app

そして、Snap Storeからパッケージをインストールします。先ほどまで付けていた--dangerousオプションは不要です。

sudo snap install my-new-app

先ほど同様の方法で、アプリが起動できるはずです。

いくつかの注意事項

最後に、筆者が作業した中で直面した注意事項について、いくつか紹介します。

パッケージのアップデートについて

既に公開済みのパッケージをアップデートしたいことがあると思います。例えば、機能を追加したり、バグを修正したりして。そういった場合は、package.jsonのversionキーの値を修正し、"yarn app:dist"コマンドにて新しい.snapファイルを作成後、同じ手順でSnap StoreにuploadすればOKです。但し、1つ注意点として、"yarn app:dist"を実行する前に"npm run make"を実行しておいてください。筆者が試したところ、ソースコードを修正しただけの状態や、"npm start"を実行しただけの状態では、修正内容が反映された.snapファイルは正常に生成されませんでした。恐らく、electron-builderによるビルドが、Electron Forgeによってパッケージ化されたファイルを元に行われているからだと思います。

snapcraft login実行時のエラー

"snapcraft login"コマンドを実行して認証しようとすると、以下のエラーが発生することがあるようです。

craft-store error: Credentials found for 'snapcraft' on 'dashboard.snapcraft.io'.

その時は、一度logoutすれば改善すると思います。

snapcraft logout

Discussion