Goなどでクロスコンパイルしたワンバイナリなソフトウェアを最小工数でdeb/RPMパッケージにしてくれるNFPMが便利

みなさんはdeb/RPMパッケージを作ったことはありますか?

私はtcpdpのパッケージ作成ではじめて作りました。具体的にはここらへんです。

tcpdpはlibpcapに依存していることもあり、DockerでUbuntu/CentOSの環境を作ってその上でコンパイルして、合わせてdeb/RPMパッケージ化までするようにしています。

本来Goはクロスコンパイルができるので、コンパイルしてできたバイナリをdeb/RPMパッケージにできればいいわけです。

ただ、意外にdeb/RPMパッケージをするための前準備が面倒です。

debパッケージであればcontrolファイルを書かないといけないですし、RPMパッケージであればspecファイルを書く必要があります。それぞれ覚えているほど書く機会はないので毎回ググるはめに。

ビルド方法にもお作法があります。これもあまり覚えていませんがディレクトリ構成などいろいろ面倒だった記憶があります。。

いくらワンバイナリとはいえaptやyumで管理できるメリットは大きいので、できればdeb/RPMパッケージも用意したいのですが、上記のような面倒さから億劫になっていました。

NFPM

ここで見つけたのがNFPMです。

github.com

NFPMはGoReleaserのorg配下にあり、GoReleaser( .goreleaser.yml )からも呼び出せるようになっています。むしろGoReleaser経由の方法を紹介します。

実際にcgrpsの設定を元に説明します。

cgrps/.goreleaser.yml at master · k1LoW/cgrps · GitHub

builds ディレクティブで id を付与する

まず、builds ディレクティブでビルド方法を指定すると思いますが、こちらで id を付与して nfpms ディレクティブで指定できるようにします。

builds:
-
  id: cgrps-linux 👈
  env:
  - CGO_ENABLED=0
  goos:
    - linux
  goarch:
    - amd64

ちょっと古い .goreleaser.yml の書き方だと id の指定は必要ありませんが、これの機会に指定しておきましょう。

nfpms ディレクティブを追加する

nfpms ディレクティブを追加します。

cgrpsでは以下のようにしています。

nfpms:
  -
    id: cgrps-nfpms
    name_template: "{{ .ProjectName }}_{{ .Version }}-1_{{ .Arch }}"
    builds:
    - cgrps-linux 👈
    homepage: https://github.com/k1LoW/cgrps
    maintainer: Ken'ichiro Oyama <k1lowxb@gmail.com>
    description: cgrps is a set of commands for checking cgroups.
    license: MIT
    formats:
      - deb
      - rpm
    bindir: /usr/bin
    epoch: 1

詳しくは GoReleaserのNFPMの設定のドキュメント をご覧ください。

nfpms.builds には、先ほどの builds.id を指定します。

UbuntuCentOSで利用するdeb/RPMパッケージであれば、Linux用にコンパイルする設定を書いた builds.id を指定することになると思います。

goreleaserコマンドを実行

設定は、これで終わりです。

試しに以下のコマンドを実行すると dist/ ディレクトリにdeb/RPMパッケージが作成されていると思います。

$ goreleaser --snapshot --skip-publish --rm-dist

あとはいつものように goreleaser コマンドを実行することでリリースが完了します。

NFPMでは何をしているのか?

では実際にNFPMでは何をしているのか気になったので、ソースコードを見てみました。

愚直にspecファイルを書いてrpmbuildコマンドを叩いていました *1

ある意味安心な実装でしたので、みなさんも是非ご利用ください。

*1:なので、実はMacではbrew install rpmが必要です