Snap形式のパッケージの作り方

# はじめに

開発したソフトウェアは何らかの手段でユーザーのお手元にお届けしてようやく価値が出てきますが（自分向けを除く）、その方法には様々なものがあります。
例えばWebアプリの場合やユーザーがアクセスすることのできるWebサーバーで動く状態にすることで実現できます。スマホアプリであればそのOSのアプリストアに登録してインストールできるようにすることが一般的です。また、今時の言語処理系の多くはパッケージの配布システムを持っているのでそこに登録することもあるかと思います。

筆者も個人的にLinux上で動くコンパイラ的な物を作っているのですが、それを配布するためにSnapというパッケージ形式を試してみたのでその経緯を含めて説明します。

# 経緯

筆者は個人的に開発しているソフトウェアをオープンソースで公開しています。開発がある程度進んだので興味を持った人に試しに使って欲しいと思いましたが、その際に開発者と同じ手順を踏んでもらおうとすると非常に負担が大きく、利用の前に諦めてしまう方が出てしまいます。例えば、git cloneを実行し、必要なドキュメントを読んだ上でビルドツール等をインストールしてビルドを行う、場合によってはトラブルシューティングも行うというのは結構な手間です。

また、手間の問題だけでなく、セキュリティのリスクもあります。筆者を含めて多くのアプリ開発者は自分のアプリの内容について悪意を持った説明はせず、アプリそのものも悪意のある動作はしないと考えられます。しかしながら、利用者の立場になってみるとネットのどこかから手に入れたバイナリが悪さをするリスクを考える必要があります（ ~/.ssh/の内容を送信されたりしたら大変ですよね ）。
このリスクを軽減する方法としては(1)信頼できるディストリビューターが提供するシステム経由でソフトウェアを入手する。(2)動作に制限をかけられるパッケージシステムを使う。の二つがあると思います。(1)は一般的なLinuxのディストリビューションで行われている方法で、(2)はスマートフォンのアプリの配布に使われている方法です。

こういったことを念頭において、いくつかの配布手段を検討してみました。


## ディストリビューション経由での配布
ソフトウェアを利用する立場からすればaptやyumのコマンド一つでインストール可能になっているのが一番楽ですが、開発する立場からするとかなり困難な道のりです。
まず、ディストリビューションの開発チームの誰かがあなたの作ったソフトを有用であると感じてパッケージングを行ってくれることが必要です。もしかすると個人的なツテを利用して彼らの優先順位に影響を与えることもできるかもしれませんが、いつでも誰でも使える方法ではないですよね。
結局、自力で利用者に負担をかけないで気軽に使ってもらえる方法を見つけ出して有名にならないとコマンド一つでのインストール可能にならないという面倒な状況になります。
これは筆者のようにマニアックな分野のソフトウェアにおいては更に致命的です。
(ディストリビューション開発者が間に入ることによるメリットもあるのですが、筆者の経験上コミュニケーションのコストが非常に大きくて、零細の開発者にお勧めできるものではないというのが正直なところです。)


## 言語処理系のパッケージングシステムを利用しての配布
PythonやRubyのように有力なパッケージングシステム(pipやgem)を持つ処理系を用いて開発していればそこに登録することでコマンド一つでインストール可能な状態にすることができます。登録のプロセスもセルフサービスで行えて非常に簡単です。
しかし、残念ながら、筆者の処理系はC++等を用いて作成しているため、この方法を使うのは困難なのが現状です。
（これらのパッケージングシステムの多くは他言語によるモジュール込みでパッケージを作れるので、mainだけをその言語で作って残り全てを元のC++のコードにしてしまうという抜け道を結構マジメに検討しました…）

# Snap

ここまでに述べた検討を経て選んだSnapはUbuntuの開発元であるCanonical社が開発したパッケージングシステムで、Snappyと呼ばれることもあります。スマートフォンのOSに取り組んでいた頃に開発されたようで、

- 言語処理系のパッケージシステム同様にセルフサービスでアップロードできる
- 任意のバイナリをパッケージ化できる
- 許可しておいた動作しかできないように設定できる
- Ubuntuの特定のバージョンのruntimeを指定できるが、Ubuntu以外の対応ディストリビューションでもその互換runtimeで動く

といった特徴があります。
https://snapcraft.io/store Storeのページを見ていただくと名前を聞いたソフトもいくつかあるかと思います。それらの多くは開発者が公式に提供しているものです。
インストールも簡単で

```
$ sudo snap install {{パッケージの名前}}
```
とコマンド一つです。

## 作り方

https://docs.snapcraft.io/ を読んでいただくのが正しいのですが、ここでも簡単に流れを解説してみます。

まず、https://snapcraft.io/ でアカウントを作り、ログインして「Register a snap name」で自分のパッケージの名前を予約します

自分のパッケージ中に snap/snapcraft.yml を置きます。詳細は公式サイトで確認してください。

```
name: name_of_the_package # 予約したパッケージ名
base: core18
version: '1.2.3'
summary: short summary of what is name_of_the_package. # 一行で説明
description: |
  name_of_the_package does foo bar hoge huga... # 長い説明

grade: stable
confinement: strict

parts:
  name_of_the_package:
    plugin: dump
    source: pkg.tar.gz # 後述するmake pkgで作る

apps:
  name_of_the_package:
    command: name_of_the_package
    plugs:
      - home # home directoryの隠しファイル以外にアクセスできるように設定
```

いくつかの言語処理系に対してはパッケージをビルドしてファイルを取り込む方法が提供されているのですが、関連するファイルの取り込みなどが発生する場合はマニュアルでtarファイルを作る方式を選ぶのが簡単なようなのでそれを選択しました。
筆者の手元のMakefileは次のような感じです。

```
…
pkg:
	rm -rf pkg
	mkdir pkg
	cp command pkg/
	cp other_files pkg/
	tar czf pkg.tar.gz pkg
```

リリースの流れは次のような感じになります。

```
$ make pkg
$ snapcraft debug
… 結構時間がかかる
$ ls
name_of_the_package_1.2.3_amd64.snap
$ snap remove name_of_the_package　# インストール済だったら削除
$ snap install name_of_the_package_1.2.3_amd64.snap # ビルドした物をインストール

… テストしてみる

$ snapcraft login # snapcraft.ioで作ったアカウントを聞かれるので入力してログイン
$ snapcraft upload --release candidate name_of_the_package_1.2.3_amd64.snap # 手元のパッケージをupload
$ snapcraft status name_of_the_package # uploadされたものの状態をチェック
Track Arch Channel Version Revision
latest amd64 candidate 1.2.3 1
…
$ snapcraft release name_of_the_package 1 stable # Revision 1をstableにも配信
```

あとはTwitterやQiitaで「sudo snap install name_of_the_package」と宣伝すれば興味を持ったユーザーがインストールしてくれるかもしれません。 https://snapcraft.io/name_of_the_package から説明やアイコンを設定することによってユーザーが増えることも考えられます。

例えばVisual Studio Codeの配布ページは https://snapcraft.io/code ですが、世界中のどんな地域からインストールされているかわかりますし、開発者としてログインしていれば地域ごとの人数も見ることができます。